Add: toggle dark/night mode styles automatically (#736)
* Add: color-scheme.js * Add: handle color scheme * Add: styleManager.setMeta * Add: make setupLivePrefs work with radio * Change: drop setupRadioButtons * Add: UI for schemeSwitcher * Add: prefer-scheme select in installation page * Fix: add alarm listener * Add: display excluded reason in popup * Fix: rely on data-value-type instead of input name * Fix: oldValue and newValue should have the same type * Change: detect media change in content script * Fix: duplicate capitalize * Fix: minor * Update web-ext * Fix: valueAsNumber doesn't work for all inputs * Fix: disable colorscheme selection after install * Fix: API error
This commit is contained in:
		
							parent
							
								
									19ebeedf6a
								
							
						
					
					
						commit
						6c13db1468
					
				|  | @ -596,6 +596,18 @@ | |||
|     "message": "Update style", | ||||
|     "description": "Label for update button" | ||||
|   }, | ||||
|   "installPreferSchemeLabel": { | ||||
|     "message": "The style should be applied:" | ||||
|   }, | ||||
|   "installPreferSchemeNone": { | ||||
|     "message": "Always" | ||||
|   }, | ||||
|   "installPreferSchemeDark": { | ||||
|     "message": "In Dark Mode" | ||||
|   }, | ||||
|   "installPreferSchemeLight": { | ||||
|     "message": "In Light Mode" | ||||
|   }, | ||||
|   "installUpdate": { | ||||
|     "message": "Install update", | ||||
|     "description": "Label for the button to install an update for a single style" | ||||
|  | @ -1053,6 +1065,18 @@ | |||
|   "optionsAdvancedNewStyleAsUsercss": { | ||||
|     "message": "Write new style as usercss" | ||||
|   }, | ||||
|   "optionsAdvancedAutoSwitchScheme": { | ||||
|     "message": "Toggle Light/Dark Mode styles automatically" | ||||
|   }, | ||||
|   "optionsAdvancedAutoSwitchSchemeNever": { | ||||
|     "message": "Never" | ||||
|   }, | ||||
|   "optionsAdvancedAutoSwitchSchemeBySystem": { | ||||
|     "message": "By system preference" | ||||
|   }, | ||||
|   "optionsAdvancedAutoSwitchSchemeByTime": { | ||||
|     "message": "By night time:" | ||||
|   }, | ||||
|   "optionsAdvancedPatchCsp": { | ||||
|     "message": "Patch <code>CSP</code> to allow style assets" | ||||
|   }, | ||||
|  | @ -1533,6 +1557,12 @@ | |||
|     "message": "Style was not applied due to its incorrect usage of 'regexp()'", | ||||
|     "description": "Tooltip in the popup for styles that were not applied at all" | ||||
|   }, | ||||
|   "styleNotAppliedSchemeDark": { | ||||
|     "message": "This style is only applied in Dark Mode" | ||||
|   }, | ||||
|   "styleNotAppliedSchemeLight": { | ||||
|     "message": "This style is only applied in Light Mode" | ||||
|   }, | ||||
|   "styleRegexpInvalidExplanation": { | ||||
|     "message": "Some 'regexp()' rules that could not be compiled at all." | ||||
|   }, | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
|   findExistingTab | ||||
|   openURL | ||||
| */ // toolbox.js | ||||
| /* global colorScheme */ // color-scheme.js
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| //#region API
 | ||||
|  | @ -41,6 +42,7 @@ addAPI(/** @namespace API */ { | |||
|   updater: updateMan, | ||||
|   usercss: usercssMan, | ||||
|   usw: uswApi, | ||||
|   colorScheme, | ||||
|   /** @type {BackgroundWorker} */ | ||||
|   worker: createWorker({url: '/background/background-worker'}), | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										100
									
								
								background/color-scheme.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								background/color-scheme.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,100 @@ | |||
| /* global prefs */ | ||||
| /* exported colorScheme */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| const colorScheme = (() => { | ||||
|   let systemPreferDark = false; | ||||
|   let timePreferDark = false; | ||||
|   const changeListeners = new Set(); | ||||
| 
 | ||||
|   const checkTime = ['schemeSwitcher.nightStart', 'schemeSwitcher.nightEnd']; | ||||
|   prefs.subscribe(checkTime, (key, value) => { | ||||
|     updateTimePreferDark(); | ||||
|     createAlarm(key, value); | ||||
|   }); | ||||
|   checkTime.forEach(key => createAlarm(key, prefs.get(key))); | ||||
| 
 | ||||
|   prefs.subscribe(['schemeSwitcher.enabled'], emitChange); | ||||
| 
 | ||||
|   chrome.alarms.onAlarm.addListener(info => { | ||||
|     if (checkTime.includes(info.name)) { | ||||
|       updateTimePreferDark(); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   updateSystemPreferDark(); | ||||
|   updateTimePreferDark(); | ||||
| 
 | ||||
|   return {shouldIncludeStyle, onChange, updateSystemPreferDark}; | ||||
| 
 | ||||
|   function createAlarm(key, value) { | ||||
|     const date = new Date(); | ||||
|     applyDate(date, value); | ||||
|     if (date.getTime() < Date.now()) { | ||||
|       date.setDate(date.getDate() + 1); | ||||
|     } | ||||
|     chrome.alarms.create(key, { | ||||
|       when: date.getTime(), | ||||
|       periodInMinutes: 24 * 60, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function shouldIncludeStyle(style) { | ||||
|     const isDark = style.preferScheme === 'dark'; | ||||
|     const isLight = style.preferScheme === 'light'; | ||||
|     if (!isDark && !isLight) { | ||||
|       return true; | ||||
|     } | ||||
|     const switcherState = prefs.get('schemeSwitcher.enabled'); | ||||
|     if (switcherState === 'never') { | ||||
|       return true; | ||||
|     } | ||||
|     if (switcherState === 'system') { | ||||
|       return systemPreferDark && isDark || | ||||
|         !systemPreferDark && isLight; | ||||
|     } | ||||
|     return timePreferDark && isDark || | ||||
|       !timePreferDark && isLight; | ||||
|   } | ||||
| 
 | ||||
|   function updateSystemPreferDark() { | ||||
|     const oldValue = systemPreferDark; | ||||
|     systemPreferDark = window.matchMedia('(prefers-color-scheme: dark)').matches; | ||||
|     if (systemPreferDark !== oldValue) { | ||||
|       emitChange(); | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   function updateTimePreferDark() { | ||||
|     const oldValue = timePreferDark; | ||||
|     const date = new Date(); | ||||
|     const now = date.getTime(); | ||||
|     applyDate(date, prefs.get('schemeSwitcher.nightStart')); | ||||
|     const start = date.getTime(); | ||||
|     applyDate(date, prefs.get('schemeSwitcher.nightEnd')); | ||||
|     const end = date.getTime(); | ||||
|     timePreferDark = start > end ? | ||||
|       now >= start || now < end : | ||||
|       now >= start && now < end; | ||||
|     if (timePreferDark !== oldValue) { | ||||
|       emitChange(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function applyDate(date, time) { | ||||
|     const [h, m] = time.split(':').map(Number); | ||||
|     date.setHours(h, m, 0, 0); | ||||
|   } | ||||
| 
 | ||||
|   function onChange(listener) { | ||||
|     changeListeners.add(listener); | ||||
|   } | ||||
| 
 | ||||
|   function emitChange() { | ||||
|     for (const listener of changeListeners) { | ||||
|       listener(); | ||||
|     } | ||||
|   } | ||||
| })(); | ||||
|  | @ -6,6 +6,7 @@ | |||
| /* global prefs */ | ||||
| /* global tabMan */ | ||||
| /* global usercssMan */ | ||||
| /* global colorScheme */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| /* | ||||
|  | @ -61,6 +62,14 @@ const styleMan = (() => { | |||
|   let ready = init(); | ||||
| 
 | ||||
|   chrome.runtime.onConnect.addListener(handleLivePreview); | ||||
|   // function handleColorScheme() {
 | ||||
|   colorScheme.onChange(() => { | ||||
|     for (const {style: data} of dataMap.values()) { | ||||
|       if (data.preferScheme === 'dark' || data.preferScheme === 'light') { | ||||
|         broadcastStyleUpdated(data, 'colorScheme', undefined, false); | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   //#endregion
 | ||||
|   //#region Exports
 | ||||
|  | @ -183,6 +192,7 @@ const styleMan = (() => { | |||
|       const query = createMatchQuery(url); | ||||
|       for (const style of styles) { | ||||
|         let excluded = false; | ||||
|         let excludedScheme = false; | ||||
|         let sloppy = false; | ||||
|         let sectionMatched = false; | ||||
|         const match = urlMatchStyle(query, style); | ||||
|  | @ -193,6 +203,9 @@ const styleMan = (() => { | |||
|         if (match === 'excluded') { | ||||
|           excluded = true; | ||||
|         } | ||||
|         if (match === 'excludedScheme') { | ||||
|           excludedScheme = true; | ||||
|         } | ||||
|         for (const section of style.sections) { | ||||
|           if (styleSectionGlobal(section) && styleCodeEmpty(section.code)) { | ||||
|             continue; | ||||
|  | @ -207,7 +220,7 @@ const styleMan = (() => { | |||
|           } | ||||
|         } | ||||
|         if (sectionMatched) { | ||||
|           result.push(/** @namespace StylesByUrlResult */ {style, excluded, sloppy}); | ||||
|           result.push(/** @namespace StylesByUrlResult */ {style, excluded, sloppy, excludedScheme}); | ||||
|         } | ||||
|       } | ||||
|       return result; | ||||
|  | @ -546,6 +559,9 @@ const styleMan = (() => { | |||
|     if (!style.enabled) { | ||||
|       return 'disabled'; | ||||
|     } | ||||
|     if (!colorScheme.shouldIncludeStyle(style)) { | ||||
|       return 'excludedScheme'; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -68,6 +68,13 @@ | |||
|     window.addEventListener(orphanEventId, orphanCheck, true); | ||||
|   } | ||||
| 
 | ||||
|   // detect media change in content script
 | ||||
|   // FIXME: move this to background page when following bugs are fixed:
 | ||||
|   // https://bugzilla.mozilla.org/show_bug.cgi?id=1561546
 | ||||
|   // https://bugs.chromium.org/p/chromium/issues/detail?id=968651
 | ||||
|   const media = window.matchMedia('(prefers-color-scheme: dark)'); | ||||
|   media.addListener(() => API.colorScheme.updateSystemPreferDark().catch(console.error)); | ||||
| 
 | ||||
|   function onInjectorUpdate() { | ||||
|     if (!isOrphaned) { | ||||
|       updateCount(); | ||||
|  |  | |||
|  | @ -50,6 +50,14 @@ | |||
|             <svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg> | ||||
|             <span i18n-text="liveReloadLabel"></span> | ||||
|           </label> | ||||
|           <label class="set-prefer-scheme"> | ||||
|             <span i18n-text="installPreferSchemeLabel"></span> | ||||
|             <select> | ||||
|               <option value="none" i18n-text="installPreferSchemeNone"></option> | ||||
|               <option value="dark" i18n-text="installPreferSchemeDark"></option> | ||||
|               <option value="light" i18n-text="installPreferSchemeLight"></option> | ||||
|             </select> | ||||
|           </label> | ||||
|           <p hidden class="installed-actions"> | ||||
|             <a href="manage.html" tabindex="0"><button i18n-text="openManage"></button></a> | ||||
|             <a href="edit.html?id=" tabindex="0"><button i18n-text="editStyleLabel"></button></a> | ||||
|  |  | |||
|  | @ -242,7 +242,6 @@ h2.installed.active ~ .configure-usercss:hover svg { | |||
| .set-update-url { | ||||
|   flex-wrap: wrap; | ||||
| } | ||||
| 
 | ||||
| .set-update-url p { | ||||
|   word-break: break-all; | ||||
|   opacity: .5; | ||||
|  | @ -250,6 +249,10 @@ h2.installed.active ~ .configure-usercss:hover svg { | |||
|   margin: .25em 0 .25em; | ||||
| } | ||||
| 
 | ||||
| label.set-prefer-scheme:not(.unavailable) { | ||||
|   padding-left: 0; | ||||
| } | ||||
| 
 | ||||
| .external { | ||||
|   text-align: center; | ||||
| } | ||||
|  | @ -299,6 +302,7 @@ li { | |||
| } | ||||
| 
 | ||||
| label { | ||||
|   /* FIXME: why do we want to give all labels a padding? */ | ||||
|   padding-left: 16px; | ||||
|   position: relative; | ||||
| } | ||||
|  |  | |||
|  | @ -135,6 +135,13 @@ setTimeout(() => !cm && showSpinner($('#header')), 200); | |||
|   $('.set-update-url p').textContent = updateUrl.href.length < 300 ? updateUrl.href : | ||||
|     updateUrl.href.slice(0, 300) + '...'; | ||||
| 
 | ||||
|   // set prefer scheme
 | ||||
|   const preferScheme = $('.set-prefer-scheme select'); | ||||
|   preferScheme.onchange = () => { | ||||
|     style.preferScheme = preferScheme.value; | ||||
|   }; | ||||
|   preferScheme.onchange(); | ||||
| 
 | ||||
|   if (URLS.isLocalhost(initialUrl)) { | ||||
|     $('.live-reload input').onchange = liveReload.onToggled; | ||||
|   } else { | ||||
|  | @ -167,6 +174,9 @@ function updateMeta(style, dup = installedDup) { | |||
|   $('.meta-name').textContent = data.name; | ||||
|   $('.meta-version').textContent = data.version; | ||||
|   $('.meta-description').textContent = data.description; | ||||
|   $('.set-prefer-scheme select').value = | ||||
|     style.preferScheme === 'dark' ? 'dark' : | ||||
|     style.preferScheme === 'light' ? 'light' : 'none'; | ||||
| 
 | ||||
|   if (data.author) { | ||||
|     $('.meta-author').parentNode.style.display = ''; | ||||
|  | @ -305,6 +315,7 @@ function install(style) { | |||
|   $('.set-update-url input[type=checkbox]').disabled = true; | ||||
|   $('.set-update-url').title = style.updateUrl ? | ||||
|     t('installUpdateFrom', style.updateUrl) : ''; | ||||
|   $('.set-prefer-scheme select').disabled = true; | ||||
|   enablePostActions(); | ||||
|   updateMeta(style); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										59
									
								
								js/dom.js
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								js/dom.js
									
									
									
									
									
								
							|  | @ -291,39 +291,64 @@ function scrollElementIntoView(element, {invalidMarginRatio = 0} = {}) { | |||
|  * Accepts an array of pref names (values are fetched via prefs.get) | ||||
|  * and establishes a two-way connection between the document elements and the actual prefs | ||||
|  */ | ||||
| function setupLivePrefs(ids = prefs.knownKeys.filter(id => $('#' + id))) { | ||||
| function setupLivePrefs(ids = prefs.knownKeys.filter(id => $(`#${CSS.escape(id)}, [name=${CSS.escape(id)}]`))) { | ||||
|   let forceUpdate = true; | ||||
|   prefs.subscribe(ids, updateElement, {runNow: true}); | ||||
|   forceUpdate = false; | ||||
|   ids.forEach(id => $('#' + id).on('change', onChange)); | ||||
| 
 | ||||
|   for (const id of ids) { | ||||
|     const elements = $$(`#${CSS.escape(id)}, [name=${CSS.escape(id)}]`); | ||||
|     for (const element of elements) { | ||||
|       element.addEventListener('change', onChange); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function onChange() { | ||||
|     prefs.set(this.id, this[getPropName(this)]); | ||||
|     if (!this.checkValidity()) { | ||||
|       return; | ||||
|     } | ||||
|     if (this.type === 'radio' && !this.checked) { | ||||
|       return; | ||||
|     } | ||||
|     prefs.set(this.id || this.name, getValue(this)); | ||||
|   } | ||||
| 
 | ||||
|   function getPropName(el) { | ||||
|     return el.type === 'checkbox' ? 'checked' | ||||
|       : el.type === 'number' ? 'valueAsNumber' : | ||||
|         'value'; | ||||
|   function getValue(el) { | ||||
|     const type = el.dataset.valueType || el.type; | ||||
|     return type === 'checkbox' ? el.checked : | ||||
|       // https://stackoverflow.com/questions/18062069/why-does-valueasnumber-return-nan-as-a-value
 | ||||
|       // valueAsNumber is not applicable for input[text/radio] or select
 | ||||
|       type === 'number' ? Number(el.value) : | ||||
|       el.value; | ||||
|   } | ||||
| 
 | ||||
|   function isSame(el, propName, value) { | ||||
|     return el[propName] === value || | ||||
|   function isSame(el, oldValue, value) { | ||||
|     return oldValue === value || | ||||
|       typeof value === 'boolean' && | ||||
|       el.tagName === 'SELECT' && | ||||
|       el[propName] === `${value}`; | ||||
|       oldValue === `${value}` || | ||||
|       el.type === 'radio' && (oldValue === value) === el.checked; | ||||
|   } | ||||
| 
 | ||||
|   function updateElement(id, value) { | ||||
|     const el = $('#' + id); | ||||
|     if (el) { | ||||
|       const prop = getPropName(el); | ||||
|       if (!isSame(el, prop, value) || forceUpdate) { | ||||
|         el[prop] = value; | ||||
|     const els = $$(`#${CSS.escape(id)}, [name=${CSS.escape(id)}]`); | ||||
|     if (!els.length) { | ||||
|       // FIXME: why do we unsub all ids when a single id is missing from the page
 | ||||
|       prefs.unsubscribe(ids, updateElement); | ||||
|       return; | ||||
|     } | ||||
|     for (const el of els) { | ||||
|       const oldValue = getValue(el); | ||||
|       if (!isSame(el, oldValue, value) || forceUpdate) { | ||||
|         if (el.type === 'radio') { | ||||
|           el.checked = value === oldValue; | ||||
|         } else if (el.type === 'checkbox') { | ||||
|           el.checked = value; | ||||
|         } else { | ||||
|           el.value = value; | ||||
|         } | ||||
|         el.dispatchEvent(new Event('change', {bubbles: true})); | ||||
|       } | ||||
|     } else { | ||||
|       prefs.unsubscribe(ids, updateElement); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -30,6 +30,10 @@ | |||
|     // checkbox in style config dialog
 | ||||
|     'config.autosave': true, | ||||
| 
 | ||||
|     'schemeSwitcher.enabled': 'never', | ||||
|     'schemeSwitcher.nightStart': '18:00', | ||||
|     'schemeSwitcher.nightEnd': '06:00', | ||||
| 
 | ||||
|     'popup.breadcrumbs': true,      // display 'New style' links as URL breadcrumbs
 | ||||
|     'popup.breadcrumbs.usePath': false, // use URL path for 'this URL'
 | ||||
|     'popup.enabledFirst': true,     // display enabled styles before disabled styles
 | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ | |||
|       "background/common.js", | ||||
| 
 | ||||
|       "background/db.js", | ||||
|       "background/color-scheme.js", | ||||
|       "background/icon-manager.js", | ||||
|       "background/navigation-manager.js", | ||||
|       "background/style-search-db.js", | ||||
|  |  | |||
							
								
								
									
										24
									
								
								options.html
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								options.html
									
									
									
									
									
								
							|  | @ -52,7 +52,7 @@ | |||
|           <label> | ||||
|             <span i18n-text="optionsIconDark"></span> | ||||
|             <div class="iconset"> | ||||
|               <input type="radio" name="iconset"> | ||||
|               <input type="radio" name="iconset" value="0" data-value-type="number"> | ||||
|               <img src="/images/icon/16.png"> | ||||
|               <img src="/images/icon/16w.png"> | ||||
|               <img src="/images/icon/16x.png"> | ||||
|  | @ -61,7 +61,7 @@ | |||
|           <label> | ||||
|             <span i18n-text="optionsIconLight"></span> | ||||
|             <div class="iconset"> | ||||
|               <input type="radio" name="iconset"> | ||||
|               <input type="radio" name="iconset" value="1" data-value-type="number"> | ||||
|               <img src="/images/icon/light/16.png"> | ||||
|               <img src="/images/icon/light/16w.png"> | ||||
|               <img src="/images/icon/light/16x.png"> | ||||
|  | @ -272,6 +272,26 @@ | |||
|               <span></span> | ||||
|             </span> | ||||
|           </label> | ||||
|           <div class="radio-group"> | ||||
|             <span i18n-text="optionsAdvancedAutoSwitchScheme" class="radio-group-label"></span> | ||||
|             <label class="radio-group-item"> | ||||
|               <input type="radio" value="never" name="schemeSwitcher.enabled" class="radio"> | ||||
|               <span i18n-text="optionsAdvancedAutoSwitchSchemeNever"></span> | ||||
|             </label> | ||||
|             <label class="radio-group-item"> | ||||
|               <input type="radio" value="system" name="schemeSwitcher.enabled" class="radio"> | ||||
|               <span i18n-text="optionsAdvancedAutoSwitchSchemeBySystem"></span> | ||||
|             </label> | ||||
|             <label class="radio-group-item"> | ||||
|               <input type="radio" value="time" name="schemeSwitcher.enabled" class="radio"> | ||||
|               <span> | ||||
|                 <span i18n-text="optionsAdvancedAutoSwitchSchemeByTime"></span> | ||||
|                 <input type="text" pattern="\d{2}:\d{2}" required id="schemeSwitcher.nightStart" class="input-sm"> | ||||
|                 ~ | ||||
|                 <input type="text" pattern="\d{2}:\d{2}" required id="schemeSwitcher.nightEnd" class="input-sm"> | ||||
|               </span> | ||||
|             </label> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|  |  | |||
|  | @ -192,7 +192,8 @@ input[type=number] { | |||
|   text-align: right; | ||||
| } | ||||
| 
 | ||||
| input[type=number]:invalid { | ||||
| input[type=number]:invalid, | ||||
| input[type=text]:invalid { | ||||
|   background-color: rgba(255, 0, 0, 0.1); | ||||
|   color: darkred; | ||||
| } | ||||
|  | @ -376,6 +377,40 @@ html:not(.firefox):not(.opera) #updates { | |||
|   position: relative; | ||||
| } | ||||
| 
 | ||||
| /* radio group */ | ||||
| .radio-group-item { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   min-height: 1.5em; | ||||
| } | ||||
| .radio-group-item > input { | ||||
|   margin: 0 8px 0 0; | ||||
|   flex-grow: 0; | ||||
| } | ||||
| .radio-group-label { | ||||
|   display: block; | ||||
|   margin: 0 0 .3em; | ||||
| } | ||||
| 
 | ||||
| .input-sm { | ||||
|   width: 3em; | ||||
| } | ||||
| 
 | ||||
| /* pixel perfect radio */ | ||||
| input[type="radio"].radio::after { | ||||
|   position: absolute; | ||||
|   top: -1px; | ||||
|   right: -1px; | ||||
|   bottom: -1px; | ||||
|   left: -1px; | ||||
|   height: auto; | ||||
|   width: auto; | ||||
|   transform: scale(0); | ||||
| } | ||||
| input[type="radio"].radio:checked::after { | ||||
|   transform: scale(.65); | ||||
| } | ||||
| 
 | ||||
| @keyframes fadeinout { | ||||
|   0%   { opacity: 0 } | ||||
|   10%  { opacity: 1 } | ||||
|  |  | |||
|  | @ -23,7 +23,6 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| setupLivePrefs(); | ||||
| setupRadioButtons(); | ||||
| $$('input[min], input[max]').forEach(enforceInputRange); | ||||
| 
 | ||||
| if (CHROME_POPUP_BORDER_BUG) { | ||||
|  | @ -196,29 +195,6 @@ function checkUpdates() { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| function setupRadioButtons() { | ||||
|   const sets = {}; | ||||
|   const onChange = function () { | ||||
|     const newValue = sets[this.name].indexOf(this); | ||||
|     if (newValue >= 0 && prefs.get(this.name) !== newValue) { | ||||
|       prefs.set(this.name, newValue); | ||||
|     } | ||||
|   }; | ||||
|   // group all radio-inputs by name="prefName" attribute
 | ||||
|   for (const el of $$('input[type="radio"][name]')) { | ||||
|     (sets[el.name] = sets[el.name] || []).push(el); | ||||
|     el.on('change', onChange); | ||||
|   } | ||||
|   // select the input corresponding to the actual pref value
 | ||||
|   for (const name in sets) { | ||||
|     sets[name][prefs.get(name)].checked = true; | ||||
|   } | ||||
|   // listen to pref changes and update the values
 | ||||
|   prefs.subscribe(Object.keys(sets), (key, value) => { | ||||
|     sets[key][value].checked = true; | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function customizeHotkeys() { | ||||
|   // command name -> i18n id
 | ||||
|   const hotkeys = new Map([ | ||||
|  |  | |||
							
								
								
									
										8351
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8351
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -27,7 +27,7 @@ | |||
|     "make-fetch-happen": "^8.0.7", | ||||
|     "sync-version": "^1.0.1", | ||||
|     "tiny-glob": "^0.2.6", | ||||
|     "web-ext": "^5.5.0" | ||||
|     "web-ext": "^6.5.0" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "lint": "eslint \"**/*.js\" --cache", | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
|   CHROME_POPUP_BORDER_BUG | ||||
|   FIREFOX | ||||
|   URLS | ||||
|   capitalize | ||||
|   getActiveTab | ||||
|   isEmptyObj | ||||
| */// toolbox.js
 | ||||
|  | @ -372,13 +373,14 @@ function createStyleElement(style) { | |||
|   const styleName = $('.style-name', entry); | ||||
|   styleName.lastChild.textContent = style.customName || style.name; | ||||
|   setTimeout(() => { | ||||
|     styleName.title = entry.styleMeta.sloppy ? | ||||
|       t('styleNotAppliedRegexpProblemTooltip') : | ||||
|         styleName.scrollWidth > styleName.clientWidth + 1 ? | ||||
|           styleName.textContent : ''; | ||||
|     styleName.title = | ||||
|       entry.styleMeta.sloppy ? t('styleNotAppliedRegexpProblemTooltip') : | ||||
|       entry.styleMeta.excludedScheme ? t(`styleNotAppliedScheme${capitalize(entry.styleMeta.preferScheme)}`) : | ||||
|       styleName.scrollWidth > styleName.clientWidth + 1 ? styleName.textContent : | ||||
|       ''; | ||||
|   }); | ||||
| 
 | ||||
|   entry.classList.toggle('not-applied', style.excluded || style.sloppy); | ||||
|   entry.classList.toggle('not-applied', style.excluded || style.sloppy || style.excludedScheme); | ||||
|   entry.classList.toggle('regexp-partial', style.sloppy); | ||||
| 
 | ||||
|   $('.exclude-by-domain-checkbox', entry).checked = Events.isStyleExcluded(style, 'domain'); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user