aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/data/schemas/options-schema.json9
-rw-r--r--ext/js/background/backend.js20
-rw-r--r--ext/js/data/options-util.js16
-rw-r--r--ext/js/display/element-overflow-controller.js4
-rw-r--r--ext/js/pages/action-popup-main.js12
-rw-r--r--ext/js/pages/settings/backup-controller.js4
-rw-r--r--ext/js/pages/settings/collapsible-dictionary-controller.js21
-rw-r--r--ext/js/pages/settings/dictionary-controller.js114
-rw-r--r--ext/js/pages/settings/dictionary-import-controller.js15
-rw-r--r--ext/js/pages/settings/secondary-search-dictionary-controller.js14
-rw-r--r--test/test-options-util.js9
11 files changed, 158 insertions, 80 deletions
diff --git a/ext/data/schemas/options-schema.json b/ext/data/schemas/options-schema.json
index d829b392..ad6fb869 100644
--- a/ext/data/schemas/options-schema.json
+++ b/ext/data/schemas/options-schema.json
@@ -726,16 +726,21 @@
}
},
"dictionaries": {
- "type": "object",
- "additionalProperties": {
+ "type": "array",
+ "items": {
"type": "object",
"required": [
+ "name",
"priority",
"enabled",
"allowSecondarySearches",
"definitionsCollapsible"
],
"properties": {
+ "name": {
+ "type": "string",
+ "default": ""
+ },
"priority": {
"type": "number",
"default": 0
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js
index 0fd083bf..cf8137e5 100644
--- a/ext/js/background/backend.js
+++ b/ext/js/background/backend.js
@@ -1271,6 +1271,17 @@ class Backend {
if (!Array.isArray(array)) { throw new Error('Invalid target type'); }
return array.splice(start, deleteCount, ...items);
}
+ case 'push':
+ {
+ const {path, items} = target;
+ if (typeof path !== 'string') { throw new Error('Invalid path'); }
+ if (!Array.isArray(items)) { throw new Error('Invalid items'); }
+ const array = accessor.get(ObjectPropertyAccessor.getPathArray(path));
+ if (!Array.isArray(array)) { throw new Error('Invalid target type'); }
+ const start = array.length;
+ array.push(...items);
+ return start;
+ }
default:
throw new Error(`Unknown action: ${action}`);
}
@@ -1358,7 +1369,7 @@ class Backend {
}
_isAnyDictionaryEnabled(options) {
- for (const {enabled} of Object.values(options.dictionaries)) {
+ for (const {enabled} of options.dictionaries) {
if (enabled) {
return true;
}
@@ -1900,12 +1911,9 @@ class Backend {
_getTranslatorEnabledDictionaryMap(options) {
const enabledDictionaryMap = new Map();
- const {dictionaries} = options;
- for (const title in dictionaries) {
- if (!Object.prototype.hasOwnProperty.call(dictionaries, title)) { continue; }
- const dictionary = dictionaries[title];
+ for (const dictionary of options.dictionaries) {
if (!dictionary.enabled) { continue; }
- enabledDictionaryMap.set(title, {
+ enabledDictionaryMap.set(dictionary.name, {
index: enabledDictionaryMap.size,
priority: dictionary.priority,
allowSecondarySearches: dictionary.allowSecondarySearches
diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js
index ca122e89..857ef630 100644
--- a/ext/js/data/options-util.js
+++ b/ext/js/data/options-util.js
@@ -459,7 +459,8 @@ class OptionsUtil {
{async: false, update: this._updateVersion7.bind(this)},
{async: true, update: this._updateVersion8.bind(this)},
{async: false, update: this._updateVersion9.bind(this)},
- {async: true, update: this._updateVersion10.bind(this)}
+ {async: true, update: this._updateVersion10.bind(this)},
+ {async: true, update: this._updateVersion11.bind(this)}
];
}
@@ -786,4 +787,17 @@ class OptionsUtil {
}
return options;
}
+
+ _updateVersion11(options) {
+ // Version 11 changes:
+ // Changed dictionaries to an array.
+ for (const profile of options.profiles) {
+ const dictionariesNew = [];
+ for (const [name, {priority, enabled, allowSecondarySearches, definitionsCollapsible}] of Object.entries(profile.options.dictionaries)) {
+ dictionariesNew.push({name, priority, enabled, allowSecondarySearches, definitionsCollapsible});
+ }
+ profile.options.dictionaries = dictionariesNew;
+ }
+ return options;
+ }
}
diff --git a/ext/js/display/element-overflow-controller.js b/ext/js/display/element-overflow-controller.js
index ccea1417..abbf3332 100644
--- a/ext/js/display/element-overflow-controller.js
+++ b/ext/js/display/element-overflow-controller.js
@@ -29,7 +29,7 @@ class ElementOverflowController {
setOptions(options) {
this._dictionaries.clear();
- for (const [dictionary, {definitionsCollapsible}] of Object.entries(options.dictionaries)) {
+ for (const {name, definitionsCollapsible} of options.dictionaries) {
let collapsible = false;
let collapsed = false;
switch (definitionsCollapsible) {
@@ -42,7 +42,7 @@ class ElementOverflowController {
break;
}
if (!collapsible) { continue; }
- this._dictionaries.set(dictionary, {collapsed});
+ this._dictionaries.set(name, {collapsed});
}
}
diff --git a/ext/js/pages/action-popup-main.js b/ext/js/pages/action-popup-main.js
index 2f18e028..2de986da 100644
--- a/ext/js/pages/action-popup-main.js
+++ b/ext/js/pages/action-popup-main.js
@@ -191,12 +191,16 @@ class DisplayController {
const noDictionariesEnabledWarnings = document.querySelectorAll('.no-dictionaries-enabled-warning');
const dictionaries = await yomichan.api.getDictionaryInfo();
+ const enabledDictionaries = new Set();
+ for (const {name, enabled} of options.dictionaries) {
+ if (enabled) {
+ enabledDictionaries.add(name);
+ }
+ }
+
let enabledCount = 0;
for (const {title} of dictionaries) {
- if (
- Object.prototype.hasOwnProperty.call(options.dictionaries, title) &&
- options.dictionaries[title].enabled
- ) {
+ if (enabledDictionaries.has(title)) {
++enabledCount;
}
}
diff --git a/ext/js/pages/settings/backup-controller.js b/ext/js/pages/settings/backup-controller.js
index 9b80f0b4..c961d40e 100644
--- a/ext/js/pages/settings/backup-controller.js
+++ b/ext/js/pages/settings/backup-controller.js
@@ -368,7 +368,7 @@ class BackupController {
}
// Update dictionaries
- await DictionaryController.ensureDictionarySettings(this._settingsController, void 0, optionsFull, true, false);
+ await DictionaryController.ensureDictionarySettings(this._settingsController, void 0, optionsFull, false, false);
// Assign options
await this._settingsImportSetOptionsFull(optionsFull);
@@ -404,7 +404,7 @@ class BackupController {
const optionsFull = this._optionsUtil.getDefault();
// Update dictionaries
- await DictionaryController.ensureDictionarySettings(this._settingsController, void 0, optionsFull, true, false);
+ await DictionaryController.ensureDictionarySettings(this._settingsController, void 0, optionsFull, false, false);
// Assign options
try {
diff --git a/ext/js/pages/settings/collapsible-dictionary-controller.js b/ext/js/pages/settings/collapsible-dictionary-controller.js
index 37793c6f..6eb3a9b4 100644
--- a/ext/js/pages/settings/collapsible-dictionary-controller.js
+++ b/ext/js/pages/settings/collapsible-dictionary-controller.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * ObjectPropertyAccessor
- */
-
class CollapsibleDictionaryController {
constructor(settingsController) {
this._settingsController = settingsController;
@@ -65,12 +61,14 @@ class CollapsibleDictionaryController {
this._setupAllSelect(fragment, options);
- for (const dictionary of Object.keys(options.dictionaries)) {
- const dictionaryInfo = this._dictionaryInfoMap.get(dictionary);
+ const {dictionaries} = options;
+ for (let i = 0, ii = dictionaries.length; i < ii; ++i) {
+ const {name} = dictionaries[i];
+ const dictionaryInfo = this._dictionaryInfoMap.get(name);
if (typeof dictionaryInfo === 'undefined') { continue; }
- const select = this._addSelect(fragment, dictionary, `rev.${dictionaryInfo.revision}`);
- select.dataset.setting = ObjectPropertyAccessor.getPathString(['dictionaries', dictionary, 'definitionsCollapsible']);
+ const select = this._addSelect(fragment, name, `rev.${dictionaryInfo.revision}`);
+ select.dataset.setting = `dictionaries[${i}].definitionsCollapsible`;
this._eventListeners.addEventListener(select, 'settingChanged', this._onDefinitionsCollapsibleChange.bind(this), false);
this._selects.push(select);
@@ -125,7 +123,7 @@ class CollapsibleDictionaryController {
_updateAllSelect(options) {
let value = null;
let varies = false;
- for (const {definitionsCollapsible} of Object.values(options.dictionaries)) {
+ for (const {definitionsCollapsible} of options.dictionaries) {
if (value === null) {
value = definitionsCollapsible;
} else if (value !== definitionsCollapsible) {
@@ -140,8 +138,9 @@ class CollapsibleDictionaryController {
async _setDefinitionsCollapsibleAll(value) {
const options = await this._settingsController.getOptions();
const targets = [];
- for (const dictionary of Object.keys(options.dictionaries)) {
- const path = ObjectPropertyAccessor.getPathString(['dictionaries', dictionary, 'definitionsCollapsible']);
+ const {dictionaries} = options;
+ for (let i = 0, ii = dictionaries.length; i < ii; ++i) {
+ const path = `dictionaries[${i}].definitionsCollapsible`;
targets.push({action: 'set', path, value});
}
await this._settingsController.modifyProfileSettings(targets);
diff --git a/ext/js/pages/settings/dictionary-controller.js b/ext/js/pages/settings/dictionary-controller.js
index 7862b207..23a47f9a 100644
--- a/ext/js/pages/settings/dictionary-controller.js
+++ b/ext/js/pages/settings/dictionary-controller.js
@@ -17,13 +17,13 @@
/* global
* DictionaryDatabase
- * ObjectPropertyAccessor
*/
class DictionaryEntry {
- constructor(dictionaryController, node, dictionaryInfo) {
+ constructor(dictionaryController, node, index, dictionaryInfo) {
this._dictionaryController = dictionaryController;
this._node = node;
+ this._index = index;
this._dictionaryInfo = dictionaryInfo;
this._eventListeners = new EventListenerCollection();
this._detailsContainer = null;
@@ -41,6 +41,7 @@ class DictionaryEntry {
prepare() {
const node = this._node;
+ const index = this._index;
const {title, revision, prefixWildcardsSupported, version} = this._dictionaryInfo;
this._detailsContainer = node.querySelector('.dictionary-details');
@@ -72,14 +73,14 @@ class DictionaryEntry {
detailsToggleLink.hidden = !hasDetails;
}
if (enabledCheckbox !== null) {
- enabledCheckbox.dataset.setting = ObjectPropertyAccessor.getPathString(['dictionaries', title, 'enabled']);
+ enabledCheckbox.dataset.setting = `dictionaries[${index}].enabled`;
this._eventListeners.addEventListener(enabledCheckbox, 'settingChanged', this._onEnabledChanged.bind(this), false);
}
if (priorityInput !== null) {
- priorityInput.dataset.setting = ObjectPropertyAccessor.getPathString(['dictionaries', title, 'priority']);
+ priorityInput.dataset.setting = `dictionaries[${index}].priority`;
}
if (allowSecondarySearchesCheckbox !== null) {
- allowSecondarySearchesCheckbox.dataset.setting = ObjectPropertyAccessor.getPathString(['dictionaries', title, 'allowSecondarySearches']);
+ allowSecondarySearchesCheckbox.dataset.setting = `dictionaries[${index}].allowSecondarySearches`;
}
if (deleteButton !== null) {
this._eventListeners.addEventListener(deleteButton, 'click', this._onDeleteButtonClicked.bind(this), false);
@@ -248,8 +249,9 @@ class DictionaryController {
this._updateDictionariesEnabledWarnings(options);
}
- static createDefaultDictionarySettings(enabled) {
+ static createDefaultDictionarySettings(name, enabled) {
return {
+ name,
priority: 0,
enabled,
allowSecondarySearches: false,
@@ -257,7 +259,7 @@ class DictionaryController {
};
}
- static async ensureDictionarySettings(settingsController, dictionaries, optionsFull, modifyOptionsFull, newDictionariesEnabled) {
+ static async ensureDictionarySettings(settingsController, dictionaries, optionsFull, modifyGlobalSettings, newDictionariesEnabled) {
if (typeof dictionaries === 'undefined') {
dictionaries = await settingsController.getDictionaryInfo();
}
@@ -265,24 +267,43 @@ class DictionaryController {
optionsFull = await settingsController.getOptionsFull();
}
- const targets = [];
- const {profiles} = optionsFull;
+ const installedDictionaries = new Set();
for (const {title} of dictionaries) {
- for (let i = 0, ii = profiles.length; i < ii; ++i) {
- const {options: {dictionaries: dictionaryOptions}} = profiles[i];
- if (Object.prototype.hasOwnProperty.call(dictionaryOptions, title)) { continue; }
+ installedDictionaries.add(title);
+ }
- const value = DictionaryController.createDefaultDictionarySettings(newDictionariesEnabled);
- if (modifyOptionsFull) {
- dictionaryOptions[title] = value;
+ const targets = [];
+ const {profiles} = optionsFull;
+ for (let i = 0, ii = profiles.length; i < ii; ++i) {
+ let modified = false;
+ const missingDictionaries = new Set([...installedDictionaries]);
+ const dictionaryOptionsArray = profiles[i].options.dictionaries;
+ for (let j = dictionaryOptionsArray.length - 1; j >= 0; --j) {
+ const {name} = dictionaryOptionsArray[j];
+ if (installedDictionaries.has(name)) {
+ missingDictionaries.delete(name);
} else {
- const path = ObjectPropertyAccessor.getPathString(['profiles', i, 'options', 'dictionaries', title]);
- targets.push({action: 'set', path, value});
+ dictionaryOptionsArray.splice(j, 1);
+ modified = true;
}
}
+
+ for (const name of missingDictionaries) {
+ const value = DictionaryController.createDefaultDictionarySettings(name, newDictionariesEnabled);
+ dictionaryOptionsArray.push(value);
+ modified = true;
+ }
+
+ if (modified) {
+ targets.push({
+ action: 'set',
+ path: `profiles[${i}].options.dictionaries`,
+ value: dictionaryOptionsArray
+ });
+ }
}
- if (!modifyOptionsFull && targets.length > 0) {
+ if (modifyGlobalSettings && targets.length > 0) {
await settingsController.modifyGlobalSettings(targets);
}
}
@@ -291,6 +312,9 @@ class DictionaryController {
_onOptionsChanged({options}) {
this._updateDictionariesEnabledWarnings(options);
+ if (this._dictionaries !== null) {
+ this._updateEntries();
+ }
}
async _onDatabaseUpdated() {
@@ -298,10 +322,14 @@ class DictionaryController {
this._databaseStateToken = token;
this._dictionaries = null;
const dictionaries = await this._settingsController.getDictionaryInfo();
- const options = await this._settingsController.getOptions();
if (this._databaseStateToken !== token) { return; }
this._dictionaries = dictionaries;
+ await this._updateEntries();
+ }
+
+ async _updateEntries() {
+ const dictionaries = this._dictionaries;
this._updateMainDictionarySelectOptions(dictionaries);
for (const entry of this._dictionaryEntries) {
@@ -318,23 +346,38 @@ class DictionaryController {
node.hidden = hasDictionary;
}
+ await DictionaryController.ensureDictionarySettings(this._settingsController, dictionaries, void 0, true, false);
+
+ const options = await this._settingsController.getOptions();
this._updateDictionariesEnabledWarnings(options);
- await DictionaryController.ensureDictionarySettings(this._settingsController, dictionaries, void 0, false, false);
- for (const dictionary of dictionaries) {
- this._createDictionaryEntry(dictionary);
+ const dictionaryInfoMap = new Map();
+ for (const dictionary of this._dictionaries) {
+ dictionaryInfoMap.set(dictionary.title, dictionary);
+ }
+
+ const dictionaryOptionsArray = options.dictionaries;
+ for (let i = 0, ii = dictionaryOptionsArray.length; i < ii; ++i) {
+ const {name} = dictionaryOptionsArray[i];
+ const dictionaryInfo = dictionaryInfoMap.get(name);
+ if (typeof dictionaryInfo === 'undefined') { continue; }
+ this._createDictionaryEntry(i, dictionaryInfo);
}
}
_updateDictionariesEnabledWarnings(options) {
let enabledCount = 0;
if (this._dictionaries !== null) {
+ const enabledDictionaries = new Set();
+ for (const {name, enabled} of options.dictionaries) {
+ if (enabled) {
+ enabledDictionaries.add(name);
+ }
+ }
+
for (const {title} of this._dictionaries) {
- if (Object.prototype.hasOwnProperty.call(options.dictionaries, title)) {
- const {enabled} = options.dictionaries[title];
- if (enabled) {
- ++enabledCount;
- }
+ if (enabledDictionaries.has(title)) {
+ ++enabledCount;
}
}
}
@@ -459,11 +502,11 @@ class DictionaryController {
parent.removeChild(node);
}
- _createDictionaryEntry(dictionary) {
+ _createDictionaryEntry(index, dictionaryInfo) {
const node = this.instantiateTemplate('dictionary');
this._dictionaryEntryContainer.appendChild(node);
- const entry = new DictionaryEntry(this, node, dictionary);
+ const entry = new DictionaryEntry(this, node, index, dictionaryInfo);
this._dictionaryEntries.push(entry);
entry.prepare();
}
@@ -553,9 +596,16 @@ class DictionaryController {
const targets = [];
for (let i = 0, ii = profiles.length; i < ii; ++i) {
const {options: {dictionaries}} = profiles[i];
- if (Object.prototype.hasOwnProperty.call(dictionaries, dictionaryTitle)) {
- const path = ObjectPropertyAccessor.getPathString(['profiles', i, 'options', 'dictionaries', dictionaryTitle]);
- targets.push({action: 'delete', path});
+ for (let j = 0, jj = dictionaries.length; j < jj; ++j) {
+ if (dictionaries[j].name !== dictionaryTitle) { continue; }
+ const path = `profiles[${i}].options.dictionaries`;
+ targets.push({
+ action: 'splice',
+ path,
+ start: j,
+ deleteCount: 1,
+ items: []
+ });
}
}
await this._settingsController.modifyGlobalSettings(targets);
diff --git a/ext/js/pages/settings/dictionary-import-controller.js b/ext/js/pages/settings/dictionary-import-controller.js
index afa45899..c7b72110 100644
--- a/ext/js/pages/settings/dictionary-import-controller.js
+++ b/ext/js/pages/settings/dictionary-import-controller.js
@@ -19,7 +19,6 @@
* DictionaryController
* DictionaryDatabase
* DictionaryImporter
- * ObjectPropertyAccessor
*/
class DictionaryImportController {
@@ -213,12 +212,12 @@ class DictionaryImportController {
const profileCount = optionsFull.profiles.length;
for (let i = 0; i < profileCount; ++i) {
const {options} = optionsFull.profiles[i];
- const value = DictionaryController.createDefaultDictionarySettings(true);
- const path1 = ObjectPropertyAccessor.getPathString(['profiles', i, 'options', 'dictionaries', title]);
- targets.push({action: 'set', path: path1, value});
+ const value = DictionaryController.createDefaultDictionarySettings(title, true);
+ const path1 = `profiles[${i}].options.dictionaries`;
+ targets.push({action: 'push', path: path1, items: [value]});
if (sequenced && options.general.mainDictionary === '') {
- const path2 = ObjectPropertyAccessor.getPathString(['profiles', i, 'options', 'general', 'mainDictionary']);
+ const path2 = `profiles[${i}].options.general.mainDictionary`;
targets.push({action: 'set', path: path2, value: title});
}
}
@@ -230,9 +229,9 @@ class DictionaryImportController {
const targets = [];
const profileCount = optionsFull.profiles.length;
for (let i = 0; i < profileCount; ++i) {
- const path1 = ObjectPropertyAccessor.getPathString(['profiles', i, 'options', 'dictionaries']);
- targets.push({action: 'set', path: path1, value: {}});
- const path2 = ObjectPropertyAccessor.getPathString(['profiles', i, 'options', 'general', 'mainDictionary']);
+ const path1 = `profiles[${i}].options.dictionaries`;
+ targets.push({action: 'set', path: path1, value: []});
+ const path2 = `profiles[${i}].options.general.mainDictionary`;
targets.push({action: 'set', path: path2, value: ''});
}
return await this._modifyGlobalSettings(targets);
diff --git a/ext/js/pages/settings/secondary-search-dictionary-controller.js b/ext/js/pages/settings/secondary-search-dictionary-controller.js
index 13e1dcf5..8ffd9a9b 100644
--- a/ext/js/pages/settings/secondary-search-dictionary-controller.js
+++ b/ext/js/pages/settings/secondary-search-dictionary-controller.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * ObjectPropertyAccessor
- */
-
class SecondarySearchDictionaryController {
constructor(settingsController) {
this._settingsController = settingsController;
@@ -60,21 +56,23 @@ class SecondarySearchDictionaryController {
const fragment = document.createDocumentFragment();
- for (const dictionary of Object.keys(options.dictionaries)) {
- const dictionaryInfo = this._dictionaryInfoMap.get(dictionary);
+ const {dictionaries} = options;
+ for (let i = 0, ii = dictionaries.length; i < ii; ++i) {
+ const {name} = dictionaries[i];
+ const dictionaryInfo = this._dictionaryInfoMap.get(name);
if (typeof dictionaryInfo === 'undefined') { continue; }
const node = this._settingsController.instantiateTemplate('secondary-search-dictionary');
fragment.appendChild(node);
const nameNode = node.querySelector('.dictionary-title');
- nameNode.textContent = dictionary;
+ nameNode.textContent = name;
const versionNode = node.querySelector('.dictionary-version');
versionNode.textContent = `rev.${dictionaryInfo.revision}`;
const toggle = node.querySelector('.dictionary-allow-secondary-searches');
- toggle.dataset.setting = ObjectPropertyAccessor.getPathString(['dictionaries', dictionary, 'allowSecondarySearches']);
+ toggle.dataset.setting = `dictionaries[${i}].allowSecondarySearches`;
this._eventListeners.addEventListener(toggle, 'settingChanged', this._onEnabledChanged.bind(this, node), false);
}
diff --git a/test/test-options-util.js b/test/test-options-util.js
index ce12bca9..cd948c91 100644
--- a/test/test-options-util.js
+++ b/test/test-options-util.js
@@ -407,14 +407,15 @@ function createProfileOptionsUpdatedTestData1() {
groups: []
}
},
- dictionaries: {
- 'Test Dictionary': {
+ dictionaries: [
+ {
+ name: 'Test Dictionary',
priority: 0,
enabled: true,
allowSecondarySearches: false,
definitionsCollapsible: 'not-collapsible'
}
- },
+ ],
parsing: {
enableScanningParser: true,
enableMecabParser: false,
@@ -574,7 +575,7 @@ function createOptionsUpdatedTestData1() {
}
],
profileCurrent: 0,
- version: 10,
+ version: 11,
global: {
database: {
prefixWildcardsSupported: false