From 51e17b35e3a855c0db6c4be94a8cb416b14c8ad7 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 11 Apr 2020 15:21:43 -0400 Subject: Convert some util* functions into api* functions --- ext/bg/js/settings/anki.js | 10 +++++----- ext/bg/js/settings/dictionaries.js | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/settings/anki.js b/ext/bg/js/settings/anki.js index b32a9517..ff1277ed 100644 --- a/ext/bg/js/settings/anki.js +++ b/ext/bg/js/settings/anki.js @@ -16,13 +16,13 @@ */ /* global + * apiGetAnkiDeckNames + * apiGetAnkiModelFieldNames + * apiGetAnkiModelNames * getOptionsContext * getOptionsMutable * onFormOptionsChanged * settingsSaveOptions - * utilAnkiGetDeckNames - * utilAnkiGetModelFieldNames - * utilAnkiGetModelNames * utilBackgroundIsolate */ @@ -107,7 +107,7 @@ async function _ankiDeckAndModelPopulate(options) { const kanjiModel = {value: options.anki.kanji.model, selector: '#anki-kanji-model'}; try { _ankiSpinnerShow(true); - const [deckNames, modelNames] = await Promise.all([utilAnkiGetDeckNames(), utilAnkiGetModelNames()]); + const [deckNames, modelNames] = await Promise.all([apiGetAnkiDeckNames(), apiGetAnkiModelNames()]); deckNames.sort(); modelNames.sort(); termsDeck.values = deckNames; @@ -180,7 +180,7 @@ async function _onAnkiModelChanged(e) { let fieldNames; try { const modelName = node.value; - fieldNames = await utilAnkiGetModelFieldNames(modelName); + fieldNames = await apiGetAnkiModelFieldNames(modelName); _ankiSetError(null); } catch (error) { _ankiSetError(error); diff --git a/ext/bg/js/settings/dictionaries.js b/ext/bg/js/settings/dictionaries.js index 1a6d452b..7eed4273 100644 --- a/ext/bg/js/settings/dictionaries.js +++ b/ext/bg/js/settings/dictionaries.js @@ -17,8 +17,11 @@ /* global * PageExitPrevention + * apiGetDictionaryCounts + * apiGetDictionaryInfo * apiOptionsGet * apiOptionsGetFull + * apiPurgeDatabase * getOptionsContext * getOptionsFullMutable * getOptionsMutable @@ -27,10 +30,7 @@ * storageUpdateStats * utilBackgroundIsolate * utilDatabaseDeleteDictionary - * utilDatabaseGetDictionaryCounts - * utilDatabaseGetDictionaryInfo * utilDatabaseImport - * utilDatabasePurge */ let dictionaryUI = null; @@ -431,7 +431,7 @@ async function onDictionaryOptionsChanged() { async function onDatabaseUpdated() { try { - const dictionaries = await utilDatabaseGetDictionaryInfo(); + const dictionaries = await apiGetDictionaryInfo(); dictionaryUI.setDictionaries(dictionaries); document.querySelector('#dict-warning').hidden = (dictionaries.length > 0); @@ -439,7 +439,7 @@ async function onDatabaseUpdated() { updateMainDictionarySelectOptions(dictionaries); await updateMainDictionarySelectValue(); - const {counts, total} = await utilDatabaseGetDictionaryCounts(dictionaries.map((v) => v.title), true); + const {counts, total} = await apiGetDictionaryCounts(dictionaries.map((v) => v.title), true); dictionaryUI.setCounts(counts, total); } catch (e) { dictionaryErrorsShow([e]); @@ -618,7 +618,7 @@ async function onDictionaryPurge(e) { dictionaryErrorsShow(null); dictionarySpinnerShow(true); - await utilDatabasePurge(); + await apiPurgeDatabase(); for (const {options} of toIterable((await getOptionsFullMutable()).profiles)) { options.dictionaries = utilBackgroundIsolate({}); options.general.mainDictionary = ''; -- cgit v1.2.3 From 7fc3882607f48bb9371649ceacddf2fe278282d2 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 10 Apr 2020 13:44:31 -0400 Subject: Update the parameters passed to various audio-related functions --- ext/bg/js/anki-note-builder.js | 4 ++-- ext/bg/js/audio-uri-builder.js | 26 ++++++++++++-------------- ext/bg/js/backend.js | 22 +++++++--------------- ext/bg/js/settings/audio.js | 7 +------ ext/mixed/js/api.js | 4 ++-- ext/mixed/js/audio-system.js | 21 ++++++++++++--------- ext/mixed/js/display.js | 17 +++++++++-------- 7 files changed, 45 insertions(+), 56 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/anki-note-builder.js b/ext/bg/js/anki-note-builder.js index 700d8237..9bab095d 100644 --- a/ext/bg/js/anki-note-builder.js +++ b/ext/bg/js/anki-note-builder.js @@ -85,14 +85,14 @@ class AnkiNoteBuilder { }); } - async injectAudio(definition, fields, sources, optionsContext) { + async injectAudio(definition, fields, sources, details) { if (!this._containsMarker(fields, 'audio')) { return; } try { const expressions = definition.expressions; const audioSourceDefinition = Array.isArray(expressions) ? expressions[0] : definition; - const {uri} = await this._audioSystem.getDefinitionAudio(audioSourceDefinition, sources, {tts: false, optionsContext}); + const {uri} = await this._audioSystem.getDefinitionAudio(audioSourceDefinition, sources, details); const filename = this._createInjectedAudioFileName(audioSourceDefinition); if (filename !== null) { definition.audio = {url: uri, filename}; diff --git a/ext/bg/js/audio-uri-builder.js b/ext/bg/js/audio-uri-builder.js index dfd195d8..27e97680 100644 --- a/ext/bg/js/audio-uri-builder.js +++ b/ext/bg/js/audio-uri-builder.js @@ -49,11 +49,11 @@ class AudioUriBuilder { return url; } - async getUri(definition, source, options) { + async getUri(definition, source, details) { const handler = this._getUrlHandlers.get(source); if (typeof handler === 'function') { try { - return await handler(definition, options); + return await handler(definition, details); } catch (e) { // NOP } @@ -132,26 +132,24 @@ class AudioUriBuilder { throw new Error('Failed to find audio URL'); } - async _getUriTextToSpeech(definition, options) { - const voiceURI = options.audio.textToSpeechVoice; - if (!voiceURI) { + async _getUriTextToSpeech(definition, {textToSpeechVoice}) { + if (!textToSpeechVoice) { throw new Error('No voice'); } - - return `tts:?text=${encodeURIComponent(definition.expression)}&voice=${encodeURIComponent(voiceURI)}`; + return `tts:?text=${encodeURIComponent(definition.expression)}&voice=${encodeURIComponent(textToSpeechVoice)}`; } - async _getUriTextToSpeechReading(definition, options) { - const voiceURI = options.audio.textToSpeechVoice; - if (!voiceURI) { + async _getUriTextToSpeechReading(definition, {textToSpeechVoice}) { + if (!textToSpeechVoice) { throw new Error('No voice'); } - - return `tts:?text=${encodeURIComponent(definition.reading || definition.expression)}&voice=${encodeURIComponent(voiceURI)}`; + return `tts:?text=${encodeURIComponent(definition.reading || definition.expression)}&voice=${encodeURIComponent(textToSpeechVoice)}`; } - async _getUriCustom(definition, options) { - const customSourceUrl = options.audio.customSourceUrl; + async _getUriCustom(definition, {customSourceUrl}) { + if (typeof customSourceUrl !== 'string') { + throw new Error('No custom URL defined'); + } return customSourceUrl.replace(/\{([^}]*)\}/g, (m0, m1) => (hasOwn(definition, m1) ? `${definition[m1]}` : m0)); } } diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index a1b788df..79402e67 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -51,8 +51,10 @@ class Backend { this.options = null; this.optionsSchema = null; this.defaultAnkiFieldTemplates = null; - this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)}); this.audioUriBuilder = new AudioUriBuilder(); + this.audioSystem = new AudioSystem({ + audioUriBuilder: this.audioUriBuilder + }); this.ankiNoteBuilder = new AnkiNoteBuilder({ anki: this.anki, audioSystem: this.audioSystem, @@ -494,11 +496,12 @@ class Backend { const templates = this.defaultAnkiFieldTemplates; if (mode !== 'kanji') { + const {customSourceUrl} = options.audio; await this.ankiNoteBuilder.injectAudio( definition, options.anki.terms.fields, options.audio.sources, - optionsContext + {textToSpeechVoice: null, customSourceUrl} ); } @@ -573,9 +576,8 @@ class Backend { return this._runCommand(command, params); } - async _onApiAudioGetUri({definition, source, optionsContext}) { - const options = this.getOptions(optionsContext); - return await this.audioUriBuilder.getUri(definition, source, options); + async _onApiAudioGetUri({definition, source, details}) { + return await this.audioUriBuilder.getUri(definition, source, details); } _onApiScreenshotGet({options}, sender) { @@ -861,16 +863,6 @@ class Backend { } } - async _getAudioUri(definition, source, details) { - let optionsContext = (typeof details === 'object' && details !== null ? details.optionsContext : null); - if (!(typeof optionsContext === 'object' && optionsContext !== null)) { - optionsContext = this.optionsContext; - } - - const options = this.getOptions(optionsContext); - return await this.audioUriBuilder.getUri(definition, source, options); - } - async _renderTemplate(template, data) { return handlebarsRenderDynamic(template, data); } diff --git a/ext/bg/js/settings/audio.js b/ext/bg/js/settings/audio.js index 3c6e126c..e9aa72e1 100644 --- a/ext/bg/js/settings/audio.js +++ b/ext/bg/js/settings/audio.js @@ -28,12 +28,7 @@ let audioSourceUI = null; let audioSystem = null; async function audioSettingsInitialize() { - audioSystem = new AudioSystem({ - getAudioUri: async (definition, source) => { - const optionsContext = getOptionsContext(); - return await apiAudioGetUri(definition, source, optionsContext); - } - }); + audioSystem = new AudioSystem({audioUriBuilder: null}); const optionsContext = getOptionsContext(); const options = await getOptionsMutable(optionsContext); diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index 7080d93a..c97dc687 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -64,8 +64,8 @@ function apiTemplateRender(template, data) { return _apiInvoke('templateRender', {data, template}); } -function apiAudioGetUri(definition, source, optionsContext) { - return _apiInvoke('audioGetUri', {definition, source, optionsContext}); +function apiAudioGetUri(definition, source, details) { + return _apiInvoke('audioGetUri', {definition, source, details}); } function apiCommandExec(command, params) { diff --git a/ext/mixed/js/audio-system.js b/ext/mixed/js/audio-system.js index 45b733fc..574ad3dc 100644 --- a/ext/mixed/js/audio-system.js +++ b/ext/mixed/js/audio-system.js @@ -66,10 +66,10 @@ class TextToSpeechAudio { } class AudioSystem { - constructor({getAudioUri}) { + constructor({audioUriBuilder}) { this._cache = new Map(); this._cacheSizeMaximum = 32; - this._getAudioUri = getAudioUri; + this._audioUriBuilder = audioUriBuilder; if (typeof speechSynthesis !== 'undefined') { // speechSynthesis.getVoices() will not be populated unless some API call is made. @@ -90,7 +90,7 @@ class AudioSystem { if (uri === null) { continue; } try { - const audio = await this._createAudio(uri, details); + const audio = await this._createAudio(uri); this._cacheCheck(); this._cache.set(key, {audio, uri, source}); return {audio, uri, source}; @@ -114,20 +114,23 @@ class AudioSystem { // NOP } - async _createAudio(uri, details) { + async _createAudio(uri) { const ttsParameters = this._getTextToSpeechParameters(uri); if (ttsParameters !== null) { - if (typeof details === 'object' && details !== null) { - if (details.tts === false) { - throw new Error('Text-to-speech not permitted'); - } - } return this.createTextToSpeechAudio(ttsParameters); } return await this._createAudioFromUrl(uri); } + _getAudioUri(definition, source, details) { + return ( + this._audioUriBuilder !== null ? + this._audioUriBuilder.getUri(definition, source, details) : + null + ); + } + _createAudioFromUrl(url) { return new Promise((resolve, reject) => { const audio = new Audio(url); diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 63687dc2..7f3ba859 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -45,7 +45,13 @@ class Display { this.index = 0; this.audioPlaying = null; this.audioFallback = null; - this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)}); + this.audioSystem = new AudioSystem({ + audioUriBuilder: { + async getUri(definition, source, details) { + return await apiAudioGetUri(definition, source, details); + } + } + }); this.styleNode = null; this.eventListeners = new EventListenerCollection(); @@ -789,10 +795,10 @@ class Display { this.audioPlaying = null; } - const sources = this.options.audio.sources; let audio, source, info; try { - ({audio, source} = await this.audioSystem.getDefinitionAudio(expression, sources)); + const {sources, textToSpeechVoice, customSourceUrl} = this.options.audio; + ({audio, source} = await this.audioSystem.getDefinitionAudio(expression, sources, {textToSpeechVoice, customSourceUrl})); info = `From source ${1 + sources.indexOf(source)}: ${source}`; } catch (e) { if (this.audioFallback === null) { @@ -947,9 +953,4 @@ class Display { } }; } - - async _getAudioUri(definition, source) { - const optionsContext = this.getOptionsContext(); - return await apiAudioGetUri(definition, source, optionsContext); - } } -- cgit v1.2.3 From 823c026533dcd758c2a93038fa526978a5fa9cc3 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 10 Apr 2020 13:51:47 -0400 Subject: Remove de/structuring from public API --- ext/bg/js/settings/audio.js | 2 +- ext/mixed/js/audio-system.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/settings/audio.js b/ext/bg/js/settings/audio.js index e9aa72e1..68dfe71e 100644 --- a/ext/bg/js/settings/audio.js +++ b/ext/bg/js/settings/audio.js @@ -110,7 +110,7 @@ function textToSpeechTest() { const text = document.querySelector('#text-to-speech-voice-test').dataset.speechText || ''; const voiceUri = document.querySelector('#text-to-speech-voice').value; - const audio = audioSystem.createTextToSpeechAudio({text, voiceUri}); + const audio = audioSystem.createTextToSpeechAudio(text, voiceUri); audio.volume = 1.0; audio.play(); } catch (e) { diff --git a/ext/mixed/js/audio-system.js b/ext/mixed/js/audio-system.js index 574ad3dc..5366e3e0 100644 --- a/ext/mixed/js/audio-system.js +++ b/ext/mixed/js/audio-system.js @@ -102,7 +102,7 @@ class AudioSystem { throw new Error('Could not create audio'); } - createTextToSpeechAudio({text, voiceUri}) { + createTextToSpeechAudio(text, voiceUri) { const voice = this._getTextToSpeechVoiceFromVoiceUri(voiceUri); if (voice === null) { throw new Error('Invalid text-to-speech voice'); @@ -117,7 +117,8 @@ class AudioSystem { async _createAudio(uri) { const ttsParameters = this._getTextToSpeechParameters(uri); if (ttsParameters !== null) { - return this.createTextToSpeechAudio(ttsParameters); + const {text, voiceUri} = ttsParameters; + return this.createTextToSpeechAudio(text, voiceUri); } return await this._createAudioFromUrl(uri); -- cgit v1.2.3 From e1ebfb02f724518432b2e1c5ec2a80ff03b38fd8 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 10 Apr 2020 16:12:55 -0400 Subject: Disable cache on the backend and fix a bug with the cache key --- ext/bg/js/backend.js | 3 ++- ext/bg/js/settings/audio.js | 5 ++++- ext/mixed/js/audio-system.js | 22 ++++++++++++++-------- ext/mixed/js/display.js | 3 ++- 4 files changed, 22 insertions(+), 11 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 79402e67..9d1fa6c1 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -53,7 +53,8 @@ class Backend { this.defaultAnkiFieldTemplates = null; this.audioUriBuilder = new AudioUriBuilder(); this.audioSystem = new AudioSystem({ - audioUriBuilder: this.audioUriBuilder + audioUriBuilder: this.audioUriBuilder, + useCache: false }); this.ankiNoteBuilder = new AnkiNoteBuilder({ anki: this.anki, diff --git a/ext/bg/js/settings/audio.js b/ext/bg/js/settings/audio.js index 68dfe71e..98ed9b8b 100644 --- a/ext/bg/js/settings/audio.js +++ b/ext/bg/js/settings/audio.js @@ -28,7 +28,10 @@ let audioSourceUI = null; let audioSystem = null; async function audioSettingsInitialize() { - audioSystem = new AudioSystem({audioUriBuilder: null}); + audioSystem = new AudioSystem({ + audioUriBuilder: null, + useCache: true + }); const optionsContext = getOptionsContext(); const options = await getOptionsMutable(optionsContext); diff --git a/ext/mixed/js/audio-system.js b/ext/mixed/js/audio-system.js index 5366e3e0..255a96de 100644 --- a/ext/mixed/js/audio-system.js +++ b/ext/mixed/js/audio-system.js @@ -66,8 +66,8 @@ class TextToSpeechAudio { } class AudioSystem { - constructor({audioUriBuilder}) { - this._cache = new Map(); + constructor({audioUriBuilder, useCache}) { + this._cache = useCache ? new Map() : null; this._cacheSizeMaximum = 32; this._audioUriBuilder = audioUriBuilder; @@ -79,10 +79,14 @@ class AudioSystem { async getDefinitionAudio(definition, sources, details) { const key = `${definition.expression}:${definition.reading}`; - const cacheValue = this._cache.get(definition); - if (typeof cacheValue !== 'undefined') { - const {audio, uri, source} = cacheValue; - return {audio, uri, source}; + const hasCache = (this._cache !== null); + + if (hasCache) { + const cacheValue = this._cache.get(key); + if (typeof cacheValue !== 'undefined') { + const {audio, uri, source} = cacheValue; + return {audio, uri, source}; + } } for (const source of sources) { @@ -91,8 +95,10 @@ class AudioSystem { try { const audio = await this._createAudio(uri); - this._cacheCheck(); - this._cache.set(key, {audio, uri, source}); + if (hasCache) { + this._cacheCheck(); + this._cache.set(key, {audio, uri, source}); + } return {audio, uri, source}; } catch (e) { // NOP diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 7f3ba859..8edae7c9 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -50,7 +50,8 @@ class Display { async getUri(definition, source, details) { return await apiAudioGetUri(definition, source, details); } - } + }, + useCache: true }); this.styleNode = null; -- cgit v1.2.3 From 9fe7b9ad29958b148162bfc2d065a7e32a986291 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 14 Apr 2020 18:26:24 -0400 Subject: Remove unused global --- ext/bg/js/settings/audio.js | 1 - 1 file changed, 1 deletion(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/settings/audio.js b/ext/bg/js/settings/audio.js index 98ed9b8b..ac2d82f3 100644 --- a/ext/bg/js/settings/audio.js +++ b/ext/bg/js/settings/audio.js @@ -18,7 +18,6 @@ /* global * AudioSourceUI * AudioSystem - * apiAudioGetUri * getOptionsContext * getOptionsMutable * settingsSaveOptions -- cgit v1.2.3 From 7c578f75827ae0a72b13f04b8342fd8129f702d4 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 19 Apr 2020 14:28:29 -0400 Subject: Create popup-preview-frame-main.js --- ext/bg/js/settings/popup-preview-frame-main.js | 27 ++++++++++++++++++++++++++ ext/bg/js/settings/popup-preview-frame.js | 11 ----------- ext/bg/settings-popup-preview.html | 2 ++ 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 ext/bg/js/settings/popup-preview-frame-main.js (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/settings/popup-preview-frame-main.js b/ext/bg/js/settings/popup-preview-frame-main.js new file mode 100644 index 00000000..e6e4727f --- /dev/null +++ b/ext/bg/js/settings/popup-preview-frame-main.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019-2020 Yomichan Authors + * + * 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 . + */ + +/* global + * SettingsPopupPreview + */ + +async function main() { + const instance = new SettingsPopupPreview(); + await instance.prepare(); +} + +main(); diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index fba114e2..420a7c5a 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -41,12 +41,6 @@ class SettingsPopupPreview { ]); } - static create() { - const instance = new SettingsPopupPreview(); - instance.prepare(); - return instance; - } - async prepare() { // Setup events window.addEventListener('message', this.onMessage.bind(this), false); @@ -178,8 +172,3 @@ class SettingsPopupPreview { this.setInfoVisible(!this.popupShown); } } - -SettingsPopupPreview.instance = SettingsPopupPreview.create(); - - - diff --git a/ext/bg/settings-popup-preview.html b/ext/bg/settings-popup-preview.html index f33ecedf..66475b7c 100644 --- a/ext/bg/settings-popup-preview.html +++ b/ext/bg/settings-popup-preview.html @@ -129,5 +129,7 @@ + + -- cgit v1.2.3 From c43dac19a467b055b2e6d16e08cab25dcc748419 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 20 Apr 2020 22:18:37 -0400 Subject: Use wanakana object directly --- ext/bg/js/search.js | 9 +++++---- ext/bg/js/settings/popup-preview.js | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 871c576b..006536c0 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -22,6 +22,7 @@ * apiClipboardGet * apiOptionsSet * apiTermsFind + * wanakana */ class DisplaySearch extends Display { @@ -89,7 +90,7 @@ class DisplaySearch extends Display { if (this.options.general.enableWanakana === true) { this.wanakanaEnable.checked = true; - window.wanakana.bind(this.query); + wanakana.bind(this.query); } else { this.wanakanaEnable.checked = false; } @@ -256,9 +257,9 @@ class DisplaySearch extends Display { onWanakanaEnableChange(e) { const enableWanakana = e.target.checked; if (enableWanakana) { - window.wanakana.bind(this.query); + wanakana.bind(this.query); } else { - window.wanakana.unbind(this.query); + wanakana.unbind(this.query); } apiOptionsSet({general: {enableWanakana}}, this.getOptionsContext()); } @@ -298,7 +299,7 @@ class DisplaySearch extends Display { } setQuery(query) { - const interpretedQuery = this.isWanakanaEnabled() ? window.wanakana.toKana(query) : query; + const interpretedQuery = this.isWanakanaEnabled() ? wanakana.toKana(query) : query; this.query.value = interpretedQuery; this.queryParser.setText(interpretedQuery); } diff --git a/ext/bg/js/settings/popup-preview.js b/ext/bg/js/settings/popup-preview.js index 091872be..45bf531e 100644 --- a/ext/bg/js/settings/popup-preview.js +++ b/ext/bg/js/settings/popup-preview.js @@ -15,6 +15,9 @@ * along with this program. If not, see . */ +/* global + * wanakana + */ function appearanceInitialize() { let previewVisible = false; @@ -37,7 +40,7 @@ function showAppearancePreview() { frame.src = '/bg/settings-popup-preview.html'; frame.id = 'settings-popup-preview-frame'; - window.wanakana.bind(text[0]); + wanakana.bind(text[0]); const targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); -- cgit v1.2.3 From d8276a9d5d119edf1747593608d3e135947019f0 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Wed, 22 Apr 2020 21:42:20 -0400 Subject: Use IIFE for entry points --- ext/bg/js/background-main.js | 6 ++---- ext/bg/js/context-main.js | 6 ++---- ext/bg/js/search-main.js | 6 ++---- ext/bg/js/settings/popup-preview-frame-main.js | 6 ++---- ext/fg/js/content-script-main.js | 6 ++---- ext/fg/js/float-main.js | 6 ++---- 6 files changed, 12 insertions(+), 24 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/background-main.js b/ext/bg/js/background-main.js index c000c38d..24117f4e 100644 --- a/ext/bg/js/background-main.js +++ b/ext/bg/js/background-main.js @@ -19,9 +19,7 @@ * Backend */ -async function main() { +(async () => { window.yomichanBackend = new Backend(); await window.yomichanBackend.prepare(); -} - -main(); +})(); diff --git a/ext/bg/js/context-main.js b/ext/bg/js/context-main.js index 67bb4e18..e2086a96 100644 --- a/ext/bg/js/context-main.js +++ b/ext/bg/js/context-main.js @@ -88,8 +88,6 @@ async function mainInner() { }); } -async function main() { +(async () => { window.addEventListener('DOMContentLoaded', mainInner, false); -} - -main(); +})(); diff --git a/ext/bg/js/search-main.js b/ext/bg/js/search-main.js index 91c39222..38b6d99a 100644 --- a/ext/bg/js/search-main.js +++ b/ext/bg/js/search-main.js @@ -52,7 +52,7 @@ function injectSearchFrontend() { } } -async function main() { +(async () => { await yomichan.prepare(); const displaySearch = new DisplaySearch(); @@ -78,6 +78,4 @@ async function main() { yomichan.on('optionsUpdated', applyOptions); await applyOptions(); -} - -main(); +})(); diff --git a/ext/bg/js/settings/popup-preview-frame-main.js b/ext/bg/js/settings/popup-preview-frame-main.js index e6e4727f..86c2814c 100644 --- a/ext/bg/js/settings/popup-preview-frame-main.js +++ b/ext/bg/js/settings/popup-preview-frame-main.js @@ -19,9 +19,7 @@ * SettingsPopupPreview */ -async function main() { +(async () => { const instance = new SettingsPopupPreview(); await instance.prepare(); -} - -main(); +})(); diff --git a/ext/fg/js/content-script-main.js b/ext/fg/js/content-script-main.js index 974320cc..14285536 100644 --- a/ext/fg/js/content-script-main.js +++ b/ext/fg/js/content-script-main.js @@ -61,7 +61,7 @@ async function createPopupProxy(depth, id, parentFrameId, url) { return popup; } -async function contentScriptMain() { +(async () => { await yomichan.prepare(); const data = window.frontendInitializationData || {}; @@ -128,6 +128,4 @@ async function contentScriptMain() { window.addEventListener('fullscreenchange', applyOptions, false); await applyOptions(); -} - -contentScriptMain(); +})(); diff --git a/ext/fg/js/float-main.js b/ext/fg/js/float-main.js index 072c86e0..f056f707 100644 --- a/ext/fg/js/float-main.js +++ b/ext/fg/js/float-main.js @@ -67,8 +67,6 @@ async function popupNestedInitialize(id, depth, parentFrameId, url) { await applyOptions(); } -async function main() { +(async () => { new DisplayFloat(); -} - -main(); +})(); -- cgit v1.2.3 From ca033a87a0d302151b430acfdf9d480514c14aed Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 26 Apr 2020 22:33:50 +0300 Subject: Update Popup and DisplayFloat optionsContext from Frontend (#464) * set optionsContext from Frontend * update Popup+Display options on Frontend change * remove popup setOptions * only update DisplayFloat options from Frontend * fix optionsContext usage * fix preview frame arguments * keep Frontend URL up to date * cache url * fix preview frame * trigger modifyingProfileChange in correct places * remove async from function not using await * refactor optionsContext in Frontend --- ext/bg/js/search.js | 4 +- ext/bg/js/settings/popup-preview-frame-main.js | 5 +-- ext/bg/js/settings/popup-preview-frame.js | 22 ++++++++--- ext/bg/js/settings/popup-preview.js | 18 +++++++++ ext/bg/js/settings/profiles.js | 10 +++++ ext/fg/js/content-script-main.js | 31 +++++++++++---- ext/fg/js/float.js | 23 ++++++----- ext/fg/js/frontend.js | 55 ++++++++++++++------------ ext/fg/js/popup-proxy-host.js | 17 +++++--- ext/fg/js/popup-proxy.js | 19 +++++---- ext/fg/js/popup.js | 27 ++++++++----- ext/mixed/js/display.js | 2 - 12 files changed, 154 insertions(+), 79 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 684ea6d3..a5484fc3 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -76,6 +76,8 @@ class DisplaySearch extends Display { async prepare() { try { await super.prepare(); + await this.updateOptions(); + yomichan.on('optionsUpdated', () => this.updateOptions()); await this.queryParser.prepare(); const {queryParams: {query='', mode=''}} = parseUrl(window.location.href); @@ -231,7 +233,7 @@ class DisplaySearch extends Display { this.setIntroVisible(!valid, animate); this.updateSearchButton(); if (valid) { - const {definitions} = await apiTermsFind(query, details, this.optionsContext); + const {definitions} = await apiTermsFind(query, details, this.getOptionsContext()); this.setContent('terms', {definitions, context: { focus: false, disableHistory: true, diff --git a/ext/bg/js/settings/popup-preview-frame-main.js b/ext/bg/js/settings/popup-preview-frame-main.js index 86c2814c..2ab6af6b 100644 --- a/ext/bg/js/settings/popup-preview-frame-main.js +++ b/ext/bg/js/settings/popup-preview-frame-main.js @@ -19,7 +19,6 @@ * SettingsPopupPreview */ -(async () => { - const instance = new SettingsPopupPreview(); - await instance.prepare(); +(() => { + new SettingsPopupPreview(); })(); diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index 420a7c5a..05a2a41b 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -32,19 +32,24 @@ class SettingsPopupPreview { this.popupShown = false; this.themeChangeTimeout = null; this.textSource = null; + this.optionsContext = null; this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); this._windowMessageHandlers = new Map([ + ['prepare', ({optionsContext}) => this.prepare(optionsContext)], ['setText', ({text}) => this.setText(text)], ['setCustomCss', ({css}) => this.setCustomCss(css)], - ['setCustomOuterCss', ({css}) => this.setCustomOuterCss(css)] + ['setCustomOuterCss', ({css}) => this.setCustomOuterCss(css)], + ['updateOptionsContext', ({optionsContext}) => this.updateOptionsContext(optionsContext)] ]); - } - async prepare() { - // Setup events window.addEventListener('message', this.onMessage.bind(this), false); + } + + async prepare(optionsContext) { + this.optionsContext = optionsContext; + // Setup events document.querySelector('#theme-dark-checkbox').addEventListener('change', this.onThemeDarkCheckboxChanged.bind(this), false); // Overwrite API functions @@ -62,8 +67,9 @@ class SettingsPopupPreview { this.frontend = new Frontend(this.popup); + this.frontend.getOptionsContext = async () => this.optionsContext; this.frontend.setEnabled = () => {}; - this.frontend.searchClear = () => {}; + this.frontend.onSearchClear = () => {}; await this.frontend.prepare(); @@ -145,6 +151,12 @@ class SettingsPopupPreview { this.frontend.popup.setCustomOuterCss(css, false); } + async updateOptionsContext(optionsContext) { + this.optionsContext = optionsContext; + await this.frontend.updateOptions(); + await this.updateSearch(); + } + async updateSearch() { const exampleText = document.querySelector('#example-text'); if (exampleText === null) { return; } diff --git a/ext/bg/js/settings/popup-preview.js b/ext/bg/js/settings/popup-preview.js index 45bf531e..fdc3dd94 100644 --- a/ext/bg/js/settings/popup-preview.js +++ b/ext/bg/js/settings/popup-preview.js @@ -16,6 +16,7 @@ */ /* global + * getOptionsContext * wanakana */ @@ -60,6 +61,23 @@ function showAppearancePreview() { frame.contentWindow.postMessage({action, params}, targetOrigin); }); + const updateOptionsContext = () => { + const action = 'updateOptionsContext'; + const params = { + optionsContext: getOptionsContext() + }; + frame.contentWindow.postMessage({action, params}, targetOrigin); + }; + yomichan.on('modifyingProfileChange', updateOptionsContext); + + frame.addEventListener('load', () => { + const action = 'prepare'; + const params = { + optionsContext: getOptionsContext() + }; + frame.contentWindow.postMessage({action, params}, targetOrigin); + }); + container.append(frame); buttonContainer.remove(); settings.css('display', ''); diff --git a/ext/bg/js/settings/profiles.js b/ext/bg/js/settings/profiles.js index 867b17aa..3f4b1da7 100644 --- a/ext/bg/js/settings/profiles.js +++ b/ext/bg/js/settings/profiles.js @@ -190,6 +190,8 @@ async function onTargetProfileChanged() { currentProfileIndex = index; await profileOptionsUpdateTarget(optionsFull); + + yomichan.trigger('modifyingProfileChange'); } async function onProfileAdd() { @@ -197,9 +199,13 @@ async function onProfileAdd() { const profile = utilBackgroundIsolate(optionsFull.profiles[currentProfileIndex]); profile.name = profileOptionsCreateCopyName(profile.name, optionsFull.profiles, 100); optionsFull.profiles.push(profile); + currentProfileIndex = optionsFull.profiles.length - 1; + await profileOptionsUpdateTarget(optionsFull); await settingsSaveOptions(); + + yomichan.trigger('modifyingProfileChange'); } async function onProfileRemove(e) { @@ -238,6 +244,8 @@ async function onProfileRemoveConfirm() { await profileOptionsUpdateTarget(optionsFull); await settingsSaveOptions(); + + yomichan.trigger('modifyingProfileChange'); } function onProfileNameChanged() { @@ -263,6 +271,8 @@ async function onProfileMove(offset) { await profileOptionsUpdateTarget(optionsFull); await settingsSaveOptions(); + + yomichan.trigger('modifyingProfileChange'); } async function onProfileCopy() { diff --git a/ext/fg/js/content-script-main.js b/ext/fg/js/content-script-main.js index 14285536..0b852644 100644 --- a/ext/fg/js/content-script-main.js +++ b/ext/fg/js/content-script-main.js @@ -25,7 +25,7 @@ * apiOptionsGet */ -async function createIframePopupProxy(url, frameOffsetForwarder, setDisabled) { +async function createIframePopupProxy(frameOffsetForwarder, setDisabled) { const rootPopupInformationPromise = yomichan.getTemporaryListenerResult( chrome.runtime.onMessage, ({action, params}, {resolve}) => { @@ -39,7 +39,7 @@ async function createIframePopupProxy(url, frameOffsetForwarder, setDisabled) { const getFrameOffset = frameOffsetForwarder.getOffset.bind(frameOffsetForwarder); - const popup = new PopupProxy(popupId, 0, null, frameId, url, getFrameOffset, setDisabled); + const popup = new PopupProxy(popupId, 0, null, frameId, getFrameOffset, setDisabled); await popup.prepare(); return popup; @@ -54,8 +54,8 @@ async function getOrCreatePopup(depth) { return popup; } -async function createPopupProxy(depth, id, parentFrameId, url) { - const popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); +async function createPopupProxy(depth, id, parentFrameId) { + const popup = new PopupProxy(null, depth + 1, id, parentFrameId); await popup.prepare(); return popup; @@ -86,8 +86,22 @@ async function createPopupProxy(depth, id, parentFrameId, url) { applyOptions(); }; + let urlUpdatedAt = 0; + let proxyHostUrlCached = url; + const getProxyHostUrl = async () => { + const now = Date.now(); + if (popups.proxy !== null && now - urlUpdatedAt > 500) { + proxyHostUrlCached = await popups.proxy.getHostUrl(); + urlUpdatedAt = now; + } + return proxyHostUrlCached; + }; + const applyOptions = async () => { - const optionsContext = {depth: isSearchPage ? 0 : depth, url}; + const optionsContext = { + depth: isSearchPage ? 0 : depth, + url: proxy ? await getProxyHostUrl() : window.location.href + }; const options = await apiOptionsGet(optionsContext); if (!proxy && frameOffsetForwarder === null) { @@ -97,10 +111,10 @@ async function createPopupProxy(depth, id, parentFrameId, url) { let popup; if (isIframe && options.general.showIframePopupsInRootFrame && DOM.getFullscreenElement() === null && iframePopupsInRootFrameAvailable) { - popup = popups.iframe || await createIframePopupProxy(url, frameOffsetForwarder, disableIframePopupsInRootFrame); + popup = popups.iframe || await createIframePopupProxy(frameOffsetForwarder, disableIframePopupsInRootFrame); popups.iframe = popup; } else if (proxy) { - popup = popups.proxy || await createPopupProxy(depth, id, parentFrameId, url); + popup = popups.proxy || await createPopupProxy(depth, id, parentFrameId); popups.proxy = popup; } else { popup = popups.normal || await getOrCreatePopup(depth); @@ -108,7 +122,8 @@ async function createPopupProxy(depth, id, parentFrameId, url) { } if (frontend === null) { - frontend = new Frontend(popup); + const getUrl = proxy ? getProxyHostUrl : null; + frontend = new Frontend(popup, getUrl); frontendPreparePromise = frontend.prepare(); await frontendPreparePromise; } else { diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index 18d15a72..2a5eba83 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -29,11 +29,6 @@ class DisplayFloat extends Display { this._popupId = null; - this.optionsContext = { - depth: 0, - url: window.location.href - }; - this._orphaned = false; this._prepareInvoked = false; this._messageToken = null; @@ -51,10 +46,11 @@ class DisplayFloat extends Display { ]); this._windowMessageHandlers = new Map([ + ['setOptionsContext', ({optionsContext}) => this.setOptionsContext(optionsContext)], ['setContent', ({type, details}) => this.setContent(type, details)], ['clearAutoPlayTimer', () => this.clearAutoPlayTimer()], ['setCustomCss', ({css}) => this.setCustomCss(css)], - ['prepare', ({popupInfo, url, childrenSupported, scale}) => this.prepare(popupInfo, url, childrenSupported, scale)], + ['prepare', ({popupInfo, optionsContext, childrenSupported, scale}) => this.prepare(popupInfo, optionsContext, childrenSupported, scale)], ['setContentScale', ({scale}) => this.setContentScale(scale)] ]); @@ -62,18 +58,20 @@ class DisplayFloat extends Display { window.addEventListener('message', this.onMessage.bind(this), false); } - async prepare(popupInfo, url, childrenSupported, scale) { + async prepare(popupInfo, optionsContext, childrenSupported, scale) { if (this._prepareInvoked) { return; } this._prepareInvoked = true; - const {id, depth, parentFrameId} = popupInfo; + const {id, parentFrameId} = popupInfo; this._popupId = id; - this.optionsContext.depth = depth; - this.optionsContext.url = url; + + this.optionsContext = optionsContext; await super.prepare(); + await this.updateOptions(); if (childrenSupported) { + const {depth, url} = optionsContext; popupNestedInitialize(id, depth, parentFrameId, url); } @@ -158,6 +156,11 @@ class DisplayFloat extends Display { } } + async setOptionsContext(optionsContext) { + this.optionsContext = optionsContext; + await this.updateOptions(); + } + setContentScale(scale) { document.body.style.fontSize = `${scale}em`; } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index eecfe2e1..46921d36 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -26,24 +26,23 @@ */ class Frontend extends TextScanner { - constructor(popup) { + constructor(popup, getUrl=null) { super( window, () => this.popup.isProxy() ? [] : [this.popup.getContainer()], [(x, y) => this.popup.containsPoint(x, y)] ); + this._id = yomichan.generateId(16); + this.popup = popup; + this._getUrl = getUrl; + this._disabledOverride = false; this.options = null; - this.optionsContext = { - depth: popup.depth, - url: popup.url - }; - this._pageZoomFactor = 1.0; this._contentScale = 1.0; this._orphaned = false; @@ -143,11 +142,12 @@ class Frontend extends TextScanner { async setPopup(popup) { this.onSearchClear(false); this.popup = popup; - await popup.setOptions(this.options); + await popup.setOptionsContext(await this.getOptionsContext(), this._id); } async updateOptions() { - this.options = await apiOptionsGet(this.getOptionsContext()); + const optionsContext = await this.getOptionsContext(); + this.options = await apiOptionsGet(optionsContext); this.setOptions(this.options, this._canEnable()); const ignoreNodes = ['.scan-disable', '.scan-disable *']; @@ -156,7 +156,7 @@ class Frontend extends TextScanner { } this.ignoreNodes = ignoreNodes.join(','); - await this.popup.setOptions(this.options); + await this.popup.setOptionsContext(optionsContext, this._id); this._updateContentScale(); @@ -170,19 +170,20 @@ class Frontend extends TextScanner { try { if (textSource !== null) { + const optionsContext = await this.getOptionsContext(); results = ( - await this.findTerms(textSource) || - await this.findKanji(textSource) + await this.findTerms(textSource, optionsContext) || + await this.findKanji(textSource, optionsContext) ); if (results !== null) { const focus = (cause === 'mouse'); - this.showContent(textSource, focus, results.definitions, results.type); + this.showContent(textSource, focus, results.definitions, results.type, optionsContext); } } } catch (e) { if (this._orphaned) { if (textSource !== null && this.options.scanning.modifier !== 'none') { - this._showPopupContent(textSource, 'orphaned'); + this._showPopupContent(textSource, await this.getOptionsContext(), 'orphaned'); } } else { this.onError(e); @@ -196,11 +197,12 @@ class Frontend extends TextScanner { return results; } - showContent(textSource, focus, definitions, type) { + showContent(textSource, focus, definitions, type, optionsContext) { + const {url} = optionsContext; const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); - const url = window.location.href; this._showPopupContent( textSource, + optionsContext, type, {definitions, context: {sentence, url, focus, disableHistory: true}} ); @@ -210,13 +212,13 @@ class Frontend extends TextScanner { return this._lastShowPromise; } - async findTerms(textSource) { + async findTerms(textSource, optionsContext) { this.setTextSourceScanLength(textSource, this.options.scanning.length); const searchText = textSource.text(); if (searchText.length === 0) { return null; } - const {definitions, length} = await apiTermsFind(searchText, {}, this.getOptionsContext()); + const {definitions, length} = await apiTermsFind(searchText, {}, optionsContext); if (definitions.length === 0) { return null; } textSource.setEndOffset(length); @@ -224,13 +226,13 @@ class Frontend extends TextScanner { return {definitions, type: 'terms'}; } - async findKanji(textSource) { + async findKanji(textSource, optionsContext) { this.setTextSourceScanLength(textSource, 1); const searchText = textSource.text(); if (searchText.length === 0) { return null; } - const definitions = await apiKanjiFind(searchText, this.getOptionsContext()); + const definitions = await apiKanjiFind(searchText, optionsContext); if (definitions.length === 0) { return null; } return {definitions, type: 'kanji'}; @@ -242,17 +244,20 @@ class Frontend extends TextScanner { super.onSearchClear(changeFocus); } - getOptionsContext() { - this.optionsContext.url = this.popup.url; - return this.optionsContext; + async getOptionsContext() { + const url = this._getUrl !== null ? await this._getUrl() : window.location.href; + const depth = this.popup.depth; + return {depth, url}; } - _showPopupContent(textSource, type=null, details=null) { + _showPopupContent(textSource, optionsContext, type=null, details=null) { + const context = {optionsContext, source: this._id}; this._lastShowPromise = this.popup.showContent( textSource.getRect(), textSource.getWritingMode(), type, - details + details, + context ); return this._lastShowPromise; } @@ -294,7 +299,7 @@ class Frontend extends TextScanner { async _updatePopupPosition() { const textSource = this.getCurrentTextSource(); if (textSource !== null && await this.popup.isVisible()) { - this._showPopupContent(textSource); + this._showPopupContent(textSource, await this.getOptionsContext()); } } diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 958462ff..d87553e6 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -37,7 +37,7 @@ class PopupProxyHost { this._apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${this._frameId}`, new Map([ ['getOrCreatePopup', this._onApiGetOrCreatePopup.bind(this)], - ['setOptions', this._onApiSetOptions.bind(this)], + ['setOptionsContext', this._onApiSetOptionsContext.bind(this)], ['hide', this._onApiHide.bind(this)], ['isVisible', this._onApiIsVisibleAsync.bind(this)], ['setVisibleOverride', this._onApiSetVisibleOverride.bind(this)], @@ -45,7 +45,8 @@ class PopupProxyHost { ['showContent', this._onApiShowContent.bind(this)], ['setCustomCss', this._onApiSetCustomCss.bind(this)], ['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)], - ['setContentScale', this._onApiSetContentScale.bind(this)] + ['setContentScale', this._onApiSetContentScale.bind(this)], + ['getHostUrl', this._onApiGetHostUrl.bind(this)] ])); } @@ -103,9 +104,9 @@ class PopupProxyHost { }; } - async _onApiSetOptions({id, options}) { + async _onApiSetOptionsContext({id, optionsContext, source}) { const popup = this._getPopup(id); - return await popup.setOptions(options); + return await popup.setOptionsContext(optionsContext, source); } async _onApiHide({id, changeFocus}) { @@ -129,11 +130,11 @@ class PopupProxyHost { return await popup.containsPoint(x, y); } - async _onApiShowContent({id, elementRect, writingMode, type, details}) { + async _onApiShowContent({id, elementRect, writingMode, type, details, context}) { const popup = this._getPopup(id); elementRect = PopupProxyHost._convertJsonRectToDOMRect(popup, elementRect); if (!PopupProxyHost._popupCanShow(popup)) { return; } - return await popup.showContent(elementRect, writingMode, type, details); + return await popup.showContent(elementRect, writingMode, type, details, context); } async _onApiSetCustomCss({id, css}) { @@ -151,6 +152,10 @@ class PopupProxyHost { return popup.setContentScale(scale); } + async _onApiGetHostUrl() { + return window.location.href; + } + // Private functions _getPopup(id) { diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 3af83db2..cd3c1bc9 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -20,12 +20,11 @@ */ class PopupProxy { - constructor(id, depth, parentId, parentFrameId, url, getFrameOffset=null, setDisabled=null) { + constructor(id, depth, parentId, parentFrameId, getFrameOffset=null, setDisabled=null) { this._parentId = parentId; this._parentFrameId = parentFrameId; this._id = id; this._depth = depth; - this._url = url; this._apiSender = new FrontendApiSender(); this._getFrameOffset = getFrameOffset; this._setDisabled = setDisabled; @@ -49,10 +48,6 @@ class PopupProxy { return this._depth; } - get url() { - return this._url; - } - // Public functions async prepare() { @@ -64,8 +59,8 @@ class PopupProxy { return true; } - async setOptions(options) { - return await this._invokeHostApi('setOptions', {id: this._id, options}); + async setOptionsContext(optionsContext, source) { + return await this._invokeHostApi('setOptionsContext', {id: this._id, optionsContext, source}); } hide(changeFocus) { @@ -88,14 +83,14 @@ class PopupProxy { return await this._invokeHostApi('containsPoint', {id: this._id, x, y}); } - async showContent(elementRect, writingMode, type=null, details=null) { + async showContent(elementRect, writingMode, type, details, context) { let {x, y, width, height} = elementRect; if (this._getFrameOffset !== null) { await this._updateFrameOffset(); [x, y] = this._applyFrameOffset(x, y); } elementRect = {x, y, width, height}; - return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details}); + return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details, context}); } async setCustomCss(css) { @@ -110,6 +105,10 @@ class PopupProxy { this._invokeHostApi('setContentScale', {id: this._id, scale}); } + async getHostUrl() { + return await this._invokeHostApi('getHostUrl', {}); + } + // Private _invokeHostApi(action, params={}) { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index ae158263..e735431b 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -19,6 +19,7 @@ * DOM * apiGetMessageToken * apiInjectStylesheet + * apiOptionsGet */ class Popup { @@ -33,10 +34,12 @@ class Popup { this._visible = false; this._visibleOverride = null; this._options = null; + this._optionsContext = null; this._contentScale = 1.0; this._containerSizeContentScale = null; this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); this._messageToken = null; + this._previousOptionsContextSource = null; this._container = document.createElement('iframe'); this._container.className = 'yomichan-float'; @@ -72,19 +75,20 @@ class Popup { return this._frameId; } - get url() { - return window.location.href; - } - // Public functions isProxy() { return false; } - async setOptions(options) { - this._options = options; + async setOptionsContext(optionsContext, source) { + this._optionsContext = optionsContext; + this._previousOptionsContextSource = source; + + this._options = await apiOptionsGet(optionsContext); this.updateTheme(); + + this._invokeApi('setOptionsContext', {optionsContext}); } hide(changeFocus) { @@ -120,8 +124,14 @@ class Popup { return false; } - async showContent(elementRect, writingMode, type=null, details=null) { + async showContent(elementRect, writingMode, type, details, context) { if (this._options === null) { throw new Error('Options not assigned'); } + + const {optionsContext, source} = context; + if (source !== this._previousOptionsContextSource) { + await this.setOptionsContext(optionsContext, source); + } + await this._show(elementRect, writingMode); if (type === null) { return; } this._invokeApi('setContent', {type, details}); @@ -219,10 +229,9 @@ class Popup { this._invokeApi('prepare', { popupInfo: { id: this._id, - depth: this._depth, parentFrameId }, - url: this.url, + optionsContext: this._optionsContext, childrenSupported: this._childrenSupported, scale: this._contentScale }); diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 9587ec3b..70b7fcd3 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -177,8 +177,6 @@ class Display { async prepare() { await yomichan.prepare(); await this.displayGenerator.prepare(); - await this.updateOptions(); - yomichan.on('optionsUpdated', () => this.updateOptions()); } onError(_error) { -- cgit v1.2.3 From 5b96559df819f496b39acb75c679f6b3d8c8e65d Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 26 Apr 2020 16:55:25 -0400 Subject: Error logging refactoring (#454) * Create new logging methods on yomichan object * Use new yomichan.logError instead of global logError * Remove old logError * Handle unhandledrejection events * Add addEventListener stub * Update log function * Update error conversion to support more types * Add log event * Add API log function * Log errors to the backend * Make error/warning logs update the badge * Clear log error indicator on extension button click * Log correct URL on the background page * Fix incorrect error conversion * Remove unhandledrejection handling Firefox doesn't support it properly. * Remove unused argument type from log function * Improve function name * Change console.warn to yomichan.logWarning * Move log forwarding initialization into main scripts --- .eslintrc.json | 1 - ext/bg/js/backend.js | 50 ++++++++++- ext/bg/js/context-main.js | 5 ++ ext/bg/js/database.js | 2 +- ext/bg/js/mecab.js | 2 +- ext/bg/js/search-main.js | 2 + ext/bg/js/search-query-parser.js | 2 +- ext/bg/js/search.js | 2 +- ext/bg/js/settings/backup.js | 2 +- ext/bg/js/settings/dictionaries.js | 2 +- ext/bg/js/settings/main.js | 2 + ext/bg/js/settings/popup-preview-frame-main.js | 2 + ext/fg/js/content-script-main.js | 2 + ext/fg/js/float-main.js | 2 + ext/fg/js/float.js | 2 +- ext/fg/js/frontend-api-sender.js | 8 +- ext/fg/js/popup-proxy.js | 2 +- ext/mixed/js/api.js | 22 +++++ ext/mixed/js/core.js | 112 +++++++++++++++++++------ ext/mixed/js/text-scanner.js | 2 +- test/test-database.js | 5 +- 21 files changed, 186 insertions(+), 45 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/.eslintrc.json b/.eslintrc.json index 8882cb42..78fec27c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -80,7 +80,6 @@ "yomichan": "readonly", "errorToJson": "readonly", "jsonToError": "readonly", - "logError": "readonly", "isObject": "readonly", "hasOwn": "readonly", "toIterable": "readonly", diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 693a9ad6..3c47b14e 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -78,6 +78,7 @@ class Backend { this._isPrepared = false; this._prepareError = false; this._badgePrepareDelayTimer = null; + this._logErrorLevel = null; this._messageHandlers = new Map([ ['yomichanCoreReady', {handler: this._onApiYomichanCoreReady.bind(this), async: false}], @@ -112,7 +113,9 @@ class Backend { ['getDictionaryInfo', {handler: this._onApiGetDictionaryInfo.bind(this), async: true}], ['getDictionaryCounts', {handler: this._onApiGetDictionaryCounts.bind(this), async: true}], ['purgeDatabase', {handler: this._onApiPurgeDatabase.bind(this), async: true}], - ['getMedia', {handler: this._onApiGetMedia.bind(this), async: true}] + ['getMedia', {handler: this._onApiGetMedia.bind(this), async: true}], + ['log', {handler: this._onApiLog.bind(this), async: false}], + ['logIndicatorClear', {handler: this._onApiLogIndicatorClear.bind(this), async: false}] ]); this._commandHandlers = new Map([ @@ -164,7 +167,7 @@ class Backend { this._isPrepared = true; } catch (e) { this._prepareError = true; - logError(e); + yomichan.logError(e); throw e; } finally { if (this._badgePrepareDelayTimer !== null) { @@ -260,7 +263,7 @@ class Backend { this.options = JsonSchema.getValidValueOrDefault(this.optionsSchema, utilIsolate(options)); } catch (e) { // This shouldn't happen, but catch errors just in case of bugs - logError(e); + yomichan.logError(e); } } @@ -767,8 +770,34 @@ class Backend { return await this.database.getMedia(targets); } + _onApiLog({error, level, context}) { + yomichan.log(jsonToError(error), level, context); + + const levelValue = this._getErrorLevelValue(level); + if (levelValue <= this._getErrorLevelValue(this._logErrorLevel)) { return; } + + this._logErrorLevel = level; + this._updateBadge(); + } + + _onApiLogIndicatorClear() { + if (this._logErrorLevel === null) { return; } + this._logErrorLevel = null; + this._updateBadge(); + } + // Command handlers + _getErrorLevelValue(errorLevel) { + switch (errorLevel) { + case 'info': return 0; + case 'debug': return 0; + case 'warn': return 1; + case 'error': return 2; + default: return 0; + } + } + async _onCommandSearch(params) { const {mode='existingOrNewTab', query} = params || {}; @@ -890,7 +919,20 @@ class Backend { let color = null; let status = null; - if (!this._isPrepared) { + if (this._logErrorLevel !== null) { + switch (this._logErrorLevel) { + case 'error': + text = '!!'; + color = '#f04e4e'; + status = 'Error'; + break; + default: // 'warn' + text = '!'; + color = '#f0ad4e'; + status = 'Warning'; + break; + } + } else if (!this._isPrepared) { if (this._prepareError) { text = '!!'; color = '#f04e4e'; diff --git a/ext/bg/js/context-main.js b/ext/bg/js/context-main.js index e2086a96..dbba0272 100644 --- a/ext/bg/js/context-main.js +++ b/ext/bg/js/context-main.js @@ -17,7 +17,9 @@ /* global * apiCommandExec + * apiForwardLogsToBackend * apiGetEnvironmentInfo + * apiLogIndicatorClear * apiOptionsGet */ @@ -52,8 +54,11 @@ function setupButtonEvents(selector, command, url) { } async function mainInner() { + apiForwardLogsToBackend(); await yomichan.prepare(); + await apiLogIndicatorClear(); + showExtensionInfo(); apiGetEnvironmentInfo().then(({browser}) => { diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 16612403..a94aa720 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -104,7 +104,7 @@ class Database { }); return true; } catch (e) { - logError(e); + yomichan.logError(e); return false; } } diff --git a/ext/bg/js/mecab.js b/ext/bg/js/mecab.js index 597dceae..815ee860 100644 --- a/ext/bg/js/mecab.js +++ b/ext/bg/js/mecab.js @@ -24,7 +24,7 @@ class Mecab { } onError(error) { - logError(error, false); + yomichan.logError(error); } async checkVersion() { diff --git a/ext/bg/js/search-main.js b/ext/bg/js/search-main.js index 38b6d99a..5e4d7a20 100644 --- a/ext/bg/js/search-main.js +++ b/ext/bg/js/search-main.js @@ -17,6 +17,7 @@ /* global * DisplaySearch + * apiForwardLogsToBackend * apiOptionsGet */ @@ -53,6 +54,7 @@ function injectSearchFrontend() { } (async () => { + apiForwardLogsToBackend(); await yomichan.prepare(); const displaySearch = new DisplaySearch(); diff --git a/ext/bg/js/search-query-parser.js b/ext/bg/js/search-query-parser.js index eb3b681c..0001c9ff 100644 --- a/ext/bg/js/search-query-parser.js +++ b/ext/bg/js/search-query-parser.js @@ -45,7 +45,7 @@ class QueryParser extends TextScanner { } onError(error) { - logError(error, false); + yomichan.logError(error); } onClick(e) { diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index a5484fc3..cbd7b562 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -122,7 +122,7 @@ class DisplaySearch extends Display { } onError(error) { - logError(error, true); + yomichan.logError(error); } onSearchClear() { diff --git a/ext/bg/js/settings/backup.js b/ext/bg/js/settings/backup.js index bdfef658..faf4e592 100644 --- a/ext/bg/js/settings/backup.js +++ b/ext/bg/js/settings/backup.js @@ -133,7 +133,7 @@ async function _settingsImportSetOptionsFull(optionsFull) { } function _showSettingsImportError(error) { - logError(error); + yomichan.logError(error); document.querySelector('#settings-import-error-modal-message').textContent = `${error}`; $('#settings-import-error-modal').modal('show'); } diff --git a/ext/bg/js/settings/dictionaries.js b/ext/bg/js/settings/dictionaries.js index 7eed4273..50add4c7 100644 --- a/ext/bg/js/settings/dictionaries.js +++ b/ext/bg/js/settings/dictionaries.js @@ -554,7 +554,7 @@ function dictionaryErrorsShow(errors) { if (errors !== null && errors.length > 0) { const uniqueErrors = new Map(); for (let e of errors) { - logError(e); + yomichan.logError(e); e = dictionaryErrorToString(e); let count = uniqueErrors.get(e); if (typeof count === 'undefined') { diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index 308e92eb..f03cc631 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -21,6 +21,7 @@ * ankiInitialize * ankiTemplatesInitialize * ankiTemplatesUpdateValue + * apiForwardLogsToBackend * apiOptionsSave * appearanceInitialize * audioSettingsInitialize @@ -284,6 +285,7 @@ function showExtensionInformation() { async function onReady() { + apiForwardLogsToBackend(); await yomichan.prepare(); showExtensionInformation(); diff --git a/ext/bg/js/settings/popup-preview-frame-main.js b/ext/bg/js/settings/popup-preview-frame-main.js index 2ab6af6b..8228125f 100644 --- a/ext/bg/js/settings/popup-preview-frame-main.js +++ b/ext/bg/js/settings/popup-preview-frame-main.js @@ -17,8 +17,10 @@ /* global * SettingsPopupPreview + * apiForwardLogsToBackend */ (() => { + apiForwardLogsToBackend(); new SettingsPopupPreview(); })(); diff --git a/ext/fg/js/content-script-main.js b/ext/fg/js/content-script-main.js index 0b852644..277e6567 100644 --- a/ext/fg/js/content-script-main.js +++ b/ext/fg/js/content-script-main.js @@ -22,6 +22,7 @@ * PopupProxy * PopupProxyHost * apiBroadcastTab + * apiForwardLogsToBackend * apiOptionsGet */ @@ -62,6 +63,7 @@ async function createPopupProxy(depth, id, parentFrameId) { } (async () => { + apiForwardLogsToBackend(); await yomichan.prepare(); const data = window.frontendInitializationData || {}; diff --git a/ext/fg/js/float-main.js b/ext/fg/js/float-main.js index f056f707..5ef4b07c 100644 --- a/ext/fg/js/float-main.js +++ b/ext/fg/js/float-main.js @@ -17,6 +17,7 @@ /* global * DisplayFloat + * apiForwardLogsToBackend * apiOptionsGet */ @@ -68,5 +69,6 @@ async function popupNestedInitialize(id, depth, parentFrameId, url) { } (async () => { + apiForwardLogsToBackend(); new DisplayFloat(); })(); diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index 2a5eba83..fd3b92cc 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -84,7 +84,7 @@ class DisplayFloat extends Display { if (this._orphaned) { this.setContent('orphaned'); } else { - logError(error, true); + yomichan.logError(error); } } diff --git a/ext/fg/js/frontend-api-sender.js b/ext/fg/js/frontend-api-sender.js index 1d539cab..0ad3f085 100644 --- a/ext/fg/js/frontend-api-sender.js +++ b/ext/fg/js/frontend-api-sender.js @@ -81,12 +81,12 @@ class FrontendApiSender { onAck(id) { const info = this.callbacks.get(id); if (typeof info === 'undefined') { - console.warn(`ID ${id} not found for ack`); + yomichan.logWarning(new Error(`ID ${id} not found for ack`)); return; } if (info.ack) { - console.warn(`Request ${id} already ack'd`); + yomichan.logWarning(new Error(`Request ${id} already ack'd`)); return; } @@ -98,12 +98,12 @@ class FrontendApiSender { onResult(id, data) { const info = this.callbacks.get(id); if (typeof info === 'undefined') { - console.warn(`ID ${id} not found`); + yomichan.logWarning(new Error(`ID ${id} not found`)); return; } if (!info.ack) { - console.warn(`Request ${id} not ack'd`); + yomichan.logWarning(new Error(`Request ${id} not ack'd`)); return; } diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index cd3c1bc9..93418202 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -148,7 +148,7 @@ class PopupProxy { } this._frameOffsetUpdatedAt = now; } catch (e) { - logError(e); + yomichan.logError(e); } finally { this._frameOffsetPromise = null; } diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index 52f41646..afd68aa2 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -144,6 +144,14 @@ function apiGetMedia(targets) { return _apiInvoke('getMedia', {targets}); } +function apiLog(error, level, context) { + return _apiInvoke('log', {error, level, context}); +} + +function apiLogIndicatorClear() { + return _apiInvoke('logIndicatorClear'); +} + function _apiInvoke(action, params={}) { const data = {action, params}; return new Promise((resolve, reject) => { @@ -171,3 +179,17 @@ function _apiInvoke(action, params={}) { function _apiCheckLastError() { // NOP } + +let _apiForwardLogsToBackendEnabled = false; +function apiForwardLogsToBackend() { + if (_apiForwardLogsToBackendEnabled) { return; } + _apiForwardLogsToBackendEnabled = true; + + yomichan.on('log', async ({error, level, context}) => { + try { + await apiLog(errorToJson(error), level, context); + } catch (e) { + // NOP + } + }); +} diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 6a3298fc..fbe9943a 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -52,15 +52,28 @@ if (EXTENSION_IS_BROWSER_EDGE) { */ function errorToJson(error) { + try { + if (isObject(error)) { + return { + name: error.name, + message: error.message, + stack: error.stack, + data: error.data + }; + } + } catch (e) { + // NOP + } return { - name: error.name, - message: error.message, - stack: error.stack, - data: error.data + value: error, + hasValue: true }; } function jsonToError(jsonError) { + if (jsonError.hasValue) { + return jsonError.value; + } const error = new Error(jsonError.message); error.name = jsonError.name; error.stack = jsonError.stack; @@ -68,28 +81,6 @@ function jsonToError(jsonError) { return error; } -function logError(error, alert) { - const manifest = chrome.runtime.getManifest(); - let errorMessage = `${manifest.name} v${manifest.version} has encountered an error.\n`; - errorMessage += `Originating URL: ${window.location.href}\n`; - - const errorString = `${error.toString ? error.toString() : error}`; - const stack = `${error.stack}`.trimRight(); - if (!stack.startsWith(errorString)) { errorMessage += `${errorString}\n`; } - errorMessage += stack; - - const data = error.data; - if (typeof data !== 'undefined') { errorMessage += `\nData: ${JSON.stringify(data, null, 4)}`; } - - errorMessage += '\n\nIssues can be reported at https://github.com/FooSoft/yomichan/issues'; - - console.error(errorMessage); - - if (alert) { - window.alert(`${errorString}\n\nCheck the developer console for more details.`); - } -} - /* * Common helpers @@ -361,8 +352,77 @@ const yomichan = (() => { }); } + logWarning(error) { + this.log(error, 'warn'); + } + + logError(error) { + this.log(error, 'error'); + } + + log(error, level, context=null) { + if (!isObject(context)) { + context = this._getLogContext(); + } + + let errorString; + try { + errorString = error.toString(); + if (/^\[object \w+\]$/.test(errorString)) { + errorString = JSON.stringify(error); + } + } catch (e) { + errorString = `${error}`; + } + + let errorStack; + try { + errorStack = (typeof error.stack === 'string' ? error.stack.trimRight() : ''); + } catch (e) { + errorStack = ''; + } + + let errorData; + try { + errorData = error.data; + } catch (e) { + // NOP + } + + if (errorStack.startsWith(errorString)) { + errorString = errorStack; + } else if (errorStack.length > 0) { + errorString += `\n${errorStack}`; + } + + const manifest = chrome.runtime.getManifest(); + let message = `${manifest.name} v${manifest.version} has encountered a problem.`; + message += `\nOriginating URL: ${context.url}\n`; + message += errorString; + if (typeof errorData !== 'undefined') { + message += `\nData: ${JSON.stringify(errorData, null, 4)}`; + } + message += '\n\nIssues can be reported at https://github.com/FooSoft/yomichan/issues'; + + switch (level) { + case 'info': console.info(message); break; + case 'debug': console.debug(message); break; + case 'warn': console.warn(message); break; + case 'error': console.error(message); break; + default: console.log(message); break; + } + + this.trigger('log', {error, level, context}); + } + // Private + _getLogContext() { + return { + url: window.location.href + }; + } + _onMessage({action, params}, sender, callback) { const handler = this._messageHandlers.get(action); if (typeof handler !== 'function') { return false; } diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index 0cd12cd7..1c32714b 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -201,7 +201,7 @@ class TextScanner { } onError(error) { - logError(error, false); + yomichan.logError(error); } async scanTimerWait() { diff --git a/test/test-database.js b/test/test-database.js index e9ec3f0b..3684051b 100644 --- a/test/test-database.js +++ b/test/test-database.js @@ -145,7 +145,10 @@ const vm = new VM({ XMLHttpRequest, indexedDB: global.indexedDB, IDBKeyRange: global.IDBKeyRange, - JSZip: yomichanTest.JSZip + JSZip: yomichanTest.JSZip, + addEventListener() { + // NOP + } }); vm.context.window = vm.context; -- cgit v1.2.3 From 48c7010f4ea8daafd30e5650625c377affa0cecd Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 27 Apr 2020 18:10:37 -0400 Subject: Frontend refactor (part 1) (#484) * Remove _getVisualViewportScale * Use super's mouse event listener definitions * Remove redundant override * Remove getTouchEventListeners override * Rename Display.onSearchClear to onEscape * Change onSearchClear to clearSelection and use an event * Update how text is marked for selection and deselection * Replace onError with yomichan.logError * Update setEnabled to refresh all event listeners --- ext/bg/js/search-query-parser.js | 25 ++--------------- ext/bg/js/search.js | 2 +- ext/bg/js/settings/popup-preview-frame.js | 2 +- ext/fg/js/float.js | 2 +- ext/fg/js/frontend.js | 26 ++++++++--------- ext/mixed/js/display.js | 4 +-- ext/mixed/js/text-scanner.js | 46 +++++++++++++++---------------- 7 files changed, 42 insertions(+), 65 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/js/search-query-parser.js b/ext/bg/js/search-query-parser.js index 0001c9ff..3215f8e4 100644 --- a/ext/bg/js/search-query-parser.js +++ b/ext/bg/js/search-query-parser.js @@ -44,12 +44,7 @@ class QueryParser extends TextScanner { await this.queryParserGenerator.prepare(); } - onError(error) { - yomichan.logError(error); - } - - onClick(e) { - super.onClick(e); + onClick2(e) { this.searchAt(e.clientX, e.clientY, 'click'); } @@ -84,22 +79,8 @@ class QueryParser extends TextScanner { getMouseEventListeners() { return [ - [this.node, 'click', this.onClick.bind(this)], - [this.node, 'mousedown', this.onMouseDown.bind(this)], - [this.node, 'mousemove', this.onMouseMove.bind(this)], - [this.node, 'mouseover', this.onMouseOver.bind(this)], - [this.node, 'mouseout', this.onMouseOut.bind(this)] - ]; - } - - getTouchEventListeners() { - return [ - [this.node, 'auxclick', this.onAuxClick.bind(this)], - [this.node, 'touchstart', this.onTouchStart.bind(this)], - [this.node, 'touchend', this.onTouchEnd.bind(this)], - [this.node, 'touchcancel', this.onTouchCancel.bind(this)], - [this.node, 'touchmove', this.onTouchMove.bind(this), {passive: false}], - [this.node, 'contextmenu', this.onContextMenu.bind(this)] + ...super.getMouseEventListeners(), + [this.node, 'click', this.onClick2.bind(this)] ]; } diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index cbd7b562..b7d2eed8 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -125,7 +125,7 @@ class DisplaySearch extends Display { yomichan.logError(error); } - onSearchClear() { + onEscape() { if (this.query === null) { return; } diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index 05a2a41b..e73c04a0 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -69,7 +69,7 @@ class SettingsPopupPreview { this.frontend.getOptionsContext = async () => this.optionsContext; this.frontend.setEnabled = () => {}; - this.frontend.onSearchClear = () => {}; + this.frontend.clearSelection = () => {}; await this.frontend.prepare(); diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index fd3b92cc..294093cd 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -92,7 +92,7 @@ class DisplayFloat extends Display { this._orphaned = true; } - onSearchClear() { + onEscape() { window.parent.postMessage('popupClose', '*'); } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 46921d36..50f52724 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -49,7 +49,7 @@ class Frontend extends TextScanner { this._lastShowPromise = Promise.resolve(); this._windowMessageHandlers = new Map([ - ['popupClose', () => this.onSearchClear(true)], + ['popupClose', () => this.clearSelection(false)], ['selectionCopy', () => document.execCommand('copy')] ]); @@ -79,10 +79,12 @@ class Frontend extends TextScanner { yomichan.on('zoomChanged', this.onZoomChanged.bind(this)); chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); + this.on('clearSelection', this.onClearSelection.bind(this)); + this._updateContentScale(); this._broadcastRootPopupInformation(); } catch (e) { - this.onError(e); + yomichan.logError(e); } } @@ -140,7 +142,7 @@ class Frontend extends TextScanner { } async setPopup(popup) { - this.onSearchClear(false); + this.clearSelection(true); this.popup = popup; await popup.setOptionsContext(await this.getOptionsContext(), this._id); } @@ -186,11 +188,11 @@ class Frontend extends TextScanner { this._showPopupContent(textSource, await this.getOptionsContext(), 'orphaned'); } } else { - this.onError(e); + yomichan.logError(e); } } finally { if (results === null && this.options.scanning.autoHideResults) { - this.onSearchClear(true); + this.clearSelection(false); } } @@ -238,10 +240,9 @@ class Frontend extends TextScanner { return {definitions, type: 'kanji'}; } - onSearchClear(changeFocus) { - this.popup.hide(changeFocus); + onClearSelection({passive}) { + this.popup.hide(!passive); this.popup.clearAutoPlayTimer(); - super.onSearchClear(changeFocus); } async getOptionsContext() { @@ -269,7 +270,9 @@ class Frontend extends TextScanner { contentScale /= this._pageZoomFactor; } if (popupScaleRelativeToVisualViewport) { - contentScale /= Frontend._getVisualViewportScale(); + const visualViewport = window.visualViewport; + const visualViewportScale = (visualViewport !== null && typeof visualViewport === 'object' ? visualViewport.scale : 1.0); + contentScale /= visualViewportScale; } if (contentScale === this._contentScale) { return; } @@ -302,9 +305,4 @@ class Frontend extends TextScanner { this._showPopupContent(textSource, await this.getOptionsContext()); } } - - static _getVisualViewportScale() { - const visualViewport = window.visualViewport; - return visualViewport !== null && typeof visualViewport === 'object' ? visualViewport.scale : 1.0; - } } diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 70b7fcd3..32081c70 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -69,7 +69,7 @@ class Display { this._onKeyDownHandlers = new Map([ ['Escape', () => { - this.onSearchClear(); + this.onEscape(); return true; }], ['PageUp', (e) => { @@ -183,7 +183,7 @@ class Display { throw new Error('Override me'); } - onSearchClear() { + onEscape() { throw new Error('Override me'); } diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index 1c32714b..c582ccd8 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -21,8 +21,9 @@ * docRangeFromPoint */ -class TextScanner { +class TextScanner extends EventDispatcher { constructor(node, ignoreElements, ignorePoints) { + super(); this.node = node; this.ignoreElements = ignoreElements; this.ignorePoints = ignorePoints; @@ -32,6 +33,7 @@ class TextScanner { this.scanTimerPromise = null; this.causeCurrent = null; this.textSourceCurrent = null; + this.textSourceCurrentSelected = false; this.pendingLookup = false; this.options = null; @@ -92,7 +94,7 @@ class TextScanner { if (DOM.isMouseButtonDown(e, 'primary')) { this.scanTimerClear(); - this.onSearchClear(true); + this.clearSelection(false); } } @@ -200,10 +202,6 @@ class TextScanner { throw new Error('Override me'); } - onError(error) { - yomichan.logError(error); - } - async scanTimerWait() { const delay = this.options.scanning.delay; const promise = promiseTimeout(delay, true); @@ -225,17 +223,12 @@ class TextScanner { } setEnabled(enabled, canEnable) { - if (enabled && canEnable) { - if (!this.enabled) { - this.hookEvents(); - this.enabled = true; - } + this.eventListeners.removeAllEventListeners(); + this.enabled = enabled && canEnable; + if (this.enabled) { + this.hookEvents(); } else { - if (this.enabled) { - this.eventListeners.removeAllEventListeners(); - this.enabled = false; - } - this.onSearchClear(false); + this.clearSelection(true); } } @@ -300,10 +293,7 @@ class TextScanner { const result = await this.onSearchSource(textSource, cause); if (result !== null) { this.causeCurrent = cause; - this.textSourceCurrent = textSource; - if (this.options.scanning.selectText) { - textSource.select(); - } + this.setCurrentTextSource(textSource); } this.pendingLookup = false; } finally { @@ -312,7 +302,7 @@ class TextScanner { } } } catch (e) { - this.onError(e); + yomichan.logError(e); } } @@ -333,13 +323,15 @@ class TextScanner { } } - onSearchClear(_) { + clearSelection(passive) { if (this.textSourceCurrent !== null) { - if (this.options.scanning.selectText) { + if (this.textSourceCurrentSelected) { this.textSourceCurrent.deselect(); } this.textSourceCurrent = null; + this.textSourceCurrentSelected = false; } + this.trigger('clearSelection', {passive}); } getCurrentTextSource() { @@ -347,7 +339,13 @@ class TextScanner { } setCurrentTextSource(textSource) { - return this.textSourceCurrent = textSource; + this.textSourceCurrent = textSource; + if (this.options.scanning.selectText) { + this.textSourceCurrent.select(); + this.textSourceCurrentSelected = true; + } else { + this.textSourceCurrentSelected = false; + } } static isScanningModifierPressed(scanningModifier, mouseEvent) { -- cgit v1.2.3 From 0956634d61ef2b6202645ec4b502239573c2e743 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 27 Apr 2020 18:10:59 -0400 Subject: Add duplicateScope: 'deck' option (#476) * Add duplicateScope: 'deck' option * Add option to control duplicate scope * Use duplicateScope for findNoteIds * Update location of quotes --- ext/bg/data/options-schema.json | 6 ++++++ ext/bg/js/anki-note-builder.js | 5 ++++- ext/bg/js/anki.js | 15 +++++++-------- ext/bg/js/backend.js | 2 +- ext/bg/js/options.js | 1 + ext/bg/js/settings/main.js | 2 ++ ext/bg/settings.html | 8 ++++++++ 7 files changed, 29 insertions(+), 10 deletions(-) (limited to 'ext/bg/js/settings') diff --git a/ext/bg/data/options-schema.json b/ext/bg/data/options-schema.json index 4f9e694d..8622f16b 100644 --- a/ext/bg/data/options-schema.json +++ b/ext/bg/data/options-schema.json @@ -492,6 +492,7 @@ "screenshot", "terms", "kanji", + "duplicateScope", "fieldTemplates" ], "properties": { @@ -587,6 +588,11 @@ } } }, + "duplicateScope": { + "type": "string", + "default": "collection", + "enum": ["collection", "deck"] + }, "fieldTemplates": { "type": ["string", "null"], "default": null diff --git a/ext/bg/js/anki-note-builder.js b/ext/bg/js/anki-note-builder.js index 9bab095d..dc1e9427 100644 --- a/ext/bg/js/anki-note-builder.js +++ b/ext/bg/js/anki-note-builder.js @@ -32,7 +32,10 @@ class AnkiNoteBuilder { fields: {}, tags, deckName: modeOptions.deck, - modelName: modeOptions.model + modelName: modeOptions.model, + options: { + duplicateScope: options.anki.duplicateScope + } }; for (const [fieldName, fieldValue] of modeOptionsFieldEntries) { diff --git a/ext/bg/js/anki.js b/ext/bg/js/anki.js index 38823431..0d38837c 100644 --- a/ext/bg/js/anki.js +++ b/ext/bg/js/anki.js @@ -87,15 +87,14 @@ class AnkiConnect { return await this._invoke('storeMediaFile', {filename, data: dataBase64}); } - async findNoteIds(notes) { + async findNoteIds(notes, duplicateScope) { if (!this._enabled) { return []; } await this._checkVersion(); - const actions = notes.map((note) => ({ - action: 'findNotes', - params: { - query: `deck:"${this._escapeQuery(note.deckName)}" ${this._fieldsToQuery(note.fields)}` - } - })); + const actions = notes.map((note) => { + let query = (duplicateScope === 'deck' ? `"deck:${this._escapeQuery(note.deckName)}" ` : ''); + query += this._fieldsToQuery(note.fields); + return {action: 'findNotes', params: {query}}; + }); return await this._invoke('multi', {actions}); } @@ -132,6 +131,6 @@ class AnkiConnect { } const key = fieldNames[0]; - return `${key.toLowerCase()}:"${this._escapeQuery(fields[key])}"`; + return `"${key.toLowerCase()}:${this._escapeQuery(fields[key])}"`; } } diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 3c47b14e..dd1fd8e9 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -555,7 +555,7 @@ class Backend { } if (cannotAdd.length > 0) { - const noteIdsArray = await this.anki.findNoteIds(cannotAdd.map((e) => e[0])); + const noteIdsArray = await this.anki.findNoteIds(cannotAdd.map((e) => e[0]), options.anki.duplicateScope); for (let i = 0, ii = Math.min(cannotAdd.length, noteIdsArray.length); i < ii; ++i) { const noteIds = noteIdsArray[i]; if (noteIds.length > 0) { diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index da26b628..8e1814ed 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -201,6 +201,7 @@ function profileOptionsCreateDefaults() { screenshot: {format: 'png', quality: 92}, terms: {deck: '', model: '', fields: {}}, kanji: {deck: '', model: '', fields: {}}, + duplicateScope: 'collection', fieldTemplates: null } }; diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index f03cc631..cf75d629 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -131,6 +131,7 @@ async function formRead(options) { 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.duplicateScope = $('#duplicate-scope').val(); options.anki.screenshot.format = $('#screenshot-format').val(); options.anki.screenshot.quality = parseInt($('#screenshot-quality').val(), 10); @@ -212,6 +213,7 @@ async function formWrite(options) { $('#card-tags').val(options.anki.tags.join(' ')); $('#sentence-detection-extent').val(options.anki.sentenceExt); $('#interface-server').val(options.anki.server); + $('#duplicate-scope').val(options.anki.duplicateScope); $('#screenshot-format').val(options.anki.screenshot.format); $('#screenshot-quality').val(options.anki.screenshot.quality); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index f0236193..b6120b5f 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -820,6 +820,14 @@ +
+ + +
+
-
+