From ae94f84ffd576a338e6baabd3330c45df1a2c59d Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 1 Dec 2019 15:22:37 -0500 Subject: Move settings scripts --- ext/bg/js/settings-dictionaries.js | 618 ----------------------- ext/bg/js/settings-popup-preview.js | 186 ------- ext/bg/js/settings-profiles.js | 281 ----------- ext/bg/js/settings.js | 810 ------------------------------ ext/bg/js/settings/dictionaries.js | 618 +++++++++++++++++++++++ ext/bg/js/settings/main.js | 810 ++++++++++++++++++++++++++++++ ext/bg/js/settings/popup-preview-frame.js | 186 +++++++ ext/bg/js/settings/profiles.js | 281 +++++++++++ ext/bg/settings-popup-preview.html | 2 +- ext/bg/settings.html | 7 +- 10 files changed, 1900 insertions(+), 1899 deletions(-) delete mode 100644 ext/bg/js/settings-dictionaries.js delete mode 100644 ext/bg/js/settings-popup-preview.js delete mode 100644 ext/bg/js/settings-profiles.js delete mode 100644 ext/bg/js/settings.js create mode 100644 ext/bg/js/settings/dictionaries.js create mode 100644 ext/bg/js/settings/main.js create mode 100644 ext/bg/js/settings/popup-preview-frame.js create mode 100644 ext/bg/js/settings/profiles.js diff --git a/ext/bg/js/settings-dictionaries.js b/ext/bg/js/settings-dictionaries.js deleted file mode 100644 index 065a8abc..00000000 --- a/ext/bg/js/settings-dictionaries.js +++ /dev/null @@ -1,618 +0,0 @@ -/* - * Copyright (C) 2019 Alex Yatskov - * Author: Alex Yatskov - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -let dictionaryUI = null; - - -class SettingsDictionaryListUI { - constructor(container, template, extraContainer, extraTemplate) { - this.container = container; - this.template = template; - this.extraContainer = extraContainer; - this.extraTemplate = extraTemplate; - this.optionsDictionaries = null; - this.dictionaries = null; - this.dictionaryEntries = []; - this.extra = null; - - document.querySelector('#dict-delete-confirm').addEventListener('click', (e) => this.onDictionaryConfirmDelete(e), false); - } - - setOptionsDictionaries(optionsDictionaries) { - this.optionsDictionaries = optionsDictionaries; - if (this.dictionaries !== null) { - this.setDictionaries(this.dictionaries); - } - } - - setDictionaries(dictionaries) { - for (const dictionaryEntry of this.dictionaryEntries) { - dictionaryEntry.cleanup(); - } - - this.dictionaryEntries = []; - this.dictionaries = toIterable(dictionaries); - - if (this.optionsDictionaries === null) { - return; - } - - let changed = false; - for (const dictionaryInfo of this.dictionaries) { - if (this.createEntry(dictionaryInfo)) { - changed = true; - } - } - - this.updateDictionaryOrder(); - - const titles = this.dictionaryEntries.map((e) => e.dictionaryInfo.title); - const removeKeys = Object.keys(this.optionsDictionaries).filter((key) => titles.indexOf(key) < 0); - if (removeKeys.length > 0) { - for (const key of toIterable(removeKeys)) { - delete this.optionsDictionaries[key]; - } - changed = true; - } - - if (changed) { - this.save(); - } - } - - createEntry(dictionaryInfo) { - const title = dictionaryInfo.title; - let changed = false; - let optionsDictionary; - const optionsDictionaries = this.optionsDictionaries; - if (hasOwn(optionsDictionaries, title)) { - optionsDictionary = optionsDictionaries[title]; - } else { - optionsDictionary = SettingsDictionaryListUI.createDictionaryOptions(); - optionsDictionaries[title] = optionsDictionary; - changed = true; - } - - const content = document.importNode(this.template.content, true).firstChild; - - this.dictionaryEntries.push(new SettingsDictionaryEntryUI(this, dictionaryInfo, content, optionsDictionary)); - - return changed; - } - - static createDictionaryOptions() { - return utilBackgroundIsolate({ - priority: 0, - enabled: false, - allowSecondarySearches: false - }); - } - - createExtra(totalCounts, remainders, totalRemainder) { - const content = document.importNode(this.extraTemplate.content, true).firstChild; - this.extraContainer.appendChild(content); - return new SettingsDictionaryExtraUI(this, totalCounts, remainders, totalRemainder, content); - } - - setCounts(dictionaryCounts, totalCounts) { - const remainders = Object.assign({}, totalCounts); - const keys = Object.keys(remainders); - - for (let i = 0, ii = Math.min(this.dictionaryEntries.length, dictionaryCounts.length); i < ii; ++i) { - const counts = dictionaryCounts[i]; - this.dictionaryEntries[i].setCounts(counts); - - for (const key of keys) { - remainders[key] -= counts[key]; - } - } - - let totalRemainder = 0; - for (const key of keys) { - totalRemainder += remainders[key]; - } - - if (this.extra !== null) { - this.extra.cleanup(); - this.extra = null; - } - - if (totalRemainder > 0) { - this.extra = this.createExtra(totalCounts, remainders, totalRemainder); - } - } - - updateDictionaryOrder() { - const sortInfo = this.dictionaryEntries.map((e, i) => [e, i]); - sortInfo.sort((a, b) => { - const i = b[0].optionsDictionary.priority - a[0].optionsDictionary.priority; - return (i !== 0 ? i : a[1] - b[1]); - }); - - for (const [e] of sortInfo) { - this.container.appendChild(e.content); - } - } - - save() { - // Overwrite - } - - onDictionaryConfirmDelete(e) { - e.preventDefault(); - const n = document.querySelector('#dict-delete-modal'); - const title = n.dataset.dict; - delete n.dataset.dict; - $(n).modal('hide'); - - const index = this.dictionaryEntries.findIndex((e) => e.dictionaryInfo.title === title); - if (index >= 0) { - this.dictionaryEntries[index].deleteDictionary(); - } - } -} - -class SettingsDictionaryEntryUI { - constructor(parent, dictionaryInfo, content, optionsDictionary) { - this.parent = parent; - this.dictionaryInfo = dictionaryInfo; - this.optionsDictionary = optionsDictionary; - this.counts = null; - this.eventListeners = []; - this.isDeleting = false; - - this.content = content; - this.enabledCheckbox = this.content.querySelector('.dict-enabled'); - this.allowSecondarySearchesCheckbox = this.content.querySelector('.dict-allow-secondary-searches'); - this.priorityInput = this.content.querySelector('.dict-priority'); - this.deleteButton = this.content.querySelector('.dict-delete-button'); - - if (this.dictionaryInfo.version < 3) { - this.content.querySelector('.dict-outdated').hidden = false; - } - - this.content.querySelector('.dict-title').textContent = this.dictionaryInfo.title; - this.content.querySelector('.dict-revision').textContent = `rev.${this.dictionaryInfo.revision}`; - - this.applyValues(); - - this.addEventListener(this.enabledCheckbox, 'change', (e) => this.onEnabledChanged(e), false); - this.addEventListener(this.allowSecondarySearchesCheckbox, 'change', (e) => this.onAllowSecondarySearchesChanged(e), false); - this.addEventListener(this.priorityInput, 'change', (e) => this.onPriorityChanged(e), false); - this.addEventListener(this.deleteButton, 'click', (e) => this.onDeleteButtonClicked(e), false); - } - - cleanup() { - if (this.content !== null) { - if (this.content.parentNode !== null) { - this.content.parentNode.removeChild(this.content); - } - this.content = null; - } - this.dictionaryInfo = null; - this.clearEventListeners(); - } - - setCounts(counts) { - this.counts = counts; - const node = this.content.querySelector('.dict-counts'); - node.textContent = JSON.stringify({ - info: this.dictionaryInfo, - counts - }, null, 4); - node.removeAttribute('hidden'); - } - - save() { - this.parent.save(); - } - - addEventListener(node, type, listener, options) { - node.addEventListener(type, listener, options); - this.eventListeners.push([node, type, listener, options]); - } - - clearEventListeners() { - for (const [node, type, listener, options] of this.eventListeners) { - node.removeEventListener(type, listener, options); - } - this.eventListeners = []; - } - - applyValues() { - this.enabledCheckbox.checked = this.optionsDictionary.enabled; - this.allowSecondarySearchesCheckbox.checked = this.optionsDictionary.allowSecondarySearches; - this.priorityInput.value = `${this.optionsDictionary.priority}`; - } - - async deleteDictionary() { - if (this.isDeleting) { - return; - } - - const progress = this.content.querySelector('.progress'); - progress.hidden = false; - const progressBar = this.content.querySelector('.progress-bar'); - this.isDeleting = true; - - const prevention = new PageExitPrevention(); - try { - prevention.start(); - - const onProgress = ({processed, count, storeCount, storesProcesed}) => { - let percent = 0.0; - if (count > 0 && storesProcesed > 0) { - percent = (processed / count) * (storesProcesed / storeCount) * 100.0; - } - progressBar.style.width = `${percent}%`; - }; - - await utilDatabaseDeleteDictionary(this.dictionaryInfo.title, onProgress, {rate: 1000}); - } catch (e) { - dictionaryErrorsShow([e]); - } finally { - prevention.end(); - this.isDeleting = false; - progress.hidden = true; - - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - onDatabaseUpdated(options); - } - } - - onEnabledChanged(e) { - this.optionsDictionary.enabled = !!e.target.checked; - this.save(); - } - - onAllowSecondarySearchesChanged(e) { - this.optionsDictionary.allowSecondarySearches = !!e.target.checked; - this.save(); - } - - onPriorityChanged(e) { - let value = Number.parseFloat(e.target.value); - if (Number.isNaN(value)) { - value = this.optionsDictionary.priority; - } else { - this.optionsDictionary.priority = value; - this.save(); - } - - e.target.value = `${value}`; - - this.parent.updateDictionaryOrder(); - } - - onDeleteButtonClicked(e) { - e.preventDefault(); - - if (this.isDeleting) { - return; - } - - const title = this.dictionaryInfo.title; - const n = document.querySelector('#dict-delete-modal'); - n.dataset.dict = title; - document.querySelector('#dict-remove-modal-dict-name').textContent = title; - $(n).modal('show'); - } -} - -class SettingsDictionaryExtraUI { - constructor(parent, totalCounts, remainders, totalRemainder, content) { - this.parent = parent; - this.content = content; - - this.content.querySelector('.dict-total-count').textContent = `${totalRemainder} item${totalRemainder !== 1 ? 's' : ''}`; - - const node = this.content.querySelector('.dict-counts'); - node.textContent = JSON.stringify({ - counts: totalCounts, - remainders: remainders - }, null, 4); - node.removeAttribute('hidden'); - } - - cleanup() { - if (this.content !== null) { - if (this.content.parentNode !== null) { - this.content.parentNode.removeChild(this.content); - } - this.content = null; - } - } -} - - -async function dictSettingsInitialize() { - dictionaryUI = new SettingsDictionaryListUI( - document.querySelector('#dict-groups'), - document.querySelector('#dict-template'), - document.querySelector('#dict-groups-extra'), - document.querySelector('#dict-extra-template') - ); - dictionaryUI.save = () => settingsSaveOptions(); - - document.querySelector('#dict-purge-button').addEventListener('click', (e) => onDictionaryPurgeButtonClick(e), false); - document.querySelector('#dict-purge-confirm').addEventListener('click', (e) => onDictionaryPurge(e), false); - document.querySelector('#dict-file-button').addEventListener('click', (e) => onDictionaryImportButtonClick(e), false); - document.querySelector('#dict-file').addEventListener('change', (e) => onDictionaryImport(e), false); - document.querySelector('#dict-main').addEventListener('change', (e) => onDictionaryMainChanged(e), false); - - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - onDictionaryOptionsChanged(options); - onDatabaseUpdated(options); -} - -async function onDictionaryOptionsChanged(options) { - if (dictionaryUI === null) { return; } - dictionaryUI.setOptionsDictionaries(options.dictionaries); -} - -async function onDatabaseUpdated(options) { - try { - const dictionaries = await utilDatabaseGetDictionaryInfo(); - dictionaryUI.setDictionaries(dictionaries); - - document.querySelector('#dict-warning').hidden = (dictionaries.length > 0); - - updateMainDictionarySelect(options, dictionaries); - - const {counts, total} = await utilDatabaseGetDictionaryCounts(dictionaries.map((v) => v.title), true); - dictionaryUI.setCounts(counts, total); - } catch (e) { - dictionaryErrorsShow([e]); - } -} - -async function updateMainDictionarySelect(options, dictionaries) { - const select = document.querySelector('#dict-main'); - select.textContent = ''; // Empty - - let option = document.createElement('option'); - option.className = 'text-muted'; - option.value = ''; - option.textContent = 'Not selected'; - select.appendChild(option); - - let value = ''; - const currentValue = options.general.mainDictionary; - for (const {title, sequenced} of toIterable(dictionaries)) { - if (!sequenced) { continue; } - - option = document.createElement('option'); - option.value = title; - option.textContent = title; - select.appendChild(option); - - if (title === currentValue) { - value = title; - } - } - - select.value = value; - - if (options.general.mainDictionary !== value) { - options.general.mainDictionary = value; - settingsSaveOptions(); - } -} - -async function onDictionaryMainChanged(e) { - const value = e.target.value; - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - options.general.mainDictionary = value; - settingsSaveOptions(); -} - - -function dictionaryErrorToString(error) { - if (error.toString) { - error = error.toString(); - } else { - error = `${error}`; - } - - for (const [match, subst] of dictionaryErrorToString.overrides) { - if (error.includes(match)) { - error = subst; - break; - } - } - - return error; -} -dictionaryErrorToString.overrides = [ - [ - 'A mutation operation was attempted on a database that did not allow mutations.', - 'Access to IndexedDB appears to be restricted. Firefox seems to require that the history preference is set to "Remember history" before IndexedDB use of any kind is allowed.' - ], - [ - 'The operation failed for reasons unrelated to the database itself and not covered by any other error code.', - 'Unable to access IndexedDB due to a possibly corrupt user profile. Try using the "Refresh Firefox" feature to reset your user profile.' - ], - [ - 'BulkError', - 'Unable to finish importing dictionary data into IndexedDB. This may indicate that you do not have sufficient disk space available to complete this operation.' - ] -]; - -function dictionaryErrorsShow(errors) { - const dialog = document.querySelector('#dict-error'); - dialog.textContent = ''; - - if (errors !== null && errors.length > 0) { - const uniqueErrors = {}; - for (let e of errors) { - console.error(e); - e = dictionaryErrorToString(e); - uniqueErrors[e] = hasOwn(uniqueErrors, e) ? uniqueErrors[e] + 1 : 1; - } - - for (const e in uniqueErrors) { - const count = uniqueErrors[e]; - const div = document.createElement('p'); - if (count > 1) { - div.textContent = `${e} `; - const em = document.createElement('em'); - em.textContent = `(${count})`; - div.appendChild(em); - } else { - div.textContent = `${e}`; - } - dialog.appendChild(div); - } - - dialog.hidden = false; - } else { - dialog.hidden = true; - } -} - - -function dictionarySpinnerShow(show) { - const spinner = $('#dict-spinner'); - if (show) { - spinner.show(); - } else { - spinner.hide(); - } -} - -function onDictionaryImportButtonClick() { - const dictFile = document.querySelector('#dict-file'); - dictFile.click(); -} - -function onDictionaryPurgeButtonClick(e) { - e.preventDefault(); - $('#dict-purge-modal').modal('show'); -} - -async function onDictionaryPurge(e) { - e.preventDefault(); - - $('#dict-purge-modal').modal('hide'); - - const dictControls = $('#dict-importer, #dict-groups, #dict-groups-extra, #dict-main-group').hide(); - const dictProgress = document.querySelector('#dict-purge'); - dictProgress.hidden = false; - - const prevention = new PageExitPrevention(); - - try { - prevention.start(); - dictionaryErrorsShow(null); - dictionarySpinnerShow(true); - - await utilDatabasePurge(); - for (const options of toIterable(await getOptionsArray())) { - options.dictionaries = utilBackgroundIsolate({}); - options.general.mainDictionary = ''; - } - await settingsSaveOptions(); - - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - onDatabaseUpdated(options); - } catch (err) { - dictionaryErrorsShow([err]); - } finally { - prevention.end(); - - dictionarySpinnerShow(false); - - dictControls.show(); - dictProgress.hidden = true; - - if (storageEstimate.mostRecent !== null) { - storageUpdateStats(); - } - } -} - -async function onDictionaryImport(e) { - const dictFile = $('#dict-file'); - const dictControls = $('#dict-importer').hide(); - const dictProgress = $('#dict-import-progress').show(); - const dictImportInfo = document.querySelector('#dict-import-info'); - - const prevention = new PageExitPrevention(); - - try { - prevention.start(); - dictionaryErrorsShow(null); - dictionarySpinnerShow(true); - - const setProgress = (percent) => dictProgress.find('.progress-bar').css('width', `${percent}%`); - const updateProgress = (total, current) => { - setProgress(current / total * 100.0); - if (storageEstimate.mostRecent !== null && !storageUpdateStats.isUpdating) { - storageUpdateStats(); - } - }; - - const exceptions = []; - const files = [...e.target.files]; - - for (let i = 0, ii = files.length; i < ii; ++i) { - setProgress(0.0); - if (ii > 1) { - dictImportInfo.hidden = false; - dictImportInfo.textContent = `(${i + 1} of ${ii})`; - } - - const summary = await utilDatabaseImport(files[i], updateProgress, exceptions); - for (const options of toIterable(await getOptionsArray())) { - const dictionaryOptions = SettingsDictionaryListUI.createDictionaryOptions(); - dictionaryOptions.enabled = true; - options.dictionaries[summary.title] = dictionaryOptions; - if (summary.sequenced && options.general.mainDictionary === '') { - options.general.mainDictionary = summary.title; - } - } - - await settingsSaveOptions(); - - if (exceptions.length > 0) { - exceptions.push(`Dictionary may not have been imported properly: ${exceptions.length} error${exceptions.length === 1 ? '' : 's'} reported.`); - dictionaryErrorsShow(exceptions); - } - - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - onDatabaseUpdated(options); - } - } catch (err) { - dictionaryErrorsShow([err]); - } finally { - prevention.end(); - dictionarySpinnerShow(false); - - dictImportInfo.hidden = false; - dictImportInfo.textContent = ''; - dictFile.val(''); - dictControls.show(); - dictProgress.hide(); - } -} diff --git a/ext/bg/js/settings-popup-preview.js b/ext/bg/js/settings-popup-preview.js deleted file mode 100644 index 49409968..00000000 --- a/ext/bg/js/settings-popup-preview.js +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2019 Alex Yatskov - * Author: Alex Yatskov - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -class SettingsPopupPreview { - constructor() { - this.frontend = null; - this.apiOptionsGetOld = apiOptionsGet; - this.popupInjectOuterStylesheetOld = Popup.injectOuterStylesheet; - this.popupShown = false; - this.themeChangeTimeout = null; - } - - static create() { - const instance = new SettingsPopupPreview(); - instance.prepare(); - return instance; - } - - async prepare() { - // Setup events - window.addEventListener('resize', (e) => this.onWindowResize(e), false); - window.addEventListener('message', (e) => this.onMessage(e), false); - - const themeDarkCheckbox = document.querySelector('#theme-dark-checkbox'); - if (themeDarkCheckbox !== null) { - themeDarkCheckbox.addEventListener('change', () => this.onThemeDarkCheckboxChanged(themeDarkCheckbox), false); - } - - // Overwrite API functions - window.apiOptionsGet = (...args) => this.apiOptionsGet(...args); - - // Overwrite frontend - this.frontend = Frontend.create(); - window.yomichan_frontend = this.frontend; - - this.frontend.setEnabled = function () {}; - this.frontend.searchClear = function () {}; - - this.frontend.popup.childrenSupported = false; - this.frontend.popup.interactive = false; - - await this.frontend.isPrepared(); - - // Overwrite popup - Popup.injectOuterStylesheet = (...args) => this.popupInjectOuterStylesheet(...args); - - // Update search - this.updateSearch(); - } - - async apiOptionsGet(...args) { - const options = await this.apiOptionsGetOld(...args); - options.general.enable = true; - options.general.debugInfo = false; - options.general.popupWidth = 400; - options.general.popupHeight = 250; - options.general.popupHorizontalOffset = 0; - options.general.popupVerticalOffset = 10; - options.general.popupHorizontalOffset2 = 10; - options.general.popupVerticalOffset2 = 0; - options.general.popupHorizontalTextPosition = 'below'; - options.general.popupVerticalTextPosition = 'before'; - options.scanning.selectText = false; - return options; - } - - popupInjectOuterStylesheet(...args) { - // This simulates the stylesheet priorities when injecting using the web extension API. - const result = this.popupInjectOuterStylesheetOld(...args); - - const outerStylesheet = Popup.outerStylesheet; - const node = document.querySelector('#client-css'); - if (node !== null && outerStylesheet !== null) { - node.parentNode.insertBefore(outerStylesheet, node); - } - - return result; - } - - onWindowResize() { - if (this.frontend === null) { return; } - const textSource = this.frontend.textSourceLast; - if (textSource === null) { return; } - - const elementRect = textSource.getRect(); - const writingMode = textSource.getWritingMode(); - this.frontend.popup.showContent(elementRect, writingMode); - } - - onMessage(e) { - const {action, params} = e.data; - const handlers = SettingsPopupPreview.messageHandlers; - if (hasOwn(handlers, action)) { - const handler = handlers[action]; - handler(this, params); - } - } - - onThemeDarkCheckboxChanged(node) { - document.documentElement.classList.toggle('dark', node.checked); - if (this.themeChangeTimeout !== null) { - clearTimeout(this.themeChangeTimeout); - } - this.themeChangeTimeout = setTimeout(() => { - this.themeChangeTimeout = null; - this.frontend.popup.updateTheme(); - }, 300); - } - - setText(text) { - const exampleText = document.querySelector('#example-text'); - if (exampleText === null) { return; } - - exampleText.textContent = text; - this.updateSearch(); - } - - setInfoVisible(visible) { - const node = document.querySelector('.placeholder-info'); - if (node === null) { return; } - - node.classList.toggle('placeholder-info-visible', visible); - } - - setCustomCss(css) { - if (this.frontend === null) { return; } - this.frontend.popup.setCustomCss(css); - } - - setCustomOuterCss(css) { - if (this.frontend === null) { return; } - this.frontend.popup.setCustomOuterCss(css, true); - } - - async updateSearch() { - const exampleText = document.querySelector('#example-text'); - if (exampleText === null) { return; } - - const textNode = exampleText.firstChild; - if (textNode === null) { return; } - - const range = document.createRange(); - range.selectNode(textNode); - const source = new TextSourceRange(range, range.toString(), null); - - try { - await this.frontend.searchSource(source, 'script'); - } finally { - source.cleanup(); - } - await this.frontend.lastShowPromise; - - if (this.frontend.popup.isVisible()) { - this.popupShown = true; - } - - this.setInfoVisible(!this.popupShown); - } -} - -SettingsPopupPreview.messageHandlers = { - setText: (self, {text}) => self.setText(text), - setCustomCss: (self, {css}) => self.setCustomCss(css), - setCustomOuterCss: (self, {css}) => self.setCustomOuterCss(css) -}; - -SettingsPopupPreview.instance = SettingsPopupPreview.create(); - - - diff --git a/ext/bg/js/settings-profiles.js b/ext/bg/js/settings-profiles.js deleted file mode 100644 index 8c218e97..00000000 --- a/ext/bg/js/settings-profiles.js +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) 2019 Alex Yatskov - * Author: Alex Yatskov - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -let currentProfileIndex = 0; -let profileConditionsContainer = null; - -function getOptionsContext() { - return { - index: currentProfileIndex - }; -} - - -async function profileOptionsSetup() { - const optionsFull = await apiOptionsGetFull(); - currentProfileIndex = optionsFull.profileCurrent; - - profileOptionsSetupEventListeners(); - await profileOptionsUpdateTarget(optionsFull); -} - -function profileOptionsSetupEventListeners() { - $('#profile-target').change((e) => onTargetProfileChanged(e)); - $('#profile-name').change((e) => onProfileNameChanged(e)); - $('#profile-add').click((e) => onProfileAdd(e)); - $('#profile-remove').click((e) => onProfileRemove(e)); - $('#profile-remove-confirm').click((e) => onProfileRemoveConfirm(e)); - $('#profile-copy').click((e) => onProfileCopy(e)); - $('#profile-copy-confirm').click((e) => onProfileCopyConfirm(e)); - $('#profile-move-up').click(() => onProfileMove(-1)); - $('#profile-move-down').click(() => onProfileMove(1)); - $('.profile-form').find('input, select, textarea').not('.profile-form-manual').change((e) => onProfileOptionsChanged(e)); -} - -function tryGetIntegerValue(selector, min, max) { - const value = parseInt($(selector).val(), 10); - return ( - typeof value === 'number' && - Number.isFinite(value) && - Math.floor(value) === value && - value >= min && - value < max - ) ? value : null; -} - -async function profileFormRead(optionsFull) { - const profile = optionsFull.profiles[currentProfileIndex]; - - // Current profile - const index = tryGetIntegerValue('#profile-active', 0, optionsFull.profiles.length); - if (index !== null) { - optionsFull.profileCurrent = index; - } - - // Profile name - profile.name = $('#profile-name').val(); -} - -async function profileFormWrite(optionsFull) { - const profile = optionsFull.profiles[currentProfileIndex]; - - profileOptionsPopulateSelect($('#profile-active'), optionsFull.profiles, optionsFull.profileCurrent, null); - profileOptionsPopulateSelect($('#profile-target'), optionsFull.profiles, currentProfileIndex, null); - $('#profile-remove').prop('disabled', optionsFull.profiles.length <= 1); - $('#profile-copy').prop('disabled', optionsFull.profiles.length <= 1); - $('#profile-move-up').prop('disabled', currentProfileIndex <= 0); - $('#profile-move-down').prop('disabled', currentProfileIndex >= optionsFull.profiles.length - 1); - - $('#profile-name').val(profile.name); - - if (profileConditionsContainer !== null) { - profileConditionsContainer.cleanup(); - } - - profileConditionsContainer = new ConditionsUI.Container( - profileConditionsDescriptor, - 'popupLevel', - profile.conditionGroups, - $('#profile-condition-groups'), - $('#profile-add-condition-group') - ); - profileConditionsContainer.save = () => { - settingsSaveOptions(); - conditionsClearCaches(profileConditionsDescriptor); - }; - profileConditionsContainer.isolate = utilBackgroundIsolate; -} - -function profileOptionsPopulateSelect(select, profiles, currentValue, ignoreIndices) { - select.empty(); - - - for (let i = 0; i < profiles.length; ++i) { - if (ignoreIndices !== null && ignoreIndices.indexOf(i) >= 0) { - continue; - } - const profile = profiles[i]; - select.append($(``)); - } - - select.val(`${currentValue}`); -} - -async function profileOptionsUpdateTarget(optionsFull) { - profileFormWrite(optionsFull); - - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - await formWrite(options); -} - -function profileOptionsCreateCopyName(name, profiles, maxUniqueAttempts) { - let space, index, prefix, suffix; - const match = /^([\w\W]*\(Copy)((\s+)(\d+))?(\)\s*)$/.exec(name); - if (match === null) { - prefix = `${name} (Copy`; - space = ''; - index = ''; - suffix = ')'; - } else { - prefix = match[1]; - suffix = match[5]; - if (typeof match[2] === 'string') { - space = match[3]; - index = parseInt(match[4], 10) + 1; - } else { - space = ' '; - index = 2; - } - } - - let i = 0; - while (true) { - const newName = `${prefix}${space}${index}${suffix}`; - if (i++ >= maxUniqueAttempts || profiles.findIndex((profile) => profile.name === newName) < 0) { - return newName; - } - if (typeof index !== 'number') { - index = 2; - space = ' '; - } else { - ++index; - } - } -} - -async function onProfileOptionsChanged(e) { - if (!e.originalEvent && !e.isTrigger) { - return; - } - - const optionsFull = await apiOptionsGetFull(); - await profileFormRead(optionsFull); - await settingsSaveOptions(); -} - -async function onTargetProfileChanged() { - const optionsFull = await apiOptionsGetFull(); - const index = tryGetIntegerValue('#profile-target', 0, optionsFull.profiles.length); - if (index === null || currentProfileIndex === index) { - return; - } - - currentProfileIndex = index; - - await profileOptionsUpdateTarget(optionsFull); -} - -async function onProfileAdd() { - const optionsFull = await apiOptionsGetFull(); - const profile = 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(); -} - -async function onProfileRemove(e) { - if (e.shiftKey) { - return await onProfileRemoveConfirm(); - } - - const optionsFull = await apiOptionsGetFull(); - if (optionsFull.profiles.length <= 1) { - return; - } - - const profile = optionsFull.profiles[currentProfileIndex]; - - $('#profile-remove-modal-profile-name').text(profile.name); - $('#profile-remove-modal').modal('show'); -} - -async function onProfileRemoveConfirm() { - $('#profile-remove-modal').modal('hide'); - - const optionsFull = await apiOptionsGetFull(); - if (optionsFull.profiles.length <= 1) { - return; - } - - optionsFull.profiles.splice(currentProfileIndex, 1); - - if (currentProfileIndex >= optionsFull.profiles.length) { - --currentProfileIndex; - } - - if (optionsFull.profileCurrent >= optionsFull.profiles.length) { - optionsFull.profileCurrent = optionsFull.profiles.length - 1; - } - - await profileOptionsUpdateTarget(optionsFull); - await settingsSaveOptions(); -} - -function onProfileNameChanged() { - $('#profile-active, #profile-target').find(`[value="${currentProfileIndex}"]`).text(this.value); -} - -async function onProfileMove(offset) { - const optionsFull = await apiOptionsGetFull(); - const index = currentProfileIndex + offset; - if (index < 0 || index >= optionsFull.profiles.length) { - return; - } - - const profile = optionsFull.profiles[currentProfileIndex]; - optionsFull.profiles.splice(currentProfileIndex, 1); - optionsFull.profiles.splice(index, 0, profile); - - if (optionsFull.profileCurrent === currentProfileIndex) { - optionsFull.profileCurrent = index; - } - - currentProfileIndex = index; - - await profileOptionsUpdateTarget(optionsFull); - await settingsSaveOptions(); -} - -async function onProfileCopy() { - const optionsFull = await apiOptionsGetFull(); - if (optionsFull.profiles.length <= 1) { - return; - } - - profileOptionsPopulateSelect($('#profile-copy-source'), optionsFull.profiles, currentProfileIndex === 0 ? 1 : 0, [currentProfileIndex]); - $('#profile-copy-modal').modal('show'); -} - -async function onProfileCopyConfirm() { - $('#profile-copy-modal').modal('hide'); - - const optionsFull = await apiOptionsGetFull(); - const index = tryGetIntegerValue('#profile-copy-source', 0, optionsFull.profiles.length); - if (index === null || index === currentProfileIndex) { - return; - } - - const profileOptions = utilBackgroundIsolate(optionsFull.profiles[index].options); - optionsFull.profiles[currentProfileIndex].options = profileOptions; - - await profileOptionsUpdateTarget(optionsFull); - await settingsSaveOptions(); -} diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js deleted file mode 100644 index 9e9604e2..00000000 --- a/ext/bg/js/settings.js +++ /dev/null @@ -1,810 +0,0 @@ -/* - * Copyright (C) 2016-2017 Alex Yatskov - * Author: Alex Yatskov - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -async function getOptionsArray() { - const optionsFull = await apiOptionsGetFull(); - return optionsFull.profiles.map((profile) => profile.options); -} - -async function formRead(options) { - options.general.enable = $('#enable').prop('checked'); - options.general.showGuide = $('#show-usage-guide').prop('checked'); - options.general.compactTags = $('#compact-tags').prop('checked'); - options.general.compactGlossaries = $('#compact-glossaries').prop('checked'); - options.general.resultOutputMode = $('#result-output-mode').val(); - options.general.debugInfo = $('#show-debug-info').prop('checked'); - options.general.showAdvanced = $('#show-advanced-options').prop('checked'); - options.general.maxResults = parseInt($('#max-displayed-results').val(), 10); - options.general.popupDisplayMode = $('#popup-display-mode').val(); - options.general.popupHorizontalTextPosition = $('#popup-horizontal-text-position').val(); - options.general.popupVerticalTextPosition = $('#popup-vertical-text-position').val(); - options.general.popupWidth = parseInt($('#popup-width').val(), 10); - options.general.popupHeight = parseInt($('#popup-height').val(), 10); - options.general.popupHorizontalOffset = parseInt($('#popup-horizontal-offset').val(), 0); - options.general.popupVerticalOffset = parseInt($('#popup-vertical-offset').val(), 10); - options.general.popupHorizontalOffset2 = parseInt($('#popup-horizontal-offset2').val(), 0); - options.general.popupVerticalOffset2 = parseInt($('#popup-vertical-offset2').val(), 10); - options.general.popupTheme = $('#popup-theme').val(); - options.general.popupOuterTheme = $('#popup-outer-theme').val(); - options.general.customPopupCss = $('#custom-popup-css').val(); - options.general.customPopupOuterCss = $('#custom-popup-outer-css').val(); - - options.audio.enabled = $('#audio-playback-enabled').prop('checked'); - options.audio.autoPlay = $('#auto-play-audio').prop('checked'); - options.audio.volume = parseFloat($('#audio-playback-volume').val()); - options.audio.customSourceUrl = $('#audio-custom-source').val(); - options.audio.textToSpeechVoice = $('#text-to-speech-voice').val(); - - options.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked'); - options.scanning.touchInputEnabled = $('#touch-input-enabled').prop('checked'); - options.scanning.selectText = $('#select-matched-text').prop('checked'); - options.scanning.alphanumeric = $('#search-alphanumeric').prop('checked'); - options.scanning.autoHideResults = $('#auto-hide-results').prop('checked'); - options.scanning.deepDomScan = $('#deep-dom-scan').prop('checked'); - options.scanning.enablePopupSearch = $('#enable-search-within-first-popup').prop('checked'); - options.scanning.enableOnPopupExpressions = $('#enable-scanning-of-popup-expressions').prop('checked'); - options.scanning.enableOnSearchPage = $('#enable-scanning-on-search-page').prop('checked'); - options.scanning.delay = parseInt($('#scan-delay').val(), 10); - options.scanning.length = parseInt($('#scan-length').val(), 10); - options.scanning.modifier = $('#scan-modifier-key').val(); - options.scanning.popupNestingMaxDepth = parseInt($('#popup-nesting-max-depth').val(), 10); - - options.parsing.enableScanningParser = $('#parsing-scan-enable').prop('checked'); - options.parsing.enableMecabParser = $('#parsing-mecab-enable').prop('checked'); - options.parsing.readingMode = $('#parsing-reading-mode').val(); - - const optionsAnkiEnableOld = options.anki.enable; - options.anki.enable = $('#anki-enable').prop('checked'); - options.anki.tags = utilBackgroundIsolate($('#card-tags').val().split(/[,; ]+/)); - options.anki.sentenceExt = parseInt($('#sentence-detection-extent').val(), 10); - options.anki.server = $('#interface-server').val(); - options.anki.screenshot.format = $('#screenshot-format').val(); - options.anki.screenshot.quality = parseInt($('#screenshot-quality').val(), 10); - options.anki.fieldTemplates = $('#field-templates').val(); - - if (optionsAnkiEnableOld && !ankiErrorShown()) { - options.anki.terms.deck = $('#anki-terms-deck').val(); - options.anki.terms.model = $('#anki-terms-model').val(); - options.anki.terms.fields = utilBackgroundIsolate(ankiFieldsToDict($('#terms .anki-field-value'))); - options.anki.kanji.deck = $('#anki-kanji-deck').val(); - options.anki.kanji.model = $('#anki-kanji-model').val(); - options.anki.kanji.fields = utilBackgroundIsolate(ankiFieldsToDict($('#kanji .anki-field-value'))); - } -} - -async function formWrite(options) { - $('#enable').prop('checked', options.general.enable); - $('#show-usage-guide').prop('checked', options.general.showGuide); - $('#compact-tags').prop('checked', options.general.compactTags); - $('#compact-glossaries').prop('checked', options.general.compactGlossaries); - $('#result-output-mode').val(options.general.resultOutputMode); - $('#show-debug-info').prop('checked', options.general.debugInfo); - $('#show-advanced-options').prop('checked', options.general.showAdvanced); - $('#max-displayed-results').val(options.general.maxResults); - $('#popup-display-mode').val(options.general.popupDisplayMode); - $('#popup-horizontal-text-position').val(options.general.popupHorizontalTextPosition); - $('#popup-vertical-text-position').val(options.general.popupVerticalTextPosition); - $('#popup-width').val(options.general.popupWidth); - $('#popup-height').val(options.general.popupHeight); - $('#popup-horizontal-offset').val(options.general.popupHorizontalOffset); - $('#popup-vertical-offset').val(options.general.popupVerticalOffset); - $('#popup-horizontal-offset2').val(options.general.popupHorizontalOffset2); - $('#popup-vertical-offset2').val(options.general.popupVerticalOffset2); - $('#popup-theme').val(options.general.popupTheme); - $('#popup-outer-theme').val(options.general.popupOuterTheme); - $('#custom-popup-css').val(options.general.customPopupCss); - $('#custom-popup-outer-css').val(options.general.customPopupOuterCss); - - $('#audio-playback-enabled').prop('checked', options.audio.enabled); - $('#auto-play-audio').prop('checked', options.audio.autoPlay); - $('#audio-playback-volume').val(options.audio.volume); - $('#audio-custom-source').val(options.audio.customSourceUrl); - $('#text-to-speech-voice').val(options.audio.textToSpeechVoice).attr('data-value', options.audio.textToSpeechVoice); - - $('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse); - $('#touch-input-enabled').prop('checked', options.scanning.touchInputEnabled); - $('#select-matched-text').prop('checked', options.scanning.selectText); - $('#search-alphanumeric').prop('checked', options.scanning.alphanumeric); - $('#auto-hide-results').prop('checked', options.scanning.autoHideResults); - $('#deep-dom-scan').prop('checked', options.scanning.deepDomScan); - $('#enable-search-within-first-popup').prop('checked', options.scanning.enablePopupSearch); - $('#enable-scanning-of-popup-expressions').prop('checked', options.scanning.enableOnPopupExpressions); - $('#enable-scanning-on-search-page').prop('checked', options.scanning.enableOnSearchPage); - $('#scan-delay').val(options.scanning.delay); - $('#scan-length').val(options.scanning.length); - $('#scan-modifier-key').val(options.scanning.modifier); - $('#popup-nesting-max-depth').val(options.scanning.popupNestingMaxDepth); - - $('#parsing-scan-enable').prop('checked', options.parsing.enableScanningParser); - $('#parsing-mecab-enable').prop('checked', options.parsing.enableMecabParser); - $('#parsing-reading-mode').val(options.parsing.readingMode); - - $('#anki-enable').prop('checked', options.anki.enable); - $('#card-tags').val(options.anki.tags.join(' ')); - $('#sentence-detection-extent').val(options.anki.sentenceExt); - $('#interface-server').val(options.anki.server); - $('#screenshot-format').val(options.anki.screenshot.format); - $('#screenshot-quality').val(options.anki.screenshot.quality); - $('#field-templates').val(options.anki.fieldTemplates); - - onAnkiTemplatesValidateCompile(); - await onDictionaryOptionsChanged(options); - - try { - await ankiDeckAndModelPopulate(options); - } catch (e) { - ankiErrorShow(e); - } - - formUpdateVisibility(options); -} - -function formSetupEventListeners() { - $('input, select, textarea').not('.anki-model').not('.ignore-form-changes *').change((e) => onFormOptionsChanged(e)); - $('.anki-model').change((e) => onAnkiModelChanged(e)); -} - -function formUpdateVisibility(options) { - document.documentElement.dataset.optionsAnkiEnable = `${!!options.anki.enable}`; - document.documentElement.dataset.optionsGeneralDebugInfo = `${!!options.general.debugInfo}`; - document.documentElement.dataset.optionsGeneralShowAdvanced = `${!!options.general.showAdvanced}`; - document.documentElement.dataset.optionsGeneralResultOutputMode = `${options.general.resultOutputMode}`; - - if (options.general.debugInfo) { - const temp = utilIsolate(options); - temp.anki.fieldTemplates = '...'; - const text = JSON.stringify(temp, null, 4); - $('#debug').text(text); - } -} - -async function onFormOptionsChanged(e) { - if (!e.originalEvent && !e.isTrigger) { - return; - } - - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - const optionsAnkiEnableOld = options.anki.enable; - const optionsAnkiServerOld = options.anki.server; - - await formRead(options); - await settingsSaveOptions(); - formUpdateVisibility(options); - - try { - const ankiUpdated = - options.anki.enable !== optionsAnkiEnableOld || - options.anki.server !== optionsAnkiServerOld; - - if (ankiUpdated) { - ankiSpinnerShow(true); - await ankiDeckAndModelPopulate(options); - ankiErrorShow(); - } - } catch (error) { - ankiErrorShow(error); - } finally { - ankiSpinnerShow(false); - } -} - -async function onReady() { - showExtensionInformation(); - - formSetupEventListeners(); - appearanceInitialize(); - await audioSettingsInitialize(); - await profileOptionsSetup(); - await dictSettingsInitialize(); - ankiTemplatesInitialize(); - - storageInfoInitialize(); - - chrome.runtime.onMessage.addListener(onMessage); -} - -$(document).ready(() => onReady()); - - -/* - * Appearance - */ - -function appearanceInitialize() { - let previewVisible = false; - $('#settings-popup-preview-button').on('click', () => { - if (previewVisible) { return; } - showAppearancePreview(); - previewVisible = true; - }); -} - -function showAppearancePreview() { - const container = $('#settings-popup-preview-container'); - const buttonContainer = $('#settings-popup-preview-button-container'); - const settings = $('#settings-popup-preview-settings'); - const text = $('#settings-popup-preview-text'); - const customCss = $('#custom-popup-css'); - const customOuterCss = $('#custom-popup-outer-css'); - - const frame = document.createElement('iframe'); - frame.src = '/bg/settings-popup-preview.html'; - frame.id = 'settings-popup-preview-frame'; - - window.wanakana.bind(text[0]); - - text.on('input', () => { - const action = 'setText'; - const params = {text: text.val()}; - frame.contentWindow.postMessage({action, params}, '*'); - }); - customCss.on('input', () => { - const action = 'setCustomCss'; - const params = {css: customCss.val()}; - frame.contentWindow.postMessage({action, params}, '*'); - }); - customOuterCss.on('input', () => { - const action = 'setCustomOuterCss'; - const params = {css: customOuterCss.val()}; - frame.contentWindow.postMessage({action, params}, '*'); - }); - - container.append(frame); - buttonContainer.remove(); - settings.css('display', ''); -} - - -/* - * Audio - */ - -let audioSourceUI = null; - -async function audioSettingsInitialize() { - const optionsContext = getOptionsContext(); - const options = await apiOptionsGet(optionsContext); - audioSourceUI = new AudioSourceUI.Container(options.audio.sources, $('.audio-source-list'), $('.audio-source-add')); - audioSourceUI.save = () => settingsSaveOptions(); - - textToSpeechInitialize(); -} - -function textToSpeechInitialize() { - if (typeof speechSynthesis === 'undefined') { return; } - - speechSynthesis.addEventListener('voiceschanged', () => updateTextToSpeechVoices(), false); - updateTextToSpeechVoices(); - - $('#text-to-speech-voice-test').on('click', () => textToSpeechTest()); -} - -function updateTextToSpeechVoices() { - const voices = Array.prototype.map.call(speechSynthesis.getVoices(), (voice, index) => ({voice, index})); - voices.sort(textToSpeechVoiceCompare); - if (voices.length > 0) { - $('#text-to-speech-voice-container').css('display', ''); - } - - const select = $('#text-to-speech-voice'); - select.empty(); - select.append($('