wikiparser-node 0.4.0 → 0.6.2-b
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/bundle/bundle.min.js +34 -0
- package/package.json +12 -5
- package/.eslintrc.json +0 -714
- package/README.md +0 -39
- package/config/default.json +0 -769
- package/config/llwiki.json +0 -630
- package/config/moegirl.json +0 -727
- package/config/zhwiki.json +0 -1269
- package/errors/README +0 -1
- package/index.js +0 -245
- package/jsconfig.json +0 -7
- package/lib/element.js +0 -755
- package/lib/node.js +0 -585
- package/lib/ranges.js +0 -131
- package/lib/text.js +0 -146
- package/lib/title.js +0 -69
- package/mixin/attributeParent.js +0 -113
- package/mixin/fixedToken.js +0 -40
- package/mixin/hidden.js +0 -19
- package/mixin/sol.js +0 -59
- package/parser/brackets.js +0 -112
- package/parser/commentAndExt.js +0 -63
- package/parser/converter.js +0 -45
- package/parser/externalLinks.js +0 -31
- package/parser/hrAndDoubleUnderscore.js +0 -35
- package/parser/html.js +0 -42
- package/parser/links.js +0 -98
- package/parser/list.js +0 -59
- package/parser/magicLinks.js +0 -41
- package/parser/quotes.js +0 -64
- package/parser/selector.js +0 -175
- package/parser/table.js +0 -112
- package/printed/README +0 -1
- package/printed/example.json +0 -120
- package/src/arg.js +0 -169
- package/src/atom/hidden.js +0 -13
- package/src/atom/index.js +0 -41
- package/src/attribute.js +0 -422
- package/src/converter.js +0 -157
- package/src/converterFlags.js +0 -232
- package/src/converterRule.js +0 -253
- package/src/extLink.js +0 -167
- package/src/gallery.js +0 -91
- package/src/heading.js +0 -100
- package/src/html.js +0 -202
- package/src/imageParameter.js +0 -254
- package/src/index.js +0 -737
- package/src/link/category.js +0 -53
- package/src/link/file.js +0 -265
- package/src/link/galleryImage.js +0 -61
- package/src/link/index.js +0 -322
- package/src/magicLink.js +0 -108
- package/src/nowiki/comment.js +0 -57
- package/src/nowiki/dd.js +0 -59
- package/src/nowiki/doubleUnderscore.js +0 -51
- package/src/nowiki/hr.js +0 -41
- package/src/nowiki/index.js +0 -44
- package/src/nowiki/list.js +0 -16
- package/src/nowiki/noinclude.js +0 -28
- package/src/nowiki/quote.js +0 -36
- package/src/onlyinclude.js +0 -54
- package/src/parameter.js +0 -187
- package/src/syntax.js +0 -83
- package/src/table/index.js +0 -967
- package/src/table/td.js +0 -308
- package/src/table/tr.js +0 -282
- package/src/tagPair/ext.js +0 -105
- package/src/tagPair/include.js +0 -50
- package/src/tagPair/index.js +0 -117
- package/src/transclude.js +0 -703
- package/test/api.js +0 -83
- package/test/real.js +0 -133
- package/test/test.js +0 -28
- package/test/util.js +0 -80
- package/tool/index.js +0 -918
- 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/util/debug.js +0 -73
- package/util/string.js +0 -88
package/src/link/category.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const Title = require('../../lib/title'),
|
|
4
|
-
Parser = require('../..'),
|
|
5
|
-
LinkToken = require('.'),
|
|
6
|
-
Token = require('..');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 分类
|
|
10
|
-
* @classdesc `{childNodes: [AtomToken, ?Token]}`
|
|
11
|
-
*/
|
|
12
|
-
class CategoryToken extends LinkToken {
|
|
13
|
-
type = 'category';
|
|
14
|
-
|
|
15
|
-
setLangLink = undefined;
|
|
16
|
-
setFragment = undefined;
|
|
17
|
-
asSelfLink = undefined;
|
|
18
|
-
pipeTrick = undefined;
|
|
19
|
-
|
|
20
|
-
/** 分类排序关键字 */
|
|
21
|
-
get sortkey() {
|
|
22
|
-
return this.children[1]?.text()
|
|
23
|
-
?.replaceAll(/&#(\d+);/gu, /** @param {string} p */ (_, p) => String.fromCodePoint(Number(p)))
|
|
24
|
-
?.replaceAll(/&#x([\da-f]+);/giu, /** @param {string} p */ (_, p) => String.fromCodePoint(parseInt(p, 16)))
|
|
25
|
-
?.replaceAll('\n', '') ?? '';
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
set sortkey(text) {
|
|
29
|
-
this.setSortkey(text);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @param {string} link 分类名
|
|
34
|
-
* @param {string|undefined} text 排序关键字
|
|
35
|
-
* @param {Title} title 分类页面标题对象
|
|
36
|
-
* @param {accum} accum
|
|
37
|
-
*/
|
|
38
|
-
constructor(link, text, title, config = Parser.getConfig(), accum = []) {
|
|
39
|
-
super(link, text, title, config, accum);
|
|
40
|
-
this.seal(['setFragment', 'asSelfLink', 'pipeTrick'], true);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 设置排序关键字
|
|
45
|
-
* @param {string} text 排序关键字
|
|
46
|
-
*/
|
|
47
|
-
setSortkey(text) {
|
|
48
|
-
this.setLinkText(text);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
Parser.classes.CategoryToken = __filename;
|
|
53
|
-
module.exports = CategoryToken;
|
package/src/link/file.js
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const Title = require('../../lib/title'),
|
|
4
|
-
{explode, noWrap} = require('../../util/string'),
|
|
5
|
-
{externalUse} = require('../../util/debug'),
|
|
6
|
-
Parser = require('../..'),
|
|
7
|
-
LinkToken = require('.'),
|
|
8
|
-
ImageParameterToken = require('../imageParameter');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 图片
|
|
12
|
-
* @classdesc `{childNodes: [AtomToken, ...ImageParameterToken]}`
|
|
13
|
-
*/
|
|
14
|
-
class FileToken extends LinkToken {
|
|
15
|
-
type = 'file';
|
|
16
|
-
/** @type {Set<string>} */ #keys = new Set();
|
|
17
|
-
/** @type {Record<string, Set<ImageParameterToken>>} */ #args = {};
|
|
18
|
-
|
|
19
|
-
setLangLink = undefined;
|
|
20
|
-
setFragment = undefined;
|
|
21
|
-
asSelfLink = undefined;
|
|
22
|
-
setLinkText = undefined;
|
|
23
|
-
pipeTrick = undefined;
|
|
24
|
-
|
|
25
|
-
/** 图片链接 */
|
|
26
|
-
get link() {
|
|
27
|
-
return this.getArg('link')?.link;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
set link(value) {
|
|
31
|
-
this.setValue('link', value);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** 图片大小 */
|
|
35
|
-
get size() {
|
|
36
|
-
return this.getArg('width')?.size;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** 图片宽度 */
|
|
40
|
-
get width() {
|
|
41
|
-
return this.size?.width;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
set width(width) {
|
|
45
|
-
const arg = this.getArg('width');
|
|
46
|
-
if (arg) {
|
|
47
|
-
arg.width = width;
|
|
48
|
-
} else {
|
|
49
|
-
this.setValue('width', width);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** 图片高度 */
|
|
54
|
-
get height() {
|
|
55
|
-
return this.size?.height;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
set height(height) {
|
|
59
|
-
const arg = this.getArg('width');
|
|
60
|
-
if (arg) {
|
|
61
|
-
arg.height = height;
|
|
62
|
-
} else {
|
|
63
|
-
this.setValue('width', `x${height}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @param {string} link 文件名
|
|
69
|
-
* @param {string|undefined} text 图片参数
|
|
70
|
-
* @param {Title} title 文件标题对象
|
|
71
|
-
* @param {accum} accum
|
|
72
|
-
* @complexity `n`
|
|
73
|
-
*/
|
|
74
|
-
constructor(link, text, title, config = Parser.getConfig(), accum = []) {
|
|
75
|
-
super(link, undefined, title, config, accum);
|
|
76
|
-
this.setAttribute('acceptable', {AtomToken: 0, ImageParameterToken: '1:'});
|
|
77
|
-
this.append(...explode('-{', '}-', '|', text).map(part => new ImageParameterToken(part, config, accum)));
|
|
78
|
-
this.seal(['setLangLink', 'setFragment', 'asSelfLink', 'setLinkText', 'pipeTrick'], true);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @override
|
|
83
|
-
* @param {number} i 移除位置
|
|
84
|
-
* @complexity `n`
|
|
85
|
-
*/
|
|
86
|
-
removeAt(i) {
|
|
87
|
-
const /** @type {ImageParameterToken} */ token = super.removeAt(i),
|
|
88
|
-
args = this.getArgs(token.name, false, false);
|
|
89
|
-
args.delete(token);
|
|
90
|
-
if (args.size === 0) {
|
|
91
|
-
this.#keys.delete(token.name);
|
|
92
|
-
}
|
|
93
|
-
return token;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @override
|
|
98
|
-
* @param {ImageParameterToken} token 待插入的子节点
|
|
99
|
-
* @param {number} i 插入位置
|
|
100
|
-
* @complexity `n`
|
|
101
|
-
*/
|
|
102
|
-
insertAt(token, i = this.childNodes.length) {
|
|
103
|
-
if (!Parser.running) {
|
|
104
|
-
this.getArgs(token.name, false, false).add(token);
|
|
105
|
-
this.#keys.add(token.name);
|
|
106
|
-
}
|
|
107
|
-
return super.insertAt(token, i);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* 获取所有图片参数节点
|
|
112
|
-
* @returns {ImageParameterToken[]}
|
|
113
|
-
*/
|
|
114
|
-
getAllArgs() {
|
|
115
|
-
return this.childNodes.slice(1);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* 获取图片框架属性参数节点
|
|
120
|
-
* @complexity `n`
|
|
121
|
-
*/
|
|
122
|
-
getFrameArgs() {
|
|
123
|
-
const args = this.getAllArgs()
|
|
124
|
-
.filter(({name}) => ['manualthumb', 'frameless', 'framed', 'thumbnail'].includes(name));
|
|
125
|
-
if (args.length > 1) {
|
|
126
|
-
Parser.error(`图片 ${this.name} 带有 ${args.length} 个框架参数,只有第 1 个 ${args[0].name} 会生效!`);
|
|
127
|
-
}
|
|
128
|
-
return args;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* 获取指定图片参数
|
|
133
|
-
* @param {string} key 参数名
|
|
134
|
-
* @param {boolean} copy 是否返回备份
|
|
135
|
-
* @complexity `n`
|
|
136
|
-
*/
|
|
137
|
-
getArgs(key, copy = true) {
|
|
138
|
-
if (typeof key !== 'string') {
|
|
139
|
-
this.typeError('getArgs', 'String');
|
|
140
|
-
}
|
|
141
|
-
copy ||= !Parser.debugging && externalUse('getArgs');
|
|
142
|
-
let args = this.#args[key];
|
|
143
|
-
if (!args) {
|
|
144
|
-
args = new Set(this.getAllArgs().filter(({name}) => key === name));
|
|
145
|
-
this.#args[key] = args;
|
|
146
|
-
}
|
|
147
|
-
return copy ? new Set(args) : args;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* 是否具有指定图片参数
|
|
152
|
-
* @param {string} key 参数名
|
|
153
|
-
* @complexity `n`
|
|
154
|
-
*/
|
|
155
|
-
hasArg(key) {
|
|
156
|
-
return this.getArgs(key, false).size > 0;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* 获取生效的指定图片参数
|
|
161
|
-
* @param {string} key 参数名
|
|
162
|
-
* @complexity `n`
|
|
163
|
-
*/
|
|
164
|
-
getArg(key) {
|
|
165
|
-
return [...this.getArgs(key, false)].sort((a, b) => a.compareDocumentPosition(b)).at(-1);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* 移除指定图片参数
|
|
170
|
-
* @param {string} key 参数名
|
|
171
|
-
* @complexity `n`
|
|
172
|
-
*/
|
|
173
|
-
removeArg(key) {
|
|
174
|
-
for (const token of this.getArgs(key, false)) {
|
|
175
|
-
this.removeChild(token);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* 获取图片参数名
|
|
181
|
-
* @complexity `n`
|
|
182
|
-
*/
|
|
183
|
-
getKeys() {
|
|
184
|
-
const args = this.getAllArgs();
|
|
185
|
-
if (this.#keys.size === 0 && args.length > 0) {
|
|
186
|
-
for (const {name} of args) {
|
|
187
|
-
this.#keys.add(name);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return [...this.#keys];
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* 获取指定的图片参数值
|
|
195
|
-
* @param {string} key 参数名
|
|
196
|
-
* @complexity `n`
|
|
197
|
-
*/
|
|
198
|
-
getValues(key) {
|
|
199
|
-
return [...this.getArgs(key, false)].map(token => token.getValue());
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* 获取生效的指定图片参数值
|
|
204
|
-
* @template {string|undefined} T
|
|
205
|
-
* @param {T} key 参数名
|
|
206
|
-
* @returns {T extends undefined ? Object<string, string> : string|true}
|
|
207
|
-
* @complexity `n`
|
|
208
|
-
*/
|
|
209
|
-
getValue(key) {
|
|
210
|
-
return key === undefined
|
|
211
|
-
? Object.fromEntries(this.getKeys().map(k => [k, this.getValue(k)]))
|
|
212
|
-
: this.getArg(key)?.getValue();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* 设置图片参数
|
|
217
|
-
* @param {string} key 参数名
|
|
218
|
-
* @param {string|boolean} value 参数值
|
|
219
|
-
* @complexity `n`
|
|
220
|
-
* @throws `RangeError` 未定义的图片参数
|
|
221
|
-
* @throws `SyntaxError` 非法的参数
|
|
222
|
-
*/
|
|
223
|
-
setValue(key, value) {
|
|
224
|
-
if (typeof key !== 'string') {
|
|
225
|
-
this.typeError('setValue', 'String');
|
|
226
|
-
} else if (value === false) {
|
|
227
|
-
this.removeArg(key);
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
const token = this.getArg(key);
|
|
231
|
-
value = value === true ? value : String(value);
|
|
232
|
-
if (token) {
|
|
233
|
-
token.setValue(value);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
let syntax = '';
|
|
237
|
-
const config = this.getAttribute('config');
|
|
238
|
-
if (key !== 'caption') {
|
|
239
|
-
syntax = Object.entries(config.img).find(([, name]) => name === key)?.[0];
|
|
240
|
-
if (!syntax) {
|
|
241
|
-
throw new RangeError(`未定义的图片参数: ${key}`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
if (value === true) {
|
|
245
|
-
if (syntax.includes('$1')) {
|
|
246
|
-
this.typeError('setValue', 'Boolean');
|
|
247
|
-
}
|
|
248
|
-
const newArg = Parser.run(() => new ImageParameterToken(syntax, config));
|
|
249
|
-
this.appendChild(newArg);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
const wikitext = `[[File:F|${syntax ? syntax.replace('$1', value) : value}]]`,
|
|
253
|
-
root = Parser.parse(wikitext, this.getAttribute('include'), 6, config),
|
|
254
|
-
{childNodes: {length}, firstElementChild} = root;
|
|
255
|
-
if (length !== 1 || !firstElementChild?.matches('file#File\\:F')
|
|
256
|
-
|| firstElementChild.childNodes.length !== 2 || firstElementChild.lastElementChild.name !== key
|
|
257
|
-
) {
|
|
258
|
-
throw new SyntaxError(`非法的 ${key} 参数:${noWrap(value)}`);
|
|
259
|
-
}
|
|
260
|
-
this.appendChild(firstElementChild.lastChild);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
Parser.classes.FileToken = __filename;
|
|
265
|
-
module.exports = FileToken;
|
package/src/link/galleryImage.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const Title = require('../../lib/title'),
|
|
4
|
-
Parser = require('../..'),
|
|
5
|
-
Token = require('..'),
|
|
6
|
-
FileToken = require('./file');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 图片
|
|
10
|
-
* @classdesc `{childNodes: [AtomToken, ...ImageParameterToken]}`
|
|
11
|
-
*/
|
|
12
|
-
class GalleryImageToken extends FileToken {
|
|
13
|
-
type = 'gallery-image';
|
|
14
|
-
|
|
15
|
-
size = undefined;
|
|
16
|
-
width = undefined;
|
|
17
|
-
height = undefined;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @param {string} link 图片文件名
|
|
21
|
-
* @param {string|undefined} text 图片参数
|
|
22
|
-
* @param {Title} title 图片文件标题对象
|
|
23
|
-
* @param {accum} accum
|
|
24
|
-
*/
|
|
25
|
-
constructor(link, text, title, config = Parser.getConfig(), accum = []) {
|
|
26
|
-
let token;
|
|
27
|
-
if (text !== undefined) {
|
|
28
|
-
token = new Token(text, config, true, accum);
|
|
29
|
-
token.type = 'temp';
|
|
30
|
-
for (let n = 1; n < Parser.MAX_STAGE; n++) {
|
|
31
|
-
token.getAttribute('parseOnce')();
|
|
32
|
-
}
|
|
33
|
-
accum.splice(accum.indexOf(token), 1);
|
|
34
|
-
}
|
|
35
|
-
const newConfig = structuredClone(config);
|
|
36
|
-
newConfig.img = Object.fromEntries(Object.entries(config.img).filter(([, param]) => param !== 'width'));
|
|
37
|
-
super(link, token?.toString(), title, newConfig, accum);
|
|
38
|
-
this.seal(['size', 'width', 'height'], true);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** @override */
|
|
42
|
-
getPadding() {
|
|
43
|
-
return 0;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @override
|
|
48
|
-
* @param {string} selector
|
|
49
|
-
*/
|
|
50
|
-
toString(selector) {
|
|
51
|
-
return super.toString(selector).replaceAll('\n', ' ');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** @override */
|
|
55
|
-
text() {
|
|
56
|
-
return super.text().replaceAll('\n', ' ');
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
Parser.classes.GalleryImageToken = __filename;
|
|
61
|
-
module.exports = GalleryImageToken;
|
package/src/link/index.js
DELETED
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const Title = require('../../lib/title'),
|
|
4
|
-
{noWrap} = require('../../util/string'),
|
|
5
|
-
{undo} = require('../../util/debug'),
|
|
6
|
-
Parser = require('../..'),
|
|
7
|
-
AstText = require('../../lib/text'),
|
|
8
|
-
Token = require('..');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 内链
|
|
12
|
-
* @classdesc `{childNodes: [AtomToken, ?Token]}`
|
|
13
|
-
*/
|
|
14
|
-
class LinkToken extends Token {
|
|
15
|
-
type = 'link';
|
|
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
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* @param {string} link 链接标题
|
|
75
|
-
* @param {string|undefined} linkText 链接显示文字
|
|
76
|
-
* @param {Title} title 链接标题对象
|
|
77
|
-
* @param {accum} accum
|
|
78
|
-
*/
|
|
79
|
-
constructor(link, linkText, title, config = Parser.getConfig(), accum = []) {
|
|
80
|
-
super(undefined, config, true, accum, {AtomToken: 0, Token: 1});
|
|
81
|
-
const AtomToken = require('../atom');
|
|
82
|
-
this.appendChild(new AtomToken(link, 'link-target', config, accum, {
|
|
83
|
-
'Stage-2': ':', '!ExtToken': '', '!HeadingToken': '',
|
|
84
|
-
}));
|
|
85
|
-
if (linkText !== undefined) {
|
|
86
|
-
const inner = new Token(linkText, config, true, accum, {'Stage-5': ':', ConverterToken: ':'});
|
|
87
|
-
inner.type = 'link-text';
|
|
88
|
-
this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
|
|
89
|
-
}
|
|
90
|
-
this.setAttribute('name', title.title).getAttribute('protectChildren')(0);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** 生成Title对象 */
|
|
94
|
-
#getTitle() {
|
|
95
|
-
return this.normalizeTitle(this.firstElementChild.text());
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** @override */
|
|
99
|
-
cloneNode() {
|
|
100
|
-
const [link, ...linkText] = this.cloneChildNodes();
|
|
101
|
-
return Parser.run(() => {
|
|
102
|
-
/** @type {this & {constructor: typeof LinkToken}} */
|
|
103
|
-
const {constructor} = this,
|
|
104
|
-
token = new constructor('', undefined, this.#getTitle(), this.getAttribute('config'));
|
|
105
|
-
token.firstElementChild.safeReplaceWith(link);
|
|
106
|
-
token.append(...linkText);
|
|
107
|
-
return token.afterBuild();
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* @override
|
|
113
|
-
* @throws `Error` 非法的内链目标
|
|
114
|
-
* @throws `Error` 不可更改命名空间
|
|
115
|
-
*/
|
|
116
|
-
afterBuild() {
|
|
117
|
-
const /** @type {AstListener} */ linkListener = (e, data) => {
|
|
118
|
-
const {prevTarget} = e;
|
|
119
|
-
if (prevTarget?.type === 'link-target') {
|
|
120
|
-
const name = prevTarget.text(),
|
|
121
|
-
{title, interwiki, ns, valid} = this.normalizeTitle(name);
|
|
122
|
-
if (!valid) {
|
|
123
|
-
undo(e, data);
|
|
124
|
-
throw new Error(`非法的内链目标:${name}`);
|
|
125
|
-
} else if (this.type === 'category' && (interwiki || ns !== 14)
|
|
126
|
-
|| this.type === 'file' && (interwiki || ns !== 6)
|
|
127
|
-
) {
|
|
128
|
-
undo(e, data);
|
|
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, ':');
|
|
134
|
-
} else {
|
|
135
|
-
prevTarget.prepend(':');
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
this.setAttribute('name', title);
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
this.addEventListener(['remove', 'insert', 'replace', 'text'], linkListener);
|
|
142
|
-
return this;
|
|
143
|
-
}
|
|
144
|
-
|
|
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}]]`;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** @override */
|
|
155
|
-
getPadding() {
|
|
156
|
-
return 2;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** @override */
|
|
160
|
-
getGaps() {
|
|
161
|
-
return 1;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** @override */
|
|
165
|
-
text() {
|
|
166
|
-
const str = super.text('|');
|
|
167
|
-
return this.type === 'gallery-image' ? str : `[[${str}]]`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* 设置链接目标
|
|
172
|
-
* @param {string} link 链接目标
|
|
173
|
-
* @throws `SyntaxError` 非法的链接目标
|
|
174
|
-
*/
|
|
175
|
-
setTarget(link) {
|
|
176
|
-
link = String(link);
|
|
177
|
-
if (this.type === 'link' && !/^\s*[:#]/u.test(link)) {
|
|
178
|
-
link = `:${link}`;
|
|
179
|
-
}
|
|
180
|
-
const root = Parser.parse(`[[${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
|
|
181
|
-
{childNodes: {length}, firstElementChild} = root;
|
|
182
|
-
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 1) {
|
|
183
|
-
const msgs = {link: '内链', file: '文件链接', category: '分类', 'gallery-image': '文件链接'};
|
|
184
|
-
throw new SyntaxError(`非法的${msgs[this.type]}目标:${link}`);
|
|
185
|
-
}
|
|
186
|
-
const {firstChild} = firstElementChild;
|
|
187
|
-
firstElementChild.destroy(true);
|
|
188
|
-
this.firstElementChild.safeReplaceWith(firstChild);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* 设置跨语言链接
|
|
193
|
-
* @param {string} lang 语言前缀
|
|
194
|
-
* @param {string} link 页面标题
|
|
195
|
-
* @throws `SyntaxError` 非法的跨语言链接
|
|
196
|
-
*/
|
|
197
|
-
setLangLink(lang, link) {
|
|
198
|
-
if (typeof lang !== 'string') {
|
|
199
|
-
this.typeError('setLangLink', 'String');
|
|
200
|
-
}
|
|
201
|
-
link = String(link).trim();
|
|
202
|
-
const [char] = link;
|
|
203
|
-
if (char === '#') {
|
|
204
|
-
throw new SyntaxError(`跨语言链接不能仅为fragment!`);
|
|
205
|
-
} else if (char === ':') {
|
|
206
|
-
link = link.slice(1);
|
|
207
|
-
}
|
|
208
|
-
const root = Parser.parse(`[[${lang}:${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
|
|
209
|
-
/** @type {Token & {firstElementChild: LinkToken}} */ {childNodes: {length}, firstElementChild} = root;
|
|
210
|
-
if (length !== 1 || firstElementChild?.type !== 'link' || firstElementChild.childNodes.length !== 1
|
|
211
|
-
|| firstElementChild.interwiki !== lang.toLowerCase()
|
|
212
|
-
) {
|
|
213
|
-
throw new SyntaxError(`非法的跨语言链接目标:${lang}:${link}`);
|
|
214
|
-
}
|
|
215
|
-
const {firstChild} = firstElementChild;
|
|
216
|
-
firstElementChild.destroy(true);
|
|
217
|
-
this.firstElementChild.safeReplaceWith(firstChild);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* 设置fragment
|
|
222
|
-
* @param {string} fragment fragment
|
|
223
|
-
* @param {boolean} page 是否是其他页面
|
|
224
|
-
* @throws `SyntaxError` 非法的fragment
|
|
225
|
-
*/
|
|
226
|
-
#setFragment(fragment, page = true) {
|
|
227
|
-
fragment = String(fragment).replaceAll(/[<>[\]#|=]/gu, p => encodeURIComponent(p));
|
|
228
|
-
const include = this.getAttribute('include'),
|
|
229
|
-
config = this.getAttribute('config'),
|
|
230
|
-
root = Parser.parse(`[[${page ? `:${this.name}` : ''}#${fragment}]]`, include, 6, config),
|
|
231
|
-
{childNodes: {length}, firstElementChild} = root;
|
|
232
|
-
if (length !== 1 || firstElementChild?.type !== 'link' || firstElementChild.childNodes.length !== 1) {
|
|
233
|
-
throw new SyntaxError(`非法的 fragment:${fragment}`);
|
|
234
|
-
}
|
|
235
|
-
if (page) {
|
|
236
|
-
Parser.warn(`${this.constructor.name}.setFragment 方法会同时规范化页面名!`);
|
|
237
|
-
}
|
|
238
|
-
const {firstChild} = firstElementChild;
|
|
239
|
-
firstElementChild.destroy(true);
|
|
240
|
-
this.firstElementChild.safeReplaceWith(firstChild);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* 设置fragment
|
|
245
|
-
* @param {string} fragment fragment
|
|
246
|
-
*/
|
|
247
|
-
setFragment(fragment) {
|
|
248
|
-
this.#setFragment(fragment);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* 修改为到自身的链接
|
|
253
|
-
* @param {string} fragment fragment
|
|
254
|
-
* @throws `RangeError` 空fragment
|
|
255
|
-
*/
|
|
256
|
-
asSelfLink(fragment = this.fragment) {
|
|
257
|
-
fragment = String(fragment);
|
|
258
|
-
if (!fragment.trim()) {
|
|
259
|
-
throw new RangeError(`${this.constructor.name}.asSelfLink 方法必须指定非空的 fragment!`);
|
|
260
|
-
}
|
|
261
|
-
this.#setFragment(fragment, false);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* 设置链接显示文字
|
|
266
|
-
* @param {string} linkText 链接显示文字
|
|
267
|
-
* @throws `SyntaxError` 非法的链接显示文字
|
|
268
|
-
*/
|
|
269
|
-
setLinkText(linkText = '') {
|
|
270
|
-
linkText = String(linkText);
|
|
271
|
-
let lastElementChild;
|
|
272
|
-
const config = this.getAttribute('config');
|
|
273
|
-
if (linkText) {
|
|
274
|
-
const root = Parser.parse(`[[${
|
|
275
|
-
this.type === 'category' ? 'Category:' : ''
|
|
276
|
-
}L|${linkText}]]`, this.getAttribute('include'), 6, config),
|
|
277
|
-
{childNodes: {length}, firstElementChild} = root;
|
|
278
|
-
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 2) {
|
|
279
|
-
throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${noWrap(linkText)}`);
|
|
280
|
-
}
|
|
281
|
-
({lastElementChild} = firstElementChild);
|
|
282
|
-
} else {
|
|
283
|
-
lastElementChild = Parser.run(() => new Token('', config));
|
|
284
|
-
lastElementChild.setAttribute('stage', 7).type = 'link-text';
|
|
285
|
-
}
|
|
286
|
-
if (this.childNodes.length === 1) {
|
|
287
|
-
this.appendChild(lastElementChild);
|
|
288
|
-
} else {
|
|
289
|
-
this.lastElementChild.safeReplaceWith(lastElementChild);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* 自动生成管道符后的链接文字
|
|
295
|
-
* @throws `Error` 带有"#"或"%"时不可用
|
|
296
|
-
*/
|
|
297
|
-
pipeTrick() {
|
|
298
|
-
const linkText = this.firstElementChild.text();
|
|
299
|
-
if (linkText.includes('#') || linkText.includes('%')) {
|
|
300
|
-
throw new Error('Pipe trick 不能用于带有"#"或"%"的场合!');
|
|
301
|
-
}
|
|
302
|
-
const m1 = /^:?(?:[ \w\x80-\xFF-]+:)?([^(]+)\(.+\)$/u.exec(linkText);
|
|
303
|
-
if (m1) {
|
|
304
|
-
this.setLinkText(m1[1].trim());
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
const m2 = /^:?(?:[ \w\x80-\xFF-]+:)?([^(]+)(.+)$/u.exec(linkText);
|
|
308
|
-
if (m2) {
|
|
309
|
-
this.setLinkText(m2[1].trim());
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
const m3 = /^:?(?:[ \w\x80-\xFF-]+:)?(.+?)(?:(?<!\()\(.+\))?(?:, |,|، )./u.exec(linkText);
|
|
313
|
-
if (m3) {
|
|
314
|
-
this.setLinkText(m3[1].trim());
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
this.setLinkText(linkText);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
Parser.classes.LinkToken = __filename;
|
|
322
|
-
module.exports = LinkToken;
|