diff options
author | Alex Yatskov <alex@foosoft.net> | 2020-02-24 21:31:14 -0800 |
---|---|---|
committer | Alex Yatskov <alex@foosoft.net> | 2020-02-24 21:31:14 -0800 |
commit | d32f4def0eeed1599857bc04c973337a2a13dd8b (patch) | |
tree | 61149656f361dd2d9998d67d68249dc184b73fbb /ext/bg/js/settings | |
parent | 0c5b9b1fa1599cbf769d96cdebc226310f9dd8bc (diff) | |
parent | 706c3edcffb0078d71fd5b58775f16cf5fc1205b (diff) |
Merge branch 'master' into testing
Diffstat (limited to 'ext/bg/js/settings')
-rw-r--r-- | ext/bg/js/settings/anki-templates.js | 3 | ||||
-rw-r--r-- | ext/bg/js/settings/anki.js | 28 | ||||
-rw-r--r-- | ext/bg/js/settings/audio-ui.js | 1 | ||||
-rw-r--r-- | ext/bg/js/settings/audio.js | 2 | ||||
-rw-r--r-- | ext/bg/js/settings/backup.js | 5 | ||||
-rw-r--r-- | ext/bg/js/settings/conditions-ui.js | 1 | ||||
-rw-r--r-- | ext/bg/js/settings/dictionaries.js | 125 | ||||
-rw-r--r-- | ext/bg/js/settings/main.js | 35 | ||||
-rw-r--r-- | ext/bg/js/settings/popup-preview-frame.js | 34 | ||||
-rw-r--r-- | ext/bg/js/settings/popup-preview.js | 8 | ||||
-rw-r--r-- | ext/bg/js/settings/profiles.js | 4 | ||||
-rw-r--r-- | ext/bg/js/settings/storage.js | 1 |
12 files changed, 166 insertions, 81 deletions
diff --git a/ext/bg/js/settings/anki-templates.js b/ext/bg/js/settings/anki-templates.js index 5e74358f..2e80e334 100644 --- a/ext/bg/js/settings/anki-templates.js +++ b/ext/bg/js/settings/anki-templates.js @@ -16,6 +16,9 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global getOptionsContext, getOptionsMutable, settingsSaveOptions +profileOptionsGetDefaultFieldTemplates, ankiGetFieldMarkers, ankiGetFieldMarkersHtml, dictFieldFormat +apiOptionsGet, apiTermsFind*/ function onAnkiFieldTemplatesReset(e) { e.preventDefault(); diff --git a/ext/bg/js/settings/anki.js b/ext/bg/js/settings/anki.js index 9adb2f2a..4263fc51 100644 --- a/ext/bg/js/settings/anki.js +++ b/ext/bg/js/settings/anki.js @@ -16,6 +16,9 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global getOptionsContext, getOptionsMutable, settingsSaveOptions +utilBackgroundIsolate, utilAnkiGetDeckNames, utilAnkiGetModelNames, utilAnkiGetModelFieldNames +onFormOptionsChanged*/ // Private @@ -33,14 +36,27 @@ function _ankiSpinnerShow(show) { function _ankiSetError(error) { const node = document.querySelector('#anki-error'); - if (!node) { return; } + const node2 = document.querySelector('#anki-invalid-response-error'); if (error) { - node.hidden = false; - node.textContent = `${error}`; - _ankiSetErrorData(node, error); + const errorString = `${error}`; + if (node !== null) { + node.hidden = false; + node.textContent = errorString; + _ankiSetErrorData(node, error); + } + + if (node2 !== null) { + node2.hidden = (errorString.indexOf('Invalid response') < 0); + } } else { - node.hidden = true; - node.textContent = ''; + if (node !== null) { + node.hidden = true; + node.textContent = ''; + } + + if (node2 !== null) { + node2.hidden = true; + } } } diff --git a/ext/bg/js/settings/audio-ui.js b/ext/bg/js/settings/audio-ui.js index 711c2291..555380b4 100644 --- a/ext/bg/js/settings/audio-ui.js +++ b/ext/bg/js/settings/audio-ui.js @@ -16,7 +16,6 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ - class AudioSourceUI { static instantiateTemplate(templateSelector) { const template = document.querySelector(templateSelector); diff --git a/ext/bg/js/settings/audio.js b/ext/bg/js/settings/audio.js index cff3f521..588d9a11 100644 --- a/ext/bg/js/settings/audio.js +++ b/ext/bg/js/settings/audio.js @@ -16,6 +16,8 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global getOptionsContext, getOptionsMutable, settingsSaveOptions +AudioSourceUI, audioGetTextToSpeechVoice*/ let audioSourceUI = null; diff --git a/ext/bg/js/settings/backup.js b/ext/bg/js/settings/backup.js index becdc568..f4d622a4 100644 --- a/ext/bg/js/settings/backup.js +++ b/ext/bg/js/settings/backup.js @@ -16,6 +16,10 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global apiOptionsGetFull, apiGetEnvironmentInfo +utilBackend, utilIsolate, utilBackgroundIsolate, utilReadFileArrayBuffer +optionsGetDefault, optionsUpdateVersion +profileOptionsGetDefaultFieldTemplates*/ // Exporting @@ -159,7 +163,6 @@ async function _showSettingsImportWarnings(warnings) { sanitize: e.currentTarget.dataset.importSanitize === 'true' }); modalNode.modal('hide'); - }; const onModalHide = () => { complete({result: false}); diff --git a/ext/bg/js/settings/conditions-ui.js b/ext/bg/js/settings/conditions-ui.js index 4d041451..5a271321 100644 --- a/ext/bg/js/settings/conditions-ui.js +++ b/ext/bg/js/settings/conditions-ui.js @@ -16,6 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global conditionsNormalizeOptionValue*/ class ConditionsUI { static instantiateTemplate(templateSelector) { diff --git a/ext/bg/js/settings/dictionaries.js b/ext/bg/js/settings/dictionaries.js index ed171ae9..70a22a16 100644 --- a/ext/bg/js/settings/dictionaries.js +++ b/ext/bg/js/settings/dictionaries.js @@ -16,6 +16,11 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global getOptionsContext, getOptionsMutable, getOptionsFullMutable, settingsSaveOptions, apiOptionsGetFull, apiOptionsGet +utilBackgroundIsolate, utilDatabaseDeleteDictionary, utilDatabaseGetDictionaryInfo, utilDatabaseGetDictionaryCounts +utilDatabasePurge, utilDatabaseImport +storageUpdateStats, storageEstimate +PageExitPrevention*/ let dictionaryUI = null; @@ -161,7 +166,7 @@ class SettingsDictionaryListUI { delete n.dataset.dict; $(n).modal('hide'); - const index = this.dictionaryEntries.findIndex((e) => e.dictionaryInfo.title === title); + const index = this.dictionaryEntries.findIndex((entry) => entry.dictionaryInfo.title === title); if (index >= 0) { this.dictionaryEntries[index].deleteDictionary(); } @@ -174,7 +179,7 @@ class SettingsDictionaryEntryUI { this.dictionaryInfo = dictionaryInfo; this.optionsDictionary = optionsDictionary; this.counts = null; - this.eventListeners = []; + this.eventListeners = new EventListenerCollection(); this.isDeleting = false; this.content = content; @@ -193,10 +198,10 @@ class SettingsDictionaryEntryUI { this.applyValues(); - this.addEventListener(this.enabledCheckbox, 'change', (e) => this.onEnabledChanged(e), false); - this.addEventListener(this.allowSecondarySearchesCheckbox, 'change', (e) => this.onAllowSecondarySearchesChanged(e), false); - this.addEventListener(this.priorityInput, 'change', (e) => this.onPriorityChanged(e), false); - this.addEventListener(this.deleteButton, 'click', (e) => this.onDeleteButtonClicked(e), false); + this.eventListeners.addEventListener(this.enabledCheckbox, 'change', (e) => this.onEnabledChanged(e), false); + this.eventListeners.addEventListener(this.allowSecondarySearchesCheckbox, 'change', (e) => this.onAllowSecondarySearchesChanged(e), false); + this.eventListeners.addEventListener(this.priorityInput, 'change', (e) => this.onPriorityChanged(e), false); + this.eventListeners.addEventListener(this.deleteButton, 'click', (e) => this.onDeleteButtonClicked(e), false); } cleanup() { @@ -207,7 +212,7 @@ class SettingsDictionaryEntryUI { this.content = null; } this.dictionaryInfo = null; - this.clearEventListeners(); + this.eventListeners.removeAllEventListeners(); } setCounts(counts) { @@ -224,18 +229,6 @@ class SettingsDictionaryEntryUI { this.parent.save(); } - addEventListener(node, type, listener, options) { - node.addEventListener(type, listener, options); - this.eventListeners.push([node, type, listener, options]); - } - - clearEventListeners() { - for (const [node, type, listener, options] of this.eventListeners) { - node.removeEventListener(type, listener, options); - } - this.eventListeners = []; - } - applyValues() { this.enabledCheckbox.checked = this.optionsDictionary.enabled; this.allowSecondarySearchesCheckbox.checked = this.optionsDictionary.allowSecondarySearches; @@ -272,9 +265,7 @@ class SettingsDictionaryEntryUI { this.isDeleting = false; progress.hidden = true; - const optionsContext = getOptionsContext(); - const options = await getOptionsMutable(optionsContext); - onDatabaseUpdated(options); + onDatabaseUpdated(); } } @@ -359,28 +350,33 @@ async function dictSettingsInitialize() { document.querySelector('#dict-main').addEventListener('change', (e) => onDictionaryMainChanged(e), false); document.querySelector('#database-enable-prefix-wildcard-searches').addEventListener('change', (e) => onDatabaseEnablePrefixWildcardSearchesChanged(e), false); - const optionsContext = getOptionsContext(); - const options = await getOptionsMutable(optionsContext); - onDictionaryOptionsChanged(options); - onDatabaseUpdated(options); + await onDictionaryOptionsChanged(); + await onDatabaseUpdated(); } -async function onDictionaryOptionsChanged(options) { +async function onDictionaryOptionsChanged() { if (dictionaryUI === null) { return; } + + const optionsContext = getOptionsContext(); + const options = await getOptionsMutable(optionsContext); + dictionaryUI.setOptionsDictionaries(options.dictionaries); const optionsFull = await apiOptionsGetFull(); document.querySelector('#database-enable-prefix-wildcard-searches').checked = optionsFull.global.database.prefixWildcardsSupported; + + await updateMainDictionarySelectValue(); } -async function onDatabaseUpdated(options) { +async function onDatabaseUpdated() { try { const dictionaries = await utilDatabaseGetDictionaryInfo(); dictionaryUI.setDictionaries(dictionaries); document.querySelector('#dict-warning').hidden = (dictionaries.length > 0); - updateMainDictionarySelect(options, dictionaries); + updateMainDictionarySelectOptions(dictionaries); + await updateMainDictionarySelectValue(); const {counts, total} = await utilDatabaseGetDictionaryCounts(dictionaries.map((v) => v.title), true); dictionaryUI.setCounts(counts, total); @@ -389,7 +385,7 @@ async function onDatabaseUpdated(options) { } } -async function updateMainDictionarySelect(options, dictionaries) { +function updateMainDictionarySelectOptions(dictionaries) { const select = document.querySelector('#dict-main'); select.textContent = ''; // Empty @@ -399,8 +395,6 @@ async function updateMainDictionarySelect(options, dictionaries) { option.textContent = 'Not selected'; select.appendChild(option); - let value = ''; - const currentValue = options.general.mainDictionary; for (const {title, sequenced} of toIterable(dictionaries)) { if (!sequenced) { continue; } @@ -408,26 +402,56 @@ async function updateMainDictionarySelect(options, dictionaries) { option.value = title; option.textContent = title; select.appendChild(option); + } +} + +async function updateMainDictionarySelectValue() { + const optionsContext = getOptionsContext(); + const options = await apiOptionsGet(optionsContext); - if (title === currentValue) { - value = title; + const value = options.general.mainDictionary; + + const select = document.querySelector('#dict-main'); + let selectValue = null; + for (const child of select.children) { + if (child.nodeName.toUpperCase() === 'OPTION' && child.value === value) { + selectValue = value; + break; } } - select.value = value; - - if (options.general.mainDictionary !== value) { - options.general.mainDictionary = value; - settingsSaveOptions(); + let missingNodeOption = select.querySelector('option[data-not-installed=true]'); + if (selectValue === null) { + if (missingNodeOption === null) { + missingNodeOption = document.createElement('option'); + missingNodeOption.className = 'text-muted'; + missingNodeOption.value = value; + missingNodeOption.textContent = `${value} (Not installed)`; + missingNodeOption.dataset.notInstalled = 'true'; + select.appendChild(missingNodeOption); + } + } else { + if (missingNodeOption !== null) { + missingNodeOption.parentNode.removeChild(missingNodeOption); + } } + + select.value = value; } async function onDictionaryMainChanged(e) { - const value = e.target.value; + const select = e.target; + const value = select.value; + + const missingNodeOption = select.querySelector('option[data-not-installed=true]'); + if (missingNodeOption !== null && missingNodeOption.value !== value) { + missingNodeOption.parentNode.removeChild(missingNodeOption); + } + const optionsContext = getOptionsContext(); const options = await getOptionsMutable(optionsContext); options.general.mainDictionary = value; - settingsSaveOptions(); + await settingsSaveOptions(); } @@ -467,15 +491,18 @@ function dictionaryErrorsShow(errors) { dialog.textContent = ''; if (errors !== null && errors.length > 0) { - const uniqueErrors = {}; + const uniqueErrors = new Map(); for (let e of errors) { console.error(e); e = dictionaryErrorToString(e); - uniqueErrors[e] = hasOwn(uniqueErrors, e) ? uniqueErrors[e] + 1 : 1; + let count = uniqueErrors.get(e); + if (typeof count === 'undefined') { + count = 0; + } + uniqueErrors.set(e, count + 1); } - for (const e in uniqueErrors) { - const count = uniqueErrors[e]; + for (const [e, count] of uniqueErrors.entries()) { const div = document.createElement('p'); if (count > 1) { div.textContent = `${e} `; @@ -537,9 +564,7 @@ async function onDictionaryPurge(e) { } await settingsSaveOptions(); - const optionsContext = getOptionsContext(); - const options = await getOptionsMutable(optionsContext); - onDatabaseUpdated(options); + onDatabaseUpdated(); } catch (err) { dictionaryErrorsShow([err]); } finally { @@ -611,9 +636,7 @@ async function onDictionaryImport(e) { dictionaryErrorsShow(errors); } - const optionsContext = getOptionsContext(); - const options = await getOptionsMutable(optionsContext); - onDatabaseUpdated(options); + onDatabaseUpdated(); } } catch (err) { dictionaryErrorsShow([err]); diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index 3bf65eda..d1ad2c6b 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -16,6 +16,14 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global getOptionsContext, apiOptionsSave +utilBackend, utilIsolate, utilBackgroundIsolate +ankiErrorShown, ankiFieldsToDict +ankiTemplatesUpdateValue, onAnkiOptionsChanged, onDictionaryOptionsChanged +appearanceInitialize, audioSettingsInitialize, profileOptionsSetup, dictSettingsInitialize +ankiInitialize, ankiTemplatesInitialize, storageInfoInitialize +*/ + function getOptionsMutable(optionsContext) { return utilBackend().getOptions( utilBackgroundIsolate(optionsContext) @@ -28,6 +36,22 @@ function getOptionsFullMutable() { async function formRead(options) { options.general.enable = $('#enable').prop('checked'); + const enableClipboardPopups = $('#enable-clipboard-popups').prop('checked'); + if (enableClipboardPopups) { + options.general.enableClipboardPopups = await new Promise((resolve, _reject) => { + chrome.permissions.request( + {permissions: ['clipboardRead']}, + (granted) => { + if (!granted) { + $('#enable-clipboard-popups').prop('checked', false); + } + resolve(granted); + } + ); + }); + } else { + options.general.enableClipboardPopups = false; + } options.general.showGuide = $('#show-usage-guide').prop('checked'); options.general.compactTags = $('#compact-tags').prop('checked'); options.general.compactGlossaries = $('#compact-glossaries').prop('checked'); @@ -44,7 +68,7 @@ async function formRead(options) { options.general.popupVerticalOffset = parseInt($('#popup-vertical-offset').val(), 10); options.general.popupHorizontalOffset2 = parseInt($('#popup-horizontal-offset2').val(), 0); options.general.popupVerticalOffset2 = parseInt($('#popup-vertical-offset2').val(), 10); - options.general.popupScalingFactor = parseInt($('#popup-scaling-factor').val(), 10); + options.general.popupScalingFactor = parseFloat($('#popup-scaling-factor').val()); options.general.popupScaleRelativeToPageZoom = $('#popup-scale-relative-to-page-zoom').prop('checked'); options.general.popupScaleRelativeToVisualViewport = $('#popup-scale-relative-to-visual-viewport').prop('checked'); options.general.popupTheme = $('#popup-theme').val(); @@ -67,6 +91,7 @@ async function formRead(options) { options.scanning.enablePopupSearch = $('#enable-search-within-first-popup').prop('checked'); options.scanning.enableOnPopupExpressions = $('#enable-scanning-of-popup-expressions').prop('checked'); options.scanning.enableOnSearchPage = $('#enable-scanning-on-search-page').prop('checked'); + options.scanning.enableSearchTags = $('#enable-search-tags').prop('checked'); options.scanning.delay = parseInt($('#scan-delay').val(), 10); options.scanning.length = parseInt($('#scan-length').val(), 10); options.scanning.modifier = $('#scan-modifier-key').val(); @@ -103,6 +128,7 @@ async function formRead(options) { async function formWrite(options) { $('#enable').prop('checked', options.general.enable); + $('#enable-clipboard-popups').prop('checked', options.general.enableClipboardPopups); $('#show-usage-guide').prop('checked', options.general.showGuide); $('#compact-tags').prop('checked', options.general.compactTags); $('#compact-glossaries').prop('checked', options.general.compactGlossaries); @@ -142,6 +168,7 @@ async function formWrite(options) { $('#enable-search-within-first-popup').prop('checked', options.scanning.enablePopupSearch); $('#enable-scanning-of-popup-expressions').prop('checked', options.scanning.enableOnPopupExpressions); $('#enable-scanning-on-search-page').prop('checked', options.scanning.enableOnSearchPage); + $('#enable-search-tags').prop('checked', options.scanning.enableSearchTags); $('#scan-delay').val(options.scanning.delay); $('#scan-length').val(options.scanning.length); $('#scan-modifier-key').val(options.scanning.modifier); @@ -167,7 +194,7 @@ async function formWrite(options) { await ankiTemplatesUpdateValue(); await onAnkiOptionsChanged(options); - await onDictionaryOptionsChanged(options); + await onDictionaryOptionsChanged(); formUpdateVisibility(options); } @@ -215,7 +242,7 @@ async function settingsSaveOptions() { await apiOptionsSave(source); } -async function onOptionsUpdate({source}) { +async function onOptionsUpdated({source}) { const thisSource = await settingsGetSource(); if (source === thisSource) { return; } @@ -247,7 +274,7 @@ async function onReady() { storageInfoInitialize(); - yomichan.on('optionsUpdate', onOptionsUpdate); + yomichan.on('optionsUpdated', onOptionsUpdated); } $(document).ready(() => onReady()); diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index 37a4b416..aa2b6100 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -16,15 +16,18 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global apiOptionsGet, Popup, PopupProxyHost, Frontend, TextSourceRange*/ class SettingsPopupPreview { constructor() { this.frontend = null; this.apiOptionsGetOld = apiOptionsGet; - this.popupInjectOuterStylesheetOld = Popup.injectOuterStylesheet; + this.popup = null; + this.popupSetCustomOuterCssOld = null; this.popupShown = false; this.themeChangeTimeout = null; this.textSource = null; + this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); } static create() { @@ -49,18 +52,18 @@ class SettingsPopupPreview { const popupHost = new PopupProxyHost(); await popupHost.prepare(); - const popup = popupHost.createPopup(null, 0); - popup.setChildrenSupported(false); + this.popup = popupHost.getOrCreatePopup(); + this.popup.setChildrenSupported(false); - this.frontend = new Frontend(popup); + this.popupSetCustomOuterCssOld = this.popup.setCustomOuterCss; + this.popup.setCustomOuterCss = (...args) => this.popupSetCustomOuterCss(...args); - this.frontend.setEnabled = function () {}; - this.frontend.searchClear = function () {}; + this.frontend = new Frontend(this.popup); - await this.frontend.prepare(); + this.frontend.setEnabled = () => {}; + this.frontend.searchClear = () => {}; - // Overwrite popup - Popup.injectOuterStylesheet = (...args) => this.popupInjectOuterStylesheet(...args); + await this.frontend.prepare(); // Update search this.updateSearch(); @@ -82,20 +85,21 @@ class SettingsPopupPreview { return options; } - popupInjectOuterStylesheet(...args) { + async popupSetCustomOuterCss(...args) { // This simulates the stylesheet priorities when injecting using the web extension API. - const result = this.popupInjectOuterStylesheetOld(...args); + const result = await this.popupSetCustomOuterCssOld.call(this.popup, ...args); - const outerStylesheet = Popup.outerStylesheet; const node = document.querySelector('#client-css'); - if (node !== null && outerStylesheet !== null) { - node.parentNode.insertBefore(outerStylesheet, node); + if (node !== null && result !== null) { + node.parentNode.insertBefore(result, node); } return result; } onMessage(e) { + if (e.origin !== this._targetOrigin) { return; } + const {action, params} = e.data; const handler = SettingsPopupPreview._messageHandlers.get(action); if (typeof handler !== 'function') { return; } @@ -136,7 +140,7 @@ class SettingsPopupPreview { setCustomOuterCss(css) { if (this.frontend === null) { return; } - this.frontend.popup.setCustomOuterCss(css, true); + this.frontend.popup.setCustomOuterCss(css, false); } async updateSearch() { diff --git a/ext/bg/js/settings/popup-preview.js b/ext/bg/js/settings/popup-preview.js index 0d20471e..d1d2ff5e 100644 --- a/ext/bg/js/settings/popup-preview.js +++ b/ext/bg/js/settings/popup-preview.js @@ -40,20 +40,22 @@ function showAppearancePreview() { window.wanakana.bind(text[0]); + const targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); + text.on('input', () => { const action = 'setText'; const params = {text: text.val()}; - frame.contentWindow.postMessage({action, params}, '*'); + frame.contentWindow.postMessage({action, params}, targetOrigin); }); customCss.on('input', () => { const action = 'setCustomCss'; const params = {css: customCss.val()}; - frame.contentWindow.postMessage({action, params}, '*'); + frame.contentWindow.postMessage({action, params}, targetOrigin); }); customOuterCss.on('input', () => { const action = 'setCustomOuterCss'; const params = {css: customOuterCss.val()}; - frame.contentWindow.postMessage({action, params}, '*'); + frame.contentWindow.postMessage({action, params}, targetOrigin); }); container.append(frame); diff --git a/ext/bg/js/settings/profiles.js b/ext/bg/js/settings/profiles.js index c4e68b53..3e589809 100644 --- a/ext/bg/js/settings/profiles.js +++ b/ext/bg/js/settings/profiles.js @@ -16,6 +16,10 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global getOptionsMutable, getOptionsFullMutable, settingsSaveOptions, apiOptionsGetFull +utilBackgroundIsolate, formWrite +conditionsClearCaches, ConditionsUI, profileConditionsDescriptor*/ + let currentProfileIndex = 0; let profileConditionsContainer = null; diff --git a/ext/bg/js/settings/storage.js b/ext/bg/js/settings/storage.js index 6c10f665..cbe1bb4d 100644 --- a/ext/bg/js/settings/storage.js +++ b/ext/bg/js/settings/storage.js @@ -16,6 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/*global apiGetEnvironmentInfo*/ function storageBytesToLabeledString(size) { const base = 1000; |