wikiparser-node 0.10.0 → 0.11.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 (155) hide show
  1. package/README.md +44 -32
  2. package/config/.schema.json +1 -8
  3. package/config/default.json +1 -2
  4. package/config/llwiki.json +1 -36
  5. package/config/moegirl.json +1 -45
  6. package/config/zhwiki.json +1 -467
  7. package/dist/index.d.ts +110 -0
  8. package/dist/lib/element.d.ts +48 -0
  9. package/dist/lib/node.d.ts +108 -0
  10. package/dist/lib/text.d.ts +30 -0
  11. package/dist/lib/title.d.ts +15 -0
  12. package/dist/mixin/hidden.d.ts +8 -0
  13. package/dist/parser/brackets.d.ts +12 -0
  14. package/dist/parser/commentAndExt.d.ts +8 -0
  15. package/dist/parser/converter.d.ts +7 -0
  16. package/dist/parser/externalLinks.d.ts +7 -0
  17. package/dist/parser/hrAndDoubleUnderscore.d.ts +11 -0
  18. package/dist/parser/html.d.ts +7 -0
  19. package/dist/parser/links.d.ts +7 -0
  20. package/dist/parser/list.d.ts +7 -0
  21. package/dist/parser/magicLinks.d.ts +7 -0
  22. package/dist/parser/quotes.d.ts +7 -0
  23. package/dist/parser/table.d.ts +11 -0
  24. package/dist/src/arg.d.ts +25 -0
  25. package/dist/src/atom/hidden.d.ts +5 -0
  26. package/dist/src/atom/index.d.ts +14 -0
  27. package/dist/src/attribute.d.ts +42 -0
  28. package/dist/src/attributes.d.ts +33 -0
  29. package/dist/src/converter.d.ts +29 -0
  30. package/dist/src/converterFlags.d.ts +27 -0
  31. package/dist/src/converterRule.d.ts +29 -0
  32. package/dist/src/extLink.d.ts +22 -0
  33. package/dist/src/gallery.d.ts +19 -0
  34. package/dist/src/hasNowiki/index.d.ts +14 -0
  35. package/dist/src/hasNowiki/pre.d.ts +13 -0
  36. package/dist/src/heading.d.ts +24 -0
  37. package/dist/src/html.d.ts +33 -0
  38. package/dist/src/imageParameter.d.ts +23 -0
  39. package/dist/src/imagemap.d.ts +26 -0
  40. package/dist/src/imagemapLink.d.ts +16 -0
  41. package/dist/src/index.d.ts +53 -0
  42. package/dist/src/link/category.d.ts +8 -0
  43. package/dist/src/link/file.d.ts +27 -0
  44. package/dist/src/link/galleryImage.d.ts +15 -0
  45. package/dist/src/link/index.d.ts +29 -0
  46. package/dist/src/magicLink.d.ts +14 -0
  47. package/dist/src/nested/choose.d.ts +13 -0
  48. package/dist/src/nested/combobox.d.ts +13 -0
  49. package/dist/src/nested/index.d.ts +20 -0
  50. package/dist/src/nested/references.d.ts +13 -0
  51. package/dist/src/nowiki/comment.d.ts +19 -0
  52. package/dist/src/nowiki/dd.d.ts +8 -0
  53. package/dist/src/nowiki/doubleUnderscore.d.ts +12 -0
  54. package/dist/src/nowiki/hr.d.ts +13 -0
  55. package/dist/src/nowiki/index.d.ts +18 -0
  56. package/dist/src/nowiki/list.d.ts +8 -0
  57. package/dist/src/nowiki/noinclude.d.ts +8 -0
  58. package/dist/src/nowiki/quote.d.ts +20 -0
  59. package/dist/src/onlyinclude.d.ts +17 -0
  60. package/dist/src/paramTag/index.d.ts +24 -0
  61. package/dist/src/paramTag/inputbox.d.ts +8 -0
  62. package/dist/src/parameter.d.ts +28 -0
  63. package/dist/src/syntax.d.ts +15 -0
  64. package/dist/src/table/index.d.ts +16 -0
  65. package/dist/src/table/td.d.ts +39 -0
  66. package/dist/src/table/tr.d.ts +17 -0
  67. package/dist/src/tagPair/ext.d.ts +17 -0
  68. package/dist/src/tagPair/include.d.ts +16 -0
  69. package/dist/src/tagPair/index.d.ts +28 -0
  70. package/dist/src/transclude.d.ts +75 -0
  71. package/dist/util/lint.d.ts +28 -0
  72. package/dist/util/string.d.ts +31 -0
  73. package/i18n/zh-hans.json +1 -1
  74. package/i18n/zh-hant.json +1 -1
  75. package/index.js +5 -257
  76. package/lib/element.js +7 -482
  77. package/lib/node.js +11 -540
  78. package/lib/text.js +3 -96
  79. package/lib/title.js +1 -28
  80. package/mixin/hidden.js +0 -3
  81. package/package.json +11 -5
  82. package/parser/brackets.js +3 -2
  83. package/parser/commentAndExt.js +8 -6
  84. package/parser/converter.js +1 -2
  85. package/parser/externalLinks.js +1 -2
  86. package/parser/hrAndDoubleUnderscore.js +1 -2
  87. package/parser/html.js +1 -2
  88. package/parser/links.js +5 -6
  89. package/parser/list.js +1 -2
  90. package/parser/magicLinks.js +1 -2
  91. package/parser/quotes.js +1 -2
  92. package/parser/table.js +1 -2
  93. package/src/arg.js +4 -118
  94. package/src/atom/hidden.js +0 -2
  95. package/src/atom/index.js +1 -18
  96. package/src/attribute.js +6 -190
  97. package/src/attributes.js +5 -308
  98. package/src/converter.js +3 -109
  99. package/src/converterFlags.js +1 -188
  100. package/src/converterRule.js +2 -184
  101. package/src/extLink.js +2 -122
  102. package/src/gallery.js +2 -56
  103. package/src/hasNowiki/index.js +1 -13
  104. package/src/hasNowiki/pre.js +1 -13
  105. package/src/heading.js +4 -55
  106. package/src/html.js +5 -120
  107. package/src/imageParameter.js +3 -165
  108. package/src/imagemap.js +3 -62
  109. package/src/imagemapLink.js +2 -14
  110. package/src/index.js +9 -530
  111. package/src/link/category.js +1 -32
  112. package/src/link/file.js +3 -158
  113. package/src/link/galleryImage.js +3 -61
  114. package/src/link/index.js +4 -273
  115. package/src/magicLink.js +6 -87
  116. package/src/nested/choose.js +1 -2
  117. package/src/nested/combobox.js +1 -2
  118. package/src/nested/index.js +8 -32
  119. package/src/nested/references.js +1 -2
  120. package/src/nowiki/comment.js +2 -26
  121. package/src/nowiki/dd.js +1 -47
  122. package/src/nowiki/doubleUnderscore.js +1 -31
  123. package/src/nowiki/hr.js +2 -21
  124. package/src/nowiki/index.js +2 -24
  125. package/src/nowiki/list.js +2 -5
  126. package/src/nowiki/noinclude.js +0 -14
  127. package/src/nowiki/quote.js +2 -16
  128. package/src/onlyinclude.js +2 -27
  129. package/src/paramTag/index.js +2 -25
  130. package/src/paramTag/inputbox.js +2 -5
  131. package/src/parameter.js +6 -148
  132. package/src/syntax.js +1 -69
  133. package/src/table/index.js +2 -939
  134. package/src/table/td.js +6 -226
  135. package/src/table/tr.js +3 -248
  136. package/src/tagPair/ext.js +4 -23
  137. package/src/tagPair/include.js +1 -25
  138. package/src/tagPair/index.js +3 -52
  139. package/src/transclude.js +6 -513
  140. package/typings/api.d.ts +9 -0
  141. package/typings/index.d.ts +51 -0
  142. package/typings/node.d.ts +16 -0
  143. package/typings/parser.d.ts +5 -0
  144. package/typings/token.d.ts +28 -0
  145. package/util/lint.js +2 -0
  146. package/util/string.js +0 -51
  147. package/lib/ranges.js +0 -130
  148. package/mixin/attributeParent.js +0 -117
  149. package/mixin/fixedToken.js +0 -40
  150. package/mixin/singleLine.js +0 -31
  151. package/mixin/sol.js +0 -54
  152. package/parser/selector.js +0 -177
  153. package/src/charinsert.js +0 -97
  154. package/tool/index.js +0 -1202
  155. package/util/debug.js +0 -73
package/src/attribute.js CHANGED
@@ -1,16 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ /** @typedef {import('../typings/token').ParserConfig} ParserConfig */
4
+
3
5
  const {generateForChild} = require('../util/lint'),
4
- {noWrap, removeComment} = require('../util/string'),
5
- fixedToken = require('../mixin/fixedToken'),
6
+ {removeComment} = require('../util/string'),
6
7
  Parser = require('..'),
7
8
  Token = require('.'),
8
9
  AtomToken = require('./atom');
9
10
 
10
- const stages = {'ext-attr': 0, 'html-attr': 2, 'table-attr': 3},
11
- pre = {'ext-attr': '<pre ', 'html-attr': '<p ', 'table-attr': '{|'},
12
- post = {'ext-attr': '/>', 'html-attr': '>', 'table-attr': ''},
13
- commonHtmlAttrs = new Set([
11
+ const commonHtmlAttrs = new Set([
14
12
  'id',
15
13
  'class',
16
14
  'style',
@@ -109,37 +107,6 @@ const stages = {'ext-attr': 0, 'html-attr': 2, 'table-attr': 3},
109
107
  poll: new Set(['id', 'show-results-before-voting']),
110
108
  sm2: typeAttrs,
111
109
  flashmp3: typeAttrs,
112
- score: new Set([
113
- 'line_width_inches',
114
- 'lang',
115
- 'override_midi',
116
- 'raw',
117
- 'note-language',
118
- 'override_audio',
119
- 'override_ogg',
120
- 'sound',
121
- 'vorbis',
122
- ]),
123
- seo: new Set([
124
- 'title',
125
- 'title_mode',
126
- 'title_separator',
127
- 'keywords',
128
- 'description',
129
- 'robots',
130
- 'google_bot',
131
- 'image',
132
- 'image_width',
133
- 'image_height',
134
- 'image_alt',
135
- 'type',
136
- 'site_name',
137
- 'locale',
138
- 'section',
139
- 'author',
140
- 'published_time',
141
- 'twitter_site',
142
- ]),
143
110
  tab: new Set([
144
111
  'nested',
145
112
  'name',
@@ -177,7 +144,7 @@ const stages = {'ext-attr': 0, 'html-attr': 2, 'table-attr': 3},
177
144
  * 扩展和HTML标签属性
178
145
  * @classdesc `{childNodes: [AtomToken, Token|AtomToken]}`
179
146
  */
180
- class AttributeToken extends fixedToken(Token) {
147
+ class AttributeToken extends Token {
181
148
  #equal;
182
149
  #quotes;
183
150
  #tag;
@@ -192,10 +159,6 @@ class AttributeToken extends fixedToken(Token) {
192
159
  return this.getValue();
193
160
  }
194
161
 
195
- set value(value) {
196
- this.setValue(value);
197
- }
198
-
199
162
  /** 标签名 */
200
163
  get tag() {
201
164
  return this.#tag;
@@ -208,40 +171,27 @@ class AttributeToken extends fixedToken(Token) {
208
171
  * @param {string} equal 等号
209
172
  * @param {string} value 属性值
210
173
  * @param {string[]} quotes 引号
211
- * @param {accum} accum
174
+ * @param {import('../typings/token').accum} accum
212
175
  */
213
176
  constructor(type, tag, key, equal = '', value = '', quotes = [], config = Parser.getConfig(), accum = []) {
214
177
  const keyToken = new AtomToken(key, 'attr-key', config, accum, {
215
- [type === 'ext-attr' ? 'AstText' : 'Stage-1']: ':', ArgToken: ':', TranscludeToken: ':',
216
178
  });
217
179
  let valueToken;
218
180
  if (key === 'title') {
219
181
  valueToken = new Token(value, config, true, accum, {
220
- [`Stage-${stages[type]}`]: ':', ConverterToken: ':',
221
182
  }).setAttribute('type', 'attr-value').setAttribute('stage', Parser.MAX_STAGE - 1);
222
183
  } else if (tag === 'gallery' && key === 'caption') {
223
184
  /** @type {ParserConfig} */
224
185
  const newConfig = {...config, excludes: [...config.excludes, 'quote', 'extLink', 'magicLink', 'list']};
225
186
  valueToken = new Token(value, newConfig, true, accum, {
226
- AstText: ':', LinkToken: ':', FileToken: ':', CategoryToken: ':', ConverterToken: ':',
227
187
  }).setAttribute('type', 'attr-value').setAttribute('stage', 5);
228
188
  } else if (tag === 'choose' && (key === 'before' || key === 'after')) {
229
189
  /** @type {ParserConfig} */
230
190
  const newConfig = {...config, excludes: [...config.excludes, 'heading', 'html', 'table', 'hr', 'list']};
231
191
  valueToken = new Token(value, newConfig, true, accum, {
232
- ArgToken: ':',
233
- TranscludeToken: ':',
234
- LinkToken: ':',
235
- FileToken: ':',
236
- CategoryToken: ':',
237
- QuoteToken: ':',
238
- ExtLinkToken: ':',
239
- MagicLinkToken: ':',
240
- ConverterToken: ':',
241
192
  }).setAttribute('type', 'attr-value').setAttribute('stage', 1);
242
193
  } else {
243
194
  valueToken = new AtomToken(value, 'attr-value', config, accum, {
244
- [`Stage-${stages[type]}`]: ':',
245
195
  });
246
196
  }
247
197
  super(undefined, config, true, accum);
@@ -266,13 +216,9 @@ class AttributeToken extends fixedToken(Token) {
266
216
 
267
217
  /**
268
218
  * @override
269
- * @param {string} selector
270
219
  * @returns {string}
271
220
  */
272
221
  toString(selector) {
273
- if (selector && this.matches(selector)) {
274
- return '';
275
- }
276
222
  const [quoteStart = '', quoteEnd = ''] = this.#quotes;
277
223
  return this.#equal
278
224
  ? `${super.toString(selector, `${this.#equal}${quoteStart}`)}${quoteEnd}`
@@ -292,12 +238,6 @@ class AttributeToken extends fixedToken(Token) {
292
238
  return this.#equal ? this.#equal.length + (this.#quotes[0]?.length ?? 0) : 0;
293
239
  }
294
240
 
295
- /** @override */
296
- print() {
297
- const [quoteStart = '', quoteEnd = ''] = this.#quotes;
298
- return this.#equal ? super.print({sep: `${this.#equal}${quoteStart}`, post: quoteEnd}) : super.print();
299
- }
300
-
301
241
  /**
302
242
  * @override
303
243
  * @param {number} start 起始位置
@@ -341,130 +281,6 @@ class AttributeToken extends fixedToken(Token) {
341
281
  }
342
282
  return true;
343
283
  }
344
-
345
- /**
346
- * @override
347
- * @template {string} T
348
- * @param {T} key 属性键
349
- * @returns {TokenAttribute<T>}
350
- */
351
- getAttribute(key) {
352
- if (key === 'equal') {
353
- return this.#equal;
354
- }
355
- return key === 'quotes' ? this.#quotes : super.getAttribute(key);
356
- }
357
-
358
- /**
359
- * @override
360
- * @param {PropertyKey} key 属性键
361
- */
362
- hasAttribute(key) {
363
- return key === 'equal' || key === 'quotes' || super.hasAttribute(key);
364
- }
365
-
366
- /** @override */
367
- cloneNode() {
368
- const [key, value] = this.cloneChildNodes(),
369
- config = this.getAttribute('config');
370
- return Parser.run(() => {
371
- const token = new AttributeToken(this.type, this.#tag, '', this.#equal, '', this.#quotes, config);
372
- token.firstChild.safeReplaceWith(key);
373
- token.lastChild.safeReplaceWith(value);
374
- token.afterBuild();
375
- return token;
376
- });
377
- }
378
-
379
- /** 转义等号 */
380
- escape() {
381
- this.#equal = '{{=}}';
382
- }
383
-
384
- /** 闭合引号 */
385
- close() {
386
- [this.#quotes[1]] = this.#quotes;
387
- }
388
-
389
- /**
390
- * 设置属性值
391
- * @param {string|boolean} value 参数值
392
- * @throws `SyntaxError` 非法的标签属性
393
- */
394
- setValue(value) {
395
- if (value === false) {
396
- this.remove();
397
- return;
398
- } else if (value === true) {
399
- this.#equal = '';
400
- return;
401
- }
402
- value = String(value);
403
- const {type} = this,
404
- key = this.name === 'title' ? 'title' : 'data',
405
- wikitext = `${pre[type]}${key}="${value}"${post[type]}`,
406
- root = Parser.parse(wikitext, this.getAttribute('include'), stages[type] + 1, this.getAttribute('config')),
407
- {length, firstChild: tag} = root;
408
- let attrs;
409
- if (length !== 1 || tag.type !== type.slice(0, -5)) {
410
- throw new SyntaxError(`非法的标签属性:${noWrap(value)}`);
411
- } else if (type === 'table-attr') {
412
- if (tag.length !== 2) {
413
- throw new SyntaxError(`非法的标签属性:${noWrap(value)}`);
414
- }
415
- attrs = tag.lastChild;
416
- } else {
417
- attrs = tag.firstChild;
418
- }
419
- const {length: attrsLength, firstChild} = attrs;
420
- if (attrsLength !== 1 || firstChild.type !== this.type || firstChild.name !== key) {
421
- throw new SyntaxError(`非法的标签属性:${noWrap(value)}`);
422
- }
423
- const {lastChild} = firstChild;
424
- firstChild.destroy(true);
425
- this.lastChild.safeReplaceWith(lastChild);
426
- if (this.#quotes[0]) {
427
- this.close();
428
- } else {
429
- this.#quotes = ['"', '"'];
430
- }
431
- }
432
-
433
- /**
434
- * 修改属性名
435
- * @param {string} key 新属性名
436
- * @throws `Error` title属性不能更名
437
- * @throws `SyntaxError` 非法的模板参数名
438
- */
439
- rename(key) {
440
- if (this.name === 'title') {
441
- throw new Error('title 属性不能更名!');
442
- }
443
- key = String(key);
444
- const {type} = this,
445
- wikitext = `${pre[type]}${key}${post[type]}`,
446
- root = Parser.parse(wikitext, this.getAttribute('include'), stages[type] + 1, this.getAttribute('config')),
447
- {length, firstChild: tag} = root;
448
- let attrs;
449
- if (length !== 1 || tag.type !== type.slice(0, -5)) {
450
- throw new SyntaxError(`非法的标签属性名:${noWrap(key)}`);
451
- } else if (type === 'table-attr') {
452
- if (tag.length !== 2) {
453
- throw new SyntaxError(`非法的标签属性名:${noWrap(key)}`);
454
- }
455
- attrs = tag.lastChild;
456
- } else {
457
- attrs = tag.firstChild;
458
- }
459
- const {length: attrsLength, firstChild: attr} = attrs;
460
- if (attrsLength !== 1 || attr.type !== this.type || attr.value !== true) {
461
- throw new SyntaxError(`非法的标签属性名:${noWrap(key)}`);
462
- }
463
- const {firstChild} = attr;
464
- attr.destroy(true);
465
- this.firstChild.safeReplaceWith(firstChild);
466
- }
467
284
  }
468
285
 
469
- Parser.classes.AttributeToken = __filename;
470
286
  module.exports = AttributeToken;
package/src/attributes.js CHANGED
@@ -1,97 +1,25 @@
1
1
  'use strict';
2
2
 
3
3
  const {generateForSelf, generateForChild} = require('../util/lint'),
4
- {toCase, normalizeSpace, text, removeComment} = require('../util/string'),
4
+ {removeComment} = require('../util/string'),
5
5
  Parser = require('..'),
6
6
  Token = require('.'),
7
7
  AtomToken = require('./atom'),
8
8
  AttributeToken = require('./attribute');
9
9
 
10
- const stages = {'ext-attrs': 0, 'html-attrs': 2, 'table-attrs': 3};
11
-
12
10
  /**
13
11
  * 扩展和HTML标签属性
14
12
  * @classdesc `{childNodes: ...AtomToken|AttributeToken}`
15
13
  */
16
14
  class AttributesToken extends Token {
17
- /**
18
- * @override
19
- * @param {string} key 属性键
20
- * @param {string|undefined} equal 属性规则运算符,`equal`存在时`val`和`i`也一定存在
21
- * @param {string|undefined} val 属性值
22
- * @param {string|undefined} i 是否对大小写不敏感
23
- */
24
- #matchesAttr = (key, equal, val, i) => {
25
- if (!equal) {
26
- return this.hasAttr(key);
27
- } else if (!this.hasAttr(key)) {
28
- return equal === '!=';
29
- }
30
- val = toCase(val, i);
31
- const attr = this.getAttr(key),
32
- thisVal = toCase(attr === true ? '' : attr, i);
33
- switch (equal) {
34
- case '~=':
35
- return attr !== true && thisVal.split(/\s/u).includes(val);
36
- case '|=': // 允许`val === ''`
37
- return thisVal === val || thisVal.startsWith(`${val}-`);
38
- case '^=':
39
- return attr !== true && thisVal.startsWith(val);
40
- case '$=':
41
- return attr !== true && thisVal.endsWith(val);
42
- case '*=':
43
- return attr !== true && thisVal.includes(val);
44
- case '!=':
45
- return thisVal !== val;
46
- default: // `=`
47
- return thisVal === val;
48
- }
49
- };
50
-
51
- /** getAttrs()方法的getter写法 */
52
- get attributes() {
53
- return this.getAttrs();
54
- }
55
-
56
- /** 以字符串表示的class属性 */
57
- get className() {
58
- const attr = this.getAttr('class');
59
- return typeof attr === 'string' ? attr : '';
60
- }
61
-
62
- set className(className) {
63
- this.setAttr('class', className);
64
- }
65
-
66
- /** 以Set表示的class属性 */
67
- get classList() {
68
- return new Set(this.className.split(/\s/u));
69
- }
70
-
71
- /** id属性 */
72
- get id() {
73
- const attr = this.getAttr('id');
74
- return typeof attr === 'string' ? attr : '';
75
- }
76
-
77
- set id(id) {
78
- this.setAttr('id', id);
79
- }
80
-
81
- /** 是否含有无效属性 */
82
- get sanitized() {
83
- return this.getDirtyAttrs().length === 0;
84
- }
85
-
86
15
  /**
87
16
  * @param {string} attr 标签属性
88
17
  * @param {'ext-attrs'|'html-attrs'|'table-attrs'} type 标签类型
89
18
  * @param {string} name 标签名
90
- * @param {accum} accum
19
+ * @param {import('../typings/token').accum} accum
91
20
  */
92
21
  constructor(attr, type, name, config = Parser.getConfig(), accum = []) {
93
22
  super(undefined, config, true, accum, {
94
- AtomToken: ':', AttributeToken: ':',
95
23
  });
96
24
  this.type = type;
97
25
  this.setAttribute('name', name);
@@ -112,7 +40,6 @@ class AttributesToken extends Token {
112
40
  const insertDirty = /** 插入无效属性 */ () => {
113
41
  if (out) {
114
42
  super.insertAt(new AtomToken(out, `${type.slice(0, -1)}-dirty`, config, accum, {
115
- [`Stage-${stages[type]}`]: ':',
116
43
  }));
117
44
  out = '';
118
45
  }
@@ -154,11 +81,9 @@ class AttributesToken extends Token {
154
81
  * @returns {AttributeToken[]}
155
82
  */
156
83
  getAttrTokens(key) {
157
- return typeof key === 'string'
158
- ? this.childNodes.filter(
159
- child => child instanceof AttributeToken && child.name === key.toLowerCase().trim(),
160
- )
161
- : this.typeError('getAttrTokens', 'String');
84
+ return this.childNodes.filter(
85
+ child => child instanceof AttributeToken && child.name === key.toLowerCase().trim(),
86
+ );
162
87
  }
163
88
 
164
89
  /**
@@ -220,234 +145,6 @@ class AttributesToken extends Token {
220
145
  }
221
146
  return errors;
222
147
  }
223
-
224
- /**
225
- * @override
226
- * @this {AttributesToken & {parentNode: HtmlToken}}
227
- */
228
- print() {
229
- const HtmlToken = require('./html');
230
- return String(this)
231
- ? `<span class="wpb-${this.type}">${this.childNodes.map(child => child.print({
232
- class: child instanceof AtomToken && child.text().trim() && 'hidden',
233
- })).join('')}</span>`
234
- : '';
235
- }
236
-
237
- /** 清理标签属性 */
238
- sanitize() {
239
- let dirty = false;
240
- for (let i = this.length - 1; i >= 0; i--) {
241
- const child = this.childNodes[i];
242
- if (child instanceof AtomToken && child.text().trim()) {
243
- dirty = true;
244
- this.removeAt(i);
245
- }
246
- }
247
- if (!Parser.running && dirty) {
248
- Parser.warn(`${this.constructor.name}.sanitize 方法将清理无效属性!`);
249
- }
250
- }
251
-
252
- /** @override */
253
- cloneNode() {
254
- const cloned = this.cloneChildNodes();
255
- return Parser.run(() => {
256
- const token = new AttributesToken(undefined, this.type, this.name, this.getAttribute('config'));
257
- token.append(...cloned);
258
- return token;
259
- });
260
- }
261
-
262
- /**
263
- * 所有无效属性
264
- * @returns {AtomToken[]}
265
- */
266
- getDirtyAttrs() {
267
- return this.childNodes.filter(child => child instanceof AtomToken && child.text().trim());
268
- }
269
-
270
- /**
271
- * @override
272
- * @param {AttributeToken} token 待插入的子节点
273
- * @param {number} i 插入位置
274
- * @throws `RangeError` 不是AttributeToken或标签不匹配
275
- */
276
- insertAt(token, i = this.length) {
277
- if (!(token instanceof AttributeToken)) {
278
- throw new RangeError(`${this.constructor.name}只能插入AttributeToken!`);
279
- } else if (token.type !== this.type.slice(0, -1) || token.tag !== this.name) {
280
- throw new RangeError(`待插入的AttributeToken只可用于${token.tag}标签!`);
281
- } else if (i === this.length) {
282
- const {lastChild} = this;
283
- if (lastChild instanceof AttributeToken) {
284
- lastChild.close();
285
- }
286
- } else {
287
- token.close();
288
- }
289
- if (this.closest('parameter')) {
290
- token.escape();
291
- }
292
- super.insertAt(token, i);
293
- const {previousVisibleSibling, nextVisibleSibling} = token,
294
- type = `${this.type.slice(0, -1)}-dirty`,
295
- config = this.getAttribute('config'),
296
- acceptable = {[`Stage-${stages[this.type]}`]: ':'};
297
- if (nextVisibleSibling && !/^\s/u.test(String(nextVisibleSibling))) {
298
- super.insertAt(Parser.run(() => new AtomToken(' ', type, config, [], acceptable)), i + 1);
299
- }
300
- if (previousVisibleSibling && !/\s$/u.test(String(previousVisibleSibling))) {
301
- super.insertAt(Parser.run(() => new AtomToken(' ', type, config, [], acceptable)), i);
302
- }
303
- return token;
304
- }
305
-
306
- /**
307
- * 设置标签属性
308
- * @param {string} key 属性键
309
- * @param {string|boolean} value 属性值
310
- * @throws `RangeError` 扩展标签属性不能包含">"
311
- * @throws `RangeError` 无效的属性名
312
- */
313
- setAttr(key, value) {
314
- if (typeof key !== 'string' || typeof value !== 'string' && typeof value !== 'boolean') {
315
- this.typeError('setAttr', 'String', 'Boolean');
316
- } else if (this.type === 'ext-attrs' && typeof value === 'string' && value.includes('>')) {
317
- throw new RangeError('扩展标签属性不能包含 ">"!');
318
- }
319
- key = key.toLowerCase().trim();
320
- const attr = this.getAttrToken(key);
321
- if (attr) {
322
- attr.setValue(value);
323
- return;
324
- } else if (value === false) {
325
- return;
326
- }
327
- const config = this.getAttribute('config'),
328
- include = this.getAttribute('include'),
329
- parsedKey = this.type === 'ext-attrs'
330
- ? key
331
- : Parser.run(() => {
332
- const token = new Token(key, config),
333
- parseOnce = token.getAttribute('parseOnce');
334
- parseOnce(0, include);
335
- return String(parseOnce());
336
- });
337
- if (!/^(?:[\w:]|\0\d+[t!~{}+-]\x7F)(?:[\w:.-]|\0\d+[t!~{}+-]\x7F)*$/u.test(parsedKey)) {
338
- throw new RangeError(`无效的属性名:${key}!`);
339
- }
340
- const newAttr = Parser.run(() => new AttributeToken(
341
- this.type.slice(0, -1), this.name, key, value === true ? '' : '=', value, ['"', '"'], config,
342
- ));
343
- this.insertAt(newAttr);
344
- }
345
-
346
- /**
347
- * @override
348
- * @template {string} T
349
- * @param {T} key 属性键
350
- * @returns {TokenAttribute<T>}
351
- */
352
- getAttribute(key) {
353
- return key === 'matchesAttr' ? this.#matchesAttr : super.getAttribute(key);
354
- }
355
-
356
- /**
357
- * 标签是否具有某属性
358
- * @param {string} key 属性键
359
- */
360
- hasAttr(key) {
361
- return typeof key === 'string'
362
- ? this.getAttrTokens(key).length > 0
363
- : this.typeError('hasAttr', 'String');
364
- }
365
-
366
- /** 获取全部的标签属性名 */
367
- getAttrNames() {
368
- return new Set(this.childNodes.filter(child => child instanceof AttributeToken).map(({name}) => name));
369
- }
370
-
371
- /** 标签是否具有任意属性 */
372
- hasAttrs() {
373
- return this.getAttrNames().size > 0;
374
- }
375
-
376
- /** 获取全部标签属性 */
377
- getAttrs() {
378
- const /** @type {AttributeToken[]} */ attrs = this.childNodes.filter(child => child instanceof AttributeToken);
379
- return Object.fromEntries(attrs.map(({name, value}) => [name, value]));
380
- }
381
-
382
- /**
383
- * 移除标签属性
384
- * @param {string} key 属性键
385
- */
386
- removeAttr(key) {
387
- for (const attr of this.getAttrTokens(key)) {
388
- attr.remove();
389
- }
390
- }
391
-
392
- /**
393
- * 开关标签属性
394
- * @param {string} key 属性键
395
- * @param {boolean|undefined} force 强制开启或关闭
396
- * @throws `RangeError` 不为Boolean类型的属性值
397
- */
398
- toggleAttr(key, force) {
399
- if (typeof key !== 'string') {
400
- this.typeError('toggleAttr', 'String');
401
- } else if (force !== undefined) {
402
- force = Boolean(force);
403
- }
404
- key = key.toLowerCase().trim();
405
- const attr = this.getAttrToken(key);
406
- if (attr && attr.getValue() !== true) {
407
- throw new RangeError(`${key} 属性的值不为 Boolean!`);
408
- } else if (attr) {
409
- attr.setValue(force === true);
410
- } else if (force !== false) {
411
- this.setAttr(key, true);
412
- }
413
- }
414
-
415
- /**
416
- * 生成引导空格
417
- * @param {string} str 属性字符串
418
- */
419
- #leadingSpace(str = super.toString()) {
420
- const {type} = this,
421
- leadingRegex = {'ext-attrs': /^\s/u, 'html-attrs': /^[/\s]/u};
422
- return str && type !== 'table-attrs' && !leadingRegex[type].test(str) ? ' ' : '';
423
- }
424
-
425
- /**
426
- * @override
427
- * @param {string} selector
428
- */
429
- toString(selector) {
430
- if (this.type === 'table-attrs') {
431
- normalizeSpace(this);
432
- }
433
- const str = super.toString(selector);
434
- return `${this.#leadingSpace(str)}${str}`;
435
- }
436
-
437
- /** @override */
438
- getPadding() {
439
- return this.#leadingSpace().length;
440
- }
441
-
442
- /** @override */
443
- text() {
444
- if (this.type === 'table-attrs') {
445
- normalizeSpace(this);
446
- }
447
- const str = text(this.childNodes.filter(child => child instanceof AttributeToken), ' ');
448
- return `${this.#leadingSpace(str)}${str}`;
449
- }
450
148
  }
451
149
 
452
- Parser.classes.AttributesToken = __filename;
453
150
  module.exports = AttributesToken;