merged mode: implement missing stuff, refactoring

- use correct tags
- indicate popular and rare terms
- indicate definitions restricted to specific terms
- frequencies (Innocent Corpus)
This commit is contained in:
siikamiika 2017-10-03 07:20:02 +03:00
parent 3b664dd908
commit 69ad4a7c9b
8 changed files with 338 additions and 163 deletions

View File

@ -30,7 +30,7 @@ class Database {
this.db = new Dexie('dict'); this.db = new Dexie('dict');
this.db.version(2).stores({ this.db.version(2).stores({
terms: '++id,dictionary,expression,reading,sequence', terms: '++id,dictionary,expression,reading',
kanji: '++,dictionary,character', kanji: '++,dictionary,character',
tagMeta: '++,dictionary', tagMeta: '++,dictionary',
dictionaries: '++,title,version' dictionaries: '++,title,version'
@ -40,6 +40,9 @@ class Database {
kanjiMeta: '++,dictionary,character', kanjiMeta: '++,dictionary,character',
tagMeta: '++,dictionary,name' tagMeta: '++,dictionary,name'
}); });
this.db.version(4).stores({
terms: '++id,dictionary,expression,reading,sequence'
});
await this.db.open(); await this.db.open();
} }
@ -74,7 +77,7 @@ class Database {
score: row.score, score: row.score,
dictionary: row.dictionary, dictionary: row.dictionary,
id: row.id, id: row.id,
sequence: row.sequence sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence
}); });
} }
}); });
@ -82,14 +85,15 @@ class Database {
return results; return results;
} }
async findEntry(sequence) { async findTermsBySequence(sequence, dictionary) {
if (!this.db) { if (!this.db) {
throw 'Database not initialized'; throw 'Database not initialized';
} }
const entry = []; const results = [];
await this.db.terms.where('sequence').equals(sequence).each(row => { await this.db.terms.where('sequence').equals(sequence).each(row => {
entry.push({ // if (dictionary === row.dictionary) {
results.push({
expression: row.expression, expression: row.expression,
reading: row.reading, reading: row.reading,
tags: dictFieldSplit(row.tags), tags: dictFieldSplit(row.tags),
@ -97,11 +101,12 @@ class Database {
glossary: row.glossary, glossary: row.glossary,
score: row.score, score: row.score,
dictionary: row.dictionary, dictionary: row.dictionary,
id: row.id id: row.id,
sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence
}); });
}); });
return entry; return results;
} }
async findTermMeta(term, titles) { async findTermMeta(term, titles) {

View File

@ -144,6 +144,77 @@ function dictTermsGroup(definitions, dictionaries) {
return dictTermsSort(results); return dictTermsSort(results);
} }
function dictTermsMergeBySequence(definitions) {
const definitionsBySequence = {'-1': []};
for (const definition of definitions) {
if (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) {
const definitionsByGloss = {};
for (const definition of definitions) {
const gloss = JSON.stringify(definition.glossary);
if (!definitionsByGloss[gloss]) {
definitionsByGloss[gloss] = {
expression: new Set(),
reading: new Set(),
tags: new Set(),
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.tags) {
if (dictIsJmdictTermTag(tag)) {
// TODO: expand tags
result.expressions.get(definition.expression).get(definition.reading).add(tag);
} else {
definitionsByGloss[gloss].tags.add(tag);
}
}
}
return definitionsByGloss;
}
function dictTagBuildSource(name) { function dictTagBuildSource(name) {
return dictTagSanitize({name, category: 'dictionary', order: 100}); return dictTagSanitize({name, category: 'dictionary', order: 100});
} }
@ -178,6 +249,45 @@ function dictTagsSort(tags) {
}); });
} }
function dictIsJmdictTermTag(tag) {
return [
'P',
'news',
'ichi',
'spec',
'gai',
'ik',
'iK',
'ok',
'oK',
'ek',
'eK',
'io',
'oik',
'ateji',
'gikun'
].includes(tag);
}
function dictJmdictTermTagsRare(tags) {
const rareTags = [
'ik',
'iK',
'ok',
'oK',
'ek',
'eK',
'io',
'oik'
];
for (const tag of tags) {
if (rareTags.includes(tag)) {
return true;
}
}
return false;
}
function dictFieldSplit(field) { function dictFieldSplit(field) {
return field.length === 0 ? [] : field.split(' '); return field.length === 0 ? [] : field.split(' ');
} }

View File

@ -71,16 +71,6 @@ function handlebarsKanjiLinks(options) {
return result; return result;
} }
function handlebarsExpressions(options) {
const definition = options.fn(this);
return definition.expression;
}
function handlebarsReadings(options) {
const definition = options.fn(this);
return definition.reading;
}
function handlebarsMultiLine(options) { function handlebarsMultiLine(options) {
return options.fn(this).split('\n').join('<br>'); return options.fn(this).split('\n').join('<br>');
} }
@ -93,8 +83,6 @@ function handlebarsRegisterHelpers() {
Handlebars.registerHelper('furiganaPlain', handlebarsFuriganaPlain); Handlebars.registerHelper('furiganaPlain', handlebarsFuriganaPlain);
Handlebars.registerHelper('kanjiLinks', handlebarsKanjiLinks); Handlebars.registerHelper('kanjiLinks', handlebarsKanjiLinks);
Handlebars.registerHelper('multiLine', handlebarsMultiLine); Handlebars.registerHelper('multiLine', handlebarsMultiLine);
Handlebars.registerHelper('expressions', handlebarsExpressions);
Handlebars.registerHelper('readings', handlebarsReadings);
} }
} }

View File

@ -204,15 +204,30 @@ templates['model.html'] = template({"1":function(container,depth0,helpers,partia
templates['terms.html'] = template({"1":function(container,depth0,helpers,partials,data) { templates['terms.html'] = template({"1":function(container,depth0,helpers,partials,data) {
var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); 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 : "") return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.only : 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 : ""); + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.tags : depth0),{"name":"if","hash":{},"fn":container.program(6, 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(9, data, 0),"inverse":container.program(13, data, 0),"data":data})) != null ? stack1 : "");
},"2":function(container,depth0,helpers,partials,data) { },"2":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return "<div>\n" 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 : "") + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.only : depth0),{"name":"each","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n"; + " only)\n</div>\n";
},"3":function(container,depth0,helpers,partials,data) { },"3":function(container,depth0,helpers,partials,data) {
var 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(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n";
},"4":function(container,depth0,helpers,partials,data) {
return ", ";
},"6":function(container,depth0,helpers,partials,data) {
var stack1;
return "<div>\n"
+ ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n";
},"7":function(container,depth0,helpers,partials,data) {
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression; var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
return " <span class=\"label label-default tag-" return " <span class=\"label label-default tag-"
@ -222,112 +237,111 @@ 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))) + 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"; + "</span>\n";
},"5":function(container,depth0,helpers,partials,data) { },"9":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return "<ul>\n" 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 : "") + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</ul>\n"; + "</ul>\n";
},"6":function(container,depth0,helpers,partials,data) { },"10":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, buffer = var stack1, helper, options, buffer =
" <li><span class=\"glossary-item\">"; " <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(11, 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 (!helpers.multiLine) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; } if (stack1 != null) { buffer += stack1; }
return buffer + "</span></li>\n"; return buffer + "</span></li>\n";
},"7":function(container,depth0,helpers,partials,data) { },"11":function(container,depth0,helpers,partials,data) {
return container.escapeExpression(container.lambda(depth0, depth0)); return container.escapeExpression(container.lambda(depth0, depth0));
},"9":function(container,depth0,helpers,partials,data) { },"13":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, buffer = var stack1, helper, options, buffer =
"<div class=\"glossary-item\">"; "<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)); stack1 = ((helper = (helper = helpers.multiLine || (depth0 != null ? depth0.multiLine : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"multiLine","hash":{},"fn":container.program(14, 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 (!helpers.multiLine) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; } if (stack1 != null) { buffer += stack1; }
return buffer + "</div>\n"; return buffer + "</div>\n";
},"10":function(container,depth0,helpers,partials,data) { },"14":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["0"] : stack1), depth0)); return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["0"] : stack1), depth0));
},"12":function(container,depth0,helpers,partials,data) { },"16":function(container,depth0,helpers,partials,data) {
var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
return "<div class=\"entry\" data-type=\"term\">\n <div class=\"actions\">\n" 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(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.addable : depth0),{"name":"if","hash":{},"fn":container.program(17, 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 : "") + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.playback : depth0),{"name":"if","hash":{},"fn":container.program(19, 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" + " <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(17, data, 0),"inverse":container.program(21, data, 0),"data":data})) != null ? stack1 : "") + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.merged : depth0),{"name":"if","hash":{},"fn":container.program(21, data, 0),"inverse":container.program(28, data, 0),"data":data})) != null ? stack1 : "")
+ "\n" + "\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.reasons : 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(30, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n" + "\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.frequencies : depth0),{"name":"if","hash":{},"fn":container.program(28, 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(34, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n <div class=\"glossary\">\n" + "\n <div class=\"glossary\">\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.grouped : depth0),{"name":"if","hash":{},"fn":container.program(31, data, 0),"inverse":container.program(37, data, 0),"data":data})) != null ? stack1 : "") + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.grouped : depth0),{"name":"if","hash":{},"fn":container.program(37, data, 0),"inverse":container.program(43, data, 0),"data":data})) != null ? stack1 : "")
+ " </div>\n\n" + " </div>\n\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.debug : depth0),{"name":"if","hash":{},"fn":container.program(40, 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(46, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n"; + "</div>\n";
},"13":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) {
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) { },"17":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=helpers.blockHelperMissing, buffer = 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";
" <div class=\"expression\">";
stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : alias2),(options={"name":"kanjiLinks","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data}),(typeof helper === alias3 ? helper.call(alias1,options) : helper));
if (!helpers.kanjiLinks) { stack1 = alias4.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; }
buffer += "</div>\n <div>";
stack1 = ((helper = (helper = helpers.readings || (depth0 != null ? depth0.readings : depth0)) != null ? helper : alias2),(options={"name":"readings","hash":{},"fn":container.program(19, data, 0),"inverse":container.noop,"data":data}),(typeof helper === alias3 ? helper.call(alias1,options) : helper));
if (!helpers.readings) { stack1 = alias4.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; }
return buffer + "</div>\n";
},"18":function(container,depth0,helpers,partials,data) {
var stack1, helper, options;
stack1 = ((helper = (helper = helpers.expressions || (depth0 != null ? depth0.expressions : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"expressions","hash":{},"fn":container.program(19, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
if (!helpers.expressions) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { return stack1; }
else { return ''; }
},"19":function(container,depth0,helpers,partials,data) { },"19":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";
},"21":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : ""); return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.expressions : depth0),{"name":"each","hash":{},"fn":container.program(22, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
},"21":function(container,depth0,helpers,partials,data) { },"22":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, buffer = var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", buffer =
" <div class=\"expression\">"; " <div class=\"expression\">\n <span class=\"expression-"
stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"kanjiLinks","hash":{},"fn":container.program(22, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper)); + container.escapeExpression(((helper = (helper = helpers.jmdictTermFrequency || (depth0 != null ? depth0.jmdictTermFrequency : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"jmdictTermFrequency","hash":{},"data":data}) : helper)))
+ "\">";
stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : alias2),(options={"name":"kanjiLinks","hash":{},"fn":container.program(23, data, 0),"inverse":container.noop,"data":data}),(typeof helper === alias3 ? helper.call(alias1,options) : helper));
if (!helpers.kanjiLinks) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} if (!helpers.kanjiLinks) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; } if (stack1 != null) { buffer += stack1; }
return buffer + "</div>\n"; return buffer + "</span>\n "
},"22":function(container,depth0,helpers,partials,data) { + ((stack1 = helpers.unless.call(alias1,(data && data.last),{"name":"unless","hash":{},"fn":container.program(26, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n </div>\n";
},"23":function(container,depth0,helpers,partials,data) {
var stack1, helper, options; 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(19, 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(24, 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 (!helpers.furigana) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { return stack1; } if (stack1 != null) { return stack1; }
else { return ''; } else { return ''; }
},"24":function(container,depth0,helpers,partials,data) { },"24":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : "");
},"26":function(container,depth0,helpers,partials,data) {
return "、";
},"28":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, 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(23, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
if (!helpers.kanjiLinks) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; }
return buffer + "</div>\n";
},"30":function(container,depth0,helpers,partials,data) {
var stack1;
return " <div class=\"reasons\">\n" 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(25, 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(31, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </div>\n"; + " </div>\n";
},"25":function(container,depth0,helpers,partials,data) { },"31":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return " <span class=\"reasons\">" return " <span class=\"reasons\">"
+ container.escapeExpression(container.lambda(depth0, depth0)) + container.escapeExpression(container.lambda(depth0, depth0))
+ "</span> " + "</span> "
+ ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.last),{"name":"unless","hash":{},"fn":container.program(26, 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(32, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n"; + "\n";
},"26":function(container,depth0,helpers,partials,data) { },"32":function(container,depth0,helpers,partials,data) {
return "&laquo;"; return "&laquo;";
},"28":function(container,depth0,helpers,partials,data) { },"34":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return " <div>\n" return " <div>\n"
+ ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.frequencies : 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.frequencies : depth0),{"name":"each","hash":{},"fn":container.program(35, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </div>\n"; + " </div>\n";
},"29":function(container,depth0,helpers,partials,data) { },"35":function(container,depth0,helpers,partials,data) {
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression; var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
return " <span class=\"label label-default tag-frequency\">" return " <span class=\"label label-default tag-frequency\">"
@ -335,67 +349,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))) + 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"; + "</span>\n";
},"31":function(container,depth0,helpers,partials,data) { },"37":function(container,depth0,helpers,partials,data) {
var stack1; 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(32, data, 0),"inverse":container.program(35, data, 0),"data":data})) != null ? 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(38, data, 0),"inverse":container.program(41, data, 0),"data":data})) != null ? stack1 : "");
},"32":function(container,depth0,helpers,partials,data) { },"38":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return " <ol>\n" return " <ol>\n"
+ ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(33, 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(39, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </ol>\n"; + " </ol>\n";
},"33":function(container,depth0,helpers,partials,data) { },"39":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return " <li>" 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","data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "")
+ "</li>\n"; + "</li>\n";
},"35":function(container,depth0,helpers,partials,data) { },"41":function(container,depth0,helpers,partials,data) {
var stack1; 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 : ""); 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 : "");
},"37":function(container,depth0,helpers,partials,data) { },"43":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.merged : depth0),{"name":"if","hash":{},"fn":container.program(31, data, 0),"inverse":container.program(38, data, 0),"data":data})) != null ? stack1 : ""); return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.merged : depth0),{"name":"if","hash":{},"fn":container.program(37, data, 0),"inverse":container.program(44, data, 0),"data":data})) != null ? stack1 : "");
},"38":function(container,depth0,helpers,partials,data) { },"44":function(container,depth0,helpers,partials,data) {
var stack1; var stack1;
return ((stack1 = container.invokePartial(partials.definition,depth0,{"name":"definition","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") return ((stack1 = container.invokePartial(partials.definition,depth0,{"name":"definition","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "")
+ " "; + " ";
},"40":function(container,depth0,helpers,partials,data) { },"46":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, buffer = var stack1, helper, options, buffer =
" <pre>"; " <pre>";
stack1 = ((helper = (helper = helpers.dumpObject || (depth0 != null ? depth0.dumpObject : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"dumpObject","hash":{},"fn":container.program(19, 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(24, 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 (!helpers.dumpObject) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; } if (stack1 != null) { buffer += stack1; }
return buffer + "</pre>\n"; return buffer + "</pre>\n";
},"42":function(container,depth0,helpers,partials,data,blockParams,depths) { },"48":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1; var stack1;
return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(43, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(49, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
},"43":function(container,depth0,helpers,partials,data,blockParams,depths) { },"49":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1; var stack1;
return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.first),{"name":"unless","hash":{},"fn":container.program(44, 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(50, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n" + "\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]),"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 : ""); + ((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]),"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 : "");
},"44":function(container,depth0,helpers,partials,data) { },"50":function(container,depth0,helpers,partials,data) {
return "<hr>"; return "<hr>";
},"46":function(container,depth0,helpers,partials,data) { },"52":function(container,depth0,helpers,partials,data) {
return "<p class=\"note\">No results found.</p>\n"; return "<p class=\"note\">No results found.</p>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) { },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1; var stack1;
return "\n\n" return "\n\n"
+ ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(42, data, 0, blockParams, depths),"inverse":container.program(46, 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(48, data, 0, blockParams, depths),"inverse":container.program(52, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
},"main_d": function(fn, props, container, depth0, data, blockParams, depths) { },"main_d": function(fn, props, container, depth0, data, blockParams, depths) {
var decorators = container.decorators; 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(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(16, data, 0, blockParams, depths),"inverse":container.noop,"args":["term"],"data":data}) || fn;
return fn; return fn;
} }

View File

@ -49,77 +49,79 @@ class Translator {
} }
async findTermsMerged(text, dictionaries, alphanumeric) { async findTermsMerged(text, dictionaries, alphanumeric) {
// const titles = Object.keys(dictionaries); const titles = Object.keys(dictionaries);
const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric); const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric);
// const definitionsMerged = dictTermsMerge(definitions, dictionaries, this.database); const definitionsBySequence = dictTermsMergeBySequence(definitions);
// for (const definition of definitionsMerged) {
// await this.buildTermFrequencies(definition, titles);
// }
const sequences = {}; const definitionsMerged = dictTermsGroup(definitionsBySequence['-1'], dictionaries);
const stray = []; for (const sequence in definitionsBySequence) {
for (const definition of definitions) { if (!(sequence > 0)) {
if (typeof definition.sequence !== 'undefined') { continue;
if (!sequences[definition.sequence]) {
sequences[definition.sequence] = {
reasons: definition.reasons,
score: Number.MIN_SAFE_INTEGER,
expression: new Set(),
reading: new Set(),
source: definition.source,
definitions: []
};
}
const seq = sequences[definition.sequence];
seq.score = Math.max(seq.score, definition.score);
} else {
stray.push(definition);
}
} }
const definitionsMerged = dictTermsGroup(stray, dictionaries); const result = definitionsBySequence[sequence];
for (const sequence in sequences) {
const entry = await this.database.findEntry(Number(sequence));
const result = sequences[sequence]; const rawDefinitionsBySequence = await this.database.findTermsBySequence(Number(sequence));
const glossaries = new Map(); const definitionsByGloss = dictTermsMergeByGloss(result, rawDefinitionsBySequence);
for (const definition of entry) {
// postprocess glossaries
for (const gloss in definitionsByGloss) {
const definition = definitionsByGloss[gloss];
definition.glossary = JSON.parse(gloss);
const gloss = definition.glossary.join('||');
if (!glossaries.get(gloss)) {
const tags = await this.expandTags(definition.tags, definition.dictionary); const tags = await this.expandTags(definition.tags, definition.dictionary);
tags.push(dictTagBuildSource(definition.dictionary)); tags.push(dictTagBuildSource(definition.dictionary));
glossaries.set(gloss, { definition.tags = dictTagsSort(tags);
expressions: new Set(),
readings: new Set(),
tags: dictTagsSort(tags), // TODO: use correct tags
source: result.source,
reasons: [],
score: definition.score,
id: definition.id,
dictionary: definition.dictionary
});
}
glossaries.get(gloss).expressions.add(definition.expression);
glossaries.get(gloss).readings.add(definition.reading);
result.expression.add(definition.expression); definition.only = [];
result.reading.add(definition.reading); 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);
}
} }
for (const gloss of glossaries.keys()) {
const definition = glossaries.get(gloss);
definition.glossary = gloss.split('||');
result.definitions.push(definition); result.definitions.push(definition);
} }
//dictTermsSort(groupDefs, dictionaries)
result.definitions.sort(definition => -definition.id);
// turn the Map()/Set() mess to [{expression: E1, reading: R1}, {...}] and tag popular/normal/rare instead of actual tags
const expressions = [];
for (const expression of result.expressions.keys()) {
for (const reading of result.expressions.get(expression).keys()) {
expressions.push({
expression: expression,
reading: reading,
jmdictTermFrequency: (tags => {
if (tags.has('P')) {
return 'popular';
} else if (dictJmdictTermTagsRare(tags)) {
return 'rare';
} else {
return 'normal';
}
})(result.expressions.get(expression).get(reading))
});
}
}
result.expressions = expressions;
result.expression = Array.from(result.expression).join(', '); result.expression = Array.from(result.expression).join(', ');
result.reading = Array.from(result.reading).join(', '); result.reading = Array.from(result.reading).join(', ');
definitionsMerged.push(result); definitionsMerged.push(result);
} }
for (const definition of definitionsMerged) {
await this.buildTermFrequencies(definition, titles);
}
return {length, definitions: dictTermsSort(definitionsMerged)}; return {length, definitions: dictTermsSort(definitionsMerged)};
} }
@ -234,10 +236,18 @@ class Translator {
} }
async buildTermFrequencies(definition, titles) { async buildTermFrequencies(definition, titles) {
definition.frequencies = []; let terms = [];
for (const meta of await this.database.findTermMeta(definition.expression, titles)) { 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') { if (meta.mode === 'freq') {
definition.frequencies.push({ term.frequencies.push({
expression: meta.expression, expression: meta.expression,
frequency: meta.data, frequency: meta.data,
dictionary: meta.dictionary dictionary: meta.dictionary
@ -245,6 +255,7 @@ class Translator {
} }
} }
} }
}
async expandTags(names, title) { async expandTags(names, title) {
const tags = []; const tags = [];

View File

@ -26,6 +26,32 @@ function utilIsolate(data) {
return JSON.parse(JSON.stringify(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 utilBackend() { function utilBackend() {
return chrome.extension.getBackgroundPage().yomichan_backend; return chrome.extension.getBackgroundPage().yomichan_backend;
} }

View File

@ -124,6 +124,14 @@ hr {
text-decoration: none; text-decoration: none;
} }
.expression-popular, .expression-popular a {
color: #0275d8;
}
.expression-rare, .expression-rare a {
color: #999;
}
.reasons { .reasons {
color: #777; color: #777;
display: inline-block; display: inline-block;

View File

@ -1,4 +1,13 @@
{{#*inline "definition"}} {{#*inline "definition"}}
{{#if only}}
<div>
(
{{~#each only~}}
{{{.}}}{{#unless @last}}, {{/unless}}
{{/each}}
only)
</div>
{{/if}}
{{#if tags}} {{#if tags}}
<div> <div>
{{#each tags}} {{#each tags}}
@ -32,8 +41,12 @@
</div> </div>
{{#if merged}} {{#if merged}}
<div class="expression">{{#kanjiLinks}}{{#expressions}}{{{.}}}{{/expressions}}{{/kanjiLinks}}</div> {{#each expressions}}
<div>{{#readings}}{{{.}}}{{/readings}}</div> <div class="expression">
<span class="expression-{{jmdictTermFrequency}}">{{#kanjiLinks}}{{#furigana}}{{{.}}}{{/furigana}}{{/kanjiLinks}}</span>
{{#unless @last}}、{{/unless}}
</div>
{{/each}}
{{else}} {{else}}
<div class="expression">{{#kanjiLinks}}{{#furigana}}{{{.}}}{{/furigana}}{{/kanjiLinks}}</div> <div class="expression">{{#kanjiLinks}}{{#furigana}}{{{.}}}{{/furigana}}{{/kanjiLinks}}</div>
{{/if}} {{/if}}