Merge branch 'dev'
This commit is contained in:
commit
d505edb94b
34
README.md
34
README.md
@ -258,14 +258,34 @@ versions packaged.
|
||||
|
||||
## Frequently Asked Questions ##
|
||||
|
||||
* **Why does the Kanji results page display "No data found" for several fields?**
|
||||
**I'm having problems importing dictionaries in Firefox, what do I do?**
|
||||
|
||||
Yomichan uses the cross-browser IndexedDB system for storing imported dictionary data into your user profile. Although
|
||||
everything "just works" in Chrome, depending on settings, Firefox users can run into problems due browser bugs.
|
||||
Yomichan catches errors and tries to offer suggestions about how to work around Firefox issues, but in general at least
|
||||
one of the following solutions should work for you:
|
||||
|
||||
* Make sure you have cookies enabled. It appears that disabling them also disables IndexedDB for some reason. You
|
||||
can still have cookies be disabled on other sites; just make sure to add the Yomichan extension to the whitelist of
|
||||
whatever tool you are using to restrict cookies. You can get the extension "URL" by looking at the address bar when
|
||||
you have the search page open.
|
||||
* Make sure that you have sufficient disk space available on the drive Firefox uses to store your user profile.
|
||||
Firefox limits the amount of space that can be used by IndexedDB to a small fraction of the disk space actually
|
||||
available on your computer.
|
||||
* Make sure that you have history set to "Remember history" enabled in your privacy settings. When this option is
|
||||
set to "Never remember history", IndexedDB access is once again disabled for an inexplicable reason.
|
||||
* As a last resort, try using the [Refresh Firefox](https://support.mozilla.org/en-US/kb/reset-preferences-fix-problems)
|
||||
feature to reset your user profile. It appears that the Firefox profile system can corrupt itself preventing
|
||||
IndexedDB from being accessible to Yomichan.
|
||||
|
||||
**Why does the Kanji results page display "No data found" for several fields?**
|
||||
|
||||
You are using data from the KANJIDIC dictionary that was exported for an earlier version of Yomichan. It does not
|
||||
contain the additional information which newer versions of Yofomichan expect. Unfortunately, since major browser
|
||||
implementations of IndexedDB do not provide reliable means for selective bulk data deletion, you will need purge
|
||||
your database and install the latest version of the KANJIDIC to see additional information about characters.
|
||||
|
||||
* **Can I still create cards without HTML formatting? The option for it is gone!**
|
||||
**Can I still create cards without HTML formatting? The option for it is gone!**
|
||||
|
||||
Developing Yomichan is a constant balance between including useful features and keeping complexity at a minimum.
|
||||
With the new user-editable card template system, it is possible to create text-only cards without having to double
|
||||
@ -274,32 +294,32 @@ versions packaged.
|
||||
the Anki settings page (make sure you have the *Show advanced options* checkbox ticked), making sure to replace the
|
||||
existing values.
|
||||
|
||||
* **Will you add support for online dictionaries?**
|
||||
**Will you add support for online dictionaries?**
|
||||
|
||||
Online dictionaries will never be implemented because it is impossible to support them in a robust way. In order to
|
||||
perform Japanese deinflection, Yomichan must execute dozens of database queries per every single word. Factoring in
|
||||
network latency and the fragility of web scraping, I do not believe that it is possible to realize a good user
|
||||
experience.
|
||||
|
||||
* **Is it possible to use Yomichan with files saved locally on my computer with Chrome?**
|
||||
**Is it possible to use Yomichan with files saved locally on my computer with Chrome?**
|
||||
|
||||
In order to use Yomichan with local files in Chrome, you must first tick the *Allow access to file URLs* checkbox
|
||||
for Yomichan on the extensions page. Due to the restrictions placed on browser addons in the WebExtensions model, it
|
||||
will likely never be possible to use Yomichan with PDF files.
|
||||
|
||||
* **Is it possible to delete individual dictionaries without purging the database?**
|
||||
**Is it possible to delete individual dictionaries without purging the database?**
|
||||
|
||||
Although it is technically possible to purge specific dictionaries, due to the limitations of the underlying browser
|
||||
IndexedDB system, this process is *extremely* slow. For example, it can take up to ten minutes to delete a single
|
||||
moderately-sized term dictionary! Instead of including a borderline unusable feature in Yomichan, I have chosen to
|
||||
disable dictionary deletion entirely.
|
||||
|
||||
* **Why aren't EPWING dictionaries bundled with Yomichan?**
|
||||
**Why aren't EPWING dictionaries bundled with Yomichan?**
|
||||
|
||||
The vast majority of EPWING dictionaries are proprietary, so unfortunately I am unable to legally include them in
|
||||
this extension for copyright reasons.
|
||||
|
||||
* **When are you going to add support for $MYLANGUAGE?**
|
||||
**When are you going to add support for $MYLANGUAGE?**
|
||||
|
||||
Developing Yomichan requires a significant understanding of Japanese sentence structure and grammar. I have no time
|
||||
to invest in learning yet another language; therefore other languages will not be supported. I will also not accept
|
||||
|
@ -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,
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
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,
|
||||
sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -163,7 +220,7 @@ class Database {
|
||||
return result;
|
||||
}
|
||||
|
||||
async getTitles() {
|
||||
async summarize() {
|
||||
if (this.db) {
|
||||
return this.db.dictionaries.toArray();
|
||||
} else {
|
||||
@ -177,7 +234,7 @@ class Database {
|
||||
}
|
||||
|
||||
const indexDataLoaded = async summary => {
|
||||
if (summary.version > 2) {
|
||||
if (summary.version > 3) {
|
||||
throw 'Unsupported dictionary version';
|
||||
}
|
||||
|
||||
@ -196,11 +253,11 @@ class Database {
|
||||
|
||||
const rows = [];
|
||||
if (summary.version === 1) {
|
||||
for (const [expression, reading, tags, rules, score, ...glossary] of entries) {
|
||||
for (const [expression, reading, definitionTags, rules, score, ...glossary] of entries) {
|
||||
rows.push({
|
||||
expression,
|
||||
reading,
|
||||
tags,
|
||||
definitionTags,
|
||||
rules,
|
||||
score,
|
||||
glossary,
|
||||
@ -208,14 +265,16 @@ class Database {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const [expression, reading, tags, rules, score, glossary] of entries) {
|
||||
for (const [expression, reading, definitionTags, rules, score, glossary, sequence, termTags] of entries) {
|
||||
rows.push({
|
||||
expression,
|
||||
reading,
|
||||
tags,
|
||||
definitionTags,
|
||||
rules,
|
||||
score,
|
||||
glossary,
|
||||
sequence,
|
||||
termTags,
|
||||
dictionary: summary.title
|
||||
});
|
||||
}
|
||||
@ -300,12 +359,13 @@ class Database {
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
for (const [name, category, order, notes] of entries) {
|
||||
for (const [name, category, order, notes, score] of entries) {
|
||||
const row = dictTagSanitize({
|
||||
name,
|
||||
category,
|
||||
order,
|
||||
notes,
|
||||
score,
|
||||
dictionary: summary.title
|
||||
});
|
||||
|
||||
@ -350,12 +410,11 @@ class Database {
|
||||
const summary = {
|
||||
title: index.title,
|
||||
revision: index.revision,
|
||||
sequenced: index.sequenced,
|
||||
version: index.format || index.version
|
||||
};
|
||||
|
||||
if (indexDataLoaded) {
|
||||
await indexDataLoaded(summary);
|
||||
}
|
||||
|
||||
const buildTermBankName = index => `term_bank_${index + 1}.json`;
|
||||
const buildTermMetaBankName = index => `term_meta_bank_${index + 1}.json`;
|
||||
@ -390,7 +449,7 @@ class Database {
|
||||
const bank = [];
|
||||
for (const name in index.tagMeta) {
|
||||
const tag = index.tagMeta[name];
|
||||
bank.push([name, tag.category, tag.order, tag.notes]);
|
||||
bank.push([name, tag.category, tag.order, tag.notes, tag.score]);
|
||||
}
|
||||
|
||||
tagDataLoaded(summary, bank, ++bankTotalCount, bankLoadedCount++);
|
||||
|
@ -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);
|
||||
|
@ -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]~}}
|
||||
{{~#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,6 +55,24 @@ function optionsFieldTemplates() {
|
||||
{{/inline}}
|
||||
|
||||
{{#*inline "expression"}}
|
||||
{{~#if merge~}}
|
||||
{{~#if modeTermKana~}}
|
||||
{{~#each definition.reading~}}
|
||||
{{{.}}}
|
||||
{{~#unless @last}}、{{/unless~}}
|
||||
{{~else~}}
|
||||
{{~#each definition.expression~}}
|
||||
{{{.}}}
|
||||
{{~#unless @last}}、{{/unless~}}
|
||||
{{~/each~}}
|
||||
{{~/each~}}
|
||||
{{~else~}}
|
||||
{{~#each definition.expression~}}
|
||||
{{{.}}}
|
||||
{{~#unless @last}}、{{/unless~}}
|
||||
{{~/each~}}
|
||||
{{~/if~}}
|
||||
{{~else~}}
|
||||
{{~#if modeTermKana~}}
|
||||
{{~#if definition.reading~}}
|
||||
{{definition.reading}}
|
||||
@ -50,14 +82,29 @@ function optionsFieldTemplates() {
|
||||
{{~else~}}
|
||||
{{definition.expression}}
|
||||
{{~/if~}}
|
||||
{{~/if~}}
|
||||
{{/inline}}
|
||||
|
||||
{{#*inline "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"}}
|
||||
{{~#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~}}
|
||||
{{~> 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 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();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -22,9 +22,11 @@ async function formRead() {
|
||||
const optionsNew = $.extend(true, {}, optionsOld);
|
||||
|
||||
optionsNew.general.showGuide = $('#show-usage-guide').prop('checked');
|
||||
optionsNew.general.compactTags = $('#compact-tags').prop('checked');
|
||||
optionsNew.general.compactGlossaries = $('#compact-glossaries').prop('checked');
|
||||
optionsNew.general.resultOutputMode = $('#result-output-mode').val();
|
||||
optionsNew.general.audioSource = $('#audio-playback-source').val();
|
||||
optionsNew.general.audioVolume = parseFloat($('#audio-playback-volume').val());
|
||||
optionsNew.general.groupResults = $('#group-terms-results').prop('checked');
|
||||
optionsNew.general.debugInfo = $('#show-debug-info').prop('checked');
|
||||
optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked');
|
||||
optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10);
|
||||
@ -55,12 +57,14 @@ async function formRead() {
|
||||
optionsNew.anki.kanji.fields = ankiFieldsToDict($('#kanji .anki-field-value'));
|
||||
}
|
||||
|
||||
optionsNew.general.mainDictionary = $('#dict-main').val();
|
||||
$('.dict-group').each((index, element) => {
|
||||
const dictionary = $(element);
|
||||
const title = dictionary.data('title');
|
||||
const priority = parseInt(dictionary.find('.dict-priority').val(), 10);
|
||||
const enabled = dictionary.find('.dict-enabled').prop('checked');
|
||||
optionsNew.dictionaries[title] = {priority, enabled};
|
||||
optionsNew.dictionaries[dictionary.data('title')] = {
|
||||
priority: parseInt(dictionary.find('.dict-priority').val(), 10),
|
||||
enabled: dictionary.find('.dict-enabled').prop('checked'),
|
||||
allowSecondarySearches: dictionary.find('.dict-allow-secondary-searches').prop('checked')
|
||||
};
|
||||
});
|
||||
|
||||
return {optionsNew, optionsOld};
|
||||
@ -81,6 +85,13 @@ function formUpdateVisibility(options) {
|
||||
advanced.hide();
|
||||
}
|
||||
|
||||
const mainGroup = $('#dict-main-group');
|
||||
if (options.general.resultOutputMode === 'merge') {
|
||||
mainGroup.show();
|
||||
} else {
|
||||
mainGroup.hide();
|
||||
}
|
||||
|
||||
const debug = $('#debug');
|
||||
if (options.general.debugInfo) {
|
||||
const temp = utilIsolate(options);
|
||||
@ -93,17 +104,33 @@ function formUpdateVisibility(options) {
|
||||
}
|
||||
}
|
||||
|
||||
async function formMainDictionaryOptionsPopulate(options) {
|
||||
const select = $('#dict-main').empty();
|
||||
select.append($('<option class="text-muted" value="">Not selected</option>'));
|
||||
|
||||
let mainDictionary = '';
|
||||
for (const dictRow of await utilDatabaseSummarize()) {
|
||||
if (dictRow.sequenced) {
|
||||
select.append($(`<option value="${dictRow.title}">${dictRow.title}</option>`));
|
||||
if (dictRow.title === options.general.mainDictionary) {
|
||||
mainDictionary = dictRow.title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select.val(mainDictionary);
|
||||
}
|
||||
|
||||
async function onFormOptionsChanged(e) {
|
||||
try {
|
||||
if (!e.originalEvent && !e.isTrigger) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {optionsNew, optionsOld} = await formRead();
|
||||
await optionsSave(optionsNew);
|
||||
|
||||
formUpdateVisibility(optionsNew);
|
||||
|
||||
try {
|
||||
const ankiUpdated =
|
||||
optionsNew.anki.enable !== optionsOld.anki.enable ||
|
||||
optionsNew.anki.server !== optionsOld.anki.server;
|
||||
@ -124,9 +151,11 @@ async function onReady() {
|
||||
const options = await optionsLoad();
|
||||
|
||||
$('#show-usage-guide').prop('checked', options.general.showGuide);
|
||||
$('#compact-tags').prop('checked', options.general.compactTags);
|
||||
$('#compact-glossaries').prop('checked', options.general.compactGlossaries);
|
||||
$('#result-output-mode').val(options.general.resultOutputMode);
|
||||
$('#audio-playback-source').val(options.general.audioSource);
|
||||
$('#audio-playback-volume').val(options.general.audioVolume);
|
||||
$('#group-terms-results').prop('checked', options.general.groupResults);
|
||||
$('#show-debug-info').prop('checked', options.general.debugInfo);
|
||||
$('#show-advanced-options').prop('checked', options.general.showAdvanced);
|
||||
$('#max-displayed-results').val(options.general.maxResults);
|
||||
@ -156,6 +185,7 @@ async function onReady() {
|
||||
|
||||
try {
|
||||
await dictionaryGroupsPopulate(options);
|
||||
await formMainDictionaryOptionsPopulate(options);
|
||||
} catch (e) {
|
||||
dictionaryErrorShow(e);
|
||||
}
|
||||
@ -241,19 +271,26 @@ async function dictionaryGroupsPopulate(options) {
|
||||
const dictGroups = $('#dict-groups').empty();
|
||||
const dictWarning = $('#dict-warning').hide();
|
||||
|
||||
const dictRows = await utilDatabaseGetTitles();
|
||||
const dictRows = await utilDatabaseSummarize();
|
||||
if (dictRows.length === 0) {
|
||||
dictWarning.show();
|
||||
}
|
||||
|
||||
for (const dictRow of dictRowsSort(dictRows, options)) {
|
||||
const dictOptions = options.dictionaries[dictRow.title] || {enabled: false, priority: 0};
|
||||
const dictOptions = options.dictionaries[dictRow.title] || {
|
||||
enabled: false,
|
||||
priority: 0,
|
||||
allowSecondarySearches: false
|
||||
};
|
||||
|
||||
const dictHtml = await apiTemplateRender('dictionary.html', {
|
||||
enabled: dictOptions.enabled,
|
||||
priority: dictOptions.priority,
|
||||
allowSecondarySearches: dictOptions.allowSecondarySearches,
|
||||
title: dictRow.title,
|
||||
version: dictRow.version,
|
||||
revision: dictRow.revision,
|
||||
priority: dictOptions.priority,
|
||||
enabled: dictOptions.enabled
|
||||
outdated: dictRow.version < 3
|
||||
});
|
||||
|
||||
dictGroups.append($(dictHtml));
|
||||
@ -261,7 +298,7 @@ async function dictionaryGroupsPopulate(options) {
|
||||
|
||||
formUpdateVisibility(options);
|
||||
|
||||
$('.dict-enabled, .dict-priority').change(e => {
|
||||
$('.dict-enabled, .dict-priority, .dict-allow-secondary-searches').change(e => {
|
||||
dictionaryGroupsSort();
|
||||
onFormOptionsChanged(e);
|
||||
});
|
||||
@ -270,7 +307,7 @@ async function dictionaryGroupsPopulate(options) {
|
||||
async function onDictionaryPurge(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const dictControls = $('#dict-importer, #dict-groups').hide();
|
||||
const dictControls = $('#dict-importer, #dict-groups, #dict-main-group').hide();
|
||||
const dictProgress = $('#dict-purge').show();
|
||||
|
||||
try {
|
||||
@ -280,9 +317,11 @@ async function onDictionaryPurge(e) {
|
||||
await utilDatabasePurge();
|
||||
const options = await optionsLoad();
|
||||
options.dictionaries = {};
|
||||
options.general.mainDictionary = '';
|
||||
await optionsSave(options);
|
||||
|
||||
await dictionaryGroupsPopulate(options);
|
||||
await formMainDictionaryOptionsPopulate(options);
|
||||
} catch (e) {
|
||||
dictionaryErrorShow(e);
|
||||
} finally {
|
||||
@ -308,10 +347,14 @@ async function onDictionaryImport(e) {
|
||||
|
||||
const options = await optionsLoad();
|
||||
const summary = await utilDatabaseImport(e.target.files[0], updateProgress);
|
||||
options.dictionaries[summary.title] = {enabled: true, priority: 0};
|
||||
options.dictionaries[summary.title] = {enabled: true, priority: 0, allowSecondarySearches: false};
|
||||
if (summary.sequenced && options.general.mainDictionary === '') {
|
||||
options.general.mainDictionary = summary.title;
|
||||
}
|
||||
await optionsSave(options);
|
||||
|
||||
await dictionaryGroupsPopulate(options);
|
||||
await formMainDictionaryOptionsPopulate(options);
|
||||
} catch (e) {
|
||||
dictionaryErrorShow(e);
|
||||
} finally {
|
||||
|
@ -1,6 +1,8 @@
|
||||
(function() {
|
||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
templates['dictionary.html'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
return " <p class=\"text-warning\">This dictionary is outdated and may not support new extension features; please import the latest version.</p>\n";
|
||||
},"3":function(container,depth0,helpers,partials,data) {
|
||||
return "checked";
|
||||
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
|
||||
@ -11,9 +13,13 @@ templates['dictionary.html'] = template({"1":function(container,depth0,helpers,p
|
||||
+ alias4(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"title","hash":{},"data":data}) : helper)))
|
||||
+ " <small>rev."
|
||||
+ alias4(((helper = (helper = helpers.revision || (depth0 != null ? depth0.revision : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"revision","hash":{},"data":data}) : helper)))
|
||||
+ "</small></h4>\n\n <div class=\"checkbox\">\n <label><input type=\"checkbox\" class=\"dict-enabled\" "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.enabled : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "> Enable search</label>\n </div>\n <div class=\"form-group options-advanced\">\n <label for=\"dict-"
|
||||
+ "</small></h4>\n"
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.outdated : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "\n <div class=\"checkbox\">\n <label><input type=\"checkbox\" class=\"dict-enabled\" "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.enabled : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "> Enable search</label>\n </div>\n <div class=\"checkbox options-advanced\">\n <label><input type=\"checkbox\" class=\"dict-allow-secondary-searches\" "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.allowSecondarySearches : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "> Allow secondary searches</label>\n </div>\n <div class=\"form-group options-advanced\">\n <label for=\"dict-"
|
||||
+ alias4(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"title","hash":{},"data":data}) : helper)))
|
||||
+ "\">Result priority</label>\n <input type=\"number\" value=\""
|
||||
+ alias4(((helper = (helper = helpers.priority || (depth0 != null ? depth0.priority : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"priority","hash":{},"data":data}) : helper)))
|
||||
@ -204,15 +210,20 @@ templates['model.html'] = template({"1":function(container,depth0,helpers,partia
|
||||
templates['terms.html'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
|
||||
|
||||
return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.tags : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.program(9, data, 0),"data":data})) != null ? stack1 : "");
|
||||
return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.definitionTags : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.only : depth0),{"name":"if","hash":{},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(11, data, 0),"inverse":container.program(17, data, 0),"data":data})) != null ? stack1 : "");
|
||||
},"2":function(container,depth0,helpers,partials,data) {
|
||||
var stack1;
|
||||
var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
|
||||
|
||||
return "<div>\n"
|
||||
+ ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
return "<div "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.compactGlossaries : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ">\n"
|
||||
+ ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.definitionTags : depth0),{"name":"each","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "</div>\n";
|
||||
},"3":function(container,depth0,helpers,partials,data) {
|
||||
return "class=\"compact-info\"";
|
||||
},"5":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
|
||||
|
||||
return " <span class=\"label label-default tag-"
|
||||
@ -222,88 +233,136 @@ 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) {
|
||||
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 : "")
|
||||
+ "\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 : "")
|
||||
+ "\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 : "")
|
||||
+ " </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 : "")
|
||||
+ "</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) {
|
||||
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));
|
||||
if (!helpers.furigana) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
|
||||
if (stack1 != null) { return stack1; }
|
||||
else { return ''; }
|
||||
},"18":function(container,depth0,helpers,partials,data) {
|
||||
var stack1;
|
||||
|
||||
return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : "");
|
||||
return "compact-glossary";
|
||||
},"20":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 : "")
|
||||
+ " </div>\n";
|
||||
},"21":function(container,depth0,helpers,partials,data) {
|
||||
var stack1;
|
||||
return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["0"] : stack1), depth0));
|
||||
},"22":function(container,depth0,helpers,partials,data,blockParams,depths) {
|
||||
var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
|
||||
|
||||
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 : "")
|
||||
+ "\n";
|
||||
},"22":function(container,depth0,helpers,partials,data) {
|
||||
return "«";
|
||||
},"24":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 : "")
|
||||
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(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(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(63, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "</div>\n";
|
||||
},"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";
|
||||
},"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";
|
||||
},"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(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 ''; }
|
||||
},"31":function(container,depth0,helpers,partials,data) {
|
||||
var stack1;
|
||||
|
||||
return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : "");
|
||||
},"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\">"
|
||||
@ -311,62 +370,123 @@ 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) {
|
||||
},"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 ((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 " <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(48, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " </div>\n";
|
||||
},"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(49, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "\n";
|
||||
},"49":function(container,depth0,helpers,partials,data) {
|
||||
return "«";
|
||||
},"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(52, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " </div>\n";
|
||||
},"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\">"
|
||||
+ 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";
|
||||
},"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(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;
|
||||
}
|
||||
|
||||
|
@ -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,10 +271,18 @@ class Translator {
|
||||
}
|
||||
|
||||
async buildTermFrequencies(definition, titles) {
|
||||
definition.frequencies = [];
|
||||
for (const meta of await this.database.findTermMeta(definition.expression, titles)) {
|
||||
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') {
|
||||
definition.frequencies.push({
|
||||
term.frequencies.push({
|
||||
expression: meta.expression,
|
||||
frequency: meta.data,
|
||||
dictionary: meta.dictionary
|
||||
@ -169,6 +290,7 @@ class Translator {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async expandTags(names, title) {
|
||||
const tags = [];
|
||||
|
@ -26,6 +26,43 @@ function utilIsolate(data) {
|
||||
return JSON.parse(JSON.stringify(data));
|
||||
}
|
||||
|
||||
function utilSetEqual(setA, setB) {
|
||||
if (setA.size !== setB.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const value of setA) {
|
||||
if (!setB.has(value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function utilSetIntersection(setA, setB) {
|
||||
return new Set(
|
||||
[...setA].filter(value => setB.has(value))
|
||||
);
|
||||
}
|
||||
|
||||
function utilSetDifference(setA, setB) {
|
||||
return new Set(
|
||||
[...setA].filter(value => !setB.has(value))
|
||||
);
|
||||
}
|
||||
|
||||
function utilStringHashCode(string) {
|
||||
let hashCode = 0;
|
||||
|
||||
for (let i = 0, charCode = string.charCodeAt(i); i < string.length; charCode = string.charCodeAt(++i)) {
|
||||
hashCode = ((hashCode << 5) - hashCode) + charCode;
|
||||
hashCode |= 0;
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
function utilBackend() {
|
||||
return chrome.extension.getBackgroundPage().yomichan_backend;
|
||||
}
|
||||
@ -38,12 +75,12 @@ function utilAnkiGetDeckNames() {
|
||||
return utilBackend().anki.getDeckNames();
|
||||
}
|
||||
|
||||
function utilAnkiGetModelFieldNames(modelName) {
|
||||
return utilBackend().anki.getModelFieldNames(modelName);
|
||||
function utilDatabaseSummarize() {
|
||||
return utilBackend().translator.database.summarize();
|
||||
}
|
||||
|
||||
function utilDatabaseGetTitles() {
|
||||
return utilBackend().translator.database.getTitles();
|
||||
function utilAnkiGetModelFieldNames(modelName) {
|
||||
return utilBackend().anki.getModelFieldNames(modelName);
|
||||
}
|
||||
|
||||
function utilDatabasePurge() {
|
||||
|
@ -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">
|
||||
@ -47,6 +51,15 @@
|
||||
<label><input type="checkbox" id="show-debug-info"> Show debug information</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="result-output-mode">Result grouping</label>
|
||||
<select class="form-control" id="result-output-mode">
|
||||
<option value="group">Group results by term-reading pairs</option>
|
||||
<option value="merge">Group results by main dictionary entry</option>
|
||||
<option value="split">Split definitions to their own results</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="audio-playback-source">Audio playback source</label>
|
||||
<select class="form-control" id="audio-playback-source">
|
||||
@ -132,10 +145,14 @@
|
||||
or you can simply <a href="#" id="dict-purge-link">purge the database</a> to delete everything.
|
||||
</p>
|
||||
<p class="help-block">
|
||||
Please visit the <a href="https://foosoft.net/projects/yomichan" target="_blank">Yomichan</a> homepage to download free
|
||||
dictionaries that you can use with this extension.
|
||||
Deleting individual dictionaries is not currently feasible due to limitations of browser database technology.
|
||||
</p>
|
||||
|
||||
<div class="form-group" id="dict-main-group">
|
||||
<label for="dict-main">Main dictionary for merged mode</label>
|
||||
<select class="form-control" id="dict-main"></select>
|
||||
</div>
|
||||
|
||||
<div class="text-danger" id="dict-purge">Dictionary data is being purged, please be patient...</div>
|
||||
<div class="alert alert-warning" id="dict-warning">No dictionaries have been installed</div>
|
||||
<div class="alert alert-danger" id="dict-error"></div>
|
||||
@ -150,6 +167,11 @@
|
||||
</div>
|
||||
|
||||
<div id="dict-importer">
|
||||
<p class="help-block">
|
||||
Select a dictionary to import for use below. Please visit the Yomichan homepage to
|
||||
<a href="https://foosoft.net/projects/yomichan" target="_blank">download free dictionaries</a>
|
||||
for use with this extension and to learn about importing proprietary EPWING dictionaries.
|
||||
</p>
|
||||
<input type="file" id="dict-file">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -76,7 +76,7 @@ function docRangeFromPoint(point) {
|
||||
if (!document.caretRangeFromPoint) {
|
||||
document.caretRangeFromPoint = (x, y) => {
|
||||
const position = document.caretPositionFromPoint(x,y);
|
||||
if (position && position.offsetNode) {
|
||||
if (position && position.offsetNode && position.offsetNode.nodeType === Node.TEXT_NODE) {
|
||||
const range = document.createRange();
|
||||
range.setStart(position.offsetNode, position.offset);
|
||||
range.setEnd(position.offsetNode, position.offset);
|
||||
@ -86,7 +86,7 @@ function docRangeFromPoint(point) {
|
||||
}
|
||||
|
||||
const range = document.caretRangeFromPoint(point.x, point.y);
|
||||
if (range && range.startContainer.nodeType === 3 && range.endContainer.nodeType === 3) {
|
||||
if (range) {
|
||||
return new TextSourceRange(range);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Yomichan",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
|
||||
"description": "Japanese dictionary with Anki integration",
|
||||
"icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"},
|
||||
|
@ -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;
|
||||
|
@ -29,6 +29,7 @@ class Display {
|
||||
this.audioCache = {};
|
||||
|
||||
$(document).keydown(this.onKeyDown.bind(this));
|
||||
$(document).on('wheel', this.onWheel.bind(this));
|
||||
}
|
||||
|
||||
onError(error) {
|
||||
@ -70,8 +71,10 @@ class Display {
|
||||
|
||||
onAudioPlay(e) {
|
||||
e.preventDefault();
|
||||
const index = Display.entryIndexFind($(e.currentTarget));
|
||||
this.audioPlay(this.definitions[index]);
|
||||
const link = $(e.currentTarget);
|
||||
const definitionIndex = Display.entryIndexFind(link);
|
||||
const expressionIndex = link.closest('.entry').find('.expression .action-play-audio').index(link);
|
||||
this.audioPlay(this.definitions[definitionIndex], expressionIndex);
|
||||
}
|
||||
|
||||
onNoteAdd(e) {
|
||||
@ -182,7 +185,8 @@ class Display {
|
||||
80: /* p */ () => {
|
||||
if (e.altKey) {
|
||||
if ($('.entry').eq(this.index).data('type') === 'term') {
|
||||
this.audioPlay(this.definitions[this.index]);
|
||||
const expressionIndex = this.options.general.resultOutputMode === 'merge' ? 0 : -1;
|
||||
this.audioPlay(this.definitions[this.index], expressionIndex);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -202,6 +206,25 @@ class Display {
|
||||
}
|
||||
}
|
||||
|
||||
onWheel(e) {
|
||||
const event = e.originalEvent;
|
||||
const handler = () => {
|
||||
if (event.altKey) {
|
||||
if (event.deltaY < 0) { // scroll up
|
||||
this.entryScrollIntoView(this.index - 1, true);
|
||||
return true;
|
||||
} else if (event.deltaY > 0) { // scroll down
|
||||
this.entryScrollIntoView(this.index + 1, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (handler()) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
async termsShow(definitions, options, context) {
|
||||
try {
|
||||
window.focus();
|
||||
@ -214,8 +237,10 @@ class Display {
|
||||
const params = {
|
||||
definitions,
|
||||
addable: options.anki.enable,
|
||||
grouped: options.general.groupResults,
|
||||
grouped: options.general.resultOutputMode === 'group',
|
||||
merged: options.general.resultOutputMode === 'merge',
|
||||
playback: options.general.audioSource !== 'disabled',
|
||||
compactGlossaries: options.general.compactGlossaries,
|
||||
debug: options.general.debugInfo
|
||||
};
|
||||
|
||||
@ -359,11 +384,12 @@ class Display {
|
||||
}
|
||||
}
|
||||
|
||||
async audioPlay(definition) {
|
||||
async audioPlay(definition, expressionIndex) {
|
||||
try {
|
||||
this.spinner.show();
|
||||
|
||||
let url = await apiAudioGetUrl(definition, this.options.general.audioSource);
|
||||
const expression = expressionIndex === -1 ? definition : definition.expressions[expressionIndex];
|
||||
let url = await apiAudioGetUrl(expression, this.options.general.audioSource);
|
||||
if (!url) {
|
||||
url = '/mixed/mp3/button.mp3';
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
<div class="dict-group well well-sm" data-title="{{title}}">
|
||||
<h4><span class="text-muted glyphicon glyphicon-book"></span> {{title}} <small>rev.{{revision}}</small></h4>
|
||||
{{#if outdated}}
|
||||
<p class="text-warning">This dictionary is outdated and may not support new extension features; please import the latest version.</p>
|
||||
{{/if}}
|
||||
|
||||
<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">
|
||||
|
@ -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]}}
|
||||
{{> 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] 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>
|
||||
|
Loading…
Reference in New Issue
Block a user