diff options
| -rw-r--r-- | ext/bg/css/settings.css | 5 | ||||
| -rw-r--r-- | ext/bg/js/settings/dictionary-controller.js | 608 | ||||
| -rw-r--r-- | ext/bg/js/settings/dictionary-import-controller.js | 5 | ||||
| -rw-r--r-- | ext/bg/settings.html | 11 | ||||
| -rw-r--r-- | ext/mixed/js/dom-data-binder.js | 3 | 
5 files changed, 251 insertions, 381 deletions
| diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css index 17bb4ac0..4a49b98d 100644 --- a/ext/bg/css/settings.css +++ b/ext/bg/css/settings.css @@ -377,6 +377,11 @@ html:root[data-operating-system=openbsd] [data-hide-for-operating-system~=openbs      display: none;  } +#dict-groups { +    display: flex; +    flex-flow: column; +} +  .dict-details-container {      margin: 0.5em 0;  } diff --git a/ext/bg/js/settings/dictionary-controller.js b/ext/bg/js/settings/dictionary-controller.js index eccb0e88..75022d1f 100644 --- a/ext/bg/js/settings/dictionary-controller.js +++ b/ext/bg/js/settings/dictionary-controller.js @@ -1,5 +1,5 @@  /* - * Copyright (C) 2019-2020  Yomichan Authors + * Copyright (C) 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 @@ -16,202 +16,90 @@   */  /* global + * ObjectPropertyAccessor   * api - * utilBackgroundIsolate   */ -class SettingsDictionaryListUI extends EventDispatcher { -    constructor(container, template, extraContainer, extraTemplate) { -        super(); -        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', this.onDictionaryConfirmDelete.bind(this), false); +class DictionaryEntry { +    constructor(dictionaryController, node, dictionaryInfo) { +        this._dictionaryController = dictionaryController; +        this._node = node; +        this._dictionaryInfo = dictionaryInfo; +        this._dictionaryTitle = dictionaryInfo.title; +        this._eventListeners = new EventListenerCollection(); +        this._enabledCheckbox = node.querySelector('.dict-enabled'); +        this._allowSecondarySearchesCheckbox = node.querySelector('.dict-allow-secondary-searches'); +        this._priorityInput = node.querySelector('.dict-priority'); +        this._deleteButton = node.querySelector('.dict-delete-button'); +        this._detailsToggleLink = node.querySelector('.dict-details-toggle-link'); +        this._detailsContainer = node.querySelector('.dict-details'); +        this._detailsTable = node.querySelector('.dict-details-table');      } -    setOptionsDictionaries(optionsDictionaries) { -        this.optionsDictionaries = optionsDictionaries; -        if (this.dictionaries !== null) { -            this.setDictionaries(this.dictionaries); -        } +    get node() { +        return this._node;      } -    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(); -        } +    get dictionaryTitle() { +        return this._dictionaryTitle;      } -    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; -        } +    prepare() { +        const node = this._node; +        const dictionaryInfo = this._dictionaryInfo; +        const {title, revision, prefixWildcardsSupported} = dictionaryInfo; -        const content = document.importNode(this.template.content, true).firstChild; +        if (dictionaryInfo.version < 3) { +            node.querySelector('.dict-outdated').hidden = false; +        } -        this.dictionaryEntries.push(new SettingsDictionaryEntryUI(this, dictionaryInfo, content, optionsDictionary)); +        node.querySelector('.dict-title').textContent = title; +        node.querySelector('.dict-revision').textContent = `rev.${revision}`; +        node.querySelector('.dict-prefix-wildcard-searches-supported').checked = !!prefixWildcardsSupported; -        return changed; -    } +        this._setupDetails(dictionaryInfo); -    static createDictionaryOptions() { -        return utilBackgroundIsolate({ -            priority: 0, -            enabled: false, -            allowSecondarySearches: false -        }); -    } +        this._enabledCheckbox.dataset.setting = ObjectPropertyAccessor.getPathString(['dictionaries', title, 'enabled']); +        this._allowSecondarySearchesCheckbox.dataset.setting = ObjectPropertyAccessor.getPathString(['dictionaries', title, 'allowSecondarySearches']); +        this._priorityInput.dataset.setting = ObjectPropertyAccessor.getPathString(['dictionaries', title, 'priority']); -    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); +        this._eventListeners.addEventListener(this._deleteButton, 'click', this._onDeleteButtonClicked.bind(this), false); +        this._eventListeners.addEventListener(this._detailsToggleLink, 'click', this._onDetailsToggleLinkClicked.bind(this), false); +        this._eventListeners.addEventListener(this._priorityInput, 'settingChanged', this._onPriorityChanged.bind(this), false);      } -    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); +    cleanup() { +        this._eventListeners.removeAllEventListeners(); +        const node = this._node; +        if (node.parentNode !== null) { +            node.parentNode.removeChild(node);          }      } -    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); -        } +    setCounts(counts) { +        const node = this._node.querySelector('.dict-counts'); +        node.textContent = JSON.stringify({info: this._dictionaryInfo, counts}, null, 4); +        node.hidden = false;      } -    save() { -        // Overwrite -    } +    // Private -    preventPageExit() { -        // Overwrite -        return {end: () => {}}; +    _onDeleteButtonClicked(e) { +        e.preventDefault(); +        this._dictionaryController.deleteDictionary(this._dictionaryTitle);      } -    onDictionaryConfirmDelete(e) { +    _onDetailsToggleLinkClicked(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((entry) => entry.dictionaryInfo.title === title); -        if (index >= 0) { -            this.dictionaryEntries[index].deleteDictionary(); -        } +        this._detailsContainer.hidden = !this._detailsContainer.hidden;      } -} - -class SettingsDictionaryEntryUI { -    constructor(parent, dictionaryInfo, content, optionsDictionary) { -        this.parent = parent; -        this.dictionaryInfo = dictionaryInfo; -        this.optionsDictionary = optionsDictionary; -        this.counts = null; -        this.eventListeners = new EventListenerCollection(); -        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'); -        this.detailsToggleLink = this.content.querySelector('.dict-details-toggle-link'); -        this.detailsContainer = this.content.querySelector('.dict-details'); -        this.detailsTable = this.content.querySelector('.dict-details-table'); - -        if (this.dictionaryInfo.version < 3) { -            this.content.querySelector('.dict-outdated').hidden = false; -        } - -        this.setupDetails(dictionaryInfo); -        this.content.querySelector('.dict-title').textContent = this.dictionaryInfo.title; -        this.content.querySelector('.dict-revision').textContent = `rev.${this.dictionaryInfo.revision}`; -        this.content.querySelector('.dict-prefix-wildcard-searches-supported').checked = !!this.dictionaryInfo.prefixWildcardsSupported; - -        this.applyValues(); - -        this.eventListeners.addEventListener(this.enabledCheckbox, 'change', this.onEnabledChanged.bind(this), false); -        this.eventListeners.addEventListener(this.allowSecondarySearchesCheckbox, 'change', this.onAllowSecondarySearchesChanged.bind(this), false); -        this.eventListeners.addEventListener(this.priorityInput, 'change', this.onPriorityChanged.bind(this), false); -        this.eventListeners.addEventListener(this.deleteButton, 'click', this.onDeleteButtonClicked.bind(this), false); -        this.eventListeners.addEventListener(this.detailsToggleLink, 'click', this.onDetailsToggleLinkClicked.bind(this), false); +    _onPriorityChanged(e) { +        const {detail: {value}} = e; +        this._node.style.order = `${-value}`;      } -    setupDetails(dictionaryInfo) { +    _setupDetails(dictionaryInfo) {          const targets = [              ['Author', 'author'],              ['URL', 'url'], @@ -219,6 +107,7 @@ class SettingsDictionaryEntryUI {              ['Attribution', 'attribution']          ]; +        const fragment = document.createDocumentFragment();          let count = 0;          for (const [label, key] of targets) {              const info = dictionaryInfo[key]; @@ -238,281 +127,252 @@ class SettingsDictionaryEntryUI {              n3.textContent = info;              n1.appendChild(n3); -            this.detailsTable.appendChild(n1); +            fragment.appendChild(n1);              ++count;          } -        if (count === 0) { -            this.detailsContainer.hidden = true; -            this.detailsToggleLink.hidden = true; -        } -    } - -    cleanup() { -        if (this.content !== null) { -            if (this.content.parentNode !== null) { -                this.content.parentNode.removeChild(this.content); -            } -            this.content = null; +        if (count > 0) { +            this._detailsTable.appendChild(fragment); +        } else { +            this._detailsContainer.hidden = true; +            this._detailsToggleLink.hidden = true;          } -        this.dictionaryInfo = null; -        this.eventListeners.removeAllEventListeners(); -    } - -    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();      } +} -    applyValues() { -        this.enabledCheckbox.checked = this.optionsDictionary.enabled; -        this.allowSecondarySearchesCheckbox.checked = this.optionsDictionary.allowSecondarySearches; -        this.priorityInput.value = `${this.optionsDictionary.priority}`; +class DictionaryController { +    constructor(settingsController) { +        this._settingsController = settingsController; +        this._dictionaries = null; +        this._dictionaryEntries = []; +        this._databaseStateToken = null; +        this._checkingIntegrity = false; +        this._warningNode = null; +        this._mainDictionarySelect = null; +        this._checkIntegrityButton = null; +        this._dictionaryEntryContainer = null; +        this._integrityExtraInfoContainer = null; +        this._deleteDictionaryModal = null; +        this._integrityExtraInfoNode = null; +        this._isDeleting = false;      } -    async deleteDictionary() { -        if (this.isDeleting) { -            return; -        } +    async prepare() { +        this._warningNode = document.querySelector('#dict-warning'); +        this._mainDictionarySelect = document.querySelector('#dict-main'); +        this._checkIntegrityButton = document.querySelector('#dict-check-integrity'); +        this._dictionaryEntryContainer = document.querySelector('#dict-groups'); +        this._integrityExtraInfoContainer = document.querySelector('#dict-groups-extra'); +        this._deleteDictionaryModal = document.querySelector('#dict-delete-modal'); -        const progress = this.content.querySelector('.progress'); -        progress.hidden = false; -        const progressBar = this.content.querySelector('.progress-bar'); -        this.isDeleting = true; +        yomichan.on('databaseUpdated', this._onDatabaseUpdated.bind(this)); -        const prevention = this.parent.preventPageExit(); -        try { -            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}%`; -            }; +        document.querySelector('#dict-delete-confirm').addEventListener('click', this._onDictionaryConfirmDelete.bind(this), false); +        this._checkIntegrityButton.addEventListener('click', this._onCheckIntegrityButtonClick.bind(this), false); -            await api.deleteDictionary(this.dictionaryInfo.title, onProgress); -        } catch (e) { -            this.dictionaryErrorsShow([e]); -        } finally { -            prevention.end(); -            this.isDeleting = false; -            progress.hidden = true; -        } +        await this._onDatabaseUpdated();      } -    onEnabledChanged(e) { -        this.optionsDictionary.enabled = !!e.target.checked; -        this.save(); +    deleteDictionary(dictionaryTitle) { +        if (this._isDeleting) { return; } +        const modal = this._deleteDictionaryModal; +        modal.dataset.dictionaryTitle = dictionaryTitle; +        modal.querySelector('#dict-remove-modal-dict-name').textContent = dictionaryTitle; +        this._setModalVisible(modal, true);      } -    onAllowSecondarySearchesChanged(e) { -        this.optionsDictionary.allowSecondarySearches = !!e.target.checked; -        this.save(); -    } +    // Private -    onPriorityChanged(e) { -        let value = Number.parseFloat(e.target.value); -        if (Number.isNaN(value)) { -            value = this.optionsDictionary.priority; -        } else { -            this.optionsDictionary.priority = value; -            this.save(); +    async _onDatabaseUpdated() { +        const token = {}; +        this._databaseStateToken = token; +        this._dictionaries = null; +        const dictionaries = await api.getDictionaryInfo(); +        if (this._databaseStateToken !== token) { return; } +        this._dictionaries = dictionaries; + +        this._warningNode.hidden = (dictionaries.length > 0); +        this._updateMainDictionarySelectOptions(dictionaries); + +        for (const entry of this._dictionaryEntries) { +            entry.cleanup();          } +        this._dictionaryEntries = []; -        e.target.value = `${value}`; - -        this.parent.updateDictionaryOrder(); +        for (const dictionary of dictionaries) { +            this._createDictionaryEntry(dictionary); +        }      } -    onDeleteButtonClicked(e) { +    _onDictionaryConfirmDelete(e) {          e.preventDefault(); -        if (this.isDeleting) { -            return; -        } +        const modal = this._deleteDictionaryModal; +        this._setModalVisible(modal, false); + +        const title = modal.dataset.dictionaryTitle; +        if (typeof title !== 'string') { return; } +        delete modal.dataset.dictionaryTitle; -        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'); +        this._deleteDictionary(title);      } -    onDetailsToggleLinkClicked(e) { +    _onCheckIntegrityButtonClick(e) {          e.preventDefault(); - -        this.detailsContainer.hidden = !this.detailsContainer.hidden; +        this._checkIntegrity();      } -} -class SettingsDictionaryExtraUI { -    constructor(parent, totalCounts, remainders, totalRemainder, content) { -        this.parent = parent; -        this.content = content; +    _setModalVisible(node, visible) { +        $(node).modal(visible ? 'show' : 'hide'); +    } -        this.content.querySelector('.dict-total-count').textContent = `${totalRemainder} item${totalRemainder !== 1 ? 's' : ''}`; +    _updateMainDictionarySelectOptions(dictionaries) { +        const fragment = document.createDocumentFragment(); -        const node = this.content.querySelector('.dict-counts'); -        node.textContent = JSON.stringify({ -            counts: totalCounts, -            remainders: remainders -        }, null, 4); -        node.removeAttribute('hidden'); -    } +        let option = document.createElement('option'); +        option.className = 'text-muted'; +        option.value = ''; +        option.textContent = 'Not selected'; +        fragment.appendChild(option); -    cleanup() { -        if (this.content !== null) { -            if (this.content.parentNode !== null) { -                this.content.parentNode.removeChild(this.content); -            } -            this.content = null; +        for (const {title, sequenced} of dictionaries) { +            if (!sequenced) { continue; } +            option = document.createElement('option'); +            option.value = title; +            option.textContent = title; +            fragment.appendChild(option);          } -    } -} -class DictionaryController { -    constructor(settingsController) { -        this._settingsController = settingsController; -        this._dictionaryUI = null; +        const select = this._mainDictionarySelect; +        select.textContent = ''; // Empty +        select.appendChild(fragment);      } -    async prepare() { -        this._dictionaryUI = new SettingsDictionaryListUI( -            document.querySelector('#dict-groups'), -            document.querySelector('#dict-template'), -            document.querySelector('#dict-groups-extra'), -            document.querySelector('#dict-extra-template') -        ); -        this._dictionaryUI.save = () => this._settingsController.save(); -        this._dictionaryUI.preventPageExit = this._preventPageExit.bind(this); +    async _checkIntegrity() { +        if (this._dictionaries === null || this._checkingIntegrity || this._isDeleting) { return; } -        document.querySelector('#dict-main').addEventListener('change', this._onDictionaryMainChanged.bind(this), false); -        document.querySelector('#database-enable-prefix-wildcard-searches').addEventListener('change', this._onDatabaseEnablePrefixWildcardSearchesChanged.bind(this), false); +        try { +            this._checkingIntegrity = true; +            this._setButtonsEnabled(false); -        this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this)); +            const token = this._databaseStateToken; +            const dictionaryTitles = this._dictionaries.map(({title}) => title); +            const {counts, total} = await api.getDictionaryCounts(dictionaryTitles, true); +            if (this._databaseStateToken !== token) { return; } -        yomichan.on('databaseUpdated', this._onDatabaseUpdated.bind(this)); +            for (let i = 0, ii = Math.min(counts.length, this._dictionaryEntries.length); i < ii; ++i) { +                const entry = this._dictionaryEntries[i]; +                entry.setCounts(counts[i]); +            } -        await this._onOptionsChanged(); -        await this._onDatabaseUpdated(); +            this._setCounts(counts, total); +        } finally { +            this._setButtonsEnabled(true); +            this._checkingIntegrity = false; +        }      } -    // Private - -    async _onOptionsChanged() { -        const options = await this._settingsController.getOptionsMutable(); +    _setCounts(dictionaryCounts, totalCounts) { +        const remainders = Object.assign({}, totalCounts); +        const keys = Object.keys(remainders); -        this._dictionaryUI.setOptionsDictionaries(options.dictionaries); +        for (const counts of dictionaryCounts) { +            for (const key of keys) { +                remainders[key] -= counts[key]; +            } +        } -        const optionsFull = await this._settingsController.getOptionsFull(); -        document.querySelector('#database-enable-prefix-wildcard-searches').checked = optionsFull.global.database.prefixWildcardsSupported; +        let totalRemainder = 0; +        for (const key of keys) { +            totalRemainder += remainders[key]; +        } -        await this._updateMainDictionarySelectValue(); +        this._cleanupExtra(); +        if (totalRemainder > 0) { +            this.extra = this._createExtra(totalCounts, remainders, totalRemainder); +        }      } -    _updateMainDictionarySelectOptions(dictionaries) { -        const select = document.querySelector('#dict-main'); -        select.textContent = ''; // Empty +    _createExtra(totalCounts, remainders, totalRemainder) { +        const node = this._instantiateTemplate('#dict-extra-template'); +        this._integrityExtraInfoNode = node; -        let option = document.createElement('option'); -        option.className = 'text-muted'; -        option.value = ''; -        option.textContent = 'Not selected'; -        select.appendChild(option); +        node.querySelector('.dict-total-count').textContent = `${totalRemainder} item${totalRemainder !== 1 ? 's' : ''}`; -        for (const {title, sequenced} of toIterable(dictionaries)) { -            if (!sequenced) { continue; } +        const n = node.querySelector('.dict-counts'); +        n.textContent = JSON.stringify({counts: totalCounts, remainders}, null, 4); +        n.hidden = false; -            option = document.createElement('option'); -            option.value = title; -            option.textContent = title; -            select.appendChild(option); -        } +        this._integrityExtraInfoContainer.appendChild(node);      } -    async _updateMainDictionarySelectValue() { -        const options = await this._settingsController.getOptions(); +    _cleanupExtra() { +        const node = this._integrityExtraInfoNode; +        if (node === null) { return; } +        this._integrityExtraInfoNode = null; -        const value = options.general.mainDictionary; +        const parent = node.parentNode; +        if (parent === null) { return; } -        const select = document.querySelector('#dict-main'); -        let selectValue = null; -        for (const child of select.children) { -            if (child.nodeName.toUpperCase() === 'OPTION' && child.value === value) { -                selectValue = value; -                break; -            } -        } +        parent.removeChild(node); +    } -        let missingNodeOption = select.querySelector('option[data-not-installed=true]'); -        if (selectValue === null) { -            if (missingNodeOption === null) { -                missingNodeOption = document.createElement('option'); -                missingNodeOption.className = 'text-muted'; -                missingNodeOption.value = value; -                missingNodeOption.textContent = `${value} (Not installed)`; -                missingNodeOption.dataset.notInstalled = 'true'; -                select.appendChild(missingNodeOption); -            } -        } else { -            if (missingNodeOption !== null) { -                missingNodeOption.parentNode.removeChild(missingNodeOption); -            } -        } +    _createDictionaryEntry(dictionary) { +        const node = this._instantiateTemplate('#dict-template'); +        this._dictionaryEntryContainer.appendChild(node); -        select.value = value; +        const entry = new DictionaryEntry(this, node, dictionary); +        this._dictionaryEntries.push(entry); +        entry.prepare();      } -    async _onDatabaseUpdated() { +    async _deleteDictionary(dictionaryTitle) { +        if (this._isDeleting || this._checkingIntegrity) { return; } + +        const index = this._dictionaryEntries.findIndex((entry) => entry.dictionaryTitle === dictionaryTitle); +        if (index < 0) { return; } + +        const entry = this._dictionaryEntries[index]; +        const node = entry.node; +        const progress = node.querySelector('.progress'); +        const progressBar = node.querySelector('.progress-bar'); +        const prevention = this._settingsController.preventPageExit();          try { -            const dictionaries = await api.getDictionaryInfo(); -            this._dictionaryUI.setDictionaries(dictionaries); +            this._isDeleting = true; +            this._setButtonsEnabled(false); -            document.querySelector('#dict-warning').hidden = (dictionaries.length > 0); +            progress.hidden = false; -            this._updateMainDictionarySelectOptions(dictionaries); -            await this._updateMainDictionarySelectValue(); +            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}%`; +            }; -            const {counts, total} = await api.getDictionaryCounts(dictionaries.map((v) => v.title), true); -            this._dictionaryUI.setCounts(counts, total); +            await api.deleteDictionary(dictionaryTitle, onProgress);          } catch (e) {              yomichan.logError(e); +        } finally { +            prevention.end(); +            progress.hidden = true; +            this._setButtonsEnabled(true); +            this._isDeleting = false;          }      } -    async _onDictionaryMainChanged(e) { -        const select = e.target; -        const value = select.value; - -        const missingNodeOption = select.querySelector('option[data-not-installed=true]'); -        if (missingNodeOption !== null && missingNodeOption.value !== value) { -            missingNodeOption.parentNode.removeChild(missingNodeOption); +    _setButtonsEnabled(value) { +        value = !value; +        for (const node of document.querySelectorAll('.dictionary-modifying-input')) { +            node.disabled = value;          } - -        const options = await this._settingsController.getOptionsMutable(); -        options.general.mainDictionary = value; -        await this._settingsController.save(); -    } - -    async _onDatabaseEnablePrefixWildcardSearchesChanged(e) { -        const optionsFull = await this._settingsController.getOptionsFullMutable(); -        const v = !!e.target.checked; -        if (optionsFull.global.database.prefixWildcardsSupported === v) { return; } -        optionsFull.global.database.prefixWildcardsSupported = !!e.target.checked; -        await this._settingsController.save();      } -    _preventPageExit() { -        return this._settingsController.preventPageExit(); +    _instantiateTemplate(templateSelector) { +        const template = document.querySelector(templateSelector); +        const content = document.importNode(template.content, true); +        return content.firstChild;      }  } diff --git a/ext/bg/js/settings/dictionary-import-controller.js b/ext/bg/js/settings/dictionary-import-controller.js index cce695d0..dd4889dc 100644 --- a/ext/bg/js/settings/dictionary-import-controller.js +++ b/ext/bg/js/settings/dictionary-import-controller.js @@ -304,8 +304,9 @@ class DictionaryImportController {      _setButtonsEnabled(value) {          value = !value; -        this._purgeButton.disabled = value; -        this._importFileButton.disabled = value; +        for (const node of document.querySelectorAll('.dictionary-modifying-input')) { +            node.disabled = value; +        }      }      async _getPreparedDictionaryDatabase() { diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 8eeda187..f46282e5 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -653,7 +653,7 @@                  <div class="form-group" id="dict-main-group">                      <label for="dict-main">Main dictionary for merged mode</label> -                    <select class="form-control" id="dict-main"></select> +                    <select class="form-control" id="dict-main" data-setting="general.mainDictionary"></select>                  </div>                  <div class="text-danger" id="dict-purge" hidden>Dictionary data is being purged, please be patient...</div> @@ -678,8 +678,9 @@                          for use with this extension and to learn about importing proprietary EPWING dictionaries.                      </p>                      <div> -                        <button class="btn btn-primary" id="dict-file-button">Import Dictionary</button> -                        <button class="btn btn-danger" id="dict-purge-button">Purge Database</button> +                        <button class="btn btn-primary dictionary-modifying-input" id="dict-file-button">Import Dictionary</button> +                        <button class="btn btn-danger dictionary-modifying-input" id="dict-purge-button">Purge Database</button> +                        <button class="btn btn-default dictionary-modifying-input" id="dict-check-integrity">Check integrity</button>                      </div>                      <div hidden><input type="file" id="dict-file" accept=".zip,application/zip" multiple></div>                  </div> @@ -727,7 +728,7 @@                              </div>                              <div class="modal-footer">                                  <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> -                                <button type="button" class="btn btn-danger" id="dict-delete-confirm">Delete Dictionary</button> +                                <button type="button" class="btn btn-danger dictionary-modifying-input" id="dict-delete-confirm">Delete Dictionary</button>                              </div>                          </div>                      </div> @@ -756,7 +757,7 @@                      </div>                      <div class="dict-delete-table">                          <div> -                            <button class="btn btn-default dict-delete-button">Delete Dictionary</button> +                            <button class="btn btn-default dict-delete-button dictionary-modifying-input">Delete Dictionary</button>                          </div>                          <div>                              <div class="progress" hidden> diff --git a/ext/mixed/js/dom-data-binder.js b/ext/mixed/js/dom-data-binder.js index d46e8087..93fe956e 100644 --- a/ext/mixed/js/dom-data-binder.js +++ b/ext/mixed/js/dom-data-binder.js @@ -303,6 +303,9 @@ class DOMDataBinder {                  element.value = value;                  break;          } + +        const event = new CustomEvent('settingChanged', {detail: {value}}); +        element.dispatchEvent(event);      }      _getElementValue(element) { |