wikiparser-node 0.5.0 → 0.6.1

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 (63) hide show
  1. package/config/default.json +129 -66
  2. package/config/zhwiki.json +4 -4
  3. package/index.js +74 -65
  4. package/lib/element.js +125 -152
  5. package/lib/node.js +251 -223
  6. package/lib/ranges.js +2 -2
  7. package/lib/text.js +64 -64
  8. package/lib/title.js +8 -7
  9. package/mixin/hidden.js +2 -0
  10. package/mixin/sol.js +1 -2
  11. package/package.json +4 -3
  12. package/parser/brackets.js +8 -2
  13. package/parser/externalLinks.js +1 -1
  14. package/parser/hrAndDoubleUnderscore.js +4 -4
  15. package/parser/links.js +7 -7
  16. package/parser/table.js +12 -10
  17. package/src/arg.js +53 -48
  18. package/src/atom/index.js +7 -5
  19. package/src/attribute.js +91 -80
  20. package/src/charinsert.js +91 -0
  21. package/src/converter.js +22 -11
  22. package/src/converterFlags.js +72 -62
  23. package/src/converterRule.js +49 -49
  24. package/src/extLink.js +30 -28
  25. package/src/gallery.js +56 -32
  26. package/src/hasNowiki/index.js +42 -0
  27. package/src/hasNowiki/pre.js +40 -0
  28. package/src/heading.js +15 -11
  29. package/src/html.js +38 -38
  30. package/src/imageParameter.js +64 -48
  31. package/src/imagemap.js +205 -0
  32. package/src/imagemapLink.js +43 -0
  33. package/src/index.js +222 -124
  34. package/src/link/category.js +4 -8
  35. package/src/link/file.js +95 -59
  36. package/src/link/galleryImage.js +74 -10
  37. package/src/link/index.js +61 -39
  38. package/src/magicLink.js +21 -22
  39. package/src/nested/choose.js +24 -0
  40. package/src/nested/combobox.js +23 -0
  41. package/src/nested/index.js +88 -0
  42. package/src/nested/references.js +23 -0
  43. package/src/nowiki/comment.js +17 -17
  44. package/src/nowiki/dd.js +2 -2
  45. package/src/nowiki/doubleUnderscore.js +14 -14
  46. package/src/nowiki/index.js +12 -0
  47. package/src/onlyinclude.js +10 -8
  48. package/src/paramTag/index.js +83 -0
  49. package/src/paramTag/inputbox.js +42 -0
  50. package/src/parameter.js +32 -18
  51. package/src/syntax.js +9 -1
  52. package/src/table/index.js +33 -32
  53. package/src/table/td.js +51 -57
  54. package/src/table/tr.js +6 -6
  55. package/src/tagPair/ext.js +58 -40
  56. package/src/tagPair/include.js +1 -1
  57. package/src/tagPair/index.js +21 -20
  58. package/src/transclude.js +158 -143
  59. package/tool/index.js +720 -439
  60. package/util/base.js +17 -0
  61. package/util/debug.js +1 -1
  62. package/util/diff.js +1 -1
  63. package/util/string.js +20 -20
package/src/attribute.js CHANGED
@@ -84,6 +84,16 @@ class AttributeToken extends Token {
84
84
  this.setAttr('id', id);
85
85
  }
86
86
 
87
+ /** #sanitized */
88
+ get sanitized() {
89
+ return this.#sanitized;
90
+ }
91
+
92
+ /** #quoteBalance */
93
+ get quoteBalance() {
94
+ return this.#quoteBalance;
95
+ }
96
+
87
97
  /**
88
98
  * 从`this.#attr`更新`childNodes`
89
99
  * @complexity `n`
@@ -168,7 +178,87 @@ class AttributeToken extends Token {
168
178
  constructor(attr, type, name, config = Parser.getConfig(), accum = []) {
169
179
  super(attr, config, true, accum, {[`Stage-${stages[type]}`]: ':'});
170
180
  this.type = type;
171
- this.setAttribute('name', name).#parseAttr();
181
+ this.#parseAttr();
182
+ this.setAttribute('name', name);
183
+ }
184
+
185
+ /**
186
+ * 获取标签属性
187
+ * @template {string|undefined} T
188
+ * @param {T} key 属性键
189
+ * @returns {T extends string ? string|true : Record<string, string|true>}
190
+ */
191
+ getAttr(key) {
192
+ if (key === undefined) {
193
+ return Object.fromEntries(this.#attr);
194
+ }
195
+ return typeof key === 'string' ? this.#attr.get(key.toLowerCase().trim()) : this.typeError('getAttr', 'String');
196
+ }
197
+
198
+ /**
199
+ * 设置标签属性
200
+ * @param {string} key 属性键
201
+ * @param {string|boolean} value 属性值
202
+ * @param {boolean} init 是否是初次解析
203
+ * @complexity `n`
204
+ * @throws `RangeError` 扩展标签属性不能包含">"
205
+ * @throws `RangeError` 无效的属性名
206
+ */
207
+ setAttr(key, value, init) {
208
+ init &&= !externalUse('setAttr');
209
+ if (typeof key !== 'string' || typeof value !== 'string' && typeof value !== 'boolean') {
210
+ this.typeError('setAttr', 'String', 'Boolean');
211
+ } else if (!init && this.type === 'ext-attr' && typeof value === 'string' && value.includes('>')) {
212
+ throw new RangeError('扩展标签属性不能包含 ">"!');
213
+ }
214
+ key = key.toLowerCase().trim();
215
+ const config = this.getAttribute('config'),
216
+ include = this.getAttribute('include'),
217
+ parsedKey = this.type === 'ext-attr' || init
218
+ ? key
219
+ : Parser.run(() => {
220
+ const token = new Token(key, config),
221
+ parseOnce = token.getAttribute('parseOnce');
222
+ parseOnce(0, include);
223
+ return String(parseOnce());
224
+ });
225
+ if (!/^(?:[\w:]|\0\d+[t!~{}+-]\x7F)(?:[\w:.-]|\0\d+[t!~{}+-]\x7F)*$/u.test(parsedKey)) {
226
+ if (init) {
227
+ return false;
228
+ }
229
+ throw new RangeError(`无效的属性名:${key}!`);
230
+ } else if (value === false) {
231
+ this.#attr.delete(key);
232
+ } else {
233
+ this.#attr.set(key, value === true ? true : value.replaceAll(/\s/gu, ' ').trim());
234
+ }
235
+ if (!init) {
236
+ this.sanitize();
237
+ }
238
+ return true;
239
+ }
240
+
241
+ /**
242
+ * @override
243
+ * @this {AttributeToken & {parentNode: HtmlToken}}
244
+ * @param {number} start 起始位置
245
+ */
246
+ lint(start = 0) {
247
+ const HtmlToken = require('./html');
248
+ const errors = super.lint(start);
249
+ let /** @type {{top: number, left: number}} */ rect;
250
+ if (this.type === 'html-attr' && this.parentNode.closing && this.text().trim()) {
251
+ rect = this.getRootNode().posFromIndex(start);
252
+ errors.push(generateForSelf(this, rect, '位于闭合标签的属性'));
253
+ }
254
+ if (!this.#sanitized) {
255
+ rect ||= this.getRootNode().posFromIndex(start);
256
+ errors.push(generateForSelf(this, rect, '包含无效属性'));
257
+ } else if (!this.#quoteBalance) {
258
+ rect ||= this.getRootNode().posFromIndex(start);
259
+ errors.push(generateForSelf(this, rect, '未闭合的引号', 'warning'));
260
+ }
261
+ return errors;
172
262
  }
173
263
 
174
264
  /** @override */
@@ -231,19 +321,6 @@ class AttributeToken extends Token {
231
321
  return typeof key === 'string' ? this.#attr.has(key.toLowerCase().trim()) : this.typeError('hasAttr', 'String');
232
322
  }
233
323
 
234
- /**
235
- * 获取标签属性
236
- * @template {string|undefined} T
237
- * @param {T} key 属性键
238
- * @returns {T extends string ? string|true : Record<string, string|true>}
239
- */
240
- getAttr(key) {
241
- if (key === undefined) {
242
- return Object.fromEntries(this.#attr);
243
- }
244
- return typeof key === 'string' ? this.#attr.get(key.toLowerCase().trim()) : this.typeError('getAttr', 'String');
245
- }
246
-
247
324
  /** 获取全部的标签属性名 */
248
325
  getAttrNames() {
249
326
  return [...this.#attr.keys()];
@@ -254,49 +331,6 @@ class AttributeToken extends Token {
254
331
  return this.getAttrNames().length > 0;
255
332
  }
256
333
 
257
- /**
258
- * 设置标签属性
259
- * @param {string} key 属性键
260
- * @param {string|boolean} value 属性值
261
- * @param {boolean} init 是否是初次解析
262
- * @complexity `n`
263
- * @throws `RangeError` 扩展标签属性不能包含">"
264
- * @throws `RangeError` 无效的属性名
265
- */
266
- setAttr(key, value, init) {
267
- init &&= !externalUse('setAttr');
268
- if (typeof key !== 'string' || typeof value !== 'string' && typeof value !== 'boolean') {
269
- this.typeError('setAttr', 'String', 'Boolean');
270
- } else if (!init && this.type === 'ext-attr' && typeof value === 'string' && value.includes('>')) {
271
- throw new RangeError('扩展标签属性不能包含 ">"!');
272
- }
273
- key = key.toLowerCase().trim();
274
- const config = this.getAttribute('config'),
275
- include = this.getAttribute('include'),
276
- parsedKey = this.type === 'ext-attr' || init
277
- ? key
278
- : Parser.run(() => {
279
- const token = new Token(key, config),
280
- parseOnce = token.getAttribute('parseOnce');
281
- parseOnce(0, include);
282
- return String(parseOnce());
283
- });
284
- if (!/^(?:[\w:]|\0\d+[t!~{}+-]\x7F)(?:[\w:.-]|\0\d+[t!~{}+-]\x7F)*$/u.test(parsedKey)) {
285
- if (init) {
286
- return false;
287
- }
288
- throw new RangeError(`无效的属性名:${key}!`);
289
- } else if (value === false) {
290
- this.#attr.delete(key);
291
- } else {
292
- this.#attr.set(key, value === true ? true : value.replaceAll(/\s/gu, ' ').trim());
293
- }
294
- if (!init) {
295
- this.sanitize();
296
- }
297
- return true;
298
- }
299
-
300
334
  /**
301
335
  * 移除标签属性
302
336
  * @param {string} key 属性键
@@ -359,29 +393,6 @@ class AttributeToken extends Token {
359
393
  return this.#leadingSpace().length;
360
394
  }
361
395
 
362
- /**
363
- * @override
364
- * @this {AttributeToken & {parentNode: HtmlToken}}
365
- * @param {number} start 起始位置
366
- */
367
- lint(start = 0) {
368
- const HtmlToken = require('./html');
369
- const errors = super.lint(start);
370
- let /** @type {{top: number, left: number}} */ rect;
371
- if (this.type === 'html-attr' && this.parentNode.closing && this.text().trim()) {
372
- rect = this.getRootNode().posFromIndex(start);
373
- errors.push(generateForSelf(this, rect, '位于闭合标签的属性'));
374
- }
375
- if (!this.#sanitized) {
376
- rect ||= this.getRootNode().posFromIndex(start);
377
- errors.push(generateForSelf(this, rect, '包含无效属性'));
378
- } else if (!this.#quoteBalance) {
379
- rect ||= this.getRootNode().posFromIndex(start);
380
- errors.push(generateForSelf(this, rect, '未闭合的引号', 'warning'));
381
- }
382
- return errors;
383
- }
384
-
385
396
  /** @override */
386
397
  text() {
387
398
  if (this.type === 'table-attr') {
@@ -0,0 +1,91 @@
1
+ 'use strict';
2
+
3
+ const Parser = require('..'),
4
+ Token = require('.'),
5
+ HasNowikiToken = require('./hasNowiki');
6
+
7
+ /**
8
+ * `<charinsert>`
9
+ * @classdesc `{childNodes: [...HasNowikiToken]}`
10
+ */
11
+ class CharinsertToken extends Token {
12
+ type = 'ext-inner';
13
+ name = 'charinsert';
14
+
15
+ /**
16
+ * @param {string} wikitext wikitext
17
+ * @param {accum} accum
18
+ */
19
+ constructor(wikitext, config = Parser.getConfig(), accum = []) {
20
+ super(undefined, config, true, accum, {HasNowikiToken: ':'});
21
+ this.append(...wikitext.split('\n').map(str => new HasNowikiToken(str, 'charinsert-line', config, accum)));
22
+ }
23
+
24
+ /**
25
+ * @override
26
+ * @param {string} selector
27
+ */
28
+ toString(selector) {
29
+ return super.toString(selector, '\n');
30
+ }
31
+
32
+ /** @override */
33
+ getGaps() {
34
+ return 1;
35
+ }
36
+
37
+ /** @override */
38
+ print() {
39
+ return super.print({sep: '\n'});
40
+ }
41
+
42
+ /** @override */
43
+ cloneNode() {
44
+ const cloned = this.cloneChildNodes();
45
+ return Parser.run(() => {
46
+ const token = new CharinsertToken(undefined, this.getAttribute('config'));
47
+ token.append(...cloned);
48
+ return token;
49
+ });
50
+ }
51
+
52
+ /**
53
+ * 获取某一行的插入选项
54
+ * @param {number|HasNowikiToken} child 行号或子节点
55
+ */
56
+ getLineItems(child) {
57
+ if (Number.isInteger(child)) {
58
+ child = this.childNodes.at(child);
59
+ }
60
+ if (!(child instanceof HasNowikiToken)) {
61
+ this.typeError('getLineItems', 'Number', 'HasNowikiToken');
62
+ }
63
+ const entities = {'\t': '&#9;', '\r': '&#12;', ' ': '&#32;'};
64
+ return String(child).replaceAll(
65
+ /<nowiki>(.+?)<\/nowiki>/giu,
66
+ /** @type {function(...string): string} */ (_, inner) => inner.replaceAll(/[\t\r ]/gu, s => entities[s]),
67
+ ).split(/\s/u).filter(Boolean)
68
+ .map(item => {
69
+ const parts = item.split('+');
70
+ if (parts.length === 1) {
71
+ return parts[0];
72
+ }
73
+ return parts[0] ? parts.slice(0, 2) : '+';
74
+ });
75
+ }
76
+
77
+ /** 获取所有插入选项 */
78
+ getAllItems() {
79
+ return this.childNodes.flatMap(child => this.getLineItems(child));
80
+ }
81
+
82
+ /** @override */
83
+ text() {
84
+ return this.childNodes.map(
85
+ child => this.getLineItems(child).map(str => Array.isArray(str) ? str.join('+') : str).join(' '),
86
+ ).join('\n');
87
+ }
88
+ }
89
+
90
+ Parser.classes.CharinsertToken = __filename;
91
+ module.exports = CharinsertToken;
package/src/converter.js CHANGED
@@ -21,6 +21,11 @@ class ConverterToken extends Token {
21
21
  return this.hasFlag('R') || this.childNodes.length === 2 && !this.lastChild.variant;
22
22
  }
23
23
 
24
+ /** flags */
25
+ get flags() {
26
+ return this.getAllFlags();
27
+ }
28
+
24
29
  /**
25
30
  * @param {string[]} flags 转换类型标记
26
31
  * @param {string[]} rules 转换规则
@@ -33,7 +38,7 @@ class ConverterToken extends Token {
33
38
  hasColon = firstRule.includes(':'),
34
39
  firstRuleToken = new ConverterRuleToken(firstRule, hasColon, config, accum);
35
40
  if (hasColon && firstRuleToken.childNodes.length === 1) {
36
- this.appendChild(new ConverterRuleToken(rules.join(';'), false, config, accum));
41
+ this.insertAt(new ConverterRuleToken(rules.join(';'), false, config, accum));
37
42
  } else {
38
43
  this.append(
39
44
  firstRuleToken,
@@ -43,18 +48,10 @@ class ConverterToken extends Token {
43
48
  this.getAttribute('protectChildren')(0);
44
49
  }
45
50
 
46
- /** @override */
47
- cloneNode() {
48
- const [flags, ...rules] = this.cloneChildNodes(),
49
- token = Parser.run(() => new ConverterToken([], [], this.getAttribute('config')));
50
- token.firstChild.safeReplaceWith(flags);
51
- token.append(...rules);
52
- return token;
53
- }
54
-
55
51
  /**
56
52
  * @override
57
53
  * @param {string} selector
54
+ * @returns {string}
58
55
  */
59
56
  toString(selector) {
60
57
  const {childNodes: [flags, ...rules]} = this;
@@ -69,7 +66,7 @@ class ConverterToken extends Token {
69
66
  }
70
67
 
71
68
  /**
72
- * /** @override
69
+ * @override
73
70
  * @param {number} i 子节点位置
74
71
  */
75
72
  getGaps(i = 0) {
@@ -86,6 +83,20 @@ class ConverterToken extends Token {
86
83
  }
87
84
 
88
85
  /** @override */
86
+ cloneNode() {
87
+ const [flags, ...rules] = this.cloneChildNodes();
88
+ return Parser.run(() => {
89
+ const token = new ConverterToken([], [], this.getAttribute('config'));
90
+ token.firstChild.safeReplaceWith(flags);
91
+ token.append(...rules);
92
+ return token;
93
+ });
94
+ }
95
+
96
+ /**
97
+ * @override
98
+ * @returns {string}
99
+ */
89
100
  text() {
90
101
  const {childNodes: [flags, ...rules]} = this;
91
102
  return `-{${flags.text()}|${text(rules, ';')}}-`;
@@ -22,15 +22,6 @@ class ConverterFlagsToken extends Token {
22
22
  this.append(...flags.map(flag => new AtomToken(flag, 'converter-flag', config, accum)));
23
23
  }
24
24
 
25
- /** @override */
26
- cloneNode() {
27
- const cloned = this.cloneChildNodes(),
28
- token = Parser.run(() => new ConverterFlagsToken([], this.getAttribute('config')));
29
- token.append(...cloned);
30
- token.afterBuild();
31
- return token;
32
- }
33
-
34
25
  /**
35
26
  * @override
36
27
  * @complexity `n`
@@ -46,42 +37,6 @@ class ConverterFlagsToken extends Token {
46
37
  return this;
47
38
  }
48
39
 
49
- /**
50
- * @override
51
- * @template {string} T
52
- * @param {T} key 属性键
53
- * @returns {TokenAttribute<T>}
54
- */
55
- getAttribute(key) {
56
- if (key === 'flags') {
57
- return Parser.debugging ? this.#flags : [...this.#flags];
58
- }
59
- return super.getAttribute(key);
60
- }
61
-
62
- /**
63
- * @override
64
- * @param {number} i 移除位置
65
- * @complexity `n`
66
- */
67
- removeAt(i) {
68
- const /** @type {AtomToken} */ token = super.removeAt(i);
69
- this.#flags?.splice(i, 1);
70
- return token;
71
- }
72
-
73
- /**
74
- * @override
75
- * @param {AtomToken} token 待插入的子节点
76
- * @param {number} i 插入位置
77
- * @complexity `n`
78
- */
79
- insertAt(token, i = this.childNodes.length) {
80
- super.insertAt(token, i);
81
- this.#flags?.splice(i, 0, token.text());
82
- return token;
83
- }
84
-
85
40
  /**
86
41
  * @override
87
42
  * @param {string} selector
@@ -126,19 +81,78 @@ class ConverterFlagsToken extends Token {
126
81
  return errors;
127
82
  }
128
83
 
84
+ /**
85
+ * 获取未知转换类型标记
86
+ * @complexity `n`
87
+ */
88
+ getUnknownFlags() {
89
+ return this.#flags.filter(flag => /\{\{[^{}]+\}\}/u.test(flag));
90
+ }
91
+
92
+ /** 获取指定语言变体的转换标记 */
93
+ getVariantFlags() {
94
+ const {variants} = this.getAttribute('config');
95
+ return this.#flags.filter(flag => variants.includes(flag));
96
+ }
97
+
129
98
  /** @override */
130
- text() {
131
- return super.text(';');
99
+ cloneNode() {
100
+ const cloned = this.cloneChildNodes();
101
+ return Parser.run(() => {
102
+ const token = new ConverterFlagsToken([], this.getAttribute('config'));
103
+ token.append(...cloned);
104
+ token.afterBuild();
105
+ return token;
106
+ });
132
107
  }
133
108
 
134
109
  /**
135
- * 获取转换类型标记节点
136
- * @param {string} flag 转换类型标记
137
- * @returns {AtomToken[]}
110
+ * @override
111
+ * @template {string} T
112
+ * @param {T} key 属性键
113
+ * @returns {TokenAttribute<T>}
114
+ */
115
+ getAttribute(key) {
116
+ if (key === 'flags') {
117
+ return Parser.debugging ? this.#flags : [...this.#flags];
118
+ }
119
+ return super.getAttribute(key);
120
+ }
121
+
122
+ /**
123
+ * @override
124
+ * @param {PropertyKey} key 属性键
125
+ */
126
+ hasAttribute(key) {
127
+ return key === 'flags' || super.hasAttribute(key);
128
+ }
129
+
130
+ /**
131
+ * @override
132
+ * @param {number} i 移除位置
138
133
  * @complexity `n`
139
134
  */
140
- getFlagToken(flag) {
141
- return this.#flags.includes(flag) ? this.childNodes.filter(child => child.text().trim() === flag) : [];
135
+ removeAt(i) {
136
+ const /** @type {AtomToken} */ token = super.removeAt(i);
137
+ this.#flags?.splice(i, 1);
138
+ return token;
139
+ }
140
+
141
+ /**
142
+ * @override
143
+ * @param {AtomToken} token 待插入的子节点
144
+ * @param {number} i 插入位置
145
+ * @complexity `n`
146
+ */
147
+ insertAt(token, i = this.childNodes.length) {
148
+ super.insertAt(token, i);
149
+ this.#flags?.splice(i, 0, token.text());
150
+ return token;
151
+ }
152
+
153
+ /** @override */
154
+ text() {
155
+ return super.text(';');
142
156
  }
143
157
 
144
158
  /** 获取所有转换类型标记 */
@@ -147,17 +161,13 @@ class ConverterFlagsToken extends Token {
147
161
  }
148
162
 
149
163
  /**
150
- * 获取未知转换类型标记
164
+ * 获取转换类型标记节点
165
+ * @param {string} flag 转换类型标记
166
+ * @returns {AtomToken[]}
151
167
  * @complexity `n`
152
168
  */
153
- getUnknownFlags() {
154
- return this.#flags.filter(flag => /\{\{[^{}]+\}\}/u.test(flag));
155
- }
156
-
157
- /** 获取指定语言变体的转换标记 */
158
- getVariantFlags() {
159
- const {variants} = this.getAttribute('config');
160
- return this.#flags.filter(flag => variants.includes(flag));
169
+ getFlagToken(flag) {
170
+ return this.#flags.includes(flag) ? this.childNodes.filter(child => child.text().trim() === flag) : [];
161
171
  }
162
172
 
163
173
  /**
@@ -235,7 +245,7 @@ class ConverterFlagsToken extends Token {
235
245
  */
236
246
  #newFlag(flag) {
237
247
  const token = Parser.run(() => new AtomToken(flag, 'converter-flag', this.getAttribute('config')));
238
- this.appendChild(token);
248
+ this.insertAt(token);
239
249
  }
240
250
 
241
251
  /**
@@ -44,7 +44,7 @@ class ConverterRuleToken extends Token {
44
44
  * @param {accum} accum
45
45
  */
46
46
  constructor(rule, hasColon = true, config = Parser.getConfig(), accum = []) {
47
- super(undefined, config, true, accum, {AtomToken: ':'});
47
+ super(undefined, config, true, accum);
48
48
  if (hasColon) {
49
49
  const i = rule.indexOf(':'),
50
50
  j = rule.slice(0, i).indexOf('=>'),
@@ -65,17 +65,51 @@ class ConverterRuleToken extends Token {
65
65
  this.getAttribute('protectChildren')('1:');
66
66
  }
67
67
 
68
+ /**
69
+ * @override
70
+ * @param {string} selector
71
+ * @returns {string}
72
+ */
73
+ toString(selector) {
74
+ if (this.childNodes.length === 3 && !(selector && this.matches(selector))) {
75
+ const {childNodes: [from, variant, to]} = this;
76
+ return `${from.toString(selector)}=>${variant.toString(selector)}:${to.toString(selector)}`;
77
+ }
78
+ return super.toString(selector, ':');
79
+ }
80
+
81
+ /**
82
+ * @override
83
+ * @param {number} i 子节点序号
84
+ */
85
+ getGaps(i = 0) {
86
+ const {length} = this;
87
+ i = i < 0 ? i + length : i;
88
+ return i === 0 && length === 3 ? 2 : 1;
89
+ }
90
+
91
+ /** @override */
92
+ print() {
93
+ if (this.childNodes.length === 3) {
94
+ const {childNodes: [from, variant, to]} = this;
95
+ return `<span class="wpb-converter-rule">${from.print()}=>${variant.print()}:${to.print()}</span>`;
96
+ }
97
+ return super.print({sep: ':'});
98
+ }
99
+
68
100
  /** @override */
69
101
  cloneNode() {
70
102
  const cloned = this.cloneChildNodes(),
71
103
  placeholders = ['', 'zh:', '=>zh:'],
72
- placeholder = placeholders[cloned.length - 1],
73
- token = Parser.run(() => new ConverterRuleToken(placeholder, placeholder, this.getAttribute('config')));
74
- for (let i = 0; i < cloned.length; i++) {
75
- token.childNodes[i].safeReplaceWith(cloned[i]);
76
- }
77
- token.afterBuild();
78
- return token;
104
+ placeholder = placeholders[cloned.length - 1];
105
+ return Parser.run(() => {
106
+ const token = new ConverterRuleToken(placeholder, placeholder, this.getAttribute('config'));
107
+ for (let i = 0; i < cloned.length; i++) {
108
+ token.childNodes[i].safeReplaceWith(cloned[i]);
109
+ }
110
+ token.afterBuild();
111
+ return token;
112
+ });
79
113
  }
80
114
 
81
115
  /** @override */
@@ -121,38 +155,6 @@ class ConverterRuleToken extends Token {
121
155
  throw new Error(`转换规则语法复杂,请勿尝试对 ${this.constructor.name} 手动插入子节点!`);
122
156
  }
123
157
 
124
- /**
125
- * @override
126
- * @param {string} selector
127
- * @returns {string}
128
- */
129
- toString(selector) {
130
- if (this.childNodes.length === 3 && !(selector && this.matches(selector))) {
131
- const {childNodes: [from, variant, to]} = this;
132
- return `${from.toString(selector)}=>${variant.toString(selector)}:${to.toString(selector)}`;
133
- }
134
- return super.toString(selector, ':');
135
- }
136
-
137
- /**
138
- * @override
139
- * @param {number} i 子节点序号
140
- */
141
- getGaps(i = 0) {
142
- const {childNodes: {length}} = this;
143
- i = i < 0 ? i + length : i;
144
- return i === 0 && length === 3 ? 2 : 1;
145
- }
146
-
147
- /** @override */
148
- print() {
149
- if (this.childNodes.length === 3) {
150
- const {childNodes: [from, variant, to]} = this;
151
- return `<span class="wpb-converter-rule">${from.print()}=>${variant.print()}:${to.print()}</span>`;
152
- }
153
- return super.print({sep: ':'});
154
- }
155
-
156
158
  /**
157
159
  * @override
158
160
  * @returns {string}
@@ -167,11 +169,10 @@ class ConverterRuleToken extends Token {
167
169
 
168
170
  /** 修改为不转换 */
169
171
  noConvert() {
170
- const {childNodes: {length}, lastChild} = this;
172
+ const {length} = this;
171
173
  for (let i = 0; i < length - 1; i++) { // ConverterRuleToken只能从前往后删除子节点
172
174
  this.removeAt(0);
173
175
  }
174
- lastChild.type = 'converter-rule-noconvert';
175
176
  }
176
177
 
177
178
  /**
@@ -183,8 +184,8 @@ class ConverterRuleToken extends Token {
183
184
  to = String(to);
184
185
  const config = this.getAttribute('config'),
185
186
  root = Parser.parse(`-{|${config.variants[0]}:${to}}-`, this.getAttribute('include'), undefined, config),
186
- {childNodes: {length}, firstChild: converter} = root,
187
- {lastChild: converterRule, type, childNodes: {length: converterLength}} = converter;
187
+ {length, firstChild: converter} = root,
188
+ {lastChild: converterRule, type, length: converterLength} = converter;
188
189
  if (length !== 1 || type !== 'converter' || converterLength !== 2 || converterRule.childNodes.length !== 2) {
189
190
  throw new SyntaxError(`非法的转换目标:${noWrap(to)}`);
190
191
  }
@@ -227,12 +228,11 @@ class ConverterRuleToken extends Token {
227
228
  from = String(from);
228
229
  const config = this.getAttribute('config'),
229
230
  root = Parser.parse(`-{|${from}=>${variant}:}-`, this.getAttribute('include'), undefined, config),
230
- {childNodes: {length}, firstChild: converter} = root,
231
- {type, childNodes: {length: converterLength}, lastChild: converterRule} = converter;
231
+ {length, firstChild: converter} = root,
232
+ {type, length: converterLength, lastChild: converterRule} = converter;
232
233
  if (length !== 1 || type !== 'converter' || converterLength !== 2 || converterRule.childNodes.length !== 3) {
233
234
  throw new SyntaxError(`非法的转换原文:${noWrap(from)}`);
234
- }
235
- if (unidirectional) {
235
+ } else if (unidirectional) {
236
236
  this.firstChild.safeReplaceWith(converterRule.firstChild);
237
237
  } else {
238
238
  super.insertAt(converterRule.firstChild, 0);
@@ -250,7 +250,7 @@ class ConverterRuleToken extends Token {
250
250
  /** 修改为双向转换 */
251
251
  makeBidirectional() {
252
252
  if (this.unidirectional) {
253
- super.removeAt(0);
253
+ this.removeAt(0);
254
254
  }
255
255
  }
256
256
  }