aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/bg/js/api.js8
-rw-r--r--ext/bg/js/audio.js9
-rw-r--r--ext/bg/js/database.js107
-rw-r--r--ext/bg/js/dictionary.js147
-rw-r--r--ext/bg/js/options.js115
-rw-r--r--ext/bg/js/settings.js61
-rw-r--r--ext/bg/js/templates.js246
-rw-r--r--ext/bg/js/translator.js144
-rw-r--r--ext/bg/js/util.js41
-rw-r--r--ext/bg/settings.html20
-rw-r--r--ext/mixed/css/display.css72
-rw-r--r--ext/mixed/js/display.js16
-rw-r--r--tmpl/dictionary.html3
-rw-r--r--tmpl/terms.html74
14 files changed, 927 insertions, 136 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..fcf8ef3f 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,
+ 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
+ id: row.id,
+ sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence
});
}
});
@@ -171,12 +228,27 @@ class Database {
}
}
+ async getTitlesWithSequences() {
+ if (!this.db) {
+ throw 'Database not initialized';
+ }
+
+ const titles = [];
+ await this.db.dictionaries.each(row => {
+ if (row.hasSequences) {
+ titles.push(row.title);
+ }
+ });
+
+ return titles;
+ }
+
async importDictionary(archive, callback) {
if (!this.db) {
throw 'Database not initialized';
}
- const indexDataLoaded = async summary => {
+ const indexDataValid = async summary => {
if (summary.version > 2) {
throw 'Unsupported dictionary version';
}
@@ -185,7 +257,9 @@ class Database {
if (count > 0) {
throw 'Dictionary is already imported';
}
+ };
+ const indexDataLoaded = async summary => {
await this.db.dictionaries.add(summary);
};
@@ -196,11 +270,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,19 +282,23 @@ 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
});
}
}
+ summary.hasSequences = rows.every(row => row.sequence >= 0);
+
await this.db.terms.bulkAdd(rows);
};
@@ -300,12 +378,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
});
@@ -317,6 +396,7 @@ class Database {
return await Database.importDictionaryZip(
archive,
+ indexDataValid,
indexDataLoaded,
termDataLoaded,
termMetaDataLoaded,
@@ -328,6 +408,7 @@ class Database {
static async importDictionaryZip(
archive,
+ indexDataValid,
indexDataLoaded,
termDataLoaded,
termMetaDataLoaded,
@@ -353,9 +434,7 @@ class Database {
version: index.format || index.version
};
- if (indexDataLoaded) {
- await indexDataLoaded(summary);
- }
+ await indexDataValid(summary);
const buildTermBankName = index => `term_bank_${index + 1}.json`;
const buildTermMetaBankName = index => `term_meta_bank_${index + 1}.json`;
@@ -390,7 +469,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++);
@@ -412,6 +491,10 @@ class Database {
await loadBank(summary, buildKanjiMetaBankName, kanjiMetaBankCount, kanjiMetaDataLoaded);
await loadBank(summary, buildTagBankName, tagBankCount, tagDataLoaded);
+ if (indexDataLoaded) {
+ await indexDataLoaded(summary);
+ }
+
return summary;
}
}
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..7a9ba4f9 100644
--- a/ext/bg/js/settings.js
+++ b/ext/bg/js/settings.js
@@ -22,9 +22,12 @@ 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.mainDictionary = $('#main-dictionary').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);
@@ -60,7 +63,8 @@ async function formRead() {
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};
+ const allowSecondarySearches = dictionary.find('.dict-allow-secondary-searches').prop('checked');
+ optionsNew.dictionaries[title] = {priority, enabled, allowSecondarySearches};
});
return {optionsNew, optionsOld};
@@ -81,6 +85,13 @@ function formUpdateVisibility(options) {
advanced.hide();
}
+ const merge = $('.options-merge');
+ if (options.general.resultOutputMode === 'merge') {
+ merge.show();
+ } else {
+ merge.hide();
+ }
+
const debug = $('#debug');
if (options.general.debugInfo) {
const temp = utilIsolate(options);
@@ -93,6 +104,29 @@ function formUpdateVisibility(options) {
}
}
+async function formMainDictionaryOptionsPopulate(options) {
+ const select = $('#main-dictionary').empty();
+
+ let titles = await utilDatabaseGetTitlesWithSequences();
+ titles = titles.filter(title => options.dictionaries[title].enabled);
+ const formOptionsHtml = [];
+ let mainDictionarySelected = false;
+ for (const title of titles) {
+ if (options.general.mainDictionary === title) {
+ mainDictionarySelected = true;
+ }
+ formOptionsHtml.push(`<option value="${title}"${options.general.mainDictionary === title ? ' selected' : ''}>${title}</option>`);
+ }
+
+ if (!mainDictionarySelected) {
+ options.general.mainDictionary = '';
+ }
+
+ const notSelectedOptionHtml = `<option class="text-muted" value=""${!mainDictionarySelected ? ' selected' : ''}>Not selected</option>`;
+
+ select.append($([notSelectedOptionHtml].concat(formOptionsHtml).join('')));
+}
+
async function onFormOptionsChanged(e) {
try {
if (!e.originalEvent && !e.isTrigger) {
@@ -100,6 +134,7 @@ async function onFormOptionsChanged(e) {
}
const {optionsNew, optionsOld} = await formRead();
+ await formMainDictionaryOptionsPopulate(optionsNew);
await optionsSave(optionsNew);
formUpdateVisibility(optionsNew);
@@ -124,9 +159,12 @@ 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);
+ $('#main-dictionary').val(options.general.mainDictionary);
$('#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);
@@ -166,6 +204,8 @@ async function onReady() {
ankiErrorShow(e);
}
+ await formMainDictionaryOptionsPopulate(options);
+
formUpdateVisibility(options);
}
@@ -247,13 +287,14 @@ async function dictionaryGroupsPopulate(options) {
}
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', {
title: dictRow.title,
version: dictRow.version,
revision: dictRow.revision,
priority: dictOptions.priority,
- enabled: dictOptions.enabled
+ enabled: dictOptions.enabled,
+ allowSecondarySearches: dictOptions.allowSecondarySearches
});
dictGroups.append($(dictHtml));
@@ -261,7 +302,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);
});
@@ -280,9 +321,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 +351,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.hasSequences && !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..f3f680d5 100644
--- a/ext/bg/js/templates.js
+++ b/ext/bg/js/templates.js
@@ -13,7 +13,9 @@ templates['dictionary.html'] = template({"1":function(container,depth0,helpers,p
+ 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-"
+ + "> 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(1, 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 +206,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 +229,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.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(24, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((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 +422,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..091137ed 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;
}
@@ -46,6 +83,10 @@ function utilDatabaseGetTitles() {
return utilBackend().translator.database.getTitles();
}
+function utilDatabaseGetTitlesWithSequences() {
+ return utilBackend().translator.database.getTitlesWithSequences();
+}
+
function utilDatabasePurge() {
return utilBackend().translator.database.purge();
}
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index 4315d74b..f430d5e2 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 ID (experimental)</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>
@@ -127,6 +140,11 @@
<h3>Dictionaries</h3>
</div>
+ <div class="form-group options-merge">
+ <label for="main-dictionary">Main dictionary</label>
+ <select class="form-control" id="main-dictionary"></select>
+ </div>
+
<p class="help-block">
Yomichan can import and use a variety of dictionary formats. Unneeded dictionaries can be disabled,
or you can simply <a href="#" id="dict-purge-link">purge the database</a> to delete everything.
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 302a6280..41fe85eb 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -71,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) {
@@ -183,7 +185,7 @@ class Display {
80: /* p */ () => {
if (e.altKey) {
if ($('.entry').eq(this.index).data('type') === 'term') {
- this.audioPlay(this.definitions[this.index]);
+ this.audioPlay(this.definitions[this.index], this.options.general.resultOutputMode === 'merge' ? 0 : -1);
}
return true;
@@ -234,8 +236,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
};
@@ -379,11 +383,11 @@ class Display {
}
}
- async audioPlay(definition) {
+ async audioPlay(definition, expressionIndex) {
try {
this.spinner.show();
- let url = await apiAudioGetUrl(definition, this.options.general.audioSource);
+ let url = await apiAudioGetUrl(expressionIndex === -1 ? definition : definition.expressions[expressionIndex], this.options.general.audioSource);
if (!url) {
url = '/mixed/mp3/button.mp3';
}
diff --git a/tmpl/dictionary.html b/tmpl/dictionary.html
index f2f7f687..1dc04f0f 100644
--- a/tmpl/dictionary.html
+++ b/tmpl/dictionary.html
@@ -4,6 +4,9 @@
<div class="checkbox">
<label><input type="checkbox" class="dict-enabled" {{#if enabled}}checked{{/if}}> Enable search</label>
</div>
+ <div class="checkbox options-advanced">
+ <label><input type="checkbox" class="dict-allow-secondary-searches" {{#if allowSecondarySearches}}checked{{/if}}> Allow secondary searches</label>
+ </div>
<div class="form-group options-advanced">
<label for="dict-{{title}}">Result priority</label>
<input type="number" value="{{priority}}" id="dict-{{title}}" class="form-control dict-priority">
diff --git a/tmpl/terms.html b/tmpl/terms.html
index a130e775..245a0ea1 100644
--- a/tmpl/terms.html
+++ b/tmpl/terms.html
@@ -1,19 +1,28 @@
{{#*inline "definition"}}
-{{#if tags}}
-<div>
- {{#each tags}}
+{{#if definitionTags}}
+<div {{#if compactGlossaries}}class="compact-info"{{/if}}>
+ {{#each definitionTags}}
<span class="label label-default tag-{{category}}" title="{{notes}}">{{name}}</span>
{{/each}}
</div>
{{/if}}
+{{#if only}}
+<div {{#if compactGlossaries}}class="compact-info"{{/if}}>
+ (
+ {{~#each only~}}
+ {{{.}}}{{#unless @last}}, {{/unless}}
+ {{/each}}
+ only)
+</div>
+{{/if}}
{{#if glossary.[1]}}
-<ul>
+<ul {{#if compactGlossaries}}class="compact-glossary"{{/if}}>
{{#each glossary}}
<li><span class="glossary-item">{{#multiLine}}{{.}}{{/multiLine}}</span></li>
{{/each}}
</ul>
{{else}}
-<div class="glossary-item">{{#multiLine}}{{glossary.[0]}}{{/multiLine}}</div>
+<div class="glossary-item {{#if compactGlossaries}}compact-glossary{{/if}}">{{#multiLine}}{{glossary.[0]}}{{/multiLine}}</div>
{{/if}}
{{/inline}}
@@ -25,13 +34,50 @@
<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>
<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>
{{/if}}
+ {{#unless merged}}
{{#if playback}}
<a href="#" class="action-play-audio"><img src="/mixed/img/play-audio.png" title="Play audio (Alt + P)" alt></a>
{{/if}}
+ {{/unless}}
<img src="/mixed/img/entry-current.png" class="current" title="Current entry (Alt + Up/Down/Home/End/PgUp/PgDn)" alt>
</div>
+ {{#if merged}}
+ {{~#each expressions~}}
+ <div class="expression"><!--
+ --><span class="expression-{{termFrequency}}">{{#kanjiLinks}}{{#furigana}}{{{.}}}{{/furigana}}{{/kanjiLinks}}</span><!--
+ --><div class="peek-wrapper">
+ {{~#if ../playback~}}
+ <a href="#" class="action-play-audio"><img src="/mixed/img/play-audio.png" title="Play audio" alt></a>
+ {{~/if~}}
+ {{~#if termTags~}}
+ <div class="tags">
+ {{~#each termTags}}
+ <span class="label label-default tag-{{category}}" title="{{notes}}">{{name}}</span>
+ {{/each~}}
+ </div>
+ {{~/if~}}
+ {{~#if frequencies~}}
+ <div class="frequencies">
+ {{~#each frequencies}}
+ <span class="label label-default tag-frequency">{{dictionary}}:{{frequency}}</span>
+ {{/each~}}
+ </div>
+ {{~/if~}}
+ </div><!--
+ --><span class="{{#if @last}}invisible{{/if}}">、</span><!--
+ --></div>
+ {{~/each~}}
+ {{else}}
<div class="expression">{{#kanjiLinks}}{{#furigana}}{{{.}}}{{/furigana}}{{/kanjiLinks}}</div>
+ {{#if termTags}}
+ <div style="display: inline-block;">
+ {{#each termTags}}
+ <span class="label label-default tag-{{category}}" title="{{notes}}">{{name}}</span>
+ {{/each}}
+ </div>
+ {{/if}}
+ {{/if}}
{{#if reasons}}
<div class="reasons">
@@ -54,14 +100,24 @@
{{#if definitions.[1]}}
<ol>
{{#each definitions}}
- <li>{{> definition}}</li>
+ <li>{{> definition compactGlossaries=../compactGlossaries}}</li>
+ {{/each}}
+ </ol>
+ {{else}}
+ {{> definition definitions.[0] compactGlossaries=compactGlossaries}}
+ {{/if}}
+ {{else if merged}}
+ {{#if definitions.[1]}}
+ <ol>
+ {{#each definitions}}
+ <li>{{> definition compactGlossaries=../compactGlossaries}}</li>
{{/each}}
</ol>
{{else}}
- {{> definition definitions.[0]}}
+ {{> definition definitions.[0] compactGlossaries=compactGlossaries}}
{{/if}}
{{else}}
- {{> definition}}
+ {{> definition compactGlossaries=compactGlossaries}}
{{/if}}
</div>
@@ -74,7 +130,7 @@
{{#if definitions}}
{{#each definitions}}
{{#unless @first}}<hr>{{/unless}}
-{{> term debug=../debug grouped=../grouped addable=../addable playback=../playback}}
+{{> term debug=../debug grouped=../grouped merged=../merged addable=../addable playback=../playback compactGlossaries=../compactGlossaries}}
{{/each}}
{{else}}
<p class="note">No results found.</p>