aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/bg/css/settings.css2
-rw-r--r--ext/bg/js/settings/dictionaries.js544
-rw-r--r--ext/bg/js/settings/main.js25
-rw-r--r--ext/bg/js/settings/storage.js199
-rw-r--r--ext/bg/settings.html1
5 files changed, 384 insertions, 387 deletions
diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css
index f55082e7..eb11d77e 100644
--- a/ext/bg/css/settings.css
+++ b/ext/bg/css/settings.css
@@ -18,7 +18,7 @@
#anki-spinner,
#dict-spinner, #dict-import-progress,
-.storage-hidden, #storage-spinner {
+.storage-hidden {
display: none;
}
diff --git a/ext/bg/js/settings/dictionaries.js b/ext/bg/js/settings/dictionaries.js
index 4d307f0f..dd6dd1c1 100644
--- a/ext/bg/js/settings/dictionaries.js
+++ b/ext/bg/js/settings/dictionaries.js
@@ -22,14 +22,9 @@
* getOptionsFullMutable
* getOptionsMutable
* settingsSaveOptions
- * storageEstimate
- * storageUpdateStats
* utilBackgroundIsolate
*/
-let dictionaryUI = null;
-
-
class SettingsDictionaryListUI {
constructor(container, template, extraContainer, extraTemplate) {
this.container = container;
@@ -308,13 +303,13 @@ class SettingsDictionaryEntryUI {
await api.deleteDictionary(this.dictionaryInfo.title, onProgress);
} catch (e) {
- dictionaryErrorsShow([e]);
+ this.dictionaryErrorsShow([e]);
} finally {
prevention.end();
this.isDeleting = false;
progress.hidden = true;
- onDatabaseUpdated();
+ this.onDatabaseUpdated();
}
}
@@ -388,340 +383,341 @@ class SettingsDictionaryExtraUI {
}
}
+class DictionaryController {
+ constructor(storageController) {
+ this._storageController = storageController;
+ this._dictionaryUI = null;
+ this._dictionaryErrorToStringOverrides = [
+ [
+ 'A mutation operation was attempted on a database that did not allow mutations.',
+ 'Access to IndexedDB appears to be restricted. Firefox seems to require that the history preference is set to "Remember history" before IndexedDB use of any kind is allowed.'
+ ],
+ [
+ 'The operation failed for reasons unrelated to the database itself and not covered by any other error code.',
+ 'Unable to access IndexedDB due to a possibly corrupt user profile. Try using the "Refresh Firefox" feature to reset your user profile.'
+ ],
+ [
+ 'BulkError',
+ 'Unable to finish importing dictionary data into IndexedDB. This may indicate that you do not have sufficient disk space available to complete this operation.'
+ ]
+ ];
+ }
-async function dictSettingsInitialize() {
- dictionaryUI = new SettingsDictionaryListUI(
- document.querySelector('#dict-groups'),
- document.querySelector('#dict-template'),
- document.querySelector('#dict-groups-extra'),
- document.querySelector('#dict-extra-template')
- );
- dictionaryUI.save = settingsSaveOptions;
-
- document.querySelector('#dict-purge-button').addEventListener('click', onDictionaryPurgeButtonClick, false);
- document.querySelector('#dict-purge-confirm').addEventListener('click', onDictionaryPurge, false);
- document.querySelector('#dict-file-button').addEventListener('click', onDictionaryImportButtonClick, false);
- document.querySelector('#dict-file').addEventListener('change', onDictionaryImport, false);
- document.querySelector('#dict-main').addEventListener('change', onDictionaryMainChanged, false);
- document.querySelector('#database-enable-prefix-wildcard-searches').addEventListener('change', onDatabaseEnablePrefixWildcardSearchesChanged, false);
-
- await onDictionaryOptionsChanged();
- await onDatabaseUpdated();
-}
-
-async function onDictionaryOptionsChanged() {
- if (dictionaryUI === null) { return; }
-
- const optionsContext = getOptionsContext();
- const options = await getOptionsMutable(optionsContext);
+ async prepare() {
+ this._dictionaryUI = new SettingsDictionaryListUI(
+ document.querySelector('#dict-groups'),
+ document.querySelector('#dict-template'),
+ document.querySelector('#dict-groups-extra'),
+ document.querySelector('#dict-extra-template')
+ );
+ this._dictionaryUI.save = settingsSaveOptions;
- dictionaryUI.setOptionsDictionaries(options.dictionaries);
+ document.querySelector('#dict-purge-button').addEventListener('click', this._onPurgeButtonClick.bind(this), false);
+ document.querySelector('#dict-purge-confirm').addEventListener('click', this._onPurgeConfirmButtonClick.bind(this), false);
+ document.querySelector('#dict-file-button').addEventListener('click', this._onImportButtonClick.bind(this), false);
+ document.querySelector('#dict-file').addEventListener('change', this._onImportFileChange.bind(this), false);
+ document.querySelector('#dict-main').addEventListener('change', this._onDictionaryMainChanged.bind(this), false);
+ document.querySelector('#database-enable-prefix-wildcard-searches').addEventListener('change', this._onDatabaseEnablePrefixWildcardSearchesChanged.bind(this), false);
- const optionsFull = await api.optionsGetFull();
- document.querySelector('#database-enable-prefix-wildcard-searches').checked = optionsFull.global.database.prefixWildcardsSupported;
+ await this.optionsChanged();
+ await this._onDatabaseUpdated();
+ }
- await updateMainDictionarySelectValue();
-}
+ async optionsChanged() {
+ if (this._dictionaryUI === null) { return; }
-async function onDatabaseUpdated() {
- try {
- const dictionaries = await api.getDictionaryInfo();
- dictionaryUI.setDictionaries(dictionaries);
+ const optionsContext = getOptionsContext();
+ const options = await getOptionsMutable(optionsContext);
- document.querySelector('#dict-warning').hidden = (dictionaries.length > 0);
+ this._dictionaryUI.setOptionsDictionaries(options.dictionaries);
- updateMainDictionarySelectOptions(dictionaries);
- await updateMainDictionarySelectValue();
+ const optionsFull = await api.optionsGetFull();
+ document.querySelector('#database-enable-prefix-wildcard-searches').checked = optionsFull.global.database.prefixWildcardsSupported;
- const {counts, total} = await api.getDictionaryCounts(dictionaries.map((v) => v.title), true);
- dictionaryUI.setCounts(counts, total);
- } catch (e) {
- dictionaryErrorsShow([e]);
+ await this._updateMainDictionarySelectValue();
}
-}
-
-function updateMainDictionarySelectOptions(dictionaries) {
- const select = document.querySelector('#dict-main');
- select.textContent = ''; // Empty
- let option = document.createElement('option');
- option.className = 'text-muted';
- option.value = '';
- option.textContent = 'Not selected';
- select.appendChild(option);
+ // Private
- for (const {title, sequenced} of toIterable(dictionaries)) {
- if (!sequenced) { continue; }
+ _updateMainDictionarySelectOptions(dictionaries) {
+ const select = document.querySelector('#dict-main');
+ select.textContent = ''; // Empty
- option = document.createElement('option');
- option.value = title;
- option.textContent = title;
+ let option = document.createElement('option');
+ option.className = 'text-muted';
+ option.value = '';
+ option.textContent = 'Not selected';
select.appendChild(option);
- }
-}
-
-async function updateMainDictionarySelectValue() {
- const optionsContext = getOptionsContext();
- const options = await api.optionsGet(optionsContext);
- const value = options.general.mainDictionary;
+ for (const {title, sequenced} of toIterable(dictionaries)) {
+ if (!sequenced) { continue; }
- 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;
+ option = document.createElement('option');
+ option.value = title;
+ option.textContent = title;
+ select.appendChild(option);
}
}
- 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);
+ async _updateMainDictionarySelectValue() {
+ const optionsContext = getOptionsContext();
+ const options = await api.optionsGet(optionsContext);
+
+ 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;
+ }
}
- } else {
- if (missingNodeOption !== null) {
- missingNodeOption.parentNode.removeChild(missingNodeOption);
+
+ 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;
}
- select.value = value;
-}
+ _dictionaryErrorToString(error) {
+ if (error.toString) {
+ error = error.toString();
+ } else {
+ error = `${error}`;
+ }
-async function onDictionaryMainChanged(e) {
- const select = e.target;
- const value = select.value;
+ for (const [match, subst] of this._dictionaryErrorToStringOverrides) {
+ if (error.includes(match)) {
+ error = subst;
+ break;
+ }
+ }
- const missingNodeOption = select.querySelector('option[data-not-installed=true]');
- if (missingNodeOption !== null && missingNodeOption.value !== value) {
- missingNodeOption.parentNode.removeChild(missingNodeOption);
+ return error;
}
- const optionsContext = getOptionsContext();
- const options = await getOptionsMutable(optionsContext);
- options.general.mainDictionary = value;
- await settingsSaveOptions();
-}
+ _dictionaryErrorsShow(errors) {
+ const dialog = document.querySelector('#dict-error');
+ dialog.textContent = '';
+ if (errors !== null && errors.length > 0) {
+ const uniqueErrors = new Map();
+ for (let e of errors) {
+ yomichan.logError(e);
+ e = this._dictionaryErrorToString(e);
+ let count = uniqueErrors.get(e);
+ if (typeof count === 'undefined') {
+ count = 0;
+ }
+ uniqueErrors.set(e, count + 1);
+ }
-function dictionaryErrorToString(error) {
- if (error.toString) {
- error = error.toString();
- } else {
- error = `${error}`;
- }
+ for (const [e, count] of uniqueErrors.entries()) {
+ const div = document.createElement('p');
+ if (count > 1) {
+ div.textContent = `${e} `;
+ const em = document.createElement('em');
+ em.textContent = `(${count})`;
+ div.appendChild(em);
+ } else {
+ div.textContent = `${e}`;
+ }
+ dialog.appendChild(div);
+ }
- for (const [match, subst] of dictionaryErrorToString.overrides) {
- if (error.includes(match)) {
- error = subst;
- break;
+ dialog.hidden = false;
+ } else {
+ dialog.hidden = true;
}
}
- return error;
-}
-dictionaryErrorToString.overrides = [
- [
- 'A mutation operation was attempted on a database that did not allow mutations.',
- 'Access to IndexedDB appears to be restricted. Firefox seems to require that the history preference is set to "Remember history" before IndexedDB use of any kind is allowed.'
- ],
- [
- 'The operation failed for reasons unrelated to the database itself and not covered by any other error code.',
- 'Unable to access IndexedDB due to a possibly corrupt user profile. Try using the "Refresh Firefox" feature to reset your user profile.'
- ],
- [
- 'BulkError',
- 'Unable to finish importing dictionary data into IndexedDB. This may indicate that you do not have sufficient disk space available to complete this operation.'
- ]
-];
-
-function dictionaryErrorsShow(errors) {
- const dialog = document.querySelector('#dict-error');
- dialog.textContent = '';
-
- if (errors !== null && errors.length > 0) {
- const uniqueErrors = new Map();
- for (let e of errors) {
- yomichan.logError(e);
- e = dictionaryErrorToString(e);
- let count = uniqueErrors.get(e);
- if (typeof count === 'undefined') {
- count = 0;
- }
- uniqueErrors.set(e, count + 1);
- }
-
- for (const [e, count] of uniqueErrors.entries()) {
- const div = document.createElement('p');
- if (count > 1) {
- div.textContent = `${e} `;
- const em = document.createElement('em');
- em.textContent = `(${count})`;
- div.appendChild(em);
- } else {
- div.textContent = `${e}`;
- }
- dialog.appendChild(div);
+ _dictionarySpinnerShow(show) {
+ const spinner = $('#dict-spinner');
+ if (show) {
+ spinner.show();
+ } else {
+ spinner.hide();
}
+ }
- dialog.hidden = false;
- } else {
- dialog.hidden = true;
+ _dictReadFile(file) {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = () => resolve(reader.result);
+ reader.onerror = () => reject(reader.error);
+ reader.readAsBinaryString(file);
+ });
+ }
+
+ async _onDatabaseUpdated() {
+ try {
+ const dictionaries = await api.getDictionaryInfo();
+ this._dictionaryUI.setDictionaries(dictionaries);
+
+ document.querySelector('#dict-warning').hidden = (dictionaries.length > 0);
+
+ this._updateMainDictionarySelectOptions(dictionaries);
+ await this._updateMainDictionarySelectValue();
+
+ const {counts, total} = await api.getDictionaryCounts(dictionaries.map((v) => v.title), true);
+ this._dictionaryUI.setCounts(counts, total);
+ } catch (e) {
+ this._dictionaryErrorsShow([e]);
+ }
}
-}
+ async _onDictionaryMainChanged(e) {
+ 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);
+ }
-function dictionarySpinnerShow(show) {
- const spinner = $('#dict-spinner');
- if (show) {
- spinner.show();
- } else {
- spinner.hide();
+ const optionsContext = getOptionsContext();
+ const options = await getOptionsMutable(optionsContext);
+ options.general.mainDictionary = value;
+ await settingsSaveOptions();
}
-}
-function onDictionaryImportButtonClick() {
- const dictFile = document.querySelector('#dict-file');
- dictFile.click();
-}
+ _onImportButtonClick() {
+ const dictFile = document.querySelector('#dict-file');
+ dictFile.click();
+ }
-function onDictionaryPurgeButtonClick(e) {
- e.preventDefault();
- $('#dict-purge-modal').modal('show');
-}
+ _onPurgeButtonClick(e) {
+ e.preventDefault();
+ $('#dict-purge-modal').modal('show');
+ }
-async function onDictionaryPurge(e) {
- e.preventDefault();
+ async _onPurgeConfirmButtonClick(e) {
+ e.preventDefault();
- $('#dict-purge-modal').modal('hide');
+ $('#dict-purge-modal').modal('hide');
- const dictControls = $('#dict-importer, #dict-groups, #dict-groups-extra, #dict-main-group').hide();
- const dictProgress = document.querySelector('#dict-purge');
- dictProgress.hidden = false;
+ const dictControls = $('#dict-importer, #dict-groups, #dict-groups-extra, #dict-main-group').hide();
+ const dictProgress = document.querySelector('#dict-purge');
+ dictProgress.hidden = false;
- const prevention = new PageExitPrevention();
+ const prevention = new PageExitPrevention();
- try {
- prevention.start();
- dictionaryErrorsShow(null);
- dictionarySpinnerShow(true);
+ try {
+ prevention.start();
+ this._dictionaryErrorsShow(null);
+ this._dictionarySpinnerShow(true);
- await api.purgeDatabase();
- for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) {
- options.dictionaries = utilBackgroundIsolate({});
- options.general.mainDictionary = '';
- }
- await settingsSaveOptions();
+ await api.purgeDatabase();
+ for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) {
+ options.dictionaries = utilBackgroundIsolate({});
+ options.general.mainDictionary = '';
+ }
+ await settingsSaveOptions();
- onDatabaseUpdated();
- } catch (err) {
- dictionaryErrorsShow([err]);
- } finally {
- prevention.end();
+ this._onDatabaseUpdated();
+ } catch (err) {
+ this._dictionaryErrorsShow([err]);
+ } finally {
+ prevention.end();
- dictionarySpinnerShow(false);
+ this._dictionarySpinnerShow(false);
- dictControls.show();
- dictProgress.hidden = true;
+ dictControls.show();
+ dictProgress.hidden = true;
- if (storageEstimate.mostRecent !== null) {
- storageUpdateStats();
+ this._storageController.updateStats();
}
}
-}
-async function onDictionaryImport(e) {
- const files = [...e.target.files];
- e.target.value = null;
+ async _onImportFileChange(e) {
+ const files = [...e.target.files];
+ e.target.value = null;
- const dictFile = $('#dict-file');
- const dictControls = $('#dict-importer').hide();
- const dictProgress = $('#dict-import-progress').show();
- const dictImportInfo = document.querySelector('#dict-import-info');
+ const dictFile = $('#dict-file');
+ const dictControls = $('#dict-importer').hide();
+ const dictProgress = $('#dict-import-progress').show();
+ const dictImportInfo = document.querySelector('#dict-import-info');
- const prevention = new PageExitPrevention();
+ const prevention = new PageExitPrevention();
- try {
- prevention.start();
- dictionaryErrorsShow(null);
- dictionarySpinnerShow(true);
+ try {
+ prevention.start();
+ this._dictionaryErrorsShow(null);
+ this._dictionarySpinnerShow(true);
- const setProgress = (percent) => dictProgress.find('.progress-bar').css('width', `${percent}%`);
- const updateProgress = (total, current) => {
- setProgress(current / total * 100.0);
- if (storageEstimate.mostRecent !== null && !storageUpdateStats.isUpdating) {
- storageUpdateStats();
- }
- };
+ const setProgress = (percent) => dictProgress.find('.progress-bar').css('width', `${percent}%`);
+ const updateProgress = (total, current) => {
+ setProgress(current / total * 100.0);
+ this._storageController.updateStats();
+ };
- const optionsFull = await api.optionsGetFull();
+ const optionsFull = await api.optionsGetFull();
- const importDetails = {
- prefixWildcardsSupported: optionsFull.global.database.prefixWildcardsSupported
- };
+ const importDetails = {
+ prefixWildcardsSupported: optionsFull.global.database.prefixWildcardsSupported
+ };
- for (let i = 0, ii = files.length; i < ii; ++i) {
- setProgress(0.0);
- if (ii > 1) {
- dictImportInfo.hidden = false;
- dictImportInfo.textContent = `(${i + 1} of ${ii})`;
- }
+ for (let i = 0, ii = files.length; i < ii; ++i) {
+ setProgress(0.0);
+ if (ii > 1) {
+ dictImportInfo.hidden = false;
+ dictImportInfo.textContent = `(${i + 1} of ${ii})`;
+ }
- const archiveContent = await dictReadFile(files[i]);
- const {result, errors} = await api.importDictionaryArchive(archiveContent, importDetails, updateProgress);
- for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) {
- const dictionaryOptions = SettingsDictionaryListUI.createDictionaryOptions();
- dictionaryOptions.enabled = true;
- options.dictionaries[result.title] = dictionaryOptions;
- if (result.sequenced && options.general.mainDictionary === '') {
- options.general.mainDictionary = result.title;
+ const archiveContent = await this._dictReadFile(files[i]);
+ const {result, errors} = await api.importDictionaryArchive(archiveContent, importDetails, updateProgress);
+ for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) {
+ const dictionaryOptions = SettingsDictionaryListUI.createDictionaryOptions();
+ dictionaryOptions.enabled = true;
+ options.dictionaries[result.title] = dictionaryOptions;
+ if (result.sequenced && options.general.mainDictionary === '') {
+ options.general.mainDictionary = result.title;
+ }
}
- }
- await settingsSaveOptions();
+ await settingsSaveOptions();
+
+ if (errors.length > 0) {
+ const errors2 = errors.map((error) => jsonToError(error));
+ errors2.push(`Dictionary may not have been imported properly: ${errors2.length} error${errors2.length === 1 ? '' : 's'} reported.`);
+ this._dictionaryErrorsShow(errors2);
+ }
- if (errors.length > 0) {
- const errors2 = errors.map((error) => jsonToError(error));
- errors2.push(`Dictionary may not have been imported properly: ${errors2.length} error${errors2.length === 1 ? '' : 's'} reported.`);
- dictionaryErrorsShow(errors2);
+ this._onDatabaseUpdated();
}
+ } catch (err) {
+ this._dictionaryErrorsShow([err]);
+ } finally {
+ prevention.end();
+ this._dictionarySpinnerShow(false);
- onDatabaseUpdated();
+ dictImportInfo.hidden = false;
+ dictImportInfo.textContent = '';
+ dictFile.val('');
+ dictControls.show();
+ dictProgress.hide();
}
- } catch (err) {
- dictionaryErrorsShow([err]);
- } finally {
- prevention.end();
- dictionarySpinnerShow(false);
-
- dictImportInfo.hidden = false;
- dictImportInfo.textContent = '';
- dictFile.val('');
- dictControls.show();
- dictProgress.hide();
}
-}
-
-function dictReadFile(file) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onload = () => resolve(reader.result);
- reader.onerror = () => reject(reader.error);
- reader.readAsBinaryString(file);
- });
-}
-
-async function onDatabaseEnablePrefixWildcardSearchesChanged(e) {
- const optionsFull = await getOptionsFullMutable();
- const v = !!e.target.checked;
- if (optionsFull.global.database.prefixWildcardsSupported === v) { return; }
- optionsFull.global.database.prefixWildcardsSupported = !!e.target.checked;
- await settingsSaveOptions();
+ async _onDatabaseEnablePrefixWildcardSearchesChanged(e) {
+ const optionsFull = await getOptionsFullMutable();
+ const v = !!e.target.checked;
+ if (optionsFull.global.database.prefixWildcardsSupported === v) { return; }
+ optionsFull.global.database.prefixWildcardsSupported = !!e.target.checked;
+ await settingsSaveOptions();
+ }
}
diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js
index dddaef6c..1d387749 100644
--- a/ext/bg/js/settings/main.js
+++ b/ext/bg/js/settings/main.js
@@ -19,14 +19,13 @@
* AnkiController
* AnkiTemplatesController
* AudioController
+ * DictionaryController
* ProfileController
* SettingsBackup
* SettingsController
+ * StorageController
* api
* appearanceInitialize
- * dictSettingsInitialize
- * onDictionaryOptionsChanged
- * storageInfoInitialize
* utilBackend
* utilBackgroundIsolate
*/
@@ -270,7 +269,9 @@ async function onOptionsUpdated({source}) {
if (ankiTemplatesController !== null) {
ankiTemplatesController.updateValue();
}
- onDictionaryOptionsChanged();
+ if (dictionaryController !== null) {
+ dictionaryController.optionsChanged();
+ }
if (ankiController !== null) {
ankiController.optionsChanged();
}
@@ -304,8 +305,15 @@ async function settingsPopulateModifierKeys() {
}
}
+async function setupEnvironmentInfo() {
+ const {browser, platform} = await api.getEnvironmentInfo();
+ document.documentElement.dataset.browser = browser;
+ document.documentElement.dataset.operatingSystem = platform.os;
+}
+
let ankiController = null;
let ankiTemplatesController = null;
+let dictionaryController = null;
async function onReady() {
api.forwardLogsToBackend();
@@ -314,22 +322,25 @@ async function onReady() {
const settingsController = new SettingsController();
settingsController.prepare();
+ setupEnvironmentInfo();
showExtensionInformation();
+ const storageController = new StorageController();
+ storageController.prepare();
+
await settingsPopulateModifierKeys();
formSetupEventListeners();
appearanceInitialize();
new AudioController().prepare();
await (new ProfileController()).prepare();
- await dictSettingsInitialize();
+ dictionaryController = new DictionaryController(storageController);
+ dictionaryController.prepare();
ankiController = new AnkiController();
ankiController.prepare();
ankiTemplatesController = new AnkiTemplatesController(ankiController);
ankiTemplatesController.prepare();
new SettingsBackup().prepare();
- storageInfoInitialize();
-
yomichan.on('optionsUpdated', onOptionsUpdated);
}
diff --git a/ext/bg/js/settings/storage.js b/ext/bg/js/settings/storage.js
index 73c93fa1..24c6d7ef 100644
--- a/ext/bg/js/settings/storage.js
+++ b/ext/bg/js/settings/storage.js
@@ -15,126 +15,117 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
-function storageBytesToLabeledString(size) {
- const base = 1000;
- const labels = [' bytes', 'KB', 'MB', 'GB'];
- let labelIndex = 0;
- while (size >= base) {
- size /= base;
- ++labelIndex;
+class StorageController {
+ constructor() {
+ this._mostRecentStorageEstimate = null;
+ this._storageEstimateFailed = false;
+ this._isUpdating = false;
}
- const label = labelIndex === 0 ? `${size}` : size.toFixed(1);
- return `${label}${labels[labelIndex]}`;
-}
-async function storageEstimate() {
- try {
- return (storageEstimate.mostRecent = await navigator.storage.estimate());
- } catch (e) {
- // NOP
+ prepare() {
+ this._preparePersistentStorage();
+ this.updateStats();
+ document.querySelector('#storage-refresh').addEventListener('click', this.updateStats.bind(this), false);
}
- return null;
-}
-storageEstimate.mostRecent = null;
-
-async function isStoragePeristent() {
- try {
- return await navigator.storage.persisted();
- } catch (e) {
- // NOP
- }
- return false;
-}
-
-async function storageInfoInitialize() {
- storagePersistInitialize();
- const {browser, platform} = await api.getEnvironmentInfo();
- document.documentElement.dataset.browser = browser;
- document.documentElement.dataset.operatingSystem = platform.os;
-
- await storageShowInfo();
- document.querySelector('#storage-refresh').addEventListener('click', storageShowInfo, false);
-}
-
-async function storageUpdateStats() {
- storageUpdateStats.isUpdating = true;
-
- const estimate = await storageEstimate();
- const valid = (estimate !== null);
-
- if (valid) {
- // Firefox reports usage as 0 when persistent storage is enabled.
- const finite = (estimate.usage > 0 || !(await isStoragePeristent()));
- if (finite) {
- document.querySelector('#storage-usage').textContent = storageBytesToLabeledString(estimate.usage);
- document.querySelector('#storage-quota').textContent = storageBytesToLabeledString(estimate.quota);
+ async updateStats() {
+ try {
+ this._isUpdating = true;
+
+ const estimate = await this._storageEstimate();
+ const valid = (estimate !== null);
+
+ if (valid) {
+ // Firefox reports usage as 0 when persistent storage is enabled.
+ const finite = (estimate.usage > 0 || !(await this._isStoragePeristent()));
+ if (finite) {
+ document.querySelector('#storage-usage').textContent = this._bytesToLabeledString(estimate.usage);
+ document.querySelector('#storage-quota').textContent = this._bytesToLabeledString(estimate.quota);
+ }
+ document.querySelector('#storage-use-finite').classList.toggle('storage-hidden', !finite);
+ document.querySelector('#storage-use-infinite').classList.toggle('storage-hidden', finite);
+ }
+
+ document.querySelector('#storage-use').classList.toggle('storage-hidden', !valid);
+ document.querySelector('#storage-error').classList.toggle('storage-hidden', valid);
+
+ return valid;
+ } finally {
+ this._isUpdating = false;
}
- document.querySelector('#storage-use-finite').classList.toggle('storage-hidden', !finite);
- document.querySelector('#storage-use-infinite').classList.toggle('storage-hidden', finite);
}
- storageUpdateStats.isUpdating = false;
- return valid;
-}
-storageUpdateStats.isUpdating = false;
-
-async function storageShowInfo() {
- storageSpinnerShow(true);
-
- const valid = await storageUpdateStats();
- document.querySelector('#storage-use').classList.toggle('storage-hidden', !valid);
- document.querySelector('#storage-error').classList.toggle('storage-hidden', valid);
+ // Private
- storageSpinnerShow(false);
-}
+ async _preparePersistentStorage() {
+ if (!(navigator.storage && navigator.storage.persist)) {
+ // Not supported
+ return;
+ }
-function storageSpinnerShow(show) {
- const spinner = $('#storage-spinner');
- if (show) {
- spinner.show();
- } else {
- spinner.hide();
+ const info = document.querySelector('#storage-persist-info');
+ const button = document.querySelector('#storage-persist-button');
+ const checkbox = document.querySelector('#storage-persist-button-checkbox');
+
+ info.classList.remove('storage-hidden');
+ button.classList.remove('storage-hidden');
+
+ let persisted = await this._isStoragePeristent();
+ checkbox.checked = persisted;
+
+ button.addEventListener('click', async () => {
+ if (persisted) {
+ return;
+ }
+ let result = false;
+ try {
+ result = await navigator.storage.persist();
+ } catch (e) {
+ // NOP
+ }
+
+ if (result) {
+ persisted = true;
+ checkbox.checked = true;
+ this.updateStats();
+ } else {
+ document.querySelector('.storage-persist-fail-warning').classList.remove('storage-hidden');
+ }
+ }, false);
}
-}
-async function storagePersistInitialize() {
- if (!(navigator.storage && navigator.storage.persist)) {
- // Not supported
- return;
+ async _storageEstimate() {
+ if (this._storageEstimateFailed && this._mostRecentStorageEstimate === null) {
+ return null;
+ }
+ try {
+ const value = await navigator.storage.estimate();
+ this._mostRecentStorageEstimate = value;
+ return value;
+ } catch (e) {
+ this._storageEstimateFailed = true;
+ }
+ return null;
}
- const info = document.querySelector('#storage-persist-info');
- const button = document.querySelector('#storage-persist-button');
- const checkbox = document.querySelector('#storage-persist-button-checkbox');
-
- info.classList.remove('storage-hidden');
- button.classList.remove('storage-hidden');
-
- let persisted = await isStoragePeristent();
- checkbox.checked = persisted;
-
- button.addEventListener('click', async () => {
- if (persisted) {
- return;
+ _bytesToLabeledString(size) {
+ const base = 1000;
+ const labels = [' bytes', 'KB', 'MB', 'GB'];
+ let labelIndex = 0;
+ while (size >= base) {
+ size /= base;
+ ++labelIndex;
}
- let result = false;
+ const label = labelIndex === 0 ? `${size}` : size.toFixed(1);
+ return `${label}${labels[labelIndex]}`;
+ }
+
+ async _isStoragePeristent() {
try {
- result = await navigator.storage.persist();
+ return await navigator.storage.persisted();
} catch (e) {
// NOP
}
-
- if (result) {
- persisted = true;
- checkbox.checked = true;
- storageShowInfo();
- } else {
- $('.storage-persist-fail-warning').removeClass('storage-hidden');
- }
- }, false);
+ return false;
+ }
}
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index 5c7fde41..4856b0b4 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -711,7 +711,6 @@
<div id="storage-info">
<div>
- <img src="/mixed/img/spinner.gif" class="pull-right" id="storage-spinner" />
<h3>Storage</h3>
</div>