summaryrefslogtreecommitdiff
path: root/ext/bg/js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/bg/js')
-rw-r--r--ext/bg/js/dictionary-database.js17
-rw-r--r--ext/bg/js/dictionary.js298
-rw-r--r--ext/bg/js/translator.js346
3 files changed, 317 insertions, 344 deletions
diff --git a/ext/bg/js/dictionary-database.js b/ext/bg/js/dictionary-database.js
index 671be7a8..c6798fb3 100644
--- a/ext/bg/js/dictionary-database.js
+++ b/ext/bg/js/dictionary-database.js
@@ -17,7 +17,6 @@
/* global
* Database
- * dictFieldSplit
*/
class DictionaryDatabase {
@@ -436,9 +435,9 @@ class DictionaryDatabase {
index,
expression: row.expression,
reading: row.reading,
- definitionTags: dictFieldSplit(row.definitionTags || row.tags || ''),
- termTags: dictFieldSplit(row.termTags || ''),
- rules: dictFieldSplit(row.rules),
+ definitionTags: this._splitField(row.definitionTags || row.tags || ''),
+ termTags: this._splitField(row.termTags || ''),
+ rules: this._splitField(row.rules),
glossary: row.glossary,
score: row.score,
dictionary: row.dictionary,
@@ -451,9 +450,9 @@ class DictionaryDatabase {
return {
index,
character: row.character,
- onyomi: dictFieldSplit(row.onyomi),
- kunyomi: dictFieldSplit(row.kunyomi),
- tags: dictFieldSplit(row.tags),
+ onyomi: this._splitField(row.onyomi),
+ kunyomi: this._splitField(row.kunyomi),
+ tags: this._splitField(row.tags),
glossary: row.meanings,
stats: row.stats,
dictionary: row.dictionary
@@ -471,4 +470,8 @@ class DictionaryDatabase {
_createMedia(row, index) {
return Object.assign({}, row, {index});
}
+
+ _splitField(field) {
+ return field.length === 0 ? [] : field.split(' ');
+ }
}
diff --git a/ext/bg/js/dictionary.js b/ext/bg/js/dictionary.js
deleted file mode 100644
index 2ad6578a..00000000
--- a/ext/bg/js/dictionary.js
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2016-2020 Yomichan Authors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-function dictEnabledSet(options) {
- const enabledDictionaryMap = new Map();
- for (const [title, {enabled, priority, allowSecondarySearches}] of Object.entries(options.dictionaries)) {
- if (!enabled) { continue; }
- enabledDictionaryMap.set(title, {priority, allowSecondarySearches});
- }
- return enabledDictionaryMap;
-}
-
-function dictTermsSort(definitions, dictionaries=null) {
- return definitions.sort((v1, v2) => {
- let i;
- if (dictionaries !== null) {
- const dictionaryInfo1 = dictionaries.get(v1.dictionary);
- const dictionaryInfo2 = dictionaries.get(v2.dictionary);
- const priority1 = typeof dictionaryInfo1 !== 'undefined' ? dictionaryInfo1.priority : 0;
- const priority2 = typeof dictionaryInfo2 !== 'undefined' ? dictionaryInfo2.priority : 0;
- i = priority2 - priority1;
- if (i !== 0) { return i; }
- }
-
- i = v2.source.length - v1.source.length;
- if (i !== 0) { return i; }
-
- i = v1.reasons.length - v2.reasons.length;
- if (i !== 0) { return i; }
-
- i = v2.score - v1.score;
- if (i !== 0) { return i; }
-
- return v2.expression.toString().localeCompare(v1.expression.toString());
- });
-}
-
-function dictTermsUndupe(definitions) {
- const definitionGroups = new Map();
- for (const definition of definitions) {
- const id = definition.id;
- const definitionExisting = definitionGroups.get(id);
- if (typeof definitionExisting === 'undefined' || definition.expression.length > definitionExisting.expression.length) {
- definitionGroups.set(id, definition);
- }
- }
-
- return [...definitionGroups.values()];
-}
-
-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 = new Map();
- for (const definition of definitions) {
- const key = [definition.source, definition.expression, ...definition.reasons];
- if (definition.reading) {
- key.push(definition.reading);
- }
-
- const keyString = key.toString();
- let groupDefinitions = groups.get(keyString);
- if (typeof groupDefinitions === 'undefined') {
- groupDefinitions = [];
- groups.set(keyString, groupDefinitions);
- }
-
- groupDefinitions.push(definition);
- }
-
- const results = [];
- for (const groupDefinitions of groups.values()) {
- const firstDef = groupDefinitions[0];
- dictTermsSort(groupDefinitions, dictionaries);
- results.push({
- definitions: groupDefinitions,
- expression: firstDef.expression,
- reading: firstDef.reading,
- furiganaSegments: firstDef.furiganaSegments,
- reasons: firstDef.reasons,
- termTags: firstDef.termTags,
- score: groupDefinitions.reduce((p, v) => v.score > p ? v.score : p, Number.MIN_SAFE_INTEGER),
- source: firstDef.source
- });
- }
-
- return dictTermsSort(results);
-}
-
-function dictTermsMergeBySequence(definitions, mainDictionary) {
- const sequencedDefinitions = new Map();
- const nonSequencedDefinitions = [];
- for (const definition of definitions) {
- const sequence = definition.sequence;
- if (mainDictionary === definition.dictionary && sequence >= 0) {
- let sequencedDefinition = sequencedDefinitions.get(sequence);
- if (typeof sequencedDefinition === 'undefined') {
- sequencedDefinition = {
- reasons: definition.reasons,
- score: definition.score,
- expression: new Set(),
- reading: new Set(),
- expressions: new Map(),
- source: definition.source,
- dictionary: definition.dictionary,
- definitions: []
- };
- sequencedDefinitions.set(sequence, sequencedDefinition);
- } else {
- sequencedDefinition.score = Math.max(sequencedDefinition.score, definition.score);
- }
- } else {
- nonSequencedDefinitions.push(definition);
- }
- }
-
- return [sequencedDefinitions, nonSequencedDefinitions];
-}
-
-function dictTermsMergeByGloss(result, definitions, appendTo=null, mergedIndices=null) {
- const definitionsByGloss = appendTo !== null ? appendTo : new Map();
-
- const resultExpressionsMap = result.expressions;
- const resultExpressionSet = result.expression;
- const resultReadingSet = result.reading;
- const resultSource = result.source;
-
- for (const [index, definition] of definitions.entries()) {
- const {expression, reading} = definition;
-
- if (mergedIndices !== null) {
- const expressionMap = resultExpressionsMap.get(expression);
- if (
- typeof expressionMap !== 'undefined' &&
- typeof expressionMap.get(reading) !== 'undefined'
- ) {
- mergedIndices.add(index);
- } else {
- continue;
- }
- }
-
- const gloss = JSON.stringify(definition.glossary.concat(definition.dictionary));
- let glossDefinition = definitionsByGloss.get(gloss);
- if (typeof glossDefinition === 'undefined') {
- glossDefinition = {
- expression: new Set(),
- reading: new Set(),
- definitionTags: [],
- glossary: definition.glossary,
- source: resultSource,
- reasons: [],
- score: definition.score,
- id: definition.id,
- dictionary: definition.dictionary
- };
- definitionsByGloss.set(gloss, glossDefinition);
- }
-
- glossDefinition.expression.add(expression);
- glossDefinition.reading.add(reading);
-
- resultExpressionSet.add(expression);
- resultReadingSet.add(reading);
-
- for (const tag of definition.definitionTags) {
- if (!glossDefinition.definitionTags.find((existingTag) => existingTag.name === tag.name)) {
- glossDefinition.definitionTags.push(tag);
- }
- }
-
- if (appendTo === null) {
- /*
- Data layout:
- resultExpressionsMap = new Map([
- [expression, new Map([
- [reading, new Map([
- [tagName, tagInfo],
- ...
- ])],
- ...
- ])],
- ...
- ]);
- */
- let readingMap = resultExpressionsMap.get(expression);
- if (typeof readingMap === 'undefined') {
- readingMap = new Map();
- resultExpressionsMap.set(expression, readingMap);
- }
-
- let termTagsMap = readingMap.get(reading);
- if (typeof termTagsMap === 'undefined') {
- termTagsMap = new Map();
- readingMap.set(reading, termTagsMap);
- }
-
- for (const tag of definition.termTags) {
- if (!termTagsMap.has(tag.name)) {
- termTagsMap.set(tag.name, tag);
- }
- }
- }
- }
-
- for (const definition of definitionsByGloss.values()) {
- const only = [];
- const expressionSet = definition.expression;
- const readingSet = definition.reading;
- if (!areSetsEqual(expressionSet, resultExpressionSet)) {
- only.push(...getSetIntersection(expressionSet, resultExpressionSet));
- }
- if (!areSetsEqual(readingSet, resultReadingSet)) {
- only.push(...getSetIntersection(readingSet, resultReadingSet));
- }
- definition.only = only;
- }
-
- return definitionsByGloss;
-}
-
-function dictTagBuildSource(name) {
- return dictTagSanitize({name, category: 'dictionary', order: 100});
-}
-
-function dictTagSanitize(tag) {
- tag.name = tag.name || 'untitled';
- tag.category = tag.category || 'default';
- tag.notes = tag.notes || '';
- tag.order = tag.order || 0;
- tag.score = tag.score || 0;
- return tag;
-}
-
-function dictTagsSort(tags) {
- return tags.sort((v1, v2) => {
- const order1 = v1.order;
- const order2 = v2.order;
- if (order1 < order2) {
- return -1;
- } else if (order1 > order2) {
- return 1;
- }
-
- const name1 = v1.name;
- const name2 = v2.name;
- if (name1 < name2) {
- return -1;
- } else if (name1 > name2) {
- return 1;
- }
-
- return 0;
- });
-}
-
-function dictFieldSplit(field) {
- return field.length === 0 ? [] : field.split(' ');
-}
diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js
index 7005b4b5..aea04322 100644
--- a/ext/bg/js/translator.js
+++ b/ext/bg/js/translator.js
@@ -18,16 +18,6 @@
/* global
* Deinflector
* TextSourceMap
- * dictEnabledSet
- * dictTagBuildSource
- * dictTagSanitize
- * dictTagsSort
- * dictTermsCompressTags
- * dictTermsGroup
- * dictTermsMergeByGloss
- * dictTermsMergeBySequence
- * dictTermsSort
- * dictTermsUndupe
* jp
*/
@@ -63,7 +53,7 @@ class Translator {
}
async findKanji(text, options) {
- const dictionaries = dictEnabledSet(options);
+ const dictionaries = this._getEnabledDictionaryMap(options);
const kanjiUnique = new Set();
for (const c of text) {
kanjiUnique.add(c);
@@ -80,8 +70,8 @@ class Translator {
for (const definition of definitions) {
const tags = await this._expandTags(definition.tags, definition.dictionary);
- tags.push(dictTagBuildSource(definition.dictionary));
- dictTagsSort(tags);
+ tags.push(this._createDictionaryTag(definition.dictionary));
+ this._sortTags(tags);
const stats = await this._expandStats(definition.stats, definition.dictionary);
@@ -97,7 +87,7 @@ class Translator {
// Private
async _getSequencedDefinitions(definitions, mainDictionary) {
- const [definitionsBySequence, defaultDefinitions] = dictTermsMergeBySequence(definitions, mainDictionary);
+ const [definitionsBySequence, defaultDefinitions] = this._mergeBySequence(definitions, mainDictionary);
const sequenceList = [];
const sequencedDefinitions = [];
@@ -131,7 +121,7 @@ class Translator {
const definitions = await this._database.findTermsExactBulk(expressionList, readingList, secondarySearchDictionaries);
for (const definition of definitions) {
const definitionTags = await this._expandTags(definition.definitionTags, definition.dictionary);
- definitionTags.push(dictTagBuildSource(definition.dictionary));
+ definitionTags.push(this._createDictionaryTag(definition.dictionary));
definition.definitionTags = definitionTags;
const termTags = await this._expandTags(definition.termTags, definition.dictionary);
definition.termTags = termTags;
@@ -150,30 +140,30 @@ class Translator {
for (const definition of rawDefinitionsBySequence) {
const definitionTags = await this._expandTags(definition.definitionTags, definition.dictionary);
- definitionTags.push(dictTagBuildSource(definition.dictionary));
+ definitionTags.push(this._createDictionaryTag(definition.dictionary));
definition.definitionTags = definitionTags;
const termTags = await this._expandTags(definition.termTags, definition.dictionary);
definition.termTags = termTags;
}
- const definitionsByGloss = dictTermsMergeByGloss(result, rawDefinitionsBySequence);
+ const definitionsByGloss = this._mergeByGlossary(result, rawDefinitionsBySequence);
const secondarySearchResults = await this._getMergedSecondarySearchResults(text, result.expressions, secondarySearchDictionaries);
- dictTermsMergeByGloss(result, defaultDefinitions.concat(secondarySearchResults), definitionsByGloss, mergedByTermIndices);
+ this._mergeByGlossary(result, defaultDefinitions.concat(secondarySearchResults), definitionsByGloss, mergedByTermIndices);
for (const definition of definitionsByGloss.values()) {
- dictTagsSort(definition.definitionTags);
+ this._sortTags(definition.definitionTags);
result.definitions.push(definition);
}
- dictTermsSort(result.definitions, dictionaries);
+ this._sortDefinitions(result.definitions, dictionaries);
const expressions = [];
for (const [expression, readingMap] of result.expressions.entries()) {
for (const [reading, termTagsMap] of readingMap.entries()) {
const termTags = [...termTagsMap.values()];
const score = termTags.map((tag) => tag.score).reduce((p, v) => p + v, 0);
- expressions.push(this._createExpression(expression, reading, dictTagsSort(termTags), this._scoreToTermFrequency(score)));
+ expressions.push(this._createExpression(expression, reading, this._sortTags(termTags), this._scoreToTermFrequency(score)));
}
}
@@ -185,15 +175,15 @@ class Translator {
}
async _findTermsGrouped(text, details, options) {
- const dictionaries = dictEnabledSet(options);
+ const dictionaries = this._getEnabledDictionaryMap(options);
const [definitions, length] = await this._findTermsInternal(text, dictionaries, details, options);
- const definitionsGrouped = dictTermsGroup(definitions, dictionaries);
+ const definitionsGrouped = this._groupTerms(definitions, dictionaries);
await this._buildTermMeta(definitionsGrouped, dictionaries);
if (options.general.compactTags) {
for (const definition of definitionsGrouped) {
- dictTermsCompressTags(definition.definitions);
+ this._compressDefinitionTags(definition.definitions);
}
}
@@ -201,7 +191,7 @@ class Translator {
}
async _findTermsMerged(text, details, options) {
- const dictionaries = dictEnabledSet(options);
+ const dictionaries = this._getEnabledDictionaryMap(options);
const secondarySearchDictionaries = new Map();
for (const [title, dictionary] of dictionaries.entries()) {
if (!dictionary.allowSecondarySearches) { continue; }
@@ -226,7 +216,7 @@ class Translator {
}
const strayDefinitions = defaultDefinitions.filter((definition, index) => !mergedByTermIndices.has(index));
- for (const groupedDefinition of dictTermsGroup(strayDefinitions, dictionaries)) {
+ for (const groupedDefinition of this._groupTerms(strayDefinitions, dictionaries)) {
// from dictTermsMergeBySequence
const {reasons, score, expression, reading, source, dictionary} = groupedDefinition;
const compatibilityDefinition = {
@@ -246,15 +236,15 @@ class Translator {
if (options.general.compactTags) {
for (const definition of definitionsMerged) {
- dictTermsCompressTags(definition.definitions);
+ this._compressDefinitionTags(definition.definitions);
}
}
- return [dictTermsSort(definitionsMerged), length];
+ return [this._sortDefinitions(definitionsMerged), length];
}
async _findTermsSplit(text, details, options) {
- const dictionaries = dictEnabledSet(options);
+ const dictionaries = this._getEnabledDictionaryMap(options);
const [definitions, length] = await this._findTermsInternal(text, dictionaries, details, options);
await this._buildTermMeta(definitions, dictionaries);
@@ -263,9 +253,9 @@ class Translator {
}
async _findTermsSimple(text, details, options) {
- const dictionaries = dictEnabledSet(options);
+ const dictionaries = this._getEnabledDictionaryMap(options);
const [definitions, length] = await this._findTermsInternal(text, dictionaries, details, options);
- dictTermsSort(definitions);
+ this._sortDefinitions(definitions);
return [definitions, length];
}
@@ -285,7 +275,7 @@ class Translator {
for (const deinflection of deinflections) {
for (const definition of deinflection.definitions) {
const definitionTags = await this._expandTags(definition.definitionTags, definition.dictionary);
- definitionTags.push(dictTagBuildSource(definition.dictionary));
+ definitionTags.push(this._createDictionaryTag(definition.dictionary));
const termTags = await this._expandTags(definition.termTags, definition.dictionary);
const {expression, reading} = definition;
@@ -302,15 +292,15 @@ class Translator {
reading,
furiganaSegments,
glossary: definition.glossary,
- definitionTags: dictTagsSort(definitionTags),
- termTags: dictTagsSort(termTags),
+ definitionTags: this._sortTags(definitionTags),
+ termTags: this._sortTags(termTags),
sequence: definition.sequence
});
}
}
- definitions = dictTermsUndupe(definitions);
- definitions = dictTermsSort(definitions, dictionaries);
+ definitions = this._removeDuplicateDefinitions(definitions);
+ definitions = this._sortDefinitions(definitions, dictionaries);
let length = 0;
for (const definition of definitions) {
@@ -515,8 +505,8 @@ class Translator {
const tagMetaList = await this._getTagMetaList(names, title);
return tagMetaList.map((meta, index) => {
const name = names[index];
- const tag = dictTagSanitize(Object.assign({}, meta !== null ? meta : {}, {name}));
- return dictTagSanitize(tag);
+ const tag = this._sanitizeTag(Object.assign({}, meta !== null ? meta : {}, {name}));
+ return this._sanitizeTag(tag);
});
}
@@ -538,7 +528,7 @@ class Translator {
}
const stat = Object.assign({}, meta, {name, value: items[name]});
- group.push(dictTagSanitize(stat));
+ group.push(this._sanitizeTag(stat));
}
const stats = {};
@@ -674,4 +664,282 @@ class Translator {
}
return await response.json();
}
+
+ _getEnabledDictionaryMap(options) {
+ const enabledDictionaryMap = new Map();
+ for (const [title, {enabled, priority, allowSecondarySearches}] of Object.entries(options.dictionaries)) {
+ if (!enabled) { continue; }
+ enabledDictionaryMap.set(title, {priority, allowSecondarySearches});
+ }
+ return enabledDictionaryMap;
+ }
+
+ _sortDefinitions(definitions, dictionaries=null) {
+ return definitions.sort((v1, v2) => {
+ let i;
+ if (dictionaries !== null) {
+ const dictionaryInfo1 = dictionaries.get(v1.dictionary);
+ const dictionaryInfo2 = dictionaries.get(v2.dictionary);
+ const priority1 = typeof dictionaryInfo1 !== 'undefined' ? dictionaryInfo1.priority : 0;
+ const priority2 = typeof dictionaryInfo2 !== 'undefined' ? dictionaryInfo2.priority : 0;
+ i = priority2 - priority1;
+ if (i !== 0) { return i; }
+ }
+
+ i = v2.source.length - v1.source.length;
+ if (i !== 0) { return i; }
+
+ i = v1.reasons.length - v2.reasons.length;
+ if (i !== 0) { return i; }
+
+ i = v2.score - v1.score;
+ if (i !== 0) { return i; }
+
+ return v2.expression.toString().localeCompare(v1.expression.toString());
+ });
+ }
+
+ _removeDuplicateDefinitions(definitions) {
+ const definitionGroups = new Map();
+ for (const definition of definitions) {
+ const id = definition.id;
+ const definitionExisting = definitionGroups.get(id);
+ if (typeof definitionExisting === 'undefined' || definition.expression.length > definitionExisting.expression.length) {
+ definitionGroups.set(id, definition);
+ }
+ }
+
+ return [...definitionGroups.values()];
+ }
+
+ _compressDefinitionTags(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));
+ }
+ }
+
+ _groupTerms(definitions, dictionaries) {
+ const groups = new Map();
+ for (const definition of definitions) {
+ const key = [definition.source, definition.expression, ...definition.reasons];
+ if (definition.reading) {
+ key.push(definition.reading);
+ }
+
+ const keyString = key.toString();
+ let groupDefinitions = groups.get(keyString);
+ if (typeof groupDefinitions === 'undefined') {
+ groupDefinitions = [];
+ groups.set(keyString, groupDefinitions);
+ }
+
+ groupDefinitions.push(definition);
+ }
+
+ const results = [];
+ for (const groupDefinitions of groups.values()) {
+ const firstDef = groupDefinitions[0];
+ this._sortDefinitions(groupDefinitions, dictionaries);
+ results.push({
+ definitions: groupDefinitions,
+ expression: firstDef.expression,
+ reading: firstDef.reading,
+ furiganaSegments: firstDef.furiganaSegments,
+ reasons: firstDef.reasons,
+ termTags: firstDef.termTags,
+ score: groupDefinitions.reduce((p, v) => v.score > p ? v.score : p, Number.MIN_SAFE_INTEGER),
+ source: firstDef.source
+ });
+ }
+
+ return this._sortDefinitions(results);
+ }
+
+ _mergeBySequence(definitions, mainDictionary) {
+ const sequencedDefinitions = new Map();
+ const nonSequencedDefinitions = [];
+ for (const definition of definitions) {
+ const sequence = definition.sequence;
+ if (mainDictionary === definition.dictionary && sequence >= 0) {
+ let sequencedDefinition = sequencedDefinitions.get(sequence);
+ if (typeof sequencedDefinition === 'undefined') {
+ sequencedDefinition = {
+ reasons: definition.reasons,
+ score: definition.score,
+ expression: new Set(),
+ reading: new Set(),
+ expressions: new Map(),
+ source: definition.source,
+ dictionary: definition.dictionary,
+ definitions: []
+ };
+ sequencedDefinitions.set(sequence, sequencedDefinition);
+ } else {
+ sequencedDefinition.score = Math.max(sequencedDefinition.score, definition.score);
+ }
+ } else {
+ nonSequencedDefinitions.push(definition);
+ }
+ }
+
+ return [sequencedDefinitions, nonSequencedDefinitions];
+ }
+
+ _mergeByGlossary(result, definitions, appendTo=null, mergedIndices=null) {
+ const definitionsByGlossary = appendTo !== null ? appendTo : new Map();
+
+ const resultExpressionsMap = result.expressions;
+ const resultExpressionSet = result.expression;
+ const resultReadingSet = result.reading;
+ const resultSource = result.source;
+
+ for (const [index, definition] of definitions.entries()) {
+ const {expression, reading} = definition;
+
+ if (mergedIndices !== null) {
+ const expressionMap = resultExpressionsMap.get(expression);
+ if (
+ typeof expressionMap !== 'undefined' &&
+ typeof expressionMap.get(reading) !== 'undefined'
+ ) {
+ mergedIndices.add(index);
+ } else {
+ continue;
+ }
+ }
+
+ const gloss = JSON.stringify(definition.glossary.concat(definition.dictionary));
+ let glossDefinition = definitionsByGlossary.get(gloss);
+ if (typeof glossDefinition === 'undefined') {
+ glossDefinition = {
+ expression: new Set(),
+ reading: new Set(),
+ definitionTags: [],
+ glossary: definition.glossary,
+ source: resultSource,
+ reasons: [],
+ score: definition.score,
+ id: definition.id,
+ dictionary: definition.dictionary
+ };
+ definitionsByGlossary.set(gloss, glossDefinition);
+ }
+
+ glossDefinition.expression.add(expression);
+ glossDefinition.reading.add(reading);
+
+ resultExpressionSet.add(expression);
+ resultReadingSet.add(reading);
+
+ for (const tag of definition.definitionTags) {
+ if (!glossDefinition.definitionTags.find((existingTag) => existingTag.name === tag.name)) {
+ glossDefinition.definitionTags.push(tag);
+ }
+ }
+
+ if (appendTo === null) {
+ /*
+ Data layout:
+ resultExpressionsMap = new Map([
+ [expression, new Map([
+ [reading, new Map([
+ [tagName, tagInfo],
+ ...
+ ])],
+ ...
+ ])],
+ ...
+ ]);
+ */
+ let readingMap = resultExpressionsMap.get(expression);
+ if (typeof readingMap === 'undefined') {
+ readingMap = new Map();
+ resultExpressionsMap.set(expression, readingMap);
+ }
+
+ let termTagsMap = readingMap.get(reading);
+ if (typeof termTagsMap === 'undefined') {
+ termTagsMap = new Map();
+ readingMap.set(reading, termTagsMap);
+ }
+
+ for (const tag of definition.termTags) {
+ if (!termTagsMap.has(tag.name)) {
+ termTagsMap.set(tag.name, tag);
+ }
+ }
+ }
+ }
+
+ for (const definition of definitionsByGlossary.values()) {
+ const only = [];
+ const expressionSet = definition.expression;
+ const readingSet = definition.reading;
+ if (!areSetsEqual(expressionSet, resultExpressionSet)) {
+ only.push(...getSetIntersection(expressionSet, resultExpressionSet));
+ }
+ if (!areSetsEqual(readingSet, resultReadingSet)) {
+ only.push(...getSetIntersection(readingSet, resultReadingSet));
+ }
+ definition.only = only;
+ }
+
+ return definitionsByGlossary;
+ }
+
+ _createDictionaryTag(name) {
+ return this._sanitizeTag({name, category: 'dictionary', order: 100});
+ }
+
+ _sanitizeTag(tag) {
+ tag.name = tag.name || 'untitled';
+ tag.category = tag.category || 'default';
+ tag.notes = tag.notes || '';
+ tag.order = tag.order || 0;
+ tag.score = tag.score || 0;
+ return tag;
+ }
+
+ _sortTags(tags) {
+ return tags.sort((v1, v2) => {
+ const order1 = v1.order;
+ const order2 = v2.order;
+ if (order1 < order2) {
+ return -1;
+ } else if (order1 > order2) {
+ return 1;
+ }
+
+ const name1 = v1.name;
+ const name2 = v2.name;
+ if (name1 < name2) {
+ return -1;
+ } else if (name1 > name2) {
+ return 1;
+ }
+
+ return 0;
+ });
+ }
}