yomitan-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +485 -0
- package/dist/anki-connect-BQyCGW3O.cjs +513 -0
- package/dist/anki-connect-BQyCGW3O.cjs.map +1 -0
- package/dist/anki-connect-CPPuhyiQ.js +6 -0
- package/dist/anki-connect-DbrQHphS.js +495 -0
- package/dist/anki-connect-DbrQHphS.js.map +1 -0
- package/dist/anki-connect-DcheJrp-.cjs +6 -0
- package/dist/anki.cjs +1758 -0
- package/dist/anki.cjs.map +1 -0
- package/dist/anki.d.cts +751 -0
- package/dist/anki.d.cts.map +1 -0
- package/dist/anki.d.ts +751 -0
- package/dist/anki.d.ts.map +1 -0
- package/dist/anki.js +1751 -0
- package/dist/anki.js.map +1 -0
- package/dist/audio-D9DvYyB7.d.cts +48 -0
- package/dist/audio-D9DvYyB7.d.cts.map +1 -0
- package/dist/audio-DQulUkDM.d.ts +48 -0
- package/dist/audio-DQulUkDM.d.ts.map +1 -0
- package/dist/audio-url-generator-BXvQaqUi.cjs +4 -0
- package/dist/audio-url-generator-Dy2hb2Mm.cjs +414 -0
- package/dist/audio-url-generator-Dy2hb2Mm.cjs.map +1 -0
- package/dist/audio-url-generator-Qi0rfzHz.js +4 -0
- package/dist/audio-url-generator-pFQAB5Nb.js +390 -0
- package/dist/audio-url-generator-pFQAB5Nb.js.map +1 -0
- package/dist/audio.cjs +7 -0
- package/dist/audio.d.cts +86 -0
- package/dist/audio.d.cts.map +1 -0
- package/dist/audio.d.ts +86 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +4 -0
- package/dist/batch-processor-BR-gB3H3.js +84 -0
- package/dist/batch-processor-BR-gB3H3.js.map +1 -0
- package/dist/batch-processor-CSF1acTw.cjs +3 -0
- package/dist/batch-processor-DFqM_L-_.cjs +91 -0
- package/dist/batch-processor-DFqM_L-_.cjs.map +1 -0
- package/dist/batch-processor-Quo9jUyf.js +3 -0
- package/dist/chunk-BCwAaXi7.cjs +31 -0
- package/dist/cjk-util-Dp0ZU0sh.cjs +167 -0
- package/dist/cjk-util-Dp0ZU0sh.cjs.map +1 -0
- package/dist/cjk-util-DubXBGDG.js +94 -0
- package/dist/cjk-util-DubXBGDG.js.map +1 -0
- package/dist/core-BUpclilG.d.cts +102 -0
- package/dist/core-BUpclilG.d.cts.map +1 -0
- package/dist/core-DFUj5GtA.d.ts +102 -0
- package/dist/core-DFUj5GtA.d.ts.map +1 -0
- package/dist/database.cjs +7 -0
- package/dist/database.d.cts +4 -0
- package/dist/database.d.ts +4 -0
- package/dist/database.js +5 -0
- package/dist/dictionary-D7l-qFt1.d.cts +316 -0
- package/dist/dictionary-D7l-qFt1.d.cts.map +1 -0
- package/dist/dictionary-_vzfBLWi.d.ts +316 -0
- package/dist/dictionary-_vzfBLWi.d.ts.map +1 -0
- package/dist/dictionary-data-util-CHnRdYZ9.cjs +378 -0
- package/dist/dictionary-data-util-CHnRdYZ9.cjs.map +1 -0
- package/dist/dictionary-data-util-CfOLfEDE.js +323 -0
- package/dist/dictionary-data-util-CfOLfEDE.js.map +1 -0
- package/dist/dictionary-database-BDC2f9zc.d.ts +58 -0
- package/dist/dictionary-database-BDC2f9zc.d.ts.map +1 -0
- package/dist/dictionary-database-CU4TsvCC.js +393 -0
- package/dist/dictionary-database-CU4TsvCC.js.map +1 -0
- package/dist/dictionary-database-DsOi04Sg.d.cts +58 -0
- package/dist/dictionary-database-DsOi04Sg.d.cts.map +1 -0
- package/dist/dictionary-database-lvFvftnO.cjs +412 -0
- package/dist/dictionary-database-lvFvftnO.cjs.map +1 -0
- package/dist/dictionary-importer-BkQQSBhm.d.ts +237 -0
- package/dist/dictionary-importer-BkQQSBhm.d.ts.map +1 -0
- package/dist/dictionary-importer-Cen1z6co.js +1821 -0
- package/dist/dictionary-importer-Cen1z6co.js.map +1 -0
- package/dist/dictionary-importer-DYmmWmcX.cjs +8 -0
- package/dist/dictionary-importer-Da3AuTZw.d.cts +237 -0
- package/dist/dictionary-importer-Da3AuTZw.d.cts.map +1 -0
- package/dist/dictionary-importer-Dhn75iZ4.cjs +1834 -0
- package/dist/dictionary-importer-Dhn75iZ4.cjs.map +1 -0
- package/dist/dictionary-importer-xWkel0h-.js +8 -0
- package/dist/dictionary-update-checker-BNE4pGTx.js +4 -0
- package/dist/dictionary-update-checker-Byjvifd2.cjs +75 -0
- package/dist/dictionary-update-checker-Byjvifd2.cjs.map +1 -0
- package/dist/dictionary-update-checker-YdpalZ41.cjs +4 -0
- package/dist/dictionary-update-checker-kKukiovj.js +69 -0
- package/dist/dictionary-update-checker-kKukiovj.js.map +1 -0
- package/dist/display-generator-BGVWiI0t.js +746 -0
- package/dist/display-generator-BGVWiI0t.js.map +1 -0
- package/dist/display-generator-BMQmG5Ov.cjs +9 -0
- package/dist/display-generator-BxZ7mBjP.js +9 -0
- package/dist/display-generator-DyP-HNzP.cjs +758 -0
- package/dist/display-generator-DyP-HNzP.cjs.map +1 -0
- package/dist/errors-BSezaJwm.cjs +35 -0
- package/dist/errors-BSezaJwm.cjs.map +1 -0
- package/dist/errors-DuuDSO5N.js +22 -0
- package/dist/errors-DuuDSO5N.js.map +1 -0
- package/dist/frequency-ranking-BXjfhhUQ.js +3 -0
- package/dist/frequency-ranking-Cx1kkIrw.cjs +3 -0
- package/dist/frequency-ranking-DEJMTMdg.js +159 -0
- package/dist/frequency-ranking-DEJMTMdg.js.map +1 -0
- package/dist/frequency-ranking-DVYxTXN-.cjs +166 -0
- package/dist/frequency-ranking-DVYxTXN-.cjs.map +1 -0
- package/dist/furigana-5HK97CY8.js +4 -0
- package/dist/furigana-9bBI9-qe.d.ts +47 -0
- package/dist/furigana-9bBI9-qe.d.ts.map +1 -0
- package/dist/furigana-B3-0y231.js +471 -0
- package/dist/furigana-B3-0y231.js.map +1 -0
- package/dist/furigana-CjOhzvZt.d.cts +47 -0
- package/dist/furigana-CjOhzvZt.d.cts.map +1 -0
- package/dist/furigana-DpZLcues.cjs +609 -0
- package/dist/furigana-DpZLcues.cjs.map +1 -0
- package/dist/furigana-h3v2ub4-.cjs +4 -0
- package/dist/import.cjs +12 -0
- package/dist/import.d.cts +107 -0
- package/dist/import.d.cts.map +1 -0
- package/dist/import.d.ts +107 -0
- package/dist/import.d.ts.map +1 -0
- package/dist/import.js +9 -0
- package/dist/index.cjs +275 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +211 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +211 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +238 -0
- package/dist/index.js.map +1 -0
- package/dist/json-DGd-cunA.js +17 -0
- package/dist/json-DGd-cunA.js.map +1 -0
- package/dist/json-DKWp-B7Y.cjs +30 -0
- package/dist/json-DKWp-B7Y.cjs.map +1 -0
- package/dist/language-KN_u-nTR.d.ts +104 -0
- package/dist/language-KN_u-nTR.d.ts.map +1 -0
- package/dist/language-xAbQxgXc.d.cts +104 -0
- package/dist/language-xAbQxgXc.d.cts.map +1 -0
- package/dist/language.cjs +15626 -0
- package/dist/language.cjs.map +1 -0
- package/dist/language.d.cts +959 -0
- package/dist/language.d.cts.map +1 -0
- package/dist/language.d.ts +959 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +15522 -0
- package/dist/language.js.map +1 -0
- package/dist/log-D8KtR3aP.cjs +67 -0
- package/dist/log-D8KtR3aP.cjs.map +1 -0
- package/dist/log-hgSll-dS.js +60 -0
- package/dist/log-hgSll-dS.js.map +1 -0
- package/dist/lookup.cjs +13 -0
- package/dist/lookup.d.cts +161 -0
- package/dist/lookup.d.cts.map +1 -0
- package/dist/lookup.d.ts +161 -0
- package/dist/lookup.d.ts.map +1 -0
- package/dist/lookup.js +10 -0
- package/dist/media-loader-BABA_E4W.js +3 -0
- package/dist/media-loader-Ce9cuANS.cjs +21 -0
- package/dist/media-loader-Ce9cuANS.cjs.map +1 -0
- package/dist/media-loader-qRti-Q6h.js +14 -0
- package/dist/media-loader-qRti-Q6h.js.map +1 -0
- package/dist/media-loader-xlUGaJrx.cjs +3 -0
- package/dist/multi-language-transformer-AlxOM6b3.js +637 -0
- package/dist/multi-language-transformer-AlxOM6b3.js.map +1 -0
- package/dist/multi-language-transformer-MdbQBBOt.cjs +685 -0
- package/dist/multi-language-transformer-MdbQBBOt.cjs.map +1 -0
- package/dist/multi-language-transformer-SEhcJXEB.d.ts +63 -0
- package/dist/multi-language-transformer-SEhcJXEB.d.ts.map +1 -0
- package/dist/multi-language-transformer-Ul9mbRce.d.cts +63 -0
- package/dist/multi-language-transformer-Ul9mbRce.d.cts.map +1 -0
- package/dist/pronunciation-generator-BtBc4q_V.js +397 -0
- package/dist/pronunciation-generator-BtBc4q_V.js.map +1 -0
- package/dist/pronunciation-generator-CBYdXYou.js +4 -0
- package/dist/pronunciation-generator-CFbZlf5J.cjs +445 -0
- package/dist/pronunciation-generator-CFbZlf5J.cjs.map +1 -0
- package/dist/pronunciation-generator-DOz9hEuk.cjs +4 -0
- package/dist/render.cjs +2796 -0
- package/dist/render.cjs.map +1 -0
- package/dist/render.d.cts +424 -0
- package/dist/render.d.cts.map +1 -0
- package/dist/render.d.ts +424 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +2777 -0
- package/dist/render.js.map +1 -0
- package/dist/sentence-parser-BPAJNzqW.js +126 -0
- package/dist/sentence-parser-BPAJNzqW.js.map +1 -0
- package/dist/sentence-parser-BVIOI64h.cjs +132 -0
- package/dist/sentence-parser-BVIOI64h.cjs.map +1 -0
- package/dist/sentence-parser-BoHO3cHn.js +5 -0
- package/dist/sentence-parser-DQVLSW0z.cjs +5 -0
- package/dist/structured-content-generator-BtOApkTW.cjs +4 -0
- package/dist/structured-content-generator-Bx62RYa8.js +4 -0
- package/dist/structured-content-generator-CLnybumI.js +276 -0
- package/dist/structured-content-generator-CLnybumI.js.map +1 -0
- package/dist/structured-content-generator-DrwkB0-k.cjs +282 -0
- package/dist/structured-content-generator-DrwkB0-k.cjs.map +1 -0
- package/dist/text-utilities-B7PIythe.js +8 -0
- package/dist/text-utilities-B7PIythe.js.map +1 -0
- package/dist/text-utilities-Del2Ivkg.cjs +15 -0
- package/dist/text-utilities-Del2Ivkg.cjs.map +1 -0
- package/dist/translator-CRPlPzqi.cjs +1545 -0
- package/dist/translator-CRPlPzqi.cjs.map +1 -0
- package/dist/translator-CWgG5drA.js +1539 -0
- package/dist/translator-CWgG5drA.js.map +1 -0
- package/dist/translator-CaGtJvnQ.cjs +6 -0
- package/dist/translator-Cc6OGxrW.d.ts +180 -0
- package/dist/translator-Cc6OGxrW.d.ts.map +1 -0
- package/dist/translator-CcA-s-W4.d.cts +180 -0
- package/dist/translator-CcA-s-W4.d.cts.map +1 -0
- package/dist/translator-CuJOTK6l.js +6 -0
- package/dist/utilities-C-lbZaJE.cjs +52 -0
- package/dist/utilities-C-lbZaJE.cjs.map +1 -0
- package/dist/utilities-bi3EF-q5.js +33 -0
- package/dist/utilities-bi3EF-q5.js.map +1 -0
- package/package.json +102 -0
package/dist/anki.js
ADDED
|
@@ -0,0 +1,1751 @@
|
|
|
1
|
+
import { deferPromise } from "./utilities-bi3EF-q5.js";
|
|
2
|
+
import "./cjk-util-DubXBGDG.js";
|
|
3
|
+
import { YomitanError } from "./errors-DuuDSO5N.js";
|
|
4
|
+
import { getDisambiguations, getGroupedPronunciations, getPronunciationsOfType, getTermFrequency, groupTermTags, isNonNounVerbOrAdjective } from "./dictionary-data-util-CfOLfEDE.js";
|
|
5
|
+
import "./json-DGd-cunA.js";
|
|
6
|
+
import { distributeFurigana } from "./furigana-B3-0y231.js";
|
|
7
|
+
import { AnkiConnect, cloneFieldMarkerPattern, getRootDeckName } from "./anki-connect-DbrQHphS.js";
|
|
8
|
+
|
|
9
|
+
//#region src/anki/anki-note-builder.ts
|
|
10
|
+
/**
|
|
11
|
+
* Builds Anki notes from dictionary entries using template rendering.
|
|
12
|
+
*/
|
|
13
|
+
var AnkiNoteBuilder = class {
|
|
14
|
+
_api;
|
|
15
|
+
_markerPattern;
|
|
16
|
+
_templateRenderer;
|
|
17
|
+
_batchedRequests;
|
|
18
|
+
_batchedRequestsQueued;
|
|
19
|
+
constructor(templateRenderer, api) {
|
|
20
|
+
this._api = api ?? null;
|
|
21
|
+
this._markerPattern = cloneFieldMarkerPattern(true);
|
|
22
|
+
this._templateRenderer = templateRenderer;
|
|
23
|
+
this._batchedRequests = [];
|
|
24
|
+
this._batchedRequestsQueued = false;
|
|
25
|
+
}
|
|
26
|
+
async createNote({ dictionaryEntry, cardFormat, context, template, tags = [], requirements = [], duplicateScope = "collection", duplicateScopeCheckAllModels = false, resultOutputMode = "split", glossaryLayoutMode = "default", compactTags = false, mediaOptions = null, dictionaryStylesMap = new Map() }) {
|
|
27
|
+
const { deck: deckName, model: modelName, fields: fieldsSettings } = cardFormat;
|
|
28
|
+
const fields = Object.entries(fieldsSettings);
|
|
29
|
+
let duplicateScopeDeckName = null;
|
|
30
|
+
let duplicateScopeCheckChildren = false;
|
|
31
|
+
if (duplicateScope === "deck-root") {
|
|
32
|
+
duplicateScope = "deck";
|
|
33
|
+
duplicateScopeDeckName = getRootDeckName(deckName);
|
|
34
|
+
duplicateScopeCheckChildren = true;
|
|
35
|
+
}
|
|
36
|
+
const allErrors = [];
|
|
37
|
+
let media;
|
|
38
|
+
if (requirements.length > 0 && mediaOptions !== null && this._api !== null) {
|
|
39
|
+
let errors;
|
|
40
|
+
({media, errors} = await this._injectMedia(dictionaryEntry, requirements, mediaOptions));
|
|
41
|
+
for (const error of errors) allErrors.push(this._deserializeError(error));
|
|
42
|
+
}
|
|
43
|
+
const commonData = this._createData(dictionaryEntry, cardFormat, context, resultOutputMode, glossaryLayoutMode, compactTags, media, dictionaryStylesMap);
|
|
44
|
+
const formattedFieldValuePromises = [];
|
|
45
|
+
for (const [, { value: fieldValue }] of fields) {
|
|
46
|
+
const formattedFieldValuePromise = this._formatField(fieldValue, commonData, template);
|
|
47
|
+
formattedFieldValuePromises.push(formattedFieldValuePromise);
|
|
48
|
+
}
|
|
49
|
+
const formattedFieldValues = await Promise.all(formattedFieldValuePromises);
|
|
50
|
+
const uniqueRequirements = new Map();
|
|
51
|
+
const noteFields = {};
|
|
52
|
+
for (let i = 0, ii = fields.length; i < ii; ++i) {
|
|
53
|
+
const fieldName = fields[i][0];
|
|
54
|
+
const { value, errors: fieldErrors, requirements: fieldRequirements } = formattedFieldValues[i];
|
|
55
|
+
noteFields[fieldName] = value;
|
|
56
|
+
allErrors.push(...fieldErrors);
|
|
57
|
+
for (const requirement of fieldRequirements) {
|
|
58
|
+
const key = JSON.stringify(requirement);
|
|
59
|
+
if (uniqueRequirements.has(key)) continue;
|
|
60
|
+
uniqueRequirements.set(key, requirement);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const note = {
|
|
64
|
+
fields: noteFields,
|
|
65
|
+
tags,
|
|
66
|
+
deckName,
|
|
67
|
+
modelName,
|
|
68
|
+
options: {
|
|
69
|
+
allowDuplicate: true,
|
|
70
|
+
duplicateScope,
|
|
71
|
+
duplicateScopeOptions: {
|
|
72
|
+
deckName: duplicateScopeDeckName,
|
|
73
|
+
checkChildren: duplicateScopeCheckChildren,
|
|
74
|
+
checkAllModels: duplicateScopeCheckAllModels
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
note,
|
|
80
|
+
errors: allErrors,
|
|
81
|
+
requirements: [...uniqueRequirements.values()]
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async getRenderingData({ dictionaryEntry, cardFormat, context, resultOutputMode = "split", glossaryLayoutMode = "default", compactTags = false, marker, dictionaryStylesMap }) {
|
|
85
|
+
const commonData = this._createData(dictionaryEntry, cardFormat, context, resultOutputMode, glossaryLayoutMode, compactTags, void 0, dictionaryStylesMap);
|
|
86
|
+
return await this._templateRenderer.getModifiedData({
|
|
87
|
+
marker,
|
|
88
|
+
commonData
|
|
89
|
+
}, "ankiNote");
|
|
90
|
+
}
|
|
91
|
+
getDictionaryEntryDetailsForNote(dictionaryEntry) {
|
|
92
|
+
const { type } = dictionaryEntry;
|
|
93
|
+
if (type === "kanji") {
|
|
94
|
+
const { character } = dictionaryEntry;
|
|
95
|
+
return {
|
|
96
|
+
type,
|
|
97
|
+
character
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const { headwords } = dictionaryEntry;
|
|
101
|
+
let bestIndex = -1;
|
|
102
|
+
for (let i = 0, ii = headwords.length; i < ii; ++i) {
|
|
103
|
+
const { term: term$1, reading: reading$1, sources } = headwords[i];
|
|
104
|
+
for (const { deinflectedText } of sources) {
|
|
105
|
+
if (term$1 === deinflectedText) {
|
|
106
|
+
bestIndex = i;
|
|
107
|
+
i = ii;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
if (reading$1 === deinflectedText && bestIndex < 0) {
|
|
111
|
+
bestIndex = i;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const { term, reading } = headwords[Math.max(0, bestIndex)];
|
|
117
|
+
return {
|
|
118
|
+
type,
|
|
119
|
+
term,
|
|
120
|
+
reading
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
_createData(dictionaryEntry, cardFormat, context, resultOutputMode, glossaryLayoutMode, compactTags, media, dictionaryStylesMap) {
|
|
124
|
+
return {
|
|
125
|
+
dictionaryEntry,
|
|
126
|
+
cardFormat,
|
|
127
|
+
context,
|
|
128
|
+
resultOutputMode,
|
|
129
|
+
glossaryLayoutMode,
|
|
130
|
+
compactTags,
|
|
131
|
+
media,
|
|
132
|
+
dictionaryStylesMap
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async _formatField(field, commonData, template) {
|
|
136
|
+
const errors = [];
|
|
137
|
+
const requirements = [];
|
|
138
|
+
const value = await this._stringReplaceAsync(field, this._markerPattern, async (match) => {
|
|
139
|
+
const marker = match[1];
|
|
140
|
+
try {
|
|
141
|
+
const { result, requirements: fieldRequirements } = await this._renderTemplateBatched(template, commonData, marker);
|
|
142
|
+
requirements.push(...fieldRequirements);
|
|
143
|
+
return result;
|
|
144
|
+
} catch (e) {
|
|
145
|
+
const error = new YomitanError(`Template render error for {${marker}}`);
|
|
146
|
+
error.data = { error: e };
|
|
147
|
+
errors.push(error);
|
|
148
|
+
return `{${marker}-render-error}`;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
value,
|
|
153
|
+
errors,
|
|
154
|
+
requirements
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
async _stringReplaceAsync(str, regex, replacer) {
|
|
158
|
+
let match;
|
|
159
|
+
let index = 0;
|
|
160
|
+
const parts = [];
|
|
161
|
+
while ((match = regex.exec(str)) !== null) {
|
|
162
|
+
parts.push(str.substring(index, match.index), replacer(match, match.index, str));
|
|
163
|
+
index = regex.lastIndex;
|
|
164
|
+
}
|
|
165
|
+
if (parts.length === 0) return str;
|
|
166
|
+
parts.push(str.substring(index));
|
|
167
|
+
return (await Promise.all(parts)).join("");
|
|
168
|
+
}
|
|
169
|
+
_getBatchedTemplateGroup(template) {
|
|
170
|
+
for (const item of this._batchedRequests) if (item.template === template) return item;
|
|
171
|
+
const result = {
|
|
172
|
+
template,
|
|
173
|
+
commonDataRequestsMap: new Map()
|
|
174
|
+
};
|
|
175
|
+
this._batchedRequests.push(result);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
_renderTemplateBatched(template, commonData, marker) {
|
|
179
|
+
const { promise, resolve, reject } = deferPromise();
|
|
180
|
+
const { commonDataRequestsMap } = this._getBatchedTemplateGroup(template);
|
|
181
|
+
let requests = commonDataRequestsMap.get(commonData);
|
|
182
|
+
if (typeof requests === "undefined") {
|
|
183
|
+
requests = [];
|
|
184
|
+
commonDataRequestsMap.set(commonData, requests);
|
|
185
|
+
}
|
|
186
|
+
requests.push({
|
|
187
|
+
resolve,
|
|
188
|
+
reject,
|
|
189
|
+
marker
|
|
190
|
+
});
|
|
191
|
+
this._runBatchedRequestsDelayed();
|
|
192
|
+
return promise;
|
|
193
|
+
}
|
|
194
|
+
_runBatchedRequestsDelayed() {
|
|
195
|
+
if (this._batchedRequestsQueued) return;
|
|
196
|
+
this._batchedRequestsQueued = true;
|
|
197
|
+
Promise.resolve().then(() => {
|
|
198
|
+
this._batchedRequestsQueued = false;
|
|
199
|
+
this._runBatchedRequests();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
_runBatchedRequests() {
|
|
203
|
+
if (this._batchedRequests.length === 0) return;
|
|
204
|
+
const allRequests = [];
|
|
205
|
+
const items = [];
|
|
206
|
+
for (const { template, commonDataRequestsMap } of this._batchedRequests) {
|
|
207
|
+
const templateItems = [];
|
|
208
|
+
for (const [commonData, requests] of commonDataRequestsMap.entries()) {
|
|
209
|
+
const datas = [];
|
|
210
|
+
for (const { marker } of requests) datas.push({ marker });
|
|
211
|
+
allRequests.push(...requests);
|
|
212
|
+
templateItems.push({
|
|
213
|
+
type: "ankiNote",
|
|
214
|
+
commonData,
|
|
215
|
+
datas
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
items.push({
|
|
219
|
+
template,
|
|
220
|
+
templateItems
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
this._batchedRequests.length = 0;
|
|
224
|
+
this._resolveBatchedRequests(items, allRequests);
|
|
225
|
+
}
|
|
226
|
+
async _resolveBatchedRequests(items, requests) {
|
|
227
|
+
let responses;
|
|
228
|
+
try {
|
|
229
|
+
responses = await this._templateRenderer.renderMulti(items);
|
|
230
|
+
} catch (e) {
|
|
231
|
+
for (const { reject } of requests) reject(e);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
for (let i = 0, ii = requests.length; i < ii; ++i) {
|
|
235
|
+
const request = requests[i];
|
|
236
|
+
try {
|
|
237
|
+
const response = responses[i];
|
|
238
|
+
const { error } = response;
|
|
239
|
+
if (typeof error !== "undefined") throw this._deserializeError(error);
|
|
240
|
+
request.resolve(response.result);
|
|
241
|
+
} catch (e) {
|
|
242
|
+
request.reject(e);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async _injectMedia(dictionaryEntry, requirements, mediaOptions) {
|
|
247
|
+
const timestamp = Date.now();
|
|
248
|
+
let injectAudio = false;
|
|
249
|
+
let injectScreenshot = false;
|
|
250
|
+
let injectClipboardImage = false;
|
|
251
|
+
let injectClipboardText = false;
|
|
252
|
+
let injectPopupSelectionText = false;
|
|
253
|
+
const dictionaryMediaDetails = [];
|
|
254
|
+
for (const requirement of requirements) {
|
|
255
|
+
const { type } = requirement;
|
|
256
|
+
switch (type) {
|
|
257
|
+
case "audio":
|
|
258
|
+
injectAudio = true;
|
|
259
|
+
break;
|
|
260
|
+
case "screenshot":
|
|
261
|
+
injectScreenshot = true;
|
|
262
|
+
break;
|
|
263
|
+
case "clipboardImage":
|
|
264
|
+
injectClipboardImage = true;
|
|
265
|
+
break;
|
|
266
|
+
case "clipboardText":
|
|
267
|
+
injectClipboardText = true;
|
|
268
|
+
break;
|
|
269
|
+
case "popupSelectionText":
|
|
270
|
+
injectPopupSelectionText = true;
|
|
271
|
+
break;
|
|
272
|
+
case "dictionaryMedia":
|
|
273
|
+
{
|
|
274
|
+
const { dictionary, path } = requirement;
|
|
275
|
+
dictionaryMediaDetails.push({
|
|
276
|
+
dictionary,
|
|
277
|
+
path
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const dictionaryEntryDetails = this.getDictionaryEntryDetailsForNote(dictionaryEntry);
|
|
284
|
+
let audioDetails = null;
|
|
285
|
+
let screenshotDetails = null;
|
|
286
|
+
const clipboardDetails = {
|
|
287
|
+
image: injectClipboardImage,
|
|
288
|
+
text: injectClipboardText
|
|
289
|
+
};
|
|
290
|
+
if (injectAudio && dictionaryEntryDetails.type !== "kanji") {
|
|
291
|
+
const audioOptions = mediaOptions.audio;
|
|
292
|
+
if (typeof audioOptions === "object" && audioOptions !== null) {
|
|
293
|
+
const { sources, preferredAudioIndex, idleTimeout, languageSummary, enableDefaultAudioSources } = audioOptions;
|
|
294
|
+
audioDetails = {
|
|
295
|
+
sources,
|
|
296
|
+
preferredAudioIndex,
|
|
297
|
+
idleTimeout,
|
|
298
|
+
languageSummary,
|
|
299
|
+
enableDefaultAudioSources
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (injectScreenshot) {
|
|
304
|
+
const screenshotOptions = mediaOptions.screenshot;
|
|
305
|
+
if (typeof screenshotOptions === "object" && screenshotOptions !== null) {
|
|
306
|
+
const { format, quality, contentOrigin: { tabId, frameId } } = screenshotOptions;
|
|
307
|
+
if (typeof tabId === "number" && typeof frameId === "number") screenshotDetails = {
|
|
308
|
+
tabId,
|
|
309
|
+
frameId,
|
|
310
|
+
format,
|
|
311
|
+
quality
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const injectedMedia = await this._api?.injectAnkiNoteMedia(timestamp, dictionaryEntryDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails);
|
|
316
|
+
if (!injectedMedia) throw new Error("Media injection API is not available");
|
|
317
|
+
const { audioFileName, screenshotFileName, clipboardImageFileName, clipboardText, dictionaryMedia: dictionaryMediaArray, errors } = injectedMedia;
|
|
318
|
+
const dictionaryMedia = {};
|
|
319
|
+
for (const { dictionary, path, fileName } of dictionaryMediaArray) {
|
|
320
|
+
if (fileName === null) continue;
|
|
321
|
+
const dictionaryMedia2 = Object.prototype.hasOwnProperty.call(dictionaryMedia, dictionary) ? dictionaryMedia[dictionary] : dictionaryMedia[dictionary] = {};
|
|
322
|
+
dictionaryMedia2[path] = { value: fileName };
|
|
323
|
+
}
|
|
324
|
+
const media = {
|
|
325
|
+
audio: typeof audioFileName === "string" ? { value: audioFileName } : void 0,
|
|
326
|
+
screenshot: typeof screenshotFileName === "string" ? { value: screenshotFileName } : void 0,
|
|
327
|
+
clipboardImage: typeof clipboardImageFileName === "string" ? { value: clipboardImageFileName } : void 0,
|
|
328
|
+
clipboardText: typeof clipboardText === "string" ? { value: clipboardText } : void 0,
|
|
329
|
+
popupSelectionText: injectPopupSelectionText ? { value: "" } : void 0,
|
|
330
|
+
textFurigana: [],
|
|
331
|
+
dictionaryMedia
|
|
332
|
+
};
|
|
333
|
+
return {
|
|
334
|
+
media,
|
|
335
|
+
errors
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
_deserializeError(error) {
|
|
339
|
+
const e = new YomitanError(error.message ?? "Unknown error");
|
|
340
|
+
e.data = error.data;
|
|
341
|
+
return e;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region src/anki/anki-note-data-creator.ts
|
|
347
|
+
function createCachedValue(getter) {
|
|
348
|
+
return {
|
|
349
|
+
getter,
|
|
350
|
+
hasValue: false,
|
|
351
|
+
value: void 0
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
function getCachedValue(item) {
|
|
355
|
+
if (item.hasValue) return item.value;
|
|
356
|
+
const value = item.getter();
|
|
357
|
+
item.value = value;
|
|
358
|
+
item.hasValue = true;
|
|
359
|
+
return value;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Creates a compatibility representation of the specified data for Anki template rendering.
|
|
363
|
+
*/
|
|
364
|
+
function createAnkiNoteData(marker, details) {
|
|
365
|
+
const { dictionaryEntry, resultOutputMode, glossaryLayoutMode, compactTags, context, media, dictionaryStylesMap } = details;
|
|
366
|
+
const definition = createCachedValue(() => getDefinition(dictionaryEntry, context, resultOutputMode, dictionaryStylesMap, glossaryLayoutMode));
|
|
367
|
+
const uniqueExpressions = createCachedValue(() => getUniqueExpressions(dictionaryEntry));
|
|
368
|
+
const uniqueReadings = createCachedValue(() => getUniqueReadings(dictionaryEntry));
|
|
369
|
+
const context2 = createCachedValue(() => getPublicContext(context));
|
|
370
|
+
const pitches = createCachedValue(() => getPitches(dictionaryEntry));
|
|
371
|
+
const pitchCount = createCachedValue(() => getPitchCount(pitches));
|
|
372
|
+
const phoneticTranscriptions = createCachedValue(() => getPhoneticTranscriptions(dictionaryEntry));
|
|
373
|
+
let resolvedMedia;
|
|
374
|
+
if (typeof media !== "object" || media === null || Array.isArray(media)) resolvedMedia = {
|
|
375
|
+
audio: void 0,
|
|
376
|
+
screenshot: void 0,
|
|
377
|
+
clipboardImage: void 0,
|
|
378
|
+
clipboardText: void 0,
|
|
379
|
+
popupSelectionText: void 0,
|
|
380
|
+
textFurigana: [],
|
|
381
|
+
dictionaryMedia: {}
|
|
382
|
+
};
|
|
383
|
+
else resolvedMedia = media;
|
|
384
|
+
const result = {
|
|
385
|
+
marker,
|
|
386
|
+
get definition() {
|
|
387
|
+
return getCachedValue(definition);
|
|
388
|
+
},
|
|
389
|
+
glossaryLayoutMode,
|
|
390
|
+
compactTags,
|
|
391
|
+
group: resultOutputMode === "group",
|
|
392
|
+
merge: resultOutputMode === "merge",
|
|
393
|
+
compactGlossaries: glossaryLayoutMode === "compact-popup-anki",
|
|
394
|
+
get uniqueExpressions() {
|
|
395
|
+
return getCachedValue(uniqueExpressions);
|
|
396
|
+
},
|
|
397
|
+
get uniqueReadings() {
|
|
398
|
+
return getCachedValue(uniqueReadings);
|
|
399
|
+
},
|
|
400
|
+
get pitches() {
|
|
401
|
+
return getCachedValue(pitches);
|
|
402
|
+
},
|
|
403
|
+
get pitchCount() {
|
|
404
|
+
return getCachedValue(pitchCount);
|
|
405
|
+
},
|
|
406
|
+
get phoneticTranscriptions() {
|
|
407
|
+
return getCachedValue(phoneticTranscriptions);
|
|
408
|
+
},
|
|
409
|
+
get context() {
|
|
410
|
+
return getCachedValue(context2);
|
|
411
|
+
},
|
|
412
|
+
media: resolvedMedia,
|
|
413
|
+
dictionaryEntry
|
|
414
|
+
};
|
|
415
|
+
Object.defineProperty(result, "dictionaryEntry", {
|
|
416
|
+
configurable: false,
|
|
417
|
+
enumerable: false,
|
|
418
|
+
writable: false,
|
|
419
|
+
value: dictionaryEntry
|
|
420
|
+
});
|
|
421
|
+
return result;
|
|
422
|
+
}
|
|
423
|
+
function getPrimarySource(dictionaryEntry) {
|
|
424
|
+
for (const headword of dictionaryEntry.headwords) for (const source of headword.sources) if (source.isPrimary) return source;
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
function getUniqueExpressions(dictionaryEntry) {
|
|
428
|
+
if (dictionaryEntry.type === "term") {
|
|
429
|
+
const results = new Set();
|
|
430
|
+
for (const { term } of dictionaryEntry.headwords) results.add(term);
|
|
431
|
+
return [...results];
|
|
432
|
+
}
|
|
433
|
+
return [];
|
|
434
|
+
}
|
|
435
|
+
function getUniqueReadings(dictionaryEntry) {
|
|
436
|
+
if (dictionaryEntry.type === "term") {
|
|
437
|
+
const results = new Set();
|
|
438
|
+
for (const { reading } of dictionaryEntry.headwords) results.add(reading);
|
|
439
|
+
return [...results];
|
|
440
|
+
}
|
|
441
|
+
return [];
|
|
442
|
+
}
|
|
443
|
+
function getPublicContext(context) {
|
|
444
|
+
let { documentTitle } = context;
|
|
445
|
+
if (typeof documentTitle !== "string") documentTitle = "";
|
|
446
|
+
return {
|
|
447
|
+
query: context.query,
|
|
448
|
+
fullQuery: context.fullQuery,
|
|
449
|
+
document: { title: documentTitle }
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function getFrequencyNumbers(dictionaryEntry, requestedHeadwordIndex) {
|
|
453
|
+
let previousDictionary;
|
|
454
|
+
const frequencies = [];
|
|
455
|
+
for (const dictionaryEntryFrequency of dictionaryEntry.frequencies) {
|
|
456
|
+
const { dictionary, frequency, displayValue } = dictionaryEntryFrequency;
|
|
457
|
+
const wrongHeadwordIndex = Number.isInteger(requestedHeadwordIndex) && "headwordIndex" in dictionaryEntryFrequency && dictionaryEntryFrequency.headwordIndex !== requestedHeadwordIndex;
|
|
458
|
+
if (dictionary === previousDictionary || wrongHeadwordIndex) continue;
|
|
459
|
+
previousDictionary = dictionary;
|
|
460
|
+
if (displayValue !== null) {
|
|
461
|
+
const frequencyMatch = displayValue.match(/^\d+/);
|
|
462
|
+
if (frequencyMatch !== null) {
|
|
463
|
+
const frequencyParsed = Number.parseInt(frequencyMatch[0], 10);
|
|
464
|
+
if (frequencyParsed > 0) {
|
|
465
|
+
frequencies.push({
|
|
466
|
+
dictionary,
|
|
467
|
+
frequency: frequencyParsed
|
|
468
|
+
});
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (frequency > 0) frequencies.push({
|
|
474
|
+
dictionary,
|
|
475
|
+
frequency
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return frequencies;
|
|
479
|
+
}
|
|
480
|
+
function getFrequencyHarmonic(dictionaryEntry, headwordIndex) {
|
|
481
|
+
const frequencies = getFrequencyNumbers(dictionaryEntry, headwordIndex);
|
|
482
|
+
if (frequencies.length === 0) return -1;
|
|
483
|
+
let total = 0;
|
|
484
|
+
for (const frequency of frequencies) total += 1 / frequency.frequency;
|
|
485
|
+
return Math.floor(frequencies.length / total);
|
|
486
|
+
}
|
|
487
|
+
function getFrequencyAverage(dictionaryEntry, headwordIndex) {
|
|
488
|
+
const frequencies = getFrequencyNumbers(dictionaryEntry, headwordIndex);
|
|
489
|
+
if (frequencies.length === 0) return -1;
|
|
490
|
+
let total = 0;
|
|
491
|
+
for (const frequency of frequencies) total += frequency.frequency;
|
|
492
|
+
return Math.floor(total / frequencies.length);
|
|
493
|
+
}
|
|
494
|
+
function getPitches(dictionaryEntry) {
|
|
495
|
+
const results = [];
|
|
496
|
+
if (dictionaryEntry.type === "term") for (const { dictionary, pronunciations } of getGroupedPronunciations(dictionaryEntry)) {
|
|
497
|
+
const pitches = [];
|
|
498
|
+
for (const groupedPronunciation of pronunciations) {
|
|
499
|
+
const { pronunciation } = groupedPronunciation;
|
|
500
|
+
if (pronunciation.type !== "pitch-accent") continue;
|
|
501
|
+
const { positions, nasalPositions, devoicePositions, tags } = pronunciation;
|
|
502
|
+
const { terms, reading, exclusiveTerms, exclusiveReadings } = groupedPronunciation;
|
|
503
|
+
pitches.push({
|
|
504
|
+
expressions: terms,
|
|
505
|
+
reading,
|
|
506
|
+
positions,
|
|
507
|
+
nasalPositions,
|
|
508
|
+
devoicePositions,
|
|
509
|
+
tags: convertPitchTags(tags),
|
|
510
|
+
exclusiveExpressions: exclusiveTerms,
|
|
511
|
+
exclusiveReadings
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
results.push({
|
|
515
|
+
dictionary,
|
|
516
|
+
pitches
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
return results;
|
|
520
|
+
}
|
|
521
|
+
function getPhoneticTranscriptions(dictionaryEntry) {
|
|
522
|
+
const results = [];
|
|
523
|
+
if (dictionaryEntry.type === "term") for (const { dictionary, pronunciations } of getGroupedPronunciations(dictionaryEntry)) {
|
|
524
|
+
const phoneticTranscriptions = [];
|
|
525
|
+
for (const groupedPronunciation of pronunciations) {
|
|
526
|
+
const { pronunciation } = groupedPronunciation;
|
|
527
|
+
if (pronunciation.type !== "phonetic-transcription") continue;
|
|
528
|
+
const { ipa, tags } = pronunciation;
|
|
529
|
+
const { terms, reading, exclusiveTerms, exclusiveReadings } = groupedPronunciation;
|
|
530
|
+
phoneticTranscriptions.push({
|
|
531
|
+
expressions: terms,
|
|
532
|
+
reading,
|
|
533
|
+
ipa,
|
|
534
|
+
tags,
|
|
535
|
+
exclusiveExpressions: exclusiveTerms,
|
|
536
|
+
exclusiveReadings
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
results.push({
|
|
540
|
+
dictionary,
|
|
541
|
+
phoneticTranscriptions
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
return results;
|
|
545
|
+
}
|
|
546
|
+
function getPitchCount(cachedPitches) {
|
|
547
|
+
const pitches = getCachedValue(cachedPitches);
|
|
548
|
+
return pitches.reduce((i, v) => i + v.pitches.length, 0);
|
|
549
|
+
}
|
|
550
|
+
function getDefinition(dictionaryEntry, context, resultOutputMode, dictionaryStylesMap, glossaryLayoutMode) {
|
|
551
|
+
switch (dictionaryEntry.type) {
|
|
552
|
+
case "term": return getTermDefinition(dictionaryEntry, context, resultOutputMode, dictionaryStylesMap, glossaryLayoutMode);
|
|
553
|
+
case "kanji": return getKanjiDefinition(dictionaryEntry, context);
|
|
554
|
+
default: return {};
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function getKanjiDefinition(dictionaryEntry, context) {
|
|
558
|
+
const { character, dictionary, dictionaryAlias, onyomi, kunyomi, definitions } = dictionaryEntry;
|
|
559
|
+
let { url } = context;
|
|
560
|
+
if (typeof url !== "string") url = "";
|
|
561
|
+
const stats = createCachedValue(() => getKanjiStats(dictionaryEntry));
|
|
562
|
+
const tags = createCachedValue(() => convertTags(dictionaryEntry.tags));
|
|
563
|
+
const frequencies = createCachedValue(() => getKanjiFrequencies(dictionaryEntry));
|
|
564
|
+
const frequencyHarmonic = createCachedValue(() => getFrequencyHarmonic(dictionaryEntry, null));
|
|
565
|
+
const frequencyAverage = createCachedValue(() => getFrequencyAverage(dictionaryEntry, null));
|
|
566
|
+
const cloze = createCachedValue(() => getCloze(dictionaryEntry, context));
|
|
567
|
+
return {
|
|
568
|
+
type: "kanji",
|
|
569
|
+
character,
|
|
570
|
+
dictionary,
|
|
571
|
+
dictionaryAlias,
|
|
572
|
+
onyomi,
|
|
573
|
+
kunyomi,
|
|
574
|
+
glossary: definitions,
|
|
575
|
+
get tags() {
|
|
576
|
+
return getCachedValue(tags);
|
|
577
|
+
},
|
|
578
|
+
get stats() {
|
|
579
|
+
return getCachedValue(stats);
|
|
580
|
+
},
|
|
581
|
+
get frequencies() {
|
|
582
|
+
return getCachedValue(frequencies);
|
|
583
|
+
},
|
|
584
|
+
get frequencyHarmonic() {
|
|
585
|
+
return getCachedValue(frequencyHarmonic);
|
|
586
|
+
},
|
|
587
|
+
get frequencyAverage() {
|
|
588
|
+
return getCachedValue(frequencyAverage);
|
|
589
|
+
},
|
|
590
|
+
url,
|
|
591
|
+
get cloze() {
|
|
592
|
+
return getCachedValue(cloze);
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function getKanjiStats(dictionaryEntry) {
|
|
597
|
+
const results = {};
|
|
598
|
+
for (const [key, value] of Object.entries(dictionaryEntry.stats)) results[key] = value.map(convertKanjiStat);
|
|
599
|
+
return results;
|
|
600
|
+
}
|
|
601
|
+
function convertKanjiStat({ name, category, content, order, score, dictionary, value }) {
|
|
602
|
+
return {
|
|
603
|
+
name,
|
|
604
|
+
category,
|
|
605
|
+
notes: content,
|
|
606
|
+
order,
|
|
607
|
+
score,
|
|
608
|
+
dictionary,
|
|
609
|
+
value
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function getKanjiFrequencies(dictionaryEntry) {
|
|
613
|
+
const results = [];
|
|
614
|
+
for (const { index, dictionary, dictionaryAlias, dictionaryIndex, character, frequency, displayValue } of dictionaryEntry.frequencies) results.push({
|
|
615
|
+
index,
|
|
616
|
+
dictionary,
|
|
617
|
+
dictionaryAlias,
|
|
618
|
+
dictionaryOrder: { index: dictionaryIndex },
|
|
619
|
+
character,
|
|
620
|
+
frequency: displayValue !== null ? displayValue : frequency
|
|
621
|
+
});
|
|
622
|
+
return results;
|
|
623
|
+
}
|
|
624
|
+
function getTermDefinition(dictionaryEntry, context, resultOutputMode, dictionaryStylesMap, glossaryLayoutMode) {
|
|
625
|
+
let type = "term";
|
|
626
|
+
switch (resultOutputMode) {
|
|
627
|
+
case "group":
|
|
628
|
+
type = "termGrouped";
|
|
629
|
+
break;
|
|
630
|
+
case "merge":
|
|
631
|
+
type = "termMerged";
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
const { inflectionRuleChainCandidates, score, dictionaryIndex, sourceTermExactMatchCount, definitions } = dictionaryEntry;
|
|
635
|
+
let { url } = context;
|
|
636
|
+
if (typeof url !== "string") url = "";
|
|
637
|
+
const primarySource = getPrimarySource(dictionaryEntry);
|
|
638
|
+
const dictionaryAliases = createCachedValue(() => getTermDictionaryAliases(dictionaryEntry));
|
|
639
|
+
const dictionaryNames = createCachedValue(() => getTermDictionaryNames(dictionaryEntry));
|
|
640
|
+
const commonInfo = createCachedValue(() => getTermDictionaryEntryCommonInfo(dictionaryEntry, type, dictionaryStylesMap, glossaryLayoutMode));
|
|
641
|
+
const termTags = createCachedValue(() => getTermTags(dictionaryEntry, type));
|
|
642
|
+
const expressions = createCachedValue(() => getTermExpressions(dictionaryEntry));
|
|
643
|
+
const frequencies = createCachedValue(() => getTermFrequenciesAnki(dictionaryEntry));
|
|
644
|
+
const frequencyNumbersCV = createCachedValue(() => getFrequencyNumbers(dictionaryEntry, null));
|
|
645
|
+
const frequencyHarmonic = createCachedValue(() => getFrequencyHarmonic(dictionaryEntry, null));
|
|
646
|
+
const frequencyAverage = createCachedValue(() => getFrequencyAverage(dictionaryEntry, null));
|
|
647
|
+
const pitches = createCachedValue(() => getTermPitches(dictionaryEntry));
|
|
648
|
+
const phoneticTranscriptions = createCachedValue(() => getTermPhoneticTranscriptions(dictionaryEntry));
|
|
649
|
+
const glossary = createCachedValue(() => getTermGlossaryArray(dictionaryEntry, type));
|
|
650
|
+
const styleInfo = createCachedValue(() => getTermStyles(dictionaryEntry, type, dictionaryStylesMap));
|
|
651
|
+
const cloze = createCachedValue(() => getCloze(dictionaryEntry, context));
|
|
652
|
+
const furiganaSegments = createCachedValue(() => getTermFuriganaSegments(dictionaryEntry, type));
|
|
653
|
+
const sequence = createCachedValue(() => getTermDictionaryEntrySequence(dictionaryEntry));
|
|
654
|
+
return {
|
|
655
|
+
type,
|
|
656
|
+
id: type === "term" && definitions.length > 0 ? definitions[0].id : void 0,
|
|
657
|
+
source: primarySource !== null ? primarySource.transformedText : null,
|
|
658
|
+
rawSource: primarySource !== null ? primarySource.originalText : null,
|
|
659
|
+
sourceTerm: type !== "termMerged" ? primarySource !== null ? primarySource.deinflectedText : null : void 0,
|
|
660
|
+
inflectionRuleChainCandidates,
|
|
661
|
+
score,
|
|
662
|
+
isPrimary: type === "term" ? dictionaryEntry.isPrimary : void 0,
|
|
663
|
+
get sequence() {
|
|
664
|
+
return getCachedValue(sequence);
|
|
665
|
+
},
|
|
666
|
+
get dictionary() {
|
|
667
|
+
return getCachedValue(dictionaryNames)[0];
|
|
668
|
+
},
|
|
669
|
+
get dictionaryAlias() {
|
|
670
|
+
return getCachedValue(dictionaryAliases)[0];
|
|
671
|
+
},
|
|
672
|
+
dictionaryOrder: { index: dictionaryIndex },
|
|
673
|
+
get dictionaryNames() {
|
|
674
|
+
return getCachedValue(dictionaryNames);
|
|
675
|
+
},
|
|
676
|
+
get expression() {
|
|
677
|
+
const { uniqueTerms } = getCachedValue(commonInfo);
|
|
678
|
+
return type === "term" || type === "termGrouped" ? uniqueTerms[0] : uniqueTerms;
|
|
679
|
+
},
|
|
680
|
+
get reading() {
|
|
681
|
+
const { uniqueReadings } = getCachedValue(commonInfo);
|
|
682
|
+
return type === "term" || type === "termGrouped" ? uniqueReadings[0] : uniqueReadings;
|
|
683
|
+
},
|
|
684
|
+
get expressions() {
|
|
685
|
+
return getCachedValue(expressions);
|
|
686
|
+
},
|
|
687
|
+
get glossary() {
|
|
688
|
+
return getCachedValue(glossary);
|
|
689
|
+
},
|
|
690
|
+
get glossaryScopedStyles() {
|
|
691
|
+
return getCachedValue(styleInfo)?.glossaryScopedStyles;
|
|
692
|
+
},
|
|
693
|
+
get dictScopedStyles() {
|
|
694
|
+
return getCachedValue(styleInfo)?.dictScopedStyles;
|
|
695
|
+
},
|
|
696
|
+
get definitionTags() {
|
|
697
|
+
return type === "term" ? getCachedValue(commonInfo).definitionTags : void 0;
|
|
698
|
+
},
|
|
699
|
+
get termTags() {
|
|
700
|
+
return getCachedValue(termTags);
|
|
701
|
+
},
|
|
702
|
+
get definitions() {
|
|
703
|
+
return getCachedValue(commonInfo).definitions;
|
|
704
|
+
},
|
|
705
|
+
get frequencies() {
|
|
706
|
+
return getCachedValue(frequencies);
|
|
707
|
+
},
|
|
708
|
+
get frequencyNumbers() {
|
|
709
|
+
return getCachedValue(frequencyNumbersCV);
|
|
710
|
+
},
|
|
711
|
+
get frequencyHarmonic() {
|
|
712
|
+
return getCachedValue(frequencyHarmonic);
|
|
713
|
+
},
|
|
714
|
+
get frequencyAverage() {
|
|
715
|
+
return getCachedValue(frequencyAverage);
|
|
716
|
+
},
|
|
717
|
+
get pitches() {
|
|
718
|
+
return getCachedValue(pitches);
|
|
719
|
+
},
|
|
720
|
+
get phoneticTranscriptions() {
|
|
721
|
+
return getCachedValue(phoneticTranscriptions);
|
|
722
|
+
},
|
|
723
|
+
sourceTermExactMatchCount,
|
|
724
|
+
url,
|
|
725
|
+
get cloze() {
|
|
726
|
+
return getCachedValue(cloze);
|
|
727
|
+
},
|
|
728
|
+
get furiganaSegments() {
|
|
729
|
+
return getCachedValue(furiganaSegments);
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
function getTermDictionaryNames(dictionaryEntry) {
|
|
734
|
+
const dictionaryNames = new Set();
|
|
735
|
+
for (const { dictionary } of dictionaryEntry.definitions) dictionaryNames.add(dictionary);
|
|
736
|
+
return [...dictionaryNames];
|
|
737
|
+
}
|
|
738
|
+
function getTermDictionaryAliases(dictionaryEntry) {
|
|
739
|
+
const dictionaryAliases = new Set();
|
|
740
|
+
for (const { dictionaryAlias } of dictionaryEntry.definitions) dictionaryAliases.add(dictionaryAlias);
|
|
741
|
+
return [...dictionaryAliases];
|
|
742
|
+
}
|
|
743
|
+
function getTermDictionaryEntryCommonInfo(dictionaryEntry, type, _dictionaryStylesMap, _glossaryLayoutMode) {
|
|
744
|
+
const merged = type === "termMerged";
|
|
745
|
+
const hasDefinitions = type !== "term";
|
|
746
|
+
const allTermsSet = new Set();
|
|
747
|
+
const allReadingsSet = new Set();
|
|
748
|
+
for (const { term, reading } of dictionaryEntry.headwords) {
|
|
749
|
+
allTermsSet.add(term);
|
|
750
|
+
allReadingsSet.add(reading);
|
|
751
|
+
}
|
|
752
|
+
const uniqueTerms = [...allTermsSet];
|
|
753
|
+
const uniqueReadings = [...allReadingsSet];
|
|
754
|
+
const definitions = [];
|
|
755
|
+
const definitionTags = [];
|
|
756
|
+
for (const { tags, headwordIndices, entries, dictionary, dictionaryAlias, sequences } of dictionaryEntry.definitions) {
|
|
757
|
+
const definitionTags2 = [];
|
|
758
|
+
for (const tag of tags) {
|
|
759
|
+
definitionTags.push(convertTag(tag));
|
|
760
|
+
definitionTags2.push(convertTag(tag));
|
|
761
|
+
}
|
|
762
|
+
if (!hasDefinitions) continue;
|
|
763
|
+
const only = merged ? getDisambiguations(dictionaryEntry.headwords, headwordIndices, allTermsSet, allReadingsSet) : void 0;
|
|
764
|
+
definitions.push({
|
|
765
|
+
sequence: sequences[0],
|
|
766
|
+
dictionary,
|
|
767
|
+
dictionaryAlias,
|
|
768
|
+
glossaryScopedStyles: "",
|
|
769
|
+
dictScopedStyles: "",
|
|
770
|
+
glossary: entries,
|
|
771
|
+
definitionTags: definitionTags2,
|
|
772
|
+
only
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
return {
|
|
776
|
+
uniqueTerms,
|
|
777
|
+
uniqueReadings,
|
|
778
|
+
definitionTags,
|
|
779
|
+
definitions: hasDefinitions ? definitions : void 0
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
function getTermFrequenciesAnki(dictionaryEntry) {
|
|
783
|
+
const results = [];
|
|
784
|
+
const { headwords } = dictionaryEntry;
|
|
785
|
+
for (const { headwordIndex, dictionary, dictionaryAlias, dictionaryIndex, hasReading, frequency, displayValue } of dictionaryEntry.frequencies) {
|
|
786
|
+
const { term, reading } = headwords[headwordIndex];
|
|
787
|
+
results.push({
|
|
788
|
+
index: results.length,
|
|
789
|
+
expressionIndex: headwordIndex,
|
|
790
|
+
dictionary,
|
|
791
|
+
dictionaryAlias,
|
|
792
|
+
dictionaryOrder: { index: dictionaryIndex },
|
|
793
|
+
expression: term,
|
|
794
|
+
reading,
|
|
795
|
+
hasReading,
|
|
796
|
+
frequency: displayValue !== null ? displayValue : frequency
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
return results;
|
|
800
|
+
}
|
|
801
|
+
function getTermPitches(dictionaryEntry) {
|
|
802
|
+
const results = [];
|
|
803
|
+
const { headwords } = dictionaryEntry;
|
|
804
|
+
for (const { headwordIndex, dictionary, dictionaryAlias, dictionaryIndex, pronunciations } of dictionaryEntry.pronunciations) {
|
|
805
|
+
const { term, reading } = headwords[headwordIndex];
|
|
806
|
+
const pitchAccents = getPronunciationsOfType(pronunciations, "pitch-accent");
|
|
807
|
+
const cachedPitches = createCachedValue(() => getTermPitchesInner(pitchAccents));
|
|
808
|
+
results.push({
|
|
809
|
+
index: results.length,
|
|
810
|
+
expressionIndex: headwordIndex,
|
|
811
|
+
dictionary,
|
|
812
|
+
dictionaryAlias,
|
|
813
|
+
dictionaryOrder: { index: dictionaryIndex },
|
|
814
|
+
expression: term,
|
|
815
|
+
reading,
|
|
816
|
+
get pitches() {
|
|
817
|
+
return getCachedValue(cachedPitches);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
return results;
|
|
822
|
+
}
|
|
823
|
+
function getTermPitchesInner(pitches) {
|
|
824
|
+
const results = [];
|
|
825
|
+
for (const { positions, tags } of pitches) {
|
|
826
|
+
const cachedTags = createCachedValue(() => convertTags(tags));
|
|
827
|
+
results.push({
|
|
828
|
+
positions,
|
|
829
|
+
get tags() {
|
|
830
|
+
return getCachedValue(cachedTags);
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
return results;
|
|
835
|
+
}
|
|
836
|
+
function getTermPhoneticTranscriptions(dictionaryEntry) {
|
|
837
|
+
const results = [];
|
|
838
|
+
const { headwords } = dictionaryEntry;
|
|
839
|
+
for (const { headwordIndex, dictionary, dictionaryAlias, dictionaryIndex, pronunciations } of dictionaryEntry.pronunciations) {
|
|
840
|
+
const { term, reading } = headwords[headwordIndex];
|
|
841
|
+
const phoneticTranscriptions = getPronunciationsOfType(pronunciations, "phonetic-transcription");
|
|
842
|
+
const termPhoneticTranscriptions = getTermPhoneticTranscriptionsInner(phoneticTranscriptions);
|
|
843
|
+
results.push({
|
|
844
|
+
index: results.length,
|
|
845
|
+
expressionIndex: headwordIndex,
|
|
846
|
+
dictionary,
|
|
847
|
+
dictionaryAlias,
|
|
848
|
+
dictionaryOrder: { index: dictionaryIndex },
|
|
849
|
+
expression: term,
|
|
850
|
+
reading,
|
|
851
|
+
get phoneticTranscriptions() {
|
|
852
|
+
return termPhoneticTranscriptions;
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
return results;
|
|
857
|
+
}
|
|
858
|
+
function getTermPhoneticTranscriptionsInner(phoneticTranscriptions) {
|
|
859
|
+
const results = [];
|
|
860
|
+
for (const { ipa, tags } of phoneticTranscriptions) {
|
|
861
|
+
const cachedTags = createCachedValue(() => convertTags(tags));
|
|
862
|
+
results.push({
|
|
863
|
+
ipa,
|
|
864
|
+
get tags() {
|
|
865
|
+
return getCachedValue(cachedTags);
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
return results;
|
|
870
|
+
}
|
|
871
|
+
function getTermExpressions(dictionaryEntry) {
|
|
872
|
+
const results = [];
|
|
873
|
+
const { headwords } = dictionaryEntry;
|
|
874
|
+
for (let i = 0, ii = headwords.length; i < ii; ++i) {
|
|
875
|
+
const { term, reading, tags, sources: [{ deinflectedText }], wordClasses } = headwords[i];
|
|
876
|
+
const termTagsCV = createCachedValue(() => convertTags(tags));
|
|
877
|
+
const frequenciesCV = createCachedValue(() => getTermExpressionFrequencies(dictionaryEntry, i));
|
|
878
|
+
const pitchesCV = createCachedValue(() => getTermExpressionPitches(dictionaryEntry, i));
|
|
879
|
+
const termFrequencyCV = createCachedValue(() => getTermExpressionTermFrequency(termTagsCV));
|
|
880
|
+
const furiganaSegmentsCV = createCachedValue(() => getTermHeadwordFuriganaSegments(term, reading));
|
|
881
|
+
const item = {
|
|
882
|
+
sourceTerm: deinflectedText,
|
|
883
|
+
expression: term,
|
|
884
|
+
reading,
|
|
885
|
+
get termTags() {
|
|
886
|
+
return getCachedValue(termTagsCV);
|
|
887
|
+
},
|
|
888
|
+
get frequencies() {
|
|
889
|
+
return getCachedValue(frequenciesCV);
|
|
890
|
+
},
|
|
891
|
+
get pitches() {
|
|
892
|
+
return getCachedValue(pitchesCV);
|
|
893
|
+
},
|
|
894
|
+
get furiganaSegments() {
|
|
895
|
+
return getCachedValue(furiganaSegmentsCV);
|
|
896
|
+
},
|
|
897
|
+
get termFrequency() {
|
|
898
|
+
return getCachedValue(termFrequencyCV);
|
|
899
|
+
},
|
|
900
|
+
wordClasses
|
|
901
|
+
};
|
|
902
|
+
results.push(item);
|
|
903
|
+
}
|
|
904
|
+
return results;
|
|
905
|
+
}
|
|
906
|
+
function getTermExpressionFrequencies(dictionaryEntry, i) {
|
|
907
|
+
const results = [];
|
|
908
|
+
const { headwords, frequencies } = dictionaryEntry;
|
|
909
|
+
for (const { headwordIndex, dictionary, dictionaryAlias, dictionaryIndex, hasReading, frequency, displayValue } of frequencies) {
|
|
910
|
+
if (headwordIndex !== i) continue;
|
|
911
|
+
const { term, reading } = headwords[headwordIndex];
|
|
912
|
+
results.push({
|
|
913
|
+
index: results.length,
|
|
914
|
+
expressionIndex: headwordIndex,
|
|
915
|
+
dictionary,
|
|
916
|
+
dictionaryAlias,
|
|
917
|
+
dictionaryOrder: { index: dictionaryIndex },
|
|
918
|
+
expression: term,
|
|
919
|
+
reading,
|
|
920
|
+
hasReading,
|
|
921
|
+
frequency: displayValue !== null ? displayValue : frequency
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
return results;
|
|
925
|
+
}
|
|
926
|
+
function getTermExpressionPitches(dictionaryEntry, i) {
|
|
927
|
+
const results = [];
|
|
928
|
+
const { headwords, pronunciations: termPronunciations } = dictionaryEntry;
|
|
929
|
+
for (const { headwordIndex, dictionary, dictionaryAlias, dictionaryIndex, pronunciations } of termPronunciations) {
|
|
930
|
+
if (headwordIndex !== i) continue;
|
|
931
|
+
const { term, reading } = headwords[headwordIndex];
|
|
932
|
+
const pitchAccents = getPronunciationsOfType(pronunciations, "pitch-accent");
|
|
933
|
+
const cachedPitches = createCachedValue(() => getTermPitchesInner(pitchAccents));
|
|
934
|
+
results.push({
|
|
935
|
+
index: results.length,
|
|
936
|
+
expressionIndex: headwordIndex,
|
|
937
|
+
dictionary,
|
|
938
|
+
dictionaryAlias,
|
|
939
|
+
dictionaryOrder: { index: dictionaryIndex },
|
|
940
|
+
expression: term,
|
|
941
|
+
reading,
|
|
942
|
+
get pitches() {
|
|
943
|
+
return getCachedValue(cachedPitches);
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
return results;
|
|
948
|
+
}
|
|
949
|
+
function getTermExpressionTermFrequency(cachedTermTags) {
|
|
950
|
+
const termTags = getCachedValue(cachedTermTags);
|
|
951
|
+
return getTermFrequency(termTags);
|
|
952
|
+
}
|
|
953
|
+
function getTermGlossaryArray(dictionaryEntry, type) {
|
|
954
|
+
if (type === "term") {
|
|
955
|
+
const results = [];
|
|
956
|
+
for (const { entries } of dictionaryEntry.definitions) results.push(...entries);
|
|
957
|
+
return results;
|
|
958
|
+
}
|
|
959
|
+
return void 0;
|
|
960
|
+
}
|
|
961
|
+
function getTermStyles(dictionaryEntry, type, _dictionaryStylesMap) {
|
|
962
|
+
if (type !== "term") return void 0;
|
|
963
|
+
return {
|
|
964
|
+
glossaryScopedStyles: "",
|
|
965
|
+
dictScopedStyles: ""
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
function getTermTags(dictionaryEntry, type) {
|
|
969
|
+
if (type !== "termMerged") {
|
|
970
|
+
const results = [];
|
|
971
|
+
for (const { tag } of groupTermTags(dictionaryEntry)) results.push(convertTag(tag));
|
|
972
|
+
return results;
|
|
973
|
+
}
|
|
974
|
+
return void 0;
|
|
975
|
+
}
|
|
976
|
+
function convertTags(tags) {
|
|
977
|
+
const results = [];
|
|
978
|
+
for (const tag of tags) results.push(convertTag(tag));
|
|
979
|
+
return results;
|
|
980
|
+
}
|
|
981
|
+
function convertTag({ name, category, content, order, score, dictionaries, redundant }) {
|
|
982
|
+
return {
|
|
983
|
+
name,
|
|
984
|
+
category,
|
|
985
|
+
notes: content.length > 0 ? content[0] : "",
|
|
986
|
+
order,
|
|
987
|
+
score,
|
|
988
|
+
dictionary: dictionaries.length > 0 ? dictionaries[0] : "",
|
|
989
|
+
redundant
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
function convertPitchTags(tags) {
|
|
993
|
+
const results = [];
|
|
994
|
+
for (const tag of tags) results.push(convertPitchTag(tag));
|
|
995
|
+
return results;
|
|
996
|
+
}
|
|
997
|
+
function convertPitchTag({ name, category, content, order, score, dictionaries, redundant }) {
|
|
998
|
+
return {
|
|
999
|
+
name,
|
|
1000
|
+
category,
|
|
1001
|
+
order,
|
|
1002
|
+
score,
|
|
1003
|
+
content: [...content],
|
|
1004
|
+
dictionaries: [...dictionaries],
|
|
1005
|
+
redundant
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
function getCloze(dictionaryEntry, context) {
|
|
1009
|
+
let originalText = "";
|
|
1010
|
+
let term = "";
|
|
1011
|
+
let reading = "";
|
|
1012
|
+
switch (dictionaryEntry.type) {
|
|
1013
|
+
case "term":
|
|
1014
|
+
{
|
|
1015
|
+
term = dictionaryEntry.headwords[0].term;
|
|
1016
|
+
reading = dictionaryEntry.headwords[0].reading;
|
|
1017
|
+
const primarySource = getPrimarySource(dictionaryEntry);
|
|
1018
|
+
if (primarySource !== null) originalText = primarySource.originalText;
|
|
1019
|
+
}
|
|
1020
|
+
break;
|
|
1021
|
+
case "kanji":
|
|
1022
|
+
originalText = dictionaryEntry.character;
|
|
1023
|
+
break;
|
|
1024
|
+
}
|
|
1025
|
+
const { sentence } = context;
|
|
1026
|
+
let text;
|
|
1027
|
+
let offset;
|
|
1028
|
+
if (typeof sentence === "object" && sentence !== null) ({text, offset} = sentence);
|
|
1029
|
+
if (typeof text !== "string") text = "";
|
|
1030
|
+
if (typeof offset !== "number") offset = 0;
|
|
1031
|
+
const textChars = [...text];
|
|
1032
|
+
const textSegments = [];
|
|
1033
|
+
for (const { text: text2, reading: reading2 } of distributeFurigana(term, textChars.slice(offset, offset + originalText.length).join(""))) textSegments.push(reading2.length > 0 ? reading2 : text2);
|
|
1034
|
+
return {
|
|
1035
|
+
sentence: textChars.join(""),
|
|
1036
|
+
prefix: textChars.slice(0, offset).join(""),
|
|
1037
|
+
body: textChars.slice(offset, offset + originalText.length).join(""),
|
|
1038
|
+
bodyKana: textSegments.join(""),
|
|
1039
|
+
suffix: textChars.slice(offset + originalText.length).join("")
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
function getTermFuriganaSegments(dictionaryEntry, type) {
|
|
1043
|
+
if (type === "term") for (const { term, reading } of dictionaryEntry.headwords) return getTermHeadwordFuriganaSegments(term, reading);
|
|
1044
|
+
return void 0;
|
|
1045
|
+
}
|
|
1046
|
+
function getTermHeadwordFuriganaSegments(term, reading) {
|
|
1047
|
+
const result = [];
|
|
1048
|
+
for (const { text, reading: reading2 } of distributeFurigana(term, reading)) result.push({
|
|
1049
|
+
text,
|
|
1050
|
+
furigana: reading2
|
|
1051
|
+
});
|
|
1052
|
+
return result;
|
|
1053
|
+
}
|
|
1054
|
+
function getTermDictionaryEntrySequence(dictionaryEntry) {
|
|
1055
|
+
let hasSequence = false;
|
|
1056
|
+
let mainSequence = -1;
|
|
1057
|
+
if (!dictionaryEntry.isPrimary) return mainSequence;
|
|
1058
|
+
for (const { sequences } of dictionaryEntry.definitions) {
|
|
1059
|
+
const sequence = sequences[0];
|
|
1060
|
+
if (!hasSequence) {
|
|
1061
|
+
mainSequence = sequence;
|
|
1062
|
+
hasSequence = true;
|
|
1063
|
+
if (mainSequence === -1) break;
|
|
1064
|
+
} else if (mainSequence !== sequence) {
|
|
1065
|
+
mainSequence = -1;
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return mainSequence;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
//#endregion
|
|
1073
|
+
//#region src/anki/anki-template-renderer.ts
|
|
1074
|
+
/**
|
|
1075
|
+
* Manages media access during template rendering.
|
|
1076
|
+
*/
|
|
1077
|
+
var TemplateRendererMediaProvider = class {
|
|
1078
|
+
_requirements;
|
|
1079
|
+
constructor() {
|
|
1080
|
+
this._requirements = null;
|
|
1081
|
+
}
|
|
1082
|
+
get requirements() {
|
|
1083
|
+
return this._requirements;
|
|
1084
|
+
}
|
|
1085
|
+
set requirements(value) {
|
|
1086
|
+
this._requirements = value;
|
|
1087
|
+
}
|
|
1088
|
+
hasMedia(root, args, namedArgs) {
|
|
1089
|
+
const { media } = root;
|
|
1090
|
+
const data = this._getMediaData(media, args, namedArgs);
|
|
1091
|
+
return data !== null;
|
|
1092
|
+
}
|
|
1093
|
+
getMedia(root, args, namedArgs, handlebars) {
|
|
1094
|
+
const { media } = root;
|
|
1095
|
+
const data = this._getMediaData(media, args, namedArgs);
|
|
1096
|
+
if (data !== null) {
|
|
1097
|
+
const result = this._getFormattedValue(data, namedArgs, handlebars);
|
|
1098
|
+
if (typeof result === "string") return result.replaceAll("\n", "<br>\n");
|
|
1099
|
+
}
|
|
1100
|
+
const defaultValue = namedArgs.default;
|
|
1101
|
+
return defaultValue === null || typeof defaultValue === "string" ? defaultValue : "";
|
|
1102
|
+
}
|
|
1103
|
+
_addRequirement(value) {
|
|
1104
|
+
if (this._requirements === null) return;
|
|
1105
|
+
this._requirements.push(value);
|
|
1106
|
+
}
|
|
1107
|
+
_getFormattedValue(data, namedArgs, handlebars) {
|
|
1108
|
+
let { value } = data;
|
|
1109
|
+
const { escape = true } = namedArgs;
|
|
1110
|
+
if (escape) value = handlebars.Utils.escapeExpression(value);
|
|
1111
|
+
return value;
|
|
1112
|
+
}
|
|
1113
|
+
_getMediaData(media, args, namedArgs) {
|
|
1114
|
+
const type = args[0];
|
|
1115
|
+
switch (type) {
|
|
1116
|
+
case "audio": return this._getSimpleMediaData(media, "audio");
|
|
1117
|
+
case "screenshot": return this._getSimpleMediaData(media, "screenshot");
|
|
1118
|
+
case "clipboardImage": return this._getSimpleMediaData(media, "clipboardImage");
|
|
1119
|
+
case "clipboardText": return this._getSimpleMediaData(media, "clipboardText");
|
|
1120
|
+
case "popupSelectionText": return this._getSimpleMediaData(media, "popupSelectionText");
|
|
1121
|
+
case "textFurigana": return this._getTextFurigana(media, args[1], namedArgs, "furiganaHtml");
|
|
1122
|
+
case "textFuriganaPlain": return this._getTextFurigana(media, args[1], namedArgs, "furiganaPlain");
|
|
1123
|
+
case "dictionaryMedia": return this._getDictionaryMedia(media, args[1], namedArgs);
|
|
1124
|
+
default: return null;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
_getSimpleMediaData(media, type) {
|
|
1128
|
+
const result = media[type];
|
|
1129
|
+
if (typeof result === "object" && result !== null) return result;
|
|
1130
|
+
this._addRequirement({ type });
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
_getDictionaryMedia(media, path, namedArgs) {
|
|
1134
|
+
if (typeof path !== "string") return null;
|
|
1135
|
+
const { dictionaryMedia } = media;
|
|
1136
|
+
const { dictionary } = namedArgs;
|
|
1137
|
+
if (typeof dictionary !== "string") return null;
|
|
1138
|
+
if (typeof dictionaryMedia !== "undefined" && Object.prototype.hasOwnProperty.call(dictionaryMedia, dictionary)) {
|
|
1139
|
+
const dictionaryMedia2 = dictionaryMedia[dictionary];
|
|
1140
|
+
if (Object.prototype.hasOwnProperty.call(dictionaryMedia2, path)) {
|
|
1141
|
+
const result = dictionaryMedia2[path];
|
|
1142
|
+
if (typeof result === "object" && result !== null) return result;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
this._addRequirement({
|
|
1146
|
+
type: "dictionaryMedia",
|
|
1147
|
+
dictionary,
|
|
1148
|
+
path
|
|
1149
|
+
});
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
_getTextFurigana(media, text, namedArgs, furiganaFormat) {
|
|
1153
|
+
if (typeof text !== "string") return null;
|
|
1154
|
+
const readingMode = this._normalizeReadingMode(namedArgs.readingMode);
|
|
1155
|
+
const { textFurigana } = media;
|
|
1156
|
+
if (Array.isArray(textFurigana)) for (const entry of textFurigana) {
|
|
1157
|
+
if (entry.text !== text || entry.readingMode !== readingMode) continue;
|
|
1158
|
+
switch (furiganaFormat) {
|
|
1159
|
+
case "furiganaHtml": return entry.detailsHtml;
|
|
1160
|
+
case "furiganaPlain": return entry.detailsPlain;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
this._addRequirement({
|
|
1164
|
+
type: "textFurigana",
|
|
1165
|
+
text,
|
|
1166
|
+
readingMode
|
|
1167
|
+
});
|
|
1168
|
+
return null;
|
|
1169
|
+
}
|
|
1170
|
+
_normalizeReadingMode(value) {
|
|
1171
|
+
switch (value) {
|
|
1172
|
+
case "hiragana":
|
|
1173
|
+
case "katakana": return value;
|
|
1174
|
+
default: return null;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
var TemplateRendererCore = class {
|
|
1179
|
+
_cache;
|
|
1180
|
+
_cacheMaxSize;
|
|
1181
|
+
_dataTypes;
|
|
1182
|
+
_renderSetup;
|
|
1183
|
+
_renderCleanup;
|
|
1184
|
+
_handlebars;
|
|
1185
|
+
constructor(handlebars) {
|
|
1186
|
+
this._cache = new Map();
|
|
1187
|
+
this._cacheMaxSize = 5;
|
|
1188
|
+
this._dataTypes = new Map();
|
|
1189
|
+
this._renderSetup = null;
|
|
1190
|
+
this._renderCleanup = null;
|
|
1191
|
+
this._handlebars = handlebars;
|
|
1192
|
+
}
|
|
1193
|
+
registerHelpers(helpers) {
|
|
1194
|
+
for (const [name, helper] of helpers) this._registerHelper(name, helper);
|
|
1195
|
+
}
|
|
1196
|
+
registerDataType(name, details) {
|
|
1197
|
+
this._dataTypes.set(name, details);
|
|
1198
|
+
}
|
|
1199
|
+
setRenderCallbacks(setup, cleanup) {
|
|
1200
|
+
this._renderSetup = setup;
|
|
1201
|
+
this._renderCleanup = cleanup;
|
|
1202
|
+
}
|
|
1203
|
+
render(template, data, type) {
|
|
1204
|
+
const instance = this._getTemplateInstance(template);
|
|
1205
|
+
const modifiedData = this._getModifiedData(data, void 0, type);
|
|
1206
|
+
return this._renderTemplate(instance, modifiedData);
|
|
1207
|
+
}
|
|
1208
|
+
renderMulti(items) {
|
|
1209
|
+
const results = [];
|
|
1210
|
+
for (const { template, templateItems } of items) {
|
|
1211
|
+
const instance = this._getTemplateInstance(template);
|
|
1212
|
+
for (const { type, commonData, datas } of templateItems) for (const data of datas) {
|
|
1213
|
+
let result;
|
|
1214
|
+
try {
|
|
1215
|
+
const data2 = this._getModifiedData(data, commonData, type);
|
|
1216
|
+
const renderResult = this._renderTemplate(instance, data2);
|
|
1217
|
+
result = { result: renderResult };
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
const e = error instanceof Error ? error : new Error(`${error}`);
|
|
1220
|
+
result = { error: {
|
|
1221
|
+
name: e.name,
|
|
1222
|
+
message: e.message,
|
|
1223
|
+
stack: e.stack
|
|
1224
|
+
} };
|
|
1225
|
+
}
|
|
1226
|
+
results.push(result);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return results;
|
|
1230
|
+
}
|
|
1231
|
+
getModifiedData(data, type) {
|
|
1232
|
+
return this._getModifiedData(data, void 0, type);
|
|
1233
|
+
}
|
|
1234
|
+
_getTemplateInstance(template) {
|
|
1235
|
+
const cache = this._cache;
|
|
1236
|
+
let instance = cache.get(template);
|
|
1237
|
+
if (typeof instance === "undefined") {
|
|
1238
|
+
this._updateCacheSize(this._cacheMaxSize - 1);
|
|
1239
|
+
instance = this._handlebars.compileAST(template);
|
|
1240
|
+
cache.set(template, instance);
|
|
1241
|
+
}
|
|
1242
|
+
return instance;
|
|
1243
|
+
}
|
|
1244
|
+
_renderTemplate(instance, data) {
|
|
1245
|
+
const renderSetup = this._renderSetup;
|
|
1246
|
+
const renderCleanup = this._renderCleanup;
|
|
1247
|
+
let result;
|
|
1248
|
+
let additions1;
|
|
1249
|
+
try {
|
|
1250
|
+
additions1 = typeof renderSetup === "function" ? renderSetup(data) : null;
|
|
1251
|
+
result = instance(data).replace(/^\n+|\n+$/g, "");
|
|
1252
|
+
} finally {
|
|
1253
|
+
if (typeof renderCleanup === "function") renderCleanup(data);
|
|
1254
|
+
}
|
|
1255
|
+
return {
|
|
1256
|
+
result,
|
|
1257
|
+
requirements: additions1?.requirements ?? []
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
_getModifiedData(data, commonData, type) {
|
|
1261
|
+
if (typeof type === "string") {
|
|
1262
|
+
const typeInfo = this._dataTypes.get(type);
|
|
1263
|
+
if (typeof typeInfo !== "undefined") {
|
|
1264
|
+
let compositeData;
|
|
1265
|
+
if (typeof commonData !== "undefined") compositeData = typeInfo.composeData(data, commonData);
|
|
1266
|
+
else if (typeof data.commonData === "undefined") throw new Error("Incomplete data");
|
|
1267
|
+
else compositeData = data;
|
|
1268
|
+
return typeInfo.modifier(compositeData);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
throw new Error(`Invalid type: ${type}`);
|
|
1272
|
+
}
|
|
1273
|
+
_updateCacheSize(maxSize) {
|
|
1274
|
+
const cache = this._cache;
|
|
1275
|
+
let removeCount = cache.size - maxSize;
|
|
1276
|
+
if (removeCount <= 0) return;
|
|
1277
|
+
for (const key of cache.keys()) {
|
|
1278
|
+
cache.delete(key);
|
|
1279
|
+
if (--removeCount <= 0) break;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
_registerHelper(name, helper) {
|
|
1283
|
+
const wrapper = function(...args) {
|
|
1284
|
+
const argCountM1 = Math.max(0, args.length - 1);
|
|
1285
|
+
const options = args[argCountM1];
|
|
1286
|
+
args.length = argCountM1;
|
|
1287
|
+
return helper(args, this, options);
|
|
1288
|
+
};
|
|
1289
|
+
this._handlebars.registerHelper(name, wrapper);
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
/**
|
|
1293
|
+
* This class contains all Anki-specific template rendering functionality.
|
|
1294
|
+
* It registers all 25+ Handlebars helpers and provides template rendering.
|
|
1295
|
+
*
|
|
1296
|
+
* Requires a Handlebars-compatible engine to be injected via the constructor.
|
|
1297
|
+
*/
|
|
1298
|
+
var AnkiTemplateRenderer = class {
|
|
1299
|
+
_templateRenderer;
|
|
1300
|
+
_mediaProvider;
|
|
1301
|
+
_stateStack;
|
|
1302
|
+
_requirements;
|
|
1303
|
+
_cleanupCallbacks;
|
|
1304
|
+
_handlebars;
|
|
1305
|
+
constructor(handlebars) {
|
|
1306
|
+
this._handlebars = handlebars;
|
|
1307
|
+
this._templateRenderer = new TemplateRendererCore(handlebars);
|
|
1308
|
+
this._mediaProvider = new TemplateRendererMediaProvider();
|
|
1309
|
+
this._stateStack = null;
|
|
1310
|
+
this._requirements = null;
|
|
1311
|
+
this._cleanupCallbacks = [];
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Gets the core TemplateRenderer instance.
|
|
1315
|
+
*/
|
|
1316
|
+
get templateRenderer() {
|
|
1317
|
+
return this._templateRenderer;
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Prepares the renderer by registering all Handlebars helpers and data types.
|
|
1321
|
+
*/
|
|
1322
|
+
async prepare() {
|
|
1323
|
+
this._templateRenderer.registerHelpers([
|
|
1324
|
+
["dumpObject", this._dumpObject.bind(this)],
|
|
1325
|
+
["furigana", this._furigana.bind(this)],
|
|
1326
|
+
["furiganaPlain", this._furiganaPlain.bind(this)],
|
|
1327
|
+
["multiLine", this._multiLine.bind(this)],
|
|
1328
|
+
["regexReplace", this._regexReplace.bind(this)],
|
|
1329
|
+
["regexMatch", this._regexMatch.bind(this)],
|
|
1330
|
+
["mergeTags", this._mergeTags.bind(this)],
|
|
1331
|
+
["eachUpTo", this._eachUpTo.bind(this)],
|
|
1332
|
+
["spread", this._spread.bind(this)],
|
|
1333
|
+
["op", this._op.bind(this)],
|
|
1334
|
+
["get", this._get.bind(this)],
|
|
1335
|
+
["set", this._set.bind(this)],
|
|
1336
|
+
["scope", this._scope.bind(this)],
|
|
1337
|
+
["property", this._property.bind(this)],
|
|
1338
|
+
["noop", this._noop.bind(this)],
|
|
1339
|
+
["isMoraPitchHigh", this._isMoraPitchHigh.bind(this)],
|
|
1340
|
+
["getKanaMorae", this._getKanaMorae.bind(this)],
|
|
1341
|
+
["typeof", this._getTypeof.bind(this)],
|
|
1342
|
+
["join", this._join.bind(this)],
|
|
1343
|
+
["concat", this._concat.bind(this)],
|
|
1344
|
+
["pitchCategories", this._pitchCategories.bind(this)],
|
|
1345
|
+
["formatGlossary", this._formatGlossary.bind(this)],
|
|
1346
|
+
["hasMedia", this._hasMedia.bind(this)],
|
|
1347
|
+
["getMedia", this._getMedia.bind(this)],
|
|
1348
|
+
["hiragana", this._hiragana.bind(this)],
|
|
1349
|
+
["katakana", this._katakana.bind(this)]
|
|
1350
|
+
]);
|
|
1351
|
+
this._templateRenderer.registerDataType("ankiNote", {
|
|
1352
|
+
modifier: ({ marker, commonData }) => createAnkiNoteData(marker, commonData),
|
|
1353
|
+
composeData: ({ marker }, commonData) => ({
|
|
1354
|
+
marker,
|
|
1355
|
+
commonData
|
|
1356
|
+
})
|
|
1357
|
+
});
|
|
1358
|
+
this._templateRenderer.setRenderCallbacks(this._onRenderSetup.bind(this), this._onRenderCleanup.bind(this));
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Renders a single template with given data.
|
|
1362
|
+
*/
|
|
1363
|
+
render(template, data, type) {
|
|
1364
|
+
return this._templateRenderer.render(template, data, type);
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Renders multiple templates with given data.
|
|
1368
|
+
*/
|
|
1369
|
+
renderMulti(items) {
|
|
1370
|
+
return this._templateRenderer.renderMulti(items);
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Gets modified data for a given type.
|
|
1374
|
+
*/
|
|
1375
|
+
getModifiedData(data, type) {
|
|
1376
|
+
return this._templateRenderer.getModifiedData(data, type);
|
|
1377
|
+
}
|
|
1378
|
+
_onRenderSetup() {
|
|
1379
|
+
const requirements = [];
|
|
1380
|
+
this._stateStack = [new Map()];
|
|
1381
|
+
this._requirements = requirements;
|
|
1382
|
+
this._mediaProvider.requirements = requirements;
|
|
1383
|
+
return { requirements };
|
|
1384
|
+
}
|
|
1385
|
+
_onRenderCleanup() {
|
|
1386
|
+
for (const callback of this._cleanupCallbacks) callback();
|
|
1387
|
+
this._stateStack = null;
|
|
1388
|
+
this._requirements = null;
|
|
1389
|
+
this._mediaProvider.requirements = null;
|
|
1390
|
+
this._cleanupCallbacks.length = 0;
|
|
1391
|
+
}
|
|
1392
|
+
_safeString(text) {
|
|
1393
|
+
return new this._handlebars.SafeString(text);
|
|
1394
|
+
}
|
|
1395
|
+
_dumpObject(args) {
|
|
1396
|
+
return JSON.stringify(args[0], null, 4);
|
|
1397
|
+
}
|
|
1398
|
+
_furigana(args, context, options) {
|
|
1399
|
+
const { expression, reading } = this._getFuriganaExpressionAndReading(args);
|
|
1400
|
+
const segments = distributeFurigana(expression, reading);
|
|
1401
|
+
let result = "";
|
|
1402
|
+
for (const { text, reading: reading2 } of segments) result += reading2.length > 0 ? `<ruby>${text}<rt>${reading2}</rt></ruby>` : text;
|
|
1403
|
+
return this._safeString(result);
|
|
1404
|
+
}
|
|
1405
|
+
_furiganaPlain(args) {
|
|
1406
|
+
const { expression, reading } = this._getFuriganaExpressionAndReading(args);
|
|
1407
|
+
const segments = distributeFurigana(expression, reading);
|
|
1408
|
+
let result = "";
|
|
1409
|
+
for (const { text, reading: reading2 } of segments) if (reading2.length > 0) {
|
|
1410
|
+
if (result.length > 0) result += " ";
|
|
1411
|
+
result += `${text}[${reading2}]`;
|
|
1412
|
+
} else result += text;
|
|
1413
|
+
return result;
|
|
1414
|
+
}
|
|
1415
|
+
_getFuriganaExpressionAndReading(args) {
|
|
1416
|
+
let expression;
|
|
1417
|
+
let reading;
|
|
1418
|
+
if (args.length >= 2) [expression, reading] = args;
|
|
1419
|
+
else {
|
|
1420
|
+
const obj = args[0];
|
|
1421
|
+
({expression, reading} = obj);
|
|
1422
|
+
}
|
|
1423
|
+
return {
|
|
1424
|
+
expression: typeof expression === "string" ? expression : "",
|
|
1425
|
+
reading: typeof reading === "string" ? reading : ""
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
_stringToMultiLineHtml(string) {
|
|
1429
|
+
return string.split("\n").join("<br>");
|
|
1430
|
+
}
|
|
1431
|
+
_multiLine(_args, context, options) {
|
|
1432
|
+
return this._safeString(this._stringToMultiLineHtml(this._computeValueString(options, context)));
|
|
1433
|
+
}
|
|
1434
|
+
_regexReplace(args, context, options) {
|
|
1435
|
+
const argCount = args.length;
|
|
1436
|
+
let value = this._computeValueString(options, context);
|
|
1437
|
+
if (argCount > 3) value = `${args.slice(3).join("")}${value}`;
|
|
1438
|
+
if (argCount > 1) try {
|
|
1439
|
+
const [pattern, replacement, flags] = args;
|
|
1440
|
+
if (typeof pattern !== "string") throw new Error("Invalid pattern");
|
|
1441
|
+
if (typeof replacement !== "string") throw new Error("Invalid replacement");
|
|
1442
|
+
const regex = new RegExp(pattern, typeof flags === "string" ? flags : "g");
|
|
1443
|
+
value = value.replace(regex, replacement);
|
|
1444
|
+
} catch (e) {
|
|
1445
|
+
return `${e}`;
|
|
1446
|
+
}
|
|
1447
|
+
return value;
|
|
1448
|
+
}
|
|
1449
|
+
_regexMatch(args, context, options) {
|
|
1450
|
+
const argCount = args.length;
|
|
1451
|
+
let value = this._computeValueString(options, context);
|
|
1452
|
+
if (argCount > 2) value = `${args.slice(2).join("")}${value}`;
|
|
1453
|
+
if (argCount > 0) try {
|
|
1454
|
+
const [pattern, flags] = args;
|
|
1455
|
+
if (typeof pattern !== "string") throw new Error("Invalid pattern");
|
|
1456
|
+
const regex = new RegExp(pattern, typeof flags === "string" ? flags : "");
|
|
1457
|
+
const parts = [];
|
|
1458
|
+
value.replace(regex, (g0) => {
|
|
1459
|
+
parts.push(g0);
|
|
1460
|
+
return g0;
|
|
1461
|
+
});
|
|
1462
|
+
value = parts.join("");
|
|
1463
|
+
} catch (e) {
|
|
1464
|
+
return `${e}`;
|
|
1465
|
+
}
|
|
1466
|
+
return value;
|
|
1467
|
+
}
|
|
1468
|
+
_mergeTags(args) {
|
|
1469
|
+
const [object, isGroupMode, isMergeMode] = args;
|
|
1470
|
+
const tagSources = [];
|
|
1471
|
+
if (Array.isArray(object.termTags)) tagSources.push(object.termTags);
|
|
1472
|
+
if (isGroupMode || isMergeMode) {
|
|
1473
|
+
const { definitions } = object;
|
|
1474
|
+
if (Array.isArray(definitions)) for (const definition of definitions) tagSources.push(definition.definitionTags);
|
|
1475
|
+
} else if (Array.isArray(object.definitionTags)) tagSources.push(object.definitionTags);
|
|
1476
|
+
const tags = new Set();
|
|
1477
|
+
for (const tagSource of tagSources) for (const tag of tagSource) tags.add(tag.name);
|
|
1478
|
+
return [...tags].join(", ");
|
|
1479
|
+
}
|
|
1480
|
+
_eachUpTo(args, context, options) {
|
|
1481
|
+
const [iterable, maxCount] = args;
|
|
1482
|
+
if (iterable) {
|
|
1483
|
+
const results = [];
|
|
1484
|
+
let any = false;
|
|
1485
|
+
for (const entry of iterable) {
|
|
1486
|
+
any = true;
|
|
1487
|
+
if (results.length >= maxCount) break;
|
|
1488
|
+
const processedEntry = this._computeValue(options, entry);
|
|
1489
|
+
results.push(`${processedEntry}`);
|
|
1490
|
+
}
|
|
1491
|
+
if (any) return results.join("");
|
|
1492
|
+
}
|
|
1493
|
+
return this._computeInverseString(options, context);
|
|
1494
|
+
}
|
|
1495
|
+
_spread(args) {
|
|
1496
|
+
const result = [];
|
|
1497
|
+
for (const array of args) try {
|
|
1498
|
+
result.push(...array);
|
|
1499
|
+
} catch (_e) {}
|
|
1500
|
+
return result;
|
|
1501
|
+
}
|
|
1502
|
+
_op(args) {
|
|
1503
|
+
const [operator] = args;
|
|
1504
|
+
switch (args.length) {
|
|
1505
|
+
case 2: return this._evaluateUnaryExpression(operator, args[1]);
|
|
1506
|
+
case 3: return this._evaluateBinaryExpression(operator, args[1], args[2]);
|
|
1507
|
+
case 4: return this._evaluateTernaryExpression(operator, args[1], args[2], args[3]);
|
|
1508
|
+
default: return void 0;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
_evaluateUnaryExpression(operator, operand1) {
|
|
1512
|
+
switch (operator) {
|
|
1513
|
+
case "+": return +operand1;
|
|
1514
|
+
case "-": return -operand1;
|
|
1515
|
+
case "~": return ~operand1;
|
|
1516
|
+
case "!": return !operand1;
|
|
1517
|
+
default: return void 0;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
_evaluateBinaryExpression(operator, operand1, operand2) {
|
|
1521
|
+
switch (operator) {
|
|
1522
|
+
case "+": return operand1 + operand2;
|
|
1523
|
+
case "-": return operand1 - operand2;
|
|
1524
|
+
case "/": return operand1 / operand2;
|
|
1525
|
+
case "*": return operand1 * operand2;
|
|
1526
|
+
case "%": return operand1 % operand2;
|
|
1527
|
+
case "**": return operand1 ** operand2;
|
|
1528
|
+
case "==": return operand1 === operand2;
|
|
1529
|
+
case "!=": return operand1 !== operand2;
|
|
1530
|
+
case "===": return operand1 === operand2;
|
|
1531
|
+
case "!==": return operand1 !== operand2;
|
|
1532
|
+
case "<": return operand1 < operand2;
|
|
1533
|
+
case "<=": return operand1 <= operand2;
|
|
1534
|
+
case ">": return operand1 > operand2;
|
|
1535
|
+
case ">=": return operand1 >= operand2;
|
|
1536
|
+
case "<<": return operand1 << operand2;
|
|
1537
|
+
case ">>": return operand1 >> operand2;
|
|
1538
|
+
case ">>>": return operand1 >>> operand2;
|
|
1539
|
+
case "&": return operand1 & operand2;
|
|
1540
|
+
case "|": return operand1 | operand2;
|
|
1541
|
+
case "^": return operand1 ^ operand2;
|
|
1542
|
+
case "&&": return operand1 && operand2;
|
|
1543
|
+
case "||": return operand1 || operand2;
|
|
1544
|
+
default: return void 0;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
_evaluateTernaryExpression(operator, operand1, operand2, operand3) {
|
|
1548
|
+
switch (operator) {
|
|
1549
|
+
case "?:": return operand1 ? operand2 : operand3;
|
|
1550
|
+
default: return void 0;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
_get(args) {
|
|
1554
|
+
const [key] = args;
|
|
1555
|
+
const stateStack = this._stateStack;
|
|
1556
|
+
if (stateStack === null) throw new Error("Invalid state");
|
|
1557
|
+
for (let i = stateStack.length; --i >= 0;) {
|
|
1558
|
+
const map = stateStack[i];
|
|
1559
|
+
if (map.has(key)) return map.get(key);
|
|
1560
|
+
}
|
|
1561
|
+
return void 0;
|
|
1562
|
+
}
|
|
1563
|
+
_set(args, context, options) {
|
|
1564
|
+
const stateStack = this._stateStack;
|
|
1565
|
+
if (stateStack === null) throw new Error("Invalid state");
|
|
1566
|
+
switch (args.length) {
|
|
1567
|
+
case 1:
|
|
1568
|
+
{
|
|
1569
|
+
const [key] = args;
|
|
1570
|
+
const value = this._computeValue(options, context);
|
|
1571
|
+
stateStack[stateStack.length - 1].set(key, value);
|
|
1572
|
+
}
|
|
1573
|
+
break;
|
|
1574
|
+
case 2:
|
|
1575
|
+
{
|
|
1576
|
+
const [key, value] = args;
|
|
1577
|
+
stateStack[stateStack.length - 1].set(key, value);
|
|
1578
|
+
}
|
|
1579
|
+
break;
|
|
1580
|
+
}
|
|
1581
|
+
return "";
|
|
1582
|
+
}
|
|
1583
|
+
_scope(_args, context, options) {
|
|
1584
|
+
const stateStack = this._stateStack;
|
|
1585
|
+
if (stateStack === null) throw new Error("Invalid state");
|
|
1586
|
+
try {
|
|
1587
|
+
stateStack.push(new Map());
|
|
1588
|
+
return this._computeValue(options, context);
|
|
1589
|
+
} finally {
|
|
1590
|
+
if (stateStack.length > 1) stateStack.pop();
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
_property(args) {
|
|
1594
|
+
const ii = args.length;
|
|
1595
|
+
if (ii <= 0) return void 0;
|
|
1596
|
+
try {
|
|
1597
|
+
let value = args[0];
|
|
1598
|
+
for (let i = 1; i < ii; ++i) {
|
|
1599
|
+
if (typeof value !== "object" || value === null) throw new Error("Invalid object");
|
|
1600
|
+
const key = args[i];
|
|
1601
|
+
switch (typeof key) {
|
|
1602
|
+
case "number":
|
|
1603
|
+
case "string":
|
|
1604
|
+
case "symbol": break;
|
|
1605
|
+
default: throw new Error("Invalid key");
|
|
1606
|
+
}
|
|
1607
|
+
value = value[key];
|
|
1608
|
+
}
|
|
1609
|
+
return value;
|
|
1610
|
+
} catch (_e) {
|
|
1611
|
+
return void 0;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
_noop(_args, context, options) {
|
|
1615
|
+
return this._computeValue(options, context);
|
|
1616
|
+
}
|
|
1617
|
+
_isMoraPitchHigh(args) {
|
|
1618
|
+
const [index, position] = args;
|
|
1619
|
+
return isMoraPitchHigh(index, position);
|
|
1620
|
+
}
|
|
1621
|
+
_getKanaMorae(args) {
|
|
1622
|
+
const [text] = args;
|
|
1623
|
+
return getKanaMorae(`${text}`);
|
|
1624
|
+
}
|
|
1625
|
+
_getTypeof(args, context, options) {
|
|
1626
|
+
const ii = args.length;
|
|
1627
|
+
const value = ii > 0 ? args[0] : this._computeValue(options, context);
|
|
1628
|
+
return typeof value;
|
|
1629
|
+
}
|
|
1630
|
+
_join(args) {
|
|
1631
|
+
return args.length > 0 ? args.slice(1, args.length).flat().join(args[0]) : "";
|
|
1632
|
+
}
|
|
1633
|
+
_concat(args) {
|
|
1634
|
+
let result = "";
|
|
1635
|
+
for (let i = 0, ii = args.length; i < ii; ++i) result += args[i];
|
|
1636
|
+
return result;
|
|
1637
|
+
}
|
|
1638
|
+
_pitchCategories(args) {
|
|
1639
|
+
const [data] = args;
|
|
1640
|
+
const { dictionaryEntry } = data;
|
|
1641
|
+
if (dictionaryEntry.type !== "term") return [];
|
|
1642
|
+
const { pronunciations: termPronunciations, headwords } = dictionaryEntry;
|
|
1643
|
+
const categories = new Set();
|
|
1644
|
+
for (const { headwordIndex, pronunciations } of termPronunciations) {
|
|
1645
|
+
const { reading, wordClasses } = headwords[headwordIndex];
|
|
1646
|
+
const isVerbOrAdjective = isNonNounVerbOrAdjective(wordClasses);
|
|
1647
|
+
const pitches = getPronunciationsOfType(pronunciations, "pitch-accent");
|
|
1648
|
+
for (const { positions } of pitches) {
|
|
1649
|
+
const category = getPitchCategory(reading, positions, isVerbOrAdjective);
|
|
1650
|
+
if (category !== null) categories.add(category);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return [...categories];
|
|
1654
|
+
}
|
|
1655
|
+
_formatGlossary(args, _context, options) {
|
|
1656
|
+
const [_dictionary, content] = args;
|
|
1657
|
+
if (typeof content === "string") return this._safeString(this._stringToMultiLineHtml(content));
|
|
1658
|
+
if (!(typeof content === "object" && content !== null)) return "";
|
|
1659
|
+
switch (content.type) {
|
|
1660
|
+
case "text": return this._safeString(this._stringToMultiLineHtml(content.text));
|
|
1661
|
+
case "image": return "";
|
|
1662
|
+
case "structured-content": return this._safeString("[structured content]");
|
|
1663
|
+
}
|
|
1664
|
+
return "";
|
|
1665
|
+
}
|
|
1666
|
+
_hasMedia(args, _context, options) {
|
|
1667
|
+
const data = this._getNoteDataFromOptions(options);
|
|
1668
|
+
return this._mediaProvider.hasMedia(data, args, options.hash);
|
|
1669
|
+
}
|
|
1670
|
+
_getMedia(args, _context, options) {
|
|
1671
|
+
const data = this._getNoteDataFromOptions(options);
|
|
1672
|
+
return this._mediaProvider.getMedia(data, args, options.hash, this._handlebars);
|
|
1673
|
+
}
|
|
1674
|
+
_hiragana(args, context, options) {
|
|
1675
|
+
const ii = args.length;
|
|
1676
|
+
const value = ii > 0 ? args[0] : this._computeValue(options, context);
|
|
1677
|
+
return typeof value === "string" ? convertKatakanaToHiragana(value) : "";
|
|
1678
|
+
}
|
|
1679
|
+
_katakana(args, context, options) {
|
|
1680
|
+
const ii = args.length;
|
|
1681
|
+
const value = ii > 0 ? args[0] : this._computeValue(options, context);
|
|
1682
|
+
return typeof value === "string" ? convertHiraganaToKatakana(value) : "";
|
|
1683
|
+
}
|
|
1684
|
+
_getNoteDataFromOptions(options) {
|
|
1685
|
+
return options.data.root;
|
|
1686
|
+
}
|
|
1687
|
+
_asString(value) {
|
|
1688
|
+
return typeof value === "string" ? value : `${value}`;
|
|
1689
|
+
}
|
|
1690
|
+
_computeValue(options, context) {
|
|
1691
|
+
return typeof options.fn === "function" ? options.fn(context) : "";
|
|
1692
|
+
}
|
|
1693
|
+
_computeValueString(options, context) {
|
|
1694
|
+
return this._asString(this._computeValue(options, context));
|
|
1695
|
+
}
|
|
1696
|
+
_computeInverse(options, context) {
|
|
1697
|
+
return typeof options.inverse === "function" ? options.inverse(context) : "";
|
|
1698
|
+
}
|
|
1699
|
+
_computeInverseString(options, context) {
|
|
1700
|
+
return this._asString(this._computeInverse(options, context));
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
const HIRAGANA_CONVERSION_RANGE = [12353, 12438];
|
|
1704
|
+
const KATAKANA_CONVERSION_RANGE = [12449, 12534];
|
|
1705
|
+
const KANA_PROLONGED_SOUND_MARK_CODE_POINT = 12540;
|
|
1706
|
+
function convertKatakanaToHiragana(text, keepProlongedSoundMarks = false) {
|
|
1707
|
+
let result = "";
|
|
1708
|
+
const offset = HIRAGANA_CONVERSION_RANGE[0] - KATAKANA_CONVERSION_RANGE[0];
|
|
1709
|
+
for (const char of text) {
|
|
1710
|
+
const codePoint = char.codePointAt(0);
|
|
1711
|
+
if (codePoint === KANA_PROLONGED_SOUND_MARK_CODE_POINT && !keepProlongedSoundMarks) result += char;
|
|
1712
|
+
else if (codePoint >= KATAKANA_CONVERSION_RANGE[0] && codePoint <= KATAKANA_CONVERSION_RANGE[1]) result += String.fromCodePoint(codePoint + offset);
|
|
1713
|
+
else result += char;
|
|
1714
|
+
}
|
|
1715
|
+
return result;
|
|
1716
|
+
}
|
|
1717
|
+
function convertHiraganaToKatakana(text) {
|
|
1718
|
+
let result = "";
|
|
1719
|
+
const offset = KATAKANA_CONVERSION_RANGE[0] - HIRAGANA_CONVERSION_RANGE[0];
|
|
1720
|
+
for (const char of text) {
|
|
1721
|
+
const codePoint = char.codePointAt(0);
|
|
1722
|
+
if (codePoint >= HIRAGANA_CONVERSION_RANGE[0] && codePoint <= HIRAGANA_CONVERSION_RANGE[1]) result += String.fromCodePoint(codePoint + offset);
|
|
1723
|
+
else result += char;
|
|
1724
|
+
}
|
|
1725
|
+
return result;
|
|
1726
|
+
}
|
|
1727
|
+
function isMoraPitchHigh(moraIndex, pitchAccentPosition) {
|
|
1728
|
+
const pos = typeof pitchAccentPosition === "string" ? Number.parseInt(pitchAccentPosition, 10) : pitchAccentPosition;
|
|
1729
|
+
if (pos === 0) return moraIndex > 0;
|
|
1730
|
+
if (moraIndex === 0) return pos > 1;
|
|
1731
|
+
return moraIndex < pos;
|
|
1732
|
+
}
|
|
1733
|
+
function getKanaMorae(text) {
|
|
1734
|
+
const morae = [];
|
|
1735
|
+
const SMALL_KANA_SET = new Set("ぁぃぅぇぉゃゅょゎァィゥェォャュョヮ");
|
|
1736
|
+
for (const char of text) if (morae.length > 0 && SMALL_KANA_SET.has(char)) morae[morae.length - 1] += char;
|
|
1737
|
+
else morae.push(char);
|
|
1738
|
+
return morae;
|
|
1739
|
+
}
|
|
1740
|
+
function getPitchCategory(reading, positions, isVerbOrAdjective) {
|
|
1741
|
+
const pos = typeof positions === "string" ? Number.parseInt(positions, 10) : positions;
|
|
1742
|
+
if (pos === 0) return "heiban";
|
|
1743
|
+
if (pos === 1) return "atamadaka";
|
|
1744
|
+
const morae = getKanaMorae(reading);
|
|
1745
|
+
if (pos >= morae.length) return isVerbOrAdjective ? "kifuku" : "odaka";
|
|
1746
|
+
return isVerbOrAdjective ? "kifuku" : "nakadaka";
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
//#endregion
|
|
1750
|
+
export { AnkiConnect, AnkiNoteBuilder, AnkiTemplateRenderer, createAnkiNoteData, createCachedValue, getCachedValue, getFrequencyHarmonic };
|
|
1751
|
+
//# sourceMappingURL=anki.js.map
|