wikiparser-node 0.9.2-b → 0.10.0-m

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 (80) hide show
  1. package/config/.schema.json +127 -0
  2. package/config/default.json +831 -0
  3. package/config/llwiki.json +595 -0
  4. package/config/moegirl.json +685 -0
  5. package/config/zhwiki.json +803 -0
  6. package/i18n/zh-hans.json +1 -0
  7. package/i18n/zh-hant.json +1 -0
  8. package/index.js +81 -0
  9. package/lib/element.js +136 -0
  10. package/lib/node.js +236 -0
  11. package/lib/text.js +168 -0
  12. package/lib/title.js +56 -0
  13. package/mixin/hidden.js +18 -0
  14. package/package.json +48 -47
  15. package/parser/brackets.js +125 -0
  16. package/parser/commentAndExt.js +58 -0
  17. package/parser/converter.js +45 -0
  18. package/parser/externalLinks.js +32 -0
  19. package/parser/hrAndDoubleUnderscore.js +48 -0
  20. package/parser/html.js +41 -0
  21. package/parser/links.js +93 -0
  22. package/parser/list.js +58 -0
  23. package/parser/magicLinks.js +40 -0
  24. package/parser/quotes.js +63 -0
  25. package/parser/table.js +113 -0
  26. package/src/arg.js +93 -0
  27. package/src/atom/hidden.js +11 -0
  28. package/src/atom/index.js +26 -0
  29. package/src/attribute.js +290 -0
  30. package/src/attributes.js +150 -0
  31. package/src/converter.js +70 -0
  32. package/src/converterFlags.js +97 -0
  33. package/src/converterRule.js +74 -0
  34. package/src/extLink.js +60 -0
  35. package/src/gallery.js +95 -0
  36. package/src/hasNowiki/index.js +32 -0
  37. package/src/hasNowiki/pre.js +28 -0
  38. package/src/heading.js +83 -0
  39. package/src/html.js +139 -0
  40. package/src/imageParameter.js +141 -0
  41. package/src/imagemap.js +140 -0
  42. package/src/imagemapLink.js +29 -0
  43. package/src/index.js +406 -0
  44. package/src/link/category.js +13 -0
  45. package/src/link/file.js +132 -0
  46. package/src/link/galleryImage.js +62 -0
  47. package/src/link/index.js +119 -0
  48. package/src/magicLink.js +66 -0
  49. package/src/nested/choose.js +23 -0
  50. package/src/nested/combobox.js +22 -0
  51. package/src/nested/index.js +66 -0
  52. package/src/nested/references.js +22 -0
  53. package/src/nowiki/comment.js +47 -0
  54. package/src/nowiki/dd.js +13 -0
  55. package/src/nowiki/doubleUnderscore.js +26 -0
  56. package/src/nowiki/hr.js +22 -0
  57. package/src/nowiki/index.js +34 -0
  58. package/src/nowiki/list.js +13 -0
  59. package/src/nowiki/noinclude.js +14 -0
  60. package/src/nowiki/quote.js +55 -0
  61. package/src/onlyinclude.js +39 -0
  62. package/src/paramTag/index.js +66 -0
  63. package/src/paramTag/inputbox.js +32 -0
  64. package/src/parameter.js +97 -0
  65. package/src/syntax.js +23 -0
  66. package/src/table/index.js +46 -0
  67. package/src/table/td.js +118 -0
  68. package/src/table/tr.js +74 -0
  69. package/src/tagPair/ext.js +125 -0
  70. package/src/tagPair/include.js +26 -0
  71. package/src/tagPair/index.js +77 -0
  72. package/src/transclude.js +336 -0
  73. package/util/base.js +17 -0
  74. package/util/diff.js +76 -0
  75. package/util/lint.js +55 -0
  76. package/util/string.js +75 -0
  77. package/bundle/bundle.min.js +0 -38
  78. package/extensions/editor.css +0 -62
  79. package/extensions/editor.js +0 -328
  80. package/extensions/ui.css +0 -119
package/src/index.js ADDED
@@ -0,0 +1,406 @@
1
+ 'use strict';
2
+
3
+ // PHP解析器的步骤:
4
+ // -1. 替换签名和`{{subst:}}`,参见Parser::preSaveTransform;这在revision中不可能保留,可以跳过
5
+ // 0. 移除特定字符`\0`和`\x7F`,参见Parser::parse
6
+ // 1. 注释/扩展标签('<'相关),参见Preprocessor_Hash::buildDomTreeArrayFromText和Sanitizer::decodeTagAttributes
7
+ // 2. 模板/模板变量/标题,注意rightmost法则,以及`-{`和`[[`可以破坏`{{`或`{{{`语法,
8
+ // 参见Preprocessor_Hash::buildDomTreeArrayFromText
9
+ // 3. HTML标签(允许不匹配),参见Sanitizer::internalRemoveHtmlTags
10
+ // 4. 表格,参见Parser::handleTables
11
+ // 5. 水平线、状态开关和余下的标题,参见Parser::internalParse
12
+ // 6. 内链,含文件和分类,参见Parser::handleInternalLinks2
13
+ // 7. `'`,参见Parser::doQuotes
14
+ // 8. 外链,参见Parser::handleExternalLinks
15
+ // 9. ISBN、RFC(未来将废弃,不予支持)和自由外链,参见Parser::handleMagicLinks
16
+ // 10. 段落和列表,参见BlockLevelPass::execute
17
+ // 11. 转换,参见LanguageConverter::recursiveConvertTopLevel
18
+
19
+ // \0\d+.\x7F标记Token:
20
+ // e: ExtToken
21
+ // a: AttributeToken
22
+ // c: CommentToken、NoIncludeToken和IncludeToken
23
+ // !: `{{!}}`专用
24
+ // {: `{{(!}}`专用
25
+ // }: `{{!)}}`专用
26
+ // -: `{{!-}}`专用
27
+ // +: `{{!!}}`专用
28
+ // ~: `{{=}}`专用
29
+ // s: `{{{|subst:}}}`
30
+ // m: `{{fullurl:}}`、`{{canonicalurl:}}`或`{{filepath:}}`
31
+ // t: ArgToken或TranscludeToken
32
+ // h: HeadingToken
33
+ // x: HtmlToken
34
+ // b: TableToken
35
+ // r: HrToken
36
+ // u: DoubleUnderscoreToken
37
+ // l: LinkToken
38
+ // q: QuoteToken
39
+ // w: ExtLinkToken
40
+ // d: ListToken
41
+ // v: ConverterToken
42
+
43
+ const {text} = require('../util/string'),
44
+ Parser = require('..'),
45
+ AstElement = require('../lib/element'),
46
+ AstText = require('../lib/text');
47
+ const {MAX_STAGE} = Parser;
48
+
49
+ /**
50
+ * 所有节点的基类
51
+ * @classdesc `{childNodes: ...(AstText|Token)}`
52
+ */
53
+ class Token extends AstElement {
54
+ type = 'root';
55
+ #stage = 0; // 解析阶段,参见顶部注释。只对plain Token有意义。
56
+ #config;
57
+ // 这个数组起两个作用:1. 数组中的Token会在build时替换`/\0\d+.\x7F/`标记;2. 数组中的Token会依次执行parseOnce和build方法。
58
+ #accum;
59
+ /** @type {boolean} */ #include;
60
+
61
+ /**
62
+ * 将维基语法替换为占位符
63
+ * @param {number} n 解析阶段
64
+ * @param {boolean} include 是否嵌入
65
+ */
66
+ #parseOnce = (n = this.#stage, include = false) => {
67
+ if (n < this.#stage || !this.isPlain() || this.length === 0) {
68
+ return this;
69
+ }
70
+ switch (n) {
71
+ case 0:
72
+ if (this.type === 'root') {
73
+ this.#accum.shift();
74
+ }
75
+ this.#include = Boolean(include);
76
+ this.#parseCommentAndExt(include);
77
+ break;
78
+ case 1:
79
+ this.#parseBrackets();
80
+ break;
81
+ case 2:
82
+ this.#parseHtml();
83
+ break;
84
+ case 3:
85
+ this.#parseTable();
86
+ break;
87
+ case 4:
88
+ this.#parseHrAndDoubleUndescore();
89
+ break;
90
+ case 5:
91
+ this.#parseLinks();
92
+ break;
93
+ case 6:
94
+ this.#parseQuotes();
95
+ break;
96
+
97
+ case 7:
98
+ this.#parseExternalLinks();
99
+ break;
100
+ case 8:
101
+ this.#parseMagicLinks();
102
+ break;
103
+ case 9:
104
+ this.#parseList();
105
+ break;
106
+ case 10:
107
+ this.#parseConverter();
108
+ // no default
109
+ }
110
+ if (this.type === 'root') {
111
+ for (const token of this.#accum) {
112
+ token.getAttribute('parseOnce')(n, include);
113
+ }
114
+ }
115
+ this.#stage++;
116
+ return this;
117
+ };
118
+
119
+ /**
120
+ * 重建wikitext
121
+ * @template {string} T
122
+ * @param {string} str 半解析的字符串
123
+ * @param {T} type 返回类型
124
+ * @complexity `n`
125
+ * @returns {T extends 'string|text' ? string : (Token|AstText)[]}
126
+ */
127
+ #buildFromStr = (str, type) => {
128
+ const nodes = str.split(/[\0\x7F]/u).map((s, i) => {
129
+ if (i % 2 === 0) {
130
+ return new AstText(s);
131
+ } else if (isNaN(s.at(-1))) {
132
+ return this.#accum[Number(s.slice(0, -1))];
133
+ }
134
+ throw new Error(`解析错误!未正确标记的 Token:${s}`);
135
+ });
136
+ if (type === 'string') {
137
+ return nodes.map(String).join('');
138
+ } else if (type === 'text') {
139
+ return text(nodes);
140
+ }
141
+ return nodes;
142
+ };
143
+
144
+ /**
145
+ * 将占位符替换为子Token
146
+ * @complexity `n`
147
+ */
148
+ #build = () => {
149
+ this.#stage = MAX_STAGE;
150
+ const {length, firstChild} = this,
151
+ str = String(firstChild);
152
+ if (length === 1 && firstChild.type === 'text' && str.includes('\0')) {
153
+ this.replaceChildren(...this.#buildFromStr(str));
154
+ this.normalize();
155
+ if (this.type === 'root') {
156
+ for (const token of this.#accum) {
157
+ token.getAttribute('build')();
158
+ }
159
+ }
160
+ }
161
+ };
162
+
163
+ /**
164
+ * @param {string} wikitext wikitext
165
+ * @param {accum} accum
166
+ */
167
+ constructor(wikitext, config = Parser.getConfig(), halfParsed = false, accum = [], acceptable = undefined) {
168
+ super();
169
+ if (typeof wikitext === 'string') {
170
+ this.insertAt(halfParsed ? wikitext : wikitext.replace(/[\0\x7F]/gu, ''));
171
+ }
172
+ this.#config = config;
173
+ this.#accum = accum;
174
+ accum.push(this);
175
+ }
176
+
177
+ /**
178
+ * @override
179
+ * @template {string} T
180
+ * @param {T} key 属性键
181
+ * @returns {TokenAttribute<T>}
182
+ */
183
+ getAttribute(key) {
184
+ switch (key) {
185
+ case 'config':
186
+ return structuredClone(this.#config);
187
+ case 'accum':
188
+ return this.#accum;
189
+ case 'parseOnce':
190
+ return this.#parseOnce;
191
+ case 'buildFromStr':
192
+ return this.#buildFromStr;
193
+ case 'build':
194
+ return this.#build;
195
+ case 'include': {
196
+ if (this.#include !== undefined) {
197
+ return this.#include;
198
+ }
199
+ const root = this.getRootNode();
200
+ if (root.type === 'root' && root !== this) {
201
+ return root.getAttribute('include');
202
+ }
203
+ return false;
204
+ }
205
+ default:
206
+ return super.getAttribute(key);
207
+ }
208
+ }
209
+
210
+ /**
211
+ * @override
212
+ * @template {string} T
213
+ * @param {T} key 属性键
214
+ * @param {TokenAttribute<T>} value 属性值
215
+ */
216
+ setAttribute(key, value) {
217
+ switch (key) {
218
+ case 'stage':
219
+ if (this.#stage === 0 && this.type === 'root') {
220
+ this.#accum.shift();
221
+ }
222
+ this.#stage = value;
223
+ return this;
224
+ default:
225
+ return super.setAttribute(key, value);
226
+ }
227
+ }
228
+
229
+ /** 是否是普通节点 */
230
+ isPlain() {
231
+ return this.constructor === Token;
232
+ }
233
+
234
+ /**
235
+ * @override
236
+ * @template {string|Token} T
237
+ * @param {T} token 待插入的子节点
238
+ * @param {number} i 插入位置
239
+ * @complexity `n`
240
+ * @returns {T extends Token ? Token : AstText}
241
+ */
242
+ insertAt(token, i = this.length) {
243
+ if (typeof token === 'string') {
244
+ token = new AstText(token);
245
+ }
246
+ super.insertAt(token, i);
247
+ if (token.type === 'root') {
248
+ token.type = 'plain';
249
+ }
250
+ return token;
251
+ }
252
+
253
+ /**
254
+ * 规范化页面标题
255
+ * @param {string} title 标题(含或不含命名空间前缀)
256
+ * @param {number} defaultNs 命名空间
257
+ * @param {boolean} decode 是否需要解码
258
+ * @param {boolean} selfLink 是否允许selfLink
259
+ */
260
+ normalizeTitle(title, defaultNs = 0, halfParsed = false, decode = false, selfLink = false) {
261
+ return Parser.normalizeTitle(title, defaultNs, this.#include, this.#config, halfParsed, decode, selfLink);
262
+ }
263
+
264
+ /** 生成部分Token的`name`属性 */
265
+ afterBuild() {
266
+ if (this.type === 'root') {
267
+ for (const token of this.#accum) {
268
+ token.afterBuild();
269
+ }
270
+ }
271
+ }
272
+
273
+ /**
274
+ * 解析、重构、生成部分Token的`name`属性
275
+ * @param {number} n 最大解析层级
276
+ * @param {boolean} include 是否嵌入
277
+ */
278
+ parse(n = MAX_STAGE, include = false) {
279
+ while (this.#stage < n) {
280
+ this.#parseOnce(this.#stage, include);
281
+ }
282
+ if (n) {
283
+ this.#build();
284
+ this.afterBuild();
285
+ }
286
+ return this;
287
+ }
288
+
289
+ /**
290
+ * 解析HTML注释和扩展标签
291
+ * @param {boolean} includeOnly 是否嵌入
292
+ */
293
+ #parseCommentAndExt(includeOnly) {
294
+ const parseCommentAndExt = require('../parser/commentAndExt');
295
+ this.setText(parseCommentAndExt(String(this.firstChild), this.#config, this.#accum, includeOnly));
296
+ }
297
+
298
+ /** 解析花括号 */
299
+ #parseBrackets() {
300
+ const parseBrackets = require('../parser/brackets');
301
+ const str = this.type === 'root' ? String(this.firstChild) : `\0${String(this.firstChild)}`,
302
+ parsed = parseBrackets(str, this.#config, this.#accum);
303
+ this.setText(this.type === 'root' ? parsed : parsed.slice(1));
304
+ }
305
+
306
+ /** 解析HTML标签 */
307
+ #parseHtml() {
308
+ if (this.#config.excludes.includes('html')) {
309
+ return;
310
+ }
311
+ const parseHtml = require('../parser/html');
312
+ this.setText(parseHtml(String(this.firstChild), this.#config, this.#accum));
313
+ }
314
+
315
+ /** 解析表格 */
316
+ #parseTable() {
317
+ if (this.#config.excludes.includes('table')) {
318
+ return;
319
+ }
320
+ const parseTable = require('../parser/table'),
321
+ TableToken = require('./table');
322
+ this.setText(parseTable(this, this.#config, this.#accum));
323
+ for (const table of this.#accum) {
324
+ if (table instanceof TableToken && table.type !== 'td') {
325
+ table.normalize();
326
+ const {childNodes: [, child]} = table;
327
+ if (typeof child === 'string' && child.includes('\0')) {
328
+ table.removeAt(1);
329
+ const inner = new Token(child, this.#config, true, this.#accum);
330
+ table.insertAt(inner, 1);
331
+ inner.setAttribute('stage', 4);
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ /** 解析\<hr\>和状态开关 */
338
+ #parseHrAndDoubleUndescore() {
339
+ if (this.#config.excludes.includes('hr')) {
340
+ return;
341
+ }
342
+ const parseHrAndDoubleUnderscore = require('../parser/hrAndDoubleUnderscore');
343
+ this.setText(parseHrAndDoubleUnderscore(this, this.#config, this.#accum));
344
+ }
345
+
346
+ /** 解析内部链接 */
347
+ #parseLinks() {
348
+ const parseLinks = require('../parser/links');
349
+ this.setText(parseLinks(String(this.firstChild), this.#config, this.#accum));
350
+ }
351
+
352
+ /** 解析单引号 */
353
+ #parseQuotes() {
354
+ if (this.#config.excludes.includes('quote')) {
355
+ return;
356
+ }
357
+ const parseQuotes = require('../parser/quotes');
358
+ const lines = String(this.firstChild).split('\n');
359
+ for (let i = 0; i < lines.length; i++) {
360
+ lines[i] = parseQuotes(lines[i], this.#config, this.#accum);
361
+ }
362
+ this.setText(lines.join('\n'));
363
+ }
364
+
365
+ /** 解析外部链接 */
366
+ #parseExternalLinks() {
367
+ if (this.#config.excludes.includes('extLink')) {
368
+ return;
369
+ }
370
+ const parseExternalLinks = require('../parser/externalLinks');
371
+ this.setText(parseExternalLinks(String(this.firstChild), this.#config, this.#accum));
372
+ }
373
+
374
+ /** 解析自由外链 */
375
+ #parseMagicLinks() {
376
+ if (this.#config.excludes.includes('magicLink')) {
377
+ return;
378
+ }
379
+ const parseMagicLinks = require('../parser/magicLinks');
380
+ this.setText(parseMagicLinks(String(this.firstChild), this.#config, this.#accum));
381
+ }
382
+
383
+ /** 解析列表 */
384
+ #parseList() {
385
+ if (this.#config.excludes.includes('list')) {
386
+ return;
387
+ }
388
+ const parseList = require('../parser/list');
389
+ const lines = String(this.firstChild).split('\n');
390
+ let i = this.type === 'root' || this.type === 'ext-inner' && this.name === 'poem' ? 0 : 1;
391
+ for (; i < lines.length; i++) {
392
+ lines[i] = parseList(lines[i], this.#config, this.#accum);
393
+ }
394
+ this.setText(lines.join('\n'));
395
+ }
396
+
397
+ /** 解析语言变体转换 */
398
+ #parseConverter() {
399
+ if (this.#config.variants?.length > 0) {
400
+ const parseConverter = require('../parser/converter');
401
+ this.setText(parseConverter(String(this.firstChild), this.#config, this.#accum));
402
+ }
403
+ }
404
+ }
405
+
406
+ module.exports = Token;
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ const LinkToken = require('.');
4
+
5
+ /**
6
+ * 分类
7
+ * @classdesc `{childNodes: [AtomToken, ?Token]}`
8
+ */
9
+ class CategoryToken extends LinkToken {
10
+ type = 'category';
11
+ }
12
+
13
+ module.exports = CategoryToken;
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ const {explode} = require('../../util/string'),
4
+ {generateForChild} = require('../../util/lint'),
5
+ Parser = require('../..'),
6
+ LinkToken = require('.'),
7
+ ImageParameterToken = require('../imageParameter');
8
+
9
+ const frame = new Set(['manualthumb', 'frameless', 'framed', 'thumbnail']),
10
+ horizAlign = new Set(['left', 'right', 'center', 'none']),
11
+ vertAlign = new Set(['baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom']);
12
+
13
+ /**
14
+ * 图片
15
+ * @classdesc `{childNodes: [AtomToken, ...ImageParameterToken]}`
16
+ */
17
+ class FileToken extends LinkToken {
18
+ type = 'file';
19
+
20
+ /**
21
+ * @param {string} link 文件名
22
+ * @param {string|undefined} text 图片参数
23
+ * @param {accum} accum
24
+ * @param {string} delimiter `|`
25
+ * @complexity `n`
26
+ */
27
+ constructor(link, text, config = Parser.getConfig(), accum = [], delimiter = '|') {
28
+ super(link, undefined, config, accum, delimiter);
29
+ this.append(...explode('-{', '}-', '|', text).map(part => new ImageParameterToken(part, config, accum)));
30
+ }
31
+
32
+ /**
33
+ * @override
34
+ * @param {number} start 起始位置
35
+ */
36
+ lint(start = this.getAbsoluteIndex()) {
37
+ const errors = super.lint(start),
38
+ args = this.getAllArgs().filter(({childNodes}) => {
39
+ const visibleNodes = childNodes.filter(node => node.text().trim());
40
+ return visibleNodes.length !== 1 || visibleNodes[0].type !== 'arg';
41
+ }),
42
+ keys = [...new Set(args.map(({name}) => name))].filter(key => key !== 'invalid'),
43
+ frameKeys = keys.filter(key => frame.has(key)),
44
+ horizAlignKeys = keys.filter(key => horizAlign.has(key)),
45
+ vertAlignKeys = keys.filter(key => vertAlign.has(key));
46
+ if (args.length === keys.length
47
+ && frameKeys.length < 2 && horizAlignKeys.length < 2 && vertAlignKeys.length < 2
48
+ ) {
49
+ return errors;
50
+ }
51
+ const rect = {start, ...this.getRootNode().posFromIndex(start)};
52
+ for (const key of keys) {
53
+ let relevantArgs = args.filter(({name}) => name === key);
54
+ if (key === 'caption') {
55
+ relevantArgs = [
56
+ ...relevantArgs.slice(0, -1).filter(arg => arg.text()),
57
+ relevantArgs.at(-1),
58
+ ];
59
+ }
60
+ if (relevantArgs.length > 1) {
61
+ errors.push(...relevantArgs.map(
62
+ arg => generateForChild(arg, rect, Parser.msg('duplicated image $1 parameter', key)),
63
+ ));
64
+ }
65
+ }
66
+ if (frameKeys.size > 1) {
67
+ errors.push(
68
+ ...args.filter(({name}) => frame.has(name)).map(
69
+ arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'frame'),
70
+ ),
71
+ );
72
+ }
73
+ if (horizAlignKeys.size > 1) {
74
+ errors.push(
75
+ ...args.filter(({name}) => horizAlign.has(name))
76
+ .map(arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'horizontal-alignment')),
77
+ );
78
+ }
79
+ if (vertAlignKeys.size > 1) {
80
+ errors.push(
81
+ ...args.filter(({name}) => vertAlign.has(name))
82
+ .map(arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'vertical-alignment')),
83
+ );
84
+ }
85
+ return errors;
86
+ }
87
+
88
+ /**
89
+ * 获取所有图片参数节点
90
+ * @returns {ImageParameterToken[]}
91
+ */
92
+ getAllArgs() {
93
+ return this.childNodes.slice(1);
94
+ }
95
+
96
+ /**
97
+ * 获取指定图片参数
98
+ * @param {string} key 参数名
99
+ * @complexity `n`
100
+ */
101
+ getArgs(key) {
102
+ return this.getAllArgs().filter(({name}) => key === name);
103
+ }
104
+
105
+ /**
106
+ * 获取特定类型的图片属性参数节点
107
+ * @param {Set<string>} keys 接受的参数名
108
+ * @param {type} type 类型名
109
+ * @complexity `n`
110
+ */
111
+ #getTypedArgs(keys, type) {
112
+ const args = this.getAllArgs().filter(({name}) => keys.has(name));
113
+ return args;
114
+ }
115
+
116
+ /** 获取图片框架属性参数节点 */
117
+ getFrameArgs() {
118
+ return this.#getTypedArgs(frame, '框架');
119
+ }
120
+
121
+ /** 获取图片水平对齐参数节点 */
122
+ getHorizAlignArgs() {
123
+ return this.#getTypedArgs(horizAlign, '水平对齐');
124
+ }
125
+
126
+ /** 获取图片垂直对齐参数节点 */
127
+ getVertAlignArgs() {
128
+ return this.#getTypedArgs(vertAlign, '垂直对齐');
129
+ }
130
+ }
131
+
132
+ module.exports = FileToken;
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ const {generateForSelf} = require('../../util/lint'),
4
+ Parser = require('../..'),
5
+ Token = require('..'),
6
+ FileToken = require('./file');
7
+
8
+ /**
9
+ * 图片
10
+ * @classdesc `{childNodes: [AtomToken, ...ImageParameterToken]}`
11
+ */
12
+ class GalleryImageToken extends FileToken {
13
+ type = 'gallery-image';
14
+ #invalid = false;
15
+
16
+ /**
17
+ * @param {string} link 图片文件名
18
+ * @param {string|undefined} text 图片参数
19
+ * @param {accum} accum
20
+ */
21
+ constructor(link, text, config = Parser.getConfig(), accum = []) {
22
+ let token;
23
+ if (text !== undefined) {
24
+ token = new Token(text, config, true, accum);
25
+ token.type = 'temp';
26
+ for (let n = 1; n < Parser.MAX_STAGE; n++) {
27
+ token.getAttribute('parseOnce')();
28
+ }
29
+ accum.splice(accum.indexOf(token), 1);
30
+ }
31
+ super(link, token?.toString(), config, accum);
32
+ this.setAttribute('bracket', false);
33
+ }
34
+
35
+ /**
36
+ * @override
37
+ */
38
+ afterBuild() {
39
+ const initImagemap = this.type === 'imagemap-image',
40
+ titleObj = this.normalizeTitle(String(this.firstChild), initImagemap ? 0 : 6, true, !initImagemap);
41
+ this.#invalid = titleObj.ns !== 6; // 只用于gallery-image的首次解析
42
+ }
43
+
44
+ /** @override */
45
+ getPadding() {
46
+ return 0;
47
+ }
48
+
49
+ /**
50
+ * @override
51
+ * @param {number} start 起始位置
52
+ */
53
+ lint(start = this.getAbsoluteIndex()) {
54
+ const errors = super.lint(start);
55
+ if (this.#invalid) {
56
+ errors.push(generateForSelf(this, {start}, 'invalid gallery image'));
57
+ }
58
+ return errors;
59
+ }
60
+ }
61
+
62
+ module.exports = GalleryImageToken;