diff --git a/chrome/content/zotero/dictionaryManager.js b/chrome/content/zotero/dictionaryManager.js new file mode 100644 index 0000000000..d85630d740 --- /dev/null +++ b/chrome/content/zotero/dictionaryManager.js @@ -0,0 +1,193 @@ +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2021 Corporation for Digital Scholarship + Vienna, Virginia, USA + https://digitalscholar.org + + This file is part of Zotero. + + Zotero is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Zotero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with Zotero. If not, see . + + ***** END LICENSE BLOCK ***** +*/ + +"use strict"; + +// eslint-disable-next-line camelcase, no-unused-vars +var Zotero_Dictionary_Manager = new function () { + const HTML_NS = 'http://www.w3.org/1999/xhtml'; + + var installed; + var updateMap; + + this.init = async function () { + document.title = Zotero.getString('spellCheck.dictionaryManager.title'); + + installed = new Set(Zotero.Dictionaries.dictionaries.map(d => d.id)); + var installedLocales = new Set(Zotero.Dictionaries.dictionaries.map(d => d.locale)); + var availableDictionaries = await Zotero.Dictionaries.fetchDictionariesList(); + var availableUpdates = await Zotero.Dictionaries.getAvailableUpdates(availableDictionaries); + updateMap = new Map(availableUpdates.map(x => [x.old.id, x.new.id])); + + var { InlineSpellChecker } = ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", {}); + var isc = new InlineSpellChecker(); + + // Start with installed dictionaries + var list = []; + for (let d of Zotero.Dictionaries.dictionaries) { + let name = Zotero.Dictionaries.getBestDictionaryName(d.locale, isc); + list.push(Object.assign({}, d, { name })); + } + // Add remote dictionaries not in the list + for (let d of availableDictionaries) { + if (!installed.has(d.id) && !installedLocales.has(d.locale)) { + list.push(d); + } + } + var positionMap = new Map(availableDictionaries.map((d, i) => [d.locale, i + 1])); + list.sort((a, b) => { + // If both locales are in original list, use the original sort order + let posA = positionMap.get(a.locale); + let posB = positionMap.get(b.locale); + if (posA && posB) { + return posA - posB; + } + // Otherwise compare the locale codes + return Zotero.localeCompare(a.locale, b.locale); + }); + + // Build list + var listbox = document.getElementById('dictionaries'); + for (let d of list) { + let name = d.name; + let li = document.createElement('richlistitem'); + let div = document.createElementNS(HTML_NS, 'div'); + + let checkbox = document.createElementNS(HTML_NS, 'input'); + checkbox.type = 'checkbox'; + checkbox.id = d.locale; + // Store properties on element + // .id will be the current id for installed dictionaries and otherwise the remote id + checkbox.dataset.dictId = d.id; + checkbox.dataset.dictLocale = d.locale; + checkbox.dataset.dictName = d.name; + // en-US is always checked and disabled + checkbox.checked = d.locale == 'en-US' || installed.has(d.id); + if (d.locale == 'en-US') { + checkbox.disabled = true; + } + checkbox.setAttribute('tabindex', -1); + + let label = document.createElementNS(HTML_NS, 'label'); + label.setAttribute('for', d.locale); + // Add " (update available)" + if (updateMap.has(d.id)) { + name = Zotero.getString('spellCheck.dictionaryManager.updateAvailable', name); + } + label.textContent = name; + // Don't toggle checkbox for single-click on label + label.onclick = (event) => { + if (event.detail == 1) { + event.preventDefault(); + } + }; + + div.appendChild(checkbox); + div.appendChild(label); + li.appendChild(div); + listbox.appendChild(li); + } + listbox.selectedIndex = 0; + }; + + this.handleAccept = async function () { + // Download selected dictionaries if updated or not currently installed + var elems = document.querySelectorAll('input[type=checkbox]'); + var toRemove = []; + var toDownload = []; + for (let elem of elems) { + if (elem.dataset.dictLocale == 'en-US') { + continue; + } + + let id = elem.dataset.dictId; + if (!elem.checked) { + if (installed.has(id)) { + toRemove.push(id); + } + continue; + } + + if (updateMap.has(id)) { + // If id is changing, delete the old one first + toRemove.push(id); + toDownload.push({ id: updateMap.get(id), name: elem.dataset.dictName }); + } + else if (!installed.has(id)) { + toDownload.push({ id, name: elem.dataset.dictName }); + } + } + if (toRemove.length) { + for (let id of toRemove) { + await Zotero.Dictionaries.remove(id); + } + } + if (toDownload.length) { + for (let { id, name } of toDownload) { + _updateStatus(Zotero.getString('general.downloading.quoted', name)); + try { + await Zotero.Dictionaries.install(id); + } + catch (e) { + Zotero.logError(e); + Zotero.alert( + null, + Zotero.getString('general.error'), + Zotero.getString('spellCheck.dictionaryManager.error.unableToInstall', name) + + "\n\n" + e.message + ); + return; + } + finally { + _updateStatus(); + } + } + } + window.close(); + }; + + function _updateStatus(msg) { + var elem = document.getElementById('status'); + elem.textContent = msg + // Use non-breaking space to maintain height when empty + || '\xA0'; + } +}; + +window.addEventListener('keypress', function (event) { + // Toggle checkbox on spacebar + if (event.key == ' ') { + if (event.target.localName == 'richlistbox') { + let elem = event.target.selectedItem.querySelector('input[type=checkbox]'); + if (!elem.disabled) { + elem.checked = !elem.checked; + } + } + } + + if (event.key == 'Enter') { + document.querySelector('button[dlgtype="accept"]').click(); + } +}); diff --git a/chrome/content/zotero/dictionaryManager.xul b/chrome/content/zotero/dictionaryManager.xul new file mode 100644 index 0000000000..482b3102ad --- /dev/null +++ b/chrome/content/zotero/dictionaryManager.xul @@ -0,0 +1,38 @@ + + + + + + + + + +