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.
- package/config/default.json +129 -66
- package/config/zhwiki.json +4 -4
- package/index.js +74 -65
- package/lib/element.js +125 -152
- package/lib/node.js +251 -223
- package/lib/ranges.js +2 -2
- package/lib/text.js +64 -64
- package/lib/title.js +8 -7
- package/mixin/hidden.js +2 -0
- package/mixin/sol.js +1 -2
- package/package.json +4 -3
- package/parser/brackets.js +8 -2
- package/parser/externalLinks.js +1 -1
- package/parser/hrAndDoubleUnderscore.js +4 -4
- package/parser/links.js +7 -7
- package/parser/table.js +12 -10
- package/src/arg.js +53 -48
- package/src/atom/index.js +7 -5
- package/src/attribute.js +91 -80
- package/src/charinsert.js +91 -0
- package/src/converter.js +22 -11
- package/src/converterFlags.js +72 -62
- package/src/converterRule.js +49 -49
- package/src/extLink.js +30 -28
- package/src/gallery.js +56 -32
- package/src/hasNowiki/index.js +42 -0
- package/src/hasNowiki/pre.js +40 -0
- package/src/heading.js +15 -11
- package/src/html.js +38 -38
- package/src/imageParameter.js +64 -48
- package/src/imagemap.js +205 -0
- package/src/imagemapLink.js +43 -0
- package/src/index.js +222 -124
- package/src/link/category.js +4 -8
- package/src/link/file.js +95 -59
- package/src/link/galleryImage.js +74 -10
- package/src/link/index.js +61 -39
- package/src/magicLink.js +21 -22
- package/src/nested/choose.js +24 -0
- package/src/nested/combobox.js +23 -0
- package/src/nested/index.js +88 -0
- package/src/nested/references.js +23 -0
- package/src/nowiki/comment.js +17 -17
- package/src/nowiki/dd.js +2 -2
- package/src/nowiki/doubleUnderscore.js +14 -14
- package/src/nowiki/index.js +12 -0
- package/src/onlyinclude.js +10 -8
- package/src/paramTag/index.js +83 -0
- package/src/paramTag/inputbox.js +42 -0
- package/src/parameter.js +32 -18
- package/src/syntax.js +9 -1
- package/src/table/index.js +33 -32
- package/src/table/td.js +51 -57
- package/src/table/tr.js +6 -6
- package/src/tagPair/ext.js +58 -40
- package/src/tagPair/include.js +1 -1
- package/src/tagPair/index.js +21 -20
- package/src/transclude.js +158 -143
- package/tool/index.js +720 -439
- package/util/base.js +17 -0
- package/util/debug.js +1 -1
- package/util/diff.js +1 -1
- 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
|
|
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': '	', '\r': '', ' ': ' '};
|
|
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.
|
|
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
|
-
*
|
|
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, ';')}}-`;
|
package/src/converterFlags.js
CHANGED
|
@@ -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
|
-
|
|
131
|
-
|
|
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
|
-
* @
|
|
137
|
-
* @
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
154
|
-
return this.#flags.
|
|
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.
|
|
248
|
+
this.insertAt(token);
|
|
239
249
|
}
|
|
240
250
|
|
|
241
251
|
/**
|
package/src/converterRule.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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 {
|
|
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
|
-
{
|
|
187
|
-
{lastChild: converterRule, type,
|
|
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
|
-
{
|
|
231
|
-
{type,
|
|
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
|
-
|
|
253
|
+
this.removeAt(0);
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
}
|