wikiparser-node 0.11.0-m → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/config/.schema.json +7 -0
  2. package/config/default.json +1 -0
  3. package/config/llwiki.json +35 -0
  4. package/config/moegirl.json +44 -0
  5. package/config/zhwiki.json +466 -0
  6. package/dist/index.d.ts +4 -0
  7. package/dist/lib/element.d.ts +116 -2
  8. package/dist/lib/node.d.ts +193 -10
  9. package/dist/lib/ranges.d.ts +37 -0
  10. package/dist/lib/text.d.ts +35 -1
  11. package/dist/lib/title.d.ts +6 -0
  12. package/dist/mixin/attributeParent.d.ts +9 -0
  13. package/dist/mixin/fixedToken.d.ts +8 -0
  14. package/dist/mixin/singleLine.d.ts +8 -0
  15. package/dist/mixin/sol.d.ts +8 -0
  16. package/dist/parser/selector.d.ts +12 -0
  17. package/dist/src/arg.d.ts +30 -1
  18. package/dist/src/atom/index.d.ts +2 -1
  19. package/dist/src/attribute.d.ts +24 -1
  20. package/dist/src/attributes.d.ts +79 -0
  21. package/dist/src/charinsert.d.ts +32 -0
  22. package/dist/src/converter.d.ts +75 -1
  23. package/dist/src/converterFlags.d.ts +57 -1
  24. package/dist/src/converterRule.d.ts +47 -1
  25. package/dist/src/extLink.d.ts +41 -1
  26. package/dist/src/gallery.d.ts +15 -1
  27. package/dist/src/heading.d.ts +22 -2
  28. package/dist/src/html.d.ts +25 -2
  29. package/dist/src/imageParameter.d.ts +43 -1
  30. package/dist/src/imagemap.d.ts +12 -1
  31. package/dist/src/imagemapLink.d.ts +5 -0
  32. package/dist/src/index.d.ts +136 -3
  33. package/dist/src/link/category.d.ts +8 -0
  34. package/dist/src/link/file.d.ts +58 -0
  35. package/dist/src/link/index.d.ts +61 -2
  36. package/dist/src/magicLink.d.ts +22 -0
  37. package/dist/src/nested/index.d.ts +3 -5
  38. package/dist/src/nowiki/comment.d.ts +13 -1
  39. package/dist/src/nowiki/dd.d.ts +9 -0
  40. package/dist/src/nowiki/doubleUnderscore.d.ts +11 -1
  41. package/dist/src/nowiki/index.d.ts +11 -2
  42. package/dist/src/nowiki/quote.d.ts +0 -7
  43. package/dist/src/onlyinclude.d.ts +8 -1
  44. package/dist/src/paramTag/index.d.ts +8 -3
  45. package/dist/src/parameter.d.ts +48 -1
  46. package/dist/src/syntax.d.ts +6 -1
  47. package/dist/src/table/index.d.ts +257 -0
  48. package/dist/src/table/td.d.ts +67 -6
  49. package/dist/src/table/tr.d.ts +74 -0
  50. package/dist/src/tagPair/ext.d.ts +2 -1
  51. package/dist/src/tagPair/include.d.ts +9 -0
  52. package/dist/src/tagPair/index.d.ts +14 -1
  53. package/dist/src/transclude.d.ts +125 -1
  54. package/dist/tool/index.d.ts +420 -0
  55. package/dist/util/base.d.ts +10 -0
  56. package/dist/util/debug.d.ts +20 -0
  57. package/dist/util/diff.d.ts +8 -0
  58. package/dist/util/string.d.ts +24 -0
  59. package/index.js +256 -4
  60. package/lib/element.js +488 -6
  61. package/lib/node.js +495 -6
  62. package/lib/ranges.js +130 -0
  63. package/lib/text.js +96 -1
  64. package/lib/title.js +28 -1
  65. package/mixin/attributeParent.js +117 -0
  66. package/mixin/fixedToken.js +40 -0
  67. package/mixin/hidden.js +3 -0
  68. package/mixin/singleLine.js +31 -0
  69. package/mixin/sol.js +54 -0
  70. package/package.json +5 -2
  71. package/parser/brackets.js +1 -0
  72. package/parser/commentAndExt.js +1 -0
  73. package/parser/converter.js +1 -0
  74. package/parser/externalLinks.js +1 -0
  75. package/parser/hrAndDoubleUnderscore.js +1 -0
  76. package/parser/html.js +1 -0
  77. package/parser/links.js +5 -4
  78. package/parser/list.js +1 -0
  79. package/parser/magicLinks.js +1 -0
  80. package/parser/quotes.js +1 -0
  81. package/parser/selector.js +180 -0
  82. package/parser/table.js +1 -0
  83. package/src/arg.js +116 -2
  84. package/src/atom/hidden.js +2 -0
  85. package/src/atom/index.js +17 -0
  86. package/src/attribute.js +189 -3
  87. package/src/attributes.js +307 -4
  88. package/src/charinsert.js +97 -0
  89. package/src/converter.js +108 -2
  90. package/src/converterFlags.js +187 -0
  91. package/src/converterRule.js +183 -1
  92. package/src/extLink.js +121 -1
  93. package/src/gallery.js +54 -0
  94. package/src/hasNowiki/index.js +12 -0
  95. package/src/hasNowiki/pre.js +12 -0
  96. package/src/heading.js +54 -3
  97. package/src/html.js +118 -3
  98. package/src/imageParameter.js +164 -2
  99. package/src/imagemap.js +61 -2
  100. package/src/imagemapLink.js +13 -1
  101. package/src/index.js +530 -3
  102. package/src/link/category.js +32 -1
  103. package/src/link/file.js +157 -2
  104. package/src/link/galleryImage.js +60 -2
  105. package/src/link/index.js +270 -1
  106. package/src/magicLink.js +83 -0
  107. package/src/nested/choose.js +1 -0
  108. package/src/nested/combobox.js +1 -0
  109. package/src/nested/index.js +27 -0
  110. package/src/nested/references.js +1 -0
  111. package/src/nowiki/comment.js +25 -1
  112. package/src/nowiki/dd.js +47 -1
  113. package/src/nowiki/doubleUnderscore.js +31 -1
  114. package/src/nowiki/hr.js +20 -1
  115. package/src/nowiki/index.js +23 -1
  116. package/src/nowiki/list.js +5 -2
  117. package/src/nowiki/noinclude.js +14 -0
  118. package/src/nowiki/quote.js +14 -0
  119. package/src/onlyinclude.js +26 -1
  120. package/src/paramTag/index.js +24 -1
  121. package/src/paramTag/inputbox.js +4 -1
  122. package/src/parameter.js +147 -5
  123. package/src/syntax.js +68 -0
  124. package/src/table/index.js +941 -2
  125. package/src/table/td.js +230 -5
  126. package/src/table/tr.js +247 -2
  127. package/src/tagPair/ext.js +21 -1
  128. package/src/tagPair/include.js +24 -0
  129. package/src/tagPair/index.js +56 -2
  130. package/src/transclude.js +512 -5
  131. package/tool/index.js +1209 -0
  132. package/typings/array.d.ts +29 -0
  133. package/typings/event.d.ts +22 -0
  134. package/typings/index.d.ts +67 -0
  135. package/typings/node.d.ts +19 -0
  136. package/typings/parser.d.ts +7 -0
  137. package/typings/table.d.ts +10 -0
  138. package/typings/token.d.ts +3 -0
  139. package/typings/tool.d.ts +6 -0
  140. package/util/debug.js +73 -0
  141. package/util/string.js +51 -0
package/src/attributes.js CHANGED
@@ -1,17 +1,88 @@
1
1
  'use strict';
2
2
 
3
3
  const {generateForSelf, generateForChild} = require('../util/lint'),
4
- {removeComment} = require('../util/string'),
4
+ {toCase, normalizeSpace, text, 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
+
10
12
  /**
11
13
  * 扩展和HTML标签属性
12
14
  * @classdesc `{childNodes: ...AtomToken|AttributeToken}`
13
15
  */
14
16
  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
+
15
86
  /**
16
87
  * @param {string} attr 标签属性
17
88
  * @param {'ext-attrs'|'html-attrs'|'table-attrs'} type 标签类型
@@ -20,6 +91,7 @@ class AttributesToken extends Token {
20
91
  */
21
92
  constructor(attr, type, name, config = Parser.getConfig(), accum = []) {
22
93
  super(undefined, config, true, accum, {
94
+ AtomToken: ':', AttributeToken: ':',
23
95
  });
24
96
  this.type = type;
25
97
  this.setAttribute('name', name);
@@ -40,6 +112,7 @@ class AttributesToken extends Token {
40
112
  const insertDirty = /** 插入无效属性 */ () => {
41
113
  if (out) {
42
114
  super.insertAt(new AtomToken(out, `${type.slice(0, -1)}-dirty`, config, accum, {
115
+ [`Stage-${stages[type]}`]: ':',
43
116
  }));
44
117
  out = '';
45
118
  }
@@ -81,9 +154,11 @@ class AttributesToken extends Token {
81
154
  * @returns {AttributeToken[]}
82
155
  */
83
156
  getAttrTokens(key) {
84
- return this.childNodes.filter(
85
- child => child instanceof AttributeToken && child.name === key.toLowerCase().trim(),
86
- );
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');
87
162
  }
88
163
 
89
164
  /**
@@ -145,6 +220,234 @@ class AttributesToken extends Token {
145
220
  }
146
221
  return errors;
147
222
  }
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 {import('../typings/node').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
+ }
148
450
  }
149
451
 
452
+ Parser.classes.AttributesToken = __filename;
150
453
  module.exports = AttributesToken;
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ const Parser = require('..'),
4
+ singleLine = require('../mixin/singleLine'),
5
+ Token = require('.'),
6
+ HasNowikiToken = require('./hasNowiki');
7
+
8
+ /**
9
+ * `<charinsert>`
10
+ * @classdesc `{childNodes: [...HasNowikiToken]}`
11
+ */
12
+ class CharinsertToken extends Token {
13
+ type = 'ext-inner';
14
+ name = 'charinsert';
15
+
16
+ /**
17
+ * @param {string} wikitext wikitext
18
+ * @param {import('../typings/token').accum} accum
19
+ */
20
+ constructor(wikitext, config = Parser.getConfig(), accum = []) {
21
+ super(undefined, config, true, accum, {
22
+ SingleLineHasNowikiToken: ':',
23
+ });
24
+ const SingleLineHasNowikiToken = singleLine(HasNowikiToken);
25
+ this.append(
26
+ ...wikitext.split('\n').map(str => new SingleLineHasNowikiToken(str, 'charinsert-line', config, accum)),
27
+ );
28
+ }
29
+
30
+ /**
31
+ * @override
32
+ * @param {string} selector
33
+ */
34
+ toString(selector) {
35
+ return super.toString(selector, '\n');
36
+ }
37
+
38
+ /** @override */
39
+ getGaps() {
40
+ return 1;
41
+ }
42
+
43
+ /** @override */
44
+ print() {
45
+ return super.print({sep: '\n'});
46
+ }
47
+
48
+ /** @override */
49
+ cloneNode() {
50
+ const cloned = this.cloneChildNodes();
51
+ return Parser.run(() => {
52
+ const token = new CharinsertToken(undefined, this.getAttribute('config'));
53
+ token.append(...cloned);
54
+ return token;
55
+ });
56
+ }
57
+
58
+ /**
59
+ * 获取某一行的插入选项
60
+ * @param {number|HasNowikiToken} child 行号或子节点
61
+ */
62
+ getLineItems(child) {
63
+ if (Number.isInteger(child)) {
64
+ child = this.childNodes.at(child);
65
+ }
66
+ if (!(child instanceof HasNowikiToken)) {
67
+ this.typeError('getLineItems', 'Number', 'HasNowikiToken');
68
+ }
69
+ const entities = {'\t': '&#9;', '\r': '&#12;', ' ': '&#32;'};
70
+ return String(child).replace(
71
+ /<nowiki>(.+?)<\/nowiki>/giu,
72
+ /** @param {string} inner */ (_, inner) => inner.replace(/[\t\r ]/gu, s => entities[s]),
73
+ ).split(/\s/u).filter(Boolean)
74
+ .map(item => {
75
+ const parts = item.split('+');
76
+ if (parts.length === 1) {
77
+ return parts[0];
78
+ }
79
+ return parts[0] ? parts.slice(0, 2) : '+';
80
+ });
81
+ }
82
+
83
+ /** 获取所有插入选项 */
84
+ getAllItems() {
85
+ return this.childNodes.flatMap(child => this.getLineItems(child));
86
+ }
87
+
88
+ /** @override */
89
+ text() {
90
+ return this.childNodes.map(
91
+ child => this.getLineItems(child).map(str => Array.isArray(str) ? str.join('+') : str).join(' '),
92
+ ).join('\n');
93
+ }
94
+ }
95
+
96
+ Parser.classes.CharinsertToken = __filename;
97
+ module.exports = CharinsertToken;
package/src/converter.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {text} = require('../util/string'),
3
+ const {text, print} = require('../util/string'),
4
4
  Parser = require('..'),
5
5
  Token = require('.'),
6
6
  ConverterFlagsToken = require('./converterFlags'),
@@ -13,6 +13,19 @@ const {text} = require('../util/string'),
13
13
  class ConverterToken extends Token {
14
14
  type = 'converter';
15
15
 
16
+ /**
17
+ * 是否无转换
18
+ * @this {ConverterToken & {lastChild: ConverterRuleToken}}
19
+ */
20
+ get noConvert() {
21
+ return this.hasFlag('R') || this.length === 2 && !this.lastChild.variant;
22
+ }
23
+
24
+ /** flags */
25
+ get flags() {
26
+ return this.getAllFlags();
27
+ }
28
+
16
29
  /**
17
30
  * @param {string[]} flags 转换类型标记
18
31
  * @param {string[]} rules 转换规则
@@ -32,15 +45,19 @@ class ConverterToken extends Token {
32
45
  ...rules.slice(1).map(rule => new ConverterRuleToken(rule, true, config, accum)),
33
46
  );
34
47
  }
48
+ this.getAttribute('protectChildren')(0);
35
49
  }
36
50
 
37
51
  /**
38
52
  * @override
53
+ * @param {string} selector
39
54
  * @returns {string}
40
55
  */
41
56
  toString(selector) {
42
57
  const {childNodes: [flags, ...rules]} = this;
43
- return `-{${flags.toString()}${flags.length > 0 ? '|' : ''}${rules.map(String).join(';')}}-`;
58
+ return selector && this.matches(selector)
59
+ ? ''
60
+ : `-{${flags.toString(selector)}${flags.length > 0 ? '|' : ''}${rules.map(String).join(';')}}-`;
44
61
  }
45
62
 
46
63
  /**
@@ -65,6 +82,95 @@ class ConverterToken extends Token {
65
82
  i = i < 0 ? i + this.length : i;
66
83
  return i || this.firstChild.length > 0 ? 1 : 0;
67
84
  }
85
+
86
+ /** @override */
87
+ print() {
88
+ const {childNodes: [flags, ...rules]} = this;
89
+ return `<span class="wpb-converter">-{${flags.print()}${
90
+ flags.length > 0 ? '|' : ''
91
+ }${print(rules, {sep: ';'})}}-</span>`;
92
+ }
93
+
94
+ /** @override */
95
+ cloneNode() {
96
+ const [flags, ...rules] = this.cloneChildNodes();
97
+ return Parser.run(() => {
98
+ const token = new ConverterToken([], [], this.getAttribute('config'));
99
+ token.firstChild.safeReplaceWith(flags);
100
+ token.append(...rules);
101
+ return token;
102
+ });
103
+ }
104
+
105
+ /**
106
+ * 获取所有转换类型标记
107
+ * @this {{firstChild: ConverterFlagsToken}}
108
+ */
109
+ getAllFlags() {
110
+ return this.firstChild.getAllFlags();
111
+ }
112
+
113
+ /**
114
+ * 获取有效的转换类型标记
115
+ * @this {{firstChild: ConverterFlagsToken}}
116
+ */
117
+ getEffectiveFlags() {
118
+ return this.firstChild.getEffectiveFlags();
119
+ }
120
+
121
+ /**
122
+ * 获取未知的转换类型标记
123
+ * @this {{firstChild: ConverterFlagsToken}}
124
+ */
125
+ getUnknownFlags() {
126
+ return this.firstChild.getUnknownFlags();
127
+ }
128
+
129
+ /**
130
+ * 是否具有某转换类型标记
131
+ * @this {{firstChild: ConverterFlagsToken}}
132
+ * @param {string} flag 转换类型标记
133
+ */
134
+ hasFlag(flag) {
135
+ return this.firstChild.hasFlag(flag);
136
+ }
137
+
138
+ /**
139
+ * 是否具有某有效的转换类型标记
140
+ * @this {{firstChild: ConverterFlagsToken}}
141
+ * @param {string} flag 转换类型标记
142
+ */
143
+ hasEffectiveFlag(flag) {
144
+ return this.firstChild.hasEffectiveFlag(flag);
145
+ }
146
+
147
+ /**
148
+ * 移除转换类型标记
149
+ * @this {{firstChild: ConverterFlagsToken}}
150
+ * @param {string} flag 转换类型标记
151
+ */
152
+ removeFlag(flag) {
153
+ this.firstChild.removeFlag(flag);
154
+ }
155
+
156
+ /**
157
+ * 设置转换类型标记
158
+ * @this {{firstChild: ConverterFlagsToken}}
159
+ * @param {string} flag 转换类型标记
160
+ */
161
+ setFlag(flag) {
162
+ this.firstChild.setFlag(flag);
163
+ }
164
+
165
+ /**
166
+ * 开关某转换类型标记
167
+ * @this {{firstChild: ConverterFlagsToken}}
168
+ * @param {string} flag 转换类型标记
169
+ */
170
+ toggleFlag(flag) {
171
+ this.firstChild.toggleFlag(flag);
172
+ }
68
173
  }
69
174
 
175
+ Parser.classes.ConverterToken = __filename;
70
176
  module.exports = ConverterToken;