diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/bg/js/settings-dictionaries.js | 482 | ||||
| -rw-r--r-- | ext/bg/js/settings.js | 261 | ||||
| -rw-r--r-- | ext/bg/js/templates.js | 27 | ||||
| -rw-r--r-- | ext/bg/js/util.js | 8 | ||||
| -rw-r--r-- | ext/bg/settings.html | 30 | 
5 files changed, 520 insertions, 288 deletions
| diff --git a/ext/bg/js/settings-dictionaries.js b/ext/bg/js/settings-dictionaries.js new file mode 100644 index 00000000..635b95c6 --- /dev/null +++ b/ext/bg/js/settings-dictionaries.js @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2019  Alex Yatskov <alex@foosoft.net> + * Author: Alex Yatskov <alex@foosoft.net> + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +let dictionaryUI = null; + + +class SettingsDictionaryListUI { +    constructor(container, template, extraContainer, extraTemplate, optionsDictionaries) { +        this.container = container; +        this.template = template; +        this.extraContainer = extraContainer; +        this.extraTemplate = extraTemplate; +        this.optionsDictionaries = optionsDictionaries; + +        this.dictionaryEntries = []; +        this.extra = null; +    } + +    setDictionaries(dictionaries) { +        for (const dictionaryEntry of this.dictionaryEntries) { +            dictionaryEntry.cleanup(); +        } + +        this.dictionaryEntries = []; + +        let changed = false; +        for (const dictionaryInfo of toIterable(dictionaries)) { +            if (this.createEntry(dictionaryInfo)) { +                changed = true; +            } +        } + +        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 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 (optionsDictionaries.hasOwnProperty(title)) { +            optionsDictionary = optionsDictionaries[title]; +        } else { +            optionsDictionary = SettingsDictionaryListUI.createDictionaryOptions(); +            optionsDictionaries[title] = optionsDictionary; +            changed = true; +        } + +        const content = document.importNode(this.template.content, true).firstChild; +        this.container.appendChild(content); + +        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); +        } +    } + +    save() { +        // Overwrite +    } +} + +class SettingsDictionaryEntryUI { +    constructor(parent, dictionaryInfo, content, optionsDictionary) { +        this.parent = parent; +        this.dictionaryInfo = dictionaryInfo; +        this.optionsDictionary = optionsDictionary; +        this.counts = null; +        this.eventListeners = []; + +        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.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); +    } + +    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}`; +    } + +    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}`; +    } +} + +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() { +    const optionsContext = getOptionsContext(); +    const options = await apiOptionsGet(optionsContext); + +    dictionaryUI = new SettingsDictionaryListUI( +        document.querySelector('#dict-groups'), +        document.querySelector('#dict-template'), +        document.querySelector('#dict-groups-extra'), +        document.querySelector('#dict-extra-template'), +        options.dictionaries +    ); +    dictionaryUI.save = () => apiOptionsSave(); + +    document.querySelector('#dict-purge-link').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); + +    onDatabaseUpdated(options); +} + +async function onDatabaseUpdated(options) { +    try { +        const dictionaries = await utilDatabaseGetDictionaryInfo(); +        dictionaryUI.setDictionaries(dictionaries); + +        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; +        apiOptionsSave(); +    } +} + +async function onDictionaryMainChanged(e) { +    const value = e.target.value; +    const optionsContext = getOptionsContext(); +    const options = await apiOptionsGet(optionsContext); +    options.general.mainDictionary = value; +    apiOptionsSave(); +} + + +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 = $('#dict-error'); +    dialog.show().text(''); + +    if (errors !== null && errors.length > 0) { +        const uniqueErrors = {}; +        for (let e of errors) { +            console.error(e); +            e = dictionaryErrorToString(e); +            uniqueErrors[e] = uniqueErrors.hasOwnProperty(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.append($(div)); +        } + +        dialog.show(); +    } else { +        dialog.hide(); +    } +} + + +function dictionarySpinnerShow(show) { +    const spinner = $('#dict-spinner'); +    if (show) { +        spinner.show(); +    } else { +        spinner.hide(); +    } +} + +function onDictionaryImportButtonClick() { +    const dictFile = document.querySelector('#dict-file'); +    dictFile.click(); +} + +async function onDictionaryPurge(e) { +    e.preventDefault(); + +    const dictControls = $('#dict-importer, #dict-groups, #dict-groups-extra, #dict-main-group').hide(); +    const dictProgress = $('#dict-purge').show(); + +    try { +        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 { +        dictionarySpinnerShow(false); + +        dictControls.show(); +        dictProgress.hide(); + +        if (storageEstimate.mostRecent !== null) { +            storageUpdateStats(); +        } +    } +} + +async function onDictionaryImport(e) { +    const dictFile = $('#dict-file'); +    const dictControls = $('#dict-importer').hide(); +    const dictProgress = $('#dict-import-progress').show(); + +    try { +        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(); +            } +        }; +        setProgress(0.0); + +        const exceptions = []; +        const summary = await utilDatabaseImport(e.target.files[0], 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 { +        dictionarySpinnerShow(false); + +        dictFile.val(''); +        dictControls.show(); +        dictProgress.hide(); +    } +} diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index f2250911..e4446851 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -81,16 +81,6 @@ async function formRead(options) {          options.anki.kanji.model = $('#anki-kanji-model').val();          options.anki.kanji.fields = utilBackgroundIsolate(ankiFieldsToDict($('#kanji .anki-field-value')));      } - -    options.general.mainDictionary = $('#dict-main').val(); -    $('.dict-group').each((index, element) => { -        const dictionary = $(element); -        options.dictionaries[dictionary.data('title')] = utilBackgroundIsolate({ -            priority: parseInt(dictionary.find('.dict-priority').val(), 10), -            enabled: dictionary.find('.dict-enabled').prop('checked'), -            allowSecondarySearches: dictionary.find('.dict-allow-secondary-searches').prop('checked') -        }); -    });  }  async function formWrite(options) { @@ -145,13 +135,6 @@ async function formWrite(options) {      $('#field-templates').val(options.anki.fieldTemplates);      try { -        await dictionaryGroupsPopulate(options); -        await formMainDictionaryOptionsPopulate(options); -    } catch (e) { -        dictionaryErrorsShow([e]); -    } - -    try {          await ankiDeckAndModelPopulate(options);      } catch (e) {          ankiErrorShow(e); @@ -161,10 +144,6 @@ async function formWrite(options) {  }  function formSetupEventListeners() { -    $('#dict-purge-link').click(utilAsync(onDictionaryPurge)); -    $('#dict-file').change(utilAsync(onDictionaryImport)); -    $('#dict-file-button').click(onDictionaryImportButtonClick); -      $('#field-templates-reset').click(utilAsync(onAnkiFieldTemplatesReset));      $('input, select, textarea').not('.anki-model').not('.ignore-form-changes *').change(utilAsync(onFormOptionsChanged));      $('.anki-model').change(utilAsync(onAnkiModelChanged)); @@ -184,23 +163,6 @@ function formUpdateVisibility(options) {      }  } -async function formMainDictionaryOptionsPopulate(options) { -    const select = $('#dict-main').empty(); -    select.append($('<option class="text-muted" value="">Not selected</option>')); - -    let mainDictionary = ''; -    for (const dictRow of toIterable(await utilDatabaseSummarize())) { -        if (dictRow.sequenced) { -            select.append($(`<option value="${dictRow.title}">${dictRow.title}</option>`)); -            if (dictRow.title === options.general.mainDictionary) { -                mainDictionary = dictRow.title; -            } -        } -    } - -    select.val(mainDictionary); -} -  async function onFormOptionsChanged(e) {      if (!e.originalEvent && !e.isTrigger) {          return; @@ -239,6 +201,7 @@ async function onReady() {      appearanceInitialize();      await audioSettingsInitialize();      await profileOptionsSetup(); +    await dictSettingsInitialize();      storageInfoInitialize(); @@ -423,228 +386,6 @@ function onMessage({action, params}, sender, callback) {  /* - * Dictionary - */ - -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 = $('#dict-error'); -    dialog.show().text(''); - -    if (errors !== null && errors.length > 0) { -        const uniqueErrors = {}; -        for (let e of errors) { -            e = dictionaryErrorToString(e); -            uniqueErrors[e] = uniqueErrors.hasOwnProperty(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.append($(div)); -        } - -        dialog.show(); -    } else { -        dialog.hide(); -    } -} - -function dictionarySpinnerShow(show) { -    const spinner = $('#dict-spinner'); -    if (show) { -        spinner.show(); -    } else { -        spinner.hide(); -    } -} - -function dictionaryGroupsSort() { -    const dictGroups = $('#dict-groups'); -    const dictGroupChildren = dictGroups.children('.dict-group').sort((ca, cb) => { -        const pa = parseInt($(ca).find('.dict-priority').val(), 10); -        const pb = parseInt($(cb).find('.dict-priority').val(), 10); -        if (pa < pb) { -            return 1; -        } else if (pa > pb) { -            return -1; -        } else { -            return 0; -        } -    }); - -    dictGroups.append(dictGroupChildren); -} - -async function dictionaryGroupsPopulate(options) { -    const dictGroups = $('#dict-groups').empty(); -    const dictWarning = $('#dict-warning').hide(); - -    const dictRows = toIterable(await utilDatabaseSummarize()); -    if (dictRows.length === 0) { -        dictWarning.show(); -    } - -    for (const dictRow of toIterable(dictRowsSort(dictRows, options))) { -        const dictOptions = options.dictionaries[dictRow.title] || { -            enabled: false, -            priority: 0, -            allowSecondarySearches: false -        }; - -        const dictHtml = await apiTemplateRender('dictionary.html', { -            enabled: dictOptions.enabled, -            priority: dictOptions.priority, -            allowSecondarySearches: dictOptions.allowSecondarySearches, -            title: dictRow.title, -            version: dictRow.version, -            revision: dictRow.revision, -            outdated: dictRow.version < 3 -        }); - -        dictGroups.append($(dictHtml)); -    } - -    formUpdateVisibility(options); - -    $('.dict-enabled, .dict-priority, .dict-allow-secondary-searches').change(e => { -        dictionaryGroupsSort(); -        onFormOptionsChanged(e); -    }); -} - -async function onDictionaryPurge(e) { -    e.preventDefault(); - -    const dictControls = $('#dict-importer, #dict-groups, #dict-main-group').hide(); -    const dictProgress = $('#dict-purge').show(); - -    try { -        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); -        await dictionaryGroupsPopulate(options); -        await formMainDictionaryOptionsPopulate(options); -    } catch (e) { -        dictionaryErrorsShow([e]); -    } finally { -        dictionarySpinnerShow(false); - -        dictControls.show(); -        dictProgress.hide(); - -        if (storageEstimate.mostRecent !== null) { -            storageUpdateStats(); -        } -    } -} - -function onDictionaryImportButtonClick() { -    const dictFile = document.querySelector('#dict-file'); -    dictFile.click(); -} - -async function onDictionaryImport(e) { -    const dictFile = $('#dict-file'); -    const dictControls = $('#dict-importer').hide(); -    const dictProgress = $('#dict-import-progress').show(); - -    try { -        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(); -            } -        }; -        setProgress(0.0); - -        const exceptions = []; -        const summary = await utilDatabaseImport(e.target.files[0], updateProgress, exceptions); -        for (const options of toIterable(await getOptionsArray())) { -            options.dictionaries[summary.title] = utilBackgroundIsolate({ -                enabled: true, -                priority: 0, -                allowSecondarySearches: false -            }); -            if (summary.sequenced && options.general.mainDictionary === '') { -                options.general.mainDictionary = summary.title; -            } -        } -        await settingsSaveOptions(); - -        if (exceptions.length > 0) { -            exceptions.push(`Dictionary may not have been imported properly: ${exceptions.length} error${exceptions.length === 1 ? '' : 's'} reported.`); -            dictionaryErrorsShow(exceptions); -        } - -        const optionsContext = getOptionsContext(); -        const options = await apiOptionsGet(optionsContext); -        await dictionaryGroupsPopulate(options); -        await formMainDictionaryOptionsPopulate(options); -    } catch (e) { -        dictionaryErrorsShow([e]); -    } finally { -        dictionarySpinnerShow(false); - -        dictFile.val(''); -        dictControls.show(); -        dictProgress.hide(); -    } -} - - -/*   * Anki   */ diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js index 59516d97..26d1575f 100644 --- a/ext/bg/js/templates.js +++ b/ext/bg/js/templates.js @@ -1,32 +1,5 @@  (function() {    var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {}; -templates['dictionary.html'] = template({"1":function(container,depth0,helpers,partials,data) { -    return "    <p class=\"text-warning\">This dictionary is outdated and may not support new extension features; please import the latest version.</p>\n"; -},"3":function(container,depth0,helpers,partials,data) { -    return "checked"; -},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { -    var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression; - -  return "<div class=\"dict-group well well-sm\" data-title=\"" -    + alias4(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"title","hash":{},"data":data}) : helper))) -    + "\">\n    <h4><span class=\"text-muted glyphicon glyphicon-book\"></span> " -    + alias4(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"title","hash":{},"data":data}) : helper))) -    + " <small>rev." -    + alias4(((helper = (helper = helpers.revision || (depth0 != null ? depth0.revision : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"revision","hash":{},"data":data}) : helper))) -    + "</small></h4>\n" -    + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.outdated : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") -    + "\n    <div class=\"checkbox\">\n        <label><input type=\"checkbox\" class=\"dict-enabled\" " -    + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.enabled : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") -    + "> Enable search</label>\n    </div>\n    <div class=\"checkbox options-advanced\">\n        <label><input type=\"checkbox\" class=\"dict-allow-secondary-searches\" " -    + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.allowSecondarySearches : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") -    + "> Allow secondary searches</label>\n    </div>\n    <div class=\"form-group options-advanced\">\n        <label for=\"dict-" -    + alias4(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"title","hash":{},"data":data}) : helper))) -    + "\">Result priority</label>\n        <input type=\"number\" value=\"" -    + alias4(((helper = (helper = helpers.priority || (depth0 != null ? depth0.priority : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"priority","hash":{},"data":data}) : helper))) -    + "\" id=\"dict-" -    + alias4(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"title","hash":{},"data":data}) : helper))) -    + "\" class=\"form-control dict-priority\">\n    </div>\n</div>\n"; -},"useData":true});  templates['kanji.html'] = template({"1":function(container,depth0,helpers,partials,data) {      var stack1; diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index 1ca0833b..3554ec3d 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -84,6 +84,14 @@ function utilDatabaseSummarize() {      return utilBackend().translator.database.summarize();  } +function utilDatabaseGetDictionaryInfo() { +    return utilBackend().translator.database.getDictionaryInfo(); +} + +function utilDatabaseGetDictionaryCounts(dictionaryNames, getTotal) { +    return utilBackend().translator.database.getDictionaryCounts(dictionaryNames, getTotal); +} +  function utilAnkiGetModelFieldNames(modelName) {      return utilBackend().anki.getModelFieldNames(modelName);  } diff --git a/ext/bg/settings.html b/ext/bg/settings.html index b281501d..7ac196f2 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -410,7 +410,7 @@                  </div>              </div> -            <div> +            <div class="ignore-form-changes">                  <div>                      <img src="/mixed/img/spinner.gif" class="pull-right" id="dict-spinner" alt>                      <h3>Dictionaries</h3> @@ -434,6 +434,7 @@                  <div class="alert alert-danger" id="dict-error"></div>                  <div id="dict-groups"></div> +                <div id="dict-groups-extra"></div>                  <div id="dict-import-progress">                      Dictionary data is being imported, please be patient... @@ -451,6 +452,32 @@                      <button class="btn btn-primary" id="dict-file-button">Import Dictionary</button>                      <div hidden><input type="file" id="dict-file"></div>                  </div> + +                <template id="dict-template"><div class="dict-group well well-sm"> +                    <h4><span class="text-muted glyphicon glyphicon-book"></span> <span class="dict-title"></span> <small class="dict-revision"></small></h4> +                    <p class="text-warning" hidden>This dictionary is outdated and may not support new extension features; please import the latest version.</p> + +                    <div class="checkbox"> +                        <label><input type="checkbox" class="dict-enabled"> Enable search</label> +                    </div> +                    <div class="checkbox options-advanced"> +                        <label><input type="checkbox" class="dict-allow-secondary-searches"> Allow secondary searches</label> +                    </div> +                    <div class="form-group options-advanced"> +                        <label class="dict-result-priority-label">Result priority</label> +                        <input type="number" class="form-control dict-priority"> +                    </div> +                    <pre class="debug dict-counts" hidden></pre> +                </div></template> + +                <template id="dict-extra-template"><div class="well well-sm"> +                    <h4><span class="text-muted glyphicon glyphicon-alert"></span> <span class="dict-title">Unassociated Data</span> <small class="dict-total-count"></small></h4> +                    <p class="text-warning"> +                        The database contains extra data which is not associated with any installed dictionary. +                        Purging the database can fix this issue. +                    </p> +                    <pre class="debug dict-counts" hidden></pre> +                </div></template>              </div>              <div id="storage-info"> @@ -700,6 +727,7 @@          <script src="/bg/js/util.js"></script>          <script src="/mixed/js/audio.js"></script> +        <script src="/bg/js/settings-dictionaries.js"></script>          <script src="/bg/js/settings-profiles.js"></script>          <script src="/bg/js/settings.js"></script>      </body> |