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/extLink.js
CHANGED
|
@@ -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.firstChild.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.lastChild.text().replace(/&[lg]t;/u, '<'))
|
|
82
|
-
) {
|
|
83
|
-
this.#space = ' ';
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
66
|
/**
|
|
88
67
|
* @override
|
|
89
68
|
* @param {string} selector
|
|
@@ -112,10 +91,33 @@ class ExtLinkToken extends Token {
|
|
|
112
91
|
|
|
113
92
|
/** @override */
|
|
114
93
|
print() {
|
|
115
|
-
const {
|
|
94
|
+
const {length} = this;
|
|
116
95
|
return super.print(length > 1 ? {pre: '[', sep: this.#space, post: ']'} : {pre: '[', post: `${this.#space}]`});
|
|
117
96
|
}
|
|
118
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
|
+
|
|
119
121
|
/** @override */
|
|
120
122
|
text() {
|
|
121
123
|
normalizeSpace(this.childNodes[1]);
|
|
@@ -138,7 +140,7 @@ class ExtLinkToken extends Token {
|
|
|
138
140
|
setTarget(url) {
|
|
139
141
|
url = String(url);
|
|
140
142
|
const root = Parser.parse(`[${url}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
141
|
-
{
|
|
143
|
+
{length, firstChild: extLink} = root;
|
|
142
144
|
if (length !== 1 || extLink.type !== 'ext-link' || extLink.childNodes.length !== 1) {
|
|
143
145
|
throw new SyntaxError(`非法的外链目标:${url}`);
|
|
144
146
|
}
|
|
@@ -155,13 +157,13 @@ class ExtLinkToken extends Token {
|
|
|
155
157
|
setLinkText(text) {
|
|
156
158
|
text = String(text);
|
|
157
159
|
const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
158
|
-
{
|
|
160
|
+
{length, firstChild: extLink} = root;
|
|
159
161
|
if (length !== 1 || extLink.type !== 'ext-link' || extLink.childNodes.length !== 2) {
|
|
160
162
|
throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
|
|
161
163
|
}
|
|
162
164
|
const {lastChild} = extLink;
|
|
163
165
|
if (this.childNodes.length === 1) {
|
|
164
|
-
this.
|
|
166
|
+
this.insertAt(lastChild);
|
|
165
167
|
} else {
|
|
166
168
|
this.lastChild.safeReplaceWith(lastChild);
|
|
167
169
|
}
|
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
|
|
@@ -66,6 +65,43 @@ class GalleryToken extends Token {
|
|
|
66
65
|
return super.print({sep: '\n'});
|
|
67
66
|
}
|
|
68
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
|
+
|
|
69
105
|
/** @override */
|
|
70
106
|
text() {
|
|
71
107
|
return super.text('\n').replaceAll(/\n\s*\n/gu, '\n');
|
|
@@ -93,28 +129,16 @@ class GalleryToken extends Token {
|
|
|
93
129
|
|
|
94
130
|
/**
|
|
95
131
|
* @override
|
|
96
|
-
* @
|
|
132
|
+
* @template {string|Token} T
|
|
133
|
+
* @param {T} token 待插入的节点
|
|
134
|
+
* @param {number} i 插入位置
|
|
135
|
+
* @throws `RangeError` 插入不可见内容
|
|
97
136
|
*/
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
for (let i = 0, cur = start; i < this.childNodes.length; i++) {
|
|
102
|
-
const child = this.childNodes[i],
|
|
103
|
-
str = String(child);
|
|
104
|
-
if (child.type === 'hidden' && str.trim() && !/^<!--.*-->$/u.test(str)) {
|
|
105
|
-
errors.push({
|
|
106
|
-
message: '图库中的无效内容',
|
|
107
|
-
startLine: top + i,
|
|
108
|
-
endLine: top + i,
|
|
109
|
-
startCol: i ? 0 : left,
|
|
110
|
-
endCol: i ? str.length : left + str.length,
|
|
111
|
-
});
|
|
112
|
-
} else if (child.type !== 'hidden' && child.type !== 'text') {
|
|
113
|
-
errors.push(...child.lint(cur));
|
|
114
|
-
}
|
|
115
|
-
cur += str.length + 1;
|
|
137
|
+
insertAt(token, i = 0) {
|
|
138
|
+
if (typeof token === 'string' && token.trim() || token instanceof HiddenToken) {
|
|
139
|
+
throw new RangeError('请勿向图库中插入不可见内容!');
|
|
116
140
|
}
|
|
117
|
-
return
|
|
141
|
+
return super.insertAt(token, i);
|
|
118
142
|
}
|
|
119
143
|
}
|
|
120
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;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Parser = require('../..'),
|
|
4
|
+
HasNowikiToken = require('.');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `<pre>`
|
|
8
|
+
* @classdesc `{childNodes: [...AstText|NoincludeToken|ConverterToken]}`
|
|
9
|
+
*/
|
|
10
|
+
class PreToken extends HasNowikiToken {
|
|
11
|
+
name = 'pre';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} wikitext wikitext
|
|
15
|
+
* @param {accum} accum
|
|
16
|
+
*/
|
|
17
|
+
constructor(wikitext, config = Parser.getConfig(), accum = []) {
|
|
18
|
+
super(wikitext, 'ext-inner', config, accum);
|
|
19
|
+
this.setAttribute('stage', Parser.MAX_STAGE - 1);
|
|
20
|
+
this.setAttribute('acceptable', {AstText: ':', NoincludeToken: ':', ConverterToken: ':'});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @override */
|
|
24
|
+
isPlain() {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @override */
|
|
29
|
+
cloneNode() {
|
|
30
|
+
const cloned = this.cloneChildNodes();
|
|
31
|
+
return Parser.run(() => {
|
|
32
|
+
const token = new PreToken(undefined, this.getAttribute('config'));
|
|
33
|
+
token.append(...cloned);
|
|
34
|
+
return token;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Parser.classes.PreToken = __filename;
|
|
40
|
+
module.exports = PreToken;
|
package/src/heading.js
CHANGED
|
@@ -28,7 +28,8 @@ class HeadingToken extends fixedToken(sol(Token)) {
|
|
|
28
28
|
this.setAttribute('name', String(level));
|
|
29
29
|
const token = new Token(input[0], config, true, accum);
|
|
30
30
|
token.type = 'heading-title';
|
|
31
|
-
token.setAttribute('name', this.name)
|
|
31
|
+
token.setAttribute('name', this.name);
|
|
32
|
+
token.setAttribute('stage', 2);
|
|
32
33
|
const SyntaxToken = require('./syntax');
|
|
33
34
|
const trail = new SyntaxToken(input[1], /^[^\S\n]*$/u, 'heading-trail', config, accum, {
|
|
34
35
|
'Stage-1': ':', '!ExtToken': '',
|
|
@@ -36,19 +37,11 @@ class HeadingToken extends fixedToken(sol(Token)) {
|
|
|
36
37
|
this.append(token, trail);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
/** @override */
|
|
40
|
-
cloneNode() {
|
|
41
|
-
const [title, trail] = this.cloneChildNodes(),
|
|
42
|
-
token = Parser.run(() => new HeadingToken(Number(this.name), [], this.getAttribute('config')));
|
|
43
|
-
token.firsthild.safeReplaceWith(title);
|
|
44
|
-
token.lastChild.safeReplaceWith(trail);
|
|
45
|
-
return token;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
40
|
/**
|
|
49
41
|
* @override
|
|
50
42
|
* @this {{prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'} & HeadingToken}
|
|
51
43
|
* @param {string} selector
|
|
44
|
+
* @returns {string}
|
|
52
45
|
*/
|
|
53
46
|
toString(selector) {
|
|
54
47
|
const equals = '='.repeat(Number(this.name));
|
|
@@ -87,6 +80,17 @@ class HeadingToken extends fixedToken(sol(Token)) {
|
|
|
87
80
|
return errors;
|
|
88
81
|
}
|
|
89
82
|
|
|
83
|
+
/** @override */
|
|
84
|
+
cloneNode() {
|
|
85
|
+
const [title, trail] = this.cloneChildNodes();
|
|
86
|
+
return Parser.run(() => {
|
|
87
|
+
const token = new HeadingToken(Number(this.name), [], this.getAttribute('config'));
|
|
88
|
+
token.firsthild.safeReplaceWith(title);
|
|
89
|
+
token.lastChild.safeReplaceWith(trail);
|
|
90
|
+
return token;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
90
94
|
/**
|
|
91
95
|
* @override
|
|
92
96
|
* @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}}
|
|
@@ -102,7 +106,7 @@ class HeadingToken extends fixedToken(sol(Token)) {
|
|
|
102
106
|
* @param {number} n 标题层级
|
|
103
107
|
*/
|
|
104
108
|
setLevel(n) {
|
|
105
|
-
if (
|
|
109
|
+
if (!Number.isInteger(n)) {
|
|
106
110
|
this.typeError('setLevel', 'Number');
|
|
107
111
|
}
|
|
108
112
|
n = Math.min(Math.max(n, 1), 6);
|
package/src/html.js
CHANGED
|
@@ -28,11 +28,11 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
28
28
|
this.#closing = false;
|
|
29
29
|
return;
|
|
30
30
|
} else if (this.#selfClosing) {
|
|
31
|
-
throw new Error(
|
|
31
|
+
throw new Error('这是一个自闭合标签!');
|
|
32
32
|
}
|
|
33
33
|
const {html: [,, tags]} = this.getAttribute('config');
|
|
34
34
|
if (tags.includes(this.name)) {
|
|
35
|
-
throw new Error(
|
|
35
|
+
throw new Error('这是一个空标签!');
|
|
36
36
|
}
|
|
37
37
|
this.#closing = true;
|
|
38
38
|
}
|
|
@@ -66,30 +66,13 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
66
66
|
*/
|
|
67
67
|
constructor(name, attr, closing, selfClosing, config = Parser.getConfig(), accum = []) {
|
|
68
68
|
super(undefined, config, true, accum);
|
|
69
|
-
this.
|
|
69
|
+
this.insertAt(attr);
|
|
70
70
|
this.setAttribute('name', name.toLowerCase());
|
|
71
71
|
this.#closing = closing;
|
|
72
72
|
this.#selfClosing = selfClosing;
|
|
73
73
|
this.#tag = name;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
/** @override */
|
|
77
|
-
cloneNode() {
|
|
78
|
-
const [attr] = this.cloneChildNodes(),
|
|
79
|
-
config = this.getAttribute('config');
|
|
80
|
-
return Parser.run(() => new HtmlToken(this.#tag, attr, this.#closing, this.#selfClosing, config));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* @override
|
|
85
|
-
* @template {string} T
|
|
86
|
-
* @param {T} key 属性键
|
|
87
|
-
* @returns {TokenAttribute<T>}
|
|
88
|
-
*/
|
|
89
|
-
getAttribute(key) {
|
|
90
|
-
return key === 'tag' ? this.#tag : super.getAttribute(key);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
76
|
/**
|
|
94
77
|
* @override
|
|
95
78
|
* @param {string} selector
|
|
@@ -133,24 +116,6 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
133
116
|
return errors;
|
|
134
117
|
}
|
|
135
118
|
|
|
136
|
-
/** @override */
|
|
137
|
-
text() {
|
|
138
|
-
return `<${this.#closing ? '/' : ''}${this.#tag}${super.text()}${this.#selfClosing ? '/' : ''}>`;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* 更换标签名
|
|
143
|
-
* @param {string} tag 标签名
|
|
144
|
-
* @throws `RangeError` 非法的HTML标签
|
|
145
|
-
*/
|
|
146
|
-
replaceTag(tag) {
|
|
147
|
-
const name = tag.toLowerCase();
|
|
148
|
-
if (!this.getAttribute('config').html.flat().includes(name)) {
|
|
149
|
-
throw new RangeError(`非法的HTML标签:${tag}`);
|
|
150
|
-
}
|
|
151
|
-
this.setAttribute('name', name).#tag = tag;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
119
|
/**
|
|
155
120
|
* 搜索匹配的标签
|
|
156
121
|
* @complexity `n`
|
|
@@ -190,6 +155,41 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
190
155
|
throw new SyntaxError(`未${this.#closing ? '匹配的闭合' : '闭合的'}标签:${string}`);
|
|
191
156
|
}
|
|
192
157
|
|
|
158
|
+
/** @override */
|
|
159
|
+
cloneNode() {
|
|
160
|
+
const [attr] = this.cloneChildNodes(),
|
|
161
|
+
config = this.getAttribute('config');
|
|
162
|
+
return Parser.run(() => new HtmlToken(this.#tag, attr, this.#closing, this.#selfClosing, config));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @override
|
|
167
|
+
* @template {string} T
|
|
168
|
+
* @param {T} key 属性键
|
|
169
|
+
* @returns {TokenAttribute<T>}
|
|
170
|
+
*/
|
|
171
|
+
getAttribute(key) {
|
|
172
|
+
return key === 'tag' ? this.#tag : super.getAttribute(key);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** @override */
|
|
176
|
+
text() {
|
|
177
|
+
return `<${this.#closing ? '/' : ''}${this.#tag}${super.text()}${this.#selfClosing ? '/' : ''}>`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 更换标签名
|
|
182
|
+
* @param {string} tag 标签名
|
|
183
|
+
* @throws `RangeError` 非法的HTML标签
|
|
184
|
+
*/
|
|
185
|
+
replaceTag(tag) {
|
|
186
|
+
const name = tag.toLowerCase();
|
|
187
|
+
if (!this.getAttribute('config').html.flat().includes(name)) {
|
|
188
|
+
throw new RangeError(`非法的HTML标签:${tag}`);
|
|
189
|
+
}
|
|
190
|
+
this.setAttribute('name', name).#tag = tag;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
193
|
/** 局部闭合 */
|
|
194
194
|
#localMatch() {
|
|
195
195
|
this.#selfClosing = false;
|
package/src/imageParameter.js
CHANGED
|
@@ -21,30 +21,36 @@ class ImageParameterToken extends Token {
|
|
|
21
21
|
*/
|
|
22
22
|
static #validate(key, value, config = Parser.getConfig(), halfParsed = false) {
|
|
23
23
|
value = value.replaceAll(/\0\d+t\x7F/gu, '').trim();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
switch (key) {
|
|
25
|
+
case 'width':
|
|
26
|
+
return /^\d*(?:x\d*)?$/u.test(value);
|
|
27
|
+
case 'link': {
|
|
28
|
+
if (!value) {
|
|
29
|
+
return this.noLink;
|
|
30
|
+
}
|
|
31
|
+
const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\0\\d+t\x7F|$)`, 'iu');
|
|
32
|
+
if (regex.test(value)) {
|
|
33
|
+
return value;
|
|
34
|
+
} else if (value.startsWith('[[') && value.endsWith(']]')) {
|
|
35
|
+
value = value.slice(2, -2);
|
|
36
|
+
}
|
|
37
|
+
if (value.includes('%')) {
|
|
38
|
+
try {
|
|
39
|
+
value = decodeURIComponent(value);
|
|
40
|
+
} catch {}
|
|
41
|
+
}
|
|
42
|
+
const title = Parser.normalizeTitle(value, 0, false, config, halfParsed);
|
|
43
|
+
return title.valid && String(title);
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
case 'lang':
|
|
46
|
+
return config.variants.includes(value);
|
|
47
|
+
case 'alt':
|
|
48
|
+
case 'class':
|
|
49
|
+
case 'manualthumb':
|
|
50
|
+
return true;
|
|
51
|
+
default:
|
|
52
|
+
return !isNaN(value);
|
|
46
53
|
}
|
|
47
|
-
return !isNaN(value);
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
type = 'image-parameter';
|
|
@@ -146,35 +152,11 @@ class ImageParameterToken extends Token {
|
|
|
146
152
|
this.setAttribute('name', 'caption').setAttribute('stage', 7);
|
|
147
153
|
}
|
|
148
154
|
|
|
149
|
-
/** @override */
|
|
150
|
-
cloneNode() {
|
|
151
|
-
const cloned = this.cloneChildNodes(),
|
|
152
|
-
config = this.getAttribute('config'),
|
|
153
|
-
token = Parser.run(() => new ImageParameterToken(this.#syntax.replace('$1', ''), config));
|
|
154
|
-
token.replaceChildren(...cloned);
|
|
155
|
-
return token;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* @override
|
|
160
|
-
* @template {string} T
|
|
161
|
-
* @param {T} key 属性键
|
|
162
|
-
* @returns {TokenAttribute<T>}
|
|
163
|
-
*/
|
|
164
|
-
getAttribute(key) {
|
|
165
|
-
return key === 'syntax' ? this.#syntax : super.getAttribute(key);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
155
|
/** @override */
|
|
169
156
|
isPlain() {
|
|
170
157
|
return true;
|
|
171
158
|
}
|
|
172
159
|
|
|
173
|
-
/** 是否是不可变参数 */
|
|
174
|
-
#isVoid() {
|
|
175
|
-
return this.#syntax && !this.#syntax.includes('$1');
|
|
176
|
-
}
|
|
177
|
-
|
|
178
160
|
/**
|
|
179
161
|
* @override
|
|
180
162
|
* @param {string} selector
|
|
@@ -197,6 +179,40 @@ class ImageParameterToken extends Token {
|
|
|
197
179
|
: super.print({class: 'image-caption'});
|
|
198
180
|
}
|
|
199
181
|
|
|
182
|
+
/** @override */
|
|
183
|
+
cloneNode() {
|
|
184
|
+
const cloned = this.cloneChildNodes(),
|
|
185
|
+
config = this.getAttribute('config');
|
|
186
|
+
return Parser.run(() => {
|
|
187
|
+
const token = new ImageParameterToken(this.#syntax.replace('$1', ''), config);
|
|
188
|
+
token.replaceChildren(...cloned);
|
|
189
|
+
return token;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @override
|
|
195
|
+
* @template {string} T
|
|
196
|
+
* @param {T} key 属性键
|
|
197
|
+
* @returns {TokenAttribute<T>}
|
|
198
|
+
*/
|
|
199
|
+
getAttribute(key) {
|
|
200
|
+
return key === 'syntax' ? this.#syntax : super.getAttribute(key);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @override
|
|
205
|
+
* @param {PropertyKey} key 属性键
|
|
206
|
+
*/
|
|
207
|
+
hasAttribute(key) {
|
|
208
|
+
return key === 'syntax' || super.hasAttribute(key);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** 是否是不可变参数 */
|
|
212
|
+
#isVoid() {
|
|
213
|
+
return this.#syntax && !this.#syntax.includes('$1');
|
|
214
|
+
}
|
|
215
|
+
|
|
200
216
|
/** @override */
|
|
201
217
|
text() {
|
|
202
218
|
return this.#syntax ? this.#syntax.replace('$1', super.text()).trim() : super.text().trim();
|
|
@@ -245,8 +261,8 @@ class ImageParameterToken extends Token {
|
|
|
245
261
|
const root = Parser.parse(`[[File:F|${
|
|
246
262
|
this.#syntax ? this.#syntax.replace('$1', value) : value
|
|
247
263
|
}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
|
|
248
|
-
{
|
|
249
|
-
{lastChild: imageParameter, type, name,
|
|
264
|
+
{length, firstChild: file} = root,
|
|
265
|
+
{lastChild: imageParameter, type, name, length: fileLength} = file;
|
|
250
266
|
if (length !== 1 || type !== 'file' || name !== 'File:F' || fileLength !== 2
|
|
251
267
|
|| imageParameter.name !== this.name
|
|
252
268
|
) {
|