/*
* Copyright (C) 2023-2024 Yomitan Authors
* Copyright (C) 2021-2022 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 .
*/
import {getDisambiguations, getGroupedPronunciations, getPronunciationsOfType, getTermFrequency, groupTermTags} from '../dictionary/dictionary-data-util.js';
import {distributeFurigana, distributeFuriganaInflected} from '../language/ja/japanese.js';
/**
* Creates a compatibility representation of the specified data.
* @param {string} marker The marker that is being used for template rendering.
* @param {import('anki-templates-internal').CreateDetails} details Information which is used to generate the data.
* @returns {import('anki-templates').NoteData} An object used for rendering Anki templates.
*/
export function createAnkiNoteData(marker, {
dictionaryEntry,
resultOutputMode,
mode,
glossaryLayoutMode,
compactTags,
context,
media
}) {
const definition = createCachedValue(getDefinition.bind(null, dictionaryEntry, context, resultOutputMode));
const uniqueExpressions = createCachedValue(getUniqueExpressions.bind(null, dictionaryEntry));
const uniqueReadings = createCachedValue(getUniqueReadings.bind(null, dictionaryEntry));
const context2 = createCachedValue(getPublicContext.bind(null, context));
const pitches = createCachedValue(getPitches.bind(null, dictionaryEntry));
const pitchCount = createCachedValue(getPitchCount.bind(null, pitches));
const phoneticTranscriptions = createCachedValue(getPhoneticTranscriptions.bind(null, dictionaryEntry));
if (typeof media !== 'object' || media === null || Array.isArray(media)) {
media = {
audio: void 0,
screenshot: void 0,
clipboardImage: void 0,
clipboardText: void 0,
selectionText: void 0,
textFurigana: [],
dictionaryMedia: {}
};
}
/** @type {import('anki-templates').NoteData} */
const result = {
marker,
get definition() { return getCachedValue(definition); },
glossaryLayoutMode,
compactTags,
group: (resultOutputMode === 'group'),
merge: (resultOutputMode === 'merge'),
modeTermKanji: (mode === 'term-kanji'),
modeTermKana: (mode === 'term-kana'),
modeKanji: (mode === 'kanji'),
compactGlossaries: (glossaryLayoutMode === 'compact'),
get uniqueExpressions() { return getCachedValue(uniqueExpressions); },
get uniqueReadings() { return getCachedValue(uniqueReadings); },
get pitches() { return getCachedValue(pitches); },
get pitchCount() { return getCachedValue(pitchCount); },
get phoneticTranscriptions() { return getCachedValue(phoneticTranscriptions); },
get context() { return getCachedValue(context2); },
media,
dictionaryEntry
};
Object.defineProperty(result, 'dictionaryEntry', {
configurable: false,
enumerable: false,
writable: false,
value: dictionaryEntry
});
return result;
}
/**
* Creates a deferred-evaluation value.
* @template [T=unknown]
* @param {() => T} getter The function to invoke to get the return value.
* @returns {import('anki-templates-internal').CachedValue} An object which can be passed into `getCachedValue`.
*/
export function createCachedValue(getter) {
return {getter, hasValue: false, value: void 0};
}
/**
* Gets the value of a cached object.
* @template [T=unknown]
* @param {import('anki-templates-internal').CachedValue} item An object that was returned from `createCachedValue`.
* @returns {T} The result of evaluating the getter, which is cached after the first invocation.
*/
export function getCachedValue(item) {
if (item.hasValue) { return /** @type {T} */ (item.value); }
const value = item.getter();
item.value = value;
item.hasValue = true;
return value;
}
// Private
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {?import('dictionary').TermSource}
*/
function getPrimarySource(dictionaryEntry) {
for (const headword of dictionaryEntry.headwords) {
for (const source of headword.sources) {
if (source.isPrimary) { return source; }
}
}
return null;
}
/**
* @param {import('dictionary').DictionaryEntry} dictionaryEntry
* @returns {string[]}
*/
function getUniqueExpressions(dictionaryEntry) {
if (dictionaryEntry.type === 'term') {
const results = new Set();
for (const {term} of dictionaryEntry.headwords) {
results.add(term);
}
return [...results];
} else {
return [];
}
}
/**
* @param {import('dictionary').DictionaryEntry} dictionaryEntry
* @returns {string[]}
*/
function getUniqueReadings(dictionaryEntry) {
if (dictionaryEntry.type === 'term') {
const results = new Set();
for (const {reading} of dictionaryEntry.headwords) {
results.add(reading);
}
return [...results];
} else {
return [];
}
}
/**
* @param {import('anki-templates-internal').Context} context
* @returns {import('anki-templates').Context}
*/
function getPublicContext(context) {
let {documentTitle, query, fullQuery} = context;
if (typeof documentTitle !== 'string') { documentTitle = ''; }
return {
query,
fullQuery,
document: {
title: documentTitle
}
};
}
/**
* @param {import('dictionary').TermDictionaryEntry|import('dictionary').KanjiDictionaryEntry} dictionaryEntry
* @returns {number[]}
*/
function getFrequencyNumbers(dictionaryEntry) {
let previousDictionary;
const frequencies = [];
for (const {dictionary, frequency, displayValue} of dictionaryEntry.frequencies) {
if (dictionary === previousDictionary) {
continue;
}
previousDictionary = dictionary;
if (displayValue !== null) {
const frequencyMatch = displayValue.match(/\d+/);
if (frequencyMatch !== null) {
frequencies.push(Number.parseInt(frequencyMatch[0], 10));
continue;
}
}
frequencies.push(frequency);
}
return frequencies;
}
/**
* @param {import('dictionary').TermDictionaryEntry|import('dictionary').KanjiDictionaryEntry} dictionaryEntry
* @returns {number}
*/
function getFrequencyHarmonic(dictionaryEntry) {
const frequencies = getFrequencyNumbers(dictionaryEntry);
if (frequencies.length === 0) {
return -1;
}
let total = 0;
for (const frequency of frequencies) {
total += 1 / frequency;
}
return Math.floor(frequencies.length / total);
}
/**
* @param {import('dictionary').TermDictionaryEntry|import('dictionary').KanjiDictionaryEntry} dictionaryEntry
* @returns {number}
*/
function getFrequencyAverage(dictionaryEntry) {
const frequencies = getFrequencyNumbers(dictionaryEntry);
if (frequencies.length === 0) {
return -1;
}
let total = 0;
for (const frequency of frequencies) {
total += frequency;
}
return Math.floor(total / frequencies.length);
}
/**
* @param {import('dictionary').DictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').PitchGroup[]}
*/
function getPitches(dictionaryEntry) {
/** @type {import('anki-templates').PitchGroup[]} */
const results = [];
if (dictionaryEntry.type === 'term') {
for (const {dictionary, pronunciations} of getGroupedPronunciations(dictionaryEntry)) {
/** @type {import('anki-templates').Pitch[]} */
const pitches = [];
for (const groupedPronunciation of pronunciations) {
const {pronunciation} = groupedPronunciation;
if (pronunciation.type !== 'pitch-accent') { continue; }
const {position, nasalPositions, devoicePositions, tags} = pronunciation;
const {terms, reading, exclusiveTerms, exclusiveReadings} = groupedPronunciation;
pitches.push({
expressions: terms,
reading,
position,
nasalPositions,
devoicePositions,
tags: convertPitchTags(tags),
exclusiveExpressions: exclusiveTerms,
exclusiveReadings
});
}
results.push({dictionary, pitches});
}
}
return results;
}
/**
* @param {import('dictionary').DictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').TranscriptionGroup[]}
*/
function getPhoneticTranscriptions(dictionaryEntry) {
const results = [];
if (dictionaryEntry.type === 'term') {
for (const {dictionary, pronunciations} of getGroupedPronunciations(dictionaryEntry)) {
const phoneticTranscriptions = [];
for (const groupedPronunciation of pronunciations) {
const {pronunciation} = groupedPronunciation;
if (pronunciation.type !== 'phonetic-transcription') { continue; }
const {ipa, tags} = pronunciation;
const {terms, reading, exclusiveTerms, exclusiveReadings} = groupedPronunciation;
phoneticTranscriptions.push({
expressions: terms,
reading,
ipa,
tags,
exclusiveExpressions: exclusiveTerms,
exclusiveReadings
});
}
results.push({dictionary, phoneticTranscriptions});
}
}
return results;
}
/**
* @param {import('anki-templates-internal').CachedValue} cachedPitches
* @returns {number}
*/
function getPitchCount(cachedPitches) {
const pitches = getCachedValue(cachedPitches);
return pitches.reduce((i, v) => i + v.pitches.length, 0);
}
/**
* @param {import('dictionary').DictionaryEntry} dictionaryEntry
* @param {import('anki-templates-internal').Context} context
* @param {import('settings').ResultOutputMode} resultOutputMode
* @returns {import('anki-templates').DictionaryEntry}
*/
function getDefinition(dictionaryEntry, context, resultOutputMode) {
switch (dictionaryEntry.type) {
case 'term':
return getTermDefinition(dictionaryEntry, context, resultOutputMode);
case 'kanji':
return getKanjiDefinition(dictionaryEntry, context);
default:
return /** @type {import('anki-templates').UnknownDictionaryEntry} */ ({});
}
}
/**
* @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
* @param {import('anki-templates-internal').Context} context
* @returns {import('anki-templates').KanjiDictionaryEntry}
*/
function getKanjiDefinition(dictionaryEntry, context) {
const {character, dictionary, onyomi, kunyomi, definitions} = dictionaryEntry;
let {url} = context;
if (typeof url !== 'string') { url = ''; }
const stats = createCachedValue(getKanjiStats.bind(null, dictionaryEntry));
const tags = createCachedValue(convertTags.bind(null, dictionaryEntry.tags));
const frequencies = createCachedValue(getKanjiFrequencies.bind(null, dictionaryEntry));
const frequencyHarmonic = createCachedValue(getFrequencyHarmonic.bind(null, dictionaryEntry));
const frequencyAverage = createCachedValue(getFrequencyAverage.bind(null, dictionaryEntry));
const cloze = createCachedValue(getCloze.bind(null, dictionaryEntry, context));
return {
type: 'kanji',
character,
dictionary,
onyomi,
kunyomi,
glossary: definitions,
get tags() { return getCachedValue(tags); },
get stats() { return getCachedValue(stats); },
get frequencies() { return getCachedValue(frequencies); },
get frequencyHarmonic() { return getCachedValue(frequencyHarmonic); },
get frequencyAverage() { return getCachedValue(frequencyAverage); },
url,
get cloze() { return getCachedValue(cloze); }
};
}
/**
* @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').KanjiStatGroups}
*/
function getKanjiStats(dictionaryEntry) {
/** @type {import('anki-templates').KanjiStatGroups} */
const results = {};
for (const [key, value] of Object.entries(dictionaryEntry.stats)) {
results[key] = value.map(convertKanjiStat);
}
return results;
}
/**
* @param {import('dictionary').KanjiStat} kanjiStat
* @returns {import('anki-templates').KanjiStat}
*/
function convertKanjiStat({name, category, content, order, score, dictionary, value}) {
return {
name,
category,
notes: content,
order,
score,
dictionary,
value
};
}
/**
* @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').KanjiFrequency[]}
*/
function getKanjiFrequencies(dictionaryEntry) {
/** @type {import('anki-templates').KanjiFrequency[]} */
const results = [];
for (const {index, dictionary, dictionaryIndex, dictionaryPriority, character, frequency, displayValue} of dictionaryEntry.frequencies) {
results.push({
index,
dictionary,
dictionaryOrder: {
index: dictionaryIndex,
priority: dictionaryPriority
},
character,
frequency: displayValue !== null ? displayValue : frequency
});
}
return results;
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {import('anki-templates-internal').Context} context
* @param {import('settings').ResultOutputMode} resultOutputMode
* @returns {import('anki-templates').TermDictionaryEntry}
*/
function getTermDefinition(dictionaryEntry, context, resultOutputMode) {
/** @type {import('anki-templates').TermDictionaryEntryType} */
let type = 'term';
switch (resultOutputMode) {
case 'group': type = 'termGrouped'; break;
case 'merge': type = 'termMerged'; break;
}
const {inflectionRuleChainCandidates, score, dictionaryIndex, dictionaryPriority, sourceTermExactMatchCount, definitions} = dictionaryEntry;
let {url} = context;
if (typeof url !== 'string') { url = ''; }
const primarySource = getPrimarySource(dictionaryEntry);
const dictionaryNames = createCachedValue(getTermDictionaryNames.bind(null, dictionaryEntry));
const commonInfo = createCachedValue(getTermDictionaryEntryCommonInfo.bind(null, dictionaryEntry, type));
const termTags = createCachedValue(getTermTags.bind(null, dictionaryEntry, type));
const expressions = createCachedValue(getTermExpressions.bind(null, dictionaryEntry));
const frequencies = createCachedValue(getTermFrequencies.bind(null, dictionaryEntry));
const frequencyHarmonic = createCachedValue(getFrequencyHarmonic.bind(null, dictionaryEntry));
const frequencyAverage = createCachedValue(getFrequencyAverage.bind(null, dictionaryEntry));
const pitches = createCachedValue(getTermPitches.bind(null, dictionaryEntry));
const phoneticTranscriptions = createCachedValue(getTermPhoneticTranscriptions.bind(null, dictionaryEntry));
const glossary = createCachedValue(getTermGlossaryArray.bind(null, dictionaryEntry, type));
const cloze = createCachedValue(getCloze.bind(null, dictionaryEntry, context));
const furiganaSegments = createCachedValue(getTermFuriganaSegments.bind(null, dictionaryEntry, type));
const sequence = createCachedValue(getTermDictionaryEntrySequence.bind(null, dictionaryEntry));
return {
type,
id: (type === 'term' && definitions.length > 0 ? definitions[0].id : void 0),
source: (primarySource !== null ? primarySource.transformedText : null),
rawSource: (primarySource !== null ? primarySource.originalText : null),
sourceTerm: (type !== 'termMerged' ? (primarySource !== null ? primarySource.deinflectedText : null) : void 0),
inflectionRuleChainCandidates,
score,
isPrimary: (type === 'term' ? dictionaryEntry.isPrimary : void 0),
get sequence() { return getCachedValue(sequence); },
get dictionary() { return getCachedValue(dictionaryNames)[0]; },
dictionaryOrder: {
index: dictionaryIndex,
priority: dictionaryPriority
},
get dictionaryNames() { return getCachedValue(dictionaryNames); },
get expression() {
const {uniqueTerms} = getCachedValue(commonInfo);
return (type === 'term' || type === 'termGrouped' ? uniqueTerms[0] : uniqueTerms);
},
get reading() {
const {uniqueReadings} = getCachedValue(commonInfo);
return (type === 'term' || type === 'termGrouped' ? uniqueReadings[0] : uniqueReadings);
},
get expressions() { return getCachedValue(expressions); },
get glossary() { return getCachedValue(glossary); },
get definitionTags() { return type === 'term' ? getCachedValue(commonInfo).definitionTags : void 0; },
get termTags() { return getCachedValue(termTags); },
get definitions() { return getCachedValue(commonInfo).definitions; },
get frequencies() { return getCachedValue(frequencies); },
get frequencyHarmonic() { return getCachedValue(frequencyHarmonic); },
get frequencyAverage() { return getCachedValue(frequencyAverage); },
get pitches() { return getCachedValue(pitches); },
get phoneticTranscriptions() { return getCachedValue(phoneticTranscriptions); },
sourceTermExactMatchCount,
url,
get cloze() { return getCachedValue(cloze); },
get furiganaSegments() { return getCachedValue(furiganaSegments); }
};
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {string[]}
*/
function getTermDictionaryNames(dictionaryEntry) {
const dictionaryNames = new Set();
for (const {dictionary} of dictionaryEntry.definitions) {
dictionaryNames.add(dictionary);
}
return [...dictionaryNames];
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {import('anki-templates').TermDictionaryEntryType} type
* @returns {import('anki-templates').TermDictionaryEntryCommonInfo}
*/
function getTermDictionaryEntryCommonInfo(dictionaryEntry, type) {
const merged = (type === 'termMerged');
const hasDefinitions = (type !== 'term');
/** @type {Set} */
const allTermsSet = new Set();
/** @type {Set} */
const allReadingsSet = new Set();
for (const {term, reading} of dictionaryEntry.headwords) {
allTermsSet.add(term);
allReadingsSet.add(reading);
}
const uniqueTerms = [...allTermsSet];
const uniqueReadings = [...allReadingsSet];
/** @type {import('anki-templates').TermDefinition[]} */
const definitions = [];
/** @type {import('anki-templates').Tag[]} */
const definitionTags = [];
for (const {tags, headwordIndices, entries, dictionary, sequences} of dictionaryEntry.definitions) {
const definitionTags2 = [];
for (const tag of tags) {
definitionTags.push(convertTag(tag));
definitionTags2.push(convertTag(tag));
}
if (!hasDefinitions) { continue; }
const only = merged ? getDisambiguations(dictionaryEntry.headwords, headwordIndices, allTermsSet, allReadingsSet) : void 0;
definitions.push({
sequence: sequences[0],
dictionary,
glossary: entries,
definitionTags: definitionTags2,
only
});
}
return {
uniqueTerms,
uniqueReadings,
definitionTags,
definitions: hasDefinitions ? definitions : void 0
};
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').TermFrequency[]}
*/
function getTermFrequencies(dictionaryEntry) {
const results = [];
const {headwords} = dictionaryEntry;
for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, hasReading, frequency, displayValue} of dictionaryEntry.frequencies) {
const {term, reading} = headwords[headwordIndex];
results.push({
index: results.length,
expressionIndex: headwordIndex,
dictionary,
dictionaryOrder: {
index: dictionaryIndex,
priority: dictionaryPriority
},
expression: term,
reading,
hasReading,
frequency: displayValue !== null ? displayValue : frequency
});
}
return results;
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').TermPitchAccent[]}
*/
function getTermPitches(dictionaryEntry) {
const results = [];
const {headwords} = dictionaryEntry;
for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of dictionaryEntry.pronunciations) {
const {term, reading} = headwords[headwordIndex];
const pitches = getPronunciationsOfType(pronunciations, 'pitch-accent');
const cachedPitches = createCachedValue(getTermPitchesInner.bind(null, pitches));
results.push({
index: results.length,
expressionIndex: headwordIndex,
dictionary,
dictionaryOrder: {
index: dictionaryIndex,
priority: dictionaryPriority
},
expression: term,
reading,
get pitches() { return getCachedValue(cachedPitches); }
});
}
return results;
}
/**
* @param {import('dictionary').PitchAccent[]} pitches
* @returns {import('anki-templates').PitchAccent[]}
*/
function getTermPitchesInner(pitches) {
const results = [];
for (const {position, tags} of pitches) {
const cachedTags = createCachedValue(convertTags.bind(null, tags));
results.push({
position,
get tags() { return getCachedValue(cachedTags); }
});
}
return results;
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').TermPhoneticTranscription[]}
*/
function getTermPhoneticTranscriptions(dictionaryEntry) {
const results = [];
const {headwords} = dictionaryEntry;
for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of dictionaryEntry.pronunciations) {
const {term, reading} = headwords[headwordIndex];
const phoneticTranscriptions = getPronunciationsOfType(pronunciations, 'phonetic-transcription');
const termPhoneticTranscriptions = getTermPhoneticTranscriptionsInner(phoneticTranscriptions);
results.push({
index: results.length,
expressionIndex: headwordIndex,
dictionary,
dictionaryOrder: {
index: dictionaryIndex,
priority: dictionaryPriority
},
expression: term,
reading,
get phoneticTranscriptions() { return termPhoneticTranscriptions; }
});
}
return results;
}
/**
* @param {import('dictionary').PhoneticTranscription[]} phoneticTranscriptions
* @returns {import('anki-templates').PhoneticTranscription[]}
*/
function getTermPhoneticTranscriptionsInner(phoneticTranscriptions) {
const results = [];
for (const {ipa, tags} of phoneticTranscriptions) {
const cachedTags = createCachedValue(convertTags.bind(null, tags));
results.push({
ipa,
get tags() { return getCachedValue(cachedTags); }
});
}
return results;
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').TermHeadword[]}
*/
function getTermExpressions(dictionaryEntry) {
const results = [];
const {headwords} = dictionaryEntry;
for (let i = 0, ii = headwords.length; i < ii; ++i) {
const {term, reading, tags, sources: [{deinflectedText}], wordClasses} = headwords[i];
const termTags = createCachedValue(convertTags.bind(null, tags));
const frequencies = createCachedValue(getTermExpressionFrequencies.bind(null, dictionaryEntry, i));
const pitches = createCachedValue(getTermExpressionPitches.bind(null, dictionaryEntry, i));
const termFrequency = createCachedValue(getTermExpressionTermFrequency.bind(null, termTags));
const furiganaSegments = createCachedValue(getTermHeadwordFuriganaSegments.bind(null, term, reading));
const item = {
sourceTerm: deinflectedText,
expression: term,
reading,
get termTags() { return getCachedValue(termTags); },
get frequencies() { return getCachedValue(frequencies); },
get pitches() { return getCachedValue(pitches); },
get furiganaSegments() { return getCachedValue(furiganaSegments); },
get termFrequency() { return getCachedValue(termFrequency); },
wordClasses
};
results.push(item);
}
return results;
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {number} i
* @returns {import('anki-templates').TermFrequency[]}
*/
function getTermExpressionFrequencies(dictionaryEntry, i) {
const results = [];
const {headwords, frequencies} = dictionaryEntry;
for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, hasReading, frequency, displayValue} of frequencies) {
if (headwordIndex !== i) { continue; }
const {term, reading} = headwords[headwordIndex];
results.push({
index: results.length,
expressionIndex: headwordIndex,
dictionary,
dictionaryOrder: {
index: dictionaryIndex,
priority: dictionaryPriority
},
expression: term,
reading,
hasReading,
frequency: displayValue !== null ? displayValue : frequency
});
}
return results;
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {number} i
* @returns {import('anki-templates').TermPitchAccent[]}
*/
function getTermExpressionPitches(dictionaryEntry, i) {
const results = [];
const {headwords, pronunciations: termPronunciations} = dictionaryEntry;
for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of termPronunciations) {
if (headwordIndex !== i) { continue; }
const {term, reading} = headwords[headwordIndex];
const pitches = getPronunciationsOfType(pronunciations, 'pitch-accent');
const cachedPitches = createCachedValue(getTermPitchesInner.bind(null, pitches));
results.push({
index: results.length,
expressionIndex: headwordIndex,
dictionary,
dictionaryOrder: {
index: dictionaryIndex,
priority: dictionaryPriority
},
expression: term,
reading,
get pitches() { return getCachedValue(cachedPitches); }
});
}
return results;
}
/**
* @param {import('anki-templates-internal').CachedValue} cachedTermTags
* @returns {import('anki-templates').TermFrequencyType}
*/
function getTermExpressionTermFrequency(cachedTermTags) {
const termTags = getCachedValue(cachedTermTags);
return getTermFrequency(termTags);
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {import('anki-templates').TermDictionaryEntryType} type
* @returns {import('dictionary-data').TermGlossary[]|undefined}
*/
function getTermGlossaryArray(dictionaryEntry, type) {
if (type === 'term') {
const results = [];
for (const {entries} of dictionaryEntry.definitions) {
results.push(...entries);
}
return results;
}
return void 0;
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {import('anki-templates').TermDictionaryEntryType} type
* @returns {import('anki-templates').Tag[]|undefined}
*/
function getTermTags(dictionaryEntry, type) {
if (type !== 'termMerged') {
const results = [];
for (const {tag} of groupTermTags(dictionaryEntry)) {
results.push(convertTag(tag));
}
return results;
}
return void 0;
}
/**
* @param {import('dictionary').Tag[]} tags
* @returns {import('anki-templates').Tag[]}
*/
function convertTags(tags) {
const results = [];
for (const tag of tags) {
results.push(convertTag(tag));
}
return results;
}
/**
* @param {import('dictionary').Tag} tag
* @returns {import('anki-templates').Tag}
*/
function convertTag({name, category, content, order, score, dictionaries, redundant}) {
return {
name,
category,
notes: (content.length > 0 ? content[0] : ''),
order,
score,
dictionary: (dictionaries.length > 0 ? dictionaries[0] : ''),
redundant
};
}
/**
* @param {import('dictionary').Tag[]} tags
* @returns {import('anki-templates').PitchTag[]}
*/
function convertPitchTags(tags) {
const results = [];
for (const tag of tags) {
results.push(convertPitchTag(tag));
}
return results;
}
/**
* @param {import('dictionary').Tag} tag
* @returns {import('anki-templates').PitchTag}
*/
function convertPitchTag({name, category, content, order, score, dictionaries, redundant}) {
return {
name,
category,
order,
score,
content: [...content],
dictionaries: [...dictionaries],
redundant
};
}
/**
* @param {import('dictionary').DictionaryEntry} dictionaryEntry
* @param {import('anki-templates-internal').Context} context
* @returns {import('anki-templates').Cloze}
*/
function getCloze(dictionaryEntry, context) {
let originalText = '';
let term = '';
let reading = '';
switch (dictionaryEntry.type) {
case 'term':
{
term = dictionaryEntry.headwords[0].term;
reading = dictionaryEntry.headwords[0].reading;
const primarySource = getPrimarySource(dictionaryEntry);
if (primarySource !== null) { originalText = primarySource.originalText; }
}
break;
case 'kanji':
originalText = dictionaryEntry.character;
break;
}
const {sentence} = context;
let text;
let offset;
if (typeof sentence === 'object' && sentence !== null) {
({text, offset} = sentence);
}
if (typeof text !== 'string') { text = ''; }
if (typeof offset !== 'number') { offset = 0; }
const textSegments = [];
for (const {text: text2, reading: reading2} of distributeFuriganaInflected(term, reading, text.substring(offset, offset + originalText.length))) {
textSegments.push(reading2.length > 0 ? reading2 : text2);
}
return {
sentence: text,
prefix: text.substring(0, offset),
body: text.substring(offset, offset + originalText.length),
bodyKana: textSegments.join(''),
suffix: text.substring(offset + originalText.length)
};
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {import('anki-templates').TermDictionaryEntryType} type
* @returns {import('anki-templates').FuriganaSegment[]|undefined}
*/
function getTermFuriganaSegments(dictionaryEntry, type) {
if (type === 'term') {
for (const {term, reading} of dictionaryEntry.headwords) {
return getTermHeadwordFuriganaSegments(term, reading);
}
}
return void 0;
}
/**
* @param {string} term
* @param {string} reading
* @returns {import('anki-templates').FuriganaSegment[]}
*/
function getTermHeadwordFuriganaSegments(term, reading) {
/** @type {import('anki-templates').FuriganaSegment[]} */
const result = [];
for (const {text, reading: reading2} of distributeFurigana(term, reading)) {
result.push({text, furigana: reading2});
}
return result;
}
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {number}
*/
function getTermDictionaryEntrySequence(dictionaryEntry) {
let hasSequence = false;
let mainSequence = -1;
if (!dictionaryEntry.isPrimary) { return mainSequence; }
for (const {sequences} of dictionaryEntry.definitions) {
const sequence = sequences[0];
if (!hasSequence) {
mainSequence = sequence;
hasSequence = true;
if (mainSequence === -1) { break; }
} else if (mainSequence !== sequence) {
mainSequence = -1;
break;
}
}
return mainSequence;
}