summaryrefslogtreecommitdiff
path: root/ext/bg/js/util.js
diff options
context:
space:
mode:
authorAlex Yatskov <alex@foosoft.net>2017-08-17 19:39:32 -0700
committerAlex Yatskov <alex@foosoft.net>2017-08-17 19:39:32 -0700
commit39fbabfe626e7ae2c2e0c1dd45bda33dd5dab66c (patch)
tree334bc2282389c910ed9f7d724458b6d8b650311a /ext/bg/js/util.js
parent65c100fe16c4829971055da384da90b1a1cddcd5 (diff)
parent7586572fbaab7de698ec13f8712cc95e24ab6273 (diff)
Merge branch 'master' into firefox-amo
Diffstat (limited to 'ext/bg/js/util.js')
-rw-r--r--ext/bg/js/util.js542
1 files changed, 20 insertions, 522 deletions
diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js
index cdd5ec31..a92fd0bc 100644
--- a/ext/bg/js/util.js
+++ b/ext/bg/js/util.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Alex Yatskov <alex@foosoft.net>
+ * Copyright (C) 2016-2017 Alex Yatskov <alex@foosoft.net>
* Author: Alex Yatskov <alex@foosoft.net>
*
* This program is free software: you can redistribute it and/or modify
@@ -16,542 +16,40 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-/*
- * Promise
- */
-
-function promiseCallback(promise, callback) {
- return promise.then(result => {
- callback({result});
- }).catch(error => {
- callback({error});
- });
-}
-
-
-/*
- * Japanese
- */
-
-function jpIsKanji(c) {
- const code = c.charCodeAt(0);
- return code >= 0x4e00 && code < 0x9fb0 || code >= 0x3400 && code < 0x4dc0;
-}
-
-function jpIsKana(c) {
- return wanakana.isKana(c);
-}
-
-
-/*
- * Commands
- */
-
-function commandExec(command) {
- instYomi().onCommand(command);
-}
-
-
-/*
- * Instance
- */
-
-function instYomi() {
- return chrome.extension.getBackgroundPage().yomichan;
-}
-
-function instDb() {
- return instYomi().translator.database;
-}
-
-function instAnki() {
- return instYomi().anki;
-}
-
-
-/*
- * Foreground
- */
-
-function fgBroadcast(action, params) {
- chrome.tabs.query({}, tabs => {
- for (const tab of tabs) {
- chrome.tabs.sendMessage(tab.id, {action, params}, () => null);
- }
- });
-}
-
-function fgOptionsSet(options) {
- fgBroadcast('optionsSet', options);
-}
-
-
-/*
- * Options
- */
-
-function optionsSetDefaults(options) {
- const defaults = {
- general: {
- enable: true,
- audioSource: 'jpod101',
- audioVolume: 100,
- groupResults: true,
- debugInfo: false,
- maxResults: 32,
- showAdvanced: false,
- popupWidth: 400,
- popupHeight: 250,
- popupOffset: 10,
- showGuide: true
- },
-
- scanning: {
- middleMouse: true,
- selectText: true,
- alphanumeric: true,
- delay: 15,
- length: 10,
- modifier: 'shift'
- },
-
- dictionaries: {},
-
- anki: {
- enable: false,
- server: 'http://127.0.0.1:8765',
- tags: ['yomichan'],
- htmlCards: true,
- sentenceExt: 200,
- terms: {deck: '', model: '', fields: {}},
- kanji: {deck: '', model: '', fields: {}}
- }
- };
-
- const combine = (target, source) => {
- for (const key in source) {
- if (!target.hasOwnProperty(key)) {
- target[key] = source[key];
- }
- }
+function utilAsync(func) {
+ return function(...args) {
+ func.apply(this, args);
};
-
- combine(options, defaults);
- combine(options.general, defaults.general);
- combine(options.scanning, defaults.scanning);
- combine(options.anki, defaults.anki);
- combine(options.anki.terms, defaults.anki.terms);
- combine(options.anki.kanji, defaults.anki.kanji);
-
- return options;
-}
-
-function optionsVersion(options) {
- const fixups = [
- () => {},
- () => {},
- () => {},
- () => {},
- () => {
- if (options.general.audioPlayback) {
- options.general.audioSource = 'jpod101';
- } else {
- options.general.audioSource = 'disabled';
- }
- },
- () => {
- options.general.showGuide = false;
- },
- () => {
- if (options.scanning.requireShift) {
- options.scanning.modifier = 'shift';
- } else {
- options.scanning.modifier = 'none';
- }
- }
- ];
-
- optionsSetDefaults(options);
- if (!options.hasOwnProperty('version')) {
- options.version = fixups.length;
- }
-
- while (options.version < fixups.length) {
- fixups[options.version++]();
- }
-
- return options;
-}
-
-function optionsLoad() {
- return new Promise((resolve, reject) => {
- chrome.storage.local.get(null, store => resolve(store.options));
- }).then(optionsStr => {
- return optionsStr ? JSON.parse(optionsStr) : {};
- }).catch(error => {
- return {};
- }).then(options => {
- return optionsVersion(options);
- });
-}
-
-function optionsSave(options) {
- return new Promise((resolve, reject) => {
- chrome.storage.local.set({options: JSON.stringify(options)}, resolve);
- }).then(() => {
- instYomi().optionsSet(options);
- fgOptionsSet(options);
- });
-}
-
-
-/*
- * Dictionary
- */
-
-function dictEnabledSet(options) {
- const dictionaries = {};
- for (const title in options.dictionaries) {
- const dictionary = options.dictionaries[title];
- if (dictionary.enabled) {
- dictionaries[title] = dictionary;
- }
- }
-
- return dictionaries;
-}
-
-function dictConfigured(options) {
- for (const title in options.dictionaries) {
- if (options.dictionaries[title].enabled) {
- return true;
- }
- }
-
- return false;
-}
-
-function dictRowsSort(rows, options) {
- return rows.sort((ra, rb) => {
- const pa = (options.dictionaries[ra.title] || {}).priority || 0;
- const pb = (options.dictionaries[rb.title] || {}).priority || 0;
- if (pa > pb) {
- return -1;
- } else if (pa < pb) {
- return 1;
- } else {
- return 0;
- }
- });
-}
-
-function dictTermsSort(definitions, dictionaries=null) {
- return definitions.sort((v1, v2) => {
- const sl1 = v1.source.length;
- const sl2 = v2.source.length;
- if (sl1 > sl2) {
- return -1;
- } else if (sl1 < sl2) {
- return 1;
- }
-
- if (dictionaries !== null) {
- const p1 = (dictionaries[v1.dictionary] || {}).priority || 0;
- const p2 = (dictionaries[v2.dictionary] || {}).priority || 0;
- if (p1 > p2) {
- return -1;
- } else if (p1 < p2) {
- return 1;
- }
- }
-
- const s1 = v1.score;
- const s2 = v2.score;
- if (s1 > s2) {
- return -1;
- } else if (s1 < s2) {
- return 1;
- }
-
- const rl1 = v1.reasons.length;
- const rl2 = v2.reasons.length;
- if (rl1 < rl2) {
- return -1;
- } else if (rl1 > rl2) {
- return 1;
- }
-
- return v2.expression.localeCompare(v1.expression);
- });
-}
-
-function dictTermsUndupe(definitions) {
- const definitionGroups = {};
- for (const definition of definitions) {
- const definitionExisting = definitionGroups[definition.id];
- if (!definitionGroups.hasOwnProperty(definition.id) || definition.expression.length > definitionExisting.expression.length) {
- definitionGroups[definition.id] = definition;
- }
- }
-
- const definitionsUnique = [];
- for (const key in definitionGroups) {
- definitionsUnique.push(definitionGroups[key]);
- }
-
- return definitionsUnique;
-}
-
-function dictTermsGroup(definitions, dictionaries) {
- const groups = {};
- for (const definition of definitions) {
- const key = [definition.source, definition.expression].concat(definition.reasons);
- if (definition.reading) {
- key.push(definition.reading);
- }
-
- const group = groups[key];
- if (group) {
- group.push(definition);
- } else {
- groups[key] = [definition];
- }
- }
-
- const results = [];
- for (const key in groups) {
- const groupDefs = groups[key];
- const firstDef = groupDefs[0];
- dictTermsSort(groupDefs, dictionaries);
- results.push({
- definitions: groupDefs,
- expression: firstDef.expression,
- reading: firstDef.reading,
- reasons: firstDef.reasons,
- score: groupDefs.reduce((p, v) => v.score > p ? v.score : p, Number.MIN_SAFE_INTEGER),
- source: firstDef.source
- });
- }
-
- return dictTermsSort(results);
-}
-
-function dictTagBuildSource(name) {
- return dictTagSanitize({name, category: 'dictionary', order: 100});
-}
-
-function dictTagBuild(name, meta) {
- const tag = {name};
- const symbol = name.split(':')[0];
- for (const prop in meta[symbol] || {}) {
- tag[prop] = meta[symbol][prop];
- }
-
- return dictTagSanitize(tag);
-}
-
-function dictTagSanitize(tag) {
- tag.name = tag.name || 'untitled';
- tag.category = tag.category || 'default';
- tag.notes = tag.notes || '';
- tag.order = tag.order || 0;
- return tag;
-}
-
-function dictTagsSort(tags) {
- return tags.sort((v1, v2) => {
- const order1 = v1.order;
- const order2 = v2.order;
- if (order1 < order2) {
- return -1;
- } else if (order1 > order2) {
- return 1;
- }
-
- const name1 = v1.name;
- const name2 = v2.name;
- if (name1 < name2) {
- return -1;
- } else if (name1 > name2) {
- return 1;
- }
-
- return 0;
- });
}
-function dictFieldSplit(field) {
- return field.length === 0 ? [] : field.split(' ');
+function utilIsolate(data) {
+ return JSON.parse(JSON.stringify(data));
}
-function dictFieldFormat(field, definition, mode, options) {
- const markers = [
- 'audio',
- 'character',
- 'cloze-body',
- 'cloze-prefix',
- 'cloze-suffix',
- 'dictionary',
- 'expression',
- 'furigana',
- 'glossary',
- 'kunyomi',
- 'onyomi',
- 'reading',
- 'sentence',
- 'tags',
- 'url'
- ];
-
- for (const marker of markers) {
- const data = {
- marker,
- definition,
- group: options.general.groupResults,
- html: options.anki.htmlCards,
- modeTermKanji: mode === 'term-kanji',
- modeTermKana: mode === 'term-kana',
- modeKanji: mode === 'kanji'
- };
-
- field = field.replace(
- `{${marker}}`,
- Handlebars.templates['fields.html'](data).trim()
- );
- }
-
- return field;
+function utilBackend() {
+ return chrome.extension.getBackgroundPage().yomichan_backend;
}
-
-/*
- * Json
- */
-
-function jsonLoad(url) {
- return new Promise((resolve, reject) => {
- const xhr = new XMLHttpRequest();
- xhr.overrideMimeType('application/json');
- xhr.addEventListener('load', () => resolve(xhr.responseText));
- xhr.addEventListener('error', () => reject('failed to execute network request'));
- xhr.open('GET', url);
- xhr.send();
- }).then(responseText => {
- try {
- return JSON.parse(responseText);
- }
- catch (e) {
- return Promise.reject('invalid JSON response');
- }
- });
+function utilAnkiGetModelNames() {
+ return utilBackend().anki.getModelNames();
}
-function jsonLoadInt(url) {
- return jsonLoad(chrome.extension.getURL(url));
+function utilAnkiGetDeckNames() {
+ return utilBackend().anki.getDeckNames();
}
-/*
- * Zip
- */
-
-function zipLoadDb(archive, indexLoaded, termsLoaded, kanjiLoaded) {
- return JSZip.loadAsync(archive).then(files => files.files).then(files => {
- const indexFile = files['index.json'];
- if (!indexFile) {
- return Promise.reject('no dictionary index found in archive');
- }
-
- return indexFile.async('string').then(indexJson => {
- const index = JSON.parse(indexJson);
- if (!index.title || !index.version || !index.revision) {
- return Promise.reject('unrecognized dictionary format');
- }
-
- return indexLoaded(
- index.title,
- index.version,
- index.revision,
- index.tagMeta || {},
- index.termBanks > 0,
- index.kanjiBanks > 0
- ).then(() => index);
- }).then(index => {
- const loaders = [];
- const banksTotal = index.termBanks + index.kanjiBanks;
- let banksLoaded = 0;
-
- for (let i = 1; i <= index.termBanks; ++i) {
- const bankFile = files[`term_bank_${i}.json`];
- if (!bankFile) {
- return Promise.reject('missing term bank file');
- }
-
- loaders.push(() => bankFile.async('string').then(bankJson => {
- const bank = JSON.parse(bankJson);
- return termsLoaded(index.title, bank, banksTotal, banksLoaded++);
- }));
- }
-
- for (let i = 1; i <= index.kanjiBanks; ++i) {
- const bankFile = files[`kanji_bank_${i}.json`];
- if (!bankFile) {
- return Promise.reject('missing kanji bank file');
- }
-
- loaders.push(() => bankFile.async('string').then(bankJson => {
- const bank = JSON.parse(bankJson);
- return kanjiLoaded(index.title, bank, banksTotal, banksLoaded++);
- }));
- }
-
- let chain = Promise.resolve();
- for (const loader of loaders) {
- chain = chain.then(loader);
- }
-
- return chain;
- });
- });
-}
-
-/*
- * Helpers
- */
-
-function handlebarsEscape(text) {
- return Handlebars.Utils.escapeExpression(text);
-}
-
-function handlebarsDumpObject(options) {
- const dump = JSON.stringify(options.fn(this), null, 4);
- return handlebarsEscape(dump);
-}
-
-function handlebarsKanjiLinks(options) {
- let result = '';
- for (const c of options.fn(this)) {
- if (jpIsKanji(c)) {
- result += `<a href="#" class="kanji-link">${c}</a>`;
- } else {
- result += c;
- }
- }
-
- return result;
+function utilAnkiGetModelFieldNames(modelName) {
+ return utilBackend().anki.getModelFieldNames(modelName);
}
-function handlebarsMultiLine(options) {
- return options.fn(this).split('\n').join('<br>');
+function utilDatabaseGetDictionaries() {
+ return utilBackend().translator.database.getDictionaries();
}
-function handlebarsRegister() {
- Handlebars.partials = Handlebars.templates;
- Handlebars.registerHelper('dumpObject', handlebarsDumpObject);
- Handlebars.registerHelper('kanjiLinks', handlebarsKanjiLinks);
- Handlebars.registerHelper('multiLine', handlebarsMultiLine);
+function utilDatabasePurge() {
+ return utilBackend().translator.database.purge();
}
-function handlebarsRender(template, data) {
- return Handlebars.templates[template](data);
+function utilDatabaseImport(data, progress) {
+ return utilBackend().translator.database.importDictionary(data, progress);
}