diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js
index 9839aef5..81772d08 100644
--- a/ext/bg/js/api.js
+++ b/ext/bg/js/api.js
@@ -17,16 +17,19 @@
*/
-function apiOptionsGetSync() {
- return utilBackend().options;
+function apiOptionsGet(optionsContext) {
+ return utilBackend().getOptions(optionsContext);
}
-async function apiOptionsGet() {
- return apiOptionsGetSync();
+async function apiOptionsSave(source) {
+ const backend = utilBackend();
+ const options = await backend.getFullOptions();
+ await optionsSave(options);
+ backend.onOptionsUpdated(source);
}
-async function apiTermsFind(text) {
- const options = apiOptionsGetSync();
+async function apiTermsFind(text, optionsContext) {
+ const options = await apiOptionsGet(optionsContext);
const translator = utilBackend().translator;
const searcher = {
@@ -38,7 +41,8 @@ async function apiTermsFind(text) {
const {definitions, length} = await searcher(
text,
dictEnabledSet(options),
- options.scanning.alphanumeric
+ options.scanning.alphanumeric,
+ options
);
return {
@@ -47,14 +51,14 @@ async function apiTermsFind(text) {
};
}
-async function apiKanjiFind(text) {
- const options = apiOptionsGetSync();
+async function apiKanjiFind(text, optionsContext) {
+ const options = await apiOptionsGet(optionsContext);
const definitions = await utilBackend().translator.findKanji(text, dictEnabledSet(options));
return definitions.slice(0, options.general.maxResults);
}
-async function apiDefinitionAdd(definition, mode, context) {
- const options = apiOptionsGetSync();
+async function apiDefinitionAdd(definition, mode, context, optionsContext) {
+ const options = await apiOptionsGet(optionsContext);
if (mode !== 'kanji') {
await audioInject(
@@ -76,14 +80,15 @@ async function apiDefinitionAdd(definition, mode, context) {
return utilBackend().anki.addNote(note);
}
-async function apiDefinitionsAddable(definitions, modes) {
+async function apiDefinitionsAddable(definitions, modes, optionsContext) {
+ const options = await apiOptionsGet(optionsContext);
const states = [];
try {
const notes = [];
for (const definition of definitions) {
for (const mode of modes) {
- const note = await dictNoteFormat(definition, mode, apiOptionsGetSync());
+ const note = await dictNoteFormat(definition, mode, options);
notes.push(note);
}
}
@@ -131,9 +136,10 @@ async function apiCommandExec(command) {
},
toggle: async () => {
- const options = apiOptionsGetSync();
+ const optionsContext = {depth: 0};
+ const options = await apiOptionsGet(optionsContext);
options.general.enable = !options.general.enable;
- await optionsSave(options);
+ await apiOptionsSave('popup');
}
};
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index b3e737da..9a300d62 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -22,44 +22,43 @@ class Backend {
this.translator = new Translator();
this.anki = new AnkiNull();
this.options = null;
+ this.optionsContext = {
+ depth: 0
+ };
+
+ this.isPreparedResolve = null;
+ this.isPreparedPromise = new Promise((resolve) => (this.isPreparedResolve = resolve));
this.apiForwarder = new BackendApiForwarder();
}
async prepare() {
await this.translator.prepare();
- this.onOptionsUpdated(await optionsLoad());
+ this.options = await optionsLoad();
+ this.onOptionsUpdated('background');
if (chrome.commands !== null && typeof chrome.commands === 'object') {
chrome.commands.onCommand.addListener(this.onCommand.bind(this));
}
chrome.runtime.onMessage.addListener(this.onMessage.bind(this));
- if (this.options.general.showGuide) {
+ const options = this.getOptionsSync(this.optionsContext);
+ if (options.general.showGuide) {
chrome.tabs.create({url: chrome.extension.getURL('/bg/guide.html')});
}
+
+ this.isPreparedResolve();
+ this.isPreparedResolve = null;
+ this.isPreparedPromise = null;
}
- onOptionsUpdated(options) {
- options = utilIsolate(options);
- this.options = options;
-
- if (!options.general.enable) {
- this.setExtensionBadgeBackgroundColor('#555555');
- this.setExtensionBadgeText('off');
- } else if (!dictConfigured(options)) {
- this.setExtensionBadgeBackgroundColor('#f0ad4e');
- this.setExtensionBadgeText('!');
- } else {
- this.setExtensionBadgeText('');
- }
-
- this.anki = options.anki.enable ? new AnkiConnect(options.anki.server) : new AnkiNull();
+ onOptionsUpdated(source) {
+ this.applyOptions();
const callback = () => this.checkLastError(chrome.runtime.lastError);
chrome.tabs.query({}, tabs => {
for (const tab of tabs) {
- chrome.tabs.sendMessage(tab.id, {action: 'optionsSet', params: {options}}, callback);
+ chrome.tabs.sendMessage(tab.id, {action: 'optionsUpdate', params: {source}}, callback);
}
});
}
@@ -78,24 +77,24 @@ class Backend {
};
const handlers = {
- optionsGet: ({callback}) => {
- forward(apiOptionsGet(), callback);
+ optionsGet: ({optionsContext, callback}) => {
+ forward(apiOptionsGet(optionsContext), callback);
},
- kanjiFind: ({text, callback}) => {
- forward(apiKanjiFind(text), callback);
+ kanjiFind: ({text, optionsContext, callback}) => {
+ forward(apiKanjiFind(text, optionsContext), callback);
},
- termsFind: ({text, callback}) => {
- forward(apiTermsFind(text), callback);
+ termsFind: ({text, optionsContext, callback}) => {
+ forward(apiTermsFind(text, optionsContext), callback);
},
- definitionAdd: ({definition, mode, context, callback}) => {
- forward(apiDefinitionAdd(definition, mode, context), callback);
+ definitionAdd: ({definition, mode, context, optionsContext, callback}) => {
+ forward(apiDefinitionAdd(definition, mode, context, optionsContext), callback);
},
- definitionsAddable: ({definitions, modes, callback}) => {
- forward(apiDefinitionsAddable(definitions, modes), callback);
+ definitionsAddable: ({definitions, modes, optionsContext, callback}) => {
+ forward(apiDefinitionsAddable(definitions, modes, optionsContext), callback);
},
noteView: ({noteId}) => {
@@ -136,6 +135,39 @@ class Backend {
return true;
}
+ applyOptions() {
+ const options = this.getOptionsSync(this.optionsContext);
+ if (!options.general.enable) {
+ this.setExtensionBadgeBackgroundColor('#555555');
+ this.setExtensionBadgeText('off');
+ } else if (!dictConfigured(options)) {
+ this.setExtensionBadgeBackgroundColor('#f0ad4e');
+ this.setExtensionBadgeText('!');
+ } else {
+ this.setExtensionBadgeText('');
+ }
+
+ this.anki = options.anki.enable ? new AnkiConnect(options.anki.server) : new AnkiNull();
+ }
+
+ async getFullOptions() {
+ if (this.isPreparedPromise !== null) {
+ await this.isPreparedPromise;
+ }
+ return this.options;
+ }
+
+ async getOptions(optionsContext) {
+ if (this.isPreparedPromise !== null) {
+ await this.isPreparedPromise;
+ }
+ return this.getOptionsSync(optionsContext);
+ }
+
+ getOptionsSync(optionsContext) {
+ return this.options;
+ }
+
setExtensionBadgeBackgroundColor(color) {
if (typeof chrome.browserAction.setBadgeBackgroundColor === 'function') {
chrome.browserAction.setBadgeBackgroundColor({color});
diff --git a/ext/bg/js/context.js b/ext/bg/js/context.js
index 689d6863..dfa224a7 100644
--- a/ext/bg/js/context.js
+++ b/ext/bg/js/context.js
@@ -22,7 +22,8 @@ $(document).ready(utilAsync(() => {
$('#open-options').click(() => apiCommandExec('options'));
$('#open-help').click(() => apiCommandExec('help'));
- optionsLoad().then(options => {
+ const optionsContext = {depth: 0};
+ apiOptionsGet(optionsContext).then(options => {
const toggle = $('#enable-search');
toggle.prop('checked', options.general.enable).change();
toggle.bootstrapToggle();
diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js
index 69c662e6..d093d0b4 100644
--- a/ext/bg/js/options.js
+++ b/ext/bg/js/options.js
@@ -330,7 +330,7 @@ function optionsLoad() {
}).then(optionsStr => {
if (typeof optionsStr === 'string') {
const options = JSON.parse(optionsStr);
- if (typeof options === 'object' && options !== null && !Array.isArray(options)) {
+ if (utilIsObject(options)) {
return options;
}
}
@@ -343,9 +343,14 @@ function optionsLoad() {
}
function optionsSave(options) {
- return new Promise((resolve) => {
- chrome.storage.local.set({options: JSON.stringify(options)}, resolve);
- }).then(() => {
- utilBackend().onOptionsUpdated(options);
+ return new Promise((resolve, reject) => {
+ chrome.storage.local.set({options: JSON.stringify(options)}, () => {
+ const error = chrome.runtime.lastError;
+ if (error) {
+ reject(error);
+ } else {
+ resolve();
+ }
+ });
});
}
diff --git a/ext/bg/js/search-frontend.js b/ext/bg/js/search-frontend.js
index 840a1ea8..df5ccf81 100644
--- a/ext/bg/js/search-frontend.js
+++ b/ext/bg/js/search-frontend.js
@@ -18,7 +18,8 @@
async function searchFrontendSetup() {
- const options = await apiOptionsGet();
+ const optionsContext = {depth: 0};
+ const options = await apiOptionsGet(optionsContext);
if (!options.scanning.enableOnSearchPage) { return; }
const scriptSrcs = [
diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js
index a3382398..6bdc47d8 100644
--- a/ext/bg/js/search.js
+++ b/ext/bg/js/search.js
@@ -21,6 +21,10 @@ class DisplaySearch extends Display {
constructor() {
super($('#spinner'), $('#content'));
+ this.optionsContext = {
+ depth: 0
+ };
+
this.search = $('#search').click(this.onSearch.bind(this));
this.query = $('#query').on('input', this.onSearchInput.bind(this));
this.intro = $('#intro');
@@ -46,8 +50,8 @@ class DisplaySearch extends Display {
try {
e.preventDefault();
this.intro.slideUp();
- const {length, definitions} = await apiTermsFind(this.query.val());
- super.termsShow(definitions, await apiOptionsGet());
+ const {length, definitions} = await apiTermsFind(this.query.val(), this.optionsContext);
+ super.termsShow(definitions, await apiOptionsGet(this.optionsContext));
} catch (e) {
this.onError(e);
}
diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js
index 83f4528c..7f3e5c69 100644
--- a/ext/bg/js/settings.js
+++ b/ext/bg/js/settings.js
@@ -16,73 +16,144 @@
* along with this program. If not, see .
*/
+function getOptionsContext() {
+ return {
+ depth: 0
+ };
+}
-async function formRead() {
- const optionsOld = await optionsLoad();
- const optionsNew = $.extend(true, {}, optionsOld);
+async function formRead(options) {
+ options.general.enable = $('#enable').prop('checked');
+ options.general.showGuide = $('#show-usage-guide').prop('checked');
+ options.general.compactTags = $('#compact-tags').prop('checked');
+ options.general.compactGlossaries = $('#compact-glossaries').prop('checked');
+ options.general.autoPlayAudio = $('#auto-play-audio').prop('checked');
+ options.general.resultOutputMode = $('#result-output-mode').val();
+ options.general.audioSource = $('#audio-playback-source').val();
+ options.general.audioVolume = parseFloat($('#audio-playback-volume').val());
+ options.general.debugInfo = $('#show-debug-info').prop('checked');
+ options.general.showAdvanced = $('#show-advanced-options').prop('checked');
+ options.general.maxResults = parseInt($('#max-displayed-results').val(), 10);
+ options.general.popupDisplayMode = $('#popup-display-mode').val();
+ options.general.popupHorizontalTextPosition = $('#popup-horizontal-text-position').val();
+ options.general.popupVerticalTextPosition = $('#popup-vertical-text-position').val();
+ options.general.popupWidth = parseInt($('#popup-width').val(), 10);
+ options.general.popupHeight = parseInt($('#popup-height').val(), 10);
+ options.general.popupHorizontalOffset = parseInt($('#popup-horizontal-offset').val(), 0);
+ 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.customPopupCss = $('#custom-popup-css').val();
- optionsNew.general.showGuide = $('#show-usage-guide').prop('checked');
- optionsNew.general.compactTags = $('#compact-tags').prop('checked');
- optionsNew.general.compactGlossaries = $('#compact-glossaries').prop('checked');
- optionsNew.general.autoPlayAudio = $('#auto-play-audio').prop('checked');
- optionsNew.general.resultOutputMode = $('#result-output-mode').val();
- optionsNew.general.audioSource = $('#audio-playback-source').val();
- optionsNew.general.audioVolume = parseFloat($('#audio-playback-volume').val());
- optionsNew.general.debugInfo = $('#show-debug-info').prop('checked');
- optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked');
- optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10);
- optionsNew.general.popupDisplayMode = $('#popup-display-mode').val();
- optionsNew.general.popupHorizontalTextPosition = $('#popup-horizontal-text-position').val();
- optionsNew.general.popupVerticalTextPosition = $('#popup-vertical-text-position').val();
- optionsNew.general.popupWidth = parseInt($('#popup-width').val(), 10);
- optionsNew.general.popupHeight = parseInt($('#popup-height').val(), 10);
- optionsNew.general.popupHorizontalOffset = parseInt($('#popup-horizontal-offset').val(), 0);
- optionsNew.general.popupVerticalOffset = parseInt($('#popup-vertical-offset').val(), 10);
- optionsNew.general.popupHorizontalOffset2 = parseInt($('#popup-horizontal-offset2').val(), 0);
- optionsNew.general.popupVerticalOffset2 = parseInt($('#popup-vertical-offset2').val(), 10);
- optionsNew.general.customPopupCss = $('#custom-popup-css').val();
+ options.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked');
+ options.scanning.touchInputEnabled = $('#touch-input-enabled').prop('checked');
+ options.scanning.selectText = $('#select-matched-text').prop('checked');
+ options.scanning.alphanumeric = $('#search-alphanumeric').prop('checked');
+ options.scanning.autoHideResults = $('#auto-hide-results').prop('checked');
+ options.scanning.deepDomScan = $('#deep-dom-scan').prop('checked');
+ options.scanning.enableOnPopupExpressions = $('#enable-scanning-of-popup-expressions').prop('checked');
+ options.scanning.enableOnSearchPage = $('#enable-scanning-on-search-page').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();
+ options.scanning.popupNestingMaxDepth = parseInt($('#popup-nesting-max-depth').val(), 10);
- optionsNew.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked');
- optionsNew.scanning.touchInputEnabled = $('#touch-input-enabled').prop('checked');
- optionsNew.scanning.selectText = $('#select-matched-text').prop('checked');
- optionsNew.scanning.alphanumeric = $('#search-alphanumeric').prop('checked');
- optionsNew.scanning.autoHideResults = $('#auto-hide-results').prop('checked');
- optionsNew.scanning.deepDomScan = $('#deep-dom-scan').prop('checked');
- optionsNew.scanning.enableOnPopupExpressions = $('#enable-scanning-of-popup-expressions').prop('checked');
- optionsNew.scanning.enableOnSearchPage = $('#enable-scanning-on-search-page').prop('checked');
- optionsNew.scanning.delay = parseInt($('#scan-delay').val(), 10);
- optionsNew.scanning.length = parseInt($('#scan-length').val(), 10);
- optionsNew.scanning.modifier = $('#scan-modifier-key').val();
- optionsNew.scanning.popupNestingMaxDepth = parseInt($('#popup-nesting-max-depth').val(), 10);
+ const optionsAnkiEnableOld = options.anki.enable;
+ options.anki.enable = $('#anki-enable').prop('checked');
+ options.anki.tags = $('#card-tags').val().split(/[,; ]+/);
+ options.anki.sentenceExt = parseInt($('#sentence-detection-extent').val(), 10);
+ options.anki.server = $('#interface-server').val();
+ options.anki.screenshot.format = $('#screenshot-format').val();
+ options.anki.screenshot.quality = parseInt($('#screenshot-quality').val(), 10);
+ options.anki.fieldTemplates = $('#field-templates').val();
- optionsNew.anki.enable = $('#anki-enable').prop('checked');
- optionsNew.anki.tags = $('#card-tags').val().split(/[,; ]+/);
- optionsNew.anki.sentenceExt = parseInt($('#sentence-detection-extent').val(), 10);
- optionsNew.anki.server = $('#interface-server').val();
- optionsNew.anki.screenshot.format = $('#screenshot-format').val();
- optionsNew.anki.screenshot.quality = parseInt($('#screenshot-quality').val(), 10);
- optionsNew.anki.fieldTemplates = $('#field-templates').val();
-
- if (optionsOld.anki.enable && !ankiErrorShown()) {
- optionsNew.anki.terms.deck = $('#anki-terms-deck').val();
- optionsNew.anki.terms.model = $('#anki-terms-model').val();
- optionsNew.anki.terms.fields = ankiFieldsToDict($('#terms .anki-field-value'));
- optionsNew.anki.kanji.deck = $('#anki-kanji-deck').val();
- optionsNew.anki.kanji.model = $('#anki-kanji-model').val();
- optionsNew.anki.kanji.fields = ankiFieldsToDict($('#kanji .anki-field-value'));
+ if (optionsAnkiEnableOld && !ankiErrorShown()) {
+ options.anki.terms.deck = $('#anki-terms-deck').val();
+ options.anki.terms.model = $('#anki-terms-model').val();
+ options.anki.terms.fields = ankiFieldsToDict($('#terms .anki-field-value'));
+ options.anki.kanji.deck = $('#anki-kanji-deck').val();
+ options.anki.kanji.model = $('#anki-kanji-model').val();
+ options.anki.kanji.fields = ankiFieldsToDict($('#kanji .anki-field-value'));
}
- optionsNew.general.mainDictionary = $('#dict-main').val();
+ options.general.mainDictionary = $('#dict-main').val();
$('.dict-group').each((index, element) => {
const dictionary = $(element);
- optionsNew.dictionaries[dictionary.data('title')] = {
+ options.dictionaries[dictionary.data('title')] = {
priority: parseInt(dictionary.find('.dict-priority').val(), 10),
enabled: dictionary.find('.dict-enabled').prop('checked'),
allowSecondarySearches: dictionary.find('.dict-allow-secondary-searches').prop('checked')
};
});
+}
- return {optionsNew, optionsOld};
+async function formWrite(options) {
+ $('#enable').prop('checked', options.general.enable);
+ $('#show-usage-guide').prop('checked', options.general.showGuide);
+ $('#compact-tags').prop('checked', options.general.compactTags);
+ $('#compact-glossaries').prop('checked', options.general.compactGlossaries);
+ $('#auto-play-audio').prop('checked', options.general.autoPlayAudio);
+ $('#result-output-mode').val(options.general.resultOutputMode);
+ $('#audio-playback-source').val(options.general.audioSource);
+ $('#audio-playback-volume').val(options.general.audioVolume);
+ $('#show-debug-info').prop('checked', options.general.debugInfo);
+ $('#show-advanced-options').prop('checked', options.general.showAdvanced);
+ $('#max-displayed-results').val(options.general.maxResults);
+ $('#popup-display-mode').val(options.general.popupDisplayMode);
+ $('#popup-horizontal-text-position').val(options.general.popupHorizontalTextPosition);
+ $('#popup-vertical-text-position').val(options.general.popupVerticalTextPosition);
+ $('#popup-width').val(options.general.popupWidth);
+ $('#popup-height').val(options.general.popupHeight);
+ $('#popup-horizontal-offset').val(options.general.popupHorizontalOffset);
+ $('#popup-vertical-offset').val(options.general.popupVerticalOffset);
+ $('#popup-horizontal-offset2').val(options.general.popupHorizontalOffset2);
+ $('#popup-vertical-offset2').val(options.general.popupVerticalOffset2);
+ $('#custom-popup-css').val(options.general.customPopupCss);
+
+ $('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse);
+ $('#touch-input-enabled').prop('checked', options.scanning.touchInputEnabled);
+ $('#select-matched-text').prop('checked', options.scanning.selectText);
+ $('#search-alphanumeric').prop('checked', options.scanning.alphanumeric);
+ $('#auto-hide-results').prop('checked', options.scanning.autoHideResults);
+ $('#deep-dom-scan').prop('checked', options.scanning.deepDomScan);
+ $('#enable-scanning-of-popup-expressions').prop('checked', options.scanning.enableOnPopupExpressions);
+ $('#enable-scanning-on-search-page').prop('checked', options.scanning.enableOnSearchPage);
+ $('#scan-delay').val(options.scanning.delay);
+ $('#scan-length').val(options.scanning.length);
+ $('#scan-modifier-key').val(options.scanning.modifier);
+ $('#popup-nesting-max-depth').val(options.scanning.popupNestingMaxDepth);
+
+ $('#anki-enable').prop('checked', options.anki.enable);
+ $('#card-tags').val(options.anki.tags.join(' '));
+ $('#sentence-detection-extent').val(options.anki.sentenceExt);
+ $('#interface-server').val(options.anki.server);
+ $('#screenshot-format').val(options.anki.screenshot.format);
+ $('#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) {
+ ankiErrorShow(e);
+ }
+
+ formUpdateVisibility(options);
+}
+
+function formSetupEventListeners() {
+ $('#dict-purge-link').click(utilAsync(onDictionaryPurge));
+ $('#dict-file').change(utilAsync(onDictionaryImport));
+
+ $('#field-templates-reset').click(utilAsync(onAnkiFieldTemplatesReset));
+ $('input, select, textarea').not('.anki-model').not('.profile-form *').change(utilAsync(onFormOptionsChanged));
+ $('.anki-model').change(utilAsync(onAnkiModelChanged));
}
function formUpdateVisibility(options) {
@@ -141,18 +212,23 @@ async function onFormOptionsChanged(e) {
return;
}
- const {optionsNew, optionsOld} = await formRead();
- await optionsSave(optionsNew);
- formUpdateVisibility(optionsNew);
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+ const optionsAnkiEnableOld = options.anki.enable;
+ const optionsAnkiServerOld = options.anki.server;
+
+ await formRead(options);
+ await settingsSaveOptions();
+ formUpdateVisibility(options);
try {
const ankiUpdated =
- optionsNew.anki.enable !== optionsOld.anki.enable ||
- optionsNew.anki.server !== optionsOld.anki.server;
+ options.anki.enable !== optionsAnkiEnableOld ||
+ options.anki.server !== optionsAnkiServerOld;
if (ankiUpdated) {
ankiSpinnerShow(true);
- await ankiDeckAndModelPopulate(optionsNew);
+ await ankiDeckAndModelPopulate(options);
ankiErrorShow();
}
} catch (e) {
@@ -163,77 +239,51 @@ async function onFormOptionsChanged(e) {
}
async function onReady() {
- const options = await optionsLoad();
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
- $('#show-usage-guide').prop('checked', options.general.showGuide);
- $('#compact-tags').prop('checked', options.general.compactTags);
- $('#compact-glossaries').prop('checked', options.general.compactGlossaries);
- $('#auto-play-audio').prop('checked', options.general.autoPlayAudio);
- $('#result-output-mode').val(options.general.resultOutputMode);
- $('#audio-playback-source').val(options.general.audioSource);
- $('#audio-playback-volume').val(options.general.audioVolume);
- $('#show-debug-info').prop('checked', options.general.debugInfo);
- $('#show-advanced-options').prop('checked', options.general.showAdvanced);
- $('#max-displayed-results').val(options.general.maxResults);
- $('#popup-display-mode').val(options.general.popupDisplayMode);
- $('#popup-horizontal-text-position').val(options.general.popupHorizontalTextPosition);
- $('#popup-vertical-text-position').val(options.general.popupVerticalTextPosition);
- $('#popup-width').val(options.general.popupWidth);
- $('#popup-height').val(options.general.popupHeight);
- $('#popup-horizontal-offset').val(options.general.popupHorizontalOffset);
- $('#popup-vertical-offset').val(options.general.popupVerticalOffset);
- $('#popup-horizontal-offset2').val(options.general.popupHorizontalOffset2);
- $('#popup-vertical-offset2').val(options.general.popupVerticalOffset2);
- $('#custom-popup-css').val(options.general.customPopupCss);
-
- $('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse);
- $('#touch-input-enabled').prop('checked', options.scanning.touchInputEnabled);
- $('#select-matched-text').prop('checked', options.scanning.selectText);
- $('#search-alphanumeric').prop('checked', options.scanning.alphanumeric);
- $('#auto-hide-results').prop('checked', options.scanning.autoHideResults);
- $('#deep-dom-scan').prop('checked', options.scanning.deepDomScan);
- $('#enable-scanning-of-popup-expressions').prop('checked', options.scanning.enableOnPopupExpressions);
- $('#enable-scanning-on-search-page').prop('checked', options.scanning.enableOnSearchPage);
- $('#scan-delay').val(options.scanning.delay);
- $('#scan-length').val(options.scanning.length);
- $('#scan-modifier-key').val(options.scanning.modifier);
- $('#popup-nesting-max-depth').val(options.scanning.popupNestingMaxDepth);
-
- $('#dict-purge-link').click(utilAsync(onDictionaryPurge));
- $('#dict-file').change(utilAsync(onDictionaryImport));
-
- $('#anki-enable').prop('checked', options.anki.enable);
- $('#card-tags').val(options.anki.tags.join(' '));
- $('#sentence-detection-extent').val(options.anki.sentenceExt);
- $('#interface-server').val(options.anki.server);
- $('#screenshot-format').val(options.anki.screenshot.format);
- $('#screenshot-quality').val(options.anki.screenshot.quality);
- $('#field-templates').val(options.anki.fieldTemplates);
- $('#field-templates-reset').click(utilAsync(onAnkiFieldTemplatesReset));
- $('input, select, textarea').not('.anki-model').change(utilAsync(onFormOptionsChanged));
- $('.anki-model').change(utilAsync(onAnkiModelChanged));
-
- try {
- await dictionaryGroupsPopulate(options);
- await formMainDictionaryOptionsPopulate(options);
- } catch (e) {
- dictionaryErrorsShow([e]);
- }
-
- try {
- await ankiDeckAndModelPopulate(options);
- } catch (e) {
- ankiErrorShow(e);
- }
-
- formUpdateVisibility(options);
+ formSetupEventListeners();
+ await formWrite(options);
storageInfoInitialize();
+
+ chrome.runtime.onMessage.addListener(onMessage);
}
$(document).ready(utilAsync(onReady));
+/*
+ * Remote options updates
+ */
+
+function settingsGetSource() {
+ return new Promise((resolve) => {
+ chrome.tabs.getCurrent((tab) => resolve(`settings${tab ? tab.id : ''}`));
+ });
+}
+
+async function settingsSaveOptions() {
+ const source = await settingsGetSource();
+ await apiOptionsSave(source);
+}
+
+async function onOptionsUpdate({source}) {
+ const thisSource = await settingsGetSource();
+ if (source === thisSource) { return; }
+
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+ await formWrite(options);
+}
+
+function onMessage({action, params}) {
+ if (action === 'optionsUpdate') {
+ onOptionsUpdate(params);
+ }
+}
+
+
/*
* Dictionary
*/
@@ -374,10 +424,11 @@ async function onDictionaryPurge(e) {
dictionarySpinnerShow(true);
await utilDatabasePurge();
- const options = await optionsLoad();
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
options.dictionaries = {};
options.general.mainDictionary = '';
- await optionsSave(options);
+ await settingsSaveOptions();
await dictionaryGroupsPopulate(options);
await formMainDictionaryOptionsPopulate(options);
@@ -414,8 +465,9 @@ async function onDictionaryImport(e) {
setProgress(0.0);
const exceptions = [];
- const options = await optionsLoad();
const summary = await utilDatabaseImport(e.target.files[0], updateProgress, exceptions);
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
options.dictionaries[summary.title] = {enabled: true, priority: 0, allowSecondarySearches: false};
if (summary.sequenced && options.general.mainDictionary === '') {
options.general.mainDictionary = summary.title;
@@ -426,7 +478,7 @@ async function onDictionaryImport(e) {
dictionaryErrorsShow(exceptions);
}
- await optionsSave(options);
+ await settingsSaveOptions();
await dictionaryGroupsPopulate(options);
await formMainDictionaryOptionsPopulate(options);
@@ -566,12 +618,14 @@ async function onAnkiModelChanged(e) {
const tab = element.closest('.tab-pane');
const tabId = tab.attr('id');
- const {optionsNew, optionsOld} = await formRead();
- optionsNew.anki[tabId].fields = {};
- await optionsSave(optionsNew);
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+ await formRead(options);
+ options.anki[tabId].fields = {};
+ await settingsSaveOptions();
ankiSpinnerShow(true);
- await ankiFieldsPopulate(element, optionsNew);
+ await ankiFieldsPopulate(element, options);
ankiErrorShow();
} catch (e) {
ankiErrorShow(e);
@@ -583,9 +637,12 @@ async function onAnkiModelChanged(e) {
async function onAnkiFieldTemplatesReset(e) {
try {
e.preventDefault();
- const options = await optionsLoad();
- $('#field-templates').val(options.anki.fieldTemplates = optionsFieldTemplates());
- await optionsSave(options);
+ const optionsContext = getOptionsContext();
+ const options = await apiOptionsGet(optionsContext);
+ const fieldTemplates = optionsFieldTemplates();
+ options.anki.fieldTemplates = fieldTemplates;
+ $('#field-templates').val(fieldTemplates);
+ await settingsSaveOptions();
} catch (e) {
ankiErrorShow(e);
}
diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js
index c89b43ff..7b952622 100644
--- a/ext/bg/js/translator.js
+++ b/ext/bg/js/translator.js
@@ -36,8 +36,7 @@ class Translator {
}
}
- async findTermsGrouped(text, dictionaries, alphanumeric) {
- const options = await apiOptionsGet();
+ async findTermsGrouped(text, dictionaries, alphanumeric, options) {
const titles = Object.keys(dictionaries);
const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric);
@@ -55,8 +54,7 @@ class Translator {
return {length, definitions: definitionsGrouped};
}
- async findTermsMerged(text, dictionaries, alphanumeric) {
- const options = await apiOptionsGet();
+ async findTermsMerged(text, dictionaries, alphanumeric, options) {
const secondarySearchTitles = Object.keys(options.dictionaries).filter(dict => options.dictionaries[dict].allowSecondarySearches);
const titles = Object.keys(dictionaries);
const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric);
diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js
index 3dc7c900..79229229 100644
--- a/ext/bg/js/util.js
+++ b/ext/bg/js/util.js
@@ -104,3 +104,7 @@ function utilReadFile(file) {
reader.readAsBinaryString(file);
});
}
+
+function utilIsObject(value) {
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
+}
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index ddda8303..577e1a1f 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -70,6 +70,10 @@
-
- Enable scanning of popup expressions
-
-
-
- Enable scanning on search page
-
-
Deep DOM scan
@@ -252,9 +248,26 @@
Shift
+
-
- Maximum nested popup depth
+
diff --git a/ext/fg/js/api.js b/ext/fg/js/api.js
index aa3b2629..d0ac649a 100644
--- a/ext/fg/js/api.js
+++ b/ext/fg/js/api.js
@@ -17,24 +17,24 @@
*/
-function apiOptionsGet() {
- return utilInvoke('optionsGet');
+function apiOptionsGet(optionsContext) {
+ return utilInvoke('optionsGet', {optionsContext});
}
-function apiTermsFind(text) {
- return utilInvoke('termsFind', {text});
+function apiTermsFind(text, optionsContext) {
+ return utilInvoke('termsFind', {text, optionsContext});
}
-function apiKanjiFind(text) {
- return utilInvoke('kanjiFind', {text});
+function apiKanjiFind(text, optionsContext) {
+ return utilInvoke('kanjiFind', {text, optionsContext});
}
-function apiDefinitionAdd(definition, mode, context) {
- return utilInvoke('definitionAdd', {definition, mode, context});
+function apiDefinitionAdd(definition, mode, context, optionsContext) {
+ return utilInvoke('definitionAdd', {definition, mode, context, optionsContext});
}
-function apiDefinitionsAddable(definitions, modes) {
- return utilInvoke('definitionsAddable', {definitions, modes}).catch(() => null);
+function apiDefinitionsAddable(definitions, modes, optionsContext) {
+ return utilInvoke('definitionsAddable', {definitions, modes, optionsContext}).catch(() => null);
}
function apiNoteView(noteId) {
diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js
index 3c521714..348c114e 100644
--- a/ext/fg/js/float.js
+++ b/ext/fg/js/float.js
@@ -23,6 +23,10 @@ class DisplayFloat extends Display {
this.autoPlayAudioTimer = null;
this.styleNode = null;
+ this.optionsContext = {
+ depth: 0
+ };
+
this.dependencies = Object.assign({}, this.dependencies, {docRangeFromPoint, docSentenceExtract});
$(window).on('message', utilAsync(this.onMessage.bind(this)));
@@ -75,6 +79,7 @@ class DisplayFloat extends Display {
},
popupNestedInitialize: ({id, depth, parentFrameId}) => {
+ this.optionsContext.depth = depth;
popupNestedInitialize(id, depth, parentFrameId);
}
};
diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js
index 52620933..0b60aa2b 100644
--- a/ext/fg/js/frontend.js
+++ b/ext/fg/js/frontend.js
@@ -28,6 +28,10 @@ class Frontend {
this.options = null;
this.ignoreNodes = (Array.isArray(ignoreNodes) && ignoreNodes.length > 0 ? ignoreNodes.join(',') : null);
+ this.optionsContext = {
+ depth: popup.depth
+ };
+
this.primaryTouchIdentifier = null;
this.contextMenuChecking = false;
this.contextMenuPrevent = false;
@@ -40,9 +44,9 @@ class Frontend {
static create() {
const initializationData = window.frontendInitializationData;
const isNested = (initializationData !== null && typeof initializationData === 'object');
- const {id, parentFrameId, ignoreNodes} = isNested ? initializationData : {};
+ const {id, depth, parentFrameId, ignoreNodes} = isNested ? initializationData : {};
- const popup = isNested ? new PopupProxy(id, parentFrameId) : PopupProxyHost.instance.createPopup(null);
+ const popup = isNested ? new PopupProxy(depth + 1, id, parentFrameId) : PopupProxyHost.instance.createPopup(null);
const frontend = new Frontend(popup, ignoreNodes);
frontend.prepare();
return frontend;
@@ -50,7 +54,7 @@ class Frontend {
async prepare() {
try {
- this.options = await apiOptionsGet();
+ this.options = await apiOptionsGet(this.optionsContext);
window.addEventListener('message', this.onFrameMessage.bind(this));
window.addEventListener('mousedown', this.onMouseDown.bind(this));
@@ -261,11 +265,8 @@ class Frontend {
onBgMessage({action, params}, sender, callback) {
const handlers = {
- optionsSet: ({options}) => {
- this.options = options;
- if (!this.options.enable) {
- this.searchClear();
- }
+ optionsUpdate: () => {
+ this.updateOptions();
},
popupSetVisible: ({visible}) => {
@@ -284,6 +285,13 @@ class Frontend {
console.log(error);
}
+ async updateOptions() {
+ this.options = await apiOptionsGet(this.optionsContext);
+ if (!this.options.enable) {
+ this.searchClear();
+ }
+ }
+
popupTimerSet(callback) {
this.popupTimerClear();
this.popupTimer = window.setTimeout(callback, this.options.scanning.delay);
@@ -347,7 +355,7 @@ class Frontend {
return;
}
- const {definitions, length} = await apiTermsFind(searchText);
+ const {definitions, length} = await apiTermsFind(searchText, this.optionsContext);
if (definitions.length === 0) {
return false;
}
@@ -380,7 +388,7 @@ class Frontend {
return;
}
- const definitions = await apiKanjiFind(searchText);
+ const definitions = await apiKanjiFind(searchText, this.optionsContext);
if (definitions.length === 0) {
return false;
}
diff --git a/ext/fg/js/popup-nested.js b/ext/fg/js/popup-nested.js
index e0376bb2..de2acccc 100644
--- a/ext/fg/js/popup-nested.js
+++ b/ext/fg/js/popup-nested.js
@@ -25,7 +25,8 @@ async function popupNestedInitialize(id, depth, parentFrameId) {
}
popupNestedInitialized = true;
- const options = await apiOptionsGet();
+ const optionsContext = {depth};
+ const options = await apiOptionsGet(optionsContext);
const popupNestingMaxDepth = options.scanning.popupNestingMaxDepth;
if (!(typeof popupNestingMaxDepth === 'number' && typeof depth === 'number' && depth < popupNestingMaxDepth)) {
diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js
index f6295079..56e710eb 100644
--- a/ext/fg/js/popup-proxy.js
+++ b/ext/fg/js/popup-proxy.js
@@ -18,14 +18,14 @@
class PopupProxy {
- constructor(parentId, parentFrameId) {
+ constructor(depth, parentId, parentFrameId) {
this.parentId = parentId;
this.parentFrameId = parentFrameId;
this.id = null;
this.idPromise = null;
this.parent = null;
this.child = null;
- this.depth = 0;
+ this.depth = depth;
this.container = null;
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index ebf56897..eca67b5e 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -27,6 +27,7 @@ class Display {
this.sequence = 0;
this.index = 0;
this.audioCache = {};
+ this.optionsContext = {};
this.dependencies = {};
@@ -66,7 +67,7 @@ class Display {
context.source.source = this.context.source;
}
- const kanjiDefs = await apiKanjiFind(link.text());
+ const kanjiDefs = await apiKanjiFind(link.text(), this.optionsContext);
this.kanjiShow(kanjiDefs, this.options, context);
} catch (e) {
this.onError(e);
@@ -89,7 +90,7 @@ class Display {
try {
textSource.setEndOffset(this.options.scanning.length);
- ({definitions, length} = await apiTermsFind(textSource.text()));
+ ({definitions, length} = await apiTermsFind(textSource.text(), this.optionsContext));
if (definitions.length === 0) {
return false;
}
@@ -379,7 +380,7 @@ class Display {
async adderButtonUpdate(modes, sequence) {
try {
- const states = await apiDefinitionsAddable(this.definitions, modes);
+ const states = await apiDefinitionsAddable(this.definitions, modes, this.optionsContext);
if (!states || sequence !== this.sequence) {
return;
}
@@ -453,7 +454,7 @@ class Display {
}
}
- const noteId = await apiDefinitionAdd(definition, mode, context);
+ const noteId = await apiDefinitionAdd(definition, mode, context, this.optionsContext);
if (noteId) {
const index = this.definitions.indexOf(definition);
Display.adderButtonFind(index, mode).addClass('disabled');