Merge pull request #362 from toasted-nutbread/more-type-refactoring
More type refactoring
This commit is contained in:
commit
f8f03f3af0
@ -168,10 +168,8 @@ async function audioInject(definition, fields, sources, optionsContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let audioSourceDefinition = definition;
|
const expressions = definition.expressions;
|
||||||
if (hasOwn(definition, 'expressions')) {
|
const audioSourceDefinition = Array.isArray(expressions) ? expressions[0] : definition;
|
||||||
audioSourceDefinition = definition.expressions[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const {url} = await audioGetFromSources(audioSourceDefinition, sources, optionsContext, true);
|
const {url} = await audioGetFromSources(audioSourceDefinition, sources, optionsContext, true);
|
||||||
if (url !== null) {
|
if (url !== null) {
|
||||||
|
@ -319,7 +319,8 @@ class Backend {
|
|||||||
|
|
||||||
async _onApiTermsFind({text, details, optionsContext}) {
|
async _onApiTermsFind({text, details, optionsContext}) {
|
||||||
const options = await this.getOptions(optionsContext);
|
const options = await this.getOptions(optionsContext);
|
||||||
const [definitions, length] = await this.translator.findTerms(text, details, options);
|
const mode = options.general.resultOutputMode;
|
||||||
|
const [definitions, length] = await this.translator.findTerms(mode, text, details, options);
|
||||||
definitions.splice(options.general.maxResults);
|
definitions.splice(options.general.maxResults);
|
||||||
return {length, definitions};
|
return {length, definitions};
|
||||||
}
|
}
|
||||||
@ -329,9 +330,9 @@ class Backend {
|
|||||||
const results = [];
|
const results = [];
|
||||||
while (text.length > 0) {
|
while (text.length > 0) {
|
||||||
const term = [];
|
const term = [];
|
||||||
const [definitions, sourceLength] = await this.translator.findTermsInternal(
|
const [definitions, sourceLength] = await this.translator.findTerms(
|
||||||
|
'simple',
|
||||||
text.substring(0, options.scanning.length),
|
text.substring(0, options.scanning.length),
|
||||||
dictEnabledSet(options),
|
|
||||||
{},
|
{},
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
@ -149,15 +149,15 @@ class Database {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTermsBulk(termList, titles, wildcard) {
|
async findTermsBulk(termList, dictionaries, wildcard) {
|
||||||
this._validate();
|
this._validate();
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const visited = {};
|
const visited = new Set();
|
||||||
const results = [];
|
const results = [];
|
||||||
const processRow = (row, index) => {
|
const processRow = (row, index) => {
|
||||||
if (titles.includes(row.dictionary) && !hasOwn(visited, row.id)) {
|
if (dictionaries.has(row.dictionary) && !visited.has(row.id)) {
|
||||||
visited[row.id] = true;
|
visited.add(row.id);
|
||||||
results.push(Database._createTerm(row, index));
|
results.push(Database._createTerm(row, index));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -184,13 +184,13 @@ class Database {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTermsExactBulk(termList, readingList, titles) {
|
async findTermsExactBulk(termList, readingList, dictionaries) {
|
||||||
this._validate();
|
this._validate();
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const results = [];
|
const results = [];
|
||||||
const processRow = (row, index) => {
|
const processRow = (row, index) => {
|
||||||
if (row.reading === readingList[index] && titles.includes(row.dictionary)) {
|
if (row.reading === readingList[index] && dictionaries.has(row.dictionary)) {
|
||||||
results.push(Database._createTerm(row, index));
|
results.push(Database._createTerm(row, index));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -234,16 +234,16 @@ class Database {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTermMetaBulk(termList, titles) {
|
async findTermMetaBulk(termList, dictionaries) {
|
||||||
return this._findGenericBulk('termMeta', 'expression', termList, titles, Database._createTermMeta);
|
return this._findGenericBulk('termMeta', 'expression', termList, dictionaries, Database._createTermMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findKanjiBulk(kanjiList, titles) {
|
async findKanjiBulk(kanjiList, dictionaries) {
|
||||||
return this._findGenericBulk('kanji', 'character', kanjiList, titles, Database._createKanji);
|
return this._findGenericBulk('kanji', 'character', kanjiList, dictionaries, Database._createKanji);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findKanjiMetaBulk(kanjiList, titles) {
|
async findKanjiMetaBulk(kanjiList, dictionaries) {
|
||||||
return this._findGenericBulk('kanjiMeta', 'character', kanjiList, titles, Database._createKanjiMeta);
|
return this._findGenericBulk('kanjiMeta', 'character', kanjiList, dictionaries, Database._createKanjiMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTagForTitle(name, title) {
|
async findTagForTitle(name, title) {
|
||||||
@ -572,13 +572,13 @@ class Database {
|
|||||||
return count > 0;
|
return count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _findGenericBulk(tableName, indexName, indexValueList, titles, createResult) {
|
async _findGenericBulk(tableName, indexName, indexValueList, dictionaries, createResult) {
|
||||||
this._validate();
|
this._validate();
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const results = [];
|
const results = [];
|
||||||
const processRow = (row, index) => {
|
const processRow = (row, index) => {
|
||||||
if (titles.includes(row.dictionary)) {
|
if (dictionaries.has(row.dictionary)) {
|
||||||
results.push(createResult(row, index));
|
results.push(createResult(row, index));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -16,18 +16,21 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*global utilSetEqual, utilSetIntersection, apiTemplateRender*/
|
/*global apiTemplateRender*/
|
||||||
|
|
||||||
function dictEnabledSet(options) {
|
function dictEnabledSet(options) {
|
||||||
const dictionaries = {};
|
const enabledDictionaryMap = new Map();
|
||||||
for (const title in options.dictionaries) {
|
const optionsDictionaries = options.dictionaries;
|
||||||
const dictionary = options.dictionaries[title];
|
for (const title in optionsDictionaries) {
|
||||||
if (dictionary.enabled) {
|
if (!hasOwn(optionsDictionaries, title)) { continue; }
|
||||||
dictionaries[title] = dictionary;
|
const dictionary = optionsDictionaries[title];
|
||||||
|
if (!dictionary.enabled) { continue; }
|
||||||
|
enabledDictionaryMap.set(title, {
|
||||||
|
priority: dictionary.priority || 0,
|
||||||
|
allowSecondarySearches: !!dictionary.allowSecondarySearches
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
return enabledDictionaryMap;
|
||||||
|
|
||||||
return dictionaries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function dictConfigured(options) {
|
function dictConfigured(options) {
|
||||||
@ -40,28 +43,15 @@ function dictConfigured(options) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function dictRowsSort(rows, options) {
|
|
||||||
return rows.sort((ra, rb) => {
|
|
||||||
const pa = (options.dictionaries[ra.title] || {}).priority || 0;
|
|
||||||
const pb = (options.dictionaries[rb.title] || {}).priority || 0;
|
|
||||||
if (pa > pb) {
|
|
||||||
return -1;
|
|
||||||
} else if (pa < pb) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function dictTermsSort(definitions, dictionaries=null) {
|
function dictTermsSort(definitions, dictionaries=null) {
|
||||||
return definitions.sort((v1, v2) => {
|
return definitions.sort((v1, v2) => {
|
||||||
let i;
|
let i;
|
||||||
if (dictionaries !== null) {
|
if (dictionaries !== null) {
|
||||||
i = (
|
const dictionaryInfo1 = dictionaries.get(v1.dictionary);
|
||||||
((dictionaries[v2.dictionary] || {}).priority || 0) -
|
const dictionaryInfo2 = dictionaries.get(v2.dictionary);
|
||||||
((dictionaries[v1.dictionary] || {}).priority || 0)
|
const priority1 = typeof dictionaryInfo1 !== 'undefined' ? dictionaryInfo1.priority : 0;
|
||||||
);
|
const priority2 = typeof dictionaryInfo2 !== 'undefined' ? dictionaryInfo2.priority : 0;
|
||||||
|
i = priority2 - priority1;
|
||||||
if (i !== 0) { return i; }
|
if (i !== 0) { return i; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,20 +69,16 @@ function dictTermsSort(definitions, dictionaries=null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function dictTermsUndupe(definitions) {
|
function dictTermsUndupe(definitions) {
|
||||||
const definitionGroups = {};
|
const definitionGroups = new Map();
|
||||||
for (const definition of definitions) {
|
for (const definition of definitions) {
|
||||||
const definitionExisting = definitionGroups[definition.id];
|
const id = definition.id;
|
||||||
if (!hasOwn(definitionGroups, definition.id) || definition.expression.length > definitionExisting.expression.length) {
|
const definitionExisting = definitionGroups.get(id);
|
||||||
definitionGroups[definition.id] = definition;
|
if (typeof definitionExisting === 'undefined' || definition.expression.length > definitionExisting.expression.length) {
|
||||||
|
definitionGroups.set(id, definition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const definitionsUnique = [];
|
return [...definitionGroups.values()];
|
||||||
for (const key in definitionGroups) {
|
|
||||||
definitionsUnique.push(definitionGroups[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return definitionsUnique;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function dictTermsCompressTags(definitions) {
|
function dictTermsCompressTags(definitions) {
|
||||||
@ -123,35 +109,35 @@ function dictTermsCompressTags(definitions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function dictTermsGroup(definitions, dictionaries) {
|
function dictTermsGroup(definitions, dictionaries) {
|
||||||
const groups = {};
|
const groups = new Map();
|
||||||
for (const definition of definitions) {
|
for (const definition of definitions) {
|
||||||
const key = [definition.source, definition.expression];
|
const key = [definition.source, definition.expression, ...definition.reasons];
|
||||||
key.push(...definition.reasons);
|
|
||||||
if (definition.reading) {
|
if (definition.reading) {
|
||||||
key.push(definition.reading);
|
key.push(definition.reading);
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyString = key.toString();
|
const keyString = key.toString();
|
||||||
if (hasOwn(groups, keyString)) {
|
let groupDefinitions = groups.get(keyString);
|
||||||
groups[keyString].push(definition);
|
if (typeof groupDefinitions === 'undefined') {
|
||||||
} else {
|
groupDefinitions = [];
|
||||||
groups[keyString] = [definition];
|
groups.set(keyString, groupDefinitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groupDefinitions.push(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
for (const key in groups) {
|
for (const groupDefinitions of groups.values()) {
|
||||||
const groupDefs = groups[key];
|
const firstDef = groupDefinitions[0];
|
||||||
const firstDef = groupDefs[0];
|
dictTermsSort(groupDefinitions, dictionaries);
|
||||||
dictTermsSort(groupDefs, dictionaries);
|
|
||||||
results.push({
|
results.push({
|
||||||
definitions: groupDefs,
|
definitions: groupDefinitions,
|
||||||
expression: firstDef.expression,
|
expression: firstDef.expression,
|
||||||
reading: firstDef.reading,
|
reading: firstDef.reading,
|
||||||
furiganaSegments: firstDef.furiganaSegments,
|
furiganaSegments: firstDef.furiganaSegments,
|
||||||
reasons: firstDef.reasons,
|
reasons: firstDef.reasons,
|
||||||
termTags: firstDef.termTags,
|
termTags: firstDef.termTags,
|
||||||
score: groupDefs.reduce((p, v) => v.score > p ? v.score : p, Number.MIN_SAFE_INTEGER),
|
score: groupDefinitions.reduce((p, v) => v.score > p ? v.score : p, Number.MIN_SAFE_INTEGER),
|
||||||
source: firstDef.source
|
source: firstDef.source
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -159,6 +145,30 @@ function dictTermsGroup(definitions, dictionaries) {
|
|||||||
return dictTermsSort(results);
|
return dictTermsSort(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dictAreSetsEqual(set1, set2) {
|
||||||
|
if (set1.size !== set2.size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const value of set1) {
|
||||||
|
if (!set2.has(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dictGetSetIntersection(set1, set2) {
|
||||||
|
const result = [];
|
||||||
|
for (const value of set1) {
|
||||||
|
if (set2.has(value)) {
|
||||||
|
result.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function dictTermsMergeBySequence(definitions, mainDictionary) {
|
function dictTermsMergeBySequence(definitions, mainDictionary) {
|
||||||
const sequencedDefinitions = new Map();
|
const sequencedDefinitions = new Map();
|
||||||
const nonSequencedDefinitions = [];
|
const nonSequencedDefinitions = [];
|
||||||
@ -189,89 +199,103 @@ function dictTermsMergeBySequence(definitions, mainDictionary) {
|
|||||||
return [sequencedDefinitions, nonSequencedDefinitions];
|
return [sequencedDefinitions, nonSequencedDefinitions];
|
||||||
}
|
}
|
||||||
|
|
||||||
function dictTermsMergeByGloss(result, definitions, appendTo, mergedIndices) {
|
function dictTermsMergeByGloss(result, definitions, appendTo=null, mergedIndices=null) {
|
||||||
const definitionsByGloss = appendTo || {};
|
const definitionsByGloss = appendTo !== null ? appendTo : new Map();
|
||||||
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) {
|
const resultExpressionsMap = result.expressions;
|
||||||
continue;
|
const resultExpressionSet = result.expression;
|
||||||
} else if (mergedIndices) {
|
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);
|
mergedIndices.add(index);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const gloss = JSON.stringify(definition.glossary.concat(definition.dictionary));
|
const gloss = JSON.stringify(definition.glossary.concat(definition.dictionary));
|
||||||
if (!definitionsByGloss[gloss]) {
|
let glossDefinition = definitionsByGloss.get(gloss);
|
||||||
definitionsByGloss[gloss] = {
|
if (typeof glossDefinition === 'undefined') {
|
||||||
|
glossDefinition = {
|
||||||
expression: new Set(),
|
expression: new Set(),
|
||||||
reading: new Set(),
|
reading: new Set(),
|
||||||
definitionTags: [],
|
definitionTags: [],
|
||||||
glossary: definition.glossary,
|
glossary: definition.glossary,
|
||||||
source: result.source,
|
source: resultSource,
|
||||||
reasons: [],
|
reasons: [],
|
||||||
score: definition.score,
|
score: definition.score,
|
||||||
id: definition.id,
|
id: definition.id,
|
||||||
dictionary: definition.dictionary
|
dictionary: definition.dictionary
|
||||||
};
|
};
|
||||||
|
definitionsByGloss.set(gloss, glossDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
definitionsByGloss[gloss].expression.add(definition.expression);
|
glossDefinition.expression.add(expression);
|
||||||
definitionsByGloss[gloss].reading.add(definition.reading);
|
glossDefinition.reading.add(reading);
|
||||||
|
|
||||||
result.expression.add(definition.expression);
|
resultExpressionSet.add(expression);
|
||||||
result.reading.add(definition.reading);
|
resultReadingSet.add(reading);
|
||||||
|
|
||||||
for (const tag of definition.definitionTags) {
|
for (const tag of definition.definitionTags) {
|
||||||
if (!definitionsByGloss[gloss].definitionTags.find((existingTag) => existingTag.name === tag.name)) {
|
if (!glossDefinition.definitionTags.find((existingTag) => existingTag.name === tag.name)) {
|
||||||
definitionsByGloss[gloss].definitionTags.push(tag);
|
glossDefinition.definitionTags.push(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!appendTo) {
|
if (appendTo === null) {
|
||||||
// result->expressions[ Expression1[ Reading1[ Tag1, Tag2 ] ], Expression2, ... ]
|
/*
|
||||||
if (!result.expressions.has(definition.expression)) {
|
Data layout:
|
||||||
result.expressions.set(definition.expression, new Map());
|
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);
|
||||||
}
|
}
|
||||||
if (!result.expressions.get(definition.expression).has(definition.reading)) {
|
|
||||||
result.expressions.get(definition.expression).set(definition.reading, []);
|
let termTagsMap = readingMap.get(reading);
|
||||||
|
if (typeof termTagsMap === 'undefined') {
|
||||||
|
termTagsMap = new Map();
|
||||||
|
readingMap.set(reading, termTagsMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tag of definition.termTags) {
|
for (const tag of definition.termTags) {
|
||||||
if (!result.expressions.get(definition.expression).get(definition.reading).find((existingTag) => existingTag.name === tag.name)) {
|
if (!termTagsMap.has(tag.name)) {
|
||||||
result.expressions.get(definition.expression).get(definition.reading).push(tag);
|
termTagsMap.set(tag.name, tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const gloss in definitionsByGloss) {
|
for (const definition of definitionsByGloss.values()) {
|
||||||
const definition = definitionsByGloss[gloss];
|
const only = [];
|
||||||
definition.only = [];
|
const expressionSet = definition.expression;
|
||||||
if (!utilSetEqual(definition.expression, result.expression)) {
|
const readingSet = definition.reading;
|
||||||
for (const expression of utilSetIntersection(definition.expression, result.expression)) {
|
if (!dictAreSetsEqual(expressionSet, resultExpressionSet)) {
|
||||||
definition.only.push(expression);
|
only.push(...dictGetSetIntersection(expressionSet, resultExpressionSet));
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!utilSetEqual(definition.reading, result.reading)) {
|
|
||||||
for (const reading of utilSetIntersection(definition.reading, result.reading)) {
|
|
||||||
definition.only.push(reading);
|
|
||||||
}
|
}
|
||||||
|
if (!dictAreSetsEqual(readingSet, resultReadingSet)) {
|
||||||
|
only.push(...dictGetSetIntersection(readingSet, resultReadingSet));
|
||||||
}
|
}
|
||||||
|
definition.only = only;
|
||||||
}
|
}
|
||||||
|
|
||||||
return definitionsByGloss;
|
return definitionsByGloss;
|
||||||
|
@ -491,15 +491,18 @@ function dictionaryErrorsShow(errors) {
|
|||||||
dialog.textContent = '';
|
dialog.textContent = '';
|
||||||
|
|
||||||
if (errors !== null && errors.length > 0) {
|
if (errors !== null && errors.length > 0) {
|
||||||
const uniqueErrors = {};
|
const uniqueErrors = new Map();
|
||||||
for (let e of errors) {
|
for (let e of errors) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
e = dictionaryErrorToString(e);
|
e = dictionaryErrorToString(e);
|
||||||
uniqueErrors[e] = hasOwn(uniqueErrors, e) ? uniqueErrors[e] + 1 : 1;
|
let count = uniqueErrors.get(e);
|
||||||
|
if (typeof count === 'undefined') {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
uniqueErrors.set(e, count + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const e in uniqueErrors) {
|
for (const [e, count] of uniqueErrors.entries()) {
|
||||||
const count = uniqueErrors[e];
|
|
||||||
const div = document.createElement('p');
|
const div = document.createElement('p');
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
div.textContent = `${e} `;
|
div.textContent = `${e} `;
|
||||||
|
@ -70,8 +70,8 @@ class Translator {
|
|||||||
return {sequencedDefinitions, defaultDefinitions};
|
return {sequencedDefinitions, defaultDefinitions};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMergedSecondarySearchResults(text, expressionsMap, secondarySearchTitles) {
|
async getMergedSecondarySearchResults(text, expressionsMap, secondarySearchDictionaries) {
|
||||||
if (secondarySearchTitles.length === 0) {
|
if (secondarySearchDictionaries.size === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ class Translator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const definitions = await this.database.findTermsExactBulk(expressionList, readingList, secondarySearchTitles);
|
const definitions = await this.database.findTermsExactBulk(expressionList, readingList, secondarySearchDictionaries);
|
||||||
for (const definition of definitions) {
|
for (const definition of definitions) {
|
||||||
const definitionTags = await this.expandTags(definition.definitionTags, definition.dictionary);
|
const definitionTags = await this.expandTags(definition.definitionTags, definition.dictionary);
|
||||||
definitionTags.push(dictTagBuildSource(definition.dictionary));
|
definitionTags.push(dictTagBuildSource(definition.dictionary));
|
||||||
@ -101,7 +101,7 @@ class Translator {
|
|||||||
return definitions;
|
return definitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMergedDefinition(text, dictionaries, sequencedDefinition, defaultDefinitions, secondarySearchTitles, mergedByTermIndices) {
|
async getMergedDefinition(text, dictionaries, sequencedDefinition, defaultDefinitions, secondarySearchDictionaries, mergedByTermIndices) {
|
||||||
const result = sequencedDefinition.definitions;
|
const result = sequencedDefinition.definitions;
|
||||||
const rawDefinitionsBySequence = sequencedDefinition.rawDefinitions;
|
const rawDefinitionsBySequence = sequencedDefinition.rawDefinitions;
|
||||||
|
|
||||||
@ -114,12 +114,11 @@ class Translator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const definitionsByGloss = dictTermsMergeByGloss(result, rawDefinitionsBySequence);
|
const definitionsByGloss = dictTermsMergeByGloss(result, rawDefinitionsBySequence);
|
||||||
const secondarySearchResults = await this.getMergedSecondarySearchResults(text, result.expressions, secondarySearchTitles);
|
const secondarySearchResults = await this.getMergedSecondarySearchResults(text, result.expressions, secondarySearchDictionaries);
|
||||||
|
|
||||||
dictTermsMergeByGloss(result, defaultDefinitions.concat(secondarySearchResults), definitionsByGloss, mergedByTermIndices);
|
dictTermsMergeByGloss(result, defaultDefinitions.concat(secondarySearchResults), definitionsByGloss, mergedByTermIndices);
|
||||||
|
|
||||||
for (const gloss in definitionsByGloss) {
|
for (const definition of definitionsByGloss.values()) {
|
||||||
const definition = definitionsByGloss[gloss];
|
|
||||||
dictTagsSort(definition.definitionTags);
|
dictTagsSort(definition.definitionTags);
|
||||||
result.definitions.push(definition);
|
result.definitions.push(definition);
|
||||||
}
|
}
|
||||||
@ -128,7 +127,8 @@ class Translator {
|
|||||||
|
|
||||||
const expressions = [];
|
const expressions = [];
|
||||||
for (const [expression, readingMap] of result.expressions.entries()) {
|
for (const [expression, readingMap] of result.expressions.entries()) {
|
||||||
for (const [reading, termTags] of readingMap.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);
|
const score = termTags.map((tag) => tag.score).reduce((p, v) => p + v, 0);
|
||||||
expressions.push(Translator.createExpression(expression, reading, dictTagsSort(termTags), Translator.scoreToTermFrequency(score)));
|
expressions.push(Translator.createExpression(expression, reading, dictTagsSort(termTags), Translator.scoreToTermFrequency(score)));
|
||||||
}
|
}
|
||||||
@ -141,14 +141,16 @@ class Translator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTerms(text, details, options) {
|
async findTerms(mode, text, details, options) {
|
||||||
switch (options.general.resultOutputMode) {
|
switch (mode) {
|
||||||
case 'group':
|
case 'group':
|
||||||
return await this.findTermsGrouped(text, details, options);
|
return await this.findTermsGrouped(text, details, options);
|
||||||
case 'merge':
|
case 'merge':
|
||||||
return await this.findTermsMerged(text, details, options);
|
return await this.findTermsMerged(text, details, options);
|
||||||
case 'split':
|
case 'split':
|
||||||
return await this.findTermsSplit(text, details, options);
|
return await this.findTermsSplit(text, details, options);
|
||||||
|
case 'simple':
|
||||||
|
return await this.findTermsSimple(text, details, options);
|
||||||
default:
|
default:
|
||||||
return [[], 0];
|
return [[], 0];
|
||||||
}
|
}
|
||||||
@ -156,11 +158,10 @@ class Translator {
|
|||||||
|
|
||||||
async findTermsGrouped(text, details, options) {
|
async findTermsGrouped(text, details, options) {
|
||||||
const dictionaries = dictEnabledSet(options);
|
const dictionaries = dictEnabledSet(options);
|
||||||
const titles = Object.keys(dictionaries);
|
|
||||||
const [definitions, length] = await this.findTermsInternal(text, dictionaries, details, options);
|
const [definitions, length] = await this.findTermsInternal(text, dictionaries, details, options);
|
||||||
|
|
||||||
const definitionsGrouped = dictTermsGroup(definitions, dictionaries);
|
const definitionsGrouped = dictTermsGroup(definitions, dictionaries);
|
||||||
await this.buildTermMeta(definitionsGrouped, titles);
|
await this.buildTermMeta(definitionsGrouped, dictionaries);
|
||||||
|
|
||||||
if (options.general.compactTags) {
|
if (options.general.compactTags) {
|
||||||
for (const definition of definitionsGrouped) {
|
for (const definition of definitionsGrouped) {
|
||||||
@ -173,8 +174,12 @@ class Translator {
|
|||||||
|
|
||||||
async findTermsMerged(text, details, options) {
|
async findTermsMerged(text, details, options) {
|
||||||
const dictionaries = dictEnabledSet(options);
|
const dictionaries = dictEnabledSet(options);
|
||||||
const secondarySearchTitles = Object.keys(options.dictionaries).filter((dict) => options.dictionaries[dict].allowSecondarySearches);
|
const secondarySearchDictionaries = new Map();
|
||||||
const titles = Object.keys(dictionaries);
|
for (const [title, dictionary] of dictionaries.entries()) {
|
||||||
|
if (!dictionary.allowSecondarySearches) { continue; }
|
||||||
|
secondarySearchDictionaries.set(title, dictionary);
|
||||||
|
}
|
||||||
|
|
||||||
const [definitions, length] = await this.findTermsInternal(text, dictionaries, details, options);
|
const [definitions, length] = await this.findTermsInternal(text, dictionaries, details, options);
|
||||||
const {sequencedDefinitions, defaultDefinitions} = await this.getSequencedDefinitions(definitions, options.general.mainDictionary);
|
const {sequencedDefinitions, defaultDefinitions} = await this.getSequencedDefinitions(definitions, options.general.mainDictionary);
|
||||||
const definitionsMerged = [];
|
const definitionsMerged = [];
|
||||||
@ -186,7 +191,7 @@ class Translator {
|
|||||||
dictionaries,
|
dictionaries,
|
||||||
sequencedDefinition,
|
sequencedDefinition,
|
||||||
defaultDefinitions,
|
defaultDefinitions,
|
||||||
secondarySearchTitles,
|
secondarySearchDictionaries,
|
||||||
mergedByTermIndices
|
mergedByTermIndices
|
||||||
);
|
);
|
||||||
definitionsMerged.push(result);
|
definitionsMerged.push(result);
|
||||||
@ -198,7 +203,7 @@ class Translator {
|
|||||||
definitionsMerged.push(groupedDefinition);
|
definitionsMerged.push(groupedDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.buildTermMeta(definitionsMerged, titles);
|
await this.buildTermMeta(definitionsMerged, dictionaries);
|
||||||
|
|
||||||
if (options.general.compactTags) {
|
if (options.general.compactTags) {
|
||||||
for (const definition of definitionsMerged) {
|
for (const definition of definitionsMerged) {
|
||||||
@ -211,25 +216,28 @@ class Translator {
|
|||||||
|
|
||||||
async findTermsSplit(text, details, options) {
|
async findTermsSplit(text, details, options) {
|
||||||
const dictionaries = dictEnabledSet(options);
|
const dictionaries = dictEnabledSet(options);
|
||||||
const titles = Object.keys(dictionaries);
|
|
||||||
const [definitions, length] = await this.findTermsInternal(text, dictionaries, details, options);
|
const [definitions, length] = await this.findTermsInternal(text, dictionaries, details, options);
|
||||||
|
|
||||||
await this.buildTermMeta(definitions, titles);
|
await this.buildTermMeta(definitions, dictionaries);
|
||||||
|
|
||||||
return [definitions, length];
|
return [definitions, length];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findTermsSimple(text, details, options) {
|
||||||
|
const dictionaries = dictEnabledSet(options);
|
||||||
|
return await this.findTermsInternal(text, dictionaries, details, options);
|
||||||
|
}
|
||||||
|
|
||||||
async findTermsInternal(text, dictionaries, details, options) {
|
async findTermsInternal(text, dictionaries, details, options) {
|
||||||
text = Translator.getSearchableText(text, options);
|
text = Translator.getSearchableText(text, options);
|
||||||
if (text.length === 0) {
|
if (text.length === 0) {
|
||||||
return [[], 0];
|
return [[], 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const titles = Object.keys(dictionaries);
|
|
||||||
const deinflections = (
|
const deinflections = (
|
||||||
details.wildcard ?
|
details.wildcard ?
|
||||||
await this.findTermWildcard(text, titles, details.wildcard) :
|
await this.findTermWildcard(text, dictionaries, details.wildcard) :
|
||||||
await this.findTermDeinflections(text, titles, options)
|
await this.findTermDeinflections(text, dictionaries, options)
|
||||||
);
|
);
|
||||||
|
|
||||||
let definitions = [];
|
let definitions = [];
|
||||||
@ -271,8 +279,8 @@ class Translator {
|
|||||||
return [definitions, length];
|
return [definitions, length];
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTermWildcard(text, titles, wildcard) {
|
async findTermWildcard(text, dictionaries, wildcard) {
|
||||||
const definitions = await this.database.findTermsBulk([text], titles, wildcard);
|
const definitions = await this.database.findTermsBulk([text], dictionaries, wildcard);
|
||||||
if (definitions.length === 0) {
|
if (definitions.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -287,7 +295,7 @@ class Translator {
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTermDeinflections(text, titles, options) {
|
async findTermDeinflections(text, dictionaries, options) {
|
||||||
const deinflections = this.getAllDeinflections(text, options);
|
const deinflections = this.getAllDeinflections(text, options);
|
||||||
|
|
||||||
if (deinflections.length === 0) {
|
if (deinflections.length === 0) {
|
||||||
@ -309,7 +317,7 @@ class Translator {
|
|||||||
deinflectionArray.push(deinflection);
|
deinflectionArray.push(deinflection);
|
||||||
}
|
}
|
||||||
|
|
||||||
const definitions = await this.database.findTermsBulk(uniqueDeinflectionTerms, titles, null);
|
const definitions = await this.database.findTermsBulk(uniqueDeinflectionTerms, dictionaries, null);
|
||||||
|
|
||||||
for (const definition of definitions) {
|
for (const definition of definitions) {
|
||||||
const definitionRules = Deinflector.rulesToRuleFlags(definition.rules);
|
const definitionRules = Deinflector.rulesToRuleFlags(definition.rules);
|
||||||
@ -399,17 +407,12 @@ class Translator {
|
|||||||
|
|
||||||
async findKanji(text, options) {
|
async findKanji(text, options) {
|
||||||
const dictionaries = dictEnabledSet(options);
|
const dictionaries = dictEnabledSet(options);
|
||||||
const titles = Object.keys(dictionaries);
|
const kanjiUnique = new Set();
|
||||||
const kanjiUnique = {};
|
|
||||||
const kanjiList = [];
|
|
||||||
for (const c of text) {
|
for (const c of text) {
|
||||||
if (!hasOwn(kanjiUnique, c)) {
|
kanjiUnique.add(c);
|
||||||
kanjiList.push(c);
|
|
||||||
kanjiUnique[c] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const definitions = await this.database.findKanjiBulk(kanjiList, titles);
|
const definitions = await this.database.findKanjiBulk([...kanjiUnique], dictionaries);
|
||||||
if (definitions.length === 0) {
|
if (definitions.length === 0) {
|
||||||
return definitions;
|
return definitions;
|
||||||
}
|
}
|
||||||
@ -429,12 +432,12 @@ class Translator {
|
|||||||
definition.stats = stats;
|
definition.stats = stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.buildKanjiMeta(definitions, titles);
|
await this.buildKanjiMeta(definitions, dictionaries);
|
||||||
|
|
||||||
return definitions;
|
return definitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildTermMeta(definitions, titles) {
|
async buildTermMeta(definitions, dictionaries) {
|
||||||
const terms = [];
|
const terms = [];
|
||||||
for (const definition of definitions) {
|
for (const definition of definitions) {
|
||||||
if (definition.expressions) {
|
if (definition.expressions) {
|
||||||
@ -468,7 +471,7 @@ class Translator {
|
|||||||
term.frequencies = [];
|
term.frequencies = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const metas = await this.database.findTermMetaBulk(expressionsUnique, titles);
|
const metas = await this.database.findTermMetaBulk(expressionsUnique, dictionaries);
|
||||||
for (const {expression, mode, data, dictionary, index} of metas) {
|
for (const {expression, mode, data, dictionary, index} of metas) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'freq':
|
case 'freq':
|
||||||
@ -480,14 +483,14 @@ class Translator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildKanjiMeta(definitions, titles) {
|
async buildKanjiMeta(definitions, dictionaries) {
|
||||||
const kanjiList = [];
|
const kanjiList = [];
|
||||||
for (const definition of definitions) {
|
for (const definition of definitions) {
|
||||||
kanjiList.push(definition.character);
|
kanjiList.push(definition.character);
|
||||||
definition.frequencies = [];
|
definition.frequencies = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const metas = await this.database.findKanjiMetaBulk(kanjiList, titles);
|
const metas = await this.database.findKanjiMetaBulk(kanjiList, dictionaries);
|
||||||
for (const {character, mode, data, dictionary, index} of metas) {
|
for (const {character, mode, data, dictionary, index} of metas) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'freq':
|
case 'freq':
|
||||||
@ -510,28 +513,29 @@ class Translator {
|
|||||||
const names = Object.keys(items);
|
const names = Object.keys(items);
|
||||||
const tagMetaList = await this.getTagMetaList(names, title);
|
const tagMetaList = await this.getTagMetaList(names, title);
|
||||||
|
|
||||||
const stats = {};
|
const statsGroups = new Map();
|
||||||
for (let i = 0; i < names.length; ++i) {
|
for (let i = 0; i < names.length; ++i) {
|
||||||
const name = names[i];
|
const name = names[i];
|
||||||
const meta = tagMetaList[i];
|
const meta = tagMetaList[i];
|
||||||
if (meta === null) { continue; }
|
if (meta === null) { continue; }
|
||||||
|
|
||||||
const category = meta.category;
|
const category = meta.category;
|
||||||
const group = (
|
let group = statsGroups.get(category);
|
||||||
hasOwn(stats, category) ?
|
if (typeof group === 'undefined') {
|
||||||
stats[category] :
|
group = [];
|
||||||
(stats[category] = [])
|
statsGroups.set(category, group);
|
||||||
);
|
}
|
||||||
|
|
||||||
const stat = Object.assign({}, meta, {name, value: items[name]});
|
const stat = Object.assign({}, meta, {name, value: items[name]});
|
||||||
group.push(dictTagSanitize(stat));
|
group.push(dictTagSanitize(stat));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stats = {};
|
||||||
const sortCompare = (a, b) => a.notes - b.notes;
|
const sortCompare = (a, b) => a.notes - b.notes;
|
||||||
for (const category in stats) {
|
for (const [category, group] of statsGroups.entries()) {
|
||||||
stats[category].sort(sortCompare);
|
group.sort(sortCompare);
|
||||||
|
stats[category] = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,32 +59,6 @@ function utilBackgroundFunctionIsolate(func) {
|
|||||||
return backgroundPage.utilFunctionIsolate(func);
|
return backgroundPage.utilFunctionIsolate(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
function utilStringHashCode(string) {
|
||||||
let hashCode = 0;
|
let hashCode = 0;
|
||||||
|
|
||||||
|
@ -72,19 +72,15 @@ class TextToSpeechAudio {
|
|||||||
const m = /^tts:[^#?]*\?([^#]*)/.exec(ttsUri);
|
const m = /^tts:[^#?]*\?([^#]*)/.exec(ttsUri);
|
||||||
if (m === null) { return null; }
|
if (m === null) { return null; }
|
||||||
|
|
||||||
const searchParameters = {};
|
const searchParameters = new URLSearchParams(m[1]);
|
||||||
for (const group of m[1].split('&')) {
|
const text = searchParameters.get('text');
|
||||||
const sep = group.indexOf('=');
|
let voice = searchParameters.get('voice');
|
||||||
if (sep < 0) { continue; }
|
if (text === null || voice === null) { return null; }
|
||||||
searchParameters[decodeURIComponent(group.substring(0, sep))] = decodeURIComponent(group.substring(sep + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!searchParameters.text) { return null; }
|
voice = audioGetTextToSpeechVoice(voice);
|
||||||
|
|
||||||
const voice = audioGetTextToSpeechVoice(searchParameters.voice);
|
|
||||||
if (voice === null) { return null; }
|
if (voice === null) { return null; }
|
||||||
|
|
||||||
return new TextToSpeechAudio(searchParameters.text, voice);
|
return new TextToSpeechAudio(text, voice);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ class TextScanner {
|
|||||||
onTouchEnd(e) {
|
onTouchEnd(e) {
|
||||||
if (
|
if (
|
||||||
this.primaryTouchIdentifier === null ||
|
this.primaryTouchIdentifier === null ||
|
||||||
TextScanner.getIndexOfTouch(e.changedTouches, this.primaryTouchIdentifier) < 0
|
TextScanner.getTouch(e.changedTouches, this.primaryTouchIdentifier) === null
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -181,13 +181,11 @@ class TextScanner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const touches = e.changedTouches;
|
const primaryTouch = TextScanner.getTouch(e.changedTouches, this.primaryTouchIdentifier);
|
||||||
const index = TextScanner.getIndexOfTouch(touches, this.primaryTouchIdentifier);
|
if (primaryTouch === null) {
|
||||||
if (index < 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryTouch = touches[index];
|
|
||||||
this.searchAt(primaryTouch.clientX, primaryTouch.clientY, 'touchMove');
|
this.searchAt(primaryTouch.clientX, primaryTouch.clientY, 'touchMove');
|
||||||
|
|
||||||
e.preventDefault(); // Disable scroll
|
e.preventDefault(); // Disable scroll
|
||||||
@ -356,13 +354,12 @@ class TextScanner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getIndexOfTouch(touchList, identifier) {
|
static getTouch(touchList, identifier) {
|
||||||
for (const i in touchList) {
|
for (const touch of touchList) {
|
||||||
const t = touchList[i];
|
if (touch.identifier === identifier) {
|
||||||
if (t.identifier === identifier) {
|
return touch;
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,9 @@ async function testDatabase1() {
|
|||||||
const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string'));
|
const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string'));
|
||||||
|
|
||||||
const title = testDictionaryIndex.title;
|
const title = testDictionaryIndex.title;
|
||||||
const titles = [title];
|
const titles = new Map([
|
||||||
|
[title, {priority: 0, allowSecondarySearches: false}]
|
||||||
|
]);
|
||||||
|
|
||||||
// Setup iteration data
|
// Setup iteration data
|
||||||
const iterations = [
|
const iterations = [
|
||||||
@ -815,7 +817,9 @@ async function testDatabase2() {
|
|||||||
const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string'));
|
const testDictionaryIndex = JSON.parse(await testDictionary.files['index.json'].async('string'));
|
||||||
|
|
||||||
const title = testDictionaryIndex.title;
|
const title = testDictionaryIndex.title;
|
||||||
const titles = [title];
|
const titles = new Map([
|
||||||
|
[title, {priority: 0, allowSecondarySearches: false}]
|
||||||
|
]);
|
||||||
|
|
||||||
// Setup database
|
// Setup database
|
||||||
const database = new Database();
|
const database = new Database();
|
||||||
|
Loading…
Reference in New Issue
Block a user