wikiparser-node 0.4.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 +97 -65
- package/lib/element.js +159 -302
- package/lib/node.js +384 -198
- package/lib/ranges.js +3 -4
- package/lib/text.js +65 -36
- package/lib/title.js +9 -8
- package/mixin/fixedToken.js +4 -4
- package/mixin/hidden.js +2 -0
- package/mixin/sol.js +16 -7
- package/package.json +14 -3
- package/parser/brackets.js +8 -2
- package/parser/commentAndExt.js +1 -1
- package/parser/converter.js +1 -1
- package/parser/externalLinks.js +2 -2
- package/parser/hrAndDoubleUnderscore.js +8 -7
- package/parser/links.js +8 -9
- package/parser/magicLinks.js +1 -1
- package/parser/selector.js +5 -5
- package/parser/table.js +18 -16
- package/src/arg.js +71 -42
- package/src/atom/index.js +7 -5
- package/src/attribute.js +102 -64
- package/src/charinsert.js +91 -0
- package/src/converter.js +34 -15
- package/src/converterFlags.js +87 -40
- package/src/converterRule.js +59 -53
- package/src/extLink.js +45 -37
- package/src/gallery.js +71 -16
- package/src/hasNowiki/index.js +42 -0
- package/src/hasNowiki/pre.js +40 -0
- package/src/heading.js +41 -18
- package/src/html.js +76 -48
- package/src/imageParameter.js +73 -51
- package/src/imagemap.js +205 -0
- package/src/imagemapLink.js +43 -0
- package/src/index.js +243 -138
- package/src/link/category.js +10 -14
- package/src/link/file.js +112 -56
- package/src/link/galleryImage.js +74 -10
- package/src/link/index.js +86 -61
- package/src/magicLink.js +48 -21
- 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 +18 -4
- package/src/nowiki/dd.js +2 -2
- package/src/nowiki/doubleUnderscore.js +16 -11
- package/src/nowiki/index.js +12 -0
- package/src/nowiki/quote.js +28 -1
- package/src/onlyinclude.js +15 -8
- package/src/paramTag/index.js +83 -0
- package/src/paramTag/inputbox.js +42 -0
- package/src/parameter.js +73 -46
- package/src/syntax.js +9 -1
- package/src/table/index.js +58 -44
- package/src/table/td.js +63 -63
- package/src/table/tr.js +52 -35
- package/src/tagPair/ext.js +60 -43
- package/src/tagPair/include.js +11 -1
- package/src/tagPair/index.js +29 -20
- package/src/transclude.js +214 -166
- package/tool/index.js +720 -439
- package/util/base.js +17 -0
- package/util/debug.js +1 -1
- package/{test/util.js → util/diff.js} +15 -19
- package/util/lint.js +40 -0
- package/util/string.js +37 -20
- package/.eslintrc.json +0 -714
- package/errors/README +0 -1
- package/jsconfig.json +0 -7
- package/printed/README +0 -1
- package/printed/example.json +0 -120
- package/test/api.js +0 -83
- package/test/real.js +0 -133
- package/test/test.js +0 -28
- package/typings/api.d.ts +0 -13
- package/typings/array.d.ts +0 -28
- package/typings/event.d.ts +0 -24
- package/typings/index.d.ts +0 -94
- package/typings/node.d.ts +0 -29
- package/typings/parser.d.ts +0 -16
- package/typings/table.d.ts +0 -14
- package/typings/token.d.ts +0 -22
- package/typings/tool.d.ts +0 -11
package/src/converterFlags.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const {generateForChild} = require('../util/lint'),
|
|
4
|
+
Parser = require('..'),
|
|
4
5
|
Token = require('.'),
|
|
5
6
|
AtomToken = require('./atom');
|
|
6
7
|
|
|
@@ -21,21 +22,12 @@ class ConverterFlagsToken extends Token {
|
|
|
21
22
|
this.append(...flags.map(flag => new AtomToken(flag, 'converter-flag', config, accum)));
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
/** @override */
|
|
25
|
-
cloneNode() {
|
|
26
|
-
const cloned = this.cloneChildNodes(),
|
|
27
|
-
token = Parser.run(() => new ConverterFlagsToken([], this.getAttribute('config')));
|
|
28
|
-
token.append(...cloned);
|
|
29
|
-
token.afterBuild();
|
|
30
|
-
return token;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
25
|
/**
|
|
34
26
|
* @override
|
|
35
27
|
* @complexity `n`
|
|
36
28
|
*/
|
|
37
29
|
afterBuild() {
|
|
38
|
-
this.#flags = this.
|
|
30
|
+
this.#flags = this.childNodes.map(child => child.text().trim());
|
|
39
31
|
const /** @type {AstListener} */ converterFlagsListener = ({prevTarget}) => {
|
|
40
32
|
if (prevTarget) {
|
|
41
33
|
this.#flags[this.childNodes.indexOf(prevTarget)] = prevTarget.text().trim();
|
|
@@ -45,6 +37,75 @@ class ConverterFlagsToken extends Token {
|
|
|
45
37
|
return this;
|
|
46
38
|
}
|
|
47
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @override
|
|
42
|
+
* @param {string} selector
|
|
43
|
+
*/
|
|
44
|
+
toString(selector) {
|
|
45
|
+
return super.toString(selector, ';');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** @override */
|
|
49
|
+
getGaps() {
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** @override */
|
|
54
|
+
print() {
|
|
55
|
+
return super.print({sep: ';'});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @override
|
|
60
|
+
* @param {number} start 起始位置
|
|
61
|
+
*/
|
|
62
|
+
lint(start = 0) {
|
|
63
|
+
const variantFlags = this.getVariantFlags(),
|
|
64
|
+
unknownFlags = this.getUnknownFlags(),
|
|
65
|
+
emptyFlags = this.#flags.filter(flag => !flag),
|
|
66
|
+
validFlags = this.#flags.filter(flag => ['A', 'T', 'R', 'D', '-', 'H', 'N'].includes(flag)),
|
|
67
|
+
knownFlagCount = this.#flags.length - unknownFlags.length - emptyFlags,
|
|
68
|
+
errors = super.lint(start);
|
|
69
|
+
if (variantFlags.length === knownFlagCount || validFlags.length === knownFlagCount) {
|
|
70
|
+
return errors;
|
|
71
|
+
}
|
|
72
|
+
const rect = this.getRootNode().posFromIndex(start);
|
|
73
|
+
for (const child of this.childNodes) {
|
|
74
|
+
const flag = child.text().trim();
|
|
75
|
+
if (flag && !variantFlags.includes(flag) && !unknownFlags.includes(flag)
|
|
76
|
+
&& (variantFlags.length > 0 || !validFlags.includes(flag))
|
|
77
|
+
) {
|
|
78
|
+
errors.push(generateForChild(child, rect, '无效的转换标记'));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return errors;
|
|
82
|
+
}
|
|
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
|
+
|
|
98
|
+
/** @override */
|
|
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
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
48
109
|
/**
|
|
49
110
|
* @override
|
|
50
111
|
* @template {string} T
|
|
@@ -58,6 +119,14 @@ class ConverterFlagsToken extends Token {
|
|
|
58
119
|
return super.getAttribute(key);
|
|
59
120
|
}
|
|
60
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @override
|
|
124
|
+
* @param {PropertyKey} key 属性键
|
|
125
|
+
*/
|
|
126
|
+
hasAttribute(key) {
|
|
127
|
+
return key === 'flags' || super.hasAttribute(key);
|
|
128
|
+
}
|
|
129
|
+
|
|
61
130
|
/**
|
|
62
131
|
* @override
|
|
63
132
|
* @param {number} i 移除位置
|
|
@@ -81,45 +150,24 @@ class ConverterFlagsToken extends Token {
|
|
|
81
150
|
return token;
|
|
82
151
|
}
|
|
83
152
|
|
|
84
|
-
/**
|
|
85
|
-
* @override
|
|
86
|
-
* @param {string} selector
|
|
87
|
-
*/
|
|
88
|
-
toString(selector) {
|
|
89
|
-
return super.toString(selector, ';');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** @override */
|
|
93
|
-
getGaps() {
|
|
94
|
-
return 1;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
153
|
/** @override */
|
|
98
154
|
text() {
|
|
99
155
|
return super.text(';');
|
|
100
156
|
}
|
|
101
157
|
|
|
102
|
-
/**
|
|
103
|
-
* 获取转换类型标记节点
|
|
104
|
-
* @param {string} flag 转换类型标记
|
|
105
|
-
* @returns {AtomToken[]}
|
|
106
|
-
* @complexity `n`
|
|
107
|
-
*/
|
|
108
|
-
getFlagToken(flag) {
|
|
109
|
-
return this.#flags.includes(flag) ? this.children.filter(child => child.text().trim() === flag) : [];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
158
|
/** 获取所有转换类型标记 */
|
|
113
159
|
getAllFlags() {
|
|
114
160
|
return new Set(this.#flags);
|
|
115
161
|
}
|
|
116
162
|
|
|
117
163
|
/**
|
|
118
|
-
*
|
|
164
|
+
* 获取转换类型标记节点
|
|
165
|
+
* @param {string} flag 转换类型标记
|
|
166
|
+
* @returns {AtomToken[]}
|
|
119
167
|
* @complexity `n`
|
|
120
168
|
*/
|
|
121
|
-
|
|
122
|
-
return this.#flags.
|
|
169
|
+
getFlagToken(flag) {
|
|
170
|
+
return this.#flags.includes(flag) ? this.childNodes.filter(child => child.text().trim() === flag) : [];
|
|
123
171
|
}
|
|
124
172
|
|
|
125
173
|
/**
|
|
@@ -127,8 +175,7 @@ class ConverterFlagsToken extends Token {
|
|
|
127
175
|
* @complexity `n`
|
|
128
176
|
*/
|
|
129
177
|
getEffectiveFlags() {
|
|
130
|
-
const
|
|
131
|
-
variantFlags = this.#flags.filter(flag => variants.includes(flag)),
|
|
178
|
+
const variantFlags = this.getVariantFlags(),
|
|
132
179
|
unknownFlags = this.getUnknownFlags();
|
|
133
180
|
if (variantFlags.length > 0) {
|
|
134
181
|
return new Set([...variantFlags, ...unknownFlags]);
|
|
@@ -198,7 +245,7 @@ class ConverterFlagsToken extends Token {
|
|
|
198
245
|
*/
|
|
199
246
|
#newFlag(flag) {
|
|
200
247
|
const token = Parser.run(() => new AtomToken(flag, 'converter-flag', this.getAttribute('config')));
|
|
201
|
-
this.
|
|
248
|
+
this.insertAt(token);
|
|
202
249
|
}
|
|
203
250
|
|
|
204
251
|
/**
|
package/src/converterRule.js
CHANGED
|
@@ -15,7 +15,7 @@ class ConverterRuleToken extends Token {
|
|
|
15
15
|
|
|
16
16
|
/** 语言变体 */
|
|
17
17
|
get variant() {
|
|
18
|
-
return this.
|
|
18
|
+
return this.childNodes.at(-2)?.text()?.trim() ?? '';
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
set variant(variant) {
|
|
@@ -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,36 +155,13 @@ 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 {children: [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
158
|
/**
|
|
148
159
|
* @override
|
|
149
160
|
* @returns {string}
|
|
150
161
|
*/
|
|
151
162
|
text() {
|
|
152
163
|
if (this.childNodes.length === 3) {
|
|
153
|
-
const {
|
|
164
|
+
const {childNodes: [from, variant, to]} = this;
|
|
154
165
|
return `${from.text()}=>${variant.text()}:${to.text()}`;
|
|
155
166
|
}
|
|
156
167
|
return super.text(':');
|
|
@@ -158,11 +169,10 @@ class ConverterRuleToken extends Token {
|
|
|
158
169
|
|
|
159
170
|
/** 修改为不转换 */
|
|
160
171
|
noConvert() {
|
|
161
|
-
const {
|
|
172
|
+
const {length} = this;
|
|
162
173
|
for (let i = 0; i < length - 1; i++) { // ConverterRuleToken只能从前往后删除子节点
|
|
163
174
|
this.removeAt(0);
|
|
164
175
|
}
|
|
165
|
-
lastChild.type = 'converter-rule-noconvert';
|
|
166
176
|
}
|
|
167
177
|
|
|
168
178
|
/**
|
|
@@ -174,16 +184,14 @@ class ConverterRuleToken extends Token {
|
|
|
174
184
|
to = String(to);
|
|
175
185
|
const config = this.getAttribute('config'),
|
|
176
186
|
root = Parser.parse(`-{|${config.variants[0]}:${to}}-`, this.getAttribute('include'), undefined, config),
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
) {
|
|
187
|
+
{length, firstChild: converter} = root,
|
|
188
|
+
{lastChild: converterRule, type, length: converterLength} = converter;
|
|
189
|
+
if (length !== 1 || type !== 'converter' || converterLength !== 2 || converterRule.childNodes.length !== 2) {
|
|
181
190
|
throw new SyntaxError(`非法的转换目标:${noWrap(to)}`);
|
|
182
191
|
}
|
|
183
|
-
const {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
this.lastElementChild.safeReplaceWith(lastChild);
|
|
192
|
+
const {lastChild} = converterRule;
|
|
193
|
+
converterRule.destroy(true);
|
|
194
|
+
this.lastChild.safeReplaceWith(lastChild);
|
|
187
195
|
}
|
|
188
196
|
|
|
189
197
|
/**
|
|
@@ -202,7 +210,7 @@ class ConverterRuleToken extends Token {
|
|
|
202
210
|
} else if (this.childNodes.length === 1) {
|
|
203
211
|
super.insertAt(Parser.run(() => new AtomToken(variant, 'converter-rule-variant', config)), 0);
|
|
204
212
|
} else {
|
|
205
|
-
this.
|
|
213
|
+
this.childNodes.at(-2).setText(variant);
|
|
206
214
|
}
|
|
207
215
|
}
|
|
208
216
|
|
|
@@ -220,16 +228,14 @@ class ConverterRuleToken extends Token {
|
|
|
220
228
|
from = String(from);
|
|
221
229
|
const config = this.getAttribute('config'),
|
|
222
230
|
root = Parser.parse(`-{|${from}=>${variant}:}-`, this.getAttribute('include'), undefined, config),
|
|
223
|
-
{
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
) {
|
|
231
|
+
{length, firstChild: converter} = root,
|
|
232
|
+
{type, length: converterLength, lastChild: converterRule} = converter;
|
|
233
|
+
if (length !== 1 || type !== 'converter' || converterLength !== 2 || converterRule.childNodes.length !== 3) {
|
|
227
234
|
throw new SyntaxError(`非法的转换原文:${noWrap(from)}`);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
this.firstElementChild.safeReplaceWith(firstElementChild.lastElementChild.firstChild);
|
|
235
|
+
} else if (unidirectional) {
|
|
236
|
+
this.firstChild.safeReplaceWith(converterRule.firstChild);
|
|
231
237
|
} else {
|
|
232
|
-
super.insertAt(
|
|
238
|
+
super.insertAt(converterRule.firstChild, 0);
|
|
233
239
|
}
|
|
234
240
|
}
|
|
235
241
|
|
|
@@ -244,7 +250,7 @@ class ConverterRuleToken extends Token {
|
|
|
244
250
|
/** 修改为双向转换 */
|
|
245
251
|
makeBidirectional() {
|
|
246
252
|
if (this.unidirectional) {
|
|
247
|
-
|
|
253
|
+
this.removeAt(0);
|
|
248
254
|
}
|
|
249
255
|
}
|
|
250
256
|
}
|
package/src/extLink.js
CHANGED
|
@@ -41,7 +41,7 @@ class ExtLinkToken extends Token {
|
|
|
41
41
|
/** 链接显示文字 */
|
|
42
42
|
get innerText() {
|
|
43
43
|
return this.childNodes.length > 1
|
|
44
|
-
? this.
|
|
44
|
+
? this.lastChild.text()
|
|
45
45
|
: `[${this.getRootNode().querySelectorAll('ext-link[childElementCount=1]').indexOf(this) + 1}]`;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -52,38 +52,17 @@ class ExtLinkToken extends Token {
|
|
|
52
52
|
* @param {accum} accum
|
|
53
53
|
*/
|
|
54
54
|
constructor(url, space, text, config = Parser.getConfig(), accum = []) {
|
|
55
|
-
super(undefined, config, true, accum, {
|
|
56
|
-
this.
|
|
55
|
+
super(undefined, config, true, accum, {MagicLinkToken: 0, Token: 1});
|
|
56
|
+
this.insertAt(new MagicLinkToken(url, true, config, accum));
|
|
57
57
|
this.#space = space;
|
|
58
58
|
if (text) {
|
|
59
59
|
const inner = new Token(text, config, true, accum, {'Stage-7': ':', ConverterToken: ':'});
|
|
60
60
|
inner.type = 'ext-link-text';
|
|
61
|
-
this.
|
|
61
|
+
this.insertAt(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
|
|
62
62
|
}
|
|
63
63
|
this.getAttribute('protectChildren')(0);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
/** @override */
|
|
67
|
-
cloneNode() {
|
|
68
|
-
const [url, text] = this.cloneChildNodes(),
|
|
69
|
-
token = Parser.run(() => new ExtLinkToken(undefined, '', '', this.getAttribute('config')));
|
|
70
|
-
token.firstElementChild.safeReplaceWith(url);
|
|
71
|
-
if (text) {
|
|
72
|
-
token.appendChild(text);
|
|
73
|
-
}
|
|
74
|
-
return token;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** 修正空白字符 */
|
|
78
|
-
#correct() {
|
|
79
|
-
if (!this.#space && this.childNodes.length > 1
|
|
80
|
-
// 都替换成`<`肯定不对,但无妨
|
|
81
|
-
&& /^[^[\]<>"{\0-\x1F\x7F\p{Zs}\uFFFD]/u.test(this.lastElementChild.text().replace(/&[lg]t;/u, '<'))
|
|
82
|
-
) {
|
|
83
|
-
this.#space = ' ';
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
66
|
/**
|
|
88
67
|
* @override
|
|
89
68
|
* @param {string} selector
|
|
@@ -95,7 +74,7 @@ class ExtLinkToken extends Token {
|
|
|
95
74
|
return `[${super.toString(selector)}${this.#space}]`;
|
|
96
75
|
}
|
|
97
76
|
this.#correct();
|
|
98
|
-
normalizeSpace(this.
|
|
77
|
+
normalizeSpace(this.lastChild);
|
|
99
78
|
return `[${super.toString(selector, this.#space)}]`;
|
|
100
79
|
}
|
|
101
80
|
|
|
@@ -110,9 +89,38 @@ class ExtLinkToken extends Token {
|
|
|
110
89
|
return this.#space.length;
|
|
111
90
|
}
|
|
112
91
|
|
|
92
|
+
/** @override */
|
|
93
|
+
print() {
|
|
94
|
+
const {length} = this;
|
|
95
|
+
return super.print(length > 1 ? {pre: '[', sep: this.#space, post: ']'} : {pre: '[', post: `${this.#space}]`});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @override */
|
|
99
|
+
cloneNode() {
|
|
100
|
+
const [url, text] = this.cloneChildNodes();
|
|
101
|
+
return Parser.run(() => {
|
|
102
|
+
const token = new ExtLinkToken(undefined, '', '', this.getAttribute('config'));
|
|
103
|
+
token.firstChild.safeReplaceWith(url);
|
|
104
|
+
if (text) {
|
|
105
|
+
token.insertAt(text);
|
|
106
|
+
}
|
|
107
|
+
return token;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** 修正空白字符 */
|
|
112
|
+
#correct() {
|
|
113
|
+
if (!this.#space && this.childNodes.length > 1
|
|
114
|
+
// 都替换成`<`肯定不对,但无妨
|
|
115
|
+
&& /^[^[\]<>"{\0-\x1F\x7F\p{Zs}\uFFFD]/u.test(this.lastChild.text().replace(/&[lg]t;/u, '<'))
|
|
116
|
+
) {
|
|
117
|
+
this.#space = ' ';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
113
121
|
/** @override */
|
|
114
122
|
text() {
|
|
115
|
-
normalizeSpace(this.
|
|
123
|
+
normalizeSpace(this.childNodes[1]);
|
|
116
124
|
return `[${super.text(' ')}]`;
|
|
117
125
|
}
|
|
118
126
|
|
|
@@ -132,13 +140,13 @@ class ExtLinkToken extends Token {
|
|
|
132
140
|
setTarget(url) {
|
|
133
141
|
url = String(url);
|
|
134
142
|
const root = Parser.parse(`[${url}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
135
|
-
{
|
|
136
|
-
if (length !== 1 ||
|
|
143
|
+
{length, firstChild: extLink} = root;
|
|
144
|
+
if (length !== 1 || extLink.type !== 'ext-link' || extLink.childNodes.length !== 1) {
|
|
137
145
|
throw new SyntaxError(`非法的外链目标:${url}`);
|
|
138
146
|
}
|
|
139
|
-
const {firstChild} =
|
|
140
|
-
|
|
141
|
-
this.
|
|
147
|
+
const {firstChild} = extLink;
|
|
148
|
+
extLink.destroy(true);
|
|
149
|
+
this.firstChild.safeReplaceWith(firstChild);
|
|
142
150
|
}
|
|
143
151
|
|
|
144
152
|
/**
|
|
@@ -149,15 +157,15 @@ class ExtLinkToken extends Token {
|
|
|
149
157
|
setLinkText(text) {
|
|
150
158
|
text = String(text);
|
|
151
159
|
const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
152
|
-
{
|
|
153
|
-
if (length !== 1 ||
|
|
160
|
+
{length, firstChild: extLink} = root;
|
|
161
|
+
if (length !== 1 || extLink.type !== 'ext-link' || extLink.childNodes.length !== 2) {
|
|
154
162
|
throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
|
|
155
163
|
}
|
|
156
|
-
const {lastChild} =
|
|
164
|
+
const {lastChild} = extLink;
|
|
157
165
|
if (this.childNodes.length === 1) {
|
|
158
|
-
this.
|
|
166
|
+
this.insertAt(lastChild);
|
|
159
167
|
} else {
|
|
160
|
-
this.
|
|
168
|
+
this.lastChild.safeReplaceWith(lastChild);
|
|
161
169
|
}
|
|
162
170
|
this.#space ||= ' ';
|
|
163
171
|
}
|
package/src/gallery.js
CHANGED
|
@@ -13,16 +13,23 @@ class GalleryToken extends Token {
|
|
|
13
13
|
type = 'ext-inner';
|
|
14
14
|
name = 'gallery';
|
|
15
15
|
|
|
16
|
+
/** 所有图片 */
|
|
17
|
+
get images() {
|
|
18
|
+
return this.childNodes.filter(({type}) => type === 'gallery-image');
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
/**
|
|
17
22
|
* @param {string} inner 标签内部wikitext
|
|
18
23
|
* @param {accum} accum
|
|
19
24
|
*/
|
|
20
25
|
constructor(inner, config = Parser.getConfig(), accum = []) {
|
|
21
|
-
super(undefined, config, true, accum, {AstText: ':', GalleryImageToken: ':'});
|
|
26
|
+
super(undefined, config, true, accum, {AstText: ':', GalleryImageToken: ':', HiddenToken: ':'});
|
|
27
|
+
const newConfig = structuredClone(config);
|
|
28
|
+
newConfig.img = Object.fromEntries(Object.entries(config.img).filter(([, param]) => param !== 'width'));
|
|
22
29
|
for (const line of inner?.split('\n') ?? []) {
|
|
23
30
|
const matches = /^([^|]+)(?:\|(.*))?/u.exec(line);
|
|
24
31
|
if (!matches) {
|
|
25
|
-
|
|
32
|
+
super.insertAt(line.trim() ? new HiddenToken(line, undefined, config, [], {AstText: ':'}) : line);
|
|
26
33
|
continue;
|
|
27
34
|
}
|
|
28
35
|
const [, file, alt] = matches;
|
|
@@ -33,21 +40,13 @@ class GalleryToken extends Token {
|
|
|
33
40
|
title = this.normalizeTitle(file, 6, true);
|
|
34
41
|
}
|
|
35
42
|
if (title.valid) {
|
|
36
|
-
|
|
43
|
+
super.insertAt(new GalleryImageToken(file, alt, title, newConfig, accum));
|
|
37
44
|
} else {
|
|
38
|
-
|
|
45
|
+
super.insertAt(new HiddenToken(line, undefined, config, [], {AstText: ':'}));
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
/** @override */
|
|
44
|
-
cloneNode() {
|
|
45
|
-
const cloned = this.cloneChildNodes(),
|
|
46
|
-
token = Parser.run(() => new GalleryToken(undefined, this.getAttribute('config')));
|
|
47
|
-
token.append(...cloned);
|
|
48
|
-
return token;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
50
|
/**
|
|
52
51
|
* @override
|
|
53
52
|
* @param {string} selector
|
|
@@ -61,6 +60,48 @@ class GalleryToken extends Token {
|
|
|
61
60
|
return 1;
|
|
62
61
|
}
|
|
63
62
|
|
|
63
|
+
/** @override */
|
|
64
|
+
print() {
|
|
65
|
+
return super.print({sep: '\n'});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @override
|
|
70
|
+
* @param {number} start 起始位置
|
|
71
|
+
*/
|
|
72
|
+
lint(start = 0) {
|
|
73
|
+
const {top, left} = this.getRootNode().posFromIndex(start),
|
|
74
|
+
/** @type {LintError[]} */ errors = [];
|
|
75
|
+
for (let i = 0, cur = start; i < this.childNodes.length; i++) {
|
|
76
|
+
const child = this.childNodes[i],
|
|
77
|
+
str = String(child),
|
|
78
|
+
trimmed = str.trim();
|
|
79
|
+
if (child.type === 'hidden' && trimmed && !/^<!--.*-->$/u.test(trimmed)) {
|
|
80
|
+
errors.push({
|
|
81
|
+
message: '图库中的无效内容',
|
|
82
|
+
startLine: top + i,
|
|
83
|
+
endLine: top + i,
|
|
84
|
+
startCol: i ? 0 : left,
|
|
85
|
+
endCol: i ? str.length : left + str.length,
|
|
86
|
+
});
|
|
87
|
+
} else if (child.type !== 'hidden' && child.type !== 'text') {
|
|
88
|
+
errors.push(...child.lint(cur));
|
|
89
|
+
}
|
|
90
|
+
cur += str.length + 1;
|
|
91
|
+
}
|
|
92
|
+
return errors;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @override */
|
|
96
|
+
cloneNode() {
|
|
97
|
+
const cloned = this.cloneChildNodes();
|
|
98
|
+
return Parser.run(() => {
|
|
99
|
+
const token = new GalleryToken(undefined, this.getAttribute('config'));
|
|
100
|
+
token.append(...cloned);
|
|
101
|
+
return token;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
64
105
|
/** @override */
|
|
65
106
|
text() {
|
|
66
107
|
return super.text('\n').replaceAll(/\n\s*\n/gu, '\n');
|
|
@@ -79,11 +120,25 @@ class GalleryToken extends Token {
|
|
|
79
120
|
} catch {
|
|
80
121
|
title = this.normalizeTitle(file, 6, true);
|
|
81
122
|
}
|
|
82
|
-
if (
|
|
83
|
-
|
|
123
|
+
if (title.valid) {
|
|
124
|
+
const token = Parser.run(() => new GalleryImageToken(file, undefined, title, this.getAttribute('config')));
|
|
125
|
+
return this.insertAt(token, i);
|
|
126
|
+
}
|
|
127
|
+
throw new SyntaxError(`非法的文件名:${file}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @override
|
|
132
|
+
* @template {string|Token} T
|
|
133
|
+
* @param {T} token 待插入的节点
|
|
134
|
+
* @param {number} i 插入位置
|
|
135
|
+
* @throws `RangeError` 插入不可见内容
|
|
136
|
+
*/
|
|
137
|
+
insertAt(token, i = 0) {
|
|
138
|
+
if (typeof token === 'string' && token.trim() || token instanceof HiddenToken) {
|
|
139
|
+
throw new RangeError('请勿向图库中插入不可见内容!');
|
|
84
140
|
}
|
|
85
|
-
|
|
86
|
-
return this.insertAt(token, i);
|
|
141
|
+
return super.insertAt(token, i);
|
|
87
142
|
}
|
|
88
143
|
}
|
|
89
144
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Parser = require('../..'),
|
|
4
|
+
Token = require('..');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `<pre>`
|
|
8
|
+
* @classdesc `{childNodes: [...AstText|NoincludeToken]}`
|
|
9
|
+
*/
|
|
10
|
+
class HasNowikiToken extends Token {
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} wikitext wikitext
|
|
13
|
+
* @param {string} type type
|
|
14
|
+
* @param {accum} accum
|
|
15
|
+
*/
|
|
16
|
+
constructor(wikitext, type, config = Parser.getConfig(), accum = []) {
|
|
17
|
+
const NoincludeToken = require('../nowiki/noinclude');
|
|
18
|
+
wikitext = wikitext.replaceAll(
|
|
19
|
+
/(<nowiki>)(.*?)(<\/nowiki>)/giu,
|
|
20
|
+
/** @type {function(...string): string} */ (_, opening, inner, closing) => {
|
|
21
|
+
new NoincludeToken(opening, config, accum);
|
|
22
|
+
new NoincludeToken(closing, config, accum);
|
|
23
|
+
return `\0${accum.length - 1}c\x7F${inner}\0${accum.length}c\x7F`;
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
super(wikitext, config, true, accum, {AstText: ':', NoincludeToken: ':'});
|
|
27
|
+
this.type = type;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** @override */
|
|
31
|
+
cloneNode() {
|
|
32
|
+
const cloned = this.cloneChildNodes();
|
|
33
|
+
return Parser.run(() => {
|
|
34
|
+
const token = new HasNowikiToken(undefined, this.type, this.getAttribute('config'));
|
|
35
|
+
token.append(...cloned);
|
|
36
|
+
return token;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Parser.classes.HasNowikiToken = __filename;
|
|
42
|
+
module.exports = HasNowikiToken;
|