wikiparser-node 0.11.0 → 1.0.0-b

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 (179) hide show
  1. package/README.md +10 -9
  2. package/bundle/bundle.min.js +37 -0
  3. package/config/llwiki.json +1 -35
  4. package/config/minimum.json +136 -0
  5. package/config/moegirl.json +1 -44
  6. package/config/zhwiki.json +1 -466
  7. package/extensions/dist/base.js +105 -0
  8. package/extensions/dist/editor.js +172 -0
  9. package/extensions/dist/highlight.js +46 -0
  10. package/extensions/dist/lint.js +43 -0
  11. package/extensions/editor.css +63 -0
  12. package/extensions/ui.css +119 -0
  13. package/i18n/zh-hans.json +1 -0
  14. package/i18n/zh-hant.json +1 -0
  15. package/package.json +38 -31
  16. package/config/.schema.json +0 -134
  17. package/dist/index.d.ts +0 -114
  18. package/dist/lib/element.d.ts +0 -162
  19. package/dist/lib/node.d.ts +0 -291
  20. package/dist/lib/ranges.d.ts +0 -37
  21. package/dist/lib/text.d.ts +0 -64
  22. package/dist/lib/title.d.ts +0 -21
  23. package/dist/mixin/attributeParent.d.ts +0 -9
  24. package/dist/mixin/fixedToken.d.ts +0 -8
  25. package/dist/mixin/hidden.d.ts +0 -8
  26. package/dist/mixin/singleLine.d.ts +0 -8
  27. package/dist/mixin/sol.d.ts +0 -8
  28. package/dist/parser/brackets.d.ts +0 -12
  29. package/dist/parser/commentAndExt.d.ts +0 -8
  30. package/dist/parser/converter.d.ts +0 -7
  31. package/dist/parser/externalLinks.d.ts +0 -7
  32. package/dist/parser/hrAndDoubleUnderscore.d.ts +0 -11
  33. package/dist/parser/html.d.ts +0 -7
  34. package/dist/parser/links.d.ts +0 -7
  35. package/dist/parser/list.d.ts +0 -7
  36. package/dist/parser/magicLinks.d.ts +0 -7
  37. package/dist/parser/quotes.d.ts +0 -7
  38. package/dist/parser/selector.d.ts +0 -12
  39. package/dist/parser/table.d.ts +0 -11
  40. package/dist/src/arg.d.ts +0 -54
  41. package/dist/src/atom/hidden.d.ts +0 -5
  42. package/dist/src/atom/index.d.ts +0 -15
  43. package/dist/src/attribute.d.ts +0 -65
  44. package/dist/src/attributes.d.ts +0 -112
  45. package/dist/src/charinsert.d.ts +0 -32
  46. package/dist/src/converter.d.ts +0 -103
  47. package/dist/src/converterFlags.d.ts +0 -83
  48. package/dist/src/converterRule.d.ts +0 -75
  49. package/dist/src/extLink.d.ts +0 -62
  50. package/dist/src/gallery.d.ts +0 -33
  51. package/dist/src/hasNowiki/index.d.ts +0 -14
  52. package/dist/src/hasNowiki/pre.d.ts +0 -13
  53. package/dist/src/heading.d.ts +0 -44
  54. package/dist/src/html.d.ts +0 -56
  55. package/dist/src/imageParameter.d.ts +0 -65
  56. package/dist/src/imagemap.d.ts +0 -37
  57. package/dist/src/imagemapLink.d.ts +0 -21
  58. package/dist/src/index.d.ts +0 -186
  59. package/dist/src/link/category.d.ts +0 -16
  60. package/dist/src/link/file.d.ts +0 -85
  61. package/dist/src/link/galleryImage.d.ts +0 -15
  62. package/dist/src/link/index.d.ts +0 -88
  63. package/dist/src/magicLink.d.ts +0 -36
  64. package/dist/src/nested/choose.d.ts +0 -13
  65. package/dist/src/nested/combobox.d.ts +0 -13
  66. package/dist/src/nested/index.d.ts +0 -18
  67. package/dist/src/nested/references.d.ts +0 -13
  68. package/dist/src/nowiki/comment.d.ts +0 -31
  69. package/dist/src/nowiki/dd.d.ts +0 -17
  70. package/dist/src/nowiki/doubleUnderscore.d.ts +0 -22
  71. package/dist/src/nowiki/hr.d.ts +0 -13
  72. package/dist/src/nowiki/index.d.ts +0 -27
  73. package/dist/src/nowiki/list.d.ts +0 -8
  74. package/dist/src/nowiki/noinclude.d.ts +0 -8
  75. package/dist/src/nowiki/quote.d.ts +0 -13
  76. package/dist/src/onlyinclude.d.ts +0 -24
  77. package/dist/src/paramTag/index.d.ts +0 -29
  78. package/dist/src/paramTag/inputbox.d.ts +0 -8
  79. package/dist/src/parameter.d.ts +0 -75
  80. package/dist/src/syntax.d.ts +0 -20
  81. package/dist/src/table/index.d.ts +0 -273
  82. package/dist/src/table/td.d.ts +0 -100
  83. package/dist/src/table/tr.d.ts +0 -91
  84. package/dist/src/tagPair/ext.d.ts +0 -18
  85. package/dist/src/tagPair/include.d.ts +0 -25
  86. package/dist/src/tagPair/index.d.ts +0 -41
  87. package/dist/src/transclude.d.ts +0 -199
  88. package/dist/tool/index.d.ts +0 -420
  89. package/dist/util/base.d.ts +0 -10
  90. package/dist/util/debug.d.ts +0 -20
  91. package/dist/util/diff.d.ts +0 -8
  92. package/dist/util/lint.d.ts +0 -28
  93. package/dist/util/string.d.ts +0 -55
  94. package/index.js +0 -333
  95. package/lib/element.js +0 -618
  96. package/lib/node.js +0 -730
  97. package/lib/ranges.js +0 -130
  98. package/lib/text.js +0 -265
  99. package/lib/title.js +0 -83
  100. package/mixin/attributeParent.js +0 -117
  101. package/mixin/fixedToken.js +0 -40
  102. package/mixin/hidden.js +0 -21
  103. package/mixin/singleLine.js +0 -31
  104. package/mixin/sol.js +0 -54
  105. package/parser/brackets.js +0 -128
  106. package/parser/commentAndExt.js +0 -62
  107. package/parser/converter.js +0 -46
  108. package/parser/externalLinks.js +0 -33
  109. package/parser/hrAndDoubleUnderscore.js +0 -49
  110. package/parser/html.js +0 -42
  111. package/parser/links.js +0 -94
  112. package/parser/list.js +0 -59
  113. package/parser/magicLinks.js +0 -41
  114. package/parser/quotes.js +0 -64
  115. package/parser/selector.js +0 -180
  116. package/parser/table.js +0 -114
  117. package/src/arg.js +0 -207
  118. package/src/atom/hidden.js +0 -13
  119. package/src/atom/index.js +0 -43
  120. package/src/attribute.js +0 -472
  121. package/src/attributes.js +0 -453
  122. package/src/charinsert.js +0 -97
  123. package/src/converter.js +0 -176
  124. package/src/converterFlags.js +0 -284
  125. package/src/converterRule.js +0 -256
  126. package/src/extLink.js +0 -180
  127. package/src/gallery.js +0 -149
  128. package/src/hasNowiki/index.js +0 -44
  129. package/src/hasNowiki/pre.js +0 -40
  130. package/src/heading.js +0 -134
  131. package/src/html.js +0 -254
  132. package/src/imageParameter.js +0 -303
  133. package/src/imagemap.js +0 -199
  134. package/src/imagemapLink.js +0 -41
  135. package/src/index.js +0 -938
  136. package/src/link/category.js +0 -44
  137. package/src/link/file.js +0 -287
  138. package/src/link/galleryImage.js +0 -120
  139. package/src/link/index.js +0 -388
  140. package/src/magicLink.js +0 -151
  141. package/src/nested/choose.js +0 -24
  142. package/src/nested/combobox.js +0 -23
  143. package/src/nested/index.js +0 -96
  144. package/src/nested/references.js +0 -23
  145. package/src/nowiki/comment.js +0 -71
  146. package/src/nowiki/dd.js +0 -59
  147. package/src/nowiki/doubleUnderscore.js +0 -56
  148. package/src/nowiki/hr.js +0 -41
  149. package/src/nowiki/index.js +0 -56
  150. package/src/nowiki/list.js +0 -16
  151. package/src/nowiki/noinclude.js +0 -28
  152. package/src/nowiki/quote.js +0 -69
  153. package/src/onlyinclude.js +0 -64
  154. package/src/paramTag/index.js +0 -89
  155. package/src/paramTag/inputbox.js +0 -35
  156. package/src/parameter.js +0 -239
  157. package/src/syntax.js +0 -91
  158. package/src/table/index.js +0 -985
  159. package/src/table/td.js +0 -343
  160. package/src/table/tr.js +0 -319
  161. package/src/tagPair/ext.js +0 -146
  162. package/src/tagPair/include.js +0 -50
  163. package/src/tagPair/index.js +0 -131
  164. package/src/transclude.js +0 -843
  165. package/tool/index.js +0 -1209
  166. package/typings/api.d.ts +0 -9
  167. package/typings/array.d.ts +0 -29
  168. package/typings/event.d.ts +0 -22
  169. package/typings/index.d.ts +0 -118
  170. package/typings/node.d.ts +0 -35
  171. package/typings/parser.d.ts +0 -12
  172. package/typings/table.d.ts +0 -10
  173. package/typings/token.d.ts +0 -31
  174. package/typings/tool.d.ts +0 -6
  175. package/util/base.js +0 -17
  176. package/util/debug.js +0 -73
  177. package/util/diff.js +0 -76
  178. package/util/lint.js +0 -57
  179. package/util/string.js +0 -126
package/src/index.js DELETED
@@ -1,938 +0,0 @@
1
- 'use strict';
2
-
3
- /** @typedef {import('../typings/token').acceptable} acceptable */
4
- /**
5
- * @template T
6
- * @typedef {import('../typings/node').TokenAttribute<T>} TokenAttribute
7
- */
8
-
9
- // PHP解析器的步骤:
10
- // -1. 替换签名和`{{subst:}}`,参见Parser::preSaveTransform;这在revision中不可能保留,可以跳过
11
- // 0. 移除特定字符`\0`和`\x7F`,参见Parser::parse
12
- // 1. 注释/扩展标签('<'相关),参见Preprocessor_Hash::buildDomTreeArrayFromText和Sanitizer::decodeTagAttributes
13
- // 2. 模板/模板变量/标题,注意rightmost法则,以及`-{`和`[[`可以破坏`{{`或`{{{`语法,
14
- // 参见Preprocessor_Hash::buildDomTreeArrayFromText
15
- // 3. HTML标签(允许不匹配),参见Sanitizer::internalRemoveHtmlTags
16
- // 4. 表格,参见Parser::handleTables
17
- // 5. 水平线、状态开关和余下的标题,参见Parser::internalParse
18
- // 6. 内链,含文件和分类,参见Parser::handleInternalLinks2
19
- // 7. `'`,参见Parser::doQuotes
20
- // 8. 外链,参见Parser::handleExternalLinks
21
- // 9. ISBN、RFC(未来将废弃,不予支持)和自由外链,参见Parser::handleMagicLinks
22
- // 10. 段落和列表,参见BlockLevelPass::execute
23
- // 11. 转换,参见LanguageConverter::recursiveConvertTopLevel
24
-
25
- // \0\d+.\x7F标记Token:
26
- // e: ExtToken
27
- // a: AttributeToken
28
- // c: CommentToken、NoIncludeToken和IncludeToken
29
- // !: `{{!}}`专用
30
- // {: `{{(!}}`专用
31
- // }: `{{!)}}`专用
32
- // -: `{{!-}}`专用
33
- // +: `{{!!}}`专用
34
- // ~: `{{=}}`专用
35
- // s: `{{{|subst:}}}`
36
- // m: `{{fullurl:}}`、`{{canonicalurl:}}`或`{{filepath:}}`
37
- // t: ArgToken或TranscludeToken
38
- // h: HeadingToken
39
- // x: HtmlToken
40
- // b: TableToken
41
- // r: HrToken
42
- // u: DoubleUnderscoreToken
43
- // l: LinkToken
44
- // q: QuoteToken
45
- // w: ExtLinkToken
46
- // d: ListToken
47
- // v: ConverterToken
48
-
49
- const {text} = require('../util/string'),
50
- {externalUse} = require('../util/debug'),
51
- assert = require('assert/strict'),
52
- Ranges = require('../lib/ranges'),
53
- Parser = require('..'),
54
- AstElement = require('../lib/element'),
55
- AstText = require('../lib/text');
56
- const {MAX_STAGE, aliases} = Parser;
57
-
58
- /**
59
- * 所有节点的基类
60
- * @classdesc `{childNodes: ...(AstText|Token)}`
61
- */
62
- class Token extends AstElement {
63
- type = 'root';
64
- #stage = 0; // 解析阶段,参见顶部注释。只对plain Token有意义。
65
- #config;
66
- // 这个数组起两个作用:1. 数组中的Token会在build时替换`/\0\d+.\x7F/`标记;2. 数组中的Token会依次执行parseOnce和build方法。
67
- #accum;
68
- /** @type {boolean} */ #include;
69
- /** @type {Record<string, Ranges>} */ #acceptable;
70
- #protectedChildren = new Ranges();
71
-
72
- /**
73
- * 将维基语法替换为占位符
74
- * @param {number} n 解析阶段
75
- * @param {boolean} include 是否嵌入
76
- */
77
- #parseOnce = (n = this.#stage, include = false) => {
78
- if (n < this.#stage || !this.isPlain() || this.length === 0) {
79
- return this;
80
- }
81
- switch (n) {
82
- case 0:
83
- if (this.type === 'root') {
84
- this.#accum.shift();
85
- }
86
- this.#include = Boolean(include);
87
- this.#parseCommentAndExt(include);
88
- break;
89
- case 1:
90
- this.#parseBrackets();
91
- break;
92
- case 2:
93
- this.#parseHtml();
94
- break;
95
- case 3:
96
- this.#parseTable();
97
- break;
98
- case 4:
99
- this.#parseHrAndDoubleUndescore();
100
- break;
101
- case 5:
102
- this.#parseLinks();
103
- break;
104
- case 6:
105
- this.#parseQuotes();
106
- break;
107
-
108
- case 7:
109
- this.#parseExternalLinks();
110
- break;
111
- case 8:
112
- this.#parseMagicLinks();
113
- break;
114
- case 9:
115
- this.#parseList();
116
- break;
117
- case 10:
118
- this.#parseConverter();
119
- // no default
120
- }
121
- if (this.type === 'root') {
122
- for (const token of this.#accum) {
123
- token.getAttribute('parseOnce')(n, include);
124
- }
125
- }
126
- this.#stage++;
127
- return this;
128
- };
129
-
130
- /**
131
- * 重建wikitext
132
- * @template {string} T
133
- * @param {string} str 半解析的字符串
134
- * @param {T} type 返回类型
135
- * @complexity `n`
136
- * @returns {T extends 'string|text' ? string : (Token|AstText)[]}
137
- */
138
- #buildFromStr = (str, type) => {
139
- const nodes = str.split(/[\0\x7F]/u).map((s, i) => {
140
- if (i % 2 === 0) {
141
- return new AstText(s);
142
- } else if (isNaN(s.at(-1))) {
143
- return this.#accum[Number(s.slice(0, -1))];
144
- }
145
- throw new Error(`解析错误!未正确标记的 Token:${s}`);
146
- });
147
- if (type === 'string') {
148
- return nodes.map(String).join('');
149
- } else if (type === 'text') {
150
- return text(nodes);
151
- }
152
- return nodes;
153
- };
154
-
155
- /**
156
- * 将占位符替换为子Token
157
- * @complexity `n`
158
- */
159
- #build = () => {
160
- this.#stage = MAX_STAGE;
161
- const {length, firstChild} = this,
162
- str = String(firstChild);
163
- if (length === 1 && firstChild.type === 'text' && str.includes('\0')) {
164
- this.replaceChildren(...this.#buildFromStr(str));
165
- this.normalize();
166
- if (this.type === 'root') {
167
- for (const token of this.#accum) {
168
- token.getAttribute('build')();
169
- }
170
- }
171
- }
172
- };
173
-
174
- /**
175
- * 保护部分子节点不被移除
176
- * @param {...string|number|Range} args 子节点范围
177
- */
178
- #protectChildren = (...args) => {
179
- this.#protectedChildren.push(...new Ranges(args));
180
- };
181
-
182
- /** 所有图片,包括图库 */
183
- get images() {
184
- return this.querySelectorAll('file, gallery-image, imagemap-image');
185
- }
186
-
187
- /** 所有内链、外链和自由外链 */
188
- get links() {
189
- return this.querySelectorAll('link, ext-link, free-ext-link, image-parameter#link');
190
- }
191
-
192
- /** 所有模板和模块 */
193
- get embeds() {
194
- return this.querySelectorAll('template, magic-word#invoke');
195
- }
196
-
197
- /**
198
- * @param {string} wikitext wikitext
199
- * @param {import('../typings/token').accum} accum
200
- * @param {acceptable} acceptable 可接受的子节点设置
201
- */
202
- constructor(wikitext, config = Parser.getConfig(), halfParsed = false, accum = [], acceptable = undefined) {
203
- super();
204
- if (typeof wikitext === 'string') {
205
- this.insertAt(halfParsed ? wikitext : wikitext.replace(/[\0\x7F]/gu, ''));
206
- }
207
- this.#config = config;
208
- this.#accum = accum;
209
- this.setAttribute('acceptable', acceptable);
210
- accum.push(this);
211
- }
212
-
213
- /**
214
- * @override
215
- * @template {string} T
216
- * @param {T} key 属性键
217
- * @returns {TokenAttribute<T>}
218
- */
219
- getAttribute(key) {
220
- switch (key) {
221
- case 'config':
222
- return structuredClone(this.#config);
223
- case 'accum':
224
- return this.#accum;
225
- case 'parseOnce':
226
- return this.#parseOnce;
227
- case 'buildFromStr':
228
- return this.#buildFromStr;
229
- case 'build':
230
- return this.#build;
231
- case 'include': {
232
- if (this.#include !== undefined) {
233
- return this.#include;
234
- }
235
- const root = this.getRootNode();
236
- if (root.type === 'root' && root !== this) {
237
- return root.getAttribute('include');
238
- }
239
- const includeToken = root.querySelector('include');
240
- if (includeToken) {
241
- return includeToken.name === 'noinclude';
242
- }
243
- const noincludeToken = root.querySelector('noinclude');
244
- return Boolean(noincludeToken) && !/^<\/?noinclude(?:\s[^>]*)?\/?>$/iu.test(String(noincludeToken));
245
- }
246
- case 'stage':
247
- return this.#stage;
248
- case 'acceptable':
249
- return this.#acceptable ? {...this.#acceptable} : undefined;
250
- case 'protectChildren':
251
- return this.#protectChildren;
252
- case 'protectedChildren':
253
- return new Ranges(this.#protectedChildren);
254
- default:
255
- return super.getAttribute(key);
256
- }
257
- }
258
-
259
- /**
260
- * @override
261
- * @template {string} T
262
- * @param {T} key 属性键
263
- * @param {TokenAttribute<T>} value 属性值
264
- */
265
- setAttribute(key, value) {
266
- switch (key) {
267
- case 'stage':
268
- if (this.#stage === 0 && this.type === 'root') {
269
- this.#accum.shift();
270
- }
271
- this.#stage = value;
272
- return this;
273
- case 'acceptable': {
274
- const /** @type {acceptable} */ acceptable = {};
275
- if (value) {
276
- for (const [k, v] of Object.entries(value)) {
277
- if (k.startsWith('Stage-')) {
278
- for (let i = 0; i <= Number(k.slice(6)); i++) {
279
- for (const type of aliases[i]) {
280
- acceptable[type] = new Ranges(v);
281
- }
282
- }
283
- } else if (k[0] === '!') { // `!`项必须放在最后
284
- delete acceptable[k.slice(1)];
285
- } else {
286
- acceptable[k] = new Ranges(v);
287
- }
288
- }
289
- }
290
- this.#acceptable = value && acceptable;
291
- return this;
292
- }
293
- default:
294
- return super.setAttribute(key, value);
295
- }
296
- }
297
-
298
- /** 是否是普通节点 */
299
- isPlain() {
300
- return this.constructor === Token;
301
- }
302
-
303
- /**
304
- * @override
305
- * @template {string|Token} T
306
- * @param {T} token 待插入的子节点
307
- * @param {number} i 插入位置
308
- * @complexity `n`
309
- * @returns {T extends Token ? Token : AstText}
310
- * @throws `RangeError` 不可插入的子节点
311
- */
312
- insertAt(token, i = this.length) {
313
- if (typeof token === 'string') {
314
- token = new AstText(token);
315
- }
316
- if (!Parser.running && this.#acceptable) {
317
- const acceptableIndices = Object.fromEntries(
318
- Object.entries(this.#acceptable)
319
- .map(([str, ranges]) => [str, ranges.applyTo(this.length + 1)]),
320
- ),
321
- nodesAfter = this.childNodes.slice(i),
322
- {constructor: {name: insertedName}} = token,
323
- k = i < 0 ? i + this.length : i;
324
- if (!acceptableIndices[insertedName].includes(k)) {
325
- throw new RangeError(`${this.constructor.name} 的第 ${k} 个子节点不能为 ${insertedName}!`);
326
- } else if (nodesAfter.some(({constructor: {name}}, j) => !acceptableIndices[name].includes(k + j + 1))) {
327
- throw new Error(`${this.constructor.name} 插入新的第 ${k} 个子节点会破坏规定的顺序!`);
328
- }
329
- }
330
- super.insertAt(token, i);
331
- if (token.type === 'root') {
332
- token.type = 'plain';
333
- }
334
- return token;
335
- }
336
-
337
- /**
338
- * 规范化页面标题
339
- * @param {string} title 标题(含或不含命名空间前缀)
340
- * @param {number} defaultNs 命名空间
341
- * @param {boolean} decode 是否需要解码
342
- * @param {boolean} selfLink 是否允许selfLink
343
- */
344
- normalizeTitle(title, defaultNs = 0, halfParsed = false, decode = false, selfLink = false) {
345
- return Parser.normalizeTitle(title, defaultNs, this.#include, this.#config, halfParsed, decode, selfLink);
346
- }
347
-
348
- /**
349
- * @override
350
- * @param {number} i 移除位置
351
- * @returns {Token}
352
- * @complexity `n`
353
- * @throws `Error` 不可移除的子节点
354
- */
355
- removeAt(i) {
356
- if (!Number.isInteger(i)) {
357
- this.typeError('removeAt', 'Number');
358
- }
359
- const iPos = i < 0 ? i + this.length : i;
360
- if (!Parser.running) {
361
- const protectedIndices = this.#protectedChildren.applyTo(this.childNodes);
362
- if (protectedIndices.includes(iPos)) {
363
- throw new Error(`${this.constructor.name} 的第 ${i} 个子节点不可移除!`);
364
- } else if (this.#acceptable) {
365
- const acceptableIndices = Object.fromEntries(
366
- Object.entries(this.#acceptable)
367
- .map(([str, ranges]) => [str, ranges.applyTo(this.length - 1)]),
368
- ),
369
- nodesAfter = i === -1 ? [] : this.childNodes.slice(i + 1);
370
- if (nodesAfter.some(({constructor: {name}}, j) => !acceptableIndices[name].includes(i + j))) {
371
- throw new Error(`移除 ${this.constructor.name} 的第 ${i} 个子节点会破坏规定的顺序!`);
372
- }
373
- }
374
- }
375
- return super.removeAt(i);
376
- }
377
-
378
- /**
379
- * 替换为同类节点
380
- * @param {Token} token 待替换的节点
381
- * @complexity `n`
382
- * @throws `Error` 不存在父节点
383
- * @throws `Error` 待替换的节点具有不同属性
384
- */
385
- safeReplaceWith(token) {
386
- const {parentNode} = this;
387
- if (!parentNode) {
388
- throw new Error('不存在父节点!');
389
- } else if (token.constructor !== this.constructor) {
390
- this.typeError('safeReplaceWith', this.constructor.name);
391
- }
392
- try {
393
- assert.deepEqual(token.getAttribute('acceptable'), this.#acceptable);
394
- } catch (e) {
395
- if (e instanceof assert.AssertionError) {
396
- throw new Error(`待替换的 ${this.constructor.name} 带有不同的 #acceptable 属性!`);
397
- }
398
- throw e;
399
- }
400
- const i = parentNode.childNodes.indexOf(this);
401
- super.removeAt.call(parentNode, i);
402
- super.insertAt.call(parentNode, token, i);
403
- if (token.type === 'root') {
404
- token.type = 'plain';
405
- }
406
- const e = new Event('replace', {bubbles: true});
407
- token.dispatchEvent(e, {position: i, oldToken: this, newToken: token});
408
- }
409
-
410
- /**
411
- * 创建HTML注释
412
- * @param {string} data 注释内容
413
- */
414
- createComment(data = '') {
415
- if (typeof data === 'string') {
416
- const CommentToken = require('./nowiki/comment');
417
- const config = this.getAttribute('config');
418
- return Parser.run(() => new CommentToken(data.replaceAll('-->', '--&gt;'), true, config));
419
- }
420
- return this.typeError('createComment', 'String');
421
- }
422
-
423
- /**
424
- * 创建标签
425
- * @param {string} tagName 标签名
426
- * @param {{selfClosing: boolean, closing: boolean}} options 选项
427
- * @throws `RangeError` 非法的标签名
428
- */
429
- createElement(tagName, {selfClosing, closing} = {}) {
430
- if (typeof tagName !== 'string') {
431
- this.typeError('createElement', 'String');
432
- }
433
- const config = this.getAttribute('config'),
434
- include = this.getAttribute('include');
435
- if (tagName === (include ? 'noinclude' : 'includeonly')) {
436
- const IncludeToken = require('./tagPair/include');
437
- return Parser.run(
438
- () => new IncludeToken(tagName, '', undefined, selfClosing ? undefined : tagName, config),
439
- );
440
- } else if (config.ext.includes(tagName)) {
441
- const ExtToken = require('./tagPair/ext');
442
- return Parser.run(() => new ExtToken(tagName, '', '', selfClosing ? undefined : '', config));
443
- } else if (config.html.flat().includes(tagName)) {
444
- const HtmlToken = require('./html');
445
- return Parser.run(() => new HtmlToken(tagName, '', closing, selfClosing, config));
446
- }
447
- throw new RangeError(`非法的标签名!${tagName}`);
448
- }
449
-
450
- /**
451
- * 创建纯文本节点
452
- * @param {string} data 文本内容
453
- */
454
- createTextNode(data = '') {
455
- return typeof data === 'string' ? new AstText(data) : this.typeError('createComment', 'String');
456
- }
457
-
458
- /**
459
- * 找到给定位置所在的节点
460
- * @param {number} index 位置
461
- */
462
- caretPositionFromIndex(index) {
463
- if (index === undefined) {
464
- return undefined;
465
- } else if (!Number.isInteger(index)) {
466
- this.typeError('caretPositionFromIndex', 'Number');
467
- }
468
- const {length} = String(this);
469
- if (index > length || index < -length) {
470
- return undefined;
471
- } else if (index < 0) {
472
- index += length;
473
- }
474
- let child = this, // eslint-disable-line unicorn/no-this-assignment
475
- acc = 0,
476
- start = 0;
477
- while (child.type !== 'text') {
478
- const {childNodes} = child;
479
- acc += child.getPadding();
480
- for (let i = 0; acc <= index && i < childNodes.length; i++) {
481
- const cur = childNodes[i],
482
- {length: l} = String(cur);
483
- acc += l;
484
- if (acc >= index) {
485
- child = cur;
486
- acc -= l;
487
- start = acc;
488
- break;
489
- }
490
- acc += child.getGaps(i);
491
- }
492
- if (child.childNodes === childNodes) {
493
- return {offsetNode: child, offset: index - start};
494
- }
495
- }
496
- return {offsetNode: child, offset: index - start};
497
- }
498
-
499
- /**
500
- * 找到给定位置所在的节点
501
- * @param {number} x 列数
502
- * @param {number} y 行数
503
- */
504
- caretPositionFromPoint(x, y) {
505
- return this.caretPositionFromIndex(this.indexFromPos(y, x));
506
- }
507
-
508
- /**
509
- * 找到给定位置所在的最外层节点
510
- * @param {number} index 位置
511
- * @throws `Error` 不是根节点
512
- */
513
- elementFromIndex(index) {
514
- if (index === undefined) {
515
- return undefined;
516
- } else if (!Number.isInteger(index)) {
517
- this.typeError('elementFromIndex', 'Number');
518
- } else if (this.type !== 'root') {
519
- throw new Error('elementFromIndex方法只可用于根节点!');
520
- }
521
- const {length} = String(this);
522
- if (index > length || index < -length) {
523
- return undefined;
524
- } else if (index < 0) {
525
- index += length;
526
- }
527
- const {childNodes} = this;
528
- let acc = 0,
529
- i = 0;
530
- for (; acc < index && i < childNodes.length; i++) {
531
- const {length: l} = String(childNodes[i]);
532
- acc += l;
533
- }
534
- return childNodes[i && i - 1];
535
- }
536
-
537
- /**
538
- * 找到给定位置所在的最外层节点
539
- * @param {number} x 列数
540
- * @param {number} y 行数
541
- */
542
- elementFromPoint(x, y) {
543
- return this.elementFromIndex(this.indexFromPos(y, x));
544
- }
545
-
546
- /**
547
- * 找到给定位置所在的所有节点
548
- * @param {number} index 位置
549
- */
550
- elementsFromIndex(index) {
551
- const offsetNode = this.caretPositionFromIndex(index)?.offsetNode;
552
- return offsetNode && [...offsetNode.getAncestors().reverse(), offsetNode];
553
- }
554
-
555
- /**
556
- * 找到给定位置所在的所有节点
557
- * @param {number} x 列数
558
- * @param {number} y 行数
559
- */
560
- elementsFromPoint(x, y) {
561
- return this.elementsFromIndex(this.indexFromPos(y, x));
562
- }
563
-
564
- /**
565
- * 判断标题是否是跨维基链接
566
- * @param {string} title 标题
567
- */
568
- isInterwiki(title) {
569
- return Parser.isInterwiki(title, this.#config);
570
- }
571
-
572
- /**
573
- * 深拷贝所有子节点
574
- * @complexity `n`
575
- * @returns {(AstText|Token)[]}
576
- */
577
- cloneChildNodes() {
578
- return this.childNodes.map(child => child.cloneNode());
579
- }
580
-
581
- /**
582
- * 深拷贝节点
583
- * @complexity `n`
584
- * @throws `Error` 未定义复制方法
585
- */
586
- cloneNode() {
587
- if (this.constructor !== Token) {
588
- throw new Error(`未定义 ${this.constructor.name} 的复制方法!`);
589
- }
590
- const cloned = this.cloneChildNodes();
591
- return Parser.run(() => {
592
- const token = new Token(undefined, this.#config, false, [], this.#acceptable);
593
- token.type = this.type;
594
- token.append(...cloned);
595
- token.getAttribute('protectChildren')(...this.#protectedChildren);
596
- return token;
597
- });
598
- }
599
-
600
- /**
601
- * 获取全部章节
602
- * @complexity `n`
603
- */
604
- sections() {
605
- if (this.type !== 'root') {
606
- return undefined;
607
- }
608
- const {childNodes} = this,
609
- headings = [...childNodes.entries()].filter(([, {type}]) => type === 'heading')
610
- .map(([i, {name}]) => [i, Number(name)]),
611
- lastHeading = [-1, -1, -1, -1, -1, -1],
612
- /** @type {(AstText|Token)[][]} */ sections = new Array(headings.length);
613
- for (let i = 0; i < headings.length; i++) {
614
- const [index, level] = headings[i];
615
- for (let j = level; j < 6; j++) {
616
- const last = lastHeading[j];
617
- if (last >= 0) {
618
- sections[last] = childNodes.slice(headings[last][0], index);
619
- }
620
- lastHeading[j] = j === level ? i : -1;
621
- }
622
- }
623
- for (const last of lastHeading) {
624
- if (last >= 0) {
625
- sections[last] = childNodes.slice(headings[last][0]);
626
- }
627
- }
628
- sections.unshift(childNodes.slice(0, headings[0]?.[0]));
629
- return sections;
630
- }
631
-
632
- /**
633
- * 获取指定章节
634
- * @param {number} n 章节序号
635
- * @complexity `n`
636
- */
637
- section(n) {
638
- return Number.isInteger(n) ? this.sections()?.[n] : this.typeError('section', 'Number');
639
- }
640
-
641
- /**
642
- * 获取指定的外层HTML标签
643
- * @param {string|undefined} tag HTML标签名
644
- * @returns {[Token, Token]}
645
- * @complexity `n`
646
- * @throws `RangeError` 非法的标签或空标签
647
- */
648
- findEnclosingHtml(tag) {
649
- if (tag !== undefined && typeof tag !== 'string') {
650
- this.typeError('findEnclosingHtml', 'String');
651
- }
652
- tag = tag?.toLowerCase();
653
- if (tag !== undefined && !this.#config.html.slice(0, 2).flat().includes(tag)) {
654
- throw new RangeError(`非法的标签或空标签:${tag}`);
655
- }
656
- const {parentNode} = this;
657
- if (!parentNode) {
658
- return undefined;
659
- }
660
- const {childNodes} = parentNode,
661
- index = childNodes.indexOf(this);
662
- let i;
663
- for (i = index - 1; i >= 0; i--) {
664
- const {type, name, selfClosing, closing} = childNodes[i];
665
- if (type === 'html' && (!tag || name === tag) && selfClosing === false && closing === false) {
666
- break;
667
- }
668
- }
669
- if (i === -1) {
670
- return parentNode.findEnclosingHtml(tag);
671
- }
672
- const opening = childNodes[i];
673
- for (i = index + 1; i < childNodes.length; i++) {
674
- const {type, name, selfClosing, closing} = childNodes[i];
675
- if (type === 'html' && name === opening.name && selfClosing === false && closing === true) {
676
- break;
677
- }
678
- }
679
- return i === childNodes.length
680
- ? parentNode.findEnclosingHtml(tag)
681
- : [opening, childNodes[i]];
682
- }
683
-
684
- /**
685
- * 获取全部分类
686
- * @complexity `n`
687
- */
688
- getCategories() {
689
- return this.querySelectorAll('category').map(({name, sortkey}) => [name, sortkey]);
690
- }
691
-
692
- /**
693
- * 重新解析单引号
694
- * @throws `Error` 不接受QuoteToken作为子节点
695
- */
696
- redoQuotes() {
697
- const acceptable = this.getAttribute('acceptable');
698
- if (acceptable && !acceptable.QuoteToken?.some(
699
- range => typeof range !== 'number' && range.start === 0 && range.end === Infinity && range.step === 1,
700
- )) {
701
- throw new Error(`${this.constructor.name} 不接受 QuoteToken 作为子节点!`);
702
- }
703
- for (const quote of this.childNodes) {
704
- if (quote.type === 'quote') {
705
- quote.replaceWith(String(quote));
706
- }
707
- }
708
- this.normalize();
709
- /** @type {[number, AstText][]} */
710
- const textNodes = [...this.childNodes.entries()].filter(([, {type}]) => type === 'text'),
711
- indices = textNodes.map(([i]) => this.getRelativeIndex(i)),
712
- token = Parser.run(() => {
713
- const root = new Token(text(textNodes.map(([, str]) => str)), this.getAttribute('config'));
714
- return root.setAttribute('stage', 6).parse(7);
715
- });
716
- for (const quote of [...token.childNodes].reverse()) {
717
- if (quote.type === 'quote') {
718
- const index = quote.getRelativeIndex(),
719
- n = indices.findLastIndex(textIndex => textIndex <= index);
720
- this.childNodes[n].splitText(index - indices[n]);
721
- this.childNodes[n + 1].splitText(Number(quote.name));
722
- this.removeAt(n + 1);
723
- this.insertAt(quote, n + 1);
724
- }
725
- }
726
- this.normalize();
727
- }
728
-
729
- /** 解析部分魔术字 */
730
- solveConst() {
731
- const ArgToken = require('./arg'),
732
- ParameterToken = require('./parameter');
733
- const targets = this.querySelectorAll('magic-word, arg'),
734
- magicWords = new Set(['if', 'ifeq', 'switch']);
735
- for (let i = targets.length - 1; i >= 0; i--) {
736
- const /** @type {ArgToken} */ target = targets[i],
737
- {type, name, default: argDefault, childNodes, length} = target;
738
- if (type === 'arg' || type === 'magic-word' && magicWords.has(name)) {
739
- let replace = '';
740
- if (type === 'arg') {
741
- replace = argDefault === false ? String(target) : argDefault;
742
- } else if (name === 'if' && !childNodes[1].querySelector('magic-word, template')) {
743
- replace = String(childNodes[String(childNodes[1] ?? '').trim() ? 2 : 3] ?? '').trim();
744
- } else if (name === 'ifeq'
745
- && !childNodes.slice(1, 3).some(child => child.querySelector('magic-word, template'))
746
- ) {
747
- replace = String(childNodes[
748
- String(childNodes[1] ?? '').trim() === String(childNodes[2] ?? '') ? 3 : 4
749
- ] ?? '').trim();
750
- } else if (name === 'switch' && !childNodes[1].querySelector('magic-word, template')) {
751
- const key = String(childNodes[1] ?? '').trim();
752
- let defaultVal = '',
753
- found = false,
754
- transclusion = false,
755
- j = 2;
756
- for (; j < length; j++) {
757
- const /** @type {ParameterToken} */ {anon, name: option, value, firstChild} = childNodes[j];
758
- transclusion = firstChild.querySelector('magic-word, template');
759
- if (anon) {
760
- if (j === length - 1) {
761
- defaultVal = value;
762
- } else if (transclusion) {
763
- break;
764
- } else {
765
- found ||= key === value;
766
- }
767
- } else if (transclusion) {
768
- break;
769
- } else if (found || option === key) {
770
- replace = value;
771
- break;
772
- } else if (option.toLowerCase() === '#default') {
773
- defaultVal = value;
774
- }
775
- if (j === length - 1) {
776
- replace = defaultVal;
777
- }
778
- }
779
- if (transclusion) {
780
- continue;
781
- }
782
- } else {
783
- continue;
784
- }
785
- target.replaceWith(replace);
786
- }
787
- }
788
- }
789
-
790
- /** 生成部分Token的`name`属性 */
791
- afterBuild() {
792
- if (!Parser.debugging && externalUse('afterBuild')) {
793
- this.debugOnly('afterBuild');
794
- } else if (this.type === 'root') {
795
- for (const token of this.#accum) {
796
- token.afterBuild();
797
- }
798
- }
799
- }
800
-
801
- /**
802
- * 解析、重构、生成部分Token的`name`属性
803
- * @param {number} n 最大解析层级
804
- * @param {boolean} include 是否嵌入
805
- */
806
- parse(n = MAX_STAGE, include = false) {
807
- if (!Number.isInteger(n)) {
808
- this.typeError('parse', 'Number');
809
- }
810
- while (this.#stage < n) {
811
- this.#parseOnce(this.#stage, include);
812
- }
813
- if (n) {
814
- this.#build();
815
- this.afterBuild();
816
- }
817
- return this;
818
- }
819
-
820
- /**
821
- * 解析HTML注释和扩展标签
822
- * @param {boolean} includeOnly 是否嵌入
823
- */
824
- #parseCommentAndExt(includeOnly) {
825
- const parseCommentAndExt = require('../parser/commentAndExt');
826
- this.setText(parseCommentAndExt(String(this.firstChild), this.#config, this.#accum, includeOnly));
827
- }
828
-
829
- /** 解析花括号 */
830
- #parseBrackets() {
831
- const parseBrackets = require('../parser/brackets');
832
- const str = this.type === 'root' ? String(this.firstChild) : `\0${String(this.firstChild)}`,
833
- parsed = parseBrackets(str, this.#config, this.#accum);
834
- this.setText(this.type === 'root' ? parsed : parsed.slice(1));
835
- }
836
-
837
- /** 解析HTML标签 */
838
- #parseHtml() {
839
- if (this.#config.excludes.includes('html')) {
840
- return;
841
- }
842
- const parseHtml = require('../parser/html');
843
- this.setText(parseHtml(String(this.firstChild), this.#config, this.#accum));
844
- }
845
-
846
- /** 解析表格 */
847
- #parseTable() {
848
- if (this.#config.excludes.includes('table')) {
849
- return;
850
- }
851
- const parseTable = require('../parser/table'),
852
- TableToken = require('./table');
853
- this.setText(parseTable(this, this.#config, this.#accum));
854
- for (const table of this.#accum) {
855
- if (table instanceof TableToken && table.type !== 'td') {
856
- table.normalize();
857
- const {childNodes: [, child]} = table;
858
- if (typeof child === 'string' && child.includes('\0')) {
859
- table.removeAt(1);
860
- const inner = new Token(child, this.#config, true, this.#accum);
861
- table.insertAt(inner, 1);
862
- inner.setAttribute('stage', 4);
863
- }
864
- }
865
- }
866
- }
867
-
868
- /** 解析\<hr\>和状态开关 */
869
- #parseHrAndDoubleUndescore() {
870
- if (this.#config.excludes.includes('hr')) {
871
- return;
872
- }
873
- const parseHrAndDoubleUnderscore = require('../parser/hrAndDoubleUnderscore');
874
- this.setText(parseHrAndDoubleUnderscore(this, this.#config, this.#accum));
875
- }
876
-
877
- /** 解析内部链接 */
878
- #parseLinks() {
879
- const parseLinks = require('../parser/links');
880
- this.setText(parseLinks(String(this.firstChild), this.#config, this.#accum));
881
- }
882
-
883
- /** 解析单引号 */
884
- #parseQuotes() {
885
- if (this.#config.excludes.includes('quote')) {
886
- return;
887
- }
888
- const parseQuotes = require('../parser/quotes');
889
- const lines = String(this.firstChild).split('\n');
890
- for (let i = 0; i < lines.length; i++) {
891
- lines[i] = parseQuotes(lines[i], this.#config, this.#accum);
892
- }
893
- this.setText(lines.join('\n'));
894
- }
895
-
896
- /** 解析外部链接 */
897
- #parseExternalLinks() {
898
- if (this.#config.excludes.includes('extLink')) {
899
- return;
900
- }
901
- const parseExternalLinks = require('../parser/externalLinks');
902
- this.setText(parseExternalLinks(String(this.firstChild), this.#config, this.#accum));
903
- }
904
-
905
- /** 解析自由外链 */
906
- #parseMagicLinks() {
907
- if (this.#config.excludes.includes('magicLink')) {
908
- return;
909
- }
910
- const parseMagicLinks = require('../parser/magicLinks');
911
- this.setText(parseMagicLinks(String(this.firstChild), this.#config, this.#accum));
912
- }
913
-
914
- /** 解析列表 */
915
- #parseList() {
916
- if (this.#config.excludes.includes('list')) {
917
- return;
918
- }
919
- const parseList = require('../parser/list');
920
- const lines = String(this.firstChild).split('\n');
921
- let i = this.type === 'root' || this.type === 'ext-inner' && this.name === 'poem' ? 0 : 1;
922
- for (; i < lines.length; i++) {
923
- lines[i] = parseList(lines[i], this.#config, this.#accum);
924
- }
925
- this.setText(lines.join('\n'));
926
- }
927
-
928
- /** 解析语言变体转换 */
929
- #parseConverter() {
930
- if (this.#config.variants?.length > 0) {
931
- const parseConverter = require('../parser/converter');
932
- this.setText(parseConverter(String(this.firstChild), this.#config, this.#accum));
933
- }
934
- }
935
- }
936
-
937
- Parser.classes.Token = __filename;
938
- module.exports = Token;