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.
Files changed (207) hide show
  1. package/README.md +485 -0
  2. package/dist/anki-connect-BQyCGW3O.cjs +513 -0
  3. package/dist/anki-connect-BQyCGW3O.cjs.map +1 -0
  4. package/dist/anki-connect-CPPuhyiQ.js +6 -0
  5. package/dist/anki-connect-DbrQHphS.js +495 -0
  6. package/dist/anki-connect-DbrQHphS.js.map +1 -0
  7. package/dist/anki-connect-DcheJrp-.cjs +6 -0
  8. package/dist/anki.cjs +1758 -0
  9. package/dist/anki.cjs.map +1 -0
  10. package/dist/anki.d.cts +751 -0
  11. package/dist/anki.d.cts.map +1 -0
  12. package/dist/anki.d.ts +751 -0
  13. package/dist/anki.d.ts.map +1 -0
  14. package/dist/anki.js +1751 -0
  15. package/dist/anki.js.map +1 -0
  16. package/dist/audio-D9DvYyB7.d.cts +48 -0
  17. package/dist/audio-D9DvYyB7.d.cts.map +1 -0
  18. package/dist/audio-DQulUkDM.d.ts +48 -0
  19. package/dist/audio-DQulUkDM.d.ts.map +1 -0
  20. package/dist/audio-url-generator-BXvQaqUi.cjs +4 -0
  21. package/dist/audio-url-generator-Dy2hb2Mm.cjs +414 -0
  22. package/dist/audio-url-generator-Dy2hb2Mm.cjs.map +1 -0
  23. package/dist/audio-url-generator-Qi0rfzHz.js +4 -0
  24. package/dist/audio-url-generator-pFQAB5Nb.js +390 -0
  25. package/dist/audio-url-generator-pFQAB5Nb.js.map +1 -0
  26. package/dist/audio.cjs +7 -0
  27. package/dist/audio.d.cts +86 -0
  28. package/dist/audio.d.cts.map +1 -0
  29. package/dist/audio.d.ts +86 -0
  30. package/dist/audio.d.ts.map +1 -0
  31. package/dist/audio.js +4 -0
  32. package/dist/batch-processor-BR-gB3H3.js +84 -0
  33. package/dist/batch-processor-BR-gB3H3.js.map +1 -0
  34. package/dist/batch-processor-CSF1acTw.cjs +3 -0
  35. package/dist/batch-processor-DFqM_L-_.cjs +91 -0
  36. package/dist/batch-processor-DFqM_L-_.cjs.map +1 -0
  37. package/dist/batch-processor-Quo9jUyf.js +3 -0
  38. package/dist/chunk-BCwAaXi7.cjs +31 -0
  39. package/dist/cjk-util-Dp0ZU0sh.cjs +167 -0
  40. package/dist/cjk-util-Dp0ZU0sh.cjs.map +1 -0
  41. package/dist/cjk-util-DubXBGDG.js +94 -0
  42. package/dist/cjk-util-DubXBGDG.js.map +1 -0
  43. package/dist/core-BUpclilG.d.cts +102 -0
  44. package/dist/core-BUpclilG.d.cts.map +1 -0
  45. package/dist/core-DFUj5GtA.d.ts +102 -0
  46. package/dist/core-DFUj5GtA.d.ts.map +1 -0
  47. package/dist/database.cjs +7 -0
  48. package/dist/database.d.cts +4 -0
  49. package/dist/database.d.ts +4 -0
  50. package/dist/database.js +5 -0
  51. package/dist/dictionary-D7l-qFt1.d.cts +316 -0
  52. package/dist/dictionary-D7l-qFt1.d.cts.map +1 -0
  53. package/dist/dictionary-_vzfBLWi.d.ts +316 -0
  54. package/dist/dictionary-_vzfBLWi.d.ts.map +1 -0
  55. package/dist/dictionary-data-util-CHnRdYZ9.cjs +378 -0
  56. package/dist/dictionary-data-util-CHnRdYZ9.cjs.map +1 -0
  57. package/dist/dictionary-data-util-CfOLfEDE.js +323 -0
  58. package/dist/dictionary-data-util-CfOLfEDE.js.map +1 -0
  59. package/dist/dictionary-database-BDC2f9zc.d.ts +58 -0
  60. package/dist/dictionary-database-BDC2f9zc.d.ts.map +1 -0
  61. package/dist/dictionary-database-CU4TsvCC.js +393 -0
  62. package/dist/dictionary-database-CU4TsvCC.js.map +1 -0
  63. package/dist/dictionary-database-DsOi04Sg.d.cts +58 -0
  64. package/dist/dictionary-database-DsOi04Sg.d.cts.map +1 -0
  65. package/dist/dictionary-database-lvFvftnO.cjs +412 -0
  66. package/dist/dictionary-database-lvFvftnO.cjs.map +1 -0
  67. package/dist/dictionary-importer-BkQQSBhm.d.ts +237 -0
  68. package/dist/dictionary-importer-BkQQSBhm.d.ts.map +1 -0
  69. package/dist/dictionary-importer-Cen1z6co.js +1821 -0
  70. package/dist/dictionary-importer-Cen1z6co.js.map +1 -0
  71. package/dist/dictionary-importer-DYmmWmcX.cjs +8 -0
  72. package/dist/dictionary-importer-Da3AuTZw.d.cts +237 -0
  73. package/dist/dictionary-importer-Da3AuTZw.d.cts.map +1 -0
  74. package/dist/dictionary-importer-Dhn75iZ4.cjs +1834 -0
  75. package/dist/dictionary-importer-Dhn75iZ4.cjs.map +1 -0
  76. package/dist/dictionary-importer-xWkel0h-.js +8 -0
  77. package/dist/dictionary-update-checker-BNE4pGTx.js +4 -0
  78. package/dist/dictionary-update-checker-Byjvifd2.cjs +75 -0
  79. package/dist/dictionary-update-checker-Byjvifd2.cjs.map +1 -0
  80. package/dist/dictionary-update-checker-YdpalZ41.cjs +4 -0
  81. package/dist/dictionary-update-checker-kKukiovj.js +69 -0
  82. package/dist/dictionary-update-checker-kKukiovj.js.map +1 -0
  83. package/dist/display-generator-BGVWiI0t.js +746 -0
  84. package/dist/display-generator-BGVWiI0t.js.map +1 -0
  85. package/dist/display-generator-BMQmG5Ov.cjs +9 -0
  86. package/dist/display-generator-BxZ7mBjP.js +9 -0
  87. package/dist/display-generator-DyP-HNzP.cjs +758 -0
  88. package/dist/display-generator-DyP-HNzP.cjs.map +1 -0
  89. package/dist/errors-BSezaJwm.cjs +35 -0
  90. package/dist/errors-BSezaJwm.cjs.map +1 -0
  91. package/dist/errors-DuuDSO5N.js +22 -0
  92. package/dist/errors-DuuDSO5N.js.map +1 -0
  93. package/dist/frequency-ranking-BXjfhhUQ.js +3 -0
  94. package/dist/frequency-ranking-Cx1kkIrw.cjs +3 -0
  95. package/dist/frequency-ranking-DEJMTMdg.js +159 -0
  96. package/dist/frequency-ranking-DEJMTMdg.js.map +1 -0
  97. package/dist/frequency-ranking-DVYxTXN-.cjs +166 -0
  98. package/dist/frequency-ranking-DVYxTXN-.cjs.map +1 -0
  99. package/dist/furigana-5HK97CY8.js +4 -0
  100. package/dist/furigana-9bBI9-qe.d.ts +47 -0
  101. package/dist/furigana-9bBI9-qe.d.ts.map +1 -0
  102. package/dist/furigana-B3-0y231.js +471 -0
  103. package/dist/furigana-B3-0y231.js.map +1 -0
  104. package/dist/furigana-CjOhzvZt.d.cts +47 -0
  105. package/dist/furigana-CjOhzvZt.d.cts.map +1 -0
  106. package/dist/furigana-DpZLcues.cjs +609 -0
  107. package/dist/furigana-DpZLcues.cjs.map +1 -0
  108. package/dist/furigana-h3v2ub4-.cjs +4 -0
  109. package/dist/import.cjs +12 -0
  110. package/dist/import.d.cts +107 -0
  111. package/dist/import.d.cts.map +1 -0
  112. package/dist/import.d.ts +107 -0
  113. package/dist/import.d.ts.map +1 -0
  114. package/dist/import.js +9 -0
  115. package/dist/index.cjs +275 -0
  116. package/dist/index.cjs.map +1 -0
  117. package/dist/index.d.cts +211 -0
  118. package/dist/index.d.cts.map +1 -0
  119. package/dist/index.d.ts +211 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +238 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/json-DGd-cunA.js +17 -0
  124. package/dist/json-DGd-cunA.js.map +1 -0
  125. package/dist/json-DKWp-B7Y.cjs +30 -0
  126. package/dist/json-DKWp-B7Y.cjs.map +1 -0
  127. package/dist/language-KN_u-nTR.d.ts +104 -0
  128. package/dist/language-KN_u-nTR.d.ts.map +1 -0
  129. package/dist/language-xAbQxgXc.d.cts +104 -0
  130. package/dist/language-xAbQxgXc.d.cts.map +1 -0
  131. package/dist/language.cjs +15626 -0
  132. package/dist/language.cjs.map +1 -0
  133. package/dist/language.d.cts +959 -0
  134. package/dist/language.d.cts.map +1 -0
  135. package/dist/language.d.ts +959 -0
  136. package/dist/language.d.ts.map +1 -0
  137. package/dist/language.js +15522 -0
  138. package/dist/language.js.map +1 -0
  139. package/dist/log-D8KtR3aP.cjs +67 -0
  140. package/dist/log-D8KtR3aP.cjs.map +1 -0
  141. package/dist/log-hgSll-dS.js +60 -0
  142. package/dist/log-hgSll-dS.js.map +1 -0
  143. package/dist/lookup.cjs +13 -0
  144. package/dist/lookup.d.cts +161 -0
  145. package/dist/lookup.d.cts.map +1 -0
  146. package/dist/lookup.d.ts +161 -0
  147. package/dist/lookup.d.ts.map +1 -0
  148. package/dist/lookup.js +10 -0
  149. package/dist/media-loader-BABA_E4W.js +3 -0
  150. package/dist/media-loader-Ce9cuANS.cjs +21 -0
  151. package/dist/media-loader-Ce9cuANS.cjs.map +1 -0
  152. package/dist/media-loader-qRti-Q6h.js +14 -0
  153. package/dist/media-loader-qRti-Q6h.js.map +1 -0
  154. package/dist/media-loader-xlUGaJrx.cjs +3 -0
  155. package/dist/multi-language-transformer-AlxOM6b3.js +637 -0
  156. package/dist/multi-language-transformer-AlxOM6b3.js.map +1 -0
  157. package/dist/multi-language-transformer-MdbQBBOt.cjs +685 -0
  158. package/dist/multi-language-transformer-MdbQBBOt.cjs.map +1 -0
  159. package/dist/multi-language-transformer-SEhcJXEB.d.ts +63 -0
  160. package/dist/multi-language-transformer-SEhcJXEB.d.ts.map +1 -0
  161. package/dist/multi-language-transformer-Ul9mbRce.d.cts +63 -0
  162. package/dist/multi-language-transformer-Ul9mbRce.d.cts.map +1 -0
  163. package/dist/pronunciation-generator-BtBc4q_V.js +397 -0
  164. package/dist/pronunciation-generator-BtBc4q_V.js.map +1 -0
  165. package/dist/pronunciation-generator-CBYdXYou.js +4 -0
  166. package/dist/pronunciation-generator-CFbZlf5J.cjs +445 -0
  167. package/dist/pronunciation-generator-CFbZlf5J.cjs.map +1 -0
  168. package/dist/pronunciation-generator-DOz9hEuk.cjs +4 -0
  169. package/dist/render.cjs +2796 -0
  170. package/dist/render.cjs.map +1 -0
  171. package/dist/render.d.cts +424 -0
  172. package/dist/render.d.cts.map +1 -0
  173. package/dist/render.d.ts +424 -0
  174. package/dist/render.d.ts.map +1 -0
  175. package/dist/render.js +2777 -0
  176. package/dist/render.js.map +1 -0
  177. package/dist/sentence-parser-BPAJNzqW.js +126 -0
  178. package/dist/sentence-parser-BPAJNzqW.js.map +1 -0
  179. package/dist/sentence-parser-BVIOI64h.cjs +132 -0
  180. package/dist/sentence-parser-BVIOI64h.cjs.map +1 -0
  181. package/dist/sentence-parser-BoHO3cHn.js +5 -0
  182. package/dist/sentence-parser-DQVLSW0z.cjs +5 -0
  183. package/dist/structured-content-generator-BtOApkTW.cjs +4 -0
  184. package/dist/structured-content-generator-Bx62RYa8.js +4 -0
  185. package/dist/structured-content-generator-CLnybumI.js +276 -0
  186. package/dist/structured-content-generator-CLnybumI.js.map +1 -0
  187. package/dist/structured-content-generator-DrwkB0-k.cjs +282 -0
  188. package/dist/structured-content-generator-DrwkB0-k.cjs.map +1 -0
  189. package/dist/text-utilities-B7PIythe.js +8 -0
  190. package/dist/text-utilities-B7PIythe.js.map +1 -0
  191. package/dist/text-utilities-Del2Ivkg.cjs +15 -0
  192. package/dist/text-utilities-Del2Ivkg.cjs.map +1 -0
  193. package/dist/translator-CRPlPzqi.cjs +1545 -0
  194. package/dist/translator-CRPlPzqi.cjs.map +1 -0
  195. package/dist/translator-CWgG5drA.js +1539 -0
  196. package/dist/translator-CWgG5drA.js.map +1 -0
  197. package/dist/translator-CaGtJvnQ.cjs +6 -0
  198. package/dist/translator-Cc6OGxrW.d.ts +180 -0
  199. package/dist/translator-Cc6OGxrW.d.ts.map +1 -0
  200. package/dist/translator-CcA-s-W4.d.cts +180 -0
  201. package/dist/translator-CcA-s-W4.d.cts.map +1 -0
  202. package/dist/translator-CuJOTK6l.js +6 -0
  203. package/dist/utilities-C-lbZaJE.cjs +52 -0
  204. package/dist/utilities-C-lbZaJE.cjs.map +1 -0
  205. package/dist/utilities-bi3EF-q5.js +33 -0
  206. package/dist/utilities-bi3EF-q5.js.map +1 -0
  207. package/package.json +102 -0
@@ -0,0 +1,1539 @@
1
+ import { CJK_IDEOGRAPH_RANGES, CJK_PUNCTUATION_RANGE, FULLWIDTH_CHARACTER_RANGES, isCodePointInRanges } from "./cjk-util-DubXBGDG.js";
2
+ import { LanguageTransformer, MultiLanguageTransformer, getAllLanguageReadingNormalizers, getAllLanguageTextProcessors } from "./multi-language-transformer-AlxOM6b3.js";
3
+
4
+ //#region src/util/regex-util.ts
5
+ const matchReplacementPattern = /\$(?:\$|&|`|'|(\d\d?)|<([^>]*)>)/g;
6
+ /**
7
+ * Applies string.replace using a regular expression and replacement string as arguments.
8
+ */
9
+ function applyTextReplacement(text, pattern, replacement) {
10
+ const isGlobal = pattern.global;
11
+ if (isGlobal) pattern.lastIndex = 0;
12
+ for (let loop = true; loop; loop = isGlobal) {
13
+ const match = pattern.exec(text);
14
+ if (match === null) break;
15
+ const matchText = match[0];
16
+ const index = match.index;
17
+ const actualReplacement = applyMatchReplacement(replacement, match);
18
+ const actualReplacementLength = actualReplacement.length;
19
+ const delta = actualReplacementLength - (matchText.length > 0 ? matchText.length : -1);
20
+ text = `${text.substring(0, index)}${actualReplacement}${text.substring(index + matchText.length)}`;
21
+ pattern.lastIndex += delta;
22
+ }
23
+ return text;
24
+ }
25
+ /**
26
+ * Applies the replacement string for a given regular expression match.
27
+ */
28
+ function applyMatchReplacement(replacement, match) {
29
+ const pattern = matchReplacementPattern;
30
+ pattern.lastIndex = 0;
31
+ const replacer = (g0, g1, g2) => {
32
+ if (typeof g1 !== "undefined") {
33
+ const matchIndex = Number.parseInt(g1, 10);
34
+ if (matchIndex >= 1 && matchIndex <= match.length) return match[matchIndex];
35
+ } else if (typeof g2 !== "undefined") {
36
+ const { groups } = match;
37
+ if (typeof groups === "object" && groups !== null && Object.prototype.hasOwnProperty.call(groups, g2)) return groups[g2];
38
+ } else {
39
+ let { index } = match;
40
+ if (typeof index !== "number") index = 0;
41
+ switch (g0) {
42
+ case "$": return "$";
43
+ case "&": return match[0];
44
+ case "`": return replacement.substring(0, index);
45
+ case "'": return replacement.substring(index + g0.length);
46
+ }
47
+ }
48
+ return g0;
49
+ };
50
+ return replacement.replace(pattern, replacer);
51
+ }
52
+
53
+ //#endregion
54
+ //#region src/lookup/translator.ts
55
+ const HIRAGANA_RANGE = [12352, 12447];
56
+ const KATAKANA_RANGE = [12448, 12543];
57
+ const JAPANESE_RANGES = [
58
+ HIRAGANA_RANGE,
59
+ KATAKANA_RANGE,
60
+ ...CJK_IDEOGRAPH_RANGES,
61
+ [65382, 65439],
62
+ [12539, 12540],
63
+ [65377, 65381],
64
+ CJK_PUNCTUATION_RANGE,
65
+ ...FULLWIDTH_CHARACTER_RANGES
66
+ ];
67
+ const BOPOMOFO_RANGE = [12544, 12591];
68
+ const BOPOMOFO_EXTENDED_RANGE = [12704, 12735];
69
+ const IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION_RANGE = [94176, 94207];
70
+ const SMALL_FORM_RANGE = [65104, 65135];
71
+ const VERTICAL_FORM_RANGE = [65040, 65055];
72
+ const CHINESE_RANGES = [
73
+ ...CJK_IDEOGRAPH_RANGES,
74
+ CJK_PUNCTUATION_RANGE,
75
+ ...FULLWIDTH_CHARACTER_RANGES,
76
+ BOPOMOFO_RANGE,
77
+ BOPOMOFO_EXTENDED_RANGE,
78
+ IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION_RANGE,
79
+ SMALL_FORM_RANGE,
80
+ VERTICAL_FORM_RANGE
81
+ ];
82
+ const HANGUL_JAMO_RANGE = [4352, 4607];
83
+ const HANGUL_COMPATIBILITY_JAMO_RANGE = [12592, 12687];
84
+ const HANGUL_SYLLABLES_RANGE = [44032, 55215];
85
+ const HANGUL_JAMO_EXTENDED_A_RANGE = [43360, 43391];
86
+ const HANGUL_JAMO_EXTENDED_B_RANGE = [55216, 55295];
87
+ const HANGUL_JAMO_HALF_WIDTH_RANGE = [65440, 65500];
88
+ const KOREAN_RANGES = [
89
+ ...CJK_IDEOGRAPH_RANGES,
90
+ CJK_PUNCTUATION_RANGE,
91
+ ...FULLWIDTH_CHARACTER_RANGES,
92
+ HANGUL_JAMO_RANGE,
93
+ HANGUL_COMPATIBILITY_JAMO_RANGE,
94
+ HANGUL_SYLLABLES_RANGE,
95
+ HANGUL_JAMO_EXTENDED_A_RANGE,
96
+ HANGUL_JAMO_EXTENDED_B_RANGE,
97
+ HANGUL_JAMO_HALF_WIDTH_RANGE
98
+ ];
99
+ function isCodePointJapanese(codePoint) {
100
+ return isCodePointInRanges(codePoint, JAPANESE_RANGES);
101
+ }
102
+ function isCodePointChinese(codePoint) {
103
+ return isCodePointInRanges(codePoint, CHINESE_RANGES);
104
+ }
105
+ function isCodePointKorean(codePoint) {
106
+ return isCodePointInRanges(codePoint, KOREAN_RANGES);
107
+ }
108
+ var TranslatorTagAggregator = class {
109
+ _tagExpansionTargetMap;
110
+ constructor() {
111
+ this._tagExpansionTargetMap = new Map();
112
+ }
113
+ addTags(tags, dictionary, tagNames) {
114
+ if (tagNames.length === 0) return;
115
+ const tagGroups = this._getOrCreateTagGroups(tags);
116
+ const tagGroup = this._getOrCreateTagGroup(tagGroups, dictionary);
117
+ this._addUniqueTags(tagGroup, tagNames);
118
+ }
119
+ getTagExpansionTargets() {
120
+ const results = [];
121
+ for (const [tags, tagGroups] of this._tagExpansionTargetMap) results.push({
122
+ tags,
123
+ tagGroups
124
+ });
125
+ return results;
126
+ }
127
+ mergeTags(tags, newTags) {
128
+ const newTagGroups = this._tagExpansionTargetMap.get(newTags);
129
+ if (typeof newTagGroups === "undefined") return;
130
+ const tagGroups = this._getOrCreateTagGroups(tags);
131
+ for (const { dictionary, tagNames } of newTagGroups) {
132
+ const tagGroup = this._getOrCreateTagGroup(tagGroups, dictionary);
133
+ this._addUniqueTags(tagGroup, tagNames);
134
+ }
135
+ }
136
+ _getOrCreateTagGroups(tags) {
137
+ let tagGroups = this._tagExpansionTargetMap.get(tags);
138
+ if (typeof tagGroups === "undefined") {
139
+ tagGroups = [];
140
+ this._tagExpansionTargetMap.set(tags, tagGroups);
141
+ }
142
+ return tagGroups;
143
+ }
144
+ _getOrCreateTagGroup(tagGroups, dictionary) {
145
+ for (const tagGroup of tagGroups) if (tagGroup.dictionary === dictionary) return tagGroup;
146
+ const newTagGroup = {
147
+ dictionary,
148
+ tagNames: []
149
+ };
150
+ tagGroups.push(newTagGroup);
151
+ return newTagGroup;
152
+ }
153
+ _addUniqueTags(tagGroup, newTagNames) {
154
+ const { tagNames } = tagGroup;
155
+ for (const tagName of newTagNames) {
156
+ if (tagNames.includes(tagName)) continue;
157
+ tagNames.push(tagName);
158
+ }
159
+ }
160
+ };
161
+ /**
162
+ * Class which finds term and kanji dictionary entries for text.
163
+ */
164
+ var Translator = class {
165
+ _database;
166
+ _multiLanguageTransformer;
167
+ _tagCache;
168
+ _stringComparer;
169
+ _numberRegex;
170
+ _textProcessors;
171
+ _readingNormalizers;
172
+ constructor(database) {
173
+ this._database = database;
174
+ this._multiLanguageTransformer = new MultiLanguageTransformer();
175
+ this._tagCache = new Map();
176
+ this._stringComparer = new Intl.Collator("en-US");
177
+ this._numberRegex = /[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?/;
178
+ this._textProcessors = new Map();
179
+ this._readingNormalizers = new Map();
180
+ }
181
+ /**
182
+ * Initializes the instance for use. The public API should not be used until this function has been called.
183
+ */
184
+ prepare() {
185
+ this._multiLanguageTransformer.prepare();
186
+ for (const { iso, textPreprocessors = [], textPostprocessors = [] } of getAllLanguageTextProcessors()) this._textProcessors.set(iso, {
187
+ textPreprocessors,
188
+ textPostprocessors
189
+ });
190
+ for (const { iso, readingNormalizer } of getAllLanguageReadingNormalizers()) this._readingNormalizers.set(iso, readingNormalizer);
191
+ }
192
+ /**
193
+ * Clears the database tag cache. This should be executed if the database is changed.
194
+ */
195
+ clearDatabaseCaches() {
196
+ this._tagCache.clear();
197
+ }
198
+ /**
199
+ * Finds term definitions for the given text.
200
+ */
201
+ async findTerms(mode, text, options) {
202
+ const { enabledDictionaryMap, excludeDictionaryDefinitions, sortFrequencyDictionary, sortFrequencyDictionaryOrder, language, primaryReading } = options;
203
+ const tagAggregator = new TranslatorTagAggregator();
204
+ let { dictionaryEntries, originalTextLength } = await this._findTermsInternal(text, options, tagAggregator, primaryReading);
205
+ switch (mode) {
206
+ case "group":
207
+ dictionaryEntries = this._groupDictionaryEntriesByHeadword(language, dictionaryEntries, tagAggregator, primaryReading);
208
+ break;
209
+ case "term":
210
+ dictionaryEntries = this._groupDictionaryEntriesByTerm(language, dictionaryEntries, tagAggregator, primaryReading);
211
+ break;
212
+ case "merge":
213
+ dictionaryEntries = await this._getRelatedDictionaryEntries(dictionaryEntries, options, tagAggregator);
214
+ break;
215
+ }
216
+ if (excludeDictionaryDefinitions !== null) this._removeExcludedDefinitions(dictionaryEntries, excludeDictionaryDefinitions);
217
+ if (mode !== "simple") {
218
+ await this._addTermMeta(dictionaryEntries, enabledDictionaryMap, tagAggregator);
219
+ await this._expandTagGroupsAndGroup(tagAggregator.getTagExpansionTargets());
220
+ } else if (sortFrequencyDictionary !== null) {
221
+ const sortDictionaryMap = new Map();
222
+ const value = enabledDictionaryMap.get(sortFrequencyDictionary);
223
+ if (typeof value !== "undefined") sortDictionaryMap.set(sortFrequencyDictionary, value);
224
+ await this._addTermMeta(dictionaryEntries, sortDictionaryMap, tagAggregator);
225
+ }
226
+ if (sortFrequencyDictionary !== null) this._updateSortFrequencies(dictionaryEntries, sortFrequencyDictionary, sortFrequencyDictionaryOrder === "ascending");
227
+ if (dictionaryEntries.length > 1) this._sortTermDictionaryEntries(dictionaryEntries);
228
+ for (const { definitions, frequencies, pronunciations } of dictionaryEntries) {
229
+ this._flagRedundantDefinitionTags(definitions);
230
+ if (definitions.length > 1) this._sortTermDictionaryEntryDefinitions(definitions);
231
+ if (frequencies.length > 1) this._sortTermDictionaryEntrySimpleData(frequencies);
232
+ if (pronunciations.length > 1) this._sortTermDictionaryEntrySimpleData(pronunciations);
233
+ }
234
+ const withUserFacingInflections = this._addUserFacingInflections(language, dictionaryEntries);
235
+ return {
236
+ dictionaryEntries: withUserFacingInflections,
237
+ originalTextLength
238
+ };
239
+ }
240
+ /**
241
+ * Finds kanji definitions for the given text.
242
+ */
243
+ async findKanji(text, options) {
244
+ if (options.removeNonJapaneseCharacters) text = this._getJapaneseChineseKoreanOnlyText(text);
245
+ const { enabledDictionaryMap } = options;
246
+ const kanjiUnique = new Set();
247
+ for (const c of text) kanjiUnique.add(c);
248
+ const databaseEntries = await this._database.findKanjiBulk([...kanjiUnique], enabledDictionaryMap);
249
+ if (databaseEntries.length === 0) return [];
250
+ this._sortDatabaseEntriesByIndex(databaseEntries);
251
+ const dictionaryEntries = [];
252
+ const tagAggregator = new TranslatorTagAggregator();
253
+ for (const { character, onyomi, kunyomi, tags, definitions, stats, dictionary } of databaseEntries) {
254
+ const expandedStats = await this._expandKanjiStats(stats, dictionary);
255
+ const dictionaryAlias = this._getDictionaryAlias(dictionary, enabledDictionaryMap);
256
+ const dictionaryEntry = this._createKanjiDictionaryEntry(character, dictionary, dictionaryAlias, onyomi, kunyomi, expandedStats, definitions, enabledDictionaryMap);
257
+ dictionaryEntries.push(dictionaryEntry);
258
+ tagAggregator.addTags(dictionaryEntry.tags, dictionary, tags);
259
+ }
260
+ if (dictionaryEntries.length > 1) this._sortKanjiDictionaryEntries(dictionaryEntries);
261
+ await this._addKanjiMeta(dictionaryEntries, enabledDictionaryMap);
262
+ await this._expandTagGroupsAndGroup(tagAggregator.getTagExpansionTargets());
263
+ this._sortKanjiDictionaryEntryData(dictionaryEntries);
264
+ return dictionaryEntries;
265
+ }
266
+ /**
267
+ * Gets a list of frequency information for a given list of term-reading pairs
268
+ * and a list of dictionaries.
269
+ */
270
+ async getTermFrequencies(termReadingList, dictionaries) {
271
+ const dictionarySet = new Set();
272
+ for (const dictionary of dictionaries) dictionarySet.add(dictionary);
273
+ const termList = termReadingList.map(({ term }) => term);
274
+ const metas = await this._database.findTermMetaBulk(termList, dictionarySet);
275
+ const results = [];
276
+ for (const { mode, data, dictionary, index } of metas) {
277
+ if (mode !== "freq") continue;
278
+ let { term, reading } = termReadingList[index];
279
+ const hasReading = data !== null && typeof data === "object" && typeof data.reading === "string";
280
+ if (hasReading && data.reading !== reading) {
281
+ if (reading !== null) continue;
282
+ reading = data.reading;
283
+ }
284
+ const frequency = hasReading ? data.frequency : data;
285
+ const { frequency: frequencyValue, displayValue, displayValueParsed } = this._getFrequencyInfo(frequency);
286
+ results.push({
287
+ term,
288
+ reading,
289
+ dictionary,
290
+ hasReading,
291
+ frequency: frequencyValue,
292
+ displayValue,
293
+ displayValueParsed
294
+ });
295
+ }
296
+ return results;
297
+ }
298
+ async _findTermsInternal(text, options, tagAggregator, primaryReading) {
299
+ const { removeNonJapaneseCharacters, enabledDictionaryMap } = options;
300
+ if (removeNonJapaneseCharacters && [
301
+ "ja",
302
+ "zh",
303
+ "yue",
304
+ "ko"
305
+ ].includes(options.language)) text = this._getJapaneseChineseKoreanOnlyText(text);
306
+ if (text.length === 0) return {
307
+ dictionaryEntries: [],
308
+ originalTextLength: 0
309
+ };
310
+ const deinflections = await this._getDeinflections(text, options);
311
+ return this._getDictionaryEntries(deinflections, enabledDictionaryMap, tagAggregator, primaryReading);
312
+ }
313
+ _getDictionaryEntries(deinflections, enabledDictionaryMap, tagAggregator, primaryReading) {
314
+ let originalTextLength = 0;
315
+ const dictionaryEntries = [];
316
+ const ids = new Set();
317
+ for (const { databaseEntries, originalText, transformedText, deinflectedText, textProcessorRuleChainCandidates, inflectionRuleChainCandidates } of deinflections) {
318
+ if (databaseEntries.length === 0) continue;
319
+ originalTextLength = Math.max(originalTextLength, originalText.length);
320
+ for (const databaseEntry of databaseEntries) {
321
+ const { id } = databaseEntry;
322
+ if (ids.has(id)) {
323
+ const existingEntryInfo = this._findExistingEntry(dictionaryEntries, id);
324
+ if (!existingEntryInfo) continue;
325
+ const { existingEntry, existingIndex } = existingEntryInfo;
326
+ const existingTransformedLength = existingEntry.headwords[0].sources[0].transformedText.length;
327
+ if (transformedText.length < existingTransformedLength) continue;
328
+ if (transformedText.length > existingTransformedLength) dictionaryEntries.splice(existingIndex, 1, this._createTermDictionaryEntryFromDatabaseEntry(databaseEntry, originalText, transformedText, deinflectedText, textProcessorRuleChainCandidates, inflectionRuleChainCandidates, true, enabledDictionaryMap, tagAggregator, primaryReading));
329
+ else {
330
+ this._mergeInflectionRuleChains(existingEntry, inflectionRuleChainCandidates);
331
+ this._mergeTextProcessorRuleChains(existingEntry, textProcessorRuleChainCandidates);
332
+ }
333
+ } else {
334
+ const dictionaryEntry = this._createTermDictionaryEntryFromDatabaseEntry(databaseEntry, originalText, transformedText, deinflectedText, textProcessorRuleChainCandidates, inflectionRuleChainCandidates, true, enabledDictionaryMap, tagAggregator, primaryReading);
335
+ dictionaryEntries.push(dictionaryEntry);
336
+ ids.add(id);
337
+ }
338
+ }
339
+ }
340
+ return {
341
+ dictionaryEntries,
342
+ originalTextLength
343
+ };
344
+ }
345
+ _findExistingEntry(dictionaryEntries, id) {
346
+ for (const [index, entry] of dictionaryEntries.entries()) if (entry.definitions.some((definition) => definition.id === id)) return {
347
+ existingEntry: entry,
348
+ existingIndex: index
349
+ };
350
+ return null;
351
+ }
352
+ _mergeTextProcessorRuleChains(existingEntry, textProcessorRuleChainCandidates) {
353
+ const existingChains = existingEntry.textProcessorRuleChainCandidates;
354
+ for (const textProcessorRules of textProcessorRuleChainCandidates) {
355
+ const duplicate = existingChains.find((existingChain) => {
356
+ return this._areArraysEqualIgnoreOrder(existingChain, textProcessorRules);
357
+ });
358
+ if (!duplicate) existingEntry.textProcessorRuleChainCandidates.push(textProcessorRules);
359
+ }
360
+ }
361
+ _mergeInflectionRuleChains(existingEntry, inflectionRuleChainCandidates) {
362
+ const existingChains = existingEntry.inflectionRuleChainCandidates;
363
+ for (const { source, inflectionRules } of inflectionRuleChainCandidates) {
364
+ const duplicate = existingChains.find((existingChain) => {
365
+ return this._areArraysEqualIgnoreOrder(existingChain.inflectionRules.map((r) => r.name), inflectionRules);
366
+ });
367
+ if (!duplicate) existingEntry.inflectionRuleChainCandidates.push({
368
+ source,
369
+ inflectionRules: inflectionRules.map((rule) => ({ name: rule }))
370
+ });
371
+ else if (duplicate.source !== source) duplicate.source = "both";
372
+ }
373
+ }
374
+ _areArraysEqualIgnoreOrder(array1, array2) {
375
+ if (array1.length !== array2.length) return false;
376
+ const frequencyCounter = new Map();
377
+ for (const element of array1) frequencyCounter.set(element, (frequencyCounter.get(element) || 0) + 1);
378
+ for (const element of array2) {
379
+ const frequency = frequencyCounter.get(element);
380
+ if (!frequency) return false;
381
+ frequencyCounter.set(element, frequency - 1);
382
+ }
383
+ return true;
384
+ }
385
+ async _getDeinflections(text, options) {
386
+ let deinflections = options.deinflect ? this._getAlgorithmDeinflections(text, options) : [this._createDeinflection(text, text, text, 0, [], [])];
387
+ if (deinflections.length === 0) return [];
388
+ const { matchType, language, enabledDictionaryMap } = options;
389
+ await this._addEntriesToDeinflections(language, deinflections, enabledDictionaryMap, matchType);
390
+ const dictionaryDeinflections = await this._getDictionaryDeinflections(language, deinflections, enabledDictionaryMap, matchType);
391
+ deinflections.push(...dictionaryDeinflections);
392
+ for (const deinflection of deinflections) {
393
+ for (const entry of deinflection.databaseEntries) entry.definitions = entry.definitions.filter((definition) => !Array.isArray(definition));
394
+ deinflection.databaseEntries = deinflection.databaseEntries.filter((entry) => entry.definitions.length);
395
+ }
396
+ deinflections = deinflections.filter((deinflection) => deinflection.databaseEntries.length);
397
+ return deinflections;
398
+ }
399
+ async _getDictionaryDeinflections(language, deinflections, enabledDictionaryMap, matchType) {
400
+ const dictionaryDeinflections = [];
401
+ for (const deinflection of deinflections) {
402
+ const { originalText, transformedText, textProcessorRuleChainCandidates, inflectionRuleChainCandidates: algorithmChains, databaseEntries } = deinflection;
403
+ for (const entry of databaseEntries) {
404
+ const { dictionary, definitions } = entry;
405
+ const entryDictionary = enabledDictionaryMap.get(dictionary);
406
+ const useDeinflections = entryDictionary?.useDeinflections ?? true;
407
+ if (!useDeinflections) continue;
408
+ for (const definition of definitions) if (Array.isArray(definition)) {
409
+ const [formOf, inflectionRules] = definition;
410
+ if (!formOf) continue;
411
+ const inflectionRuleChainCandidates = algorithmChains.map(({ inflectionRules: algInflections }) => {
412
+ return {
413
+ source: algInflections.length === 0 ? "dictionary" : "both",
414
+ inflectionRules: [...algInflections, ...inflectionRules]
415
+ };
416
+ });
417
+ const dictionaryDeinflection = this._createDeinflection(originalText, transformedText, formOf, 0, textProcessorRuleChainCandidates, inflectionRuleChainCandidates);
418
+ dictionaryDeinflections.push(dictionaryDeinflection);
419
+ }
420
+ }
421
+ }
422
+ await this._addEntriesToDeinflections(language, dictionaryDeinflections, enabledDictionaryMap, matchType);
423
+ return dictionaryDeinflections;
424
+ }
425
+ async _addEntriesToDeinflections(language, deinflections, enabledDictionaryMap, matchType) {
426
+ const uniqueDeinflectionsMap = this._groupDeinflectionsByTerm(deinflections);
427
+ const uniqueDeinflectionArrays = [...uniqueDeinflectionsMap.values()];
428
+ const uniqueDeinflectionTerms = [...uniqueDeinflectionsMap.keys()];
429
+ const databaseEntries = await this._database.findTermsBulk(uniqueDeinflectionTerms, enabledDictionaryMap, matchType);
430
+ this._matchEntriesToDeinflections(language, databaseEntries, uniqueDeinflectionArrays, enabledDictionaryMap);
431
+ }
432
+ _groupDeinflectionsByTerm(deinflections) {
433
+ const result = new Map();
434
+ for (const deinflection of deinflections) {
435
+ const { deinflectedText } = deinflection;
436
+ let deinflectionArray = result.get(deinflectedText);
437
+ if (typeof deinflectionArray === "undefined") {
438
+ deinflectionArray = [];
439
+ result.set(deinflectedText, deinflectionArray);
440
+ }
441
+ deinflectionArray.push(deinflection);
442
+ }
443
+ return result;
444
+ }
445
+ _matchEntriesToDeinflections(language, databaseEntries, uniqueDeinflectionArrays, enabledDictionaryMap) {
446
+ for (const databaseEntry of databaseEntries) {
447
+ const entryDictionary = enabledDictionaryMap.get(databaseEntry.dictionary);
448
+ if (typeof entryDictionary === "undefined") continue;
449
+ const { partsOfSpeechFilter } = entryDictionary;
450
+ const definitionConditions = this._multiLanguageTransformer.getConditionFlagsFromPartsOfSpeech(language, databaseEntry.rules);
451
+ for (const deinflection of uniqueDeinflectionArrays[databaseEntry.index]) if (!partsOfSpeechFilter || LanguageTransformer.conditionsMatch(deinflection.conditions, definitionConditions)) deinflection.databaseEntries.push(databaseEntry);
452
+ }
453
+ }
454
+ _getAlgorithmDeinflections(text, options) {
455
+ const { language } = options;
456
+ const processorsForLanguage = this._textProcessors.get(language);
457
+ if (typeof processorsForLanguage === "undefined") throw new Error(`Unsupported language: ${language}`);
458
+ const { textPreprocessors, textPostprocessors } = processorsForLanguage;
459
+ const deinflections = [];
460
+ const sourceCache = new Map();
461
+ for (let rawSource = text; rawSource.length > 0; rawSource = this._getNextSubstring(options.searchResolution, rawSource)) {
462
+ const preprocessedTextVariants = this._getTextVariants(rawSource, textPreprocessors, this._getTextReplacementsVariants(options), sourceCache);
463
+ for (const [source, preprocessorRuleChainCandidates] of preprocessedTextVariants) for (const deinflection of this._multiLanguageTransformer.transform(language, source)) {
464
+ const { trace, conditions } = deinflection;
465
+ const postprocessedTextVariants = this._getTextVariants(deinflection.text, textPostprocessors, [null], sourceCache);
466
+ for (const [transformedText, postprocessorRuleChainCandidates] of postprocessedTextVariants) {
467
+ const inflectionRuleChainCandidate = {
468
+ source: "algorithm",
469
+ inflectionRules: trace.map((frame) => frame.transform)
470
+ };
471
+ const textProcessorRuleChainCandidates = preprocessorRuleChainCandidates.flatMap((preprocessorRuleChainCandidate) => postprocessorRuleChainCandidates.map((postprocessorRuleChainCandidate) => [...preprocessorRuleChainCandidate, ...postprocessorRuleChainCandidate]));
472
+ deinflections.push(this._createDeinflection(rawSource, source, transformedText, conditions, textProcessorRuleChainCandidates, [inflectionRuleChainCandidate]));
473
+ }
474
+ }
475
+ }
476
+ return deinflections;
477
+ }
478
+ _getTextVariants(text, textProcessors, textReplacements, textCache) {
479
+ let variantsMap = new Map([[text, [[]]]]);
480
+ for (const [id, textReplacement] of textReplacements.entries()) {
481
+ if (textReplacement === null) continue;
482
+ variantsMap.set(this._applyTextReplacements(text, textReplacement), [[`Text Replacement ${id}`]]);
483
+ }
484
+ for (const { id, textProcessor: { process, options } } of textProcessors) {
485
+ const newVariantsMap = new Map();
486
+ for (const [variant, currentPreprocessorRuleChainCandidates] of variantsMap) for (const option of options) {
487
+ const processed = this._getProcessedText(textCache, variant, id, option, process);
488
+ const existingCandidates = newVariantsMap.get(processed);
489
+ if (processed === variant) if (typeof existingCandidates === "undefined") newVariantsMap.set(processed, currentPreprocessorRuleChainCandidates);
490
+ else newVariantsMap.set(processed, existingCandidates);
491
+ else if (typeof existingCandidates === "undefined") newVariantsMap.set(processed, currentPreprocessorRuleChainCandidates.map((candidate) => [...candidate, id]));
492
+ else newVariantsMap.set(processed, [...existingCandidates, ...currentPreprocessorRuleChainCandidates.map((candidate) => [...candidate, id])]);
493
+ }
494
+ variantsMap = newVariantsMap;
495
+ }
496
+ return variantsMap;
497
+ }
498
+ _getProcessedText(textCache, text, id, setting, process) {
499
+ let level1 = textCache.get(text);
500
+ if (!level1) {
501
+ level1 = new Map();
502
+ textCache.set(text, level1);
503
+ }
504
+ let level2 = level1.get(id);
505
+ if (!level2) {
506
+ level2 = new Map();
507
+ level1.set(id, level2);
508
+ }
509
+ if (!level2.has(setting)) {
510
+ text = process(text, setting);
511
+ level2.set(setting, text);
512
+ } else text = level2.get(setting) || "";
513
+ return text;
514
+ }
515
+ _getNextSubstring(searchResolution, currentString) {
516
+ const nextSubstringLength = searchResolution === "word" ? currentString.search(/[^\p{Letter}][\p{Letter}\p{Number}]*$/u) : currentString.length - 1;
517
+ return currentString.substring(0, nextSubstringLength);
518
+ }
519
+ _applyTextReplacements(text, replacements) {
520
+ for (const { pattern, replacement } of replacements) text = applyTextReplacement(text, pattern, replacement);
521
+ return text;
522
+ }
523
+ _getJapaneseChineseKoreanOnlyText(text) {
524
+ let length = 0;
525
+ for (const c of text) {
526
+ const codePoint = c.codePointAt(0);
527
+ if (!isCodePointJapanese(codePoint) && !isCodePointChinese(codePoint) && !isCodePointKorean(codePoint)) return text.substring(0, length);
528
+ length += c.length;
529
+ }
530
+ return text;
531
+ }
532
+ _getTextReplacementsVariants(options) {
533
+ return options.textReplacements;
534
+ }
535
+ _createDeinflection(originalText, transformedText, deinflectedText, conditions, textProcessorRuleChainCandidates, inflectionRuleChainCandidates) {
536
+ return {
537
+ originalText,
538
+ transformedText,
539
+ deinflectedText,
540
+ conditions,
541
+ textProcessorRuleChainCandidates,
542
+ inflectionRuleChainCandidates,
543
+ databaseEntries: []
544
+ };
545
+ }
546
+ async _getRelatedDictionaryEntries(dictionaryEntries, options, tagAggregator) {
547
+ const { mainDictionary, enabledDictionaryMap, language, primaryReading } = options;
548
+ const sequenceList = [];
549
+ const groupedDictionaryEntries = [];
550
+ const groupedDictionaryEntriesMap = new Map();
551
+ const ungroupedDictionaryEntriesMap = new Map();
552
+ for (const dictionaryEntry of dictionaryEntries) {
553
+ const { definitions: [{ id, dictionary, sequences: [sequence] }] } = dictionaryEntry;
554
+ if (mainDictionary === dictionary && sequence >= 0) {
555
+ let group = groupedDictionaryEntriesMap.get(sequence);
556
+ if (typeof group === "undefined") {
557
+ group = {
558
+ ids: new Set(),
559
+ dictionaryEntries: []
560
+ };
561
+ sequenceList.push({
562
+ query: sequence,
563
+ dictionary
564
+ });
565
+ groupedDictionaryEntries.push(group);
566
+ groupedDictionaryEntriesMap.set(sequence, group);
567
+ }
568
+ group.dictionaryEntries.push(dictionaryEntry);
569
+ group.ids.add(id);
570
+ } else ungroupedDictionaryEntriesMap.set(id, dictionaryEntry);
571
+ }
572
+ if (sequenceList.length > 0) {
573
+ const secondarySearchDictionaryMap = this._getSecondarySearchDictionaryMap(enabledDictionaryMap);
574
+ await this._addRelatedDictionaryEntries(groupedDictionaryEntries, ungroupedDictionaryEntriesMap, sequenceList, enabledDictionaryMap, tagAggregator, primaryReading);
575
+ for (const group of groupedDictionaryEntries) this._sortTermDictionaryEntriesById(group.dictionaryEntries);
576
+ if (ungroupedDictionaryEntriesMap.size > 0 || secondarySearchDictionaryMap.size > 0) await this._addSecondaryRelatedDictionaryEntries(language, groupedDictionaryEntries, ungroupedDictionaryEntriesMap, enabledDictionaryMap, secondarySearchDictionaryMap, tagAggregator, primaryReading);
577
+ }
578
+ const newDictionaryEntries = [];
579
+ for (const group of groupedDictionaryEntries) newDictionaryEntries.push(this._createGroupedDictionaryEntry(language, group.dictionaryEntries, true, tagAggregator, primaryReading));
580
+ newDictionaryEntries.push(...this._groupDictionaryEntriesByHeadword(language, ungroupedDictionaryEntriesMap.values(), tagAggregator, primaryReading));
581
+ return newDictionaryEntries;
582
+ }
583
+ async _addRelatedDictionaryEntries(groupedDictionaryEntries, ungroupedDictionaryEntriesMap, sequenceList, enabledDictionaryMap, tagAggregator, primaryReading) {
584
+ const databaseEntries = await this._database.findTermsBySequenceBulk(sequenceList);
585
+ for (const databaseEntry of databaseEntries) {
586
+ const { dictionaryEntries: groupEntries, ids } = groupedDictionaryEntries[databaseEntry.index];
587
+ const { id } = databaseEntry;
588
+ if (ids.has(id)) continue;
589
+ const { term } = databaseEntry;
590
+ const dictionaryEntry = this._createTermDictionaryEntryFromDatabaseEntry(databaseEntry, term, term, term, [], [], false, enabledDictionaryMap, tagAggregator, primaryReading);
591
+ groupEntries.push(dictionaryEntry);
592
+ ids.add(id);
593
+ ungroupedDictionaryEntriesMap.delete(id);
594
+ }
595
+ }
596
+ async _addSecondaryRelatedDictionaryEntries(language, groupedDictionaryEntries, ungroupedDictionaryEntriesMap, enabledDictionaryMap, secondarySearchDictionaryMap, tagAggregator, primaryReading) {
597
+ const termList = [];
598
+ const targetList = [];
599
+ const targetMap = new Map();
600
+ const readingNormalizer = this._readingNormalizers.get(language);
601
+ for (const group of groupedDictionaryEntries) {
602
+ const { dictionaryEntries: groupEntries } = group;
603
+ for (const dictionaryEntry of groupEntries) {
604
+ const { term, reading } = dictionaryEntry.headwords[0];
605
+ const normalizedReading = typeof readingNormalizer === "undefined" ? reading : readingNormalizer(reading);
606
+ const key = this._createMapKey([term, normalizedReading]);
607
+ let target = targetMap.get(key);
608
+ if (typeof target === "undefined") {
609
+ target = { groups: [] };
610
+ targetMap.set(key, target);
611
+ termList.push({
612
+ term,
613
+ reading
614
+ });
615
+ targetList.push(target);
616
+ }
617
+ target.groups.push(group);
618
+ }
619
+ }
620
+ for (const [id, dictionaryEntry] of ungroupedDictionaryEntriesMap.entries()) {
621
+ const { term, reading } = dictionaryEntry.headwords[0];
622
+ const normalizedReading = typeof readingNormalizer === "undefined" ? reading : readingNormalizer(reading);
623
+ const key = this._createMapKey([term, normalizedReading]);
624
+ const target = targetMap.get(key);
625
+ if (typeof target === "undefined") continue;
626
+ for (const { ids, dictionaryEntries: groupEntries } of target.groups) {
627
+ if (ids.has(id)) continue;
628
+ groupEntries.push(dictionaryEntry);
629
+ ids.add(id);
630
+ }
631
+ ungroupedDictionaryEntriesMap.delete(id);
632
+ }
633
+ if (termList.length === 0 || secondarySearchDictionaryMap.size === 0) return;
634
+ const databaseEntries = await this._database.findTermsExactBulk(termList, secondarySearchDictionaryMap);
635
+ this._sortDatabaseEntriesByIndex(databaseEntries);
636
+ for (const databaseEntry of databaseEntries) {
637
+ const { index, id } = databaseEntry;
638
+ const sourceText = termList[index].term;
639
+ const target = targetList[index];
640
+ for (const { ids, dictionaryEntries: groupEntries } of target.groups) {
641
+ if (ids.has(id)) continue;
642
+ const dictionaryEntry = this._createTermDictionaryEntryFromDatabaseEntry(databaseEntry, sourceText, sourceText, sourceText, [], [], false, enabledDictionaryMap, tagAggregator, primaryReading);
643
+ groupEntries.push(dictionaryEntry);
644
+ ids.add(id);
645
+ ungroupedDictionaryEntriesMap.delete(id);
646
+ }
647
+ }
648
+ }
649
+ _groupDictionaryEntriesByHeadword(language, dictionaryEntries, tagAggregator, primaryReading) {
650
+ const readingNormalizer = this._readingNormalizers.get(language);
651
+ const createGroupingKey = (dictionaryEntry) => {
652
+ const { inflectionRuleChainCandidates, headwords: [{ term, reading }] } = dictionaryEntry;
653
+ const normalizedReading = typeof readingNormalizer === "undefined" ? reading : readingNormalizer(reading);
654
+ return this._createMapKey([
655
+ term,
656
+ normalizedReading,
657
+ ...inflectionRuleChainCandidates
658
+ ]);
659
+ };
660
+ return this._groupDictionaryEntries(language, dictionaryEntries, tagAggregator, primaryReading, createGroupingKey);
661
+ }
662
+ _groupDictionaryEntriesByTerm(language, dictionaryEntries, tagAggregator, primaryReading) {
663
+ const createGroupingKey = (dictionaryEntry) => {
664
+ const { inflectionRuleChainCandidates, headwords: [{ term }] } = dictionaryEntry;
665
+ return this._createMapKey([term, ...inflectionRuleChainCandidates]);
666
+ };
667
+ return this._groupDictionaryEntries(language, dictionaryEntries, tagAggregator, primaryReading, createGroupingKey);
668
+ }
669
+ _groupDictionaryEntries(language, dictionaryEntries, tagAggregator, primaryReading, createGroupingKey) {
670
+ const groups = new Map();
671
+ for (const dictionaryEntry of dictionaryEntries) {
672
+ const key = createGroupingKey(dictionaryEntry);
673
+ let groupDictionaryEntries = groups.get(key);
674
+ if (typeof groupDictionaryEntries === "undefined") {
675
+ groupDictionaryEntries = [];
676
+ groups.set(key, groupDictionaryEntries);
677
+ }
678
+ groupDictionaryEntries.push(dictionaryEntry);
679
+ }
680
+ const newDictionaryEntries = [];
681
+ for (const groupDictionaryEntries of groups.values()) newDictionaryEntries.push(this._createGroupedDictionaryEntry(language, groupDictionaryEntries, false, tagAggregator, primaryReading));
682
+ return newDictionaryEntries;
683
+ }
684
+ _removeExcludedDefinitions(dictionaryEntries, excludeDictionaryDefinitions) {
685
+ for (let i = dictionaryEntries.length - 1; i >= 0; --i) {
686
+ const dictionaryEntry = dictionaryEntries[i];
687
+ const { definitions, pronunciations, frequencies, headwords } = dictionaryEntry;
688
+ const definitionsChanged = this._removeArrayItemsWithDictionary(definitions, excludeDictionaryDefinitions);
689
+ this._removeArrayItemsWithDictionary(pronunciations, excludeDictionaryDefinitions);
690
+ this._removeArrayItemsWithDictionary(frequencies, excludeDictionaryDefinitions);
691
+ this._removeTagGroupsWithDictionary(definitions, excludeDictionaryDefinitions);
692
+ this._removeTagGroupsWithDictionary(headwords, excludeDictionaryDefinitions);
693
+ if (!definitionsChanged) continue;
694
+ if (definitions.length === 0) dictionaryEntries.splice(i, 1);
695
+ else this._removeUnusedHeadwords(dictionaryEntry);
696
+ }
697
+ }
698
+ _removeUnusedHeadwords(dictionaryEntry) {
699
+ const { definitions, pronunciations, frequencies, headwords } = dictionaryEntry;
700
+ const removeHeadwordIndices = new Set();
701
+ for (let i = 0, ii = headwords.length; i < ii; ++i) removeHeadwordIndices.add(i);
702
+ for (const { headwordIndices } of definitions) for (const headwordIndex of headwordIndices) removeHeadwordIndices.delete(headwordIndex);
703
+ if (removeHeadwordIndices.size === 0) return;
704
+ const indexRemap = new Map();
705
+ let oldIndex = 0;
706
+ for (let i = 0, ii = headwords.length; i < ii; ++i) {
707
+ if (removeHeadwordIndices.has(oldIndex)) {
708
+ headwords.splice(i, 1);
709
+ --i;
710
+ --ii;
711
+ } else indexRemap.set(oldIndex, indexRemap.size);
712
+ ++oldIndex;
713
+ }
714
+ this._updateDefinitionHeadwordIndices(definitions, indexRemap);
715
+ this._updateArrayItemsHeadwordIndex(pronunciations, indexRemap);
716
+ this._updateArrayItemsHeadwordIndex(frequencies, indexRemap);
717
+ }
718
+ _updateDefinitionHeadwordIndices(definitions, indexRemap) {
719
+ for (const { headwordIndices } of definitions) for (let i = headwordIndices.length - 1; i >= 0; --i) {
720
+ const newHeadwordIndex = indexRemap.get(headwordIndices[i]);
721
+ if (typeof newHeadwordIndex === "undefined") headwordIndices.splice(i, 1);
722
+ else headwordIndices[i] = newHeadwordIndex;
723
+ }
724
+ }
725
+ _updateArrayItemsHeadwordIndex(array, indexRemap) {
726
+ for (let i = array.length - 1; i >= 0; --i) {
727
+ const item = array[i];
728
+ const { headwordIndex } = item;
729
+ const newHeadwordIndex = indexRemap.get(headwordIndex);
730
+ if (typeof newHeadwordIndex === "undefined") array.splice(i, 1);
731
+ else item.headwordIndex = newHeadwordIndex;
732
+ }
733
+ }
734
+ _removeArrayItemsWithDictionary(array, excludeDictionaryDefinitions) {
735
+ let changed = false;
736
+ for (let j = array.length - 1; j >= 0; --j) {
737
+ const { dictionary } = array[j];
738
+ if (!excludeDictionaryDefinitions.has(dictionary)) continue;
739
+ array.splice(j, 1);
740
+ changed = true;
741
+ }
742
+ return changed;
743
+ }
744
+ _removeArrayItemsWithDictionary2(array, excludeDictionaryDefinitions) {
745
+ let changed = false;
746
+ for (let j = array.length - 1; j >= 0; --j) {
747
+ const { dictionaries } = array[j];
748
+ if (this._hasAny(excludeDictionaryDefinitions, dictionaries)) continue;
749
+ array.splice(j, 1);
750
+ changed = true;
751
+ }
752
+ return changed;
753
+ }
754
+ _removeTagGroupsWithDictionary(array, excludeDictionaryDefinitions) {
755
+ for (const { tags } of array) this._removeArrayItemsWithDictionary2(tags, excludeDictionaryDefinitions);
756
+ }
757
+ async _expandTagGroupsAndGroup(tagExpansionTargets) {
758
+ await this._expandTagGroups(tagExpansionTargets);
759
+ this._groupTags(tagExpansionTargets);
760
+ }
761
+ async _expandTagGroups(tagTargets) {
762
+ const allItems = [];
763
+ const targetMap = new Map();
764
+ for (const { tagGroups, tags } of tagTargets) for (const { dictionary, tagNames } of tagGroups) {
765
+ let dictionaryItems = targetMap.get(dictionary);
766
+ if (typeof dictionaryItems === "undefined") {
767
+ dictionaryItems = new Map();
768
+ targetMap.set(dictionary, dictionaryItems);
769
+ }
770
+ for (const tagName of tagNames) {
771
+ let item = dictionaryItems.get(tagName);
772
+ if (typeof item === "undefined") {
773
+ const query = this._getNameBase(tagName);
774
+ item = {
775
+ query,
776
+ dictionary,
777
+ tagName,
778
+ cache: null,
779
+ databaseTag: null,
780
+ targets: []
781
+ };
782
+ dictionaryItems.set(tagName, item);
783
+ allItems.push(item);
784
+ }
785
+ item.targets.push(tags);
786
+ }
787
+ }
788
+ const nonCachedItems = [];
789
+ const tagCache = this._tagCache;
790
+ for (const [dictionary, dictionaryItems] of targetMap.entries()) {
791
+ let cache = tagCache.get(dictionary);
792
+ if (typeof cache === "undefined") {
793
+ cache = new Map();
794
+ tagCache.set(dictionary, cache);
795
+ }
796
+ for (const item of dictionaryItems.values()) {
797
+ const databaseTag = cache.get(item.query);
798
+ if (typeof databaseTag !== "undefined") item.databaseTag = databaseTag;
799
+ else {
800
+ item.cache = cache;
801
+ nonCachedItems.push(item);
802
+ }
803
+ }
804
+ }
805
+ const nonCachedItemCount = nonCachedItems.length;
806
+ if (nonCachedItemCount > 0) {
807
+ const databaseTags = await this._database.findTagMetaBulk(nonCachedItems);
808
+ for (let i = 0; i < nonCachedItemCount; ++i) {
809
+ const item = nonCachedItems[i];
810
+ const databaseTag = databaseTags[i];
811
+ const databaseTag2 = typeof databaseTag !== "undefined" ? databaseTag : null;
812
+ item.databaseTag = databaseTag2;
813
+ if (item.cache !== null) item.cache.set(item.query, databaseTag2);
814
+ }
815
+ }
816
+ for (const { dictionary, tagName, databaseTag, targets } of allItems) for (const tags of targets) tags.push(this._createTag(databaseTag, tagName, dictionary));
817
+ }
818
+ _groupTags(tagTargets) {
819
+ const stringComparer = this._stringComparer;
820
+ const compare = (v1, v2) => {
821
+ const i = v1.order - v2.order;
822
+ return i !== 0 ? i : stringComparer.compare(v1.name, v2.name);
823
+ };
824
+ for (const { tags } of tagTargets) {
825
+ if (tags.length <= 1) continue;
826
+ this._mergeSimilarTags(tags);
827
+ tags.sort(compare);
828
+ }
829
+ }
830
+ _mergeSimilarTags(tags) {
831
+ let tagCount = tags.length;
832
+ for (let i = 0; i < tagCount; ++i) {
833
+ const tag1 = tags[i];
834
+ const { category, name } = tag1;
835
+ for (let j = i + 1; j < tagCount; ++j) {
836
+ const tag2 = tags[j];
837
+ if (tag2.name !== name || tag2.category !== category) continue;
838
+ tag1.order = Math.min(tag1.order, tag2.order);
839
+ tag1.score = Math.max(tag1.score, tag2.score);
840
+ tag1.dictionaries.push(...tag2.dictionaries);
841
+ this._addUniqueSimple(tag1.content, tag2.content);
842
+ tags.splice(j, 1);
843
+ --tagCount;
844
+ --j;
845
+ }
846
+ }
847
+ }
848
+ _getTagNamesWithCategory(tags, category) {
849
+ const results = [];
850
+ for (const tag of tags) {
851
+ if (tag.category !== category) continue;
852
+ results.push(tag.name);
853
+ }
854
+ results.sort();
855
+ return results;
856
+ }
857
+ _flagRedundantDefinitionTags(definitions) {
858
+ if (definitions.length === 0) return;
859
+ let lastDictionary = null;
860
+ let lastPartOfSpeech = "";
861
+ const removeCategoriesSet = new Set();
862
+ for (const { dictionary, tags } of definitions) {
863
+ const partOfSpeech = this._createMapKey(this._getTagNamesWithCategory(tags, "partOfSpeech"));
864
+ if (lastDictionary !== dictionary) {
865
+ lastDictionary = dictionary;
866
+ lastPartOfSpeech = "";
867
+ }
868
+ if (lastPartOfSpeech === partOfSpeech) removeCategoriesSet.add("partOfSpeech");
869
+ else lastPartOfSpeech = partOfSpeech;
870
+ if (removeCategoriesSet.size > 0) {
871
+ for (const tag of tags) if (removeCategoriesSet.has(tag.category)) tag.redundant = true;
872
+ removeCategoriesSet.clear();
873
+ }
874
+ }
875
+ }
876
+ async _addTermMeta(dictionaryEntries, enabledDictionaryMap, tagAggregator) {
877
+ const headwordMap = new Map();
878
+ const headwordMapKeys = [];
879
+ const headwordReadingMaps = [];
880
+ for (const { headwords, pronunciations, frequencies } of dictionaryEntries) for (let i = 0, ii = headwords.length; i < ii; ++i) {
881
+ const { term, reading } = headwords[i];
882
+ let readingMap = headwordMap.get(term);
883
+ if (typeof readingMap === "undefined") {
884
+ readingMap = new Map();
885
+ headwordMap.set(term, readingMap);
886
+ headwordMapKeys.push(term);
887
+ headwordReadingMaps.push(readingMap);
888
+ }
889
+ let targets = readingMap.get(reading);
890
+ if (typeof targets === "undefined") {
891
+ targets = [];
892
+ readingMap.set(reading, targets);
893
+ }
894
+ targets.push({
895
+ headwordIndex: i,
896
+ pronunciations,
897
+ frequencies
898
+ });
899
+ }
900
+ const metas = await this._database.findTermMetaBulk(headwordMapKeys, enabledDictionaryMap);
901
+ for (const { mode, data, dictionary, index } of metas) {
902
+ const { index: dictionaryIndex } = this._getDictionaryOrder(dictionary, enabledDictionaryMap);
903
+ const dictionaryAlias = this._getDictionaryAlias(dictionary, enabledDictionaryMap);
904
+ const map2 = headwordReadingMaps[index];
905
+ for (const [reading, targets] of map2.entries()) switch (mode) {
906
+ case "freq":
907
+ {
908
+ const hasReading = data !== null && typeof data === "object" && typeof data.reading === "string";
909
+ if (hasReading && data.reading !== reading) continue;
910
+ const frequency = hasReading ? data.frequency : data;
911
+ for (const { frequencies, headwordIndex } of targets) {
912
+ const { frequency: frequencyValue, displayValue, displayValueParsed } = this._getFrequencyInfo(frequency);
913
+ frequencies.push(this._createTermFrequency(frequencies.length, headwordIndex, dictionary, dictionaryIndex, dictionaryAlias, hasReading, frequencyValue, displayValue, displayValueParsed));
914
+ }
915
+ }
916
+ break;
917
+ case "pitch":
918
+ {
919
+ if (data.reading !== reading) continue;
920
+ const pitches = [];
921
+ for (const { position, tags, nasal, devoice } of data.pitches) {
922
+ const tags2 = [];
923
+ if (Array.isArray(tags)) tagAggregator.addTags(tags2, dictionary, tags);
924
+ const nasalPositions = this._toNumberArray(nasal);
925
+ const devoicePositions = this._toNumberArray(devoice);
926
+ pitches.push({
927
+ type: "pitch-accent",
928
+ positions: position,
929
+ nasalPositions,
930
+ devoicePositions,
931
+ tags: tags2
932
+ });
933
+ }
934
+ for (const { pronunciations, headwordIndex } of targets) pronunciations.push(this._createTermPronunciation(pronunciations.length, headwordIndex, dictionary, dictionaryIndex, dictionaryAlias, pitches));
935
+ }
936
+ break;
937
+ case "ipa": {
938
+ if (data.reading !== reading) continue;
939
+ const phoneticTranscriptions = [];
940
+ for (const { ipa, tags } of data.transcriptions) {
941
+ const tags2 = [];
942
+ if (Array.isArray(tags)) tagAggregator.addTags(tags2, dictionary, tags);
943
+ phoneticTranscriptions.push({
944
+ type: "phonetic-transcription",
945
+ ipa,
946
+ tags: tags2
947
+ });
948
+ }
949
+ for (const { pronunciations, headwordIndex } of targets) pronunciations.push(this._createTermPronunciation(pronunciations.length, headwordIndex, dictionary, dictionaryIndex, dictionaryAlias, phoneticTranscriptions));
950
+ }
951
+ }
952
+ }
953
+ }
954
+ async _addKanjiMeta(dictionaryEntries, enabledDictionaryMap) {
955
+ const kanjiList = [];
956
+ for (const { character } of dictionaryEntries) kanjiList.push(character);
957
+ const metas = await this._database.findKanjiMetaBulk(kanjiList, enabledDictionaryMap);
958
+ for (const { character, mode, data, dictionary, index } of metas) {
959
+ const { index: dictionaryIndex } = this._getDictionaryOrder(dictionary, enabledDictionaryMap);
960
+ const dictionaryAlias = this._getDictionaryAlias(dictionary, enabledDictionaryMap);
961
+ switch (mode) {
962
+ case "freq":
963
+ {
964
+ const { frequencies } = dictionaryEntries[index];
965
+ const { frequency, displayValue, displayValueParsed } = this._getFrequencyInfo(data);
966
+ frequencies.push(this._createKanjiFrequency(frequencies.length, dictionary, dictionaryIndex, dictionaryAlias, character, frequency, displayValue, displayValueParsed));
967
+ }
968
+ break;
969
+ }
970
+ }
971
+ }
972
+ async _expandKanjiStats(stats, dictionary) {
973
+ const statsEntries = Object.entries(stats);
974
+ const items = [];
975
+ for (const [name] of statsEntries) {
976
+ const query = this._getNameBase(name);
977
+ items.push({
978
+ query,
979
+ dictionary
980
+ });
981
+ }
982
+ const databaseInfos = await this._database.findTagMetaBulk(items);
983
+ const statsGroups = new Map();
984
+ for (let i = 0, ii = statsEntries.length; i < ii; ++i) {
985
+ const databaseInfo = databaseInfos[i];
986
+ if (typeof databaseInfo === "undefined") continue;
987
+ const [name, value] = statsEntries[i];
988
+ const { category } = databaseInfo;
989
+ let group = statsGroups.get(category);
990
+ if (typeof group === "undefined") {
991
+ group = [];
992
+ statsGroups.set(category, group);
993
+ }
994
+ group.push(this._createKanjiStat(name, value, databaseInfo, dictionary));
995
+ }
996
+ const groupedStats = {};
997
+ for (const [category, group] of statsGroups.entries()) {
998
+ this._sortKanjiStats(group);
999
+ groupedStats[category] = group;
1000
+ }
1001
+ return groupedStats;
1002
+ }
1003
+ _sortKanjiStats(stats) {
1004
+ if (stats.length <= 1) return;
1005
+ const stringComparer = this._stringComparer;
1006
+ stats.sort((v1, v2) => {
1007
+ const i = v1.order - v2.order;
1008
+ return i !== 0 ? i : stringComparer.compare(v1.content, v2.content);
1009
+ });
1010
+ }
1011
+ _convertStringToNumber(value) {
1012
+ const match = this._numberRegex.exec(value);
1013
+ if (match === null) return 0;
1014
+ const result = Number.parseFloat(match[0]);
1015
+ return Number.isFinite(result) ? result : 0;
1016
+ }
1017
+ _getFrequencyInfo(frequency) {
1018
+ let frequencyValue = 0;
1019
+ let displayValue = null;
1020
+ let displayValueParsed = false;
1021
+ if (typeof frequency === "object" && frequency !== null) {
1022
+ const { value: frequencyValue2, displayValue: displayValue2 } = frequency;
1023
+ if (typeof frequencyValue2 === "number") frequencyValue = frequencyValue2;
1024
+ if (typeof displayValue2 === "string") displayValue = displayValue2;
1025
+ } else switch (typeof frequency) {
1026
+ case "number":
1027
+ frequencyValue = frequency;
1028
+ break;
1029
+ case "string":
1030
+ displayValue = frequency;
1031
+ displayValueParsed = true;
1032
+ frequencyValue = this._convertStringToNumber(frequency);
1033
+ break;
1034
+ }
1035
+ return {
1036
+ frequency: frequencyValue,
1037
+ displayValue,
1038
+ displayValueParsed
1039
+ };
1040
+ }
1041
+ _getNameBase(name) {
1042
+ const pos = name.indexOf(":");
1043
+ return pos >= 0 ? name.substring(0, pos) : name;
1044
+ }
1045
+ _getSecondarySearchDictionaryMap(enabledDictionaryMap) {
1046
+ const secondarySearchDictionaryMap = new Map();
1047
+ for (const [dictionary, details] of enabledDictionaryMap.entries()) {
1048
+ if (!details.allowSecondarySearches) continue;
1049
+ secondarySearchDictionaryMap.set(dictionary, details);
1050
+ }
1051
+ return secondarySearchDictionaryMap;
1052
+ }
1053
+ _getDictionaryOrder(dictionary, enabledDictionaryMap) {
1054
+ const info = enabledDictionaryMap.get(dictionary);
1055
+ const { index } = typeof info !== "undefined" ? info : { index: enabledDictionaryMap.size };
1056
+ return { index };
1057
+ }
1058
+ _getDictionaryAlias(dictionary, enabledDictionaryMap) {
1059
+ const info = enabledDictionaryMap.get(dictionary);
1060
+ return info?.alias || dictionary;
1061
+ }
1062
+ _createMapKey(array) {
1063
+ return JSON.stringify(array);
1064
+ }
1065
+ _toNumberArray(value) {
1066
+ return Array.isArray(value) ? value : typeof value === "number" ? [value] : [];
1067
+ }
1068
+ _createKanjiStat(name, value, databaseInfo, dictionary) {
1069
+ const { category, notes, order, score } = databaseInfo;
1070
+ return {
1071
+ name,
1072
+ category: typeof category === "string" && category.length > 0 ? category : "default",
1073
+ content: typeof notes === "string" ? notes : "",
1074
+ order: typeof order === "number" ? order : 0,
1075
+ score: typeof score === "number" ? score : 0,
1076
+ dictionary,
1077
+ value
1078
+ };
1079
+ }
1080
+ _createKanjiFrequency(index, dictionary, dictionaryIndex, dictionaryAlias, character, frequency, displayValue, displayValueParsed) {
1081
+ return {
1082
+ index,
1083
+ dictionary,
1084
+ dictionaryIndex,
1085
+ dictionaryAlias,
1086
+ character,
1087
+ frequency,
1088
+ displayValue,
1089
+ displayValueParsed
1090
+ };
1091
+ }
1092
+ _createKanjiDictionaryEntry(character, dictionary, dictionaryAlias, onyomi, kunyomi, stats, definitions, enabledDictionaryMap) {
1093
+ const { index: dictionaryIndex } = this._getDictionaryOrder(dictionary, enabledDictionaryMap);
1094
+ return {
1095
+ type: "kanji",
1096
+ character,
1097
+ dictionary,
1098
+ dictionaryIndex,
1099
+ dictionaryAlias,
1100
+ onyomi,
1101
+ kunyomi,
1102
+ tags: [],
1103
+ stats,
1104
+ definitions,
1105
+ frequencies: []
1106
+ };
1107
+ }
1108
+ _createTag(databaseTag, name, dictionary) {
1109
+ let category;
1110
+ let notes;
1111
+ let order;
1112
+ let score;
1113
+ if (typeof databaseTag === "object" && databaseTag !== null) ({category, notes, order, score} = databaseTag);
1114
+ return {
1115
+ name,
1116
+ category: typeof category === "string" && category.length > 0 ? category : "default",
1117
+ order: typeof order === "number" ? order : 0,
1118
+ score: typeof score === "number" ? score : 0,
1119
+ content: typeof notes === "string" && notes.length > 0 ? [notes] : [],
1120
+ dictionaries: [dictionary],
1121
+ redundant: false
1122
+ };
1123
+ }
1124
+ _createSource(originalText, transformedText, deinflectedText, matchType, matchSource, isPrimary) {
1125
+ return {
1126
+ originalText,
1127
+ transformedText,
1128
+ deinflectedText,
1129
+ matchType,
1130
+ matchSource,
1131
+ isPrimary
1132
+ };
1133
+ }
1134
+ _createTermHeadword(index, term, reading, sources, tags, wordClasses) {
1135
+ return {
1136
+ index,
1137
+ term,
1138
+ reading,
1139
+ sources,
1140
+ tags,
1141
+ wordClasses
1142
+ };
1143
+ }
1144
+ _createTermDefinition(index, headwordIndices, dictionary, dictionaryIndex, dictionaryAlias, id, score, sequences, isPrimary, tags, entries) {
1145
+ return {
1146
+ index,
1147
+ headwordIndices,
1148
+ dictionary,
1149
+ dictionaryIndex,
1150
+ dictionaryAlias,
1151
+ id,
1152
+ score,
1153
+ frequencyOrder: 0,
1154
+ sequences,
1155
+ isPrimary,
1156
+ tags,
1157
+ entries
1158
+ };
1159
+ }
1160
+ _createTermPronunciation(index, headwordIndex, dictionary, dictionaryIndex, dictionaryAlias, pronunciations) {
1161
+ return {
1162
+ index,
1163
+ headwordIndex,
1164
+ dictionary,
1165
+ dictionaryIndex,
1166
+ dictionaryAlias,
1167
+ pronunciations
1168
+ };
1169
+ }
1170
+ _createTermFrequency(index, headwordIndex, dictionary, dictionaryIndex, dictionaryAlias, hasReading, frequency, displayValue, displayValueParsed) {
1171
+ return {
1172
+ index,
1173
+ headwordIndex,
1174
+ dictionary,
1175
+ dictionaryIndex,
1176
+ dictionaryAlias,
1177
+ hasReading,
1178
+ frequency,
1179
+ displayValue,
1180
+ displayValueParsed
1181
+ };
1182
+ }
1183
+ _createTermDictionaryEntry(isPrimary, textProcessorRuleChainCandidates, inflectionRuleChainCandidates, score, dictionaryIndex, dictionaryAlias, sourceTermExactMatchCount, matchPrimaryReading, maxOriginalTextLength, headwords, definitions) {
1184
+ return {
1185
+ type: "term",
1186
+ isPrimary,
1187
+ textProcessorRuleChainCandidates,
1188
+ inflectionRuleChainCandidates,
1189
+ score,
1190
+ frequencyOrder: 0,
1191
+ dictionaryIndex,
1192
+ dictionaryAlias,
1193
+ sourceTermExactMatchCount,
1194
+ matchPrimaryReading,
1195
+ maxOriginalTextLength,
1196
+ headwords,
1197
+ definitions,
1198
+ pronunciations: [],
1199
+ frequencies: []
1200
+ };
1201
+ }
1202
+ _createTermDictionaryEntryFromDatabaseEntry(databaseEntry, originalText, transformedText, deinflectedText, textProcessorRuleChainCandidates, inflectionRuleChainCandidates, isPrimary, enabledDictionaryMap, tagAggregator, primaryReading) {
1203
+ const { matchType, matchSource, term, reading: rawReading, definitionTags, termTags, definitions, score, dictionary, id, sequence: rawSequence, rules } = databaseEntry;
1204
+ const contentDefinitions = definitions;
1205
+ const reading = rawReading.length > 0 ? rawReading : term;
1206
+ const matchPrimaryReading = primaryReading.length > 0 && reading === primaryReading;
1207
+ const { index: dictionaryIndex } = this._getDictionaryOrder(dictionary, enabledDictionaryMap);
1208
+ const dictionaryAlias = this._getDictionaryAlias(dictionary, enabledDictionaryMap);
1209
+ const sourceTermExactMatchCount = isPrimary && deinflectedText === term ? 1 : 0;
1210
+ const source = this._createSource(originalText, transformedText, deinflectedText, matchType, matchSource, isPrimary);
1211
+ const maxOriginalTextLength = originalText.length;
1212
+ const hasSequence = rawSequence >= 0;
1213
+ const sequence = hasSequence ? rawSequence : -1;
1214
+ const headwordTagGroups = [];
1215
+ const definitionTagGroups = [];
1216
+ tagAggregator.addTags(headwordTagGroups, dictionary, termTags);
1217
+ tagAggregator.addTags(definitionTagGroups, dictionary, definitionTags);
1218
+ const expandedInflectionRuleChainCandidates = inflectionRuleChainCandidates.map(({ source: src, inflectionRules }) => ({
1219
+ source: src,
1220
+ inflectionRules: inflectionRules.map((rule) => ({ name: rule }))
1221
+ }));
1222
+ return this._createTermDictionaryEntry(isPrimary, textProcessorRuleChainCandidates, expandedInflectionRuleChainCandidates, score, dictionaryIndex, dictionaryAlias, sourceTermExactMatchCount, matchPrimaryReading, maxOriginalTextLength, [this._createTermHeadword(0, term, reading, [source], headwordTagGroups, rules)], [this._createTermDefinition(0, [0], dictionary, dictionaryIndex, dictionaryAlias, id, score, [sequence], isPrimary, definitionTagGroups, contentDefinitions)]);
1223
+ }
1224
+ _createGroupedDictionaryEntry(language, dictionaryEntries, checkDuplicateDefinitions, tagAggregator, primaryReading) {
1225
+ const definitionEntries = [];
1226
+ const headwords = new Map();
1227
+ const headwordDictionaryIndices = new Map();
1228
+ for (const dictionaryEntry of dictionaryEntries) {
1229
+ const headwordIndexMap = this._addTermHeadwords(language, headwords, dictionaryEntry.headwords, tagAggregator);
1230
+ for (const headwordIndex of headwordIndexMap) {
1231
+ const existing = headwordDictionaryIndices.get(headwordIndex);
1232
+ if (typeof existing === "undefined" || dictionaryEntry.dictionaryIndex < existing) headwordDictionaryIndices.set(headwordIndex, dictionaryEntry.dictionaryIndex);
1233
+ }
1234
+ definitionEntries.push({
1235
+ index: definitionEntries.length,
1236
+ dictionaryEntry,
1237
+ headwordIndexMap
1238
+ });
1239
+ }
1240
+ if (definitionEntries.length <= 1) checkDuplicateDefinitions = false;
1241
+ let score = Number.MIN_SAFE_INTEGER;
1242
+ let dictionaryIndex = Number.MAX_SAFE_INTEGER;
1243
+ const dictionaryAlias = "";
1244
+ let maxOriginalTextLength = 0;
1245
+ let isPrimary = false;
1246
+ const definitions = [];
1247
+ const definitionsMap = checkDuplicateDefinitions ? new Map() : null;
1248
+ let inflections = null;
1249
+ let textProcesses = null;
1250
+ for (const { dictionaryEntry, headwordIndexMap } of definitionEntries) {
1251
+ score = Math.max(score, dictionaryEntry.score);
1252
+ dictionaryIndex = Math.min(dictionaryIndex, dictionaryEntry.dictionaryIndex);
1253
+ if (dictionaryEntry.isPrimary) {
1254
+ isPrimary = true;
1255
+ maxOriginalTextLength = Math.max(maxOriginalTextLength, dictionaryEntry.maxOriginalTextLength);
1256
+ const dictionaryEntryInflections = dictionaryEntry.inflectionRuleChainCandidates;
1257
+ const dictionaryEntryTextProcesses = dictionaryEntry.textProcessorRuleChainCandidates;
1258
+ if (inflections === null || dictionaryEntryInflections.length < inflections.length) inflections = dictionaryEntryInflections;
1259
+ if (textProcesses === null || dictionaryEntryTextProcesses.length < textProcesses.length) textProcesses = dictionaryEntryTextProcesses;
1260
+ }
1261
+ if (definitionsMap !== null) this._addTermDefinitions(definitions, definitionsMap, dictionaryEntry.definitions, headwordIndexMap, tagAggregator);
1262
+ else this._addTermDefinitionsFast(definitions, dictionaryEntry.definitions, headwordIndexMap);
1263
+ }
1264
+ const headwordsArray = [...headwords.values()];
1265
+ this._sortHeadwords(headwordsArray, headwordDictionaryIndices, definitions);
1266
+ const { sourceTermExactMatchCount, matchPrimaryReading } = this._getHeadwordMatchCounts(headwordsArray, primaryReading);
1267
+ return this._createTermDictionaryEntry(isPrimary, textProcesses !== null ? textProcesses : [], inflections !== null ? inflections : [], score, dictionaryIndex, dictionaryAlias, sourceTermExactMatchCount, matchPrimaryReading, maxOriginalTextLength, headwordsArray, definitions);
1268
+ }
1269
+ _sortHeadwords(headwordsArray, headwordDictionaryIndices, definitions) {
1270
+ headwordsArray.sort((a, b) => {
1271
+ const aHasPrimary = a.sources.some((s) => s.isPrimary);
1272
+ const bHasPrimary = b.sources.some((s) => s.isPrimary);
1273
+ if (aHasPrimary !== bHasPrimary) return aHasPrimary ? -1 : 1;
1274
+ const aDictIndex = headwordDictionaryIndices.get(a.index) ?? Number.MAX_SAFE_INTEGER;
1275
+ const bDictIndex = headwordDictionaryIndices.get(b.index) ?? Number.MAX_SAFE_INTEGER;
1276
+ return aDictIndex - bDictIndex;
1277
+ });
1278
+ const headwordIndexMap = new Map();
1279
+ for (let i = 0; i < headwordsArray.length; i++) {
1280
+ headwordIndexMap.set(headwordsArray[i].index, i);
1281
+ headwordsArray[i].index = i;
1282
+ }
1283
+ for (const definition of definitions) for (let i = 0; i < definition.headwordIndices.length; i++) {
1284
+ const oldIndex = definition.headwordIndices[i];
1285
+ const newIndex = headwordIndexMap.get(oldIndex);
1286
+ if (typeof newIndex === "number") definition.headwordIndices[i] = newIndex;
1287
+ }
1288
+ }
1289
+ _getHeadwordMatchCounts(headwordsArray, primaryReading) {
1290
+ let sourceTermExactMatchCount = 0;
1291
+ let matchPrimaryReading = false;
1292
+ for (const { sources, reading } of headwordsArray) {
1293
+ if (primaryReading.length > 0 && reading === primaryReading) matchPrimaryReading = true;
1294
+ for (const source of sources) if (source.isPrimary && source.matchSource === "term") {
1295
+ ++sourceTermExactMatchCount;
1296
+ break;
1297
+ }
1298
+ }
1299
+ return {
1300
+ sourceTermExactMatchCount,
1301
+ matchPrimaryReading
1302
+ };
1303
+ }
1304
+ _addUniqueSimple(list, newItems) {
1305
+ for (const item of newItems) if (!list.includes(item)) list.push(item);
1306
+ }
1307
+ _addUniqueSources(sources, newSources) {
1308
+ if (newSources.length === 0) return;
1309
+ if (sources.length === 0) {
1310
+ sources.push(...newSources);
1311
+ return;
1312
+ }
1313
+ for (const newSource of newSources) {
1314
+ const { originalText, transformedText, deinflectedText, matchType, matchSource, isPrimary } = newSource;
1315
+ let has = false;
1316
+ for (const source of sources) if (source.deinflectedText === deinflectedText && source.transformedText === transformedText && source.originalText === originalText && source.matchType === matchType && source.matchSource === matchSource) {
1317
+ if (isPrimary) source.isPrimary = true;
1318
+ has = true;
1319
+ break;
1320
+ }
1321
+ if (!has) sources.push(newSource);
1322
+ }
1323
+ }
1324
+ _addTermHeadwords(language, headwordsMap, headwords, tagAggregator) {
1325
+ const headwordIndexMap = [];
1326
+ for (const { term, reading, sources, tags, wordClasses } of headwords) {
1327
+ const readingNormalizer = this._readingNormalizers.get(language);
1328
+ const normalizedReading = typeof readingNormalizer === "undefined" ? reading : readingNormalizer(reading);
1329
+ const key = this._createMapKey([term, normalizedReading]);
1330
+ let headword = headwordsMap.get(key);
1331
+ if (typeof headword === "undefined") {
1332
+ headword = this._createTermHeadword(headwordsMap.size, term, reading, [], [], []);
1333
+ headwordsMap.set(key, headword);
1334
+ }
1335
+ this._addUniqueSources(headword.sources, sources);
1336
+ this._addUniqueSimple(headword.wordClasses, wordClasses);
1337
+ tagAggregator.mergeTags(headword.tags, tags);
1338
+ headwordIndexMap.push(headword.index);
1339
+ }
1340
+ return headwordIndexMap;
1341
+ }
1342
+ _addUniqueTermHeadwordIndex(headwordIndices, headwordIndex) {
1343
+ let end = headwordIndices.length;
1344
+ if (end === 0) {
1345
+ headwordIndices.push(headwordIndex);
1346
+ return;
1347
+ }
1348
+ let start = 0;
1349
+ while (start < end) {
1350
+ const mid = Math.floor((start + end) / 2);
1351
+ const value = headwordIndices[mid];
1352
+ if (headwordIndex === value) return;
1353
+ if (headwordIndex > value) start = mid + 1;
1354
+ else end = mid;
1355
+ }
1356
+ if (headwordIndex === headwordIndices[start]) return;
1357
+ headwordIndices.splice(start, 0, headwordIndex);
1358
+ }
1359
+ _addTermDefinitionsFast(definitions, newDefinitions, headwordIndexMap) {
1360
+ for (const { headwordIndices, dictionary, dictionaryIndex, dictionaryAlias, sequences, id, score, isPrimary, tags, entries } of newDefinitions) {
1361
+ const headwordIndicesNew = [];
1362
+ for (const headwordIndex of headwordIndices) headwordIndicesNew.push(headwordIndexMap[headwordIndex]);
1363
+ definitions.push(this._createTermDefinition(definitions.length, headwordIndicesNew, dictionary, dictionaryIndex, dictionaryAlias, id, score, sequences, isPrimary, tags, entries));
1364
+ }
1365
+ }
1366
+ _addTermDefinitions(definitions, definitionsMap, newDefinitions, headwordIndexMap, tagAggregator) {
1367
+ for (const { headwordIndices, dictionary, dictionaryIndex, dictionaryAlias, sequences, id, score, isPrimary, tags, entries } of newDefinitions) {
1368
+ const key = this._createMapKey([dictionary, ...entries]);
1369
+ let definition = definitionsMap.get(key);
1370
+ if (typeof definition === "undefined") {
1371
+ definition = this._createTermDefinition(definitions.length, [], dictionary, dictionaryIndex, dictionaryAlias, id, score, [...sequences], isPrimary, [], [...entries]);
1372
+ definitions.push(definition);
1373
+ definitionsMap.set(key, definition);
1374
+ } else {
1375
+ if (isPrimary) definition.isPrimary = true;
1376
+ this._addUniqueSimple(definition.sequences, sequences);
1377
+ }
1378
+ const newHeadwordIndices = definition.headwordIndices;
1379
+ for (const headwordIndex of headwordIndices) this._addUniqueTermHeadwordIndex(newHeadwordIndices, headwordIndexMap[headwordIndex]);
1380
+ tagAggregator.mergeTags(definition.tags, tags);
1381
+ }
1382
+ }
1383
+ _sortDatabaseEntriesByIndex(databaseEntries) {
1384
+ if (databaseEntries.length <= 1) return;
1385
+ const compareFunction = (v1, v2) => v1.index - v2.index;
1386
+ databaseEntries.sort(compareFunction);
1387
+ }
1388
+ _sortKanjiDictionaryEntries(dictionaryEntries) {
1389
+ const compareFunction = (v1, v2) => {
1390
+ return v1.dictionaryIndex - v2.dictionaryIndex;
1391
+ };
1392
+ dictionaryEntries.sort(compareFunction);
1393
+ }
1394
+ _sortTermDictionaryEntries(dictionaryEntries) {
1395
+ const stringComparer = this._stringComparer;
1396
+ const compareFunction = (v1, v2) => {
1397
+ let i = (v2.matchPrimaryReading ? 1 : 0) - (v1.matchPrimaryReading ? 1 : 0);
1398
+ if (i !== 0) return i;
1399
+ i = v2.maxOriginalTextLength - v1.maxOriginalTextLength;
1400
+ if (i !== 0) return i;
1401
+ i = this._getShortestTextProcessingChainLength(v1.textProcessorRuleChainCandidates) - this._getShortestTextProcessingChainLength(v2.textProcessorRuleChainCandidates);
1402
+ if (i !== 0) return i;
1403
+ i = this._getShortestInflectionChainLength(v1.inflectionRuleChainCandidates) - this._getShortestInflectionChainLength(v2.inflectionRuleChainCandidates);
1404
+ if (i !== 0) return i;
1405
+ i = v2.sourceTermExactMatchCount - v1.sourceTermExactMatchCount;
1406
+ if (i !== 0) return i;
1407
+ i = v1.frequencyOrder - v2.frequencyOrder;
1408
+ if (i !== 0) return i;
1409
+ i = v1.dictionaryIndex - v2.dictionaryIndex;
1410
+ if (i !== 0) return i;
1411
+ i = v2.score - v1.score;
1412
+ if (i !== 0) return i;
1413
+ const headwords1 = v1.headwords;
1414
+ const headwords2 = v2.headwords;
1415
+ for (let j = 0, jj = Math.min(headwords1.length, headwords2.length); j < jj; ++j) {
1416
+ const term1 = headwords1[j].term;
1417
+ const term2 = headwords2[j].term;
1418
+ i = term2.length - term1.length;
1419
+ if (i !== 0) return i;
1420
+ i = stringComparer.compare(term1, term2);
1421
+ if (i !== 0) return i;
1422
+ }
1423
+ i = v2.definitions.length - v1.definitions.length;
1424
+ return i;
1425
+ };
1426
+ dictionaryEntries.sort(compareFunction);
1427
+ }
1428
+ _sortTermDictionaryEntryDefinitions(definitions) {
1429
+ const compareFunction = (v1, v2) => {
1430
+ let i = v1.frequencyOrder - v2.frequencyOrder;
1431
+ if (i !== 0) return i;
1432
+ i = v1.dictionaryIndex - v2.dictionaryIndex;
1433
+ if (i !== 0) return i;
1434
+ i = v2.score - v1.score;
1435
+ if (i !== 0) return i;
1436
+ const headwordIndices1 = v1.headwordIndices;
1437
+ const headwordIndices2 = v2.headwordIndices;
1438
+ const jj = headwordIndices1.length;
1439
+ i = headwordIndices2.length - jj;
1440
+ if (i !== 0) return i;
1441
+ for (let j = 0; j < jj; ++j) {
1442
+ i = headwordIndices1[j] - headwordIndices2[j];
1443
+ if (i !== 0) return i;
1444
+ }
1445
+ i = v1.index - v2.index;
1446
+ return i;
1447
+ };
1448
+ definitions.sort(compareFunction);
1449
+ }
1450
+ _sortTermDictionaryEntriesById(dictionaryEntries) {
1451
+ if (dictionaryEntries.length <= 1) return;
1452
+ dictionaryEntries.sort((a, b) => a.definitions[0].id - b.definitions[0].id);
1453
+ }
1454
+ _sortTermDictionaryEntrySimpleData(dataList) {
1455
+ const compare = (v1, v2) => {
1456
+ let i = v1.headwordIndex - v2.headwordIndex;
1457
+ if (i !== 0) return i;
1458
+ i = v1.dictionaryIndex - v2.dictionaryIndex;
1459
+ if (i !== 0) return i;
1460
+ i = v1.index - v2.index;
1461
+ return i;
1462
+ };
1463
+ dataList.sort(compare);
1464
+ }
1465
+ _sortKanjiDictionaryEntryData(dictionaryEntries) {
1466
+ const compare = (v1, v2) => {
1467
+ let i = v1.dictionaryIndex - v2.dictionaryIndex;
1468
+ if (i !== 0) return i;
1469
+ i = v1.index - v2.index;
1470
+ return i;
1471
+ };
1472
+ for (const { frequencies } of dictionaryEntries) frequencies.sort(compare);
1473
+ }
1474
+ _updateSortFrequencies(dictionaryEntries, dictionary, ascending) {
1475
+ const frequencyMap = new Map();
1476
+ for (const dictionaryEntry of dictionaryEntries) {
1477
+ const { definitions, frequencies } = dictionaryEntry;
1478
+ let frequencyMin = Number.MAX_SAFE_INTEGER;
1479
+ let frequencyMax = Number.MIN_SAFE_INTEGER;
1480
+ for (const item of frequencies) {
1481
+ if (item.dictionary !== dictionary) continue;
1482
+ const { headwordIndex, frequency } = item;
1483
+ if (typeof frequency !== "number") continue;
1484
+ frequencyMap.set(headwordIndex, frequency);
1485
+ frequencyMin = Math.min(frequencyMin, frequency);
1486
+ frequencyMax = Math.max(frequencyMax, frequency);
1487
+ }
1488
+ dictionaryEntry.frequencyOrder = frequencyMin <= frequencyMax ? ascending ? frequencyMin : -frequencyMax : ascending ? Number.MAX_SAFE_INTEGER : 0;
1489
+ for (const definition of definitions) {
1490
+ frequencyMin = Number.MAX_SAFE_INTEGER;
1491
+ frequencyMax = Number.MIN_SAFE_INTEGER;
1492
+ const { headwordIndices } = definition;
1493
+ for (const headwordIndex of headwordIndices) {
1494
+ const frequency = frequencyMap.get(headwordIndex);
1495
+ if (typeof frequency !== "number") continue;
1496
+ frequencyMin = Math.min(frequencyMin, frequency);
1497
+ frequencyMax = Math.max(frequencyMax, frequency);
1498
+ }
1499
+ definition.frequencyOrder = frequencyMin <= frequencyMax ? ascending ? frequencyMin : -frequencyMax : ascending ? Number.MAX_SAFE_INTEGER : 0;
1500
+ }
1501
+ frequencyMap.clear();
1502
+ }
1503
+ }
1504
+ _getShortestTextProcessingChainLength(textProcessorRuleChainCandidates) {
1505
+ if (textProcessorRuleChainCandidates.length === 0) return 0;
1506
+ let length = Number.MAX_SAFE_INTEGER;
1507
+ for (const candidate of textProcessorRuleChainCandidates) length = Math.min(length, candidate.length);
1508
+ return length;
1509
+ }
1510
+ _getShortestInflectionChainLength(inflectionRuleChainCandidates) {
1511
+ if (inflectionRuleChainCandidates.length === 0) return 0;
1512
+ let length = Number.MAX_SAFE_INTEGER;
1513
+ for (const { inflectionRules } of inflectionRuleChainCandidates) length = Math.min(length, inflectionRules.length);
1514
+ return length;
1515
+ }
1516
+ _addUserFacingInflections(language, dictionaryEntries) {
1517
+ const result = [];
1518
+ for (const dictionaryEntry of dictionaryEntries) {
1519
+ const { inflectionRuleChainCandidates } = dictionaryEntry;
1520
+ const expandedChains = inflectionRuleChainCandidates.map(({ source, inflectionRules }) => ({
1521
+ source,
1522
+ inflectionRules: this._multiLanguageTransformer.getUserFacingInflectionRules(language, inflectionRules.map((r) => r.name))
1523
+ }));
1524
+ result.push({
1525
+ ...dictionaryEntry,
1526
+ inflectionRuleChainCandidates: expandedChains
1527
+ });
1528
+ }
1529
+ return result;
1530
+ }
1531
+ _hasAny(set, values) {
1532
+ for (const value of values) if (set.has(value)) return true;
1533
+ return false;
1534
+ }
1535
+ };
1536
+
1537
+ //#endregion
1538
+ export { Translator };
1539
+ //# sourceMappingURL=translator-CWgG5drA.js.map