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