├── CHANGELOG.md ├── LICENSE ├── README.md ├── images ├── icon-darkmode-128.png ├── icon-lightmode-128.png ├── logo-solid-invert.png ├── logo-solid.png ├── logo.png └── ooui-check-invert.svg ├── manifest.json ├── popup.css ├── popup.html ├── popup.js └── service-worker.js /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 3.1.0 2 | 3 | ### Fixed 4 | 5 | * Fix forgetful "backend" dropdown (Timo Tijhof). 6 | ([T395190](https://phabricator.wikimedia.org/T395190)) 7 | 8 | ### Added 9 | 10 | * Debug instrumentation (Timo Tijhof). 11 | ([T315111](https://phabricator.wikimedia.org/T315111)) 12 | 13 | ## 3.0.1 14 | 15 | ### Fixed 16 | 17 | * Update Firefox add-on label in README.md (Bryan Davis). 18 | * Remove installation warning about granting _Block content on any page_ 19 | permission to the extension. We already have host specific permissions 20 | (Antoine Musso). ([T387822](https://phabricator.wikimedia.org/T387822) 21 | 22 | ## 3.0.0 23 | 24 | ### Added 25 | 26 | * Converted to WebExtensions Manifest v3 (Bryan Davis). ([T312694](https://phabricator.wikimedia.org/T312694)) 27 | 28 | ## 2.9.0 29 | 30 | ### Added 31 | 32 | * Allow wikitech.wikimedia.org ([T371537](https://phabricator.wikimedia.org/T371537)) 33 | 34 | ## 2.8.0 35 | 36 | ### Added 37 | 38 | * Add wikifunctions.org (James Forrester). ([T275945](https://phabricator.wikimedia.org/T275945)) 39 | 40 | ## 2.7.1 41 | 42 | ### Fixed 43 | 44 | * Fix dark mode version of the new output links area. 45 | 46 | ## 2.7.0 47 | 48 | ### Added 49 | 50 | * Add "Excimer UI" option (Timo Tijhof) ([T291015](https://phabricator.wikimedia.org/T291015)) 51 | * Create new "output" area with last five xhgui/excimer/logstash links (Timo Tijhof) 52 | 53 | ### Fixed 54 | 55 | * Fix unwanted animation in Chrome when popup opens ([T331249](https://phabricator.wikimedia.org/T331249)) 56 | * Improve "Unsupported" warning to render above form controls 57 | * Improve "Read-only" label to say "Read-only DB" 58 | * Faster popup render time by removing the "empty page" phase ([change 899034](https://gerrit.wikimedia.org/r/c/performance/WikimediaDebug/+/899034)) 59 | 60 | ## 2.6.0 61 | 62 | ### Removed 63 | 64 | * Remove obsolete "querysort" option (Ori Livneh) 65 | * Remove obsolete PHP 7.4 option (Timo Tijhof) 66 | 67 | ## 2.5.0 68 | 69 | ### Added 70 | 71 | * Add "querysort" option ([T138093](https://phabricator.wikimedia.org/T138093)) (Ori Livneh) 72 | * Add PHP 7.4 option ([T312653](https://phabricator.wikimedia.org/T312653)) (mainframe98) 73 | * Add "Unsupported domain" indicator ([T269028](https://phabricator.wikimedia.org/T269028)) (Larissa Gaulia) 74 | 75 | ### Fixed 76 | 77 | * background: Faster popup opening by caching backends for 1 hour (Timo Tijhof) 78 | 79 | ## 2.4.6 80 | 81 | ### Fixed 82 | 83 | * popup: Fix beta host menu. (Larissa Gaulia) 84 | * Fix Logstash url to account for OpenSearch rebrand. (Larissa Gaulia) 85 | 86 | ## 2.4.5 87 | 88 | ### Fixed 89 | 90 | * popup: Avoid "TypeError" in developer console when using Chrome. ([T294335](https://phabricator.wikimedia.org/T294335)) (Timo Tijhof) 91 | 92 | ## 2.4.4 93 | 94 | ### Fixed 95 | 96 | * popup: Fix "character encoding" warning from Firefox devtools. (Timo Tijhof) 97 | * background: Only fetch from noc.wikimedia.org when opening the popup. 98 | Previously the list was also fetched on every page load on a WMF domain 99 | in any tab, and when WikimediaDebug is off. (Timo Tijhof) 100 | * Use x-request-id header for perf/logging link, instead of `mw.config` wgRequestId. ([T279211](https://phabricator.wikimedia.org/T279211)) (Timo Tijhof) 101 | 102 | ## 2.4.3 103 | 104 | ### Fixed 105 | 106 | * List of debug servers is now fetched from noc.wikimedia.org. (Gilles Dubuc) 107 | 108 | ## 2.4.2 109 | 110 | ### Fixed 111 | 112 | * Added mwdebug1003 to the list of servers. (Effie Mouzeli) 113 | 114 | ## 2.4.1 115 | 116 | ### Fixed 117 | 118 | * Update XHGui address in Beta Cluster. Moved from `performance-beta.wmflabs.org` to . (Timo Tijhof) 119 | 120 | ## 2.4 121 | 122 | See also [WikimediaDebug v2 blog post](https://phabricator.wikimedia.org/phame/post/view/183/wikimediadebug_v2_is_here/). 123 | 124 | ### Added 125 | 126 | * The popup has been redesigned to follow [Wikimedia Design](https://design.wikimedia.org/style-guide/) style guide, and now supports Dark Mode. (Timo Tijhof) 127 | 128 | ### Changed 129 | 130 | * The labels for the footer links are now "Find in XHGui" and "Find in Logstash", 131 | instead of "Profiling Data" and "Debug Logs". 132 | 133 | ## 2.3 134 | 135 | ### Added 136 | 137 | * Add support for XHGui in Beta Cluster. ([T180761](https://phabricator.wikimedia.org/T180761)) (Timo Tijhof) 138 | 139 | ### Changed 140 | 141 | * When the extension shuts off after 15min, it now also resets Profiling and other checkboxes. 142 | 143 | ## 2.2 144 | 145 | ### Added 146 | 147 | * Add new "Inline profile" option. ([#17](https://github.com/wikimedia/WikimediaDebug/issues/17)) (Timo Tijhof) 148 | 149 | ### Fixed 150 | 151 | * Hide production backend choices when in Beta Cluster. ([#14](https://github.com/wikimedia/WikimediaDebug/issues/14)) (Timo Tijhof) 152 | * Hide production XHGui link in Beta Cluster. (Timo Tijhof) 153 | * Fix Logstash link in Beta Cluster to use logstash-beta. (Timo Tijhof) 154 | * Update Logstash URL for Kibana 5 upgrade. (Timo Tijhof) 155 | 156 | ### Removed 157 | 158 | * Remove "PHP 7" option, this is now the default. (Timo Tijhof) 159 | 160 | ## 2.1 161 | 162 | ### Added 163 | 164 | * Add new "PHP 7" option for switching between HHVM and PHP 7. (Giuseppe Lavagetto) 165 | * Document Firefox Add-on release proces. (Timo Tijhof) 166 | 167 | ## 2.0 168 | 169 | ### Fixed 170 | 171 | * Update Codfw hostnames from mw20xx to mwdebug20xx. (Alex Monk) 172 | 173 | ## 1.9 174 | 175 | ### Added 176 | 177 | * Add URL patterns for Beta Cluster. (Bryan Davis) 178 | 179 | ## 1.8.1 180 | 181 | ### Added 182 | 183 | * Document support for Mozilla Firefox. (Kunal Mehta) 184 | * Distribute as Firefox Add-on. (Bryan Davis) 185 | 186 | ## 1.8 187 | 188 | ### Fixed 189 | 190 | * Update default value from mw10xx to mwdebug10xx. (Timo Tijhof) 191 | 192 | ## 1.7 193 | 194 | ### Fixed 195 | 196 | * Update Eqiad hostnames from mw10xx to mwdebug10xx. (addshore) 197 | 198 | ## 1.6 199 | 200 | ### Fixed 201 | 202 | * `wikidata.org` is now included in the the permission prompt. 203 | Its URL pattern was previously not working. (addshore) 204 | * Update Logstash URL for Kibana 4 upgrade. (Bryan Davis) 205 | 206 | ## 1.5 207 | 208 | ### Added 209 | 210 | * Insert footer link to find XHGui profile. (Ori Livneh) 211 | * Insert footer link to find Kibana-Logstash logs. (Ori Livneh) 212 | 213 | ## 1.4 214 | 215 | ### Added 216 | 217 | * Add new "Profile" option. (Ori Livneh) 218 | * Add new "Read only" option. (Ori Livneh) 219 | * Add new "Log" option. (Ori Livneh) 220 | 221 | ## 1.3 222 | 223 | ### Added 224 | 225 | * Add option to select from multiple backends. (Ori Livneh) 226 | 227 | ## 1.1 228 | 229 | ### Added 230 | 231 | * Add 15-min timer to automatically shut off. (Ori Livneh) 232 | * Add URL pattern for `wikidata.org`. (Marius Hoch) 233 | 234 | ## 1.0 235 | 236 | Initial release by Ori Livneh, 7 January 2015. 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018, 2019 Timo Tijhof 2 | Copyright 2015, 2016 Ori Livneh 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | "Icon for Wikimedia Debug Extension" (icon_*.png) is a derivative of "Repair" 17 | by Nicolas Vincent , used under 18 | CC BY. "Icon for Wikimedia Debug Extension" is licensed under CC BY by Ori 19 | Livneh. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WikimediaDebug 2 | 3 | Wikimedia developers can use a special HTTP header, X-Wikimedia-Debug, to 4 | enable certain debugging features. This Web extension allows you to easily 5 | inject the header into Wikimedia HTTP/S requests. Header injection can be 6 | toggled via toolbar icon. 7 | 8 | You can install [WikimediaDebug for Firefox](https://addons.mozilla.org/en-US/firefox/addon/wikimedia-debug-header/), or 9 | [WikimediaDebug for Chromium](https://chrome.google.com/webstore/detail/wikimediadebug/binmakecefompkjggiklgjenddjoifbb). 10 | 11 | ## Release process 12 | 13 | ### Test the release 14 | 15 | * Clone the repository (if not already). 16 | * Checkout latest `origin/master` and ensure a clean working copy. 17 | 18 | ``` 19 | git remote update && git checkout -B release -t origin/master 20 | # Warning: Deletes any untracked files! 21 | git clean -dffx 22 | ``` 23 | * For Chrome: 24 | - Open the extension manager at `chrome://extensions/`. 25 | - Ensure any official installation of WikimediaDebug is disabled. (Temporarily) 26 | - Ensure "Developer mode" is enabled. 27 | - Use "Load unpacked" to load this directory as an extension. 28 | - Test the extension and confirm any change in functionality from a recent commit. 29 | * For Firefox: 30 | - Open the add-on manage at `about:addons` (Tools > Add-ons). 31 | - From the gear menu, click "Debug Add-ons". 32 | - Use "Load Temporary Add-on..." and navigate to this directory and select the manifest.json file. 33 | - Test the extension and confirm any change in functionality from a recent commit. 34 | 35 | ### Create the release 36 | 37 | * After having tested the extension and having a clean working copy 38 | of `origin/master` (per the above), update the version in `manifest.json` ([example](https://gerrit.wikimedia.org/g/performance/WikimediaDebug/+/a2c6cb5b3c89258224bfa906291104e7c5bf77a8)) 39 | and create a commit for this release. 40 | * Add a bullet list of noteworthy changes to `CHANGELOG.md`. 41 | * Push the commit for review and merge it, then pull it down, then create a signed tag and push it. 42 | 43 | ``` 44 | git remote update && git reset origin/master 45 | # edit manifest.json 46 | # edit CHANGELOG.md 47 | git add -p 48 | git commit -m "Tag X.Y.Z" 49 | git review 50 | # merge the commit 51 | git pull 52 | git tag -s X.Y.Z 53 | git push --tags 54 | ``` 55 | * Create a ZIP archive of the extension directory. 56 | 57 | ``` 58 | git archive -v --format zip -9 -o /tmp/WikimediaDebug.zip HEAD 59 | ``` 60 | 61 | ### Upload to the Chrome Web Store 62 | 63 | See . 64 | 65 | * On the [Developer Dashboard](https://chrome.google.com/webstore/developer/dashboard), click "Edit" for WikimediaDebug. (Avoid "Add new item", which creates a new extension instead.) 66 | * Click "Package / Upload new package", then select your local ZIP archive. 67 | * Once back on the Edit page, click "Submit for review" at the top. 68 | * Done! 69 | 70 | ### Upload to Firefox Add-ons 71 | 72 | * Log in at . 73 | * Open "[Manage My Submissions](https://addons.mozilla.org/en-US/developers/addons)" from the Tool menu. 74 | * Select "Wikimedia Debug". 75 | * Click "Upload New Version". 76 | * Click "Select a file..." and select the ZIP archive, then continue. 77 | * When asked for release notes, use the same bullet list as used for [CHANGELOG.md](./CHANGELOG.md). 78 | * Done! 79 | -------------------------------------------------------------------------------- /images/icon-darkmode-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikimedia/performance-WikimediaDebug/ec915e2910859c78a4320697c0ce88685aa5c700/images/icon-darkmode-128.png -------------------------------------------------------------------------------- /images/icon-lightmode-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikimedia/performance-WikimediaDebug/ec915e2910859c78a4320697c0ce88685aa5c700/images/icon-lightmode-128.png -------------------------------------------------------------------------------- /images/logo-solid-invert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikimedia/performance-WikimediaDebug/ec915e2910859c78a4320697c0ce88685aa5c700/images/logo-solid-invert.png -------------------------------------------------------------------------------- /images/logo-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikimedia/performance-WikimediaDebug/ec915e2910859c78a4320697c0ce88685aa5c700/images/logo-solid.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikimedia/performance-WikimediaDebug/ec915e2910859c78a4320697c0ce88685aa5c700/images/logo.png -------------------------------------------------------------------------------- /images/ooui-check-invert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Copyright 2011-2019 OOUI Team and other contributors | https://gerrit.wikimedia.org/g/oojs/ui/+/v0.36.1/LICENSE-MIT 4 | 5 | 6 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "WikimediaDebug", 4 | "description": "Control the X-Wikimedia-Debug header on HTTP requests.", 5 | "author": "Ori Livneh", 6 | "version": "3.1.0", 7 | "icons": { 8 | "512": "images/logo.png" 9 | }, 10 | "action": { 11 | "default_icon": "images/icon-lightmode-128.png", 12 | "default_title": "WikimediaDebug", 13 | "default_popup": "popup.html", 14 | "theme_icons": [ { 15 | "dark": "images/icon-lightmode-128.png", 16 | "light": "images/icon-darkmode-128.png", 17 | "size": 128 18 | } ] 19 | }, 20 | "background": { 21 | "service_worker": "service-worker.js", 22 | "scripts": [ "service-worker.js" ] 23 | }, 24 | "permissions": [ 25 | "alarms", 26 | "storage", 27 | "tabs", 28 | "webRequest", 29 | "declarativeNetRequestWithHostAccess" 30 | ], 31 | "host_permissions": [ 32 | "*://*.mediawiki.org/*", 33 | "*://*.wikidata.org/*", 34 | "*://*.wikifunctions.org/*", 35 | "*://*.wikibooks.org/*", 36 | "*://*.wikimedia.org/*", 37 | "*://*.wikinews.org/*", 38 | "*://*.wikipedia.org/*", 39 | "*://*.wikiquote.org/*", 40 | "*://*.wikisource.org/*", 41 | "*://*.wikiversity.org/*", 42 | "*://*.wikivoyage.org/*", 43 | "*://*.wiktionary.org/*", 44 | "*://*.beta.wmflabs.org/*", 45 | "*://*.tools.wmflabs.org/*", 46 | "*://*.tools-static.wmflabs.org/*" 47 | ], 48 | "browser_specific_settings": { 49 | "gecko": { 50 | "id": "wikimediadebug@wikimedia.org", 51 | "strict_min_version": "115.0" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /popup.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* https://design.wikimedia.org/style-guide/ */ 3 | --wmui-color-base0: #000; 4 | --wmui-color-base10: #202122; 5 | --wmui-color-base20: #54595d; 6 | --wmui-color-base30: #72777d; 7 | --wmui-color-base50: #a2a9b1; 8 | --wmui-color-base70: #c8ccd1; 9 | --wmui-color-base80: #eaecf0; 10 | --wmui-color-base90: #f8f9fa; 11 | --wmui-color-base100: #fff; 12 | --wmui-color-accent30: #2a4b8d; 13 | --wmui-color-accent50: #36c; 14 | --wmui-color-accent--hover: #447ff5; 15 | --wmui-color-accent90: #eaf3ff; 16 | --wmui-color-red30: #b32424; 17 | --wmui-color-red50: #d33; 18 | --wmui-color-red90: #fee7e6; 19 | --wmui-color-yellow30: #ac6600; 20 | --wmui-color-yellow50: #fc3; 21 | --wmui-color-yellow90: #fef6e7; 22 | --wmui-color-green30: #14866d; 23 | --wmui-color-green50: #00af89; 24 | --wmui-color-green90: #d5fdf4; 25 | 26 | --xwd-body-background: var(--wmui-color-base100); 27 | --xwd-body-text: var(--wmui-color-base10); 28 | --xwd-body-text--strong: var(--wmui-color-base0); 29 | --xwd-body-border: var(--wmui-color-base50); 30 | 31 | --xwd-link-text: var(--wmui-color-accent50); 32 | 33 | --xwd-switcher-background: var(--wmui-color-base80); 34 | --xwd-switcher-background--hover: var(--wmui-color-base100); 35 | --xwd-switcher-text: var(--wmui-color-base10); 36 | --xwd-switcher-border: var(--wmui-color-base30); 37 | --xwd-switcher-accent: var(--wmui-color-accent50); 38 | --xwd-switcher-accent--hover: var(--wmui-color-accent--hover); 39 | --xwd-switcher-accent--active: var(--wmui-color-accent30); 40 | --xwd-switcher-accent--text: var(--wmui-color-base100); 41 | 42 | --xwd-check-background: var(--wmui-color-base100); 43 | --xwd-check-border: var(--wmui-color-base30); 44 | --xwd-check-accent: var(--wmui-color-accent50); 45 | --xwd-check-accent--hover: var(--wmui-color-accent--hover); 46 | --xwd-check-accent--active: var(--wmui-color-accent30); 47 | 48 | --xwd-separator-border: var(--wmui-color-base30); 49 | } 50 | @media screen and (prefers-color-scheme: dark) { 51 | :root { 52 | --xwd-body-background: var(--wmui-color-base10); 53 | --xwd-body-text: var(--wmui-color-base90); 54 | --xwd-body-text--strong: var(--wmui-color-base100); 55 | --xwd-body-border: var(--wmui-color-base0); 56 | 57 | --xwd-link-text: var(--wmui-color-accent--hover); 58 | 59 | --xwd-switcher-background: var(--wmui-color-base10); 60 | --xwd-switcher-background--hover: var(--wmui-color-base20); 61 | --xwd-switcher-text: var(--wmui-color-base90); 62 | --xwd-switcher-border: var(--wmui-color-base30); 63 | 64 | --xwd-separator-border: var(--wmui-color-base30); 65 | } 66 | } 67 | 68 | body { 69 | margin: 0; 70 | padding: 8px; 71 | width: 270px; 72 | border: 1px solid var(--xwd-body-border); 73 | background: var(--xwd-body-background); 74 | font-size: 14px; 75 | font-family: system-ui, sans-serif; 76 | color: var(--xwd-body-text); 77 | 78 | box-sizing: border-box; 79 | display: flex; 80 | flex-flow: column nowrap; 81 | /* Firefox and Chrome cap popups to 600px. 82 | Reserve about 150px for form controls, rest for output links. */ 83 | max-height: 600px; 84 | } 85 | a { 86 | color: var(--xwd-link-text); 87 | } 88 | .body-hidden { 89 | opacity: 0.5; 90 | pointer-events: none; 91 | } 92 | .warning { 93 | border: 1px solid var(--wmui-color-yellow50); 94 | background-color: var(--wmui-color-yellow90); 95 | padding: 4px 12px; 96 | margin-bottom: 16px; 97 | color: var(--wmui-color-base10); 98 | } 99 | .row { 100 | display: flex; 101 | align-items: center; /* align 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
    30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Wikimedia Foundation and contributors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 ( the "License" ); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | /* global chrome */ 18 | 19 | function debugLog( msg, ...args ) { 20 | console.info( '[WikimediaDebug/popup.js] ' + msg, ...args ); 21 | } 22 | 23 | const pendingState = new Promise( 24 | ( resolve ) => { 25 | chrome.runtime.sendMessage( 26 | { action: 'get-state' }, 27 | ( response ) => { 28 | debugLog( 'Received get-state response', response.state ); 29 | resolve( response ); 30 | } 31 | ); 32 | } 33 | ); 34 | 35 | /** 36 | * @param {string} tagName 37 | * @param {Object} props 38 | * @param {string|HTMLElement} children String to become a text node or HTMLElement 39 | * @return {HTMLElement} 40 | */ 41 | function dom( tagName, props = {}, ...children ) { 42 | const element = document.createElement( tagName ); 43 | Object.assign( element, props ); 44 | element.append( ...children ); 45 | return element; 46 | } 47 | 48 | function renderOutputList( outputList ) { 49 | const listElement = document.querySelector( '.output' ); 50 | listElement.innerHTML = ''; 51 | const d12hAgo = new Date(); 52 | d12hAgo.setHours( d12hAgo.getHours() - 12 ); 53 | let lastMainItem = null; 54 | for ( const entry of outputList ) { 55 | const itemElement = document.createElement( 'li' ); 56 | itemElement.value = entry.offset; 57 | if ( entry.isMain ) { 58 | itemElement.dataset.main = 'true'; 59 | lastMainItem = itemElement; 60 | } 61 | // Support Chrome: Chrome encodes Date objects as ISO string, 62 | // whereas Firefox performs a structuredClone() 63 | const d = new Date( entry.timestamp ); 64 | // Use undefined to let user agent decide 65 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl 66 | const fullDateFmt = d.toLocaleString( undefined, { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: false } ); 67 | const timeFmt = d < d12hAgo 68 | ? d.toLocaleDateString( undefined, { weekday: 'short' } ) 69 | : d.toLocaleTimeString( undefined, { timeStyle: 'short', hour12: false } ); 70 | entry.links.forEach( ( link, i ) => { 71 | itemElement.append( 72 | i !== 0 ? ', ' : '', 73 | dom( 'a', { 74 | href: link.href, 75 | // Support Chrome: External links fail to open by default, 76 | // whereas Firefox defaults to opening in a new tab. 77 | target: '_blank' 78 | }, link.label ) 79 | ); 80 | } ); 81 | itemElement.append( 82 | dom( 'span', { className: 'output-entry', title: `Captured from ${ entry.href } at ${ fullDateFmt }`, tabIndex: '0' }, 83 | dom( 'time', { className: 'output-entry-time' }, timeFmt ), 84 | ' ', 85 | dom( 'span', { className: 'output-entry-url' }, entry.href ), 86 | ) 87 | ); 88 | listElement.append( itemElement ); 89 | } 90 | if ( lastMainItem ) { 91 | lastMainItem.scrollIntoView( { behavior: 'instant', block: 'start', inline: 'start' } ); 92 | lastMainItem.focus(); 93 | } 94 | } 95 | 96 | function onMessage( response ) { 97 | if ( response.action === 'set-output' ) { 98 | renderOutputList( response.outputList ); 99 | } 100 | } 101 | 102 | ( async function popup() { 103 | 'use strict'; 104 | const response = await pendingState; 105 | 106 | const optionElements = [].slice.call( document.querySelectorAll( '.option' ) ); 107 | 108 | function onUpdate() { 109 | const message = { action: 'set-state', state: {} }; 110 | 111 | optionElements.forEach( ( el ) => { 112 | let newValue; 113 | if ( el.checked !== undefined ) { 114 | newValue = el.checked; 115 | } else if ( el.value !== undefined ) { 116 | newValue = el.value; 117 | } else { 118 | newValue = el.getAttribute( 'aria-checked' ) === 'true'; 119 | } 120 | message.state[ el.id ] = newValue; 121 | } ); 122 | 123 | debugLog( 'Sending set-state request', message.state ); 124 | chrome.runtime.sendMessage( message ); 125 | } 126 | 127 | function onSwitcherClick() { 128 | const newValue = !( this.getAttribute( 'aria-checked' ) === 'true' ); 129 | this.setAttribute( 'aria-checked', String( newValue ) ); 130 | const e = new Event( 'change' ); 131 | this.dispatchEvent( e ); 132 | } 133 | 134 | function onSwitcherKeypress( e ) { 135 | if ( e.key === ' ' || e.key === 'Enter' ) { 136 | onSwitcherClick.call( this ); 137 | } 138 | } 139 | 140 | if ( response.realm === 'other' ) { 141 | document.querySelector( '.warning' ).hidden = false; 142 | } 143 | 144 | if ( response.backends ) { 145 | const backendElement = document.querySelector( '#backend' ); 146 | backendElement.innerHTML = ''; 147 | response.backends.forEach( ( backend ) => { 148 | const item = document.createElement( 'option' ); 149 | item.value = backend; 150 | item.textContent = backend === '1' ? '(Unspecified backend)' : backend; 151 | backendElement.appendChild( item ); 152 | } ); 153 | } 154 | 155 | optionElements.forEach( ( el ) => { 156 | const value = response.state[ el.id ]; 157 | if ( value !== null ) { 158 | if ( el.checked !== undefined ) { 159 | // Assume boolean for 160 | el.checked = value; 161 | } else if ( el.value !== undefined ) { 162 | // Assume string option for