From 44638b7ceb8ec4e2a235ad4ffc9aa23ec66f21d2 Mon Sep 17 00:00:00 2001
From: toasted-nutbread
Date: Tue, 29 Oct 2019 16:31:27 -0400
Subject: Simplify how option visibility is changed
---
ext/bg/css/settings.css | 11 +++++++++--
ext/bg/js/settings.js | 30 +++++-------------------------
ext/bg/settings.html | 2 +-
3 files changed, 15 insertions(+), 28 deletions(-)
diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css
index b3d5b884..1036622d 100644
--- a/ext/bg/css/settings.css
+++ b/ext/bg/css/settings.css
@@ -17,9 +17,16 @@
*/
-#anki-spinner, #anki-general, #anki-error,
+#anki-spinner, #anki-error,
#dict-spinner, #dict-error, #dict-warning, #dict-purge, #dict-import-progress,
-#debug, .options-advanced, .storage-hidden, #storage-spinner {
+.storage-hidden, #storage-spinner {
+ display: none;
+}
+
+html:root:not([data-options-anki-enable=true]) #anki-general,
+html:root:not([data-options-general-debug-info=true]) .debug,
+html:root:not([data-options-general-show-advanced=true]) .options-advanced,
+html:root:not([data-options-general-result-output-mode=merge]) #dict-main-group {
display: none;
}
diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js
index 05a0604a..f2250911 100644
--- a/ext/bg/js/settings.js
+++ b/ext/bg/js/settings.js
@@ -171,36 +171,16 @@ function formSetupEventListeners() {
}
function formUpdateVisibility(options) {
- const general = $('#anki-general');
- if (options.anki.enable) {
- general.show();
- } else {
- general.hide();
- }
-
- const advanced = $('.options-advanced');
- if (options.general.showAdvanced) {
- advanced.show();
- } else {
- advanced.hide();
- }
+ document.documentElement.dataset.optionsAnkiEnable = `${!!options.anki.enable}`;
+ document.documentElement.dataset.optionsGeneralDebugInfo = `${!!options.general.debugInfo}`;
+ document.documentElement.dataset.optionsGeneralShowAdvanced = `${!!options.general.showAdvanced}`;
+ document.documentElement.dataset.optionsGeneralResultOutputMode = `${options.general.resultOutputMode}`;
- const mainGroup = $('#dict-main-group');
- if (options.general.resultOutputMode === 'merge') {
- mainGroup.show();
- } else {
- mainGroup.hide();
- }
-
- const debug = $('#debug');
if (options.general.debugInfo) {
const temp = utilIsolate(options);
temp.anki.fieldTemplates = '...';
const text = JSON.stringify(temp, null, 4);
- debug.html(handlebarsEscape(text));
- debug.show();
- } else {
- debug.hide();
+ $('#debug').text(text);
}
}
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index a3b75576..b281501d 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -673,7 +673,7 @@
-
+
• Search • Homepage • Legal
--
cgit v1.2.3
From 79069d59085e4fa50599052381b8f2c0d22270aa Mon Sep 17 00:00:00 2001
From: toasted-nutbread
Date: Thu, 31 Oct 2019 21:10:56 -0400
Subject: Add functions for getting dictionary information
---
ext/bg/js/database.js | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js
index 9f477b24..fc0af049 100644
--- a/ext/bg/js/database.js
+++ b/ext/bg/js/database.js
@@ -202,6 +202,60 @@ class Database {
return this.db.dictionaries.toArray();
}
+ async getDictionaryInfo() {
+ this.validate();
+
+ const results = [];
+ const db = this.db.backendDB();
+ const dbTransaction = db.transaction(['dictionaries'], 'readonly');
+ const dbDictionaries = dbTransaction.objectStore('dictionaries');
+
+ await Database.getAll(dbDictionaries, null, null, info => results.push(info));
+
+ return results;
+ }
+
+ async getDictionaryCounts(dictionaryNames, getTotal) {
+ this.validate();
+
+ const objectStoreNames = [
+ 'kanji',
+ 'kanjiMeta',
+ 'terms',
+ 'termMeta',
+ 'tagMeta'
+ ];
+ const db = this.db.backendDB();
+ const dbCountTransaction = db.transaction(objectStoreNames, 'readonly');
+
+ const targets = [];
+ for (const objectStoreName of objectStoreNames) {
+ targets.push([
+ objectStoreName,
+ dbCountTransaction.objectStore(objectStoreName).index('dictionary')
+ ]);
+ }
+
+ const totalPromise = getTotal ? Database.getCounts(targets, null) : null;
+
+ const counts = [];
+ const countPromises = [];
+ for (let i = 0; i < dictionaryNames.length; ++i) {
+ counts.push(null);
+ const index = i;
+ const only = IDBKeyRange.only(dictionaryNames[i]);
+ const countPromise = Database.getCounts(targets, only).then(v => counts[index] = v);
+ countPromises.push(countPromise);
+ }
+ await Promise.all(countPromises);
+
+ const result = {counts};
+ if (totalPromise !== null) {
+ result.total = await totalPromise;
+ }
+ return result;
+ }
+
async importDictionary(archive, progressCallback, exceptions) {
this.validate();
@@ -539,4 +593,23 @@ class Database {
};
});
}
+
+ static getCounts(targets, query) {
+ const countPromises = [];
+ const counts = {};
+ for (const [objectStoreName, index] of targets) {
+ const n = objectStoreName;
+ const countPromise = Database.getCount(index, query).then(count => counts[n] = count);
+ countPromises.push(countPromise);
+ }
+ return Promise.all(countPromises).then(() => counts);
+ }
+
+ static getCount(dbIndex, query) {
+ return new Promise((resolve, reject) => {
+ const request = dbIndex.count(query);
+ request.onerror = (e) => reject(e);
+ request.onsuccess = (e) => resolve(e.target.result);
+ });
+ }
}
--
cgit v1.2.3
From 2ab871e7ee93ddc4a2f1ca41aad10e4b189b6c0f Mon Sep 17 00:00:00 2001
From: toasted-nutbread
Date: Sat, 2 Nov 2019 14:06:16 -0400
Subject: Update how dictionaries are displayed on the settings page
---
ext/bg/js/settings-dictionaries.js | 482 +++++++++++++++++++++++++++++++++++++
ext/bg/js/settings.js | 261 +-------------------
ext/bg/js/templates.js | 27 ---
ext/bg/js/util.js | 8 +
ext/bg/settings.html | 30 ++-
tmpl/dictionary.html | 17 --
6 files changed, 520 insertions(+), 305 deletions(-)
create mode 100644 ext/bg/js/settings-dictionaries.js
delete mode 100644 tmpl/dictionary.html
diff --git a/ext/bg/js/settings-dictionaries.js b/ext/bg/js/settings-dictionaries.js
new file mode 100644
index 00000000..635b95c6
--- /dev/null
+++ b/ext/bg/js/settings-dictionaries.js
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2019 Alex Yatskov
+ * Author: Alex Yatskov
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+let dictionaryUI = null;
+
+
+class SettingsDictionaryListUI {
+ constructor(container, template, extraContainer, extraTemplate, optionsDictionaries) {
+ this.container = container;
+ this.template = template;
+ this.extraContainer = extraContainer;
+ this.extraTemplate = extraTemplate;
+ this.optionsDictionaries = optionsDictionaries;
+
+ this.dictionaryEntries = [];
+ this.extra = null;
+ }
+
+ setDictionaries(dictionaries) {
+ for (const dictionaryEntry of this.dictionaryEntries) {
+ dictionaryEntry.cleanup();
+ }
+
+ this.dictionaryEntries = [];
+
+ let changed = false;
+ for (const dictionaryInfo of toIterable(dictionaries)) {
+ if (this.createEntry(dictionaryInfo)) {
+ changed = true;
+ }
+ }
+
+ const titles = this.dictionaryEntries.map(e => e.dictionaryInfo.title);
+ const removeKeys = Object.keys(this.optionsDictionaries).filter(key => titles.indexOf(key) < 0);
+ if (removeKeys.length >= 0) {
+ for (const key of removeKeys) {
+ delete this.optionsDictionaries[key];
+ }
+ changed = true;
+ }
+
+ if (changed) {
+ this.save();
+ }
+ }
+
+ createEntry(dictionaryInfo) {
+ const title = dictionaryInfo.title;
+ let changed = false;
+ let optionsDictionary;
+ const optionsDictionaries = this.optionsDictionaries;
+ if (optionsDictionaries.hasOwnProperty(title)) {
+ optionsDictionary = optionsDictionaries[title];
+ } else {
+ optionsDictionary = SettingsDictionaryListUI.createDictionaryOptions();
+ optionsDictionaries[title] = optionsDictionary;
+ changed = true;
+ }
+
+ const content = document.importNode(this.template.content, true).firstChild;
+ this.container.appendChild(content);
+
+ this.dictionaryEntries.push(new SettingsDictionaryEntryUI(this, dictionaryInfo, content, optionsDictionary));
+
+ return changed;
+ }
+
+ static createDictionaryOptions() {
+ return utilBackgroundIsolate({
+ priority: 0,
+ enabled: false,
+ allowSecondarySearches: false
+ });
+ }
+
+ createExtra(totalCounts, remainders, totalRemainder) {
+ const content = document.importNode(this.extraTemplate.content, true).firstChild;
+ this.extraContainer.appendChild(content);
+ return new SettingsDictionaryExtraUI(this, totalCounts, remainders, totalRemainder, content);
+ }
+
+ setCounts(dictionaryCounts, totalCounts) {
+ const remainders = Object.assign({}, totalCounts);
+ const keys = Object.keys(remainders);
+
+ for (let i = 0, ii = Math.min(this.dictionaryEntries.length, dictionaryCounts.length); i < ii; ++i) {
+ const counts = dictionaryCounts[i];
+ this.dictionaryEntries[i].setCounts(counts);
+
+ for (const key of keys) {
+ remainders[key] -= counts[key];
+ }
+ }
+
+ let totalRemainder = 0;
+ for (const key of keys) {
+ totalRemainder += remainders[key];
+ }
+
+ if (this.extra !== null) {
+ this.extra.cleanup();
+ this.extra = null;
+ }
+
+ if (totalRemainder > 0) {
+ this.extra = this.createExtra(totalCounts, remainders, totalRemainder);
+ }
+ }
+
+ save() {
+ // Overwrite
+ }
+}
+
+class SettingsDictionaryEntryUI {
+ constructor(parent, dictionaryInfo, content, optionsDictionary) {
+ this.parent = parent;
+ this.dictionaryInfo = dictionaryInfo;
+ this.optionsDictionary = optionsDictionary;
+ this.counts = null;
+ this.eventListeners = [];
+
+ this.content = content;
+ this.enabledCheckbox = this.content.querySelector('.dict-enabled');
+ this.allowSecondarySearchesCheckbox = this.content.querySelector('.dict-allow-secondary-searches');
+ this.priorityInput = this.content.querySelector('.dict-priority');
+
+ this.content.querySelector('.dict-title').textContent = this.dictionaryInfo.title;
+ this.content.querySelector('.dict-revision').textContent = `rev.${this.dictionaryInfo.revision}`;
+
+ 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);
+ }
+
+ cleanup() {
+ if (this.content !== null) {
+ if (this.content.parentNode !== null) {
+ this.content.parentNode.removeChild(this.content);
+ }
+ this.content = null;
+ }
+ this.dictionaryInfo = null;
+ this.clearEventListeners();
+ }
+
+ setCounts(counts) {
+ this.counts = counts;
+ const node = this.content.querySelector('.dict-counts');
+ node.textContent = JSON.stringify({
+ info: this.dictionaryInfo,
+ counts
+ }, null, 4);
+ node.removeAttribute('hidden');
+ }
+
+ save() {
+ 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;
+ this.priorityInput.value = `${this.optionsDictionary.priority}`;
+ }
+
+ onEnabledChanged(e) {
+ this.optionsDictionary.enabled = !!e.target.checked;
+ this.save();
+ }
+
+ onAllowSecondarySearchesChanged(e) {
+ this.optionsDictionary.allowSecondarySearches = !!e.target.checked;
+ this.save();
+ }
+
+ onPriorityChanged(e) {
+ let value = Number.parseFloat(e.target.value);
+ if (Number.isNaN(value)) {
+ value = this.optionsDictionary.priority;
+ } else {
+ this.optionsDictionary.priority = value;
+ this.save();
+ }
+
+ e.target.value = `${value}`;
+ }
+}
+
+class SettingsDictionaryExtraUI {
+ constructor(parent, totalCounts, remainders, totalRemainder, content) {
+ this.parent = parent;
+ this.content = content;
+
+ this.content.querySelector('.dict-total-count').textContent = `${totalRemainder} item${totalRemainder !== 1 ? 's' : ''}`;
+
+ const node = this.content.querySelector('.dict-counts');
+ node.textContent = JSON.stringify({
+ counts: totalCounts,
+ remainders: remainders
+ }, null, 4);
+ node.removeAttribute('hidden');
+ }
+
+ cleanup() {
+ if (this.content !== null) {
+ if (this.content.parentNode !== null) {
+ this.content.parentNode.removeChild(this.content);
+ }
+ this.content = null;
+ }
+ }
+}
+
+
+async function dictSettingsInitialize() {
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+
+ dictionaryUI = new SettingsDictionaryListUI(
+ document.querySelector('#dict-groups'),
+ document.querySelector('#dict-template'),
+ document.querySelector('#dict-groups-extra'),
+ document.querySelector('#dict-extra-template'),
+ options.dictionaries
+ );
+ dictionaryUI.save = () => apiOptionsSave();
+
+ document.querySelector('#dict-purge-link').addEventListener('click', (e) => onDictionaryPurge(e), false);
+ document.querySelector('#dict-file-button').addEventListener('click', (e) => onDictionaryImportButtonClick(e), false);
+ document.querySelector('#dict-file').addEventListener('change', (e) => onDictionaryImport(e), false);
+ document.querySelector('#dict-main').addEventListener('change', (e) => onDictionaryMainChanged(e), false);
+
+ onDatabaseUpdated(options);
+}
+
+async function onDatabaseUpdated(options) {
+ try {
+ const dictionaries = await utilDatabaseGetDictionaryInfo();
+ dictionaryUI.setDictionaries(dictionaries);
+
+ updateMainDictionarySelect(options, dictionaries);
+
+ const {counts, total} = await utilDatabaseGetDictionaryCounts(dictionaries.map(v => v.title), true);
+ dictionaryUI.setCounts(counts, total);
+ } catch (e) {
+ dictionaryErrorsShow([e]);
+ }
+}
+
+async function updateMainDictionarySelect(options, 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);
+
+ let value = '';
+ const currentValue = options.general.mainDictionary;
+ for (const {title, sequenced} of toIterable(dictionaries)) {
+ if (!sequenced) { continue; }
+
+ option = document.createElement('option');
+ option.value = title;
+ option.textContent = title;
+ select.appendChild(option);
+
+ if (title === currentValue) {
+ value = title;
+ }
+ }
+
+ select.value = value;
+
+ if (options.general.mainDictionary !== value) {
+ options.general.mainDictionary = value;
+ apiOptionsSave();
+ }
+}
+
+async function onDictionaryMainChanged(e) {
+ const value = e.target.value;
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+ options.general.mainDictionary = value;
+ apiOptionsSave();
+}
+
+
+function dictionaryErrorToString(error) {
+ if (error.toString) {
+ error = error.toString();
+ } else {
+ error = `${error}`;
+ }
+
+ for (const [match, subst] of dictionaryErrorToString.overrides) {
+ if (error.includes(match)) {
+ error = subst;
+ break;
+ }
+ }
+
+ 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 = $('#dict-error');
+ dialog.show().text('');
+
+ if (errors !== null && errors.length > 0) {
+ const uniqueErrors = {};
+ for (let e of errors) {
+ console.error(e);
+ e = dictionaryErrorToString(e);
+ uniqueErrors[e] = uniqueErrors.hasOwnProperty(e) ? uniqueErrors[e] + 1 : 1;
+ }
+
+ for (const e in uniqueErrors) {
+ const count = uniqueErrors[e];
+ 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.append($(div));
+ }
+
+ dialog.show();
+ } else {
+ dialog.hide();
+ }
+}
+
+
+function dictionarySpinnerShow(show) {
+ const spinner = $('#dict-spinner');
+ if (show) {
+ spinner.show();
+ } else {
+ spinner.hide();
+ }
+}
+
+function onDictionaryImportButtonClick() {
+ const dictFile = document.querySelector('#dict-file');
+ dictFile.click();
+}
+
+async function onDictionaryPurge(e) {
+ e.preventDefault();
+
+ const dictControls = $('#dict-importer, #dict-groups, #dict-groups-extra, #dict-main-group').hide();
+ const dictProgress = $('#dict-purge').show();
+
+ try {
+ dictionaryErrorsShow(null);
+ dictionarySpinnerShow(true);
+
+ await utilDatabasePurge();
+ for (const options of toIterable(await getOptionsArray())) {
+ options.dictionaries = utilBackgroundIsolate({});
+ options.general.mainDictionary = '';
+ }
+ await settingsSaveOptions();
+
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+ onDatabaseUpdated(options);
+ } catch (err) {
+ dictionaryErrorsShow([err]);
+ } finally {
+ dictionarySpinnerShow(false);
+
+ dictControls.show();
+ dictProgress.hide();
+
+ if (storageEstimate.mostRecent !== null) {
+ storageUpdateStats();
+ }
+ }
+}
+
+async function onDictionaryImport(e) {
+ const dictFile = $('#dict-file');
+ const dictControls = $('#dict-importer').hide();
+ const dictProgress = $('#dict-import-progress').show();
+
+ try {
+ dictionaryErrorsShow(null);
+ 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();
+ }
+ };
+ setProgress(0.0);
+
+ const exceptions = [];
+ const summary = await utilDatabaseImport(e.target.files[0], updateProgress, exceptions);
+ for (const options of toIterable(await getOptionsArray())) {
+ const dictionaryOptions = SettingsDictionaryListUI.createDictionaryOptions();
+ dictionaryOptions.enabled = true;
+ options.dictionaries[summary.title] = dictionaryOptions;
+ if (summary.sequenced && options.general.mainDictionary === '') {
+ options.general.mainDictionary = summary.title;
+ }
+ }
+ await settingsSaveOptions();
+
+ if (exceptions.length > 0) {
+ exceptions.push(`Dictionary may not have been imported properly: ${exceptions.length} error${exceptions.length === 1 ? '' : 's'} reported.`);
+ dictionaryErrorsShow(exceptions);
+ }
+
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+ onDatabaseUpdated(options);
+ } catch (err) {
+ dictionaryErrorsShow([err]);
+ } finally {
+ dictionarySpinnerShow(false);
+
+ dictFile.val('');
+ dictControls.show();
+ dictProgress.hide();
+ }
+}
diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js
index f2250911..e4446851 100644
--- a/ext/bg/js/settings.js
+++ b/ext/bg/js/settings.js
@@ -81,16 +81,6 @@ async function formRead(options) {
options.anki.kanji.model = $('#anki-kanji-model').val();
options.anki.kanji.fields = utilBackgroundIsolate(ankiFieldsToDict($('#kanji .anki-field-value')));
}
-
- options.general.mainDictionary = $('#dict-main').val();
- $('.dict-group').each((index, element) => {
- const dictionary = $(element);
- options.dictionaries[dictionary.data('title')] = utilBackgroundIsolate({
- priority: parseInt(dictionary.find('.dict-priority').val(), 10),
- enabled: dictionary.find('.dict-enabled').prop('checked'),
- allowSecondarySearches: dictionary.find('.dict-allow-secondary-searches').prop('checked')
- });
- });
}
async function formWrite(options) {
@@ -144,13 +134,6 @@ async function formWrite(options) {
$('#screenshot-quality').val(options.anki.screenshot.quality);
$('#field-templates').val(options.anki.fieldTemplates);
- try {
- await dictionaryGroupsPopulate(options);
- await formMainDictionaryOptionsPopulate(options);
- } catch (e) {
- dictionaryErrorsShow([e]);
- }
-
try {
await ankiDeckAndModelPopulate(options);
} catch (e) {
@@ -161,10 +144,6 @@ async function formWrite(options) {
}
function formSetupEventListeners() {
- $('#dict-purge-link').click(utilAsync(onDictionaryPurge));
- $('#dict-file').change(utilAsync(onDictionaryImport));
- $('#dict-file-button').click(onDictionaryImportButtonClick);
-
$('#field-templates-reset').click(utilAsync(onAnkiFieldTemplatesReset));
$('input, select, textarea').not('.anki-model').not('.ignore-form-changes *').change(utilAsync(onFormOptionsChanged));
$('.anki-model').change(utilAsync(onAnkiModelChanged));
@@ -184,23 +163,6 @@ function formUpdateVisibility(options) {
}
}
-async function formMainDictionaryOptionsPopulate(options) {
- const select = $('#dict-main').empty();
- select.append($(''));
-
- let mainDictionary = '';
- for (const dictRow of toIterable(await utilDatabaseSummarize())) {
- if (dictRow.sequenced) {
- select.append($(``));
- if (dictRow.title === options.general.mainDictionary) {
- mainDictionary = dictRow.title;
- }
- }
- }
-
- select.val(mainDictionary);
-}
-
async function onFormOptionsChanged(e) {
if (!e.originalEvent && !e.isTrigger) {
return;
@@ -239,6 +201,7 @@ async function onReady() {
appearanceInitialize();
await audioSettingsInitialize();
await profileOptionsSetup();
+ await dictSettingsInitialize();
storageInfoInitialize();
@@ -422,228 +385,6 @@ function onMessage({action, params}, sender, callback) {
}
-/*
- * Dictionary
- */
-
-function dictionaryErrorToString(error) {
- if (error.toString) {
- error = error.toString();
- } else {
- error = `${error}`;
- }
-
- for (const [match, subst] of dictionaryErrorToString.overrides) {
- if (error.includes(match)) {
- error = subst;
- break;
- }
- }
-
- 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 = $('#dict-error');
- dialog.show().text('');
-
- if (errors !== null && errors.length > 0) {
- const uniqueErrors = {};
- for (let e of errors) {
- e = dictionaryErrorToString(e);
- uniqueErrors[e] = uniqueErrors.hasOwnProperty(e) ? uniqueErrors[e] + 1 : 1;
- }
-
- for (const e in uniqueErrors) {
- const count = uniqueErrors[e];
- 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.append($(div));
- }
-
- dialog.show();
- } else {
- dialog.hide();
- }
-}
-
-function dictionarySpinnerShow(show) {
- const spinner = $('#dict-spinner');
- if (show) {
- spinner.show();
- } else {
- spinner.hide();
- }
-}
-
-function dictionaryGroupsSort() {
- const dictGroups = $('#dict-groups');
- const dictGroupChildren = dictGroups.children('.dict-group').sort((ca, cb) => {
- const pa = parseInt($(ca).find('.dict-priority').val(), 10);
- const pb = parseInt($(cb).find('.dict-priority').val(), 10);
- if (pa < pb) {
- return 1;
- } else if (pa > pb) {
- return -1;
- } else {
- return 0;
- }
- });
-
- dictGroups.append(dictGroupChildren);
-}
-
-async function dictionaryGroupsPopulate(options) {
- const dictGroups = $('#dict-groups').empty();
- const dictWarning = $('#dict-warning').hide();
-
- const dictRows = toIterable(await utilDatabaseSummarize());
- if (dictRows.length === 0) {
- dictWarning.show();
- }
-
- for (const dictRow of toIterable(dictRowsSort(dictRows, options))) {
- const dictOptions = options.dictionaries[dictRow.title] || {
- enabled: false,
- priority: 0,
- allowSecondarySearches: false
- };
-
- const dictHtml = await apiTemplateRender('dictionary.html', {
- enabled: dictOptions.enabled,
- priority: dictOptions.priority,
- allowSecondarySearches: dictOptions.allowSecondarySearches,
- title: dictRow.title,
- version: dictRow.version,
- revision: dictRow.revision,
- outdated: dictRow.version < 3
- });
-
- dictGroups.append($(dictHtml));
- }
-
- formUpdateVisibility(options);
-
- $('.dict-enabled, .dict-priority, .dict-allow-secondary-searches').change(e => {
- dictionaryGroupsSort();
- onFormOptionsChanged(e);
- });
-}
-
-async function onDictionaryPurge(e) {
- e.preventDefault();
-
- const dictControls = $('#dict-importer, #dict-groups, #dict-main-group').hide();
- const dictProgress = $('#dict-purge').show();
-
- try {
- dictionaryErrorsShow(null);
- dictionarySpinnerShow(true);
-
- await utilDatabasePurge();
- for (const options of toIterable(await getOptionsArray())) {
- options.dictionaries = utilBackgroundIsolate({});
- options.general.mainDictionary = '';
- }
- await settingsSaveOptions();
-
- const optionsContext = getOptionsContext();
- const options = await apiOptionsGet(optionsContext);
- await dictionaryGroupsPopulate(options);
- await formMainDictionaryOptionsPopulate(options);
- } catch (e) {
- dictionaryErrorsShow([e]);
- } finally {
- dictionarySpinnerShow(false);
-
- dictControls.show();
- dictProgress.hide();
-
- if (storageEstimate.mostRecent !== null) {
- storageUpdateStats();
- }
- }
-}
-
-function onDictionaryImportButtonClick() {
- const dictFile = document.querySelector('#dict-file');
- dictFile.click();
-}
-
-async function onDictionaryImport(e) {
- const dictFile = $('#dict-file');
- const dictControls = $('#dict-importer').hide();
- const dictProgress = $('#dict-import-progress').show();
-
- try {
- dictionaryErrorsShow(null);
- 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();
- }
- };
- setProgress(0.0);
-
- const exceptions = [];
- const summary = await utilDatabaseImport(e.target.files[0], updateProgress, exceptions);
- for (const options of toIterable(await getOptionsArray())) {
- options.dictionaries[summary.title] = utilBackgroundIsolate({
- enabled: true,
- priority: 0,
- allowSecondarySearches: false
- });
- if (summary.sequenced && options.general.mainDictionary === '') {
- options.general.mainDictionary = summary.title;
- }
- }
- await settingsSaveOptions();
-
- if (exceptions.length > 0) {
- exceptions.push(`Dictionary may not have been imported properly: ${exceptions.length} error${exceptions.length === 1 ? '' : 's'} reported.`);
- dictionaryErrorsShow(exceptions);
- }
-
- const optionsContext = getOptionsContext();
- const options = await apiOptionsGet(optionsContext);
- await dictionaryGroupsPopulate(options);
- await formMainDictionaryOptionsPopulate(options);
- } catch (e) {
- dictionaryErrorsShow([e]);
- } finally {
- dictionarySpinnerShow(false);
-
- dictFile.val('');
- dictControls.show();
- dictProgress.hide();
- }
-}
-
-
/*
* Anki
*/
diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js
index 59516d97..26d1575f 100644
--- a/ext/bg/js/templates.js
+++ b/ext/bg/js/templates.js
@@ -1,32 +1,5 @@
(function() {
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
-templates['dictionary.html'] = template({"1":function(container,depth0,helpers,partials,data) {
- return " This dictionary is outdated and may not support new extension features; please import the latest version.
\n";
-},"3":function(container,depth0,helpers,partials,data) {
- return "checked";
-},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
-
- return "\n
"
- + alias4(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"title","hash":{},"data":data}) : helper)))
- + " rev."
- + alias4(((helper = (helper = helpers.revision || (depth0 != null ? depth0.revision : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"revision","hash":{},"data":data}) : helper)))
- + "
\n"
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.outdated : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + "\n
\n \n
\n
\n \n
\n
\n \n \n
\n
\n";
-},"useData":true});
templates['kanji.html'] = template({"1":function(container,depth0,helpers,partials,data) {
var stack1;
diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js
index 1ca0833b..3554ec3d 100644
--- a/ext/bg/js/util.js
+++ b/ext/bg/js/util.js
@@ -84,6 +84,14 @@ function utilDatabaseSummarize() {
return utilBackend().translator.database.summarize();
}
+function utilDatabaseGetDictionaryInfo() {
+ return utilBackend().translator.database.getDictionaryInfo();
+}
+
+function utilDatabaseGetDictionaryCounts(dictionaryNames, getTotal) {
+ return utilBackend().translator.database.getDictionaryCounts(dictionaryNames, getTotal);
+}
+
function utilAnkiGetModelFieldNames(modelName) {
return utilBackend().anki.getModelFieldNames(modelName);
}
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index b281501d..7ac196f2 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -410,7 +410,7 @@
-