wikiparser-node 0.0.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 +229 -0
- package/LICENSE +674 -0
- package/README.md +1896 -0
- package/config/default.json +766 -0
- package/config/llwiki.json +686 -0
- package/config/moegirl.json +721 -0
- package/index.js +159 -0
- package/jsconfig.json +7 -0
- package/lib/element.js +690 -0
- package/lib/node.js +357 -0
- package/lib/ranges.js +122 -0
- package/lib/title.js +57 -0
- package/mixin/attributeParent.js +67 -0
- package/mixin/fixedToken.js +32 -0
- package/mixin/hidden.js +22 -0
- package/package.json +30 -0
- package/parser/brackets.js +107 -0
- package/parser/commentAndExt.js +61 -0
- package/parser/externalLinks.js +30 -0
- package/parser/hrAndDoubleUnderscore.js +26 -0
- package/parser/html.js +41 -0
- package/parser/links.js +92 -0
- package/parser/magicLinks.js +40 -0
- package/parser/quotes.js +63 -0
- package/parser/table.js +97 -0
- package/src/arg.js +150 -0
- package/src/atom/hidden.js +10 -0
- package/src/atom/index.js +33 -0
- package/src/attribute.js +342 -0
- package/src/extLink.js +116 -0
- package/src/heading.js +91 -0
- package/src/html.js +144 -0
- package/src/imageParameter.js +172 -0
- package/src/index.js +602 -0
- package/src/link/category.js +88 -0
- package/src/link/file.js +201 -0
- package/src/link/index.js +214 -0
- package/src/listToken.js +47 -0
- package/src/magicLink.js +66 -0
- package/src/nowiki/comment.js +45 -0
- package/src/nowiki/doubleUnderscore.js +42 -0
- package/src/nowiki/hr.js +41 -0
- package/src/nowiki/index.js +37 -0
- package/src/nowiki/noinclude.js +24 -0
- package/src/nowiki/quote.js +37 -0
- package/src/onlyinclude.js +42 -0
- package/src/parameter.js +165 -0
- package/src/syntax.js +80 -0
- package/src/table/index.js +867 -0
- package/src/table/td.js +259 -0
- package/src/table/tr.js +244 -0
- package/src/tagPair/ext.js +85 -0
- package/src/tagPair/include.js +45 -0
- package/src/tagPair/index.js +91 -0
- package/src/transclude.js +627 -0
- package/tool/index.js +898 -0
- package/typings/element.d.ts +28 -0
- package/typings/index.d.ts +49 -0
- package/typings/node.d.ts +23 -0
- package/typings/parser.d.ts +9 -0
- package/typings/table.d.ts +14 -0
- package/typings/token.d.ts +21 -0
- package/typings/tool.d.ts +10 -0
- package/util/debug.js +70 -0
- package/util/string.js +60 -0
package/tool/index.js
ADDED
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {typeError, externalUse} = require('../util/debug'),
|
|
4
|
+
{text} = require('../util/string'),
|
|
5
|
+
Token = require('../src'),
|
|
6
|
+
assert = require('assert/strict');
|
|
7
|
+
|
|
8
|
+
const /** @type {WeakMap<Token, Record<string, any>>} */ dataStore = new WeakMap();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string|Token|Token[]} selector
|
|
12
|
+
* @returns {(token: Token) => boolean}
|
|
13
|
+
*/
|
|
14
|
+
const matchesGenerator = selector => {
|
|
15
|
+
if (typeof selector === 'string') {
|
|
16
|
+
return token => token instanceof Token && token.matches(selector);
|
|
17
|
+
} else if (Array.isArray(selector)) {
|
|
18
|
+
return token => selector.includes(token);
|
|
19
|
+
} else if (selector instanceof Token) {
|
|
20
|
+
return token => token === selector;
|
|
21
|
+
}
|
|
22
|
+
typeError('String', 'Token', 'Array');
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** @extends {Array<Token>} */
|
|
26
|
+
class TokenCollection extends Array {
|
|
27
|
+
/** @type {TokenCollection} */ prevObject;
|
|
28
|
+
/** @type {Set<Token>} */ #roots = new Set();
|
|
29
|
+
|
|
30
|
+
/** @param {...string|Token} arr */
|
|
31
|
+
constructor(...arr) {
|
|
32
|
+
super();
|
|
33
|
+
if (arr.length === 1 && arr[0] === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const token of arr) {
|
|
37
|
+
if (token === undefined) {
|
|
38
|
+
continue;
|
|
39
|
+
} else if (typeof token === 'string') {
|
|
40
|
+
this.push(token);
|
|
41
|
+
} else if (!(token instanceof Token)) {
|
|
42
|
+
typeError('String', 'Token');
|
|
43
|
+
} else if (!this.includes(token)) {
|
|
44
|
+
this.#roots.add(token.getRootNode());
|
|
45
|
+
this.push(token);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
Object.defineProperty(this, 'prevObject', {enumerable: false});
|
|
49
|
+
this.#sort();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#sort() {
|
|
53
|
+
if (this.some(token => typeof token === 'string')) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const rootArray = [...this.#roots];
|
|
57
|
+
this.sort((a, b) => {
|
|
58
|
+
const aRoot = a.getRootNode(),
|
|
59
|
+
bRoot = b.getRootNode();
|
|
60
|
+
return aRoot === bRoot ? a.comparePosition(b) : rootArray.indexOf(aRoot) - rootArray.indexOf(bRoot);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
toArray() {
|
|
65
|
+
return Array.from(this);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_filter(selector = '') {
|
|
69
|
+
const arr = this.toArray().filter(ele => ele instanceof Token);
|
|
70
|
+
if (selector) {
|
|
71
|
+
return arr.filter(ele => ele.matches(selector));
|
|
72
|
+
}
|
|
73
|
+
return arr;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
_find() {
|
|
77
|
+
return super.find(ele => ele instanceof Token);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** @param {(arr: Token[]) => (string|Token)[]} map */
|
|
81
|
+
_create(map, filter = '') {
|
|
82
|
+
const $arr = $(map(this._filter())),
|
|
83
|
+
$filtered = filter ? $arr.filter(filter) : $arr;
|
|
84
|
+
$filtered.prevObject = this;
|
|
85
|
+
return $filtered;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @template {unknown} T
|
|
90
|
+
* @param {T} num
|
|
91
|
+
* @returns {T extends number ? string|Token : (string|Token)[]}
|
|
92
|
+
*/
|
|
93
|
+
get(num) {
|
|
94
|
+
if (typeof num === 'number') {
|
|
95
|
+
return this.at(num);
|
|
96
|
+
}
|
|
97
|
+
return this.toArray();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** @param {CollectionCallback<void, string|Token>} callback */
|
|
101
|
+
each(callback) {
|
|
102
|
+
this.forEach((ele, i) => { // 不能使用`for`...`of`
|
|
103
|
+
callback.call(ele, i, ele);
|
|
104
|
+
});
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** @param {CollectionCallback<any, string|Token>} callback */
|
|
109
|
+
map(callback) {
|
|
110
|
+
const arr = this.toArray().map((ele, i) => callback.call(ele, i, ele));
|
|
111
|
+
try {
|
|
112
|
+
const $arr = $(arr);
|
|
113
|
+
$arr.prevObject = this;
|
|
114
|
+
return $arr;
|
|
115
|
+
} catch {
|
|
116
|
+
return arr;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {number} start
|
|
122
|
+
* @param {number} end
|
|
123
|
+
*/
|
|
124
|
+
slice(start, end) {
|
|
125
|
+
const $arr = $(this.toArray().slice(start, end));
|
|
126
|
+
$arr.prevObject = this;
|
|
127
|
+
return $arr;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
first() {
|
|
131
|
+
return this.slice(0, 1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
last() {
|
|
135
|
+
return this.slice(-1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @param {number} i */
|
|
139
|
+
eq(i) {
|
|
140
|
+
return this.slice(i, i === -1 ? undefined : i + 1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** 使用空字符串join */
|
|
144
|
+
toString() {
|
|
145
|
+
return this.toArray().map(String).join('');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @template {unknown} T
|
|
150
|
+
* @param {T} str
|
|
151
|
+
* @returns {T extends string|Function ? this : string}
|
|
152
|
+
*/
|
|
153
|
+
text(str) {
|
|
154
|
+
/** @type {(ele: Token, i: number, str: string) => string} */
|
|
155
|
+
const callback = typeof str === 'function' ? str.call : () => str;
|
|
156
|
+
if (['string', 'function'].includes(typeof str)) {
|
|
157
|
+
for (const [i, ele] of this.entries()) {
|
|
158
|
+
if (ele instanceof Token) {
|
|
159
|
+
try {
|
|
160
|
+
ele.replaceChildren(callback(ele, i, ele.text()));
|
|
161
|
+
} catch {}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
return text(this.toArray());
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** @param {string|CollectionCallback<boolean, string|Token>|Token|Token[]} selector */
|
|
170
|
+
is(selector) {
|
|
171
|
+
if (typeof selector === 'function') {
|
|
172
|
+
return this.some((ele, i) => selector.call(ele, i, ele));
|
|
173
|
+
}
|
|
174
|
+
const matches = matchesGenerator(selector);
|
|
175
|
+
return this.some(matches);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** @param {string|CollectionCallback<boolean, string|Token>|Token|Token[]} selector */
|
|
179
|
+
filter(selector) {
|
|
180
|
+
const arr = this.toArray();
|
|
181
|
+
let $arr;
|
|
182
|
+
if (typeof selector === 'function') {
|
|
183
|
+
$arr = $(arr.filter((ele, i) => selector.call(ele, i, ele)));
|
|
184
|
+
} else {
|
|
185
|
+
const matches = matchesGenerator(selector);
|
|
186
|
+
$arr = $(arr.filter(matches));
|
|
187
|
+
}
|
|
188
|
+
$arr.prevObject = this;
|
|
189
|
+
return $arr;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** @param {string|CollectionCallback<boolean, string|Token>|Token|Token[]} selector */
|
|
193
|
+
not(selector) {
|
|
194
|
+
const arr = this.toArray();
|
|
195
|
+
let $arr;
|
|
196
|
+
if (typeof selector === 'function') {
|
|
197
|
+
$arr = $(arr.filter((ele, i) => !selector.call(ele, i, ele)));
|
|
198
|
+
} else if (typeof selector === 'string') {
|
|
199
|
+
$arr = $(arr.filter(ele => ele instanceof Token && !ele.matches(selector)));
|
|
200
|
+
} else {
|
|
201
|
+
const matches = matchesGenerator(selector);
|
|
202
|
+
$arr = $(arr.filter(ele => !matches(ele)));
|
|
203
|
+
}
|
|
204
|
+
$arr.prevObject = this;
|
|
205
|
+
return $arr;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** @param {string|Token|Token[]} selector */
|
|
209
|
+
find(selector) {
|
|
210
|
+
let /** @type {CollectionMap} */ map;
|
|
211
|
+
if (typeof selector === 'string') {
|
|
212
|
+
map = arr => arr.flatMap(token => token.querySelectorAll(selector));
|
|
213
|
+
} else if (Array.isArray(selector)) {
|
|
214
|
+
map = arr => [...selector].filter(ele => arr.some(token => token.contains(ele)));
|
|
215
|
+
} else if (selector instanceof Token) {
|
|
216
|
+
map = arr => arr.some(token => token.contains(selector)) ? [selector] : [];
|
|
217
|
+
} else {
|
|
218
|
+
typeError('String', 'Token', 'Array');
|
|
219
|
+
}
|
|
220
|
+
return this._create(map);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** @param {string|Token} selector */
|
|
224
|
+
has(selector) {
|
|
225
|
+
const arr = this._filter();
|
|
226
|
+
if (typeof selector === 'string') {
|
|
227
|
+
return arr.some(ele => ele.querySelector(selector));
|
|
228
|
+
} else if (selector instanceof Token) {
|
|
229
|
+
return arr.some(ele => ele.contains(selector));
|
|
230
|
+
}
|
|
231
|
+
typeError('String', 'Token');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** @param {string} selector */
|
|
235
|
+
closest(selector) {
|
|
236
|
+
if (typeof selector !== 'string') {
|
|
237
|
+
typeError('String');
|
|
238
|
+
}
|
|
239
|
+
return this._create(arr => arr.map(ele => ele.closest(selector)));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
index() {
|
|
243
|
+
const firstToken = this._find();
|
|
244
|
+
if (!firstToken) {
|
|
245
|
+
return -1;
|
|
246
|
+
}
|
|
247
|
+
const {parentNode} = firstToken;
|
|
248
|
+
return parentNode ? parentNode.childNodes.indexOf(firstToken) : 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** @param {string|Token|(string|Token)[]} elements */
|
|
252
|
+
add(elements) {
|
|
253
|
+
elements = Array.isArray(elements) ? elements : [elements];
|
|
254
|
+
const $arr = $([...this, ...elements]);
|
|
255
|
+
$arr.prevObject = this;
|
|
256
|
+
return $arr;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** @param {string} selector */
|
|
260
|
+
addBack(selector) {
|
|
261
|
+
return this.add(selector ? this.prevObject.filter(selector) : this.prevObject);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
parent(selector = '') {
|
|
265
|
+
return this._create(arr => arr.map(ele => ele.parentNode), selector);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
parents(selector = '') {
|
|
269
|
+
return this._create(arr => arr.flatMap(ele => ele.getAncestors()), selector);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
next(selector = '') {
|
|
273
|
+
return this._create(arr => arr.map(ele => ele.nextElementSibling), selector);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
prev(selector = '') {
|
|
277
|
+
return this._create(arr => arr.map(ele => ele.previousElementSibling), selector);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @param {number|(i: number) => number} start
|
|
282
|
+
* @param {number|(i: number) => number} count
|
|
283
|
+
*/
|
|
284
|
+
_siblings(start, count, selector = '') {
|
|
285
|
+
return this._create(arr => arr.flatMap(ele => {
|
|
286
|
+
const {parentElement} = ele;
|
|
287
|
+
if (!parentElement) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
const {children} = parentElement,
|
|
291
|
+
i = children.indexOf(ele);
|
|
292
|
+
children.splice(
|
|
293
|
+
typeof start === 'function' ? start(i) : start,
|
|
294
|
+
typeof count === 'function' ? count(i) : count,
|
|
295
|
+
);
|
|
296
|
+
return children;
|
|
297
|
+
}), selector);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
nextAll(selector = '') {
|
|
301
|
+
return this._siblings(0, i => i + 1, selector);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
prevAll(selector = '') {
|
|
305
|
+
return this._siblings(i => i, Infinity, selector);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
siblings(selector = '') {
|
|
309
|
+
return this._siblings(i => i, 1, selector);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @param {'parents'|'nextAll'|'prevAll'} method
|
|
314
|
+
* @param {string|Token|Token[]} selector
|
|
315
|
+
*/
|
|
316
|
+
_until(method, selector, filter = '') {
|
|
317
|
+
const matches = matchesGenerator(selector);
|
|
318
|
+
return this._create(arr => arr.flatMap(ele => {
|
|
319
|
+
const tokens = $(ele)[method]().toArray(),
|
|
320
|
+
tokenArray = method === 'nextAll' ? tokens : tokens.reverse(),
|
|
321
|
+
until = tokenArray.findIndex(end => matches(end));
|
|
322
|
+
return tokenArray.slice(0, until);
|
|
323
|
+
}), filter);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** @param {string|Token|Token[]} selector */
|
|
327
|
+
parentsUntil(selector, filter = '') {
|
|
328
|
+
return this._until('parents', selector, filter);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** @param {string|Token|Token[]} selector */
|
|
332
|
+
nextUntil(selector, filter = '') {
|
|
333
|
+
return this._until('nextAll', selector, filter);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** @param {string|Token|Token[]} selector */
|
|
337
|
+
prevUntil(selector, filter = '') {
|
|
338
|
+
return this._until('prevAll', selector, filter);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
children(selector = '') {
|
|
342
|
+
return this._create(arr => arr.flatMap(ele => ele.children), selector);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
contents() {
|
|
346
|
+
return this._create(arr => arr.flatMap(ele => ele.childNodes));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** @param {[string|Record<string, any>]} key */
|
|
350
|
+
data(key, value) {
|
|
351
|
+
if (value !== undefined && typeof key !== 'string') {
|
|
352
|
+
typeError('String');
|
|
353
|
+
} else if (value === undefined && typeof key !== 'object') {
|
|
354
|
+
const data = $.dataStore.get(this._find());
|
|
355
|
+
return key === undefined ? data : data?.[key];
|
|
356
|
+
}
|
|
357
|
+
for (const token of this._filter()) {
|
|
358
|
+
if (!$.dataStore.has(token)) {
|
|
359
|
+
$.dataStore.set(token, {});
|
|
360
|
+
}
|
|
361
|
+
const data = $.dataStore.get(token);
|
|
362
|
+
if (typeof key === 'string') {
|
|
363
|
+
data[key] = value;
|
|
364
|
+
} else {
|
|
365
|
+
Object.assign(data, key);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return this;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** @param {string|string[]} name */
|
|
372
|
+
removeData(name) {
|
|
373
|
+
if (name !== undefined && typeof name !== 'string' && !Array.isArray(name)) {
|
|
374
|
+
typeError('String', 'Array');
|
|
375
|
+
}
|
|
376
|
+
name = typeof name === 'string' ? name.split(/\s/) : name;
|
|
377
|
+
for (const token of this._filter()) {
|
|
378
|
+
if (!$.dataStore.has(token)) {
|
|
379
|
+
continue;
|
|
380
|
+
} else if (name === undefined) {
|
|
381
|
+
$.dataStore.delete(token);
|
|
382
|
+
} else {
|
|
383
|
+
const data = $.dataStore.get(token);
|
|
384
|
+
for (const key of name) {
|
|
385
|
+
delete data[key];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return this;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @param {string|Record<string, AstListener>} events
|
|
394
|
+
* @param {string|AstListener} selector
|
|
395
|
+
* @param {AstListener} handler
|
|
396
|
+
*/
|
|
397
|
+
_addEventListener(events, selector, handler, once = false) {
|
|
398
|
+
if (!['string', 'object'].includes(typeof events)) {
|
|
399
|
+
typeError('String', 'Object');
|
|
400
|
+
} else if (typeof selector === 'function') {
|
|
401
|
+
handler = selector;
|
|
402
|
+
selector = undefined;
|
|
403
|
+
}
|
|
404
|
+
const eventPair = typeof events === 'string'
|
|
405
|
+
? events.split(/\s/).map(/** @returns {[string, AstListener]} */ event => [event, handler])
|
|
406
|
+
: Object.entries(events);
|
|
407
|
+
for (const token of this._filter(selector)) {
|
|
408
|
+
for (const [event, listener] of eventPair) {
|
|
409
|
+
token.addEventListener(event, listener, {once});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return this;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* @param {string|Record<string, AstListener>} events
|
|
417
|
+
* @param {string|AstListener} selector
|
|
418
|
+
* @param {AstListener} handler
|
|
419
|
+
*/
|
|
420
|
+
on(events, selector, handler) {
|
|
421
|
+
return this._addEventListener(events, selector, handler);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @param {string|Record<string, AstListener>} events
|
|
426
|
+
* @param {string|AstListener} selector
|
|
427
|
+
* @param {AstListener} handler
|
|
428
|
+
*/
|
|
429
|
+
one(events, selector, handler) {
|
|
430
|
+
return this._addEventListener(events, selector, handler, true);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* @param {string|Record<string, AstListener>|undefined} events
|
|
435
|
+
* @param {string|AstListener} selector
|
|
436
|
+
* @param {AstListener} handler
|
|
437
|
+
*/
|
|
438
|
+
off(events, selector, handler) {
|
|
439
|
+
if (!['string', 'object', 'undefined'].includes(typeof events)) {
|
|
440
|
+
typeError('String', 'Object');
|
|
441
|
+
}
|
|
442
|
+
handler = typeof selector === 'function' ? selector : handler;
|
|
443
|
+
let eventPair;
|
|
444
|
+
if (events) {
|
|
445
|
+
eventPair = typeof events === 'string'
|
|
446
|
+
? events.split(/\s/).map(/** @returns {[string, AstListener]} */ event => [event, handler])
|
|
447
|
+
: Object.entries(events);
|
|
448
|
+
}
|
|
449
|
+
for (const token of this._filter(selector)) {
|
|
450
|
+
if (events === undefined) {
|
|
451
|
+
token.removeAllEventListeners();
|
|
452
|
+
} else {
|
|
453
|
+
for (const [event, listener] of eventPair) {
|
|
454
|
+
if (typeof event !== 'string' || !['function', 'undefined'].includes(typeof listener)) {
|
|
455
|
+
typeError('String', 'Function');
|
|
456
|
+
} else if (listener) {
|
|
457
|
+
token.removeEventListener(event, listener);
|
|
458
|
+
} else {
|
|
459
|
+
token.removeAllEventListeners(event);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return this;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** @param {string|Event} event */
|
|
468
|
+
trigger(event, data) {
|
|
469
|
+
for (const token of this._filter()) {
|
|
470
|
+
const e = typeof event === 'string' ? new Event(event, {bubbles: true}) : new Event(event.type, event);
|
|
471
|
+
token.dispatchEvent(e, data);
|
|
472
|
+
}
|
|
473
|
+
return this;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/** @param {string|Event} event */
|
|
477
|
+
triggerHandler(event, data) {
|
|
478
|
+
const firstToken = this._find();
|
|
479
|
+
if (!firstToken) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const e = typeof event === 'string' ? new Event(event) : event,
|
|
483
|
+
listeners = firstToken.listEventListeners(typeof event === 'string' ? event : event.type);
|
|
484
|
+
let result;
|
|
485
|
+
for (const listener of listeners) {
|
|
486
|
+
result = listener(e, data);
|
|
487
|
+
}
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @param {'append'|'prepend'|'before'|'after'|'replaceChildren'|'replaceWith'} method
|
|
493
|
+
* @param {string|Token|CollectionCallback<string|Token|(string|Token)[], string>} content
|
|
494
|
+
* @param {...string|Token|(string|Token)[]} additional
|
|
495
|
+
*/
|
|
496
|
+
_insert(method, content, ...additional) {
|
|
497
|
+
if (typeof content === 'function') {
|
|
498
|
+
for (const [i, token] of this.entries()) {
|
|
499
|
+
if (token instanceof Token) {
|
|
500
|
+
const result = content.call(token, i, token.toString());
|
|
501
|
+
if (typeof result === 'string' || result instanceof Token) {
|
|
502
|
+
token[method](result);
|
|
503
|
+
} else if (Array.isArray(result)) {
|
|
504
|
+
token[method](...result);
|
|
505
|
+
} else {
|
|
506
|
+
typeError('String', 'Token');
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
} else {
|
|
511
|
+
for (const token of this) {
|
|
512
|
+
if (token instanceof Token) {
|
|
513
|
+
token[method](...content, ...additional.flat());
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return this;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* @param {string|Token|CollectionCallback<string|Token|(string|Token)[], string>} content
|
|
522
|
+
* @param {...string|Token|(string|Token)[]} additional
|
|
523
|
+
*/
|
|
524
|
+
append(content, ...additional) {
|
|
525
|
+
return this._insert('append', content, ...additional);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* @param {string|Token|CollectionCallback<string|Token|(string|Token)[], string>} content
|
|
530
|
+
* @param {...string|Token|(string|Token)[]} additional
|
|
531
|
+
*/
|
|
532
|
+
prepend(content, ...additional) {
|
|
533
|
+
return this._insert('prepend', content, ...additional);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* @param {string|Token|CollectionCallback<string|Token|(string|Token)[], string>} content
|
|
538
|
+
* @param {...string|Token|(string|Token)[]} additional
|
|
539
|
+
*/
|
|
540
|
+
before(content, ...additional) {
|
|
541
|
+
return this._insert('before', content, ...additional);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* @param {string|Token|CollectionCallback<string|Token|(string|Token)[], string>} content
|
|
546
|
+
* @param {...string|Token|(string|Token)[]} additional
|
|
547
|
+
*/
|
|
548
|
+
after(content, ...additional) {
|
|
549
|
+
return this._insert('after', content, ...additional);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* @template {unknown} T
|
|
554
|
+
* @param {T} content
|
|
555
|
+
* @returns {T extends string|Token|CollectionCallback<string|Token|(string|Token)[], string> ? this : string}
|
|
556
|
+
*/
|
|
557
|
+
html(content) {
|
|
558
|
+
if (content === undefined) {
|
|
559
|
+
return this.toString();
|
|
560
|
+
}
|
|
561
|
+
return this._insert('replaceChildren', content);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/** @param {string|Token|CollectionCallback<string|Token|(string|Token)[], string>} content */
|
|
565
|
+
replaceWith(content) {
|
|
566
|
+
return this._insert('replaceWith', content);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
remove(selector = '') {
|
|
570
|
+
for (const token of this.removeData()._filter(selector)) {
|
|
571
|
+
token.remove();
|
|
572
|
+
token.removeAllEventListeners();
|
|
573
|
+
}
|
|
574
|
+
return this;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
detach(selector = '') {
|
|
578
|
+
for (const token of this._filter(selector)) {
|
|
579
|
+
token.remove();
|
|
580
|
+
}
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
empty() {
|
|
585
|
+
for (const token of this) {
|
|
586
|
+
if (token instanceof Token) {
|
|
587
|
+
token.replaceChildren();
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return this;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* @param {'append'|'prepend'|'before'|'after'|'replaceWith'} method
|
|
595
|
+
* @param {Token|Token[]} target
|
|
596
|
+
*/
|
|
597
|
+
_insertAdjacent(method, target) {
|
|
598
|
+
if (target instanceof Token) {
|
|
599
|
+
target[method](...this);
|
|
600
|
+
} else if (Array.isArray(target)) {
|
|
601
|
+
for (const token of target) {
|
|
602
|
+
if (token instanceof Token) {
|
|
603
|
+
token[method](...this);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
typeError('Token', 'Array');
|
|
608
|
+
}
|
|
609
|
+
return this;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** @param {Token|Token[]} target */
|
|
613
|
+
appendTo(target) {
|
|
614
|
+
return this._insertAdjacent('append', target);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/** @param {Token|Token[]} target */
|
|
618
|
+
prependTo(target) {
|
|
619
|
+
return this._insertAdjacent('prepend', target);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** @param {Token|Token[]} target */
|
|
623
|
+
insertBefore(target) {
|
|
624
|
+
return this._insertAdjacent('before', target);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** @param {Token|Token[]} target */
|
|
628
|
+
insertAfter(target) {
|
|
629
|
+
return this._insertAdjacent('after', target);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** @param {Token|Token[]} target */
|
|
633
|
+
replaceAll(target) {
|
|
634
|
+
return this._insertAdjacent('replaceWith', target);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/** @param {string|string[]|CollectionCallback<string, string>} value */
|
|
638
|
+
val(value) {
|
|
639
|
+
if (value === undefined) {
|
|
640
|
+
const firstToken = this._find();
|
|
641
|
+
return firstToken?.getValue && firstToken.getValue();
|
|
642
|
+
}
|
|
643
|
+
let /** @type {(i: number, token: Token) => string} */ toValue;
|
|
644
|
+
if (typeof value === 'string') {
|
|
645
|
+
toValue = () => value;
|
|
646
|
+
} else if (typeof value === 'function') {
|
|
647
|
+
toValue = (i, token) => value.call(token, i, token.getValue && token.getValue());
|
|
648
|
+
} else if (Array.isArray(value)) {
|
|
649
|
+
toValue = i => value[i];
|
|
650
|
+
} else {
|
|
651
|
+
typeError('String', 'Array', 'Function');
|
|
652
|
+
}
|
|
653
|
+
for (const [i, token] of this.entries()) {
|
|
654
|
+
if (token instanceof Token && typeof token.setValue === 'function' && token.setValue.length === 1) {
|
|
655
|
+
token.setValue(toValue(i, token));
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return this;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* @param {'getAttr'|'getAttribute'} getter
|
|
663
|
+
* @param {'setAttr'|'setAttribute'} setter
|
|
664
|
+
* @param {string|Record<string, string>} name
|
|
665
|
+
* @param {any|CollectionCallback<string, any>} value
|
|
666
|
+
*/
|
|
667
|
+
_attr(getter, setter, name, value) {
|
|
668
|
+
if (typeof name === 'string' && value === undefined) {
|
|
669
|
+
const firstToken = this._find();
|
|
670
|
+
return firstToken?.[getter] && firstToken[getter](name);
|
|
671
|
+
}
|
|
672
|
+
for (const [i, token] of this.entries()) {
|
|
673
|
+
if (token instanceof Token && typeof token[setter] === 'function') {
|
|
674
|
+
if (typeof value === 'string') {
|
|
675
|
+
token[setter](name, value);
|
|
676
|
+
} else if (typeof value === 'function') {
|
|
677
|
+
token[setter](name, value.call(token, i, token[getter] && token[getter](name)));
|
|
678
|
+
} else if (typeof name === 'object') {
|
|
679
|
+
for (const [k, v] of Object.entries(name)) {
|
|
680
|
+
token[setter](k, v);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return this;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* @param {string|Record<string, string>} name
|
|
690
|
+
* @param {string|CollectionCallback<string, string>} value
|
|
691
|
+
*/
|
|
692
|
+
attr(name, value) {
|
|
693
|
+
return this._attr('getAttr', 'setAttr', name, value);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* @param {string|Record<string, string>} name
|
|
698
|
+
* @param {any|CollectionCallback<string, any>} value
|
|
699
|
+
*/
|
|
700
|
+
prop(name, value) {
|
|
701
|
+
return this._attr('getAttribute', 'setAttribute', name, value);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* @param {'removeAttr'|'removeAttribute'} method
|
|
706
|
+
* @param {string} name
|
|
707
|
+
*/
|
|
708
|
+
_removeAttr(method, name) {
|
|
709
|
+
for (const token of this) {
|
|
710
|
+
if (token instanceof Token && typeof token[method] === 'function') {
|
|
711
|
+
token[method](name);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return this;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/** @param {string} name */
|
|
718
|
+
removeAttr(name) {
|
|
719
|
+
return this._removeAttr('removeAttr', name);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/** @param {string} name */
|
|
723
|
+
removeProp(name) {
|
|
724
|
+
return this._removeAttr('removeAttribute', name);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* 出于实用角度,与jQuery的实现方式不同
|
|
729
|
+
* @param {string[]|CollectionCallback<string[], undefined>} wrapper
|
|
730
|
+
*/
|
|
731
|
+
wrapAll(wrapper) {
|
|
732
|
+
if (typeof wrapper !== 'function' && !Array.isArray(wrapper)) {
|
|
733
|
+
typeError('Array', 'Function');
|
|
734
|
+
}
|
|
735
|
+
const firstToken = this._find(),
|
|
736
|
+
error = new Error('wrapAll 的主体应为同一个父节点下的连续子节点!');
|
|
737
|
+
if (!firstToken || !firstToken.parentNode) {
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
const {parentNode} = firstToken,
|
|
741
|
+
{childNodes} = parentNode,
|
|
742
|
+
i = childNodes.indexOf(firstToken),
|
|
743
|
+
consecutiveSiblings = childNodes.slice(i, i + this.length);
|
|
744
|
+
try {
|
|
745
|
+
assert.deepStrictEqual(this.toArray(), consecutiveSiblings);
|
|
746
|
+
} catch (e) {
|
|
747
|
+
if (e instanceof assert.AssertionError) {
|
|
748
|
+
throw error;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
const ranges = parentNode.getAttribute('protectedChildren'),
|
|
752
|
+
index = ranges.applyTo(childNodes).find(n => n >= i && n < i + this.length);
|
|
753
|
+
if (index !== undefined) {
|
|
754
|
+
throw new Error(`第 ${index} 个子节点受到保护!`);
|
|
755
|
+
}
|
|
756
|
+
const [pre, post] = typeof wrapper === 'function' ? wrapper.call(firstToken) : wrapper,
|
|
757
|
+
config = firstToken.getRootNode().getAttribute('config'),
|
|
758
|
+
token = new Token(`${pre}${this.toString()}${post}`, config).parse();
|
|
759
|
+
if (token.childNodes.length !== 1) {
|
|
760
|
+
throw new RangeError(`非法的 wrapper:\n${pre}\n${post}`);
|
|
761
|
+
}
|
|
762
|
+
for (let j = i + this.length - 1; j >= i; j--) {
|
|
763
|
+
parentNode.removeAt(j);
|
|
764
|
+
}
|
|
765
|
+
parentNode.insertAt(token.firstChild, i);
|
|
766
|
+
return this;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* @param {'html'|'replaceWith'} method
|
|
771
|
+
* @param {string[]|CollectionCallback<string[], undefined>} wrapper
|
|
772
|
+
*/
|
|
773
|
+
_wrap(method, wrapper) {
|
|
774
|
+
if (typeof wrapper !== 'function' && !Array.isArray(wrapper)) {
|
|
775
|
+
typeError('Array', 'Function');
|
|
776
|
+
}
|
|
777
|
+
return this[method](
|
|
778
|
+
/**
|
|
779
|
+
* @this {string|Token}
|
|
780
|
+
* @param {number} i
|
|
781
|
+
* @param {string} string
|
|
782
|
+
*/
|
|
783
|
+
function(i, string) {
|
|
784
|
+
if (!(this instanceof Token)) {
|
|
785
|
+
return string;
|
|
786
|
+
}
|
|
787
|
+
const [pre, post] = typeof wrapper === 'function' ? wrapper.call(this, i) : wrapper,
|
|
788
|
+
config = this.getRootNode().getAttribute('config'),
|
|
789
|
+
token = new Token(`${pre}${string}${post}`, config).parse();
|
|
790
|
+
if (token.childNodes.length !== 1) {
|
|
791
|
+
throw new RangeError(`非法的 wrapper:\n${pre}\n${post}`);
|
|
792
|
+
}
|
|
793
|
+
return token.firstChild;
|
|
794
|
+
},
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/** @param {string[]|CollectionCallback<string[], undefined>} wrapper */
|
|
799
|
+
wrapInner(wrapper) {
|
|
800
|
+
return this._wrap('html', wrapper);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/** @param {string[]|CollectionCallback<string[], undefined>} wrapper */
|
|
804
|
+
wrap(wrapper) {
|
|
805
|
+
return this._wrap('replaceWith', wrapper);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
offset() {
|
|
809
|
+
const firstToken = this._find();
|
|
810
|
+
if (!firstToken) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
const {top, left} = firstToken.getBoundingClientRect();
|
|
814
|
+
return {top, left};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
position() {
|
|
818
|
+
const style = this._find()?.style;
|
|
819
|
+
return style && {top: style.top, left: style.left};
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
height() {
|
|
823
|
+
return this._find()?.offsetHeight;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
width() {
|
|
827
|
+
return this._find()?.offsetWidth;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/** @param {string|Token|Iterable<string|Token>} tokens */
|
|
832
|
+
const $ = tokens => {
|
|
833
|
+
if (typeof tokens === 'string' || tokens instanceof Token) {
|
|
834
|
+
tokens = [tokens];
|
|
835
|
+
}
|
|
836
|
+
return new Proxy(new TokenCollection(...tokens), {
|
|
837
|
+
/** @param {PropertyKey} prop */
|
|
838
|
+
get(obj, prop) {
|
|
839
|
+
if (prop === Symbol.iterator || typeof obj[prop] !== 'function'
|
|
840
|
+
|| !prop.startsWith('_') && Object.getOwnPropertyDescriptor(obj.constructor.prototype, prop)
|
|
841
|
+
|| !externalUse(prop, true)
|
|
842
|
+
) {
|
|
843
|
+
return obj[prop];
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
set(obj, prop, val) {
|
|
847
|
+
if (prop === 'prevObject' && (val === undefined || val instanceof TokenCollection)) {
|
|
848
|
+
obj[prop] = val;
|
|
849
|
+
return true;
|
|
850
|
+
}
|
|
851
|
+
return false;
|
|
852
|
+
},
|
|
853
|
+
});
|
|
854
|
+
};
|
|
855
|
+
$.hasData = /** @param {Token} element */ element => {
|
|
856
|
+
if (!(element instanceof Token)) {
|
|
857
|
+
typeError('Token');
|
|
858
|
+
}
|
|
859
|
+
return $.dataStore.has(element);
|
|
860
|
+
};
|
|
861
|
+
$.data = /** @type {(element: Token, key?: string, value?: any) => any} */ (element, key, value) => {
|
|
862
|
+
if (!(element instanceof Token)) {
|
|
863
|
+
typeError('Token');
|
|
864
|
+
} else if (key === undefined) {
|
|
865
|
+
return $.dataStore.get(element);
|
|
866
|
+
} else if (typeof key !== 'string') {
|
|
867
|
+
typeError('String');
|
|
868
|
+
} else if (value === undefined) {
|
|
869
|
+
return $.dataStore.get(element)?.[key];
|
|
870
|
+
} else if (!$.dataStore.has(element)) {
|
|
871
|
+
$.dataStore.set(element, {});
|
|
872
|
+
}
|
|
873
|
+
$.dataStore.get(element)[key] = value;
|
|
874
|
+
return value;
|
|
875
|
+
};
|
|
876
|
+
$.removeData = /** @type {(element: Token, name?: string) => void} */ (element, name) => {
|
|
877
|
+
if (!(element instanceof Token)) {
|
|
878
|
+
typeError('Token');
|
|
879
|
+
} else if (name === undefined) {
|
|
880
|
+
$.dataStore.delete(element);
|
|
881
|
+
} else if (typeof name !== 'string') {
|
|
882
|
+
typeError('String');
|
|
883
|
+
} else if ($.dataStore.has(element)) {
|
|
884
|
+
const data = $.dataStore.get(element);
|
|
885
|
+
delete data[name];
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
Object.defineProperty($, 'dataStore', {value: dataStore});
|
|
889
|
+
Object.defineProperty($, 'TokenCollection', {value: TokenCollection});
|
|
890
|
+
Object.defineProperty($, 'reload', {
|
|
891
|
+
value() {
|
|
892
|
+
delete require.cache[__filename];
|
|
893
|
+
const $$ = require('.');
|
|
894
|
+
return $$;
|
|
895
|
+
},
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
module.exports = $;
|