summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/bg/js/api.js8
-rw-r--r--ext/bg/js/audio.js9
-rw-r--r--ext/bg/js/database.js85
-rw-r--r--ext/bg/js/dictionary.js147
-rw-r--r--ext/bg/js/options.js115
-rw-r--r--ext/bg/js/settings.js83
-rw-r--r--ext/bg/js/templates.js254
-rw-r--r--ext/bg/js/translator.js144
-rw-r--r--ext/bg/js/util.js45
-rw-r--r--ext/bg/settings.html28
-rw-r--r--ext/fg/js/document.js4
-rw-r--r--ext/manifest.json2
-rw-r--r--ext/mixed/css/display.css72
-rw-r--r--ext/mixed/js/display.js38
14 files changed, 882 insertions, 152 deletions
diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js
index 9f65bb07..de3ad64e 100644
--- a/ext/bg/js/api.js
+++ b/ext/bg/js/api.js
@@ -29,9 +29,11 @@ async function apiTermsFind(text) {
const options = utilBackend().options;
const translator = utilBackend().translator;
- const searcher = options.general.groupResults ?
- translator.findTermsGrouped.bind(translator) :
- translator.findTermsSplit.bind(translator);
+ const searcher = {
+ 'merge': translator.findTermsMerged,
+ 'split': translator.findTermsSplit,
+ 'group': translator.findTermsGrouped
+ }[options.general.resultOutputMode].bind(translator);
const {definitions, length} = await searcher(
text,
diff --git a/ext/bg/js/audio.js b/ext/bg/js/audio.js
index ce47490c..549288f5 100644
--- a/ext/bg/js/audio.js
+++ b/ext/bg/js/audio.js
@@ -140,8 +140,13 @@ async function audioInject(definition, fields, mode) {
}
try {
- const url = await audioBuildUrl(definition, mode);
- const filename = audioBuildFilename(definition);
+ let audioSourceDefinition = definition;
+ if (definition.hasOwnProperty('expressions')) {
+ audioSourceDefinition = definition.expressions[0];
+ }
+
+ const url = await audioBuildUrl(audioSourceDefinition, mode);
+ const filename = audioBuildFilename(audioSourceDefinition);
if (url && filename) {
definition.audio = {url, filename};
diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js
index 6ceb3ec8..3c7f6aab 100644
--- a/ext/bg/js/database.js
+++ b/ext/bg/js/database.js
@@ -40,6 +40,9 @@ class Database {
kanjiMeta: '++,dictionary,character',
tagMeta: '++,dictionary,name'
});
+ this.db.version(4).stores({
+ terms: '++id,dictionary,expression,reading,sequence'
+ });
await this.db.open();
}
@@ -68,12 +71,66 @@ class Database {
results.push({
expression: row.expression,
reading: row.reading,
- tags: dictFieldSplit(row.tags),
+ definitionTags: dictFieldSplit(row.definitionTags || row.tags || ''),
+ termTags: dictFieldSplit(row.termTags || ''),
+ rules: dictFieldSplit(row.rules),
+ glossary: row.glossary,
+ score: row.score,
+ dictionary: row.dictionary,
+ id: row.id,
+ sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence
+ });
+ }
+ });
+
+ return results;
+ }
+
+ async findTermsExact(term, reading, titles) {
+ if (!this.db) {
+ throw 'Database not initialized';
+ }
+
+ const results = [];
+ await this.db.terms.where('expression').equals(term).each(row => {
+ if (row.reading === reading && titles.includes(row.dictionary)) {
+ results.push({
+ expression: row.expression,
+ reading: row.reading,
+ definitionTags: dictFieldSplit(row.definitionTags || row.tags || ''),
+ termTags: dictFieldSplit(row.termTags || ''),
rules: dictFieldSplit(row.rules),
glossary: row.glossary,
score: row.score,
dictionary: row.dictionary,
- id: row.id
+ id: row.id,
+ sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence
+ });
+ }
+ });
+
+ return results;
+ }
+
+ async findTermsBySequence(sequence, mainDictionary) {
+ if (!this.db) {
+ throw 'Database not initialized';
+ }
+
+ const results = [];
+ await this.db.terms.where('sequence').equals(sequence).each(row => {
+ if (row.dictionary === mainDictionary) {
+ results.push({
+ expression: row.expression,
+ reading: row.reading,
+ definitionTags: dictFieldSplit(row.definitionTags || row.tags || ''),
+ termTags: dictFieldSplit(row.termTags || ''),
+ rules: dictFieldSplit(row.rules),
+ glossary: row.glossary,
+ score: row.score,
+ dictionary: row.dictionary,
+ id: row.id,
+ sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence
});
}
});
@@ -163,7 +220,7 @@ class Database {
return result;
}
- async getTitles() {
+ async summarize() {
if (this.db) {
return this.db.dictionaries.toArray();
} else {
@@ -177,7 +234,7 @@ class Database {
}
const indexDataLoaded = async summary => {
- if (summary.version > 2) {
+ if (summary.version > 3) {
throw 'Unsupported dictionary version';
}
@@ -196,11 +253,11 @@ class Database {
const rows = [];
if (summary.version === 1) {
- for (const [expression, reading, tags, rules, score, ...glossary] of entries) {
+ for (const [expression, reading, definitionTags, rules, score, ...glossary] of entries) {
rows.push({
expression,
reading,
- tags,
+ definitionTags,
rules,
score,
glossary,
@@ -208,14 +265,16 @@ class Database {
});
}
} else {
- for (const [expression, reading, tags, rules, score, glossary] of entries) {
+ for (const [expression, reading, definitionTags, rules, score, glossary, sequence, termTags] of entries) {
rows.push({
expression,
reading,
- tags,
+ definitionTags,
rules,
score,
glossary,
+ sequence,
+ termTags,
dictionary: summary.title
});
}
@@ -300,12 +359,13 @@ class Database {
}
const rows = [];
- for (const [name, category, order, notes] of entries) {
+ for (const [name, category, order, notes, score] of entries) {
const row = dictTagSanitize({
name,
category,
order,
notes,
+ score,
dictionary: summary.title
});
@@ -350,12 +410,11 @@ class Database {
const summary = {
title: index.title,
revision: index.revision,
+ sequenced: index.sequenced,
version: index.format || index.version
};
- if (indexDataLoaded) {
- await indexDataLoaded(summary);
- }
+ await indexDataLoaded(summary);
const buildTermBankName = index => `term_bank_${index + 1}.json`;
const buildTermMetaBankName = index => `term_meta_bank_${index + 1}.json`;
@@ -390,7 +449,7 @@ class Database {
const bank = [];
for (const name in index.tagMeta) {
const tag = index.tagMeta[name];
- bank.push([name, tag.category, tag.order, tag.notes]);
+ bank.push([name, tag.category, tag.order, tag.notes, tag.score]);
}
tagDataLoaded(summary, bank, ++bankTotalCount, bankLoadedCount++);
diff --git a/ext/bg/js/dictionary.js b/ext/bg/js/dictionary.js
index 57acbe5e..fea5f3e5 100644
--- a/ext/bg/js/dictionary.js
+++ b/ext/bg/js/dictionary.js
@@ -89,7 +89,7 @@ function dictTermsSort(definitions, dictionaries=null) {
return 1;
}
- return v2.expression.localeCompare(v1.expression);
+ return v2.expression.toString().localeCompare(v1.expression.toString());
});
}
@@ -110,6 +110,33 @@ function dictTermsUndupe(definitions) {
return definitionsUnique;
}
+function dictTermsCompressTags(definitions) {
+ let lastDictionary = '';
+ let lastPartOfSpeech = '';
+
+ for (const definition of definitions) {
+ const dictionary = JSON.stringify(definition.definitionTags.filter(tag => tag.category === 'dictionary').map(tag => tag.name).sort());
+ const partOfSpeech = JSON.stringify(definition.definitionTags.filter(tag => tag.category === 'partOfSpeech').map(tag => tag.name).sort());
+
+ const filterOutCategories = [];
+
+ if (lastDictionary === dictionary) {
+ filterOutCategories.push('dictionary');
+ } else {
+ lastDictionary = dictionary;
+ lastPartOfSpeech = '';
+ }
+
+ if (lastPartOfSpeech === partOfSpeech) {
+ filterOutCategories.push('partOfSpeech');
+ } else {
+ lastPartOfSpeech = partOfSpeech;
+ }
+
+ definition.definitionTags = definition.definitionTags.filter(tag => !filterOutCategories.includes(tag.category));
+ }
+}
+
function dictTermsGroup(definitions, dictionaries) {
const groups = {};
for (const definition of definitions) {
@@ -136,6 +163,7 @@ function dictTermsGroup(definitions, dictionaries) {
expression: firstDef.expression,
reading: firstDef.reading,
reasons: firstDef.reasons,
+ termTags: groupDefs[0].termTags,
score: groupDefs.reduce((p, v) => v.score > p ? v.score : p, Number.MIN_SAFE_INTEGER),
source: firstDef.source
});
@@ -144,6 +172,116 @@ function dictTermsGroup(definitions, dictionaries) {
return dictTermsSort(results);
}
+function dictTermsMergeBySequence(definitions, mainDictionary) {
+ const definitionsBySequence = {'-1': []};
+ for (const definition of definitions) {
+ if (mainDictionary === definition.dictionary && definition.sequence >= 0) {
+ if (!definitionsBySequence[definition.sequence]) {
+ definitionsBySequence[definition.sequence] = {
+ reasons: definition.reasons,
+ score: Number.MIN_SAFE_INTEGER,
+ expression: new Set(),
+ reading: new Set(),
+ expressions: new Map(),
+ source: definition.source,
+ dictionary: definition.dictionary,
+ definitions: []
+ };
+ }
+ const score = Math.max(definitionsBySequence[definition.sequence].score, definition.score);
+ definitionsBySequence[definition.sequence].score = score;
+ } else {
+ definitionsBySequence['-1'].push(definition);
+ }
+ }
+
+ return definitionsBySequence;
+}
+
+function dictTermsMergeByGloss(result, definitions, appendTo, mergedIndices) {
+ const definitionsByGloss = appendTo || {};
+ for (const [index, definition] of definitions.entries()) {
+ if (appendTo) {
+ let match = false;
+ for (const expression of result.expressions.keys()) {
+ if (definition.expression === expression) {
+ for (const reading of result.expressions.get(expression).keys()) {
+ if (definition.reading === reading) {
+ match = true;
+ break;
+ }
+ }
+ }
+ if (match) {
+ break;
+ }
+ }
+
+ if (!match) {
+ continue;
+ } else if (mergedIndices) {
+ mergedIndices.add(index);
+ }
+ }
+
+ const gloss = JSON.stringify(definition.glossary.concat(definition.dictionary));
+ if (!definitionsByGloss[gloss]) {
+ definitionsByGloss[gloss] = {
+ expression: new Set(),
+ reading: new Set(),
+ definitionTags: [],
+ glossary: definition.glossary,
+ source: result.source,
+ reasons: [],
+ score: definition.score,
+ id: definition.id,
+ dictionary: definition.dictionary
+ };
+ }
+
+ definitionsByGloss[gloss].expression.add(definition.expression);
+ definitionsByGloss[gloss].reading.add(definition.reading);
+
+ result.expression.add(definition.expression);
+ result.reading.add(definition.reading);
+
+ // result->expressions[ Expression1[ Reading1[ Tag1, Tag2 ] ], Expression2, ... ]
+ if (!result.expressions.has(definition.expression)) {
+ result.expressions.set(definition.expression, new Map());
+ }
+ if (!result.expressions.get(definition.expression).has(definition.reading)) {
+ result.expressions.get(definition.expression).set(definition.reading, new Set());
+ }
+
+ for (const tag of definition.definitionTags) {
+ if (!definitionsByGloss[gloss].definitionTags.find(existingTag => existingTag.name === tag.name)) {
+ definitionsByGloss[gloss].definitionTags.push(tag);
+ }
+ }
+
+ for (const tag of definition.termTags) {
+ result.expressions.get(definition.expression).get(definition.reading).add(tag);
+ }
+ }
+
+ for (const gloss in definitionsByGloss) {
+ const definition = definitionsByGloss[gloss];
+ definition.only = [];
+ if (!utilSetEqual(definition.expression, result.expression)) {
+ for (const expression of utilSetIntersection(definition.expression, result.expression)) {
+ definition.only.push(expression);
+ }
+ }
+ if (!utilSetEqual(definition.reading, result.reading)) {
+ for (const reading of utilSetIntersection(definition.reading, result.reading)) {
+ definition.only.push(reading);
+ }
+ }
+ }
+
+ return definitionsByGloss;
+}
+
function dictTagBuildSource(name) {
return dictTagSanitize({name, category: 'dictionary', order: 100});
}
@@ -153,6 +291,7 @@ function dictTagSanitize(tag) {
tag.category = tag.category || 'default';
tag.notes = tag.notes || '';
tag.order = tag.order || 0;
+ tag.score = tag.score || 0;
return tag;
}
@@ -207,10 +346,12 @@ async function dictFieldFormat(field, definition, mode, options) {
const data = {
marker,
definition,
- group: options.general.groupResults,
+ group: options.general.resultOutputMode === 'group',
+ merge: options.general.resultOutputMode === 'merge',
modeTermKanji: mode === 'term-kanji',
modeTermKana: mode === 'term-kana',
- modeKanji: mode === 'kanji'
+ modeKanji: mode === 'kanji',
+ compactGlossaries: options.general.compactGlossaries
};
const html = await apiTemplateRender(options.anki.fieldTemplates, data, true);
diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js
index 36ab7694..9f1414ad 100644
--- a/ext/bg/js/options.js
+++ b/ext/bg/js/options.js
@@ -19,12 +19,26 @@
function optionsFieldTemplates() {
return `
+<style>
+.expression-popular {
+ color: #0275d8;
+}
+
+.expression-rare {
+ color: #999;
+}
+</style>
{{#*inline "glossary-single"}}
{{~#unless brief~}}
- {{~#if tags~}}<i>({{#each tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}})</i> {{/if~}}
+ {{~#if definitionTags~}}<i>({{#each definitionTags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}})</i> {{/if~}}
+ {{~#if only~}}({{#each only}}{{{.}}}{{#unless @last}}, {{/unless}}{{/each}} only) {{/if~}}
{{~/unless~}}
{{~#if glossary.[1]~}}
- <ul>{{#each glossary}}<li>{{#multiLine}}{{.}}{{/multiLine}}</li>{{/each}}</ul>
+ {{~#if compactGlossaries~}}
+ {{#each glossary}}{{#multiLine}}{{.}}{{/multiLine}}{{#unless @last}} | {{/unless}}{{/each}}
+ {{~else~}}
+ <ul>{{#each glossary}}<li>{{#multiLine}}{{.}}{{/multiLine}}</li>{{/each}}</ul>
+ {{~/if~}}
{{~else~}}
{{~#multiLine}}{{glossary.[0]}}{{/multiLine~}}
{{~/if~}}
@@ -41,23 +55,56 @@ function optionsFieldTemplates() {
{{/inline}}
{{#*inline "expression"}}
- {{~#if modeTermKana~}}
- {{~#if definition.reading~}}
- {{definition.reading}}
+ {{~#if merge~}}
+ {{~#if modeTermKana~}}
+ {{~#each definition.reading~}}
+ {{{.}}}
+ {{~#unless @last}}、{{/unless~}}
+ {{~else~}}
+ {{~#each definition.expression~}}
+ {{{.}}}
+ {{~#unless @last}}、{{/unless~}}
+ {{~/each~}}
+ {{~/each~}}
{{~else~}}
- {{definition.expression}}
+ {{~#each definition.expression~}}
+ {{{.}}}
+ {{~#unless @last}}、{{/unless~}}
+ {{~/each~}}
{{~/if~}}
{{~else~}}
- {{definition.expression}}
+ {{~#if modeTermKana~}}
+ {{~#if definition.reading~}}
+ {{definition.reading}}
+ {{~else~}}
+ {{definition.expression}}
+ {{~/if~}}
+ {{~else~}}
+ {{definition.expression}}
+ {{~/if~}}
{{~/if~}}
{{/inline}}
{{#*inline "furigana"}}
- {{#furigana}}{{{definition}}}{{/furigana}}
+ {{~#if merge~}}
+ {{~#each definition.expressions~}}
+ <span class="expression-{{termFrequency}}">{{~#furigana}}{{{.}}}{{/furigana~}}</span>
+ {{~#unless @last}}、{{/unless~}}
+ {{~/each~}}
+ {{~else~}}
+ {{#furigana}}{{{definition}}}{{/furigana}}
+ {{~/if~}}
{{/inline}}
{{#*inline "furigana-plain"}}
- {{#furiganaPlain}}{{{definition}}}{{/furiganaPlain}}
+ {{~#if merge~}}
+ {{~#each definition.expressions~}}
+ <span class="expression-{{termFrequency}}">{{~#furiganaPlain}}{{{.}}}{{/furiganaPlain~}}</span>
+ {{~#unless @last}}、{{/unless~}}
+ {{~/each~}}
+ {{~else~}}
+ {{#furiganaPlain}}{{{definition}}}{{/furiganaPlain}}
+ {{~/if~}}
{{/inline}}
{{#*inline "glossary"}}
@@ -71,12 +118,18 @@ function optionsFieldTemplates() {
{{~else~}}
{{~#if group~}}
{{~#if definition.definitions.[1]~}}
- <ol>{{#each definition.definitions}}<li>{{> glossary-single brief=../brief}}</li>{{/each}}</ol>
+ <ol>{{#each definition.definitions}}<li>{{> glossary-single brief=../brief compactGlossaries=../compactGlossaries}}</li>{{/each}}</ol>
+ {{~else~}}
+ {{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries~}}
+ {{~/if~}}
+ {{~else if merge~}}
+ {{~#if definition.definitions.[1]~}}
+ <ol>{{#each definition.definitions}}<li>{{> glossary-single brief=../brief compactGlossaries=../compactGlossaries}}</li>{{/each}}</ol>
{{~else~}}
- {{~> glossary-single definition.definitions.[0] brief=brief~}}
+ {{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries~}}
{{~/if~}}
{{~else~}}
- {{~> glossary-single definition brief=brief~}}
+ {{~> glossary-single definition brief=brief compactGlossaries=compactGlossaries~}}
{{~/if~}}
{{~/if~}}
</div>
@@ -95,7 +148,16 @@ function optionsFieldTemplates() {
{{/inline}}
{{#*inline "reading"}}
- {{~#unless modeTermKana}}{{definition.reading}}{{/unless~}}
+ {{~#unless modeTermKana~}}
+ {{~#if merge~}}
+ {{~#each definition.reading~}}
+ {{{.}}}
+ {{~#unless @last}}、{{/unless~}}
+ {{~/each~}}
+ {{~else~}}
+ {{~definition.reading~}}
+ {{~/if~}}
+ {{~/unless~}}
{{/inline}}
{{#*inline "sentence"}}
@@ -115,7 +177,7 @@ function optionsFieldTemplates() {
{{/inline}}
{{#*inline "tags"}}
- {{~#each definition.tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each~}}
+ {{~#each definition.definitionTags}}{{name}}{{#unless @last}}, {{/unless}}{{/each~}}
{{/inline}}
{{#*inline "url"}}
@@ -132,14 +194,17 @@ function optionsSetDefaults(options) {
enable: true,
audioSource: 'jpod101',
audioVolume: 100,
- groupResults: true,
+ resultOutputMode: 'group',
debugInfo: false,
maxResults: 32,
showAdvanced: false,
popupWidth: 400,
popupHeight: 250,
popupOffset: 10,
- showGuide: true
+ showGuide: true,
+ compactTags: false,
+ compactGlossaries: false,
+ mainDictionary: ''
},
scanning: {
@@ -205,6 +270,24 @@ function optionsVersion(options) {
} else {
options.scanning.modifier = 'none';
}
+ },
+ () => {
+ if (options.general.groupResults) {
+ options.general.resultOutputMode = 'group';
+ } else {
+ options.general.resultOutputMode = 'split';
+ }
+ if (utilStringHashCode(options.anki.fieldTemplates) !== -805327496) { // a3c8508031a1073629803d0616a2ee416cd3cccc
+ options.anki.fieldTemplates = `
+{{#if merge}}
+${optionsFieldTemplates()}
+{{else}}
+${options.anki.fieldTemplates}
+{{/if}}
+`.trim();
+ } else {
+ options.anki.fieldTemplates = optionsFieldTemplates();
+ }
}
];
diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js
index a2f22371..dcc9c43d 100644
--- a/ext/bg/js/settings.js
+++ b/ext/bg/js/settings.js
@@ -22,9 +22,11 @@ async function formRead() {
const optionsNew = $.extend(true, {}, optionsOld);
optionsNew.general.showGuide = $('#show-usage-guide').prop('checked');
+ optionsNew.general.compactTags = $('#compact-tags').prop('checked');
+ optionsNew.general.compactGlossaries = $('#compact-glossaries').prop('checked');
+ optionsNew.general.resultOutputMode = $('#result-output-mode').val();
optionsNew.general.audioSource = $('#audio-playback-source').val();
optionsNew.general.audioVolume = parseFloat($('#audio-playback-volume').val());
- optionsNew.general.groupResults = $('#group-terms-results').prop('checked');
optionsNew.general.debugInfo = $('#show-debug-info').prop('checked');
optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked');
optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10);
@@ -55,12 +57,14 @@ async function formRead() {
optionsNew.anki.kanji.fields = ankiFieldsToDict($('#kanji .anki-field-value'));
}
+ optionsNew.general.mainDictionary = $('#dict-main').val();
$('.dict-group').each((index, element) => {
const dictionary = $(element);
- const title = dictionary.data('title');
- const priority = parseInt(dictionary.find('.dict-priority').val(), 10);
- const enabled = dictionary.find('.dict-enabled').prop('checked');
- optionsNew.dictionaries[title] = {priority, enabled};
+ optionsNew.dictionaries[dictionary.data('title')] = {
+ priority: parseInt(dictionary.find('.dict-priority').val(), 10),
+ enabled: dictionary.find('.dict-enabled').prop('checked'),
+ allowSecondarySearches: dictionary.find('.dict-allow-secondary-searches').prop('checked')
+ };
});
return {optionsNew, optionsOld};
@@ -81,6 +85,13 @@ function formUpdateVisibility(options) {
advanced.hide();
}
+ const mainGroup = $('#dict-main-group');
+ if (options.general.resultOutputMode === 'merge') {
+ mainGroup.show();
+ } else {
+ mainGroup.hide();
+ }
+
const debug = $('#debug');
if (options.general.debugInfo) {
const temp = utilIsolate(options);
@@ -93,17 +104,33 @@ function formUpdateVisibility(options) {
}
}
-async function onFormOptionsChanged(e) {
- try {
- if (!e.originalEvent && !e.isTrigger) {
- return;
+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 await utilDatabaseSummarize()) {
+ if (dictRow.sequenced) {
+ select.append($(`<option value="${dictRow.title}">${dictRow.title}</option>`));
+ if (dictRow.title === options.general.mainDictionary) {
+ mainDictionary = dictRow.title;
+ }
}
+ }
- const {optionsNew, optionsOld} = await formRead();
- await optionsSave(optionsNew);
+ select.val(mainDictionary);
+}
+
+async function onFormOptionsChanged(e) {
+ if (!e.originalEvent && !e.isTrigger) {
+ return;
+ }
- formUpdateVisibility(optionsNew);
+ const {optionsNew, optionsOld} = await formRead();
+ await optionsSave(optionsNew);
+ formUpdateVisibility(optionsNew);
+ try {
const ankiUpdated =
optionsNew.anki.enable !== optionsOld.anki.enable ||
optionsNew.anki.server !== optionsOld.anki.server;
@@ -124,9 +151,11 @@ async function onReady() {
const options = await optionsLoad();
$('#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);
$('#audio-playback-source').val(options.general.audioSource);
$('#audio-playback-volume').val(options.general.audioVolume);
- $('#group-terms-results').prop('checked', options.general.groupResults);
$('#show-debug-info').prop('checked', options.general.debugInfo);
$('#show-advanced-options').prop('checked', options.general.showAdvanced);
$('#max-displayed-results').val(options.general.maxResults);
@@ -156,6 +185,7 @@ async function onReady() {
try {
await dictionaryGroupsPopulate(options);
+ await formMainDictionaryOptionsPopulate(options);
} catch (e) {
dictionaryErrorShow(e);
}
@@ -241,19 +271,26 @@ async function dictionaryGroupsPopulate(options) {
const dictGroups = $('#dict-groups').empty();
const dictWarning = $('#dict-warning').hide();
- const dictRows = await utilDatabaseGetTitles();
+ const dictRows = await utilDatabaseSummarize();
if (dictRows.length === 0) {
dictWarning.show();
}
for (const dictRow of dictRowsSort(dictRows, options)) {
- const dictOptions = options.dictionaries[dictRow.title] || {enabled: false, priority: 0};
+ 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,
- priority: dictOptions.priority,
- enabled: dictOptions.enabled
+ outdated: dictRow.version < 3
});
dictGroups.append($(dictHtml));
@@ -261,7 +298,7 @@ async function dictionaryGroupsPopulate(options) {
formUpdateVisibility(options);
- $('.dict-enabled, .dict-priority').change(e => {
+ $('.dict-enabled, .dict-priority, .dict-allow-secondary-searches').change(e => {
dictionaryGroupsSort();
onFormOptionsChanged(e);
});
@@ -270,7 +307,7 @@ async function dictionaryGroupsPopulate(options) {
async function onDictionaryPurge(e) {
e.preventDefault();
- const dictControls = $('#dict-importer, #dict-groups').hide();
+ const dictControls = $('#dict-importer, #dict-groups, #dict-main-group').hide();
const dictProgress = $('#dict-purge').show();
try {
@@ -280,9 +317,11 @@ async function onDictionaryPurge(e) {
await utilDatabasePurge();
const options = await optionsLoad();
options.dictionaries = {};
+ options.general.mainDictionary = '';
await optionsSave(options);
await dictionaryGroupsPopulate(options);
+ await formMainDictionaryOptionsPopulate(options);
} catch (e) {
dictionaryErrorShow(e);
} finally {
@@ -308,10 +347,14 @@ async function onDictionaryImport(e) {
const options = await optionsLoad();
const summary = await utilDatabaseImport(e.target.files[0], updateProgress);
- options.dictionaries[summary.title] = {enabled: true, priority: 0};
+ options.dictionaries[summary.title] = {enabled: true, priority: 0, allowSecondarySearches: false};
+ if (summary.sequenced && options.general.mainDictionary === '') {
+ options.general.mainDictionary = summary.title;
+ }
await optionsSave(options);
await dictionaryGroupsPopulate(options);
+ await formMainDictionaryOptionsPopulate(options);
} catch (e) {
dictionaryErrorShow(e);
} finally {
diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js
index b5d352fc..680ec742 100644
--- a/ext/bg/js/templates.js
+++ b/ext/bg/js/templates.js
@@ -1,6 +1,8 @@
(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;
@@ -11,9 +13,13 @@ templates['dictionary.html'] = template({"1":function(container,depth0,helpers,p
+ 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\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(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + "> Enable search</label>\n </div>\n <div class=\"form-group options-advanced\">\n <label for=\"dict-"
+ + "</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)))
@@ -204,15 +210,20 @@ templates['model.html'] = template({"1":function(container,depth0,helpers,partia
templates['terms.html'] = template({"1":function(container,depth0,helpers,partials,data) {
var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
- return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.tags : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + ((stack1 = helpers["if"].call(alias1,((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.program(9, data, 0),"data":data})) != null ? stack1 : "");
+ return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.definitionTags : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.only : depth0),{"name":"if","hash":{},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(11, data, 0),"inverse":container.program(17, data, 0),"data":data})) != null ? stack1 : "");
},"2":function(container,depth0,helpers,partials,data) {
- var stack1;
+ var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
- return "<div>\n"
- + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ return "<div "
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.compactGlossaries : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ">\n"
+ + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.definitionTags : depth0),{"name":"each","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n";
},"3":function(container,depth0,helpers,partials,data) {
+ return "class=\"compact-info\"";
+},"5":function(container,depth0,helpers,partials,data) {
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
return " <span class=\"label label-default tag-"
@@ -222,88 +233,192 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia
+ "\">"
+ alias4(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data}) : helper)))
+ "</span>\n";
-},"5":function(container,depth0,helpers,partials,data) {
+},"7":function(container,depth0,helpers,partials,data) {
+ var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
+
+ return "<div "
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.compactGlossaries : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ">\n ("
+ + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.only : depth0),{"name":"each","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + " only)\n</div>\n";
+},"8":function(container,depth0,helpers,partials,data) {
var stack1;
- return "<ul>\n"
- + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : "")
+ + ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.last),{"name":"unless","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + "\n";
+},"9":function(container,depth0,helpers,partials,data) {
+ return ", ";
+},"11":function(container,depth0,helpers,partials,data) {
+ var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
+
+ return "<ul "
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.compactGlossaries : depth0),{"name":"if","hash":{},"fn":container.program(12, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ">\n"
+ + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(14, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</ul>\n";
-},"6":function(container,depth0,helpers,partials,data) {
+},"12":function(container,depth0,helpers,partials,data) {
+ return "class=\"compact-glossary\"";
+},"14":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, buffer =
" <li><span class=\"glossary-item\">";
- stack1 = ((helper = (helper = helpers.multiLine || (depth0 != null ? depth0.multiLine : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"multiLine","hash":{},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
+ stack1 = ((helper = (helper = helpers.multiLine || (depth0 != null ? depth0.multiLine : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"multiLine","hash":{},"fn":container.program(15, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
if (!helpers.multiLine) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; }
return buffer + "</span></li>\n";
-},"7":function(container,depth0,helpers,partials,data) {
+},"15":function(container,depth0,helpers,partials,data) {
return container.escapeExpression(container.lambda(depth0, depth0));
-},"9":function(container,depth0,helpers,partials,data) {
- var stack1, helper, options, buffer =
- "<div class=\"glossary-item\">";
- stack1 = ((helper = (helper = helpers.multiLine || (depth0 != null ? depth0.multiLine : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"multiLine","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
+},"17":function(container,depth0,helpers,partials,data) {
+ var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), buffer =
+ "<div class=\"glossary-item "
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.compactGlossaries : depth0),{"name":"if","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + "\">";
+ stack1 = ((helper = (helper = helpers.multiLine || (depth0 != null ? depth0.multiLine : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"multiLine","hash":{},"fn":container.program(20, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(alias1,options) : helper));
if (!helpers.multiLine) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; }
return buffer + "</div>\n";
-},"10":function(container,depth0,helpers,partials,data) {
+},"18":function(container,depth0,helpers,partials,data) {
+ return "compact-glossary";
+},"20":function(container,depth0,helpers,partials,data) {
var stack1;
return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["0"] : stack1), depth0));
-},"12":function(container,depth0,helpers,partials,data) {
- var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), buffer =
- "<div class=\"entry\" data-type=\"term\">\n <div class=\"actions\">\n"
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.addable : depth0),{"name":"if","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.playback : depth0),{"name":"if","hash":{},"fn":container.program(15, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + " <img src=\"/mixed/img/entry-current.png\" class=\"current\" title=\"Current entry (Alt + Up/Down/Home/End/PgUp/PgDn)\" alt>\n </div>\n\n <div class=\"expression\">";
- stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"kanjiLinks","hash":{},"fn":container.program(17, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(alias1,options) : helper));
- if (!helpers.kanjiLinks) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
- if (stack1 != null) { buffer += stack1; }
- return buffer + "</div>\n\n"
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.reasons : depth0),{"name":"if","hash":{},"fn":container.program(20, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+},"22":function(container,depth0,helpers,partials,data,blockParams,depths) {
+ var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
+
+ return "<div class=\"entry\" data-type=\"term\">\n <div class=\"actions\">\n"
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.addable : depth0),{"name":"if","hash":{},"fn":container.program(23, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.merged : depth0),{"name":"unless","hash":{},"fn":container.program(25, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + " <img src=\"/mixed/img/entry-current.png\" class=\"current\" title=\"Current entry (Alt + Up/Down/Home/End/PgUp/PgDn)\" alt>\n </div>\n\n"
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.merged : depth0),{"name":"if","hash":{},"fn":container.program(28, data, 0, blockParams, depths),"inverse":container.program(43, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "")
+ "\n"
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.frequencies : depth0),{"name":"if","hash":{},"fn":container.program(24, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.reasons : depth0),{"name":"if","hash":{},"fn":container.program(47, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + "\n"
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.frequencies : depth0),{"name":"if","hash":{},"fn":container.program(51, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n <div class=\"glossary\">\n"
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.grouped : depth0),{"name":"if","hash":{},"fn":container.program(27, data, 0),"inverse":container.program(33, data, 0),"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.grouped : depth0),{"name":"if","hash":{},"fn":container.program(54, data, 0, blockParams, depths),"inverse":container.program(60, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "")
+ " </div>\n\n"
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.debug : depth0),{"name":"if","hash":{},"fn":container.program(35, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.debug : depth0),{"name":"if","hash":{},"fn":container.program(63, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n";
-},"13":function(container,depth0,helpers,partials,data) {
+},"23":function(container,depth0,helpers,partials,data) {
return " <a href=\"#\" class=\"action-view-note pending disabled\"><img src=\"/mixed/img/view-note.png\" title=\"View added note (Alt + V)\" alt></a>\n <a href=\"#\" class=\"action-add-note pending disabled\" data-mode=\"term-kanji\"><img src=\"/mixed/img/add-term-kanji.png\" title=\"Add expression (Alt + E)\" alt></a>\n <a href=\"#\" class=\"action-add-note pending disabled\" data-mode=\"term-kana\"><img src=\"/mixed/img/add-term-kana.png\" title=\"Add reading (Alt + R)\" alt></a>\n";
-},"15":function(container,depth0,helpers,partials,data) {
+},"25":function(container,depth0,helpers,partials,data) {
+ var stack1;
+
+ return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.playback : depth0),{"name":"if","hash":{},"fn":container.program(26, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
+},"26":function(container,depth0,helpers,partials,data) {
return " <a href=\"#\" class=\"action-play-audio\"><img src=\"/mixed/img/play-audio.png\" title=\"Play audio (Alt + P)\" alt></a>\n";
-},"17":function(container,depth0,helpers,partials,data) {
+},"28":function(container,depth0,helpers,partials,data,blockParams,depths) {
+ var stack1;
+
+ return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.expressions : depth0),{"name":"each","hash":{},"fn":container.program(29, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
+},"29":function(container,depth0,helpers,partials,data,blockParams,depths) {
+ var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", buffer =
+ "<div class=\"expression\">\n <span class=\"expression-"
+ + container.escapeExpression(((helper = (helper = helpers.termFrequency || (depth0 != null ? depth0.termFrequency : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"termFrequency","hash":{},"data":data}) : helper)))
+ + "\">";
+ stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : alias2),(options={"name":"kanjiLinks","hash":{},"fn":container.program(30, data, 0, blockParams, depths),"inverse":container.noop,"data":data}),(typeof helper === alias3 ? helper.call(alias1,options) : helper));
+ if (!helpers.kanjiLinks) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</span>\n <div class=\"peek-wrapper\">"
+ + ((stack1 = helpers["if"].call(alias1,(depths[1] != null ? depths[1].playback : depths[1]),{"name":"if","hash":{},"fn":container.program(33, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.termTags : depth0),{"name":"if","hash":{},"fn":container.program(35, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.frequencies : depth0),{"name":"if","hash":{},"fn":container.program(38, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + "</div>\n <span class=\""
+ + ((stack1 = helpers["if"].call(alias1,(data && data.last),{"name":"if","hash":{},"fn":container.program(41, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + "\">、</span>\n </div>";
+},"30":function(container,depth0,helpers,partials,data) {
var stack1, helper, options;
- stack1 = ((helper = (helper = helpers.furigana || (depth0 != null ? depth0.furigana : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"furigana","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
+ stack1 = ((helper = (helper = helpers.furigana || (depth0 != null ? depth0.furigana : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"furigana","hash":{},"fn":container.program(31, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
if (!helpers.furigana) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { return stack1; }
else { return ''; }
-},"18":function(container,depth0,helpers,partials,data) {
+},"31":function(container,depth0,helpers,partials,data) {
var stack1;
return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : "");
-},"20":function(container,depth0,helpers,partials,data) {
+},"33":function(container,depth0,helpers,partials,data) {
+ return "<a href=\"#\" class=\"action-play-audio\"><img src=\"/mixed/img/play-audio.png\" title=\"Play audio\" alt></a>";
+},"35":function(container,depth0,helpers,partials,data) {
+ var stack1;
+
+ return "<div class=\"tags\">"
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.termTags : depth0),{"name":"each","hash":{},"fn":container.program(36, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + "</div>";
+},"36":function(container,depth0,helpers,partials,data) {
+ var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
+
+ return " <span class=\"label label-default tag-"
+ + alias4(((helper = (helper = helpers.category || (depth0 != null ? depth0.category : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"category","hash":{},"data":data}) : helper)))
+ + "\" title=\""
+ + alias4(((helper = (helper = helpers.notes || (depth0 != null ? depth0.notes : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"notes","hash":{},"data":data}) : helper)))
+ + "\">"
+ + alias4(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data}) : helper)))
+ + "</span>\n";
+},"38":function(container,depth0,helpers,partials,data) {
+ var stack1;
+
+ return "<div class=\"frequencies\">"
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.frequencies : depth0),{"name":"each","hash":{},"fn":container.program(39, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + "</div>";
+},"39":function(container,depth0,helpers,partials,data) {
+ var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
+
+ return " <span class=\"label label-default tag-frequency\">"
+ + alias4(((helper = (helper = helpers.dictionary || (depth0 != null ? depth0.dictionary : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"dictionary","hash":{},"data":data}) : helper)))
+ + ":"
+ + alias4(((helper = (helper = helpers.frequency || (depth0 != null ? depth0.frequency : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"frequency","hash":{},"data":data}) : helper)))
+ + "</span>\n";
+},"41":function(container,depth0,helpers,partials,data) {
+ return "invisible";
+},"43":function(container,depth0,helpers,partials,data) {
+ var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), buffer =
+ " <div class=\"expression\">";
+ stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"kanjiLinks","hash":{},"fn":container.program(30, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(alias1,options) : helper));
+ if (!helpers.kanjiLinks) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</div>\n"
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.termTags : depth0),{"name":"if","hash":{},"fn":container.program(44, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
+},"44":function(container,depth0,helpers,partials,data) {
+ var stack1;
+
+ return " <div style=\"display: inline-block;\">\n"
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.termTags : depth0),{"name":"each","hash":{},"fn":container.program(45, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + " </div>\n";
+},"45":function(container,depth0,helpers,partials,data) {
+ var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
+
+ return " <span class=\"label label-default tag-"
+ + alias4(((helper = (helper = helpers.category || (depth0 != null ? depth0.category : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"category","hash":{},"data":data}) : helper)))
+ + "\" title=\""
+ + alias4(((helper = (helper = helpers.notes || (depth0 != null ? depth0.notes : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"notes","hash":{},"data":data}) : helper)))
+ + "\">"
+ + alias4(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data}) : helper)))
+ + "</span>\n";
+},"47":function(container,depth0,helpers,partials,data) {
var stack1;
return " <div class=\"reasons\">\n"
- + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.reasons : depth0),{"name":"each","hash":{},"fn":container.program(21, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.reasons : depth0),{"name":"each","hash":{},"fn":container.program(48, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </div>\n";
-},"21":function(container,depth0,helpers,partials,data) {
+},"48":function(container,depth0,helpers,partials,data) {
var stack1;
return " <span class=\"reasons\">"
+ container.escapeExpression(container.lambda(depth0, depth0))
+ "</span> "
- + ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.last),{"name":"unless","hash":{},"fn":container.program(22, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.last),{"name":"unless","hash":{},"fn":container.program(49, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n";
-},"22":function(container,depth0,helpers,partials,data) {
+},"49":function(container,depth0,helpers,partials,data) {
return "&laquo;";
-},"24":function(container,depth0,helpers,partials,data) {
+},"51":function(container,depth0,helpers,partials,data) {
var stack1;
return " <div>\n"
- + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.frequencies : depth0),{"name":"each","hash":{},"fn":container.program(25, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.frequencies : depth0),{"name":"each","hash":{},"fn":container.program(52, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </div>\n";
-},"25":function(container,depth0,helpers,partials,data) {
+},"52":function(container,depth0,helpers,partials,data) {
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
return " <span class=\"label label-default tag-frequency\">"
@@ -311,62 +426,67 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia
+ ":"
+ alias4(((helper = (helper = helpers.frequency || (depth0 != null ? depth0.frequency : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"frequency","hash":{},"data":data}) : helper)))
+ "</span>\n";
-},"27":function(container,depth0,helpers,partials,data) {
+},"54":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
- return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definitions : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(28, data, 0),"inverse":container.program(31, data, 0),"data":data})) != null ? stack1 : "");
-},"28":function(container,depth0,helpers,partials,data) {
+ return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definitions : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(55, data, 0, blockParams, depths),"inverse":container.program(58, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
+},"55":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return " <ol>\n"
- + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(29, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(56, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </ol>\n";
-},"29":function(container,depth0,helpers,partials,data) {
+},"56":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return " <li>"
- + ((stack1 = container.invokePartial(partials.definition,depth0,{"name":"definition","data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "")
+ + ((stack1 = container.invokePartial(partials.definition,depth0,{"name":"definition","hash":{"compactGlossaries":(depths[1] != null ? depths[1].compactGlossaries : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "")
+ "</li>\n";
-},"31":function(container,depth0,helpers,partials,data) {
+},"58":function(container,depth0,helpers,partials,data) {
var stack1;
- return ((stack1 = container.invokePartial(partials.definition,((stack1 = (depth0 != null ? depth0.definitions : depth0)) != null ? stack1["0"] : stack1),{"name":"definition","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
-},"33":function(container,depth0,helpers,partials,data) {
+ return ((stack1 = container.invokePartial(partials.definition,((stack1 = (depth0 != null ? depth0.definitions : depth0)) != null ? stack1["0"] : stack1),{"name":"definition","hash":{"compactGlossaries":(depth0 != null ? depth0.compactGlossaries : depth0)},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
+},"60":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
- return ((stack1 = container.invokePartial(partials.definition,depth0,{"name":"definition","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
-},"35":function(container,depth0,helpers,partials,data) {
+ return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.merged : depth0),{"name":"if","hash":{},"fn":container.program(54, data, 0, blockParams, depths),"inverse":container.program(61, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
+},"61":function(container,depth0,helpers,partials,data) {
+ var stack1;
+
+ return ((stack1 = container.invokePartial(partials.definition,depth0,{"name":"definition","hash":{"compactGlossaries":(depth0 != null ? depth0.compactGlossaries : depth0)},"data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "")
+ + " ";
+},"63":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, buffer =
" <pre>";
- stack1 = ((helper = (helper = helpers.dumpObject || (depth0 != null ? depth0.dumpObject : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"dumpObject","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
+ stack1 = ((helper = (helper = helpers.dumpObject || (depth0 != null ? depth0.dumpObject : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"dumpObject","hash":{},"fn":container.program(31, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
if (!helpers.dumpObject) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; }
return buffer + "</pre>\n";
-},"37":function(container,depth0,helpers,partials,data,blockParams,depths) {
+},"65":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
- return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(38, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
-},"38":function(container,depth0,helpers,partials,data,blockParams,depths) {
+ return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(66, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
+},"66":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
- return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.first),{"name":"unless","hash":{},"fn":container.program(39, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.first),{"name":"unless","hash":{},"fn":container.program(67, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n"
- + ((stack1 = container.invokePartial(partials.term,depth0,{"name":"term","hash":{"playback":(depths[1] != null ? depths[1].playback : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1]),"grouped":(depths[1] != null ? depths[1].grouped : depths[1]),"debug":(depths[1] != null ? depths[1].debug : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
-},"39":function(container,depth0,helpers,partials,data) {
+ + ((stack1 = container.invokePartial(partials.term,depth0,{"name":"term","hash":{"compactGlossaries":(depths[1] != null ? depths[1].compactGlossaries : depths[1]),"playback":(depths[1] != null ? depths[1].playback : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1]),"merged":(depths[1] != null ? depths[1].merged : depths[1]),"grouped":(depths[1] != null ? depths[1].grouped : depths[1]),"debug":(depths[1] != null ? depths[1].debug : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
+},"67":function(container,depth0,helpers,partials,data) {
return "<hr>";
-},"41":function(container,depth0,helpers,partials,data) {
+},"69":function(container,depth0,helpers,partials,data) {
return "<p class=\"note\">No results found.</p>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return "\n\n"
- + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(37, data, 0, blockParams, depths),"inverse":container.program(41, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
+ + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(65, data, 0, blockParams, depths),"inverse":container.program(69, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
},"main_d": function(fn, props, container, depth0, data, blockParams, depths) {
var decorators = container.decorators;
fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(1, data, 0, blockParams, depths),"inverse":container.noop,"args":["definition"],"data":data}) || fn;
- fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(12, data, 0, blockParams, depths),"inverse":container.noop,"args":["term"],"data":data}) || fn;
+ fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(22, data, 0, blockParams, depths),"inverse":container.noop,"args":["term"],"data":data}) || fn;
return fn;
}
diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js
index c915dbc0..005dd5de 100644
--- a/ext/bg/js/translator.js
+++ b/ext/bg/js/translator.js
@@ -37,6 +37,7 @@ class Translator {
}
async findTermsGrouped(text, dictionaries, alphanumeric) {
+ const options = await apiOptionsGet();
const titles = Object.keys(dictionaries);
const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric);
@@ -45,9 +46,118 @@ class Translator {
await this.buildTermFrequencies(definition, titles);
}
+ if (options.general.compactTags) {
+ for (const definition of definitionsGrouped) {
+ dictTermsCompressTags(definition.definitions);
+ }
+ }
+
return {length, definitions: definitionsGrouped};
}
+ async findTermsMerged(text, dictionaries, alphanumeric) {
+ const options = await apiOptionsGet();
+ const secondarySearchTitles = Object.keys(options.dictionaries).filter(dict => options.dictionaries[dict].allowSecondarySearches);
+ const titles = Object.keys(dictionaries);
+ const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric);
+
+ const definitionsBySequence = dictTermsMergeBySequence(definitions, options.general.mainDictionary);
+
+ const definitionsMerged = [];
+ const mergedByTermIndices = new Set();
+ for (const sequence in definitionsBySequence) {
+ if (sequence < 0) {
+ continue;
+ }
+
+ const result = definitionsBySequence[sequence];
+
+ const rawDefinitionsBySequence = await this.database.findTermsBySequence(Number(sequence), options.general.mainDictionary);
+
+ for (const definition of rawDefinitionsBySequence) {
+ const tags = await this.expandTags(definition.definitionTags, definition.dictionary);
+ tags.push(dictTagBuildSource(definition.dictionary));
+ definition.definitionTags = tags;
+ }
+
+ const definitionsByGloss = dictTermsMergeByGloss(result, rawDefinitionsBySequence);
+
+ const secondarySearchResults = [];
+ if (secondarySearchTitles.length > 0) {
+ for (const expression of result.expressions.keys()) {
+ if (expression === text) {
+ continue;
+ }
+
+ for (const reading of result.expressions.get(expression).keys()) {
+ for (const definition of await this.database.findTermsExact(expression, reading, secondarySearchTitles)) {
+ const tags = await this.expandTags(definition.definitionTags, definition.dictionary);
+ tags.push(dictTagBuildSource(definition.dictionary));
+ definition.definitionTags = tags;
+ secondarySearchResults.push(definition);
+ }
+ }
+ }
+ }
+
+ dictTermsMergeByGloss(result, definitionsBySequence['-1'].concat(secondarySearchResults), definitionsByGloss, mergedByTermIndices);
+
+ for (const gloss in definitionsByGloss) {
+ const definition = definitionsByGloss[gloss];
+ dictTagsSort(definition.definitionTags);
+ result.definitions.push(definition);
+ }
+
+ dictTermsSort(result.definitions, dictionaries);
+
+ const expressions = [];
+ for (const expression of result.expressions.keys()) {
+ for (const reading of result.expressions.get(expression).keys()) {
+ const tags = await this.expandTags(result.expressions.get(expression).get(reading), result.dictionary);
+ expressions.push({
+ expression: expression,
+ reading: reading,
+ termTags: dictTagsSort(tags),
+ termFrequency: (score => {
+ if (score > 0) {
+ return 'popular';
+ } else if (score < 0) {
+ return 'rare';
+ } else {
+ return 'normal';
+ }
+ })(tags.map(tag => tag.score).reduce((p, v) => p + v, 0))
+ });
+ }
+ }
+
+ result.expressions = expressions;
+
+ result.expression = Array.from(result.expression);
+ result.reading = Array.from(result.reading);
+
+ definitionsMerged.push(result);
+ }
+
+ const strayDefinitions = definitionsBySequence['-1'].filter((definition, index) => !mergedByTermIndices.has(index));
+ for (const groupedDefinition of dictTermsGroup(strayDefinitions, dictionaries)) {
+ groupedDefinition.expressions = [{expression: groupedDefinition.expression, reading: groupedDefinition.reading}];
+ definitionsMerged.push(groupedDefinition);
+ }
+
+ for (const definition of definitionsMerged) {
+ await this.buildTermFrequencies(definition, titles);
+ }
+
+ if (options.general.compactTags) {
+ for (const definition of definitionsMerged) {
+ dictTermsCompressTags(definition.definitions);
+ }
+ }
+
+ return {length, definitions: dictTermsSort(definitionsMerged)};
+ }
+
async findTermsSplit(text, dictionaries, alphanumeric) {
const titles = Object.keys(dictionaries);
const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric);
@@ -78,8 +188,9 @@ class Translator {
let definitions = [];
for (const deinflection of deinflections) {
for (const definition of deinflection.definitions) {
- const tags = await this.expandTags(definition.tags, definition.dictionary);
- tags.push(dictTagBuildSource(definition.dictionary));
+ const definitionTags = await this.expandTags(definition.definitionTags, definition.dictionary);
+ definitionTags.push(dictTagBuildSource(definition.dictionary));
+ const termTags = await this.expandTags(definition.termTags, definition.dictionary);
definitions.push({
source: deinflection.source,
@@ -90,7 +201,9 @@ class Translator {
expression: definition.expression,
reading: definition.reading,
glossary: definition.glossary,
- tags: dictTagsSort(tags)
+ definitionTags: dictTagsSort(definitionTags),
+ termTags: dictTagsSort(termTags),
+ sequence: definition.sequence
});
}
}
@@ -158,14 +271,23 @@ class Translator {
}
async buildTermFrequencies(definition, titles) {
- definition.frequencies = [];
- for (const meta of await this.database.findTermMeta(definition.expression, titles)) {
- if (meta.mode === 'freq') {
- definition.frequencies.push({
- expression: meta.expression,
- frequency: meta.data,
- dictionary: meta.dictionary
- });
+ let terms = [];
+ if (definition.expressions) {
+ terms = terms.concat(definition.expressions);
+ } else {
+ terms.push(definition);
+ }
+
+ for (const term of terms) {
+ term.frequencies = [];
+ for (const meta of await this.database.findTermMeta(term.expression, titles)) {
+ if (meta.mode === 'freq') {
+ term.frequencies.push({
+ expression: meta.expression,
+ frequency: meta.data,
+ dictionary: meta.dictionary
+ });
+ }
}
}
}
diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js
index f44582eb..216cef3f 100644
--- a/ext/bg/js/util.js
+++ b/ext/bg/js/util.js
@@ -26,6 +26,43 @@ function utilIsolate(data) {
return JSON.parse(JSON.stringify(data));
}
+function utilSetEqual(setA, setB) {
+ if (setA.size !== setB.size) {
+ return false;
+ }
+
+ for (const value of setA) {
+ if (!setB.has(value)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function utilSetIntersection(setA, setB) {
+ return new Set(
+ [...setA].filter(value => setB.has(value))
+ );
+}
+
+function utilSetDifference(setA, setB) {
+ return new Set(
+ [...setA].filter(value => !setB.has(value))
+ );
+}
+
+function utilStringHashCode(string) {
+ let hashCode = 0;
+
+ for (let i = 0, charCode = string.charCodeAt(i); i < string.length; charCode = string.charCodeAt(++i)) {
+ hashCode = ((hashCode << 5) - hashCode) + charCode;
+ hashCode |= 0;
+ }
+
+ return hashCode;
+}
+
function utilBackend() {
return chrome.extension.getBackgroundPage().yomichan_backend;
}
@@ -38,12 +75,12 @@ function utilAnkiGetDeckNames() {
return utilBackend().anki.getDeckNames();
}
-function utilAnkiGetModelFieldNames(modelName) {
- return utilBackend().anki.getModelFieldNames(modelName);
+function utilDatabaseSummarize() {
+ return utilBackend().translator.database.summarize();
}
-function utilDatabaseGetTitles() {
- return utilBackend().translator.database.getTitles();
+function utilAnkiGetModelFieldNames(modelName) {
+ return utilBackend().anki.getModelFieldNames(modelName);
}
function utilDatabasePurge() {
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index 4315d74b..65043d44 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -36,7 +36,11 @@
</div>
<div class="checkbox">
- <label><input type="checkbox" id="group-terms-results"> Group term results</label>
+ <label><input type="checkbox" id="compact-tags"> Compact tags</label>
+ </div>
+
+ <div class="checkbox">
+ <label><input type="checkbox" id="compact-glossaries"> Compact glossaries</label>
</div>
<div class="checkbox">
@@ -48,6 +52,15 @@
</div>
<div class="form-group">
+ <label for="result-output-mode">Result grouping</label>
+ <select class="form-control" id="result-output-mode">
+ <option value="group">Group results by term-reading pairs</option>
+ <option value="merge">Group results by main dictionary entry</option>
+ <option value="split">Split definitions to their own results</option>
+ </select>
+ </div>
+
+ <div class="form-group">
<label for="audio-playback-source">Audio playback source</label>
<select class="form-control" id="audio-playback-source">
<option value="disabled">Disabled</option>
@@ -132,10 +145,14 @@
or you can simply <a href="#" id="dict-purge-link">purge the database</a> to delete everything.
</p>
<p class="help-block">
- Please visit the <a href="https://foosoft.net/projects/yomichan" target="_blank">Yomichan</a> homepage to download free
- dictionaries that you can use with this extension.
+ Deleting individual dictionaries is not currently feasible due to limitations of browser database technology.
</p>
+ <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>
+ </div>
+
<div class="text-danger" id="dict-purge">Dictionary data is being purged, please be patient...</div>
<div class="alert alert-warning" id="dict-warning">No dictionaries have been installed</div>
<div class="alert alert-danger" id="dict-error"></div>
@@ -150,6 +167,11 @@
</div>
<div id="dict-importer">
+ <p class="help-block">
+ Select a dictionary to import for use below. Please visit the Yomichan homepage to
+ <a href="https://foosoft.net/projects/yomichan" target="_blank">download free dictionaries</a>
+ for use with this extension and to learn about importing proprietary EPWING dictionaries.
+ </p>
<input type="file" id="dict-file">
</div>
</div>
diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js
index a1806d77..821a279f 100644
--- a/ext/fg/js/document.js
+++ b/ext/fg/js/document.js
@@ -76,7 +76,7 @@ function docRangeFromPoint(point) {
if (!document.caretRangeFromPoint) {
document.caretRangeFromPoint = (x, y) => {
const position = document.caretPositionFromPoint(x,y);
- if (position && position.offsetNode) {
+ if (position && position.offsetNode && position.offsetNode.nodeType === Node.TEXT_NODE) {
const range = document.createRange();
range.setStart(position.offsetNode, position.offset);
range.setEnd(position.offsetNode, position.offset);
@@ -86,7 +86,7 @@ function docRangeFromPoint(point) {
}
const range = document.caretRangeFromPoint(point.x, point.y);
- if (range && range.startContainer.nodeType === 3 && range.endContainer.nodeType === 3) {
+ if (range) {
return new TextSourceRange(range);
}
}
diff --git a/ext/manifest.json b/ext/manifest.json
index b6d0642e..e415ece9 100644
--- a/ext/manifest.json
+++ b/ext/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Yomichan",
- "version": "1.4.0",
+ "version": "1.5.0",
"description": "Japanese dictionary with Anki integration",
"icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"},
diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css
index cdc1be8c..eadb9dae 100644
--- a/ext/mixed/css/display.css
+++ b/ext/mixed/css/display.css
@@ -46,6 +46,10 @@ hr {
display: none;
}
+.invisible {
+ visibility: hidden;
+}
+
/*
* Entries
@@ -88,6 +92,10 @@ hr {
background-color: #5cb85c;
}
+.tag-partOfSpeech {
+ background-color: #565656;
+}
+
.actions .disabled {
pointer-events: none;
cursor: default;
@@ -118,21 +126,79 @@ hr {
font-size: 24px;
}
-.expression a {
+.expression .kanji-link {
border-bottom: 1px #777 dashed;
color: #333;
text-decoration: none;
}
+.expression-popular, .expression-popular .kanji-link {
+ color: #0275d8;
+}
+
+.expression-rare, .expression-rare .kanji-link {
+ color: #999;
+}
+
+.expression .peek-wrapper {
+ font-size: 14px;
+ white-space: nowrap;
+ display: inline-block;
+ position: relative;
+ width: 0px;
+ height: 0px;
+ visibility: hidden;
+}
+
+.expression .peek-wrapper .action-play-audio {
+ position: absolute;
+ left: 0px;
+ bottom: 10px;
+}
+
+.expression .peek-wrapper .tags {
+ position: absolute;
+ left: 0px;
+ bottom: -10px;
+}
+
+.expression .peek-wrapper .frequencies {
+ position: absolute;
+ left: 0px;
+ bottom: -30px;
+}
+
+.expression:hover .peek-wrapper {
+ visibility: visible;
+}
+
.reasons {
color: #777;
display: inline-block;
}
+.compact-info {
+ display: inline-block;
+}
+
.glossary ol, .glossary ul {
padding-left: 1.4em;
}
+.glossary ul.compact-glossary {
+ display: inline;
+ list-style: none;
+ padding-left: 0px;
+}
+
+.glossary .compact-glossary li {
+ display: inline;
+}
+
+.glossary .compact-glossary li:not(:first-child):before {
+ content: " | ";
+}
+
.glossary li {
color: #777;
}
@@ -141,6 +207,10 @@ hr {
color: #000;
}
+div.glossary-item.compact-glossary {
+ display: inline;
+}
+
.glyph {
font-family: kanji-stroke-orders;
font-size: 120px;
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index 75ee339a..5d3c4f2e 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -29,6 +29,7 @@ class Display {
this.audioCache = {};
$(document).keydown(this.onKeyDown.bind(this));
+ $(document).on('wheel', this.onWheel.bind(this));
}
onError(error) {
@@ -70,8 +71,10 @@ class Display {
onAudioPlay(e) {
e.preventDefault();
- const index = Display.entryIndexFind($(e.currentTarget));
- this.audioPlay(this.definitions[index]);
+ const link = $(e.currentTarget);
+ const definitionIndex = Display.entryIndexFind(link);
+ const expressionIndex = link.closest('.entry').find('.expression .action-play-audio').index(link);
+ this.audioPlay(this.definitions[definitionIndex], expressionIndex);
}
onNoteAdd(e) {
@@ -182,7 +185,8 @@ class Display {
80: /* p */ () => {
if (e.altKey) {
if ($('.entry').eq(this.index).data('type') === 'term') {
- this.audioPlay(this.definitions[this.index]);
+ const expressionIndex = this.options.general.resultOutputMode === 'merge' ? 0 : -1;
+ this.audioPlay(this.definitions[this.index], expressionIndex);
}
return true;
@@ -202,6 +206,25 @@ class Display {
}
}
+ onWheel(e) {
+ const event = e.originalEvent;
+ const handler = () => {
+ if (event.altKey) {
+ if (event.deltaY < 0) { // scroll up
+ this.entryScrollIntoView(this.index - 1, true);
+ return true;
+ } else if (event.deltaY > 0) { // scroll down
+ this.entryScrollIntoView(this.index + 1, true);
+ return true;
+ }
+ }
+ };
+
+ if (handler()) {
+ event.preventDefault();
+ }
+ }
+
async termsShow(definitions, options, context) {
try {
window.focus();
@@ -214,8 +237,10 @@ class Display {
const params = {
definitions,
addable: options.anki.enable,
- grouped: options.general.groupResults,
+ grouped: options.general.resultOutputMode === 'group',
+ merged: options.general.resultOutputMode === 'merge',
playback: options.general.audioSource !== 'disabled',
+ compactGlossaries: options.general.compactGlossaries,
debug: options.general.debugInfo
};
@@ -359,11 +384,12 @@ class Display {
}
}
- async audioPlay(definition) {
+ async audioPlay(definition, expressionIndex) {
try {
this.spinner.show();
- let url = await apiAudioGetUrl(definition, this.options.general.audioSource);
+ const expression = expressionIndex === -1 ? definition : definition.expressions[expressionIndex];
+ let url = await apiAudioGetUrl(expression, this.options.general.audioSource);
if (!url) {
url = '/mixed/mp3/button.mp3';
}