From 91bc31d7582fb54908433cd8b6e46b5a0be4e9b3 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 11:21:06 -0400 Subject: [PATCH 01/16] Change how options updates are handled on the frontend Only an 'optionsUpdate' signal is now sent to the frontend with empty data. The frontend then responds by performing apiOptionsGet to update the options. This makes it so that there is only a single function which is responsible for requesting options from the backend. --- ext/bg/js/backend.js | 2 +- ext/fg/js/frontend.js | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index b3e737da..0394c4ec 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -59,7 +59,7 @@ class Backend { 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: {}}, callback); } }); } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 52620933..83e0cef1 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -261,11 +261,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 +281,13 @@ class Frontend { console.log(error); } + async updateOptions() { + this.options = await apiOptionsGet(); + if (!this.options.enable) { + this.searchClear(); + } + } + popupTimerSet(callback) { this.popupTimerClear(); this.popupTimer = window.setTimeout(callback, this.options.scanning.delay); From aae971a09e1b48d932126925521cf2d3ba34a41f Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 12:35:57 -0400 Subject: [PATCH 02/16] Pass options directly to translator.findTerms* --- ext/bg/js/api.js | 3 ++- ext/bg/js/translator.js | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 9839aef5..b56b3449 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -38,7 +38,8 @@ async function apiTermsFind(text) { const {definitions, length} = await searcher( text, dictEnabledSet(options), - options.scanning.alphanumeric + options.scanning.alphanumeric, + options ); return { 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); From 99ca60d4c1456f243d8142b4502db441e33340a4 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 13:11:25 -0400 Subject: [PATCH 03/16] Ensure both Popup and PopupProxy have valid depth --- ext/fg/js/frontend.js | 4 ++-- ext/fg/js/popup-proxy.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 83e0cef1..5e12d101 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -40,9 +40,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; 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; From bc8793eb56b2ce985f2e5dc0a9fd270f98fbf17a Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 13:58:19 -0400 Subject: [PATCH 04/16] Add a context object for all calls to fetch options --- ext/bg/js/api.js | 26 ++++++++++++++------------ ext/bg/js/backend.js | 20 ++++++++++---------- ext/bg/js/search-frontend.js | 3 ++- ext/bg/js/search.js | 8 ++++++-- ext/fg/js/api.js | 20 ++++++++++---------- ext/fg/js/float.js | 5 +++++ ext/fg/js/frontend.js | 12 ++++++++---- ext/fg/js/popup-nested.js | 3 ++- ext/mixed/js/display.js | 9 +++++---- 9 files changed, 62 insertions(+), 44 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index b56b3449..0b80f099 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -17,16 +17,16 @@ */ -function apiOptionsGetSync() { +function apiOptionsGetSync(optionsContext) { return utilBackend().options; } -async function apiOptionsGet() { - return apiOptionsGetSync(); +async function apiOptionsGet(optionsContext) { + return apiOptionsGetSync(optionsContext); } -async function apiTermsFind(text) { - const options = apiOptionsGetSync(); +async function apiTermsFind(text, optionsContext) { + const options = apiOptionsGetSync(optionsContext); const translator = utilBackend().translator; const searcher = { @@ -48,14 +48,14 @@ async function apiTermsFind(text) { }; } -async function apiKanjiFind(text) { - const options = apiOptionsGetSync(); +async function apiKanjiFind(text, optionsContext) { + const options = apiOptionsGetSync(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 = apiOptionsGetSync(optionsContext); if (mode !== 'kanji') { await audioInject( @@ -77,14 +77,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 = apiOptionsGetSync(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); } } @@ -132,7 +133,8 @@ async function apiCommandExec(command) { }, toggle: async () => { - const options = apiOptionsGetSync(); + const optionsContext = {depth: 0}; + const options = apiOptionsGetSync(optionsContext); options.general.enable = !options.general.enable; await optionsSave(options); } diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 0394c4ec..6afa9617 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -78,24 +78,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}) => { 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/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 5e12d101..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; @@ -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)); @@ -282,7 +286,7 @@ class Frontend { } async updateOptions() { - this.options = await apiOptionsGet(); + this.options = await apiOptionsGet(this.optionsContext); if (!this.options.enable) { this.searchClear(); } @@ -351,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; } @@ -384,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/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'); From 1b2a1e50ebcd62cf54b397516e991333afa5158c Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 14:21:26 -0400 Subject: [PATCH 05/16] Add getOptions function to backend --- ext/bg/js/api.js | 2 +- ext/bg/js/backend.js | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 0b80f099..45dc36e7 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -18,7 +18,7 @@ function apiOptionsGetSync(optionsContext) { - return utilBackend().options; + return utilBackend().getOptions(optionsContext); } async function apiOptionsGet(optionsContext) { diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 6afa9617..59de5a50 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -22,6 +22,9 @@ class Backend { this.translator = new Translator(); this.anki = new AnkiNull(); this.options = null; + this.optionsContext = { + depth: 0 + }; this.apiForwarder = new BackendApiForwarder(); } @@ -35,26 +38,15 @@ class Backend { } chrome.runtime.onMessage.addListener(this.onMessage.bind(this)); - if (this.options.general.showGuide) { + const options = this.getOptions(this.optionsContext); + if (options.general.showGuide) { chrome.tabs.create({url: chrome.extension.getURL('/bg/guide.html')}); } } 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(); + this.options = utilIsolate(options); + this.applyOptions(); const callback = () => this.checkLastError(chrome.runtime.lastError); chrome.tabs.query({}, tabs => { @@ -136,6 +128,25 @@ class Backend { return true; } + applyOptions() { + const options = this.getOptions(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(); + } + + getOptions(optionsContext) { + return this.options; + } + setExtensionBadgeBackgroundColor(color) { if (typeof chrome.browserAction.setBadgeBackgroundColor === 'function') { chrome.browserAction.setBadgeBackgroundColor({color}); From 8175f80183caa0673a946b2405feae0c9535af48 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 15:06:15 -0400 Subject: [PATCH 06/16] Remove calls to apiOptionsGetSync Use apiOptionsGet everywhere to ensure options is initialized. --- ext/bg/js/api.js | 16 ++++++---------- ext/bg/js/backend.js | 20 +++++++++++++++++--- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 45dc36e7..53e25348 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -17,16 +17,12 @@ */ -function apiOptionsGetSync(optionsContext) { +function apiOptionsGet(optionsContext) { return utilBackend().getOptions(optionsContext); } -async function apiOptionsGet(optionsContext) { - return apiOptionsGetSync(optionsContext); -} - async function apiTermsFind(text, optionsContext) { - const options = apiOptionsGetSync(optionsContext); + const options = await apiOptionsGet(optionsContext); const translator = utilBackend().translator; const searcher = { @@ -49,13 +45,13 @@ async function apiTermsFind(text, optionsContext) { } async function apiKanjiFind(text, optionsContext) { - const options = apiOptionsGetSync(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, optionsContext) { - const options = apiOptionsGetSync(optionsContext); + const options = await apiOptionsGet(optionsContext); if (mode !== 'kanji') { await audioInject( @@ -78,7 +74,7 @@ async function apiDefinitionAdd(definition, mode, context, optionsContext) { } async function apiDefinitionsAddable(definitions, modes, optionsContext) { - const options = apiOptionsGetSync(optionsContext); + const options = await apiOptionsGet(optionsContext); const states = []; try { @@ -134,7 +130,7 @@ async function apiCommandExec(command) { toggle: async () => { const optionsContext = {depth: 0}; - const options = apiOptionsGetSync(optionsContext); + const options = await apiOptionsGet(optionsContext); options.general.enable = !options.general.enable; await optionsSave(options); } diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 59de5a50..6dcf8e4d 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -26,6 +26,9 @@ class Backend { depth: 0 }; + this.isPreparedResolve = null; + this.isPreparedPromise = new Promise((resolve) => (this.isPreparedResolve = resolve)); + this.apiForwarder = new BackendApiForwarder(); } @@ -38,10 +41,14 @@ class Backend { } chrome.runtime.onMessage.addListener(this.onMessage.bind(this)); - const options = this.getOptions(this.optionsContext); + 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) { @@ -129,7 +136,7 @@ class Backend { } applyOptions() { - const options = this.getOptions(this.optionsContext); + const options = this.getOptionsSync(this.optionsContext); if (!options.general.enable) { this.setExtensionBadgeBackgroundColor('#555555'); this.setExtensionBadgeText('off'); @@ -143,7 +150,14 @@ class Backend { this.anki = options.anki.enable ? new AnkiConnect(options.anki.server) : new AnkiNull(); } - getOptions(optionsContext) { + async getOptions(optionsContext) { + if (this.isPreparedPromise !== null) { + await this.isPreparedPromise; + } + return this.getOptionsSync(optionsContext); + } + + getOptionsSync(optionsContext) { return this.options; } From 99cec1d23fc779ae26112dcd63d86b5f6fa4bddd Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 15:54:00 -0400 Subject: [PATCH 07/16] Change how formRead works The function now modifies values in-place. --- ext/bg/js/settings.js | 129 +++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 83f4528c..a59f7c0d 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -17,72 +17,68 @@ */ -async function formRead() { - const optionsOld = await optionsLoad(); - const optionsNew = $.extend(true, {}, optionsOld); +async function formRead(options) { + 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}; } function formUpdateVisibility(options) { @@ -141,18 +137,22 @@ async function onFormOptionsChanged(e) { return; } - const {optionsNew, optionsOld} = await formRead(); - await optionsSave(optionsNew); - formUpdateVisibility(optionsNew); + const options = await optionsLoad(); + const optionsAnkiEnableOld = options.anki.enable; + const optionsAnkiServerOld = options.anki.server; + + await formRead(options); + await optionsSave(options); + 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) { @@ -566,12 +566,13 @@ 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 options = await optionsLoad(); + await formRead(options); + options.anki[tabId].fields = {}; + await optionsSave(options); ankiSpinnerShow(true); - await ankiFieldsPopulate(element, optionsNew); + await ankiFieldsPopulate(element, options); ankiErrorShow(); } catch (e) { ankiErrorShow(e); From 4686a31a0a5d30a1b01e3cd2b689d1950c79c940 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 15:59:10 -0400 Subject: [PATCH 08/16] Use apiOptionsGet instead of optionsLoad --- ext/bg/js/context.js | 3 ++- ext/bg/js/settings.js | 27 ++++++++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) 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/settings.js b/ext/bg/js/settings.js index a59f7c0d..1c9198dd 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -16,6 +16,11 @@ * along with this program. If not, see . */ +function getOptionsContext() { + return { + depth: 0 + }; +} async function formRead(options) { options.general.showGuide = $('#show-usage-guide').prop('checked'); @@ -137,7 +142,8 @@ async function onFormOptionsChanged(e) { return; } - const options = await optionsLoad(); + const optionsContext = getOptionsContext(); + const options = await apiOptionsGet(optionsContext); const optionsAnkiEnableOld = options.anki.enable; const optionsAnkiServerOld = options.anki.server; @@ -163,7 +169,8 @@ 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); @@ -374,7 +381,8 @@ 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); @@ -414,8 +422,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; @@ -566,7 +575,8 @@ async function onAnkiModelChanged(e) { const tab = element.closest('.tab-pane'); const tabId = tab.attr('id'); - const options = await optionsLoad(); + const optionsContext = getOptionsContext(); + const options = await apiOptionsGet(optionsContext); await formRead(options); options.anki[tabId].fields = {}; await optionsSave(options); @@ -584,8 +594,11 @@ async function onAnkiModelChanged(e) { async function onAnkiFieldTemplatesReset(e) { try { e.preventDefault(); - const options = await optionsLoad(); - $('#field-templates').val(options.anki.fieldTemplates = optionsFieldTemplates()); + const optionsContext = getOptionsContext(); + const options = await apiOptionsGet(optionsContext); + const fieldTemplates = optionsFieldTemplates(); + options.anki.fieldTemplates = fieldTemplates; + $('#field-templates').val(fieldTemplates); await optionsSave(options); } catch (e) { ankiErrorShow(e); From 05ce350792fd60c1721bff4d0fb971e2bec24818 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 16:15:18 -0400 Subject: [PATCH 09/16] Use apiOptionsSave instead of optionsSave --- ext/bg/js/api.js | 9 ++++++++- ext/bg/js/backend.js | 7 +++++++ ext/bg/js/options.js | 13 +++++++++---- ext/bg/js/settings.js | 10 +++++----- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 53e25348..a50353c1 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -21,6 +21,13 @@ function apiOptionsGet(optionsContext) { return utilBackend().getOptions(optionsContext); } +async function apiOptionsSave() { + const backend = utilBackend(); + const options = await backend.getFullOptions(); + await optionsSave(options); + backend.onOptionsUpdated(options); +} + async function apiTermsFind(text, optionsContext) { const options = await apiOptionsGet(optionsContext); const translator = utilBackend().translator; @@ -132,7 +139,7 @@ async function apiCommandExec(command) { const optionsContext = {depth: 0}; const options = await apiOptionsGet(optionsContext); options.general.enable = !options.general.enable; - await optionsSave(options); + await apiOptionsSave(); } }; diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 6dcf8e4d..1f00f788 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -150,6 +150,13 @@ class Backend { 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; diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 69c662e6..ea8f56d5 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -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/settings.js b/ext/bg/js/settings.js index 1c9198dd..e5786804 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -148,7 +148,7 @@ async function onFormOptionsChanged(e) { const optionsAnkiServerOld = options.anki.server; await formRead(options); - await optionsSave(options); + await apiOptionsSave(); formUpdateVisibility(options); try { @@ -385,7 +385,7 @@ async function onDictionaryPurge(e) { const options = await apiOptionsGet(optionsContext); options.dictionaries = {}; options.general.mainDictionary = ''; - await optionsSave(options); + await apiOptionsSave(); await dictionaryGroupsPopulate(options); await formMainDictionaryOptionsPopulate(options); @@ -435,7 +435,7 @@ async function onDictionaryImport(e) { dictionaryErrorsShow(exceptions); } - await optionsSave(options); + await apiOptionsSave(); await dictionaryGroupsPopulate(options); await formMainDictionaryOptionsPopulate(options); @@ -579,7 +579,7 @@ async function onAnkiModelChanged(e) { const options = await apiOptionsGet(optionsContext); await formRead(options); options.anki[tabId].fields = {}; - await optionsSave(options); + await apiOptionsSave(); ankiSpinnerShow(true); await ankiFieldsPopulate(element, options); @@ -599,7 +599,7 @@ async function onAnkiFieldTemplatesReset(e) { const fieldTemplates = optionsFieldTemplates(); options.anki.fieldTemplates = fieldTemplates; $('#field-templates').val(fieldTemplates); - await optionsSave(options); + await apiOptionsSave(); } catch (e) { ankiErrorShow(e); } From c4e6d7e3d18bc87a9e682349bd96fc35d7815bbc Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 17:35:33 -0400 Subject: [PATCH 10/16] Add utility method for checking if a value is a standard object --- ext/bg/js/options.js | 2 +- ext/bg/js/util.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index ea8f56d5..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; } } 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); +} From cab39e89c93a9b1826dce5d6555c213fa722c82e Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 20:04:43 -0400 Subject: [PATCH 11/16] Create section for Popup Content Scanning Options --- ext/bg/settings.html | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/ext/bg/settings.html b/ext/bg/settings.html index ddda8303..8c368474 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -221,14 +221,6 @@ -
- -
- -
- -
-
@@ -252,9 +244,26 @@ + -
- + From 36b39e2f6530bfba5019462313b29b90a2db2aec Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 8 Sep 2019 11:23:04 -0400 Subject: [PATCH 12/16] Create formWrite function --- ext/bg/js/settings.js | 128 ++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index e5786804..3c261b88 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -86,6 +86,71 @@ async function formRead(options) { }); } +async function formWrite(options) { + $('#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); +} + function formUpdateVisibility(options) { const general = $('#anki-general'); if (options.anki.enable) { @@ -172,68 +237,7 @@ async function onReady() { 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); + await formWrite(options); storageInfoInitialize(); } From 1f8c7358cb017e0c96a454d9e9a1f53eb6d4fe15 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 8 Sep 2019 12:16:12 -0400 Subject: [PATCH 13/16] Create function for setting up form event listeners --- ext/bg/js/settings.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 3c261b88..06dba538 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -121,9 +121,6 @@ async function formWrite(options) { $('#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); @@ -131,9 +128,6 @@ async function formWrite(options) { $('#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); @@ -151,6 +145,15 @@ async function formWrite(options) { 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) { const general = $('#anki-general'); if (options.anki.enable) { @@ -237,6 +240,7 @@ async function onReady() { const optionsContext = getOptionsContext(); const options = await apiOptionsGet(optionsContext); + formSetupEventListeners(); await formWrite(options); storageInfoInitialize(); From 87e513dd77ab228953449d534a989c8c68f39858 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 10 Sep 2019 20:04:28 -0400 Subject: [PATCH 14/16] Fix options reference being changed --- ext/bg/js/api.js | 2 +- ext/bg/js/backend.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index a50353c1..13c0d73a 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -25,7 +25,7 @@ async function apiOptionsSave() { const backend = utilBackend(); const options = await backend.getFullOptions(); await optionsSave(options); - backend.onOptionsUpdated(options); + backend.onOptionsUpdated(); } async function apiTermsFind(text, optionsContext) { diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 1f00f788..4763e85d 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -34,7 +34,8 @@ class Backend { async prepare() { await this.translator.prepare(); - this.onOptionsUpdated(await optionsLoad()); + this.options = await optionsLoad(); + this.onOptionsUpdated(); if (chrome.commands !== null && typeof chrome.commands === 'object') { chrome.commands.onCommand.addListener(this.onCommand.bind(this)); @@ -51,8 +52,7 @@ class Backend { this.isPreparedPromise = null; } - onOptionsUpdated(options) { - this.options = utilIsolate(options); + onOptionsUpdated() { this.applyOptions(); const callback = () => this.checkLastError(chrome.runtime.lastError); From 71b700cd22f5a57a07cd2f9a6afa823793f5f95e Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 10 Sep 2019 20:49:17 -0400 Subject: [PATCH 15/16] Add checkbox for options.general.enable --- ext/bg/js/settings.js | 2 ++ ext/bg/settings.html | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 06dba538..f2f08b6e 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -23,6 +23,7 @@ function getOptionsContext() { } 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'); @@ -87,6 +88,7 @@ async function formRead(options) { } 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); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 8c368474..577e1a1f 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -70,6 +70,10 @@

General Options

+
+ +
+
From 84bd9ff93b15f419ce1076b7545aeb406917f9b5 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 10 Sep 2019 20:46:30 -0400 Subject: [PATCH 16/16] Update settings if a different source triggers optionsUpdate --- ext/bg/js/api.js | 6 +++--- ext/bg/js/backend.js | 6 +++--- ext/bg/js/settings.js | 43 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 13c0d73a..81772d08 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -21,11 +21,11 @@ function apiOptionsGet(optionsContext) { return utilBackend().getOptions(optionsContext); } -async function apiOptionsSave() { +async function apiOptionsSave(source) { const backend = utilBackend(); const options = await backend.getFullOptions(); await optionsSave(options); - backend.onOptionsUpdated(); + backend.onOptionsUpdated(source); } async function apiTermsFind(text, optionsContext) { @@ -139,7 +139,7 @@ async function apiCommandExec(command) { const optionsContext = {depth: 0}; const options = await apiOptionsGet(optionsContext); options.general.enable = !options.general.enable; - await apiOptionsSave(); + await apiOptionsSave('popup'); } }; diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 4763e85d..9a300d62 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -35,7 +35,7 @@ class Backend { async prepare() { await this.translator.prepare(); this.options = await optionsLoad(); - this.onOptionsUpdated(); + this.onOptionsUpdated('background'); if (chrome.commands !== null && typeof chrome.commands === 'object') { chrome.commands.onCommand.addListener(this.onCommand.bind(this)); @@ -52,13 +52,13 @@ class Backend { this.isPreparedPromise = null; } - onOptionsUpdated() { + 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: 'optionsUpdate', params: {}}, callback); + chrome.tabs.sendMessage(tab.id, {action: 'optionsUpdate', params: {source}}, callback); } }); } diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index f2f08b6e..7f3e5c69 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -218,7 +218,7 @@ async function onFormOptionsChanged(e) { const optionsAnkiServerOld = options.anki.server; await formRead(options); - await apiOptionsSave(); + await settingsSaveOptions(); formUpdateVisibility(options); try { @@ -246,11 +246,44 @@ async function onReady() { 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 */ @@ -395,7 +428,7 @@ async function onDictionaryPurge(e) { const options = await apiOptionsGet(optionsContext); options.dictionaries = {}; options.general.mainDictionary = ''; - await apiOptionsSave(); + await settingsSaveOptions(); await dictionaryGroupsPopulate(options); await formMainDictionaryOptionsPopulate(options); @@ -445,7 +478,7 @@ async function onDictionaryImport(e) { dictionaryErrorsShow(exceptions); } - await apiOptionsSave(); + await settingsSaveOptions(); await dictionaryGroupsPopulate(options); await formMainDictionaryOptionsPopulate(options); @@ -589,7 +622,7 @@ async function onAnkiModelChanged(e) { const options = await apiOptionsGet(optionsContext); await formRead(options); options.anki[tabId].fields = {}; - await apiOptionsSave(); + await settingsSaveOptions(); ankiSpinnerShow(true); await ankiFieldsPopulate(element, options); @@ -609,7 +642,7 @@ async function onAnkiFieldTemplatesReset(e) { const fieldTemplates = optionsFieldTemplates(); options.anki.fieldTemplates = fieldTemplates; $('#field-templates').val(fieldTemplates); - await apiOptionsSave(); + await settingsSaveOptions(); } catch (e) { ankiErrorShow(e); }