From aa77a7896da735b68b74c62f1726d35e8a6b1774 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 5 Sep 2019 18:30:39 -0400 Subject: Group all two-column settings together --- ext/bg/settings.html | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 85b7ee5f..6c649251 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -107,6 +107,16 @@ +
+ + +
+ +
+ + +
+
@@ -129,16 +139,6 @@
-
- - -
- -
- - -
-
-- cgit v1.2.3 From 2e87cd72bc87ebbb5eb25dd8e967f31ab14ca444 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 5 Sep 2019 18:39:28 -0400 Subject: Use consistent styling for all two-column settings --- ext/bg/settings.html | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 6c649251..ccd749b9 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -140,26 +140,41 @@
-
-
-
+
+ + +
+
+ + +
-
-
-
+
+ + +
+
+ + +
-
-
-
+
+ + +
+
+ + +
-- cgit v1.2.3 From fb8cb3db43c38e5a984cf4a45b5b9def311cf0a0 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 5 Sep 2019 18:45:42 -0400 Subject: Collapse two-column options on smaller screens --- ext/bg/settings.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ext/bg/settings.html b/ext/bg/settings.html index ccd749b9..093a3c26 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -49,6 +49,16 @@ [data-browser=firefox-mobile] [data-show-for-browser~=firefox-mobile] { display: initial; } + + @media screen and (max-width: 740px) { + .col-xs-6 { + float: none; + width: 100%; + } + .col-xs-6+.col-xs-6 { + margin-top: 15px; + } + } -- cgit v1.2.3 From dbfbf9d12dd4a5be16fa31cecf53c5eb300d8978 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 5 Sep 2019 18:53:12 -0400 Subject: Use non-bold text for label information Units, etc. --- ext/bg/settings.html | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 093a3c26..76078274 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -30,6 +30,10 @@ padding-bottom: 1em; } + .label-light { + font-weight: normal; + } + #custom-popup-css { width: 100%; min-height: 34px; @@ -118,7 +122,7 @@
- +
@@ -152,11 +156,11 @@
- +
- +
@@ -165,11 +169,11 @@
- +
- +
@@ -178,11 +182,11 @@
- +
- +
@@ -230,12 +234,12 @@
- +
- +
@@ -351,12 +355,12 @@
- +
- +
@@ -374,7 +378,7 @@
- +
-- cgit v1.2.3 From bc7759d94c4d4bbe2480e5328a4c3488ff29e493 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 5 Sep 2019 20:57:10 -0400 Subject: Add some parameters to field-templates textarea --- ext/bg/settings.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 76078274..ddda8303 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -442,7 +442,7 @@ their Anki cards. If you encounter problems with your changes you can always reset to default template settings.

- +
-- cgit v1.2.3 From 1c767711bb553fa828596f95f8ed9e91a3e13b5d Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 5 Sep 2019 19:21:50 -0400 Subject: Prevent infinite loops for corrupt options --- ext/bg/js/options.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index df95aae9..d903250e 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -305,14 +305,19 @@ function optionsVersion(options) { ]; optionsSetDefaults(options); - if (!options.hasOwnProperty('version')) { - options.version = fixups.length; + + let version = options.version; + if (typeof version !== 'number' || !Number.isFinite(version)) { + version = fixups.length; + } else { + version = Math.max(0, Math.floor(version)); } - while (options.version < fixups.length) { - fixups[options.version++](); + for (; version < fixups.length; ++version) { + fixups[version](); } + options.version = version; return options; } -- cgit v1.2.3 From ec110fa1b7299a947ea3eabc0e2c46094408c8ba Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 5 Sep 2019 20:35:04 -0400 Subject: Add some validation to options loading --- ext/bg/js/options.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index d903250e..976f8e55 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -323,10 +323,23 @@ function optionsVersion(options) { function optionsLoad() { return new Promise((resolve, reject) => { - chrome.storage.local.get(null, store => resolve(store.options)); + chrome.storage.local.get(['options'], store => { + const error = chrome.runtime.lastError; + if (error) { + reject(error); + } else { + resolve(store.options); + } + }); }).then(optionsStr => { - return optionsStr ? JSON.parse(optionsStr) : {}; - }).catch(error => { + if (typeof optionsStr === 'string') { + const options = JSON.parse(optionsStr); + if (typeof options === 'object' && options !== null && !Array.isArray(options)) { + return options; + } + } + return {}; + }).catch(() => { return {}; }).then(options => { return optionsVersion(options); @@ -334,7 +347,7 @@ function optionsLoad() { } function optionsSave(options) { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { chrome.storage.local.set({options: JSON.stringify(options)}, resolve); }).then(() => { apiOptionsSet(options); -- cgit v1.2.3 From 5ddbb0373f93632109458725e0f18f1fc75ff643 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 5 Sep 2019 19:56:29 -0400 Subject: Add function to create default options --- ext/bg/js/options.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 976f8e55..be27f71f 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -183,8 +183,8 @@ function optionsFieldTemplates() { `.trim(); } -function optionsSetDefaults(options) { - const defaults = { +function optionsCreateDefaults() { + return { general: { enable: true, audioSource: 'jpod101', @@ -238,6 +238,10 @@ function optionsSetDefaults(options) { fieldTemplates: optionsFieldTemplates() } }; +} + +function optionsSetDefaults(options) { + const defaults = optionsCreateDefaults(); const combine = (target, source) => { for (const key in source) { -- cgit v1.2.3 From a74cdbff1dbfad48ae18cc101645b7a2b9ec8817 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 6 Sep 2019 18:21:20 -0400 Subject: Change update process --- ext/bg/js/options.js | 112 ++++++++++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 60 deletions(-) diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index be27f71f..0e871567 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -17,6 +17,57 @@ */ +function optionsApplyUpdates(options, updates) { + const targetVersion = updates.length; + const currentVersion = options.version; + if (typeof currentVersion === 'number' && Number.isFinite(currentVersion)) { + for (let i = Math.max(0, Math.floor(currentVersion)); i < targetVersion; ++i) { + const update = updates[i]; + if (update !== null) { + update(options); + } + } + } + + options.version = targetVersion; + return options; +} + +const optionsVersionUpdates = [ + null, + null, + null, + null, + (options) => { + options.general.audioSource = options.general.audioPlayback ? 'jpod101' : 'disabled'; + }, + (options) => { + options.general.showGuide = false; + }, + (options) => { + options.scanning.modifier = options.scanning.requireShift ? 'shift' : 'none'; + }, + (options) => { + const fieldTemplatesDefault = profileCreateDefaultFieldTemplates(); + options.general.resultOutputMode = options.general.groupResults ? 'group' : 'split'; + options.anki.fieldTemplates = ( + (utilStringHashCode(options.anki.fieldTemplates) !== -805327496) ? + `{{#if merge}}${fieldTemplatesDefault}{{else}}${options.anki.fieldTemplates}{{/if}}` : + fieldTemplatesDefault + ); + }, + (options) => { + if (utilStringHashCode(options.anki.fieldTemplates) === 1285806040) { + options.anki.fieldTemplates = profileCreateDefaultFieldTemplates(); + } + }, + (options) => { + if (utilStringHashCode(options.anki.fieldTemplates) === -250091611) { + options.anki.fieldTemplates = profileCreateDefaultFieldTemplates(); + } + } +]; + function optionsFieldTemplates() { return ` {{#*inline "glossary-single"}} @@ -262,67 +313,8 @@ function optionsSetDefaults(options) { } function optionsVersion(options) { - const fixups = [ - () => {}, - () => {}, - () => {}, - () => {}, - () => { - if (options.general.audioPlayback) { - options.general.audioSource = 'jpod101'; - } else { - options.general.audioSource = 'disabled'; - } - }, - () => { - options.general.showGuide = false; - }, - () => { - if (options.scanning.requireShift) { - options.scanning.modifier = 'shift'; - } else { - options.scanning.modifier = 'none'; - } - }, - () => { - if (options.general.groupResults) { - options.general.resultOutputMode = 'group'; - } else { - options.general.resultOutputMode = 'split'; - } - if (utilStringHashCode(options.anki.fieldTemplates) !== -805327496) { - options.anki.fieldTemplates = `{{#if merge}}${optionsFieldTemplates()}{{else}}${options.anki.fieldTemplates}{{/if}}`; - } else { - options.anki.fieldTemplates = optionsFieldTemplates(); - } - }, - () => { - if (utilStringHashCode(options.anki.fieldTemplates) === 1285806040) { - options.anki.fieldTemplates = optionsFieldTemplates(); - } - }, - () => { - if (utilStringHashCode(options.anki.fieldTemplates) === -250091611) { - options.anki.fieldTemplates = optionsFieldTemplates(); - } - } - ]; - optionsSetDefaults(options); - - let version = options.version; - if (typeof version !== 'number' || !Number.isFinite(version)) { - version = fixups.length; - } else { - version = Math.max(0, Math.floor(version)); - } - - for (; version < fixups.length; ++version) { - fixups[version](); - } - - options.version = version; - return options; + return optionsApplyUpdates(options, optionsVersionUpdates); } function optionsLoad() { -- cgit v1.2.3 From 35ca0f35dd2cfea3223e5a38d5465002dd54993b Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 6 Sep 2019 20:49:12 -0400 Subject: Create common function for utilBackend().options --- ext/bg/js/api.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index b8ef4362..7126cab7 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -21,12 +21,16 @@ async function apiOptionsSet(options) { utilBackend().onOptionsUpdated(options); } -async function apiOptionsGet() { +function apiOptionsGetSync() { return utilBackend().options; } +async function apiOptionsGet() { + return apiOptionsGetSync(); +} + async function apiTermsFind(text) { - const options = utilBackend().options; + const options = apiOptionsGetSync(); const translator = utilBackend().translator; const searcher = { @@ -48,13 +52,13 @@ async function apiTermsFind(text) { } async function apiKanjiFind(text) { - const options = utilBackend().options; + const options = apiOptionsGetSync(); const definitions = await utilBackend().translator.findKanji(text, dictEnabledSet(options)); return definitions.slice(0, options.general.maxResults); } async function apiDefinitionAdd(definition, mode, context) { - const options = utilBackend().options; + const options = apiOptionsGetSync(); if (mode !== 'kanji') { await audioInject( @@ -83,7 +87,7 @@ async function apiDefinitionsAddable(definitions, modes) { const notes = []; for (const definition of definitions) { for (const mode of modes) { - const note = await dictNoteFormat(definition, mode, utilBackend().options); + const note = await dictNoteFormat(definition, mode, apiOptionsGetSync()); notes.push(note); } } @@ -131,7 +135,7 @@ async function apiCommandExec(command) { }, toggle: async () => { - const options = utilBackend().options; + const options = apiOptionsGetSync(); options.general.enable = !options.general.enable; await optionsSave(options); await apiOptionsSet(options); -- cgit v1.2.3 From 13b184707b1bb0c5150645d6cdd186accb345f60 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 6 Sep 2019 21:06:45 -0400 Subject: Remove unnecessary functions apiOptionsSet not required in bg/js/api.js after optionsSave; optionsSave already invokes apiOptionsSet. apiOptionsSet not required in fg/js/api.js since it's never invoked by the foreground. optionsSet handler not required in bg/js/backend.js since the message is never sent by the foreground. --- ext/bg/js/api.js | 1 - ext/bg/js/backend.js | 4 ---- ext/fg/js/api.js | 4 ---- 3 files changed, 9 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 7126cab7..ff54ae81 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -138,7 +138,6 @@ async function apiCommandExec(command) { const options = apiOptionsGetSync(); options.general.enable = !options.general.enable; await optionsSave(options); - await apiOptionsSet(options); } }; diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 39fd4288..c1cef0c5 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -85,10 +85,6 @@ class Backend { forward(apiOptionsGet(), callback); }, - optionsSet: ({options, callback}) => { - forward(apiOptionsSet(options), callback); - }, - kanjiFind: ({text, callback}) => { forward(apiKanjiFind(text), callback); }, diff --git a/ext/fg/js/api.js b/ext/fg/js/api.js index 6bcb0dbb..aa3b2629 100644 --- a/ext/fg/js/api.js +++ b/ext/fg/js/api.js @@ -17,10 +17,6 @@ */ -function apiOptionsSet(options) { - return utilInvoke('optionsSet', {options}); -} - function apiOptionsGet() { return utilInvoke('optionsGet'); } -- cgit v1.2.3 From 7db2c661054113966644c9055e5b60e29bbeb068 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 6 Sep 2019 21:07:29 -0400 Subject: Use consistent structure for params --- ext/bg/js/backend.js | 2 +- ext/fg/js/frontend.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index c1cef0c5..f05ae9e6 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -62,7 +62,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: 'optionsSet', params: {options}}, callback); } }); } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index b70bf036..6806e2c3 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -261,7 +261,7 @@ class Frontend { onBgMessage({action, params}, sender, callback) { const handlers = { - optionsSet: options => { + optionsSet: ({options}) => { this.options = options; if (!this.options.enable) { this.searchClear(); -- cgit v1.2.3 From eb98dfb1a86d42a0ecfe54d8eb978c47aa1c0f8b Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 6 Sep 2019 21:23:00 -0400 Subject: Simplify logic for how option updates are propagated --- ext/bg/js/api.js | 4 ---- ext/bg/js/backend.js | 13 +++++-------- ext/bg/js/options.js | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index ff54ae81..9839aef5 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -17,10 +17,6 @@ */ -async function apiOptionsSet(options) { - utilBackend().onOptionsUpdated(options); -} - function apiOptionsGetSync() { return utilBackend().options; } diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index f05ae9e6..b3e737da 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -28,7 +28,7 @@ class Backend { async prepare() { await this.translator.prepare(); - await apiOptionsSet(await optionsLoad()); + this.onOptionsUpdated(await optionsLoad()); if (chrome.commands !== null && typeof chrome.commands === 'object') { chrome.commands.onCommand.addListener(this.onCommand.bind(this)); @@ -41,7 +41,8 @@ class Backend { } onOptionsUpdated(options) { - this.options = utilIsolate(options); + options = utilIsolate(options); + this.options = options; if (!options.general.enable) { this.setExtensionBadgeBackgroundColor('#555555'); @@ -53,11 +54,7 @@ class Backend { this.setExtensionBadgeText(''); } - if (options.anki.enable) { - this.anki = new AnkiConnect(options.anki.server); - } else { - this.anki = new AnkiNull(); - } + this.anki = options.anki.enable ? new AnkiConnect(options.anki.server) : new AnkiNull(); const callback = () => this.checkLastError(chrome.runtime.lastError); chrome.tabs.query({}, tabs => { @@ -144,7 +141,7 @@ class Backend { chrome.browserAction.setBadgeBackgroundColor({color}); } } - + setExtensionBadgeText(text) { if (typeof chrome.browserAction.setBadgeText === 'function') { chrome.browserAction.setBadgeText({text}); diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 0e871567..69c662e6 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -346,6 +346,6 @@ function optionsSave(options) { return new Promise((resolve) => { chrome.storage.local.set({options: JSON.stringify(options)}, resolve); }).then(() => { - apiOptionsSet(options); + utilBackend().onOptionsUpdated(options); }); } -- cgit v1.2.3 From 91bc31d7582fb54908433cd8b6e46b5a0be4e9b3 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 11:21:06 -0400 Subject: 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); -- cgit v1.2.3 From aae971a09e1b48d932126925521cf2d3ba34a41f Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 12:35:57 -0400 Subject: 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); -- cgit v1.2.3 From 99ca60d4c1456f243d8142b4502db441e33340a4 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 13:11:25 -0400 Subject: 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; -- cgit v1.2.3 From bc8793eb56b2ce985f2e5dc0a9fd270f98fbf17a Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 13:58:19 -0400 Subject: 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'); -- cgit v1.2.3 From 1b2a1e50ebcd62cf54b397516e991333afa5158c Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 14:21:26 -0400 Subject: 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}); -- cgit v1.2.3 From 8175f80183caa0673a946b2405feae0c9535af48 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 15:06:15 -0400 Subject: 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; } -- cgit v1.2.3 From 99cec1d23fc779ae26112dcd63d86b5f6fa4bddd Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 15:54:00 -0400 Subject: Change how formRead works The function now modifies values in-place. --- ext/bg/js/settings.js | 135 +++++++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 67 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); - - 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(); - - 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); - - 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')); +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(); + + 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); + + 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(); + + 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); -- cgit v1.2.3 From 4686a31a0a5d30a1b01e3cd2b689d1950c79c940 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 15:59:10 -0400 Subject: 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); -- cgit v1.2.3 From 05ce350792fd60c1721bff4d0fb971e2bec24818 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 16:15:18 -0400 Subject: 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); } -- cgit v1.2.3 From c4e6d7e3d18bc87a9e682349bd96fc35d7815bbc Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 17:35:33 -0400 Subject: 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); +} -- cgit v1.2.3 From cab39e89c93a9b1826dce5d6555c213fa722c82e Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 20:04:43 -0400 Subject: 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 @@ + -
- + -- cgit v1.2.3 From 36b39e2f6530bfba5019462313b29b90a2db2aec Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 8 Sep 2019 11:23:04 -0400 Subject: 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(); } -- cgit v1.2.3 From 1f8c7358cb017e0c96a454d9e9a1f53eb6d4fe15 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 8 Sep 2019 12:16:12 -0400 Subject: 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(); -- cgit v1.2.3 From 87e513dd77ab228953449d534a989c8c68f39858 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 10 Sep 2019 20:04:28 -0400 Subject: 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); -- cgit v1.2.3 From 71b700cd22f5a57a07cd2f9a6afa823793f5f95e Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 10 Sep 2019 20:49:17 -0400 Subject: 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

+
+ +
+
-- cgit v1.2.3 From 84bd9ff93b15f419ce1076b7545aeb406917f9b5 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 10 Sep 2019 20:46:30 -0400 Subject: 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); } -- cgit v1.2.3 From c38c7fbda1e2e7b320a9c5e564d3548197c6a236 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Wed, 11 Sep 2019 19:56:13 -0400 Subject: Replace profileCreateDefaultFieldTemplates with optionsFieldTemplates --- ext/bg/js/options.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 69c662e6..8b76d4c7 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -48,7 +48,7 @@ const optionsVersionUpdates = [ options.scanning.modifier = options.scanning.requireShift ? 'shift' : 'none'; }, (options) => { - const fieldTemplatesDefault = profileCreateDefaultFieldTemplates(); + const fieldTemplatesDefault = optionsFieldTemplates(); options.general.resultOutputMode = options.general.groupResults ? 'group' : 'split'; options.anki.fieldTemplates = ( (utilStringHashCode(options.anki.fieldTemplates) !== -805327496) ? @@ -58,12 +58,12 @@ const optionsVersionUpdates = [ }, (options) => { if (utilStringHashCode(options.anki.fieldTemplates) === 1285806040) { - options.anki.fieldTemplates = profileCreateDefaultFieldTemplates(); + options.anki.fieldTemplates = optionsFieldTemplates(); } }, (options) => { if (utilStringHashCode(options.anki.fieldTemplates) === -250091611) { - options.anki.fieldTemplates = profileCreateDefaultFieldTemplates(); + options.anki.fieldTemplates = optionsFieldTemplates(); } } ]; -- cgit v1.2.3 From 76aa30cebad504c3d6811029384713044d4d0428 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Wed, 11 Sep 2019 21:29:08 -0400 Subject: Add writingMode to PopupProxy* termsShow and kanjiShow --- ext/fg/js/popup-proxy-host.js | 12 ++++++------ ext/fg/js/popup-proxy.js | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index fa61aeb4..041900ed 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -43,8 +43,8 @@ class PopupProxyHost { hide: ({id}) => this.hide(id), setVisible: ({id, visible}) => this.setVisible(id, visible), containsPoint: ({id, point}) => this.containsPoint(id, point), - termsShow: ({id, elementRect, definitions, options, context}) => this.termsShow(id, elementRect, definitions, options, context), - kanjiShow: ({id, elementRect, definitions, options, context}) => this.kanjiShow(id, elementRect, definitions, options, context), + termsShow: ({id, elementRect, writingMode, definitions, options, context}) => this.termsShow(id, elementRect, writingMode, definitions, options, context), + kanjiShow: ({id, elementRect, writingMode, definitions, options, context}) => this.kanjiShow(id, elementRect, writingMode, definitions, options, context), clearAutoPlayTimer: ({id}) => this.clearAutoPlayTimer(id) }); } @@ -113,16 +113,16 @@ class PopupProxyHost { return await popup.containsPoint(point); } - async termsShow(id, elementRect, definitions, options, context) { + async termsShow(id, elementRect, writingMode, definitions, options, context) { const popup = this.getPopup(id); elementRect = this.jsonRectToDOMRect(popup, elementRect); - return await popup.termsShow(elementRect, definitions, options, context); + return await popup.termsShow(elementRect, writingMode, definitions, options, context); } - async kanjiShow(id, elementRect, definitions, options, context) { + async kanjiShow(id, elementRect, writingMode, definitions, options, context) { const popup = this.getPopup(id); elementRect = this.jsonRectToDOMRect(popup, elementRect); - return await popup.kanjiShow(elementRect, definitions, options, context); + return await popup.kanjiShow(elementRect, writingMode, definitions, options, context); } async clearAutoPlayTimer(id) { diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index f6295079..32094102 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -76,16 +76,16 @@ class PopupProxy { return await this.invokeHostApi('containsPoint', {id: this.id, point}); } - async termsShow(elementRect, definitions, options, context) { + async termsShow(elementRect, writingMode, definitions, options, context) { const id = await this.getPopupId(); elementRect = PopupProxy.DOMRectToJson(elementRect); - return await this.invokeHostApi('termsShow', {id, elementRect, definitions, options, context}); + return await this.invokeHostApi('termsShow', {id, elementRect, writingMode, definitions, options, context}); } - async kanjiShow(elementRect, definitions, options, context) { + async kanjiShow(elementRect, writingMode, definitions, options, context) { const id = await this.getPopupId(); elementRect = PopupProxy.DOMRectToJson(elementRect); - return await this.invokeHostApi('kanjiShow', {id, elementRect, definitions, options, context}); + return await this.invokeHostApi('kanjiShow', {id, elementRect, writingMode, definitions, options, context}); } async clearAutoPlayTimer() { -- cgit v1.2.3 From bab6a13bfbc00728ed41411d83aef9f1071786ff Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Wed, 11 Sep 2019 21:32:27 -0400 Subject: Fix nested popups being shown if parent is hidden --- ext/fg/js/popup-proxy-host.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 041900ed..1f14a06f 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -116,12 +116,14 @@ class PopupProxyHost { async termsShow(id, elementRect, writingMode, definitions, options, context) { const popup = this.getPopup(id); elementRect = this.jsonRectToDOMRect(popup, elementRect); + if (!PopupProxyHost.popupCanShow(popup)) { return false; } return await popup.termsShow(elementRect, writingMode, definitions, options, context); } async kanjiShow(id, elementRect, writingMode, definitions, options, context) { const popup = this.getPopup(id); elementRect = this.jsonRectToDOMRect(popup, elementRect); + if (!PopupProxyHost.popupCanShow(popup)) { return false; } return await popup.kanjiShow(elementRect, writingMode, definitions, options, context); } @@ -129,6 +131,10 @@ class PopupProxyHost { const popup = this.getPopup(id); return popup.clearAutoPlayTimer(); } + + static popupCanShow(popup) { + return popup.parent === null || popup.parent.isVisible(); + } } PopupProxyHost.instance = PopupProxyHost.create(); -- cgit v1.2.3 From 304064dae00593856c26812ea30d3d34e33ec7bc Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 11:58:22 -0400 Subject: Defer creation of communication port until required --- ext/fg/js/frontend-api-sender.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ext/fg/js/frontend-api-sender.js b/ext/fg/js/frontend-api-sender.js index a1cb02c4..2e037e62 100644 --- a/ext/fg/js/frontend-api-sender.js +++ b/ext/fg/js/frontend-api-sender.js @@ -26,9 +26,7 @@ class FrontendApiSender { this.disconnected = false; this.nextId = 0; - this.port = chrome.runtime.connect(null, {name: 'backend-api-forwarder'}); - this.port.onDisconnect.addListener(this.onDisconnect.bind(this)); - this.port.onMessage.addListener(this.onMessage.bind(this)); + this.port = null; } invoke(action, params, target) { @@ -36,6 +34,10 @@ class FrontendApiSender { return Promise.reject('Disconnected'); } + if (this.port === null) { + this.createPort(); + } + const id = `${this.nextId}`; ++this.nextId; @@ -48,6 +50,12 @@ class FrontendApiSender { }); } + createPort() { + this.port = chrome.runtime.connect(null, {name: 'backend-api-forwarder'}); + this.port.onDisconnect.addListener(this.onDisconnect.bind(this)); + this.port.onMessage.addListener(this.onMessage.bind(this)); + } + onMessage({type, id, data, senderId}) { if (senderId !== this.senderId) { return; } switch (type) { -- cgit v1.2.3 From 964de775df5b573b8ae96e4096815e1649401eb9 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 12:02:31 -0400 Subject: Validate contentWindow before focus --- ext/fg/js/popup.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 1b15977b..08c4bfcb 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -239,9 +239,12 @@ class Popup { } focusParent() { - if (this.parent && this.parent.container) { + if (this.parent !== null) { // Chrome doesn't like focusing iframe without contentWindow. - this.parent.container.contentWindow.focus(); + const contentWindow = this.parent.container.contentWindow; + if (contentWindow !== null) { + contentWindow.focus(); + } } else { // Firefox doesn't like focusing window without first blurring the iframe. // this.container.contentWindow.blur() doesn't work on Firefox for some reason. -- cgit v1.2.3 From 721248c8210c2fb386b765eb518580765091fa67 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 14:11:06 -0400 Subject: Remove mouseDownLeft and mouseDownMiddle MouseEvent.buttons can be used instead. --- ext/fg/js/frontend.js | 57 ++++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 0b60aa2b..d0bbed69 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -21,8 +21,6 @@ class Frontend { constructor(popup, ignoreNodes) { this.popup = popup; this.popupTimer = null; - this.mouseDownLeft = false; - this.mouseDownMiddle = false; this.textSourceLast = null; this.pendingLookup = false; this.options = null; @@ -61,7 +59,6 @@ class Frontend { window.addEventListener('mousemove', this.onMouseMove.bind(this)); window.addEventListener('mouseover', this.onMouseOver.bind(this)); window.addEventListener('mouseout', this.onMouseOut.bind(this)); - window.addEventListener('mouseup', this.onMouseUp.bind(this)); window.addEventListener('resize', this.onResize.bind(this)); if (this.options.scanning.touchInputEnabled) { @@ -88,26 +85,20 @@ class Frontend { onMouseMove(e) { this.popupTimerClear(); - if (!this.options.general.enable) { - return; - } - - if (this.mouseDownLeft) { - return; - } - - if (this.pendingLookup) { + if ( + this.pendingLookup || + !this.options.general.enable || + (e.buttons & 0x1) !== 0x0 // Left mouse button + ) { return; } - const mouseScan = this.mouseDownMiddle && this.options.scanning.middleMouse; - const keyScan = - this.options.scanning.modifier === 'alt' && e.altKey || - this.options.scanning.modifier === 'ctrl' && e.ctrlKey || - this.options.scanning.modifier === 'shift' && e.shiftKey || - this.options.scanning.modifier === 'none'; - - if (!keyScan && !mouseScan) { + const scanningOptions = this.options.scanning; + const scanningModifier = scanningOptions.modifier; + if (!( + Frontend.isScanningModifierPressed(scanningModifier, e) || + (scanningOptions.middleMouse && (e.buttons & 0x4) !== 0x0) // Middle mouse button + )) { return; } @@ -119,7 +110,7 @@ class Frontend { } }; - if (this.options.scanning.modifier === 'none') { + if (scanningModifier === 'none') { this.popupTimerSet(search); } else { search(); @@ -138,20 +129,6 @@ class Frontend { this.mousePosLast = {x: e.clientX, y: e.clientY}; this.popupTimerClear(); this.searchClear(); - - if (e.which === 1) { - this.mouseDownLeft = true; - } else if (e.which === 2) { - this.mouseDownMiddle = true; - } - } - - onMouseUp(e) { - if (e.which === 1) { - this.mouseDownLeft = false; - } else if (e.which === 2) { - this.mouseDownMiddle = false; - } } onMouseOut(e) { @@ -531,6 +508,16 @@ class Frontend { textSource.setEndOffset(length); } } + + static isScanningModifierPressed(scanningModifier, mouseEvent) { + switch (scanningModifier) { + case 'alt': return mouseEvent.altKey; + case 'ctrl': return mouseEvent.ctrlKey; + case 'shift': return mouseEvent.shiftKey; + case 'none': return true; + default: return false; + } + } } window.yomichan_frontend = Frontend.create(); -- cgit v1.2.3 From 89941d404cdae213ad6c609051de3d206f433beb Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 14:11:25 -0400 Subject: Remove unused variable --- ext/fg/js/frontend.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index d0bbed69..9341e492 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -126,7 +126,6 @@ class Frontend { return false; } - this.mousePosLast = {x: e.clientX, y: e.clientY}; this.popupTimerClear(); this.searchClear(); } -- cgit v1.2.3 From 6d3037f3d6548b742fa73aec7504c4384f327674 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 14:27:25 -0400 Subject: Remove destructuring from searchAt, containsPoint, docRangeFromPoint --- ext/fg/js/document.js | 2 +- ext/fg/js/frontend.js | 10 +++++----- ext/fg/js/popup-proxy-host.js | 6 +++--- ext/fg/js/popup-proxy.js | 4 ++-- ext/fg/js/popup.js | 2 +- ext/mixed/js/display.js | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index 60b1b9bd..f2459197 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -89,7 +89,7 @@ function docImposterCreate(element, isTextarea) { return [imposter, container]; } -function docRangeFromPoint({x, y}, options) { +function docRangeFromPoint(x, y, options) { const elements = document.elementsFromPoint(x, y); let imposter = null; let imposterContainer = null; diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 9341e492..a6919b37 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -104,7 +104,7 @@ class Frontend { const search = async () => { try { - await this.searchAt({x: e.clientX, y: e.clientY}, 'mouse'); + await this.searchAt(e.clientX, e.clientY, 'mouse'); } catch (e) { this.onError(e); } @@ -280,12 +280,12 @@ class Frontend { } } - async searchAt(point, type) { - if (this.pendingLookup || await this.popup.containsPoint(point)) { + async searchAt(x, y, type) { + if (this.pendingLookup || await this.popup.containsPoint(x, y)) { return; } - const textSource = docRangeFromPoint(point, this.options); + const textSource = docRangeFromPoint(x, y, this.options); let hideResults = textSource === null; let searched = false; let success = false; @@ -470,7 +470,7 @@ class Frontend { const search = async () => { try { - await this.searchAt({x, y}, type); + await this.searchAt(x, y, type); } catch (e) { this.onError(e); } diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 041900ed..47f49b8d 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -42,7 +42,7 @@ class PopupProxyHost { showOrphaned: ({id, elementRect, options}) => this.show(id, elementRect, options), hide: ({id}) => this.hide(id), setVisible: ({id, visible}) => this.setVisible(id, visible), - containsPoint: ({id, point}) => this.containsPoint(id, point), + containsPoint: ({id, x, y}) => this.containsPoint(id, x, y), termsShow: ({id, elementRect, writingMode, definitions, options, context}) => this.termsShow(id, elementRect, writingMode, definitions, options, context), kanjiShow: ({id, elementRect, writingMode, definitions, options, context}) => this.kanjiShow(id, elementRect, writingMode, definitions, options, context), clearAutoPlayTimer: ({id}) => this.clearAutoPlayTimer(id) @@ -108,9 +108,9 @@ class PopupProxyHost { return popup.setVisible(visible); } - async containsPoint(id, point) { + async containsPoint(id, x, y) { const popup = this.getPopup(id); - return await popup.containsPoint(point); + return await popup.containsPoint(x, y); } async termsShow(id, elementRect, writingMode, definitions, options, context) { diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index c3a7bff0..f04e24e0 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -69,11 +69,11 @@ class PopupProxy { return await this.invokeHostApi('setVisible', {id, visible}); } - async containsPoint(point) { + async containsPoint(x, y) { if (this.id === null) { return false; } - return await this.invokeHostApi('containsPoint', {id: this.id, point}); + return await this.invokeHostApi('containsPoint', {id: this.id, x, y}); } async termsShow(elementRect, writingMode, definitions, options, context) { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 1b15977b..1d6fa5b3 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -251,7 +251,7 @@ class Popup { } } - async containsPoint({x, y}) { + async containsPoint(x, y) { for (let popup = this; popup !== null && popup.isVisible(); popup = popup.child) { const rect = popup.container.getBoundingClientRect(); if (x >= rect.left && y >= rect.top && x < rect.right && y < rect.bottom) { diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index eca67b5e..ca1738a6 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -81,7 +81,7 @@ class Display { const {docRangeFromPoint, docSentenceExtract} = this.dependencies; const clickedElement = $(e.target); - const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}, this.options); + const textSource = docRangeFromPoint(e.clientX, e.clientY, this.options); if (textSource === null) { return false; } -- cgit v1.2.3 From 7dddcb8dca226e79453aa7734c99df1b18af4366 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 14:31:21 -0400 Subject: Rename type to cause --- ext/fg/js/frontend.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index a6919b37..31b43ad3 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -219,8 +219,8 @@ class Frontend { } } - onAfterSearch(newRange, type, searched, success) { - if (type === 'mouse') { + onAfterSearch(newRange, cause, searched, success) { + if (cause === 'mouse') { return; } @@ -230,7 +230,7 @@ class Frontend { return; } - if (type === 'touchStart' && newRange !== null) { + if (cause === 'touchStart' && newRange !== null) { this.scrollPrevent = true; } @@ -280,7 +280,7 @@ class Frontend { } } - async searchAt(x, y, type) { + async searchAt(x, y, cause) { if (this.pendingLookup || await this.popup.containsPoint(x, y)) { return; } @@ -294,7 +294,7 @@ class Frontend { if (!hideResults && (!this.textSourceLast || !this.textSourceLast.equals(textSource))) { searched = true; this.pendingLookup = true; - const focus = (type === 'mouse'); + const focus = (cause === 'mouse'); hideResults = !await this.searchTerms(textSource, focus) && !await this.searchKanji(textSource, focus); success = true; } @@ -319,7 +319,7 @@ class Frontend { } this.pendingLookup = false; - this.onAfterSearch(this.textSourceLast, type, searched, success); + this.onAfterSearch(this.textSourceLast, cause, searched, success); } } @@ -461,7 +461,7 @@ class Frontend { this.clickPrevent = value; } - searchFromTouch(x, y, type) { + searchFromTouch(x, y, cause) { this.popupTimerClear(); if (!this.options.general.enable || this.pendingLookup) { @@ -470,7 +470,7 @@ class Frontend { const search = async () => { try { - await this.searchAt(x, y, type); + await this.searchAt(x, y, cause); } catch (e) { this.onError(e); } -- cgit v1.2.3 From 8b1e4d1c6fc5f496c05bb69fe9e6b2cd12c9090b Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 14:52:03 -0400 Subject: Return only single element when deepDomScan is not enabled --- ext/fg/js/document.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index f2459197..079a5034 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -89,8 +89,18 @@ function docImposterCreate(element, isTextarea) { return [imposter, container]; } +function docElementsFromPoint(x, y, all) { + if (all) { + return document.elementsFromPoint(x, y); + } + + const e = document.elementFromPoint(x, y); + return e !== null ? [e] : []; +} + function docRangeFromPoint(x, y, options) { - const elements = document.elementsFromPoint(x, y); + const deepDomScan = options.scanning.deepDomScan; + const elements = docElementsFromPoint(x, y, deepDomScan); let imposter = null; let imposterContainer = null; if (elements.length > 0) { @@ -108,7 +118,7 @@ function docRangeFromPoint(x, y, options) { } } - const range = caretRangeFromPointExt(x, y, options.scanning.deepDomScan ? elements : []); + const range = caretRangeFromPointExt(x, y, deepDomScan ? elements : []); if (range !== null) { if (imposter !== null) { docSetImposterStyle(imposterContainer.style, 'z-index', '-2147483646'); -- cgit v1.2.3 From 0067dfeacc3ecaf1215f1b9026c500bff31761e6 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 15:29:43 -0400 Subject: Remove redundant call of popupTimerClear Also use explicit null checks rather than truthy checks. --- ext/fg/js/frontend.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 31b43ad3..c98a9a33 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -77,7 +77,7 @@ class Frontend { } onMouseOver(e) { - if (e.target === this.popup.container && this.popupTimer) { + if (e.target === this.popup.container && this.popupTimer !== null) { this.popupTimerClear(); } } @@ -269,12 +269,11 @@ class Frontend { } popupTimerSet(callback) { - this.popupTimerClear(); this.popupTimer = window.setTimeout(callback, this.options.scanning.delay); } popupTimerClear() { - if (this.popupTimer) { + if (this.popupTimer !== null) { window.clearTimeout(this.popupTimer); this.popupTimer = null; } -- cgit v1.2.3 From 6358b655aef94279133550c93a944dbe785cee44 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 16:21:41 -0400 Subject: Isolate options objects created via settings.js Prevents dead objects created by different windows. --- ext/bg/js/settings.js | 20 ++++++++++++-------- ext/bg/js/util.js | 5 +++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 7f3e5c69..3d581ba5 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -60,7 +60,7 @@ async function formRead(options) { const optionsAnkiEnableOld = options.anki.enable; options.anki.enable = $('#anki-enable').prop('checked'); - options.anki.tags = $('#card-tags').val().split(/[,; ]+/); + options.anki.tags = utilBackgroundIsolate($('#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(); @@ -70,20 +70,20 @@ async function formRead(options) { 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.terms.fields = utilBackgroundIsolate(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')); + options.anki.kanji.fields = utilBackgroundIsolate(ankiFieldsToDict($('#kanji .anki-field-value'))); } options.general.mainDictionary = $('#dict-main').val(); $('.dict-group').each((index, element) => { const dictionary = $(element); - options.dictionaries[dictionary.data('title')] = { + options.dictionaries[dictionary.data('title')] = utilBackgroundIsolate({ priority: parseInt(dictionary.find('.dict-priority').val(), 10), enabled: dictionary.find('.dict-enabled').prop('checked'), allowSecondarySearches: dictionary.find('.dict-allow-secondary-searches').prop('checked') - }; + }); }); } @@ -426,7 +426,7 @@ async function onDictionaryPurge(e) { await utilDatabasePurge(); const optionsContext = getOptionsContext(); const options = await apiOptionsGet(optionsContext); - options.dictionaries = {}; + options.dictionaries = utilBackgroundIsolate({}); options.general.mainDictionary = ''; await settingsSaveOptions(); @@ -468,7 +468,11 @@ async function onDictionaryImport(e) { 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}; + options.dictionaries[summary.title] = utilBackgroundIsolate({ + enabled: true, + priority: 0, + allowSecondarySearches: false + }); if (summary.sequenced && options.general.mainDictionary === '') { options.general.mainDictionary = summary.title; } @@ -621,7 +625,7 @@ async function onAnkiModelChanged(e) { const optionsContext = getOptionsContext(); const options = await apiOptionsGet(optionsContext); await formRead(options); - options.anki[tabId].fields = {}; + options.anki[tabId].fields = utilBackgroundIsolate({}); await settingsSaveOptions(); ankiSpinnerShow(true); diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index 79229229..73a8396f 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -26,6 +26,11 @@ function utilIsolate(data) { return JSON.parse(JSON.stringify(data)); } +function utilBackgroundIsolate(data) { + const backgroundPage = chrome.extension.getBackgroundPage(); + return backgroundPage.utilIsolate(data); +} + function utilSetEqual(setA, setB) { if (setA.size !== setB.size) { return false; -- cgit v1.2.3 From ba1875c56a89edcd896184ab6b9734ce36f70e15 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 19:59:45 -0400 Subject: Add rel="noopener" to all anchors with target="_blank" --- ext/bg/guide.html | 2 +- ext/bg/settings.html | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/bg/guide.html b/ext/bg/guide.html index 6f98d264..7ec1d8d9 100644 --- a/ext/bg/guide.html +++ b/ext/bg/guide.html @@ -15,7 +15,7 @@

Read the steps below to get up and running with Yomichan. For complete documentation, - visit the official homepage. + visit the official homepage.

    diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 577e1a1f..62539972 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -307,7 +307,7 @@

    Select a dictionary to import for use below. Please visit the Yomichan homepage to - download free dictionaries + download free dictionaries for use with this extension and to learn about importing proprietary EPWING dictionaries.

    @@ -333,7 +333,7 @@
    On Firefox and Firefox for Android, the storage information feature may be hidden behind a browser flag. - If you would like to enable this flag, open about:config and search for the + If you would like to enable this flag, open about:config and search for the dom.storageManager.enabled option. If this option has a value of false, toggling it to true may allow storage information to be calculated.
    @@ -355,9 +355,9 @@

    - Yomichan supports automatic flashcard creation for Anki, a free application + Yomichan supports automatic flashcard creation for Anki, a free application designed to help you remember. This feature requires installation of the - AnkiConnect plugin. + AnkiConnect plugin.

    @@ -473,14 +473,14 @@ countless hours that I have devoted to this extension.

    - +


 
             
         
-- cgit v1.2.3 From f8aa926f8a554edc4fe963d8a9efddf67662d3da Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 14 Sep 2019 20:01:51 -0400 Subject: Upgrade website URLs to https --- README.md | 24 ++++++++++++------------ ext/bg/settings.html | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5671e5de..40576a5a 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Import](https://foosoft.net/projects/yomichan-import) page to learn how to conve Please be aware that the non-English dictionaries contain fewer entries than their English counterparts. Even if your primary language is not English, you may consider also importing the English version for better coverage. -* **[JMdict](http://www.edrdg.org/enamdict/enamdict_doc.html)** (Japanese vocabulary) +* **[JMdict](https://www.edrdg.org/enamdict/enamdict_doc.html)** (Japanese vocabulary) * [jmdict\_dutch.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_dutch.zip) * [jmdict\_english.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_english.zip) * [jmdict\_french.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_french.zip) @@ -71,7 +71,7 @@ primary language is not English, you may consider also importing the English ver * [jmdict\_slovenian.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_slovenian.zip) * [jmdict\_spanish.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_spanish.zip) * [jmdict\_swedish.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_swedish.zip) -* **[JMnedict](http://www.edrdg.org/enamdict/enamdict_doc.html)** (Japanese names) +* **[JMnedict](https://www.edrdg.org/enamdict/enamdict_doc.html)** (Japanese names) * [jmnedict.zip](https://foosoft.net/projects/yomichan/dl/dict/jmnedict.zip) * **[KireiCake](https://kireicake.com/rikaicakes/)** (Japanese slang) * [kireicake.zip](https://foosoft.net/projects/yomichan/dl/dict/kireicake.zip) @@ -127,7 +127,7 @@ Import](https://foosoft.net/projects/yomichan-import). Please see the project pa ## Anki Integration ## -Yomichan features automatic flashcard creation for [Anki](http://ankisrs.net/), a free application designed to help you +Yomichan features automatic flashcard creation for [Anki](https://apps.ankiweb.net/), a free application designed to help you retain knowledge. This feature requires the prior installation of an Anki plugin called [AnkiConnect](https://foosoft.net/projects/anki-connect). Please see the respective project page for more information about how to set up this software. @@ -135,7 +135,7 @@ Please see the respective project page for more information about how to set up Before flashcards can be automatically created, you must configure the templates used to create term and/or Kanji notes. If you are unfamiliar with Anki deck and model management, this would be a good time to reference the [Anki -Manual](http://ankisrs.net/docs/manual.html). In short, you must specify what information should be included in the +Manual](https://apps.ankiweb.net/docs/manual.html). In short, you must specify what information should be included in the flashcards that Yomichan creates through AnkiConnect. Flashcard fields can be configured with the following steps: @@ -145,7 +145,7 @@ Flashcard fields can be configured with the following steps: 3. Select the type of template to configure by clicking on either the *Terms* or *Kanji* tabs. 4. Select the Anki deck and model to use for new creating new flashcards of this type. 5. Fill the model fields with markers corresponding to the information you wish to include (several can be used at - once). Advanced users can also configure the actual [Handlebars](http://handlebarsjs.com/) templates used to create + once). Advanced users can also configure the actual [Handlebars](https://handlebarsjs.com/) templates used to create the flashcard contents (this is strictly optional). #### Markers for Term Cards #### @@ -243,7 +243,7 @@ following basic guidelines when creating pull requests: ### Templates ### -Yomichan uses [Handlebars](http://handlebarsjs.com/) templates for user interface generation. The source templates are +Yomichan uses [Handlebars](https://handlebarsjs.com/) templates for user interface generation. The source templates are found in the `tmpl` directory and the compiled version is stored in the `ext/bg/js/templates.js` file. If you modify the source templates, you will need to also recompile them. If you are developing on Linux or Mac OS X, you can use the included `build_tmpl.sh` and `build_tmpl_auto.sh` shell scripts to do this for you @@ -255,13 +255,13 @@ tmpl/*.html -f ext/bg/js/templates.js` from the project's base directory to comp Yomichan uses several third-party libraries to function. Below are links to homepages and snapshots of the exact versions packaged. -* Bootstrap Toggle: [homepage](http://www.bootstraptoggle.com/) - [snapshot](https://github.com/minhur/bootstrap-toggle/archive/b76c094.zip) -* Bootstrap: [homepage](http://getbootstrap.com/) - [snapshot](https://github.com/twbs/bootstrap/releases/download/v3.3.7/bootstrap-3.3.7-dist.zip) -* Dexie: [homepage](http://dexie.org/) - [snapshot](https://github.com/dfahlander/Dexie.js/archive/v2.0.0-beta.10.zip) -* Handlebars: [homepage](http://handlebarsjs.com/) - [snapshot](http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-714a4c4.js) +* Bootstrap Toggle: [homepage](https://www.bootstraptoggle.com/) - [snapshot](https://github.com/minhur/bootstrap-toggle/archive/b76c094.zip) +* Bootstrap: [homepage](https://getbootstrap.com/) - [snapshot](https://github.com/twbs/bootstrap/releases/download/v3.3.7/bootstrap-3.3.7-dist.zip) +* Dexie: [homepage](https://dexie.org/) - [snapshot](https://github.com/dfahlander/Dexie.js/archive/v2.0.0-beta.10.zip) +* Handlebars: [homepage](https://handlebarsjs.com/) - [snapshot](http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-714a4c4.js) * JQuery: [homepage](https://blog.jquery.com/) - [snapshot](https://code.jquery.com/jquery-3.2.1.min.js) -* JSZip: [homepage](http://stuk.github.io/jszip/) - [snapshot](https://raw.githubusercontent.com/Stuk/jszip/de7f52fbcba485737bef7923a83f0fad92d9f5bc/dist/jszip.min.js) -* WanaKana: [homepage](http://wanakana.com/) - [snapshot](https://raw.githubusercontent.com/WaniKani/WanaKana/7c4a052/gh-pages/assets/js/wanakana.min.js) +* JSZip: [homepage](https://stuk.github.io/jszip/) - [snapshot](https://raw.githubusercontent.com/Stuk/jszip/de7f52fbcba485737bef7923a83f0fad92d9f5bc/dist/jszip.min.js) +* WanaKana: [homepage](https://wanakana.com/) - [snapshot](https://raw.githubusercontent.com/WaniKani/WanaKana/7c4a052/gh-pages/assets/js/wanakana.min.js) ## Frequently Asked Questions ## diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 62539972..7df47980 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -355,7 +355,7 @@

- Yomichan supports automatic flashcard creation for Anki, a free application + Yomichan supports automatic flashcard creation for Anki, a free application designed to help you remember. This feature requires installation of the AnkiConnect plugin.

@@ -450,7 +450,7 @@

- Fields are formatted using the Handlebars.js template rendering + Fields are formatted using the Handlebars.js template rendering engine. Advanced users can modify these templates for ultimate control of what information gets included in their Anki cards. If you encounter problems with your changes you can always reset to default template settings. -- cgit v1.2.3 From c8171f5ec7612f0ba147d7e0887cd8c30a527827 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Sep 2019 19:50:58 -0400 Subject: Add preliminary support for profiles --- ext/bg/js/backend.js | 10 +++++- ext/bg/js/options.js | 91 +++++++++++++++++++++++++++++++++++++++++++-------- ext/bg/js/settings.js | 2 +- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 9a300d62..3839da39 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -165,7 +165,15 @@ class Backend { } getOptionsSync(optionsContext) { - return this.options; + return this.getProfileSync(optionsContext).options; + } + + getProfileSync(optionsContext) { + const profiles = this.options.profiles; + if (typeof optionsContext.index === 'number') { + return profiles[optionsContext.index]; + } + return this.options.profiles[this.options.profileCurrent]; } setExtensionBadgeBackgroundColor(color) { diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 5f04ec31..3dce5221 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -17,7 +17,11 @@ */ -function optionsApplyUpdates(options, updates) { +/* + * Generic options functions + */ + +function optionsGenericApplyUpdates(options, updates) { const targetVersion = updates.length; const currentVersion = options.version; if (typeof currentVersion === 'number' && Number.isFinite(currentVersion)) { @@ -33,7 +37,12 @@ function optionsApplyUpdates(options, updates) { return options; } -const optionsVersionUpdates = [ + +/* + * Per-profile options + */ + +const profileOptionsVersionUpdates = [ null, null, null, @@ -48,7 +57,7 @@ const optionsVersionUpdates = [ options.scanning.modifier = options.scanning.requireShift ? 'shift' : 'none'; }, (options) => { - const fieldTemplatesDefault = optionsFieldTemplates(); + const fieldTemplatesDefault = profileOptionsGetDefaultFieldTemplates(); options.general.resultOutputMode = options.general.groupResults ? 'group' : 'split'; options.anki.fieldTemplates = ( (utilStringHashCode(options.anki.fieldTemplates) !== -805327496) ? @@ -58,17 +67,17 @@ const optionsVersionUpdates = [ }, (options) => { if (utilStringHashCode(options.anki.fieldTemplates) === 1285806040) { - options.anki.fieldTemplates = optionsFieldTemplates(); + options.anki.fieldTemplates = profileOptionsGetDefaultFieldTemplates(); } }, (options) => { if (utilStringHashCode(options.anki.fieldTemplates) === -250091611) { - options.anki.fieldTemplates = optionsFieldTemplates(); + options.anki.fieldTemplates = profileOptionsGetDefaultFieldTemplates(); } } ]; -function optionsFieldTemplates() { +function profileOptionsGetDefaultFieldTemplates() { return ` {{#*inline "glossary-single"}} {{~#unless brief~}} @@ -234,7 +243,7 @@ function optionsFieldTemplates() { `.trim(); } -function optionsCreateDefaults() { +function profileOptionsCreateDefaults() { return { general: { enable: true, @@ -286,13 +295,13 @@ function optionsCreateDefaults() { screenshot: {format: 'png', quality: 92}, terms: {deck: '', model: '', fields: {}}, kanji: {deck: '', model: '', fields: {}}, - fieldTemplates: optionsFieldTemplates() + fieldTemplates: profileOptionsGetDefaultFieldTemplates() } }; } -function optionsSetDefaults(options) { - const defaults = optionsCreateDefaults(); +function profileOptionsSetDefaults(options) { + const defaults = profileOptionsCreateDefaults(); const combine = (target, source) => { for (const key in source) { @@ -312,9 +321,59 @@ function optionsSetDefaults(options) { return options; } -function optionsVersion(options) { - optionsSetDefaults(options); - return optionsApplyUpdates(options, optionsVersionUpdates); +function profileOptionsUpdateVersion(options) { + profileOptionsSetDefaults(options); + return optionsGenericApplyUpdates(options, profileOptionsVersionUpdates); +} + + +/* + * Global options + */ + +const optionsVersionUpdates = []; + +function optionsUpdateVersion(options, defaultProfileOptions) { + // Ensure profiles is an array + if (!Array.isArray(options.profiles)) { + options.profiles = []; + } + + // Remove invalid + const profiles = options.profiles; + for (let i = profiles.length - 1; i >= 0; --i) { + if (!utilIsObject(profiles[i])) { + profiles.splice(i, 1); + } + } + + // Require at least one profile + if (profiles.length === 0) { + profiles.push({ + name: 'Default', + options: defaultProfileOptions + }); + } + + // Ensure profileCurrent is valid + const profileCurrent = options.profileCurrent; + if (!( + typeof profileCurrent === 'number' && + Number.isFinite(profileCurrent) && + Math.floor(profileCurrent) === profileCurrent && + profileCurrent >= 0 && + profileCurrent < profiles.length + )) { + options.profileCurrent = 0; + } + + // Update profile options + for (const profile of profiles) { + profile.options = profileOptionsUpdateVersion(profile.options); + } + + // Generic updates + return optionsGenericApplyUpdates(options, optionsVersionUpdates); } function optionsLoad() { @@ -338,7 +397,11 @@ function optionsLoad() { }).catch(() => { return {}; }).then(options => { - return optionsVersion(options); + return ( + Array.isArray(options.profiles) ? + optionsUpdateVersion(options, {}) : + optionsUpdateVersion({}, options) + ); }); } diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 3d581ba5..88929c49 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -643,7 +643,7 @@ async function onAnkiFieldTemplatesReset(e) { e.preventDefault(); const optionsContext = getOptionsContext(); const options = await apiOptionsGet(optionsContext); - const fieldTemplates = optionsFieldTemplates(); + const fieldTemplates = profileOptionsGetDefaultFieldTemplates(); options.anki.fieldTemplates = fieldTemplates; $('#field-templates').val(fieldTemplates); await settingsSaveOptions(); -- cgit v1.2.3 From 6c571bf8284c44bf01fe20c267fe72cce235b90d Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 8 Sep 2019 13:16:05 -0400 Subject: Add UI for profiles --- ext/bg/js/api.js | 6 +- ext/bg/js/settings-profiles.js | 201 +++++++++++++++++++++++++++++++++++++++++ ext/bg/js/settings.js | 11 +-- ext/bg/settings.html | 53 +++++++++++ 4 files changed, 260 insertions(+), 11 deletions(-) create mode 100644 ext/bg/js/settings-profiles.js diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 81772d08..f32b984f 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -21,9 +21,13 @@ function apiOptionsGet(optionsContext) { return utilBackend().getOptions(optionsContext); } +function apiOptionsGetFull() { + return utilBackend().getFullOptions(); +} + async function apiOptionsSave(source) { const backend = utilBackend(); - const options = await backend.getFullOptions(); + const options = await apiOptionsGetFull(); await optionsSave(options); backend.onOptionsUpdated(source); } diff --git a/ext/bg/js/settings-profiles.js b/ext/bg/js/settings-profiles.js new file mode 100644 index 00000000..dca452d2 --- /dev/null +++ b/ext/bg/js/settings-profiles.js @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2019 Alex Yatskov + * Author: Alex Yatskov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +let currentProfileIndex = 0; + +function getOptionsContext() { + return { + index: currentProfileIndex + }; +} + + +async function profileOptionsSetup() { + const optionsFull = await apiOptionsGetFull(); + currentProfileIndex = optionsFull.profileCurrent; + + profileOptionsSetupEventListeners(); + await profileOptionsUpdateTarget(optionsFull); +} + +function profileOptionsSetupEventListeners() { + $('#profile-target').change(utilAsync(onTargetProfileChanged)); + $('#profile-name').change(onProfileNameChanged); + $('#profile-add').click(utilAsync(onProfileAdd)); + $('#profile-remove').click(utilAsync(onProfileRemove)); + $('#profile-remove-confirm').click(utilAsync(onProfileRemoveConfirm)); + $('.profile-form').find('input, select, textarea').not('.profile-form-manual').change(utilAsync(onProfileOptionsChanged)); +} + +function tryGetIntegerValue(selector, min, max) { + const value = parseInt($(selector).val(), 10); + return ( + typeof value === 'number' && + Number.isFinite(value) && + Math.floor(value) === value && + value >= min && + value < max + ) ? value : null; +} + +async function profileFormRead(optionsFull) { + const profile = optionsFull.profiles[currentProfileIndex]; + + // Current profile + const index = tryGetIntegerValue('#profile-active', 0, optionsFull.profiles.length); + if (index !== null) { + optionsFull.profileCurrent = index; + } + + // Profile name + profile.name = $('#profile-name').val(); +} + +async function profileFormWrite(optionsFull) { + const profile = optionsFull.profiles[currentProfileIndex]; + + profileOptionsPopulateSelect($('#profile-active'), optionsFull.profiles, optionsFull.profileCurrent); + profileOptionsPopulateSelect($('#profile-target'), optionsFull.profiles, currentProfileIndex); + $('#profile-remove').prop('disabled', optionsFull.profiles.length <= 1); + + $('#profile-name').val(profile.name); +} + +function profileOptionsPopulateSelect(select, profiles, currentValue) { + select.empty(); + + for (let i = 0; i < profiles.length; ++i) { + const profile = profiles[i]; + select.append($(``)); + } + + select.val(`${currentValue}`); +} + +async function profileOptionsUpdateTarget(optionsFull) { + profileFormWrite(optionsFull); + + const optionsContext = getOptionsContext(); + const options = await apiOptionsGet(optionsContext); + await formWrite(options); +} + +function profileOptionsCreateCopyName(name, profiles, maxUniqueAttempts) { + let space, index, prefix, suffix; + const match = /^([\w\W]*\(Copy)((\s+)(\d+))?(\)\s*)$/.exec(name); + if (match === null) { + prefix = `${name} (Copy`; + space = ''; + index = ''; + suffix = ')'; + } else { + prefix = match[1]; + suffix = match[5]; + if (typeof match[2] === 'string') { + space = match[3]; + index = parseInt(match[4], 10) + 1; + } else { + space = ' '; + index = 2; + } + } + + let i = 0; + while (true) { + const newName = `${prefix}${space}${index}${suffix}`; + if (i++ >= maxUniqueAttempts || profiles.findIndex(profile => profile.name === newName) < 0) { + return newName; + } + if (typeof index !== 'number') { + index = 2; + space = ' '; + } else { + ++index; + } + } +} + +async function onProfileOptionsChanged(e) { + if (!e.originalEvent && !e.isTrigger) { + return; + } + + const optionsFull = await apiOptionsGetFull(); + await profileFormRead(optionsFull); + await apiOptionsSave(); +} + +async function onTargetProfileChanged() { + const optionsFull = await apiOptionsGetFull(); + const index = tryGetIntegerValue('#profile-target', 0, optionsFull.profiles.length); + if (index === null || currentProfileIndex === index) { + return; + } + + currentProfileIndex = index; + + await profileOptionsUpdateTarget(optionsFull); +} + +async function onProfileAdd() { + const optionsFull = await apiOptionsGetFull(); + const profile = utilIsolate(optionsFull.profiles[currentProfileIndex]); + profile.name = profileOptionsCreateCopyName(profile.name, optionsFull.profiles, 100); + optionsFull.profiles.push(profile); + currentProfileIndex = optionsFull.profiles.length - 1; + await profileOptionsUpdateTarget(optionsFull); + await apiOptionsSave(); +} + +async function onProfileRemove() { + const optionsFull = await apiOptionsGetFull(); + if (optionsFull.profiles.length <= 1) { + return; + } + + const profile = optionsFull.profiles[currentProfileIndex]; + + $('#profile-remove-modal-profile-name').text(profile.name); + $('#profile-remove-modal').modal('show'); +} + +async function onProfileRemoveConfirm() { + $('#profile-remove-modal').modal('hide'); + + const optionsFull = await apiOptionsGetFull(); + if (optionsFull.profiles.length <= 1) { + return; + } + + optionsFull.profiles.splice(currentProfileIndex, 1); + + if (currentProfileIndex >= optionsFull.profiles.length) { + --currentProfileIndex; + } + + if (optionsFull.profileCurrent >= optionsFull.profiles.length) { + optionsFull.profileCurrent = optionsFull.profiles.length - 1; + } + + await profileOptionsUpdateTarget(optionsFull); + await apiOptionsSave(); +} + +function onProfileNameChanged() { + $('#profile-active, #profile-target').find(`[value="${currentProfileIndex}"]`).text(this.value); +} diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 88929c49..b6434843 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -16,12 +16,6 @@ * along with this program. If not, see . */ -function getOptionsContext() { - return { - depth: 0 - }; -} - async function formRead(options) { options.general.enable = $('#enable').prop('checked'); options.general.showGuide = $('#show-usage-guide').prop('checked'); @@ -239,11 +233,8 @@ async function onFormOptionsChanged(e) { } async function onReady() { - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - formSetupEventListeners(); - await formWrite(options); + await profileOptionsSetup(); storageInfoInitialize(); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 7df47980..1a1bc2ed 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -67,6 +67,58 @@

+
+

Profiles

+ +

+ Profiles allow you to create multiple configurations and quickly switch between them. +

+ +
+ + +
+ +
+ +
+
+ + + + +
+ +
+ +
+
+
+ +
+ + +
+ + +
+

General Options

@@ -498,6 +550,7 @@ + -- cgit v1.2.3 From c002e1bbd21341ae758819da59b7b65b99b97f4f Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 8 Sep 2019 13:27:32 -0400 Subject: Update dictionaries for all profile options --- ext/bg/js/settings.js | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index b6434843..cb3ddd4e 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -16,6 +16,11 @@ * along with this program. If not, see . */ +async function getOptionsArray() { + const optionsFull = await apiOptionsGetFull(); + return optionsFull.profiles.map(profile => profile.options); +} + async function formRead(options) { options.general.enable = $('#enable').prop('checked'); options.general.showGuide = $('#show-usage-guide').prop('checked'); @@ -415,12 +420,14 @@ async function onDictionaryPurge(e) { dictionarySpinnerShow(true); await utilDatabasePurge(); - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - options.dictionaries = utilBackgroundIsolate({}); - options.general.mainDictionary = ''; + for (const options of await getOptionsArray()) { + options.dictionaries = utilBackgroundIsolate({}); + options.general.mainDictionary = ''; + } await settingsSaveOptions(); + const optionsContext = getOptionsContext(); + const options = await apiOptionsGet(optionsContext); await dictionaryGroupsPopulate(options); await formMainDictionaryOptionsPopulate(options); } catch (e) { @@ -457,24 +464,25 @@ async function onDictionaryImport(e) { const exceptions = []; const summary = await utilDatabaseImport(e.target.files[0], updateProgress, exceptions); - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - options.dictionaries[summary.title] = utilBackgroundIsolate({ - enabled: true, - priority: 0, - allowSecondarySearches: false - }); - if (summary.sequenced && options.general.mainDictionary === '') { - options.general.mainDictionary = summary.title; + for (const options of await getOptionsArray()) { + options.dictionaries[summary.title] = utilBackgroundIsolate({ + enabled: true, + priority: 0, + allowSecondarySearches: false + }); + if (summary.sequenced && options.general.mainDictionary === '') { + options.general.mainDictionary = summary.title; + } } + await settingsSaveOptions(); if (exceptions.length > 0) { exceptions.push(`Dictionary may not have been imported properly: ${exceptions.length} error${exceptions.length === 1 ? '' : 's'} reported.`); dictionaryErrorsShow(exceptions); } - await settingsSaveOptions(); - + const optionsContext = getOptionsContext(); + const options = await apiOptionsGet(optionsContext); await dictionaryGroupsPopulate(options); await formMainDictionaryOptionsPopulate(options); } catch (e) { -- cgit v1.2.3 From 1f77506f43607c6d3c9c87fbf82b993d87039526 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 10 Sep 2019 21:20:03 -0400 Subject: Implement profile copy --- ext/bg/js/settings-profiles.js | 39 ++++++++++++++++++++++++++++++++++++--- ext/bg/settings.html | 19 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/ext/bg/js/settings-profiles.js b/ext/bg/js/settings-profiles.js index dca452d2..fa06203e 100644 --- a/ext/bg/js/settings-profiles.js +++ b/ext/bg/js/settings-profiles.js @@ -39,6 +39,8 @@ function profileOptionsSetupEventListeners() { $('#profile-add').click(utilAsync(onProfileAdd)); $('#profile-remove').click(utilAsync(onProfileRemove)); $('#profile-remove-confirm').click(utilAsync(onProfileRemoveConfirm)); + $('#profile-copy').click(utilAsync(onProfileCopy)); + $('#profile-copy-confirm').click(utilAsync(onProfileCopyConfirm)); $('.profile-form').find('input, select, textarea').not('.profile-form-manual').change(utilAsync(onProfileOptionsChanged)); } @@ -69,17 +71,22 @@ async function profileFormRead(optionsFull) { async function profileFormWrite(optionsFull) { const profile = optionsFull.profiles[currentProfileIndex]; - profileOptionsPopulateSelect($('#profile-active'), optionsFull.profiles, optionsFull.profileCurrent); - profileOptionsPopulateSelect($('#profile-target'), optionsFull.profiles, currentProfileIndex); + profileOptionsPopulateSelect($('#profile-active'), optionsFull.profiles, optionsFull.profileCurrent, null); + profileOptionsPopulateSelect($('#profile-target'), optionsFull.profiles, currentProfileIndex, null); $('#profile-remove').prop('disabled', optionsFull.profiles.length <= 1); + $('#profile-copy').prop('disabled', optionsFull.profiles.length <= 1); $('#profile-name').val(profile.name); } -function profileOptionsPopulateSelect(select, profiles, currentValue) { +function profileOptionsPopulateSelect(select, profiles, currentValue, ignoreIndices) { select.empty(); + for (let i = 0; i < profiles.length; ++i) { + if (ignoreIndices !== null && ignoreIndices.indexOf(i) >= 0) { + continue; + } const profile = profiles[i]; select.append($(``)); } @@ -199,3 +206,29 @@ async function onProfileRemoveConfirm() { function onProfileNameChanged() { $('#profile-active, #profile-target').find(`[value="${currentProfileIndex}"]`).text(this.value); } + +async function onProfileCopy() { + const optionsFull = await apiOptionsGetFull(); + if (optionsFull.profiles.length <= 1) { + return; + } + + profileOptionsPopulateSelect($('#profile-copy-source'), optionsFull.profiles, currentProfileIndex === 0 ? 1 : 0, [currentProfileIndex]); + $('#profile-copy-modal').modal('show'); +} + +async function onProfileCopyConfirm() { + $('#profile-copy-modal').modal('hide'); + + const optionsFull = await apiOptionsGetFull(); + const index = tryGetIntegerValue('#profile-copy-source', 0, optionsFull.profiles.length); + if (index === null || index === currentProfileIndex) { + return; + } + + const profile = utilIsolate(optionsFull.profiles[index].options); + optionsFull.profiles[currentProfileIndex].options = profile; + + await profileOptionsUpdateTarget(optionsFull); + await settingsSaveOptions(); +} diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 1a1bc2ed..c0489894 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -100,6 +100,25 @@
+ +