diff options
-rw-r--r-- | ext/bg/js/api.js | 17 | ||||
-rw-r--r-- | ext/bg/js/backend.js | 19 | ||||
-rw-r--r-- | ext/bg/js/options.js | 138 | ||||
-rw-r--r-- | ext/bg/settings.html | 79 | ||||
-rw-r--r-- | ext/fg/js/api.js | 4 | ||||
-rw-r--r-- | ext/fg/js/frontend.js | 2 |
6 files changed, 145 insertions, 114 deletions
diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index b8ef4362..9839aef5 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -17,16 +17,16 @@ */ -async function apiOptionsSet(options) { - utilBackend().onOptionsUpdated(options); +function apiOptionsGetSync() { + return utilBackend().options; } async function apiOptionsGet() { - return utilBackend().options; + return apiOptionsGetSync(); } async function apiTermsFind(text) { - const options = utilBackend().options; + const options = apiOptionsGetSync(); const translator = utilBackend().translator; const searcher = { @@ -48,13 +48,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 +83,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,10 +131,9 @@ 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); } }; diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 39fd4288..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,16 +54,12 @@ 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 => { 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); } }); } @@ -85,10 +82,6 @@ class Backend { forward(apiOptionsGet(), callback); }, - optionsSet: ({options, callback}) => { - forward(apiOptionsSet(options), callback); - }, - kanjiFind: ({text, callback}) => { forward(apiKanjiFind(text), callback); }, @@ -148,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 df95aae9..69c662e6 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"}} @@ -183,8 +234,8 @@ function optionsFieldTemplates() { `.trim(); } -function optionsSetDefaults(options) { - const defaults = { +function optionsCreateDefaults() { + return { general: { enable: true, audioSource: 'jpod101', @@ -238,6 +289,10 @@ function optionsSetDefaults(options) { fieldTemplates: optionsFieldTemplates() } }; +} + +function optionsSetDefaults(options) { + const defaults = optionsCreateDefaults(); const combine = (target, source) => { for (const key in source) { @@ -258,70 +313,29 @@ 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); - if (!options.hasOwnProperty('version')) { - options.version = fixups.length; - } - - while (options.version < fixups.length) { - fixups[options.version++](); - } - - return options; + return optionsApplyUpdates(options, optionsVersionUpdates); } 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); @@ -329,9 +343,9 @@ 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); + utilBackend().onOptionsUpdated(options); }); } diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 85b7ee5f..ddda8303 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; @@ -49,6 +53,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; + } + } </style> </head> <body> @@ -107,6 +121,16 @@ </select> </div> + <div class="form-group options-advanced"> + <label for="audio-playback-volume">Audio playback volume <span class="label-light">(percent)</span></label> + <input type="number" min="0" max="100" id="audio-playback-volume" class="form-control"> + </div> + + <div class="form-group options-advanced"> + <label for="max-displayed-results">Maximum displayed results</label> + <input type="number" min="1" id="max-displayed-results" class="form-control"> + </div> + <div class="form-group"> <div class="row"> <div class="col-xs-6"> @@ -130,36 +154,41 @@ </div> <div class="form-group options-advanced"> - <label for="audio-playback-volume">Audio playback volume (percent)</label> - <input type="number" min="0" max="100" id="audio-playback-volume" class="form-control"> - </div> - - <div class="form-group options-advanced"> - <label for="max-displayed-results">Maximum displayed results</label> - <input type="number" min="1" id="max-displayed-results" class="form-control"> - </div> - - <div class="form-group options-advanced"> - <label>Popup size (width × height, in pixels)</label> <div class="row"> - <div class="col-xs-6"><input type="number" min="1" id="popup-width" class="form-control"></div> - <div class="col-xs-6"><input type="number" min="1" id="popup-height" class="form-control"></div> + <div class="col-xs-6"> + <label for="popup-display-mode">Popup width <span class="label-light">(in pixels)</span></label> + <input type="number" min="1" id="popup-width" class="form-control"> + </div> + <div class="col-xs-6"> + <label for="popup-display-mode">Popup height <span class="label-light">(in pixels)</span></label> + <input type="number" min="1" id="popup-height" class="form-control"> + </div> </div> </div> <div class="form-group options-advanced"> - <label>Popup offset (horizontal, vertical; in pixels)</label> <div class="row"> - <div class="col-xs-6"><input type="number" min="0" id="popup-horizontal-offset" class="form-control"></div> - <div class="col-xs-6"><input type="number" min="0" id="popup-vertical-offset" class="form-control"></div> + <div class="col-xs-6"> + <label for="popup-display-mode">Horizontal popup offset <span class="label-light">(in pixels)</span></label> + <input type="number" min="0" id="popup-horizontal-offset" class="form-control"> + </div> + <div class="col-xs-6"> + <label for="popup-display-mode">Vertical popup offset <span class="label-light">(in pixels)</span></label> + <input type="number" min="0" id="popup-vertical-offset" class="form-control"> + </div> </div> </div> <div class="form-group options-advanced"> - <label>Popup offset for vertical text (horizontal, vertical; in pixels)</label> <div class="row"> - <div class="col-xs-6"><input type="number" min="0" id="popup-horizontal-offset2" class="form-control"></div> - <div class="col-xs-6"><input type="number" min="0" id="popup-vertical-offset2" class="form-control"></div> + <div class="col-xs-6"> + <label for="popup-display-mode">Horizontal popup offset for vertical text <span class="label-light">(in pixels)</span></label> + <input type="number" min="0" id="popup-horizontal-offset2" class="form-control"> + </div> + <div class="col-xs-6"> + <label for="popup-display-mode">Vertical popup offset for vertical text <span class="label-light">(in pixels)</span></label> + <input type="number" min="0" id="popup-vertical-offset2" class="form-control"> + </div> </div> </div> @@ -205,12 +234,12 @@ </div> <div class="form-group options-advanced"> - <label for="scan-delay">Scan delay (in milliseconds)</label> + <label for="scan-delay">Scan delay <span class="label-light">(in milliseconds)</span></label> <input type="number" min="1" id="scan-delay" class="form-control"> </div> <div class="form-group options-advanced"> - <label for="scan-length">Scan length (in characters)</label> + <label for="scan-length">Scan length <span class="label-light">(in characters)</span></label> <input type="number" min="1" id="scan-length" class="form-control"> </div> @@ -326,12 +355,12 @@ <div id="anki-general"> <div class="form-group"> - <label for="card-tags">Card tags (comma or space separated)</label> + <label for="card-tags">Card tags <span class="label-light">(comma or space separated)</span></label> <input type="text" id="card-tags" class="form-control"> </div> <div class="form-group options-advanced"> - <label for="sentence-detection-extent">Sentence detection extent (in characters)</label> + <label for="sentence-detection-extent">Sentence detection extent <span class="label-light">(in characters)</span></label> <input type="number" min="1" id="sentence-detection-extent" class="form-control"> </div> @@ -349,7 +378,7 @@ </div> <div class="form-group options-advanced"> - <label for="screenshot-quality">Screenshot quality (JPEG only)</label> + <label for="screenshot-quality">Screenshot quality <span class="label-light">(JPEG only)</span></label> <input type="number" min="0" max="100" step="1" id="screenshot-quality" class="form-control"> </div> @@ -413,7 +442,7 @@ their Anki cards. If you encounter problems with your changes you can always <a href="#" id="field-templates-reset">reset to default</a> template settings. </p> - <textarea class="form-control" rows="10" id="field-templates"></textarea> + <textarea autocomplete="off" spellcheck="false" wrap="soft" class="form-control" rows="10" id="field-templates"></textarea> </div> </div> </div> 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'); } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 079a7ef2..52620933 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(); |