wikiparser-node 0.3.0 → 0.4.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.
- package/.eslintrc.json +472 -34
- package/README.md +1 -1
- package/config/default.json +58 -30
- package/config/llwiki.json +22 -90
- package/config/moegirl.json +51 -13
- package/config/zhwiki.json +1269 -0
- package/index.js +114 -104
- package/lib/element.js +448 -440
- package/lib/node.js +335 -115
- package/lib/ranges.js +27 -18
- package/lib/text.js +146 -0
- package/lib/title.js +13 -5
- package/mixin/attributeParent.js +70 -24
- package/mixin/fixedToken.js +14 -6
- package/mixin/hidden.js +6 -4
- package/mixin/sol.js +27 -10
- package/package.json +9 -3
- package/parser/brackets.js +22 -17
- package/parser/commentAndExt.js +18 -16
- package/parser/converter.js +14 -13
- package/parser/externalLinks.js +12 -11
- package/parser/hrAndDoubleUnderscore.js +23 -14
- package/parser/html.js +10 -9
- package/parser/links.js +15 -14
- package/parser/list.js +12 -11
- package/parser/magicLinks.js +12 -11
- package/parser/quotes.js +6 -5
- package/parser/selector.js +175 -0
- package/parser/table.js +25 -18
- package/printed/example.json +120 -0
- package/src/arg.js +56 -32
- package/src/atom/hidden.js +5 -2
- package/src/atom/index.js +17 -9
- package/src/attribute.js +182 -100
- package/src/converter.js +68 -41
- package/src/converterFlags.js +67 -45
- package/src/converterRule.js +117 -65
- package/src/extLink.js +66 -18
- package/src/gallery.js +42 -15
- package/src/heading.js +34 -15
- package/src/html.js +97 -35
- package/src/imageParameter.js +83 -54
- package/src/index.js +299 -178
- package/src/link/category.js +20 -52
- package/src/link/file.js +59 -28
- package/src/link/galleryImage.js +21 -7
- package/src/link/index.js +146 -60
- package/src/magicLink.js +34 -12
- package/src/nowiki/comment.js +22 -10
- package/src/nowiki/dd.js +37 -22
- package/src/nowiki/doubleUnderscore.js +16 -7
- package/src/nowiki/hr.js +11 -7
- package/src/nowiki/index.js +16 -9
- package/src/nowiki/list.js +2 -2
- package/src/nowiki/noinclude.js +8 -4
- package/src/nowiki/quote.js +11 -7
- package/src/onlyinclude.js +19 -7
- package/src/parameter.js +65 -38
- package/src/syntax.js +26 -20
- package/src/table/index.js +260 -165
- package/src/table/td.js +98 -52
- package/src/table/tr.js +102 -58
- package/src/tagPair/ext.js +27 -19
- package/src/tagPair/include.js +16 -11
- package/src/tagPair/index.js +64 -29
- package/src/transclude.js +170 -93
- package/test/api.js +83 -0
- package/test/real.js +133 -0
- package/test/test.js +28 -0
- package/test/util.js +80 -0
- package/tool/index.js +41 -31
- package/typings/api.d.ts +13 -0
- package/typings/array.d.ts +28 -0
- package/typings/event.d.ts +24 -0
- package/typings/index.d.ts +46 -4
- package/typings/node.d.ts +15 -9
- package/typings/parser.d.ts +7 -0
- package/typings/tool.d.ts +3 -2
- package/util/debug.js +21 -18
- package/util/string.js +40 -27
- package/typings/element.d.ts +0 -28
package/src/converterFlags.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const Parser = require('..'),
|
|
4
4
|
Token = require('.'),
|
|
5
5
|
AtomToken = require('./atom');
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ class ConverterFlagsToken extends Token {
|
|
|
13
13
|
/** @type {string[]} */ #flags;
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* @param {string[]} flags
|
|
16
|
+
* @param {string[]} flags 转换类型标记
|
|
17
17
|
* @param {accum} accum
|
|
18
18
|
*/
|
|
19
19
|
constructor(flags, config = Parser.getConfig(), accum = []) {
|
|
@@ -21,30 +21,34 @@ class ConverterFlagsToken extends Token {
|
|
|
21
21
|
this.append(...flags.map(flag => new AtomToken(flag, 'converter-flag', config, accum)));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/** @override */
|
|
24
25
|
cloneNode() {
|
|
25
|
-
const cloned = this.
|
|
26
|
+
const cloned = this.cloneChildNodes(),
|
|
26
27
|
token = Parser.run(() => new ConverterFlagsToken([], this.getAttribute('config')));
|
|
27
28
|
token.append(...cloned);
|
|
28
29
|
token.afterBuild();
|
|
29
30
|
return token;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* @override
|
|
35
|
+
* @complexity `n`
|
|
36
|
+
*/
|
|
33
37
|
afterBuild() {
|
|
34
38
|
this.#flags = this.children.map(child => child.text().trim());
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
39
|
+
const /** @type {AstListener} */ converterFlagsListener = ({prevTarget}) => {
|
|
40
|
+
if (prevTarget) {
|
|
41
|
+
this.#flags[this.childNodes.indexOf(prevTarget)] = prevTarget.text().trim();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
41
44
|
this.addEventListener(['remove', 'insert', 'text', 'replace'], converterFlagsListener);
|
|
42
45
|
return this;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
/**
|
|
49
|
+
* @override
|
|
46
50
|
* @template {string} T
|
|
47
|
-
* @param {T} key
|
|
51
|
+
* @param {T} key 属性键
|
|
48
52
|
* @returns {TokenAttribute<T>}
|
|
49
53
|
*/
|
|
50
54
|
getAttribute(key) {
|
|
@@ -55,7 +59,8 @@ class ConverterFlagsToken extends Token {
|
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
|
-
* @
|
|
62
|
+
* @override
|
|
63
|
+
* @param {number} i 移除位置
|
|
59
64
|
* @complexity `n`
|
|
60
65
|
*/
|
|
61
66
|
removeAt(i) {
|
|
@@ -65,7 +70,9 @@ class ConverterFlagsToken extends Token {
|
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
/**
|
|
68
|
-
* @
|
|
73
|
+
* @override
|
|
74
|
+
* @param {AtomToken} token 待插入的子节点
|
|
75
|
+
* @param {number} i 插入位置
|
|
69
76
|
* @complexity `n`
|
|
70
77
|
*/
|
|
71
78
|
insertAt(token, i = this.childNodes.length) {
|
|
@@ -74,20 +81,27 @@ class ConverterFlagsToken extends Token {
|
|
|
74
81
|
return token;
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
/**
|
|
85
|
+
* @override
|
|
86
|
+
* @param {string} selector
|
|
87
|
+
*/
|
|
88
|
+
toString(selector) {
|
|
89
|
+
return super.toString(selector, ';');
|
|
79
90
|
}
|
|
80
91
|
|
|
92
|
+
/** @override */
|
|
81
93
|
getGaps() {
|
|
82
94
|
return 1;
|
|
83
95
|
}
|
|
84
96
|
|
|
97
|
+
/** @override */
|
|
85
98
|
text() {
|
|
86
99
|
return super.text(';');
|
|
87
100
|
}
|
|
88
101
|
|
|
89
102
|
/**
|
|
90
|
-
*
|
|
103
|
+
* 获取转换类型标记节点
|
|
104
|
+
* @param {string} flag 转换类型标记
|
|
91
105
|
* @returns {AtomToken[]}
|
|
92
106
|
* @complexity `n`
|
|
93
107
|
*/
|
|
@@ -95,16 +109,28 @@ class ConverterFlagsToken extends Token {
|
|
|
95
109
|
return this.#flags.includes(flag) ? this.children.filter(child => child.text().trim() === flag) : [];
|
|
96
110
|
}
|
|
97
111
|
|
|
112
|
+
/** 获取所有转换类型标记 */
|
|
98
113
|
getAllFlags() {
|
|
99
114
|
return new Set(this.#flags);
|
|
100
115
|
}
|
|
101
116
|
|
|
102
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* 获取未知转换类型标记
|
|
119
|
+
* @complexity `n`
|
|
120
|
+
*/
|
|
121
|
+
getUnknownFlags() {
|
|
122
|
+
return this.#flags.filter(flag => /\{\{[^{}]+\}\}/u.test(flag));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 获取有效转换类型标记
|
|
127
|
+
* @complexity `n`
|
|
128
|
+
*/
|
|
103
129
|
getEffectiveFlags() {
|
|
104
130
|
const {variants} = this.getAttribute('config'),
|
|
105
131
|
variantFlags = this.#flags.filter(flag => variants.includes(flag)),
|
|
106
|
-
unknownFlags = this
|
|
107
|
-
if (variantFlags.length) {
|
|
132
|
+
unknownFlags = this.getUnknownFlags();
|
|
133
|
+
if (variantFlags.length > 0) {
|
|
108
134
|
return new Set([...variantFlags, ...unknownFlags]);
|
|
109
135
|
}
|
|
110
136
|
const validFlags = ['A', 'T', 'R', 'D', '-', 'H', 'N'],
|
|
@@ -120,10 +146,9 @@ class ConverterFlagsToken extends Token {
|
|
|
120
146
|
} else if (flags.has('H')) {
|
|
121
147
|
const hasT = flags.has('T'),
|
|
122
148
|
hasD = flags.has('D');
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return new Set(['+', 'H', ...hasT ? ['T'] : [], ...hasD ? ['D'] : [], ...unknownFlags]);
|
|
149
|
+
return hasT && hasD
|
|
150
|
+
? new Set(['+', 'H', 'T', 'D'])
|
|
151
|
+
: new Set(['+', 'H', ...hasT ? ['T'] : [], ...hasD ? ['D'] : [], ...unknownFlags]);
|
|
127
152
|
}
|
|
128
153
|
if (flags.size === 1 && flags.has('T')) {
|
|
129
154
|
flags.add('H');
|
|
@@ -138,32 +163,26 @@ class ConverterFlagsToken extends Token {
|
|
|
138
163
|
return flags;
|
|
139
164
|
}
|
|
140
165
|
|
|
141
|
-
/**
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
/** @param {string} flag */
|
|
166
|
+
/**
|
|
167
|
+
* 是否具有某转换类型标记
|
|
168
|
+
* @param {string} flag 转换类型标记
|
|
169
|
+
*/
|
|
147
170
|
hasFlag(flag) {
|
|
148
|
-
|
|
149
|
-
this.typeError('hasFlag', 'String');
|
|
150
|
-
}
|
|
151
|
-
return this.#flags.includes(flag);
|
|
171
|
+
return typeof flag === 'string' ? this.#flags.includes(flag) : this.typeError('hasFlag', 'String');
|
|
152
172
|
}
|
|
153
173
|
|
|
154
174
|
/**
|
|
155
|
-
*
|
|
175
|
+
* 是否具有某有效转换类型标记
|
|
176
|
+
* @param {string} flag 转换类型标记
|
|
156
177
|
* @complexity `n`
|
|
157
178
|
*/
|
|
158
179
|
hasEffectiveFlag(flag) {
|
|
159
|
-
|
|
160
|
-
this.typeError('hasFlag', 'String');
|
|
161
|
-
}
|
|
162
|
-
return this.getEffectiveFlags().has(flag);
|
|
180
|
+
return typeof flag === 'string' ? this.getEffectiveFlags().has(flag) : this.typeError('hasFlag', 'String');
|
|
163
181
|
}
|
|
164
182
|
|
|
165
183
|
/**
|
|
166
|
-
*
|
|
184
|
+
* 移除某转换类型标记
|
|
185
|
+
* @param {string} flag 转换类型标记
|
|
167
186
|
* @complexity `n²`
|
|
168
187
|
*/
|
|
169
188
|
removeFlag(flag) {
|
|
@@ -173,7 +192,8 @@ class ConverterFlagsToken extends Token {
|
|
|
173
192
|
}
|
|
174
193
|
|
|
175
194
|
/**
|
|
176
|
-
*
|
|
195
|
+
* 添加转换类型标记
|
|
196
|
+
* @param {string} flag 转换类型标记
|
|
177
197
|
* @complexity `n`
|
|
178
198
|
*/
|
|
179
199
|
#newFlag(flag) {
|
|
@@ -182,7 +202,8 @@ class ConverterFlagsToken extends Token {
|
|
|
182
202
|
}
|
|
183
203
|
|
|
184
204
|
/**
|
|
185
|
-
*
|
|
205
|
+
* 设置转换类型标记
|
|
206
|
+
* @param {string} flag 转换类型标记
|
|
186
207
|
* @complexity `n`
|
|
187
208
|
*/
|
|
188
209
|
setFlag(flag) {
|
|
@@ -194,14 +215,15 @@ class ConverterFlagsToken extends Token {
|
|
|
194
215
|
}
|
|
195
216
|
|
|
196
217
|
/**
|
|
197
|
-
*
|
|
218
|
+
* 开关转换类型标记
|
|
219
|
+
* @param {string} flag 转换类型标记
|
|
198
220
|
* @complexity `n²`
|
|
199
221
|
*/
|
|
200
222
|
toggleFlag(flag) {
|
|
201
|
-
if (
|
|
202
|
-
this.#newFlag(flag);
|
|
203
|
-
} else {
|
|
223
|
+
if (this.#flags.includes(flag)) {
|
|
204
224
|
this.removeFlag(flag);
|
|
225
|
+
} else {
|
|
226
|
+
this.#newFlag(flag);
|
|
205
227
|
}
|
|
206
228
|
}
|
|
207
229
|
}
|
package/src/converterRule.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const {undo} = require('../util/debug'),
|
|
4
4
|
{noWrap} = require('../util/string'),
|
|
5
|
-
|
|
5
|
+
Parser = require('..'),
|
|
6
6
|
Token = require('.'),
|
|
7
7
|
AtomToken = require('./atom');
|
|
8
8
|
|
|
@@ -12,41 +12,62 @@ const {undo} = require('../util/debug'),
|
|
|
12
12
|
*/
|
|
13
13
|
class ConverterRuleToken extends Token {
|
|
14
14
|
type = 'converter-rule';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
|
|
16
|
+
/** 语言变体 */
|
|
17
|
+
get variant() {
|
|
18
|
+
return this.children.at(-2)?.text()?.trim() ?? '';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
set variant(variant) {
|
|
22
|
+
this.setVariant(variant);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 是否是单向转换 */
|
|
26
|
+
get unidirectional() {
|
|
27
|
+
return this.childNodes.length === 3;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
set unidirectional(unidirectional) {
|
|
31
|
+
if (unidirectional === false) {
|
|
32
|
+
this.makeBidirectional();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** 是否是双向转换 */
|
|
37
|
+
get bidirectional() {
|
|
38
|
+
return this.childNodes.length === 2;
|
|
39
|
+
}
|
|
18
40
|
|
|
19
41
|
/**
|
|
20
|
-
* @param {string} rule
|
|
42
|
+
* @param {string} rule 转换规则
|
|
43
|
+
* @param {boolean} hasColon 是否带有":"
|
|
21
44
|
* @param {accum} accum
|
|
22
45
|
*/
|
|
23
46
|
constructor(rule, hasColon = true, config = Parser.getConfig(), accum = []) {
|
|
24
47
|
super(undefined, config, true, accum, {AtomToken: ':'});
|
|
25
|
-
if (
|
|
26
|
-
super.insertAt(new AtomToken(rule, 'converter-rule-noconvert', config, accum));
|
|
27
|
-
} else {
|
|
48
|
+
if (hasColon) {
|
|
28
49
|
const i = rule.indexOf(':'),
|
|
29
50
|
j = rule.slice(0, i).indexOf('=>'),
|
|
30
|
-
v =
|
|
51
|
+
v = j === -1 ? rule.slice(0, i) : rule.slice(j + 2, i),
|
|
31
52
|
{variants} = config;
|
|
32
|
-
if (
|
|
33
|
-
super.insertAt(new AtomToken(rule, 'converter-rule-noconvert', config, accum));
|
|
34
|
-
} else {
|
|
53
|
+
if (variants.includes(v.trim())) {
|
|
35
54
|
super.insertAt(new AtomToken(v, 'converter-rule-variant', config, accum));
|
|
36
55
|
super.insertAt(new AtomToken(rule.slice(i + 1), 'converter-rule-to', config, accum));
|
|
37
|
-
if (j
|
|
38
|
-
this.bidirectional = true;
|
|
39
|
-
} else {
|
|
56
|
+
if (j !== -1) {
|
|
40
57
|
super.insertAt(new AtomToken(rule.slice(0, j), 'converter-rule-from', config, accum), 0);
|
|
41
|
-
this.unidirectional = true;
|
|
42
58
|
}
|
|
59
|
+
} else {
|
|
60
|
+
super.insertAt(new AtomToken(rule, 'converter-rule-noconvert', config, accum));
|
|
43
61
|
}
|
|
62
|
+
} else {
|
|
63
|
+
super.insertAt(new AtomToken(rule, 'converter-rule-noconvert', config, accum));
|
|
44
64
|
}
|
|
45
|
-
this.
|
|
65
|
+
this.getAttribute('protectChildren')('1:');
|
|
46
66
|
}
|
|
47
67
|
|
|
68
|
+
/** @override */
|
|
48
69
|
cloneNode() {
|
|
49
|
-
const cloned = this.
|
|
70
|
+
const cloned = this.cloneChildNodes(),
|
|
50
71
|
placeholders = ['', 'zh:', '=>zh:'],
|
|
51
72
|
placeholder = placeholders[cloned.length - 1],
|
|
52
73
|
token = Parser.run(() => new ConverterRuleToken(placeholder, placeholder, this.getAttribute('config')));
|
|
@@ -57,77 +78,98 @@ class ConverterRuleToken extends Token {
|
|
|
57
78
|
return token;
|
|
58
79
|
}
|
|
59
80
|
|
|
81
|
+
/** @override */
|
|
60
82
|
afterBuild() {
|
|
61
|
-
|
|
62
|
-
this
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
{variants} = that.getAttribute('config');
|
|
71
|
-
if (variants.includes(v)) {
|
|
72
|
-
that.setAttribute('variant', v);
|
|
73
|
-
} else {
|
|
74
|
-
undo(e, data);
|
|
75
|
-
throw new Error(`无效的语言变体:${v}`);
|
|
76
|
-
}
|
|
83
|
+
const /** @type {AstListener} */ converterRuleListener = (e, data) => {
|
|
84
|
+
const {childNodes} = this,
|
|
85
|
+
{prevTarget} = e;
|
|
86
|
+
if (childNodes.length > 1 && childNodes.at(-2) === prevTarget) {
|
|
87
|
+
const v = prevTarget.text().trim(),
|
|
88
|
+
{variants} = this.getAttribute('config');
|
|
89
|
+
if (!variants.includes(v)) {
|
|
90
|
+
undo(e, data);
|
|
91
|
+
throw new Error(`无效的语言变体:${v}`);
|
|
77
92
|
}
|
|
78
|
-
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
79
95
|
this.addEventListener(['remove', 'insert', 'text', 'replace'], converterRuleListener);
|
|
80
96
|
return this;
|
|
81
97
|
}
|
|
82
98
|
|
|
83
99
|
/**
|
|
84
|
-
* @
|
|
100
|
+
* @override
|
|
101
|
+
* @param {number} i 移除位置
|
|
85
102
|
* @returns {AtomToken}
|
|
103
|
+
* @throws `Error` 至少保留1个子节点
|
|
86
104
|
*/
|
|
87
105
|
removeAt(i) {
|
|
88
|
-
if (
|
|
89
|
-
throw new
|
|
106
|
+
if (this.childNodes.length === 1) {
|
|
107
|
+
throw new Error(`${this.constructor.name} 需至少保留 1 个子节点!`);
|
|
108
|
+
}
|
|
109
|
+
const removed = super.removeAt(i);
|
|
110
|
+
if (this.childNodes.length === 1) {
|
|
111
|
+
this.firstChild.type = 'converter-rule-noconvert';
|
|
90
112
|
}
|
|
91
|
-
return
|
|
113
|
+
return removed;
|
|
92
114
|
}
|
|
93
115
|
|
|
116
|
+
/**
|
|
117
|
+
* @override
|
|
118
|
+
* @throws `Error` 请勿手动插入子节点
|
|
119
|
+
*/
|
|
94
120
|
insertAt() {
|
|
95
|
-
throw new Error(
|
|
121
|
+
throw new Error(`转换规则语法复杂,请勿尝试对 ${this.constructor.name} 手动插入子节点!`);
|
|
96
122
|
}
|
|
97
123
|
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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 {children: [from, variant, to]} = this;
|
|
132
|
+
return `${from.toString(selector)}=>${variant.toString(selector)}:${to.toString(selector)}`;
|
|
103
133
|
}
|
|
104
|
-
return super.toString(':');
|
|
134
|
+
return super.toString(selector, ':');
|
|
105
135
|
}
|
|
106
136
|
|
|
107
|
-
/**
|
|
137
|
+
/**
|
|
138
|
+
* @override
|
|
139
|
+
* @param {number} i 子节点序号
|
|
140
|
+
*/
|
|
108
141
|
getGaps(i = 0) {
|
|
109
|
-
const {length} = this
|
|
142
|
+
const {childNodes: {length}} = this;
|
|
110
143
|
i = i < 0 ? i + length : i;
|
|
111
144
|
return i === 0 && length === 3 ? 2 : 1;
|
|
112
145
|
}
|
|
113
146
|
|
|
114
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* @override
|
|
149
|
+
* @returns {string}
|
|
150
|
+
*/
|
|
115
151
|
text() {
|
|
116
152
|
if (this.childNodes.length === 3) {
|
|
117
|
-
const [from, variant, to] = this
|
|
153
|
+
const {children: [from, variant, to]} = this;
|
|
118
154
|
return `${from.text()}=>${variant.text()}:${to.text()}`;
|
|
119
155
|
}
|
|
120
156
|
return super.text(':');
|
|
121
157
|
}
|
|
122
158
|
|
|
159
|
+
/** 修改为不转换 */
|
|
123
160
|
noConvert() {
|
|
124
|
-
|
|
125
|
-
|
|
161
|
+
const {childNodes: {length}, lastChild} = this;
|
|
162
|
+
for (let i = 0; i < length - 1; i++) { // ConverterRuleToken只能从前往后删除子节点
|
|
163
|
+
this.removeAt(0);
|
|
126
164
|
}
|
|
127
|
-
|
|
165
|
+
lastChild.type = 'converter-rule-noconvert';
|
|
128
166
|
}
|
|
129
167
|
|
|
130
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* 设置转换目标
|
|
170
|
+
* @param {string} to 转换目标
|
|
171
|
+
* @throws `SyntaxError` 非法的转换目标
|
|
172
|
+
*/
|
|
131
173
|
setTo(to) {
|
|
132
174
|
to = String(to);
|
|
133
175
|
const config = this.getAttribute('config'),
|
|
@@ -138,12 +180,17 @@ class ConverterRuleToken extends Token {
|
|
|
138
180
|
) {
|
|
139
181
|
throw new SyntaxError(`非法的转换目标:${noWrap(to)}`);
|
|
140
182
|
}
|
|
141
|
-
const {
|
|
142
|
-
|
|
183
|
+
const {lastElementChild} = firstElementChild,
|
|
184
|
+
{lastChild} = lastElementChild;
|
|
185
|
+
lastElementChild.destroy(true);
|
|
143
186
|
this.lastElementChild.safeReplaceWith(lastChild);
|
|
144
187
|
}
|
|
145
188
|
|
|
146
|
-
/**
|
|
189
|
+
/**
|
|
190
|
+
* 设置语言变体
|
|
191
|
+
* @param {string} variant 语言变体
|
|
192
|
+
* @throws `RangeError` 无效的语言变体
|
|
193
|
+
*/
|
|
147
194
|
setVariant(variant) {
|
|
148
195
|
if (typeof variant !== 'string') {
|
|
149
196
|
this.typeError('setVariant', 'String');
|
|
@@ -154,16 +201,19 @@ class ConverterRuleToken extends Token {
|
|
|
154
201
|
throw new RangeError(`无效的语言变体:${v}`);
|
|
155
202
|
} else if (this.childNodes.length === 1) {
|
|
156
203
|
super.insertAt(Parser.run(() => new AtomToken(variant, 'converter-rule-variant', config)), 0);
|
|
157
|
-
this.setAttribute('bidirectional', true);
|
|
158
204
|
} else {
|
|
159
205
|
this.children.at(-2).setText(variant);
|
|
160
206
|
}
|
|
161
|
-
this.setAttribute('variant', v);
|
|
162
207
|
}
|
|
163
208
|
|
|
164
|
-
/**
|
|
209
|
+
/**
|
|
210
|
+
* 设置转换原文
|
|
211
|
+
* @param {string} from 转换原文
|
|
212
|
+
* @throws `Error` 尚未指定语言变体
|
|
213
|
+
* @throws `SyntaxError` 非法的转换原文
|
|
214
|
+
*/
|
|
165
215
|
setFrom(from) {
|
|
166
|
-
const {variant} = this;
|
|
216
|
+
const {variant, unidirectional} = this;
|
|
167
217
|
if (!variant) {
|
|
168
218
|
throw new Error('请先指定语言变体!');
|
|
169
219
|
}
|
|
@@ -176,23 +226,25 @@ class ConverterRuleToken extends Token {
|
|
|
176
226
|
) {
|
|
177
227
|
throw new SyntaxError(`非法的转换原文:${noWrap(from)}`);
|
|
178
228
|
}
|
|
179
|
-
if (
|
|
229
|
+
if (unidirectional) {
|
|
180
230
|
this.firstElementChild.safeReplaceWith(firstElementChild.lastElementChild.firstChild);
|
|
181
231
|
} else {
|
|
182
232
|
super.insertAt(firstElementChild.lastElementChild.firstChild, 0);
|
|
183
|
-
this.setAttribute('unidirectional', true).setAttribute('bidirectional', false);
|
|
184
233
|
}
|
|
185
234
|
}
|
|
186
235
|
|
|
187
|
-
/**
|
|
236
|
+
/**
|
|
237
|
+
* 修改为单向转换
|
|
238
|
+
* @param {string} from 转换来源
|
|
239
|
+
*/
|
|
188
240
|
makeUnidirectional(from) {
|
|
189
241
|
this.setFrom(from);
|
|
190
242
|
}
|
|
191
243
|
|
|
244
|
+
/** 修改为双向转换 */
|
|
192
245
|
makeBidirectional() {
|
|
193
246
|
if (this.unidirectional) {
|
|
194
247
|
super.removeAt(0);
|
|
195
|
-
this.setAttribute('unidirectional', false).setAttribute('bidirectional', true);
|
|
196
248
|
}
|
|
197
249
|
}
|
|
198
250
|
}
|
package/src/extLink.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {noWrap, normalizeSpace} = require('../util/string'),
|
|
4
|
-
|
|
4
|
+
Parser = require('..'),
|
|
5
5
|
Token = require('.'),
|
|
6
6
|
MagicLinkToken = require('./magicLink');
|
|
7
7
|
|
|
@@ -13,19 +13,42 @@ class ExtLinkToken extends Token {
|
|
|
13
13
|
type = 'ext-link';
|
|
14
14
|
#space;
|
|
15
15
|
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* 协议
|
|
18
|
+
* @this {{firstChild: MagicLinkToken}}
|
|
19
|
+
*/
|
|
17
20
|
get protocol() {
|
|
18
21
|
return this.firstChild.protocol;
|
|
19
22
|
}
|
|
23
|
+
|
|
20
24
|
/** @this {{firstChild: MagicLinkToken}} */
|
|
21
25
|
set protocol(value) {
|
|
22
26
|
this.firstChild.protocol = value;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
/**
|
|
26
|
-
*
|
|
27
|
-
* @
|
|
28
|
-
|
|
30
|
+
* 和内链保持一致
|
|
31
|
+
* @this {{firstChild: MagicLinkToken}}
|
|
32
|
+
*/
|
|
33
|
+
get link() {
|
|
34
|
+
return this.firstChild.link;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
set link(url) {
|
|
38
|
+
this.setTarget(url);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 链接显示文字 */
|
|
42
|
+
get innerText() {
|
|
43
|
+
return this.childNodes.length > 1
|
|
44
|
+
? this.lastElementChild.text()
|
|
45
|
+
: `[${this.getRootNode().querySelectorAll('ext-link[childElementCount=1]').indexOf(this) + 1}]`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} url 网址
|
|
50
|
+
* @param {string} space 空白字符
|
|
51
|
+
* @param {string} text 链接文字
|
|
29
52
|
* @param {accum} accum
|
|
30
53
|
*/
|
|
31
54
|
constructor(url, space, text, config = Parser.getConfig(), accum = []) {
|
|
@@ -37,11 +60,12 @@ class ExtLinkToken extends Token {
|
|
|
37
60
|
inner.type = 'ext-link-text';
|
|
38
61
|
this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
|
|
39
62
|
}
|
|
40
|
-
this.protectChildren(0);
|
|
63
|
+
this.getAttribute('protectChildren')(0);
|
|
41
64
|
}
|
|
42
65
|
|
|
66
|
+
/** @override */
|
|
43
67
|
cloneNode() {
|
|
44
|
-
const [url, text] = this.
|
|
68
|
+
const [url, text] = this.cloneChildNodes(),
|
|
45
69
|
token = Parser.run(() => new ExtLinkToken(undefined, '', '', this.getAttribute('config')));
|
|
46
70
|
token.firstElementChild.safeReplaceWith(url);
|
|
47
71
|
if (text) {
|
|
@@ -50,40 +74,61 @@ class ExtLinkToken extends Token {
|
|
|
50
74
|
return token;
|
|
51
75
|
}
|
|
52
76
|
|
|
77
|
+
/** 修正空白字符 */
|
|
53
78
|
#correct() {
|
|
54
79
|
if (!this.#space && this.childNodes.length > 1
|
|
55
80
|
// 都替换成`<`肯定不对,但无妨
|
|
56
|
-
&& /^[^[\]<>"{\
|
|
81
|
+
&& /^[^[\]<>"{\0-\x1F\x7F\p{Zs}\uFFFD]/u.test(this.lastElementChild.text().replace(/&[lg]t;/u, '<'))
|
|
57
82
|
) {
|
|
58
83
|
this.#space = ' ';
|
|
59
84
|
}
|
|
60
85
|
}
|
|
61
86
|
|
|
62
|
-
|
|
87
|
+
/**
|
|
88
|
+
* @override
|
|
89
|
+
* @param {string} selector
|
|
90
|
+
*/
|
|
91
|
+
toString(selector) {
|
|
92
|
+
if (selector && this.matches(selector)) {
|
|
93
|
+
return '';
|
|
94
|
+
} else if (this.childNodes.length === 1) {
|
|
95
|
+
return `[${super.toString(selector)}${this.#space}]`;
|
|
96
|
+
}
|
|
63
97
|
this.#correct();
|
|
64
|
-
|
|
98
|
+
normalizeSpace(this.lastElementChild);
|
|
99
|
+
return `[${super.toString(selector, this.#space)}]`;
|
|
65
100
|
}
|
|
66
101
|
|
|
102
|
+
/** @override */
|
|
67
103
|
getPadding() {
|
|
68
|
-
this.#correct();
|
|
69
104
|
return 1;
|
|
70
105
|
}
|
|
71
106
|
|
|
107
|
+
/** @override */
|
|
72
108
|
getGaps() {
|
|
73
109
|
this.#correct();
|
|
74
110
|
return this.#space.length;
|
|
75
111
|
}
|
|
76
112
|
|
|
113
|
+
/** @override */
|
|
77
114
|
text() {
|
|
78
|
-
|
|
115
|
+
normalizeSpace(this.children[1]);
|
|
116
|
+
return `[${super.text(' ')}]`;
|
|
79
117
|
}
|
|
80
118
|
|
|
81
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* 获取网址
|
|
121
|
+
* @this {{firstChild: MagicLinkToken}}
|
|
122
|
+
*/
|
|
82
123
|
getUrl() {
|
|
83
|
-
return this.
|
|
124
|
+
return this.firstChild.getUrl();
|
|
84
125
|
}
|
|
85
126
|
|
|
86
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* 设置链接目标
|
|
129
|
+
* @param {string|URL} url 网址
|
|
130
|
+
* @throws `SyntaxError` 非法的外链目标
|
|
131
|
+
*/
|
|
87
132
|
setTarget(url) {
|
|
88
133
|
url = String(url);
|
|
89
134
|
const root = Parser.parse(`[${url}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
@@ -92,12 +137,15 @@ class ExtLinkToken extends Token {
|
|
|
92
137
|
throw new SyntaxError(`非法的外链目标:${url}`);
|
|
93
138
|
}
|
|
94
139
|
const {firstChild} = firstElementChild;
|
|
95
|
-
|
|
96
|
-
firstElementChild.destroy();
|
|
140
|
+
firstElementChild.destroy(true);
|
|
97
141
|
this.firstElementChild.safeReplaceWith(firstChild);
|
|
98
142
|
}
|
|
99
143
|
|
|
100
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* 设置链接显示文字
|
|
146
|
+
* @param {string} text 链接显示文字
|
|
147
|
+
* @throws `SyntaxError` 非法的链接显示文字
|
|
148
|
+
*/
|
|
101
149
|
setLinkText(text) {
|
|
102
150
|
text = String(text);
|
|
103
151
|
const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|