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/link/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const Title = require('../../lib/title'),
|
|
4
|
-
{
|
|
3
|
+
const Title = require('../../lib/title'),
|
|
4
|
+
{noWrap} = require('../../util/string'),
|
|
5
5
|
{undo} = require('../../util/debug'),
|
|
6
|
-
|
|
6
|
+
Parser = require('../..'),
|
|
7
|
+
AstText = require('../../lib/text'),
|
|
7
8
|
Token = require('..');
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -12,14 +13,67 @@ const Title = require('../../lib/title'), // eslint-disable-line no-unused-vars
|
|
|
12
13
|
*/
|
|
13
14
|
class LinkToken extends Token {
|
|
14
15
|
type = 'link';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
|
|
17
|
+
/** 完整链接,和FileToken保持一致 */
|
|
18
|
+
get link() {
|
|
19
|
+
return String(this.#getTitle());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set link(link) {
|
|
23
|
+
this.setTarget(link);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 是否链接到自身 */
|
|
27
|
+
get selfLink() {
|
|
28
|
+
return !this.#getTitle().title;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
set selfLink(selfLink) {
|
|
32
|
+
if (selfLink === true) {
|
|
33
|
+
this.asSelfLink();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** fragment */
|
|
38
|
+
get fragment() {
|
|
39
|
+
return this.#getTitle().fragment;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
set fragment(fragment) {
|
|
43
|
+
this.setFragment(fragment);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** interwiki */
|
|
47
|
+
get interwiki() {
|
|
48
|
+
return this.#getTitle().interwiki;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
set interwiki(interwiki) {
|
|
52
|
+
if (typeof interwiki !== 'string') {
|
|
53
|
+
this.typeError('set interwiki', 'String');
|
|
54
|
+
}
|
|
55
|
+
const {prefix, main, fragment} = this.#getTitle(),
|
|
56
|
+
link = `${interwiki}:${prefix}${main}${fragment && '#'}${fragment}`;
|
|
57
|
+
if (interwiki && !this.isInterwiki(link)) {
|
|
58
|
+
throw new RangeError(`${interwiki} 不是合法的跨维基前缀!`);
|
|
59
|
+
}
|
|
60
|
+
this.setTarget(link);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 链接显示文字 */
|
|
64
|
+
get innerText() {
|
|
65
|
+
if (this.type !== 'link') {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
return this.childNodes.length > 1
|
|
69
|
+
? this.lastElementChild.text()
|
|
70
|
+
: this.firstElementChild.text().replace(/^\s*:/u, '');
|
|
71
|
+
}
|
|
18
72
|
|
|
19
73
|
/**
|
|
20
|
-
* @param {string} link
|
|
21
|
-
* @param {string|undefined} linkText
|
|
22
|
-
* @param {Title} title
|
|
74
|
+
* @param {string} link 链接标题
|
|
75
|
+
* @param {string|undefined} linkText 链接显示文字
|
|
76
|
+
* @param {Title} title 链接标题对象
|
|
23
77
|
* @param {accum} accum
|
|
24
78
|
*/
|
|
25
79
|
constructor(link, linkText, title, config = Parser.getConfig(), accum = []) {
|
|
@@ -33,84 +87,94 @@ class LinkToken extends Token {
|
|
|
33
87
|
inner.type = 'link-text';
|
|
34
88
|
this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
|
|
35
89
|
}
|
|
36
|
-
this.
|
|
37
|
-
this.fragment = title.fragment;
|
|
38
|
-
this.interwiki = title.interwiki;
|
|
39
|
-
this.setAttribute('name', title.title).seal(['selfLink', 'fragment', 'interwiki']).protectChildren(0);
|
|
90
|
+
this.setAttribute('name', title.title).getAttribute('protectChildren')(0);
|
|
40
91
|
}
|
|
41
92
|
|
|
93
|
+
/** 生成Title对象 */
|
|
94
|
+
#getTitle() {
|
|
95
|
+
return this.normalizeTitle(this.firstElementChild.text());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @override */
|
|
42
99
|
cloneNode() {
|
|
43
|
-
const [link, ...linkText] = this.
|
|
100
|
+
const [link, ...linkText] = this.cloneChildNodes();
|
|
44
101
|
return Parser.run(() => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}, this.getAttribute('config'));
|
|
102
|
+
/** @type {this & {constructor: typeof LinkToken}} */
|
|
103
|
+
const {constructor} = this,
|
|
104
|
+
token = new constructor('', undefined, this.#getTitle(), this.getAttribute('config'));
|
|
49
105
|
token.firstElementChild.safeReplaceWith(link);
|
|
50
106
|
token.append(...linkText);
|
|
51
107
|
return token.afterBuild();
|
|
52
108
|
});
|
|
53
109
|
}
|
|
54
110
|
|
|
111
|
+
/**
|
|
112
|
+
* @override
|
|
113
|
+
* @throws `Error` 非法的内链目标
|
|
114
|
+
* @throws `Error` 不可更改命名空间
|
|
115
|
+
*/
|
|
55
116
|
afterBuild() {
|
|
56
|
-
if (this.name.includes('\x00')) {
|
|
57
|
-
this.setAttribute('name', text(this.buildFromStr(this.name)));
|
|
58
|
-
}
|
|
59
|
-
if (this.fragment.includes('\x00')) {
|
|
60
|
-
this.setAttribute('fragment', text(this.buildFromStr(this.fragment)));
|
|
61
|
-
}
|
|
62
|
-
const that = this;
|
|
63
117
|
const /** @type {AstListener} */ linkListener = (e, data) => {
|
|
64
118
|
const {prevTarget} = e;
|
|
65
119
|
if (prevTarget?.type === 'link-target') {
|
|
66
120
|
const name = prevTarget.text(),
|
|
67
|
-
{title, interwiki,
|
|
121
|
+
{title, interwiki, ns, valid} = this.normalizeTitle(name);
|
|
68
122
|
if (!valid) {
|
|
69
123
|
undo(e, data);
|
|
70
124
|
throw new Error(`非法的内链目标:${name}`);
|
|
71
|
-
} else if (
|
|
72
|
-
||
|
|
125
|
+
} else if (this.type === 'category' && (interwiki || ns !== 14)
|
|
126
|
+
|| this.type === 'file' && (interwiki || ns !== 6)
|
|
73
127
|
) {
|
|
74
128
|
undo(e, data);
|
|
75
|
-
throw new Error(`${
|
|
76
|
-
} else if (
|
|
77
|
-
const {firstChild} = prevTarget;
|
|
78
|
-
if (
|
|
79
|
-
|
|
129
|
+
throw new Error(`${this.type === 'file' ? '文件' : '分类'}链接不可更改命名空间:${name}`);
|
|
130
|
+
} else if (this.type === 'link' && !interwiki && (ns === 6 || ns === 14) && name.trim()[0] !== ':') {
|
|
131
|
+
const /** @type {{firstChild: AstText}} */ {firstChild} = prevTarget;
|
|
132
|
+
if (firstChild.type === 'text') {
|
|
133
|
+
firstChild.insertData(0, ':');
|
|
80
134
|
} else {
|
|
81
135
|
prevTarget.prepend(':');
|
|
82
136
|
}
|
|
83
137
|
}
|
|
84
|
-
|
|
85
|
-
.setAttribute('name', title).setAttribute('fragment', fragment);
|
|
138
|
+
this.setAttribute('name', title);
|
|
86
139
|
}
|
|
87
140
|
};
|
|
88
141
|
this.addEventListener(['remove', 'insert', 'replace', 'text'], linkListener);
|
|
89
142
|
return this;
|
|
90
143
|
}
|
|
91
144
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
145
|
+
/**
|
|
146
|
+
* @override
|
|
147
|
+
* @param {string} selector
|
|
148
|
+
*/
|
|
149
|
+
toString(selector) {
|
|
150
|
+
const str = super.toString(selector, '|');
|
|
151
|
+
return this.type === 'gallery-image' || selector && this.matches(selector) ? str : `[[${str}]]`;
|
|
95
152
|
}
|
|
96
153
|
|
|
154
|
+
/** @override */
|
|
97
155
|
getPadding() {
|
|
98
156
|
return 2;
|
|
99
157
|
}
|
|
100
158
|
|
|
159
|
+
/** @override */
|
|
101
160
|
getGaps() {
|
|
102
161
|
return 1;
|
|
103
162
|
}
|
|
104
163
|
|
|
164
|
+
/** @override */
|
|
105
165
|
text() {
|
|
106
166
|
const str = super.text('|');
|
|
107
167
|
return this.type === 'gallery-image' ? str : `[[${str}]]`;
|
|
108
168
|
}
|
|
109
169
|
|
|
110
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* 设置链接目标
|
|
172
|
+
* @param {string} link 链接目标
|
|
173
|
+
* @throws `SyntaxError` 非法的链接目标
|
|
174
|
+
*/
|
|
111
175
|
setTarget(link) {
|
|
112
176
|
link = String(link);
|
|
113
|
-
if (
|
|
177
|
+
if (this.type === 'link' && !/^\s*[:#]/u.test(link)) {
|
|
114
178
|
link = `:${link}`;
|
|
115
179
|
}
|
|
116
180
|
const root = Parser.parse(`[[${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
|
|
@@ -120,23 +184,25 @@ class LinkToken extends Token {
|
|
|
120
184
|
throw new SyntaxError(`非法的${msgs[this.type]}目标:${link}`);
|
|
121
185
|
}
|
|
122
186
|
const {firstChild} = firstElementChild;
|
|
123
|
-
|
|
124
|
-
firstElementChild.destroy();
|
|
187
|
+
firstElementChild.destroy(true);
|
|
125
188
|
this.firstElementChild.safeReplaceWith(firstChild);
|
|
126
189
|
}
|
|
127
190
|
|
|
128
191
|
/**
|
|
129
|
-
*
|
|
130
|
-
* @param {string}
|
|
192
|
+
* 设置跨语言链接
|
|
193
|
+
* @param {string} lang 语言前缀
|
|
194
|
+
* @param {string} link 页面标题
|
|
195
|
+
* @throws `SyntaxError` 非法的跨语言链接
|
|
131
196
|
*/
|
|
132
197
|
setLangLink(lang, link) {
|
|
133
198
|
if (typeof lang !== 'string') {
|
|
134
199
|
this.typeError('setLangLink', 'String');
|
|
135
200
|
}
|
|
136
201
|
link = String(link).trim();
|
|
137
|
-
|
|
202
|
+
const [char] = link;
|
|
203
|
+
if (char === '#') {
|
|
138
204
|
throw new SyntaxError(`跨语言链接不能仅为fragment!`);
|
|
139
|
-
} else if (
|
|
205
|
+
} else if (char === ':') {
|
|
140
206
|
link = link.slice(1);
|
|
141
207
|
}
|
|
142
208
|
const root = Parser.parse(`[[${lang}:${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
|
|
@@ -147,14 +213,18 @@ class LinkToken extends Token {
|
|
|
147
213
|
throw new SyntaxError(`非法的跨语言链接目标:${lang}:${link}`);
|
|
148
214
|
}
|
|
149
215
|
const {firstChild} = firstElementChild;
|
|
150
|
-
|
|
151
|
-
firstElementChild.destroy();
|
|
216
|
+
firstElementChild.destroy(true);
|
|
152
217
|
this.firstElementChild.safeReplaceWith(firstChild);
|
|
153
218
|
}
|
|
154
219
|
|
|
155
|
-
/**
|
|
220
|
+
/**
|
|
221
|
+
* 设置fragment
|
|
222
|
+
* @param {string} fragment fragment
|
|
223
|
+
* @param {boolean} page 是否是其他页面
|
|
224
|
+
* @throws `SyntaxError` 非法的fragment
|
|
225
|
+
*/
|
|
156
226
|
#setFragment(fragment, page = true) {
|
|
157
|
-
fragment = String(fragment).
|
|
227
|
+
fragment = String(fragment).replaceAll(/[<>[\]#|=]/gu, p => encodeURIComponent(p));
|
|
158
228
|
const include = this.getAttribute('include'),
|
|
159
229
|
config = this.getAttribute('config'),
|
|
160
230
|
root = Parser.parse(`[[${page ? `:${this.name}` : ''}#${fragment}]]`, include, 6, config),
|
|
@@ -166,16 +236,23 @@ class LinkToken extends Token {
|
|
|
166
236
|
Parser.warn(`${this.constructor.name}.setFragment 方法会同时规范化页面名!`);
|
|
167
237
|
}
|
|
168
238
|
const {firstChild} = firstElementChild;
|
|
169
|
-
|
|
170
|
-
firstElementChild.destroy();
|
|
239
|
+
firstElementChild.destroy(true);
|
|
171
240
|
this.firstElementChild.safeReplaceWith(firstChild);
|
|
172
241
|
}
|
|
173
242
|
|
|
174
|
-
/**
|
|
243
|
+
/**
|
|
244
|
+
* 设置fragment
|
|
245
|
+
* @param {string} fragment fragment
|
|
246
|
+
*/
|
|
175
247
|
setFragment(fragment) {
|
|
176
248
|
this.#setFragment(fragment);
|
|
177
249
|
}
|
|
178
250
|
|
|
251
|
+
/**
|
|
252
|
+
* 修改为到自身的链接
|
|
253
|
+
* @param {string} fragment fragment
|
|
254
|
+
* @throws `RangeError` 空fragment
|
|
255
|
+
*/
|
|
179
256
|
asSelfLink(fragment = this.fragment) {
|
|
180
257
|
fragment = String(fragment);
|
|
181
258
|
if (!fragment.trim()) {
|
|
@@ -184,6 +261,11 @@ class LinkToken extends Token {
|
|
|
184
261
|
this.#setFragment(fragment, false);
|
|
185
262
|
}
|
|
186
263
|
|
|
264
|
+
/**
|
|
265
|
+
* 设置链接显示文字
|
|
266
|
+
* @param {string} linkText 链接显示文字
|
|
267
|
+
* @throws `SyntaxError` 非法的链接显示文字
|
|
268
|
+
*/
|
|
187
269
|
setLinkText(linkText = '') {
|
|
188
270
|
linkText = String(linkText);
|
|
189
271
|
let lastElementChild;
|
|
@@ -208,24 +290,28 @@ class LinkToken extends Token {
|
|
|
208
290
|
}
|
|
209
291
|
}
|
|
210
292
|
|
|
293
|
+
/**
|
|
294
|
+
* 自动生成管道符后的链接文字
|
|
295
|
+
* @throws `Error` 带有"#"或"%"时不可用
|
|
296
|
+
*/
|
|
211
297
|
pipeTrick() {
|
|
212
298
|
const linkText = this.firstElementChild.text();
|
|
213
|
-
if (
|
|
299
|
+
if (linkText.includes('#') || linkText.includes('%')) {
|
|
214
300
|
throw new Error('Pipe trick 不能用于带有"#"或"%"的场合!');
|
|
215
301
|
}
|
|
216
|
-
const m1 =
|
|
302
|
+
const m1 = /^:?(?:[ \w\x80-\xFF-]+:)?([^(]+)\(.+\)$/u.exec(linkText);
|
|
217
303
|
if (m1) {
|
|
218
|
-
this.setLinkText(m1[1]);
|
|
304
|
+
this.setLinkText(m1[1].trim());
|
|
219
305
|
return;
|
|
220
306
|
}
|
|
221
|
-
const m2 =
|
|
307
|
+
const m2 = /^:?(?:[ \w\x80-\xFF-]+:)?([^(]+)(.+)$/u.exec(linkText);
|
|
222
308
|
if (m2) {
|
|
223
|
-
this.setLinkText(m2[1]);
|
|
309
|
+
this.setLinkText(m2[1].trim());
|
|
224
310
|
return;
|
|
225
311
|
}
|
|
226
|
-
const m3 =
|
|
312
|
+
const m3 = /^:?(?:[ \w\x80-\xFF-]+:)?(.+?)(?:(?<!\()\(.+\))?(?:, |,|، )./u.exec(linkText);
|
|
227
313
|
if (m3) {
|
|
228
|
-
this.setLinkText(m3[1]);
|
|
314
|
+
this.setLinkText(m3[1].trim());
|
|
229
315
|
return;
|
|
230
316
|
}
|
|
231
317
|
this.setLinkText(linkText);
|
package/src/magicLink.js
CHANGED
|
@@ -1,52 +1,66 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const Parser = require('..'),
|
|
4
4
|
Token = require('.');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* 自由外链
|
|
8
|
-
* @classdesc `{childNodes: [...
|
|
8
|
+
* @classdesc `{childNodes: [...AstText|CommentToken|IncludeToken|NoincludeToken]}`
|
|
9
9
|
*/
|
|
10
10
|
class MagicLinkToken extends Token {
|
|
11
11
|
type = 'free-ext-link';
|
|
12
12
|
#protocolRegex;
|
|
13
13
|
|
|
14
|
+
/** 协议 */
|
|
14
15
|
get protocol() {
|
|
15
|
-
return this.
|
|
16
|
+
return this.#protocolRegex.exec(this.text())?.[0];
|
|
16
17
|
}
|
|
18
|
+
|
|
17
19
|
set protocol(value) {
|
|
18
20
|
if (typeof value !== 'string') {
|
|
19
21
|
this.typeError('protocol', 'String');
|
|
20
22
|
}
|
|
21
|
-
if (!new RegExp(`${this.#protocolRegex.source}$`, '
|
|
23
|
+
if (!new RegExp(`${this.#protocolRegex.source}$`, 'iu').test(value)) {
|
|
22
24
|
throw new RangeError(`非法的外链协议:${value}`);
|
|
23
25
|
}
|
|
24
26
|
this.replaceChildren(this.text().replace(this.#protocolRegex, value));
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
/** 和内链保持一致 */
|
|
30
|
+
get link() {
|
|
31
|
+
return this.text();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
set link(url) {
|
|
35
|
+
this.setTarget(url);
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
/**
|
|
28
|
-
* @param {string} url
|
|
39
|
+
* @param {string} url 网址
|
|
40
|
+
* @param {boolean} doubleSlash 是否接受"//"作为协议
|
|
29
41
|
* @param {accum} accum
|
|
30
42
|
*/
|
|
31
|
-
constructor(url, doubleSlash
|
|
43
|
+
constructor(url, doubleSlash, config = Parser.getConfig(), accum = []) {
|
|
32
44
|
super(url, config, true, accum, {'Stage-1': ':', '!ExtToken': ''});
|
|
33
45
|
if (doubleSlash) {
|
|
34
46
|
this.type = 'ext-link-url';
|
|
35
47
|
}
|
|
36
|
-
this.#protocolRegex = new RegExp(`^(?:${config.protocol}${doubleSlash ? '|//' : ''})`, '
|
|
48
|
+
this.#protocolRegex = new RegExp(`^(?:${config.protocol}${doubleSlash ? '|//' : ''})`, 'iu');
|
|
37
49
|
}
|
|
38
50
|
|
|
51
|
+
/** @override */
|
|
39
52
|
afterBuild() {
|
|
40
|
-
const ParameterToken = require('./parameter')
|
|
41
|
-
|
|
53
|
+
const ParameterToken = require('./parameter');
|
|
54
|
+
const /** @type {ParameterToken} */ parameter = this.closest('parameter');
|
|
42
55
|
if (parameter?.getValue() === this.text()) {
|
|
43
|
-
this.replaceWith(this.
|
|
56
|
+
this.replaceWith(...this.childNodes);
|
|
44
57
|
}
|
|
45
58
|
return this;
|
|
46
59
|
}
|
|
47
60
|
|
|
61
|
+
/** @override */
|
|
48
62
|
cloneNode() {
|
|
49
|
-
const cloned = this.
|
|
63
|
+
const cloned = this.cloneChildNodes(),
|
|
50
64
|
token = Parser.run(() => new MagicLinkToken(
|
|
51
65
|
undefined, this.type === 'ext-link-url', this.getAttribute('config'),
|
|
52
66
|
));
|
|
@@ -55,6 +69,10 @@ class MagicLinkToken extends Token {
|
|
|
55
69
|
return token;
|
|
56
70
|
}
|
|
57
71
|
|
|
72
|
+
/**
|
|
73
|
+
* 获取网址
|
|
74
|
+
* @throws `Error` 非标准协议
|
|
75
|
+
*/
|
|
58
76
|
getUrl() {
|
|
59
77
|
let url = this.text();
|
|
60
78
|
if (url.startsWith('//')) {
|
|
@@ -70,7 +88,11 @@ class MagicLinkToken extends Token {
|
|
|
70
88
|
}
|
|
71
89
|
}
|
|
72
90
|
|
|
73
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* 设置外链目标
|
|
93
|
+
* @param {string|URL} url 含协议的网址
|
|
94
|
+
* @throws `SyntaxError` 非法的自由外链目标
|
|
95
|
+
*/
|
|
74
96
|
setTarget(url) {
|
|
75
97
|
url = String(url);
|
|
76
98
|
const root = Parser.parse(url, this.getAttribute('include'), 9, this.getAttribute('config')),
|
package/src/nowiki/comment.js
CHANGED
|
@@ -1,41 +1,53 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const hidden = require('../../mixin/hidden'),
|
|
4
|
-
|
|
4
|
+
Parser = require('../..'),
|
|
5
5
|
NowikiToken = require('.');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* HTML注释,不可见
|
|
9
|
-
* @classdesc `{childNodes: [
|
|
9
|
+
* @classdesc `{childNodes: [AstText]}`
|
|
10
10
|
*/
|
|
11
11
|
class CommentToken extends hidden(NowikiToken) {
|
|
12
12
|
type = 'comment';
|
|
13
13
|
closed;
|
|
14
14
|
|
|
15
|
+
/** 内部wikitext */
|
|
16
|
+
get innerText() {
|
|
17
|
+
return String(this.firstChild);
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
|
-
* @param {string} wikitext
|
|
21
|
+
* @param {string} wikitext wikitext
|
|
22
|
+
* @param {boolean} closed 是否闭合
|
|
17
23
|
* @param {accum} accum
|
|
18
24
|
*/
|
|
19
25
|
constructor(wikitext, closed = true, config = Parser.getConfig(), accum = []) {
|
|
20
26
|
super(wikitext, config, accum);
|
|
21
27
|
this.closed = closed;
|
|
28
|
+
Object.defineProperty(this, 'closed', {enumerable: false});
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
/** @
|
|
31
|
+
/** @override */
|
|
25
32
|
cloneNode() {
|
|
26
|
-
return Parser.run(() => new CommentToken(this.firstChild, this.closed, this.getAttribute('config')));
|
|
33
|
+
return Parser.run(() => new CommentToken(String(this.firstChild), this.closed, this.getAttribute('config')));
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
/**
|
|
37
|
+
* @override
|
|
38
|
+
* @param {string} selector
|
|
39
|
+
*/
|
|
40
|
+
toString(selector) {
|
|
41
|
+
if (!this.closed && this.nextSibling) {
|
|
33
42
|
Parser.error('自动闭合HTML注释', this);
|
|
34
43
|
this.closed = true;
|
|
35
44
|
}
|
|
36
|
-
return
|
|
45
|
+
return selector && this.matches(selector)
|
|
46
|
+
? ''
|
|
47
|
+
: `<!--${String(this.firstChild)}${this.closed ? '-->' : ''}`;
|
|
37
48
|
}
|
|
38
49
|
|
|
50
|
+
/** @override */
|
|
39
51
|
getPadding() {
|
|
40
52
|
return 4;
|
|
41
53
|
}
|
package/src/nowiki/dd.js
CHANGED
|
@@ -1,41 +1,56 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const Parser = require('../..'),
|
|
4
4
|
NowikiToken = require('.');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* :
|
|
8
|
-
* @classdesc `{childNodes: [
|
|
8
|
+
* @classdesc `{childNodes: [AstText]}`
|
|
9
9
|
*/
|
|
10
10
|
class DdToken extends NowikiToken {
|
|
11
11
|
type = 'dd';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/** @param {string} str */
|
|
18
|
-
#update(str) {
|
|
19
|
-
this.setAttribute('ul', str.includes('*')).setAttribute('ol', str.includes('#'))
|
|
20
|
-
.setAttribute('dt', str.includes(';')).setAttribute('indent', str.split(':').length - 1);
|
|
12
|
+
|
|
13
|
+
/** 是否包含<dt> */
|
|
14
|
+
get dt() {
|
|
15
|
+
return String(this).includes(';');
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
/** 是否包含<ul> */
|
|
19
|
+
get ul() {
|
|
20
|
+
return String(this).includes('*');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** 是否包含<ol> */
|
|
24
|
+
get ol() {
|
|
25
|
+
return String(this).includes('#');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 缩进数 */
|
|
29
|
+
get indent() {
|
|
30
|
+
return String(this).split(':').length - 1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
set indent(indent) {
|
|
34
|
+
if (this.type === 'dd') {
|
|
35
|
+
if (typeof indent !== 'number') {
|
|
36
|
+
this.typeError('set indent', 'Number');
|
|
37
|
+
} else if (!Number.isInteger(indent) || indent < 0) {
|
|
38
|
+
throw new RangeError(`indent 应为自然数!${indent}`);
|
|
39
|
+
}
|
|
40
|
+
this.setText(':'.repeat(indent));
|
|
41
|
+
}
|
|
30
42
|
}
|
|
31
43
|
|
|
32
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* @override
|
|
46
|
+
* @param {string} str 新文本
|
|
47
|
+
* @throws `RangeError` 错误的列表语法
|
|
48
|
+
*/
|
|
33
49
|
setText(str) {
|
|
34
50
|
const src = this.type === 'dd' ? ':' : ';:*#';
|
|
35
|
-
if (new RegExp(`[^${src}]
|
|
36
|
-
throw new RangeError(`${this.constructor.name} 仅能包含${src.
|
|
51
|
+
if (new RegExp(`[^${src}]`, 'u').test(str)) {
|
|
52
|
+
throw new RangeError(`${this.constructor.name} 仅能包含${[...src].map(c => `"${c}"`).join('、')}!`);
|
|
37
53
|
}
|
|
38
|
-
this.#update(str);
|
|
39
54
|
return super.setText(str);
|
|
40
55
|
}
|
|
41
56
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const hidden = require('../../mixin/hidden'),
|
|
4
|
-
|
|
4
|
+
Parser = require('../..'),
|
|
5
5
|
NowikiToken = require('.');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 状态开关
|
|
9
|
-
* @classdesc `{childNodes: [
|
|
9
|
+
* @classdesc `{childNodes: [AstText]}`
|
|
10
10
|
*/
|
|
11
11
|
class DoubleUnderscoreToken extends hidden(NowikiToken) {
|
|
12
12
|
type = 'double-underscore';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* @param {string} word
|
|
15
|
+
* @param {string} word 状态开关名
|
|
16
16
|
* @param {accum} accum
|
|
17
17
|
*/
|
|
18
18
|
constructor(word, config = Parser.getConfig(), accum = []) {
|
|
@@ -20,19 +20,28 @@ class DoubleUnderscoreToken extends hidden(NowikiToken) {
|
|
|
20
20
|
this.setAttribute('name', word.toLowerCase());
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** @override */
|
|
23
24
|
cloneNode() {
|
|
24
|
-
return Parser.run(() => new DoubleUnderscoreToken(this.firstChild, this.getAttribute('config')));
|
|
25
|
+
return Parser.run(() => new DoubleUnderscoreToken(String(this.firstChild), this.getAttribute('config')));
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* @override
|
|
30
|
+
* @param {string} selector
|
|
31
|
+
*/
|
|
32
|
+
toString(selector) {
|
|
33
|
+
return selector && this.matches(selector) ? '' : `__${String(this.firstChild)}__`;
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
/** @override */
|
|
32
37
|
getPadding() {
|
|
33
38
|
return 2;
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @override
|
|
43
|
+
* @throws `Error` 禁止修改
|
|
44
|
+
*/
|
|
36
45
|
setText() {
|
|
37
46
|
throw new Error(`禁止修改 ${this.constructor.name}!`);
|
|
38
47
|
}
|
package/src/nowiki/hr.js
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const sol = require('../../mixin/sol'),
|
|
4
|
-
|
|
4
|
+
Parser = require('../..'),
|
|
5
5
|
NowikiToken = require('.');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* `<hr>`
|
|
9
|
-
* @classdesc `{childNodes: [
|
|
9
|
+
* @classdesc `{childNodes: [AstText]}`
|
|
10
10
|
*/
|
|
11
11
|
class HrToken extends sol(NowikiToken) {
|
|
12
12
|
type = 'hr';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* @param {number} n
|
|
15
|
+
* @param {number} n 字符串长度
|
|
16
16
|
* @param {accum} accum
|
|
17
17
|
*/
|
|
18
18
|
constructor(n, config = Parser.getConfig(), accum = []) {
|
|
19
19
|
super('-'.repeat(n), config, accum);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/** @
|
|
22
|
+
/** @override */
|
|
23
23
|
cloneNode() {
|
|
24
|
-
return Parser.run(() => new HrToken(this.
|
|
24
|
+
return Parser.run(() => new HrToken(String(this).length, this.getAttribute('config')));
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* @override
|
|
29
|
+
* @param {string} str 新文本
|
|
30
|
+
* @throws `RangeError` 错误的\<hr\>语法
|
|
31
|
+
*/
|
|
28
32
|
setText(str) {
|
|
29
|
-
if (
|
|
33
|
+
if (str.length < 4 || /[^-]/u.test(str)) {
|
|
30
34
|
throw new RangeError('<hr>总是写作不少于4个的连续"-"!');
|
|
31
35
|
}
|
|
32
36
|
return super.setText(str);
|