wikiparser-node 0.4.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/default.json +129 -66
- package/config/zhwiki.json +4 -4
- package/index.js +97 -65
- package/lib/element.js +159 -302
- package/lib/node.js +384 -198
- package/lib/ranges.js +3 -4
- package/lib/text.js +65 -36
- package/lib/title.js +9 -8
- package/mixin/fixedToken.js +4 -4
- package/mixin/hidden.js +2 -0
- package/mixin/sol.js +16 -7
- package/package.json +14 -3
- package/parser/brackets.js +8 -2
- package/parser/commentAndExt.js +1 -1
- package/parser/converter.js +1 -1
- package/parser/externalLinks.js +2 -2
- package/parser/hrAndDoubleUnderscore.js +8 -7
- package/parser/links.js +8 -9
- package/parser/magicLinks.js +1 -1
- package/parser/selector.js +5 -5
- package/parser/table.js +18 -16
- package/src/arg.js +71 -42
- package/src/atom/index.js +7 -5
- package/src/attribute.js +102 -64
- package/src/charinsert.js +91 -0
- package/src/converter.js +34 -15
- package/src/converterFlags.js +87 -40
- package/src/converterRule.js +59 -53
- package/src/extLink.js +45 -37
- package/src/gallery.js +71 -16
- package/src/hasNowiki/index.js +42 -0
- package/src/hasNowiki/pre.js +40 -0
- package/src/heading.js +41 -18
- package/src/html.js +76 -48
- package/src/imageParameter.js +73 -51
- package/src/imagemap.js +205 -0
- package/src/imagemapLink.js +43 -0
- package/src/index.js +243 -138
- package/src/link/category.js +10 -14
- package/src/link/file.js +112 -56
- package/src/link/galleryImage.js +74 -10
- package/src/link/index.js +86 -61
- package/src/magicLink.js +48 -21
- package/src/nested/choose.js +24 -0
- package/src/nested/combobox.js +23 -0
- package/src/nested/index.js +88 -0
- package/src/nested/references.js +23 -0
- package/src/nowiki/comment.js +18 -4
- package/src/nowiki/dd.js +2 -2
- package/src/nowiki/doubleUnderscore.js +16 -11
- package/src/nowiki/index.js +12 -0
- package/src/nowiki/quote.js +28 -1
- package/src/onlyinclude.js +15 -8
- package/src/paramTag/index.js +83 -0
- package/src/paramTag/inputbox.js +42 -0
- package/src/parameter.js +73 -46
- package/src/syntax.js +9 -1
- package/src/table/index.js +58 -44
- package/src/table/td.js +63 -63
- package/src/table/tr.js +52 -35
- package/src/tagPair/ext.js +60 -43
- package/src/tagPair/include.js +11 -1
- package/src/tagPair/index.js +29 -20
- package/src/transclude.js +214 -166
- package/tool/index.js +720 -439
- package/util/base.js +17 -0
- package/util/debug.js +1 -1
- package/{test/util.js → util/diff.js} +15 -19
- package/util/lint.js +40 -0
- package/util/string.js +37 -20
- package/.eslintrc.json +0 -714
- package/errors/README +0 -1
- package/jsconfig.json +0 -7
- package/printed/README +0 -1
- package/printed/example.json +0 -120
- package/test/api.js +0 -83
- package/test/real.js +0 -133
- package/test/test.js +0 -28
- package/typings/api.d.ts +0 -13
- package/typings/array.d.ts +0 -28
- package/typings/event.d.ts +0 -24
- package/typings/index.d.ts +0 -94
- package/typings/node.d.ts +0 -29
- package/typings/parser.d.ts +0 -16
- package/typings/table.d.ts +0 -14
- package/typings/token.d.ts +0 -22
- package/typings/tool.d.ts +0 -11
package/lib/node.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {typeError
|
|
3
|
+
const {typeError} = require('../util/debug'),
|
|
4
4
|
{text} = require('../util/string'),
|
|
5
5
|
assert = require('assert/strict'),
|
|
6
6
|
EventEmitter = require('events'),
|
|
@@ -21,11 +21,11 @@ class AstNode {
|
|
|
21
21
|
* @throws `RangeError` 指定位置不存在子节点
|
|
22
22
|
*/
|
|
23
23
|
#verifyChild = (i, addition = 0) => {
|
|
24
|
-
if (
|
|
24
|
+
if (!Number.isInteger(i)) {
|
|
25
25
|
this.typeError('verifyChild', 'Number');
|
|
26
26
|
}
|
|
27
27
|
const {childNodes: {length}} = this;
|
|
28
|
-
if (i < -length || i >= length + addition
|
|
28
|
+
if (i < -length || i >= length + addition) {
|
|
29
29
|
throw new RangeError(`不存在第 ${i} 个子节点!`);
|
|
30
30
|
}
|
|
31
31
|
};
|
|
@@ -45,17 +45,6 @@ class AstNode {
|
|
|
45
45
|
return this.#parentNode;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/** 是否具有根节点 */
|
|
49
|
-
get isConnected() {
|
|
50
|
-
return this.getRootNode().type === 'root';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** 不是自身的根节点 */
|
|
54
|
-
get ownerDocument() {
|
|
55
|
-
const root = this.getRootNode();
|
|
56
|
-
return root.type === 'root' && root !== this ? root : undefined;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
48
|
/**
|
|
60
49
|
* 后一个兄弟节点
|
|
61
50
|
* @complexity `n`
|
|
@@ -96,6 +85,17 @@ class AstNode {
|
|
|
96
85
|
return childNodes?.slice(0, i)?.findLast(({type}) => type !== 'text');
|
|
97
86
|
}
|
|
98
87
|
|
|
88
|
+
/** 是否具有根节点 */
|
|
89
|
+
get isConnected() {
|
|
90
|
+
return this.getRootNode().type === 'root';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 不是自身的根节点 */
|
|
94
|
+
get ownerDocument() {
|
|
95
|
+
const root = this.getRootNode();
|
|
96
|
+
return root.type === 'root' && root !== this ? root : undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
99
|
/**
|
|
100
100
|
* 后方是否还有其他节点(不含后代)
|
|
101
101
|
* @returns {boolean}
|
|
@@ -113,9 +113,14 @@ class AstNode {
|
|
|
113
113
|
return nextSibling === undefined && parentNode?.eof;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
constructor() {
|
|
117
|
+
Object.defineProperty(this, 'childNodes', {writable: false});
|
|
118
|
+
Object.freeze(this.childNodes);
|
|
119
|
+
}
|
|
120
|
+
|
|
116
121
|
/**
|
|
117
122
|
* 标记仅用于代码调试的方法
|
|
118
|
-
* @param {string} method
|
|
123
|
+
* @param {string} method
|
|
119
124
|
* @throws `Error`
|
|
120
125
|
*/
|
|
121
126
|
debugOnly(method = 'debugOnly') {
|
|
@@ -124,7 +129,7 @@ class AstNode {
|
|
|
124
129
|
|
|
125
130
|
/**
|
|
126
131
|
* 抛出`TypeError`
|
|
127
|
-
* @param {string} method
|
|
132
|
+
* @param {string} method
|
|
128
133
|
* @param {...string} types 可接受的参数类型
|
|
129
134
|
*/
|
|
130
135
|
typeError(method, ...types) {
|
|
@@ -146,17 +151,12 @@ class AstNode {
|
|
|
146
151
|
}
|
|
147
152
|
for (const key of keys) {
|
|
148
153
|
Object.defineProperty(this, key, {
|
|
149
|
-
writable: false, enumerable: Boolean(this[key]), configurable: !permanent,
|
|
154
|
+
writable: false, enumerable: !permanent && Boolean(this[key]), configurable: !permanent,
|
|
150
155
|
});
|
|
151
156
|
}
|
|
152
157
|
return this;
|
|
153
158
|
}
|
|
154
159
|
|
|
155
|
-
constructor() {
|
|
156
|
-
Object.defineProperty(this, 'childNodes', {writable: false});
|
|
157
|
-
Object.freeze(this.childNodes);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
160
|
/**
|
|
161
161
|
* 是否是全同节点
|
|
162
162
|
* @param {this} node 待比较的节点
|
|
@@ -174,6 +174,47 @@ class AstNode {
|
|
|
174
174
|
return true;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/** 获取所有属性键 */
|
|
178
|
+
getAttributeNames() {
|
|
179
|
+
const names = Object.getOwnPropertyNames(this);
|
|
180
|
+
return names.filter(name => typeof this[name] !== 'function');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** 是否具有任意属性 */
|
|
184
|
+
hasAttributes() {
|
|
185
|
+
return this.getAttributeNames().length > 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 移除某属性
|
|
190
|
+
* @param {PropertyKey} key 属性键
|
|
191
|
+
* @throws `RangeError` 不可删除的属性
|
|
192
|
+
*/
|
|
193
|
+
removeAttribute(key) {
|
|
194
|
+
if (this.hasAttribute(key)) {
|
|
195
|
+
const descriptor = Object.getOwnPropertyDescriptor(this, key);
|
|
196
|
+
if (!descriptor || !descriptor.writable) {
|
|
197
|
+
throw new RangeError(`属性 ${key} 不可删除!`);
|
|
198
|
+
}
|
|
199
|
+
delete this[key];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 开关某属性
|
|
205
|
+
* @param {PropertyKey} key 属性键
|
|
206
|
+
* @param {boolean|undefined} force 强制开启或关闭
|
|
207
|
+
* @throws `RangeError` 不为Boolean类型的属性值
|
|
208
|
+
*/
|
|
209
|
+
toggleAttribute(key, force) {
|
|
210
|
+
if (force !== undefined && typeof force !== 'boolean') {
|
|
211
|
+
this.typeError('toggleAttribute', 'Boolean');
|
|
212
|
+
} else if (this.hasAttribute(key) && typeof this[key] !== 'boolean') {
|
|
213
|
+
throw new RangeError(`${key} 属性的值不为 Boolean!`);
|
|
214
|
+
}
|
|
215
|
+
this.setAttribute(key, force === true || force === undefined && !this[key]);
|
|
216
|
+
}
|
|
217
|
+
|
|
177
218
|
/**
|
|
178
219
|
* 是否具有某属性
|
|
179
220
|
* @param {PropertyKey} key 属性键
|
|
@@ -200,35 +241,18 @@ class AstNode {
|
|
|
200
241
|
return this.hasAttribute(key) ? String(this[key]) : undefined;
|
|
201
242
|
}
|
|
202
243
|
|
|
203
|
-
/** 获取所有属性键 */
|
|
204
|
-
getAttributeNames() {
|
|
205
|
-
const names = Object.getOwnPropertyNames(this);
|
|
206
|
-
return names.filter(name => typeof this[name] !== 'function');
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/** 是否具有任意属性 */
|
|
210
|
-
hasAttributes() {
|
|
211
|
-
return this.getAttributeNames().length > 0;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
244
|
/**
|
|
215
245
|
* 设置属性
|
|
216
246
|
* @template {string} T
|
|
217
247
|
* @param {T} key 属性键
|
|
218
248
|
* @param {TokenAttribute<T>} value 属性值
|
|
219
|
-
* @throws `RangeError` 禁止手动指定的属性
|
|
220
249
|
*/
|
|
221
250
|
setAttribute(key, value) {
|
|
222
251
|
if (key === 'parentNode') {
|
|
223
|
-
if (externalUse('setAttribute')) {
|
|
224
|
-
throw new RangeError(`禁止手动指定 ${key} 属性!`);
|
|
225
|
-
}
|
|
226
252
|
this.#parentNode = value;
|
|
227
|
-
} else if (
|
|
253
|
+
} else if (Object.hasOwn(this, key)) {
|
|
228
254
|
const descriptor = Object.getOwnPropertyDescriptor(this, key);
|
|
229
|
-
if (
|
|
230
|
-
throw new RangeError(`禁止手动指定 ${key} 属性!`);
|
|
231
|
-
} else if (this.#optional.includes(key)) {
|
|
255
|
+
if (this.#optional.includes(key)) {
|
|
232
256
|
descriptor.enumerable = Boolean(value);
|
|
233
257
|
}
|
|
234
258
|
const oldValue = this[key],
|
|
@@ -244,33 +268,170 @@ class AstNode {
|
|
|
244
268
|
}
|
|
245
269
|
|
|
246
270
|
/**
|
|
247
|
-
*
|
|
248
|
-
* @param {
|
|
249
|
-
* @throws `RangeError` 不可删除的属性
|
|
271
|
+
* 移除子节点
|
|
272
|
+
* @param {number} i 移除位置
|
|
250
273
|
*/
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
274
|
+
removeAt(i) {
|
|
275
|
+
this.getAttribute('verifyChild')(i);
|
|
276
|
+
const childNodes = [...this.childNodes],
|
|
277
|
+
[node] = childNodes.splice(i, 1),
|
|
278
|
+
e = new Event('remove', {bubbles: true});
|
|
279
|
+
node.setAttribute('parentNode');
|
|
280
|
+
this.setAttribute('childNodes', childNodes);
|
|
281
|
+
this.dispatchEvent(e, {position: i, removed: node});
|
|
282
|
+
return node;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 插入子节点
|
|
287
|
+
* @template {this} T
|
|
288
|
+
* @param {T} node 待插入的子节点
|
|
289
|
+
* @param {number} i 插入位置
|
|
290
|
+
* @complexity `n`
|
|
291
|
+
* @throws `RangeError` 不能插入祖先节点
|
|
292
|
+
*/
|
|
293
|
+
insertAt(node, i = this.childNodes.length) {
|
|
294
|
+
if (!(node instanceof AstNode)) {
|
|
295
|
+
this.typeError('insertAt', 'String', 'AstNode');
|
|
296
|
+
} else if (node.contains(this)) {
|
|
297
|
+
Parser.error('不能插入祖先节点!', node);
|
|
298
|
+
throw new RangeError('不能插入祖先节点!');
|
|
258
299
|
}
|
|
300
|
+
this.getAttribute('verifyChild')(i, 1);
|
|
301
|
+
const childNodes = [...this.childNodes],
|
|
302
|
+
e = new Event('insert', {bubbles: true}),
|
|
303
|
+
j = Parser.running ? -1 : childNodes.indexOf(node);
|
|
304
|
+
if (j === -1) {
|
|
305
|
+
node.parentNode?.removeChild(node);
|
|
306
|
+
node.setAttribute('parentNode', this);
|
|
307
|
+
} else {
|
|
308
|
+
childNodes.splice(j, 1);
|
|
309
|
+
}
|
|
310
|
+
childNodes.splice(i, 0, node);
|
|
311
|
+
this.setAttribute('childNodes', childNodes);
|
|
312
|
+
this.dispatchEvent(e, {position: i < 0 ? i + this.childNodes.length - 1 : i, inserted: node});
|
|
313
|
+
return node;
|
|
259
314
|
}
|
|
260
315
|
|
|
261
316
|
/**
|
|
262
|
-
*
|
|
263
|
-
* @param {
|
|
264
|
-
* @
|
|
265
|
-
* @throws `RangeError`
|
|
317
|
+
* 获取子节点的位置
|
|
318
|
+
* @param {this} node 子节点
|
|
319
|
+
* @complexity `n`
|
|
320
|
+
* @throws `RangeError` 找不到子节点
|
|
266
321
|
*/
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
322
|
+
#getChildIndex(node) {
|
|
323
|
+
const {childNodes} = this,
|
|
324
|
+
i = childNodes.indexOf(node);
|
|
325
|
+
if (i === -1) {
|
|
326
|
+
Parser.error('找不到子节点!', node);
|
|
327
|
+
throw new RangeError('找不到子节点!');
|
|
272
328
|
}
|
|
273
|
-
|
|
329
|
+
return i;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* 移除子节点
|
|
334
|
+
* @template {this} T
|
|
335
|
+
* @param {T} node 子节点
|
|
336
|
+
* @complexity `n`
|
|
337
|
+
*/
|
|
338
|
+
removeChild(node) {
|
|
339
|
+
this.removeAt(this.#getChildIndex(node));
|
|
340
|
+
return node;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 在末尾插入子节点
|
|
345
|
+
* @template {this} T
|
|
346
|
+
* @param {T} node 插入节点
|
|
347
|
+
* @complexity `n`
|
|
348
|
+
*/
|
|
349
|
+
appendChild(node) {
|
|
350
|
+
return this.insertAt(node);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 在指定位置前插入子节点
|
|
355
|
+
* @template {this} T
|
|
356
|
+
* @param {T} child 插入节点
|
|
357
|
+
* @param {this} reference 指定位置处的子节点
|
|
358
|
+
* @complexity `n`
|
|
359
|
+
*/
|
|
360
|
+
insertBefore(child, reference) {
|
|
361
|
+
return reference === undefined ? this.insertAt(child) : this.insertAt(child, this.#getChildIndex(reference));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* 替换子节点
|
|
366
|
+
* @template {this} T
|
|
367
|
+
* @param {this} newChild 新子节点
|
|
368
|
+
* @param {T} oldChild 原子节点
|
|
369
|
+
* @complexity `n`
|
|
370
|
+
*/
|
|
371
|
+
replaceChild(newChild, oldChild) {
|
|
372
|
+
const i = this.#getChildIndex(oldChild);
|
|
373
|
+
this.removeAt(i);
|
|
374
|
+
this.insertAt(newChild, i);
|
|
375
|
+
return oldChild;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 在当前节点前后插入兄弟节点
|
|
380
|
+
* @param {this[]} nodes 插入节点
|
|
381
|
+
* @param {number} offset 插入的相对位置
|
|
382
|
+
* @complexity `n`
|
|
383
|
+
* @throws `Error` 不存在父节点
|
|
384
|
+
*/
|
|
385
|
+
#insertAdjacent(nodes, offset) {
|
|
386
|
+
const {parentNode} = this;
|
|
387
|
+
if (!parentNode) {
|
|
388
|
+
throw new Error('不存在父节点!');
|
|
389
|
+
}
|
|
390
|
+
const i = parentNode.childNodes.indexOf(this) + offset;
|
|
391
|
+
for (let j = 0; j < nodes.length; j++) {
|
|
392
|
+
parentNode.insertAt(nodes[j], i + j);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* 在后方批量插入兄弟节点
|
|
398
|
+
* @param {...this} nodes 插入节点
|
|
399
|
+
* @complexity `n`
|
|
400
|
+
*/
|
|
401
|
+
after(...nodes) {
|
|
402
|
+
this.#insertAdjacent(nodes, 1);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 在前方批量插入兄弟节点
|
|
407
|
+
* @param {...this} nodes 插入节点
|
|
408
|
+
* @complexity `n`
|
|
409
|
+
*/
|
|
410
|
+
before(...nodes) {
|
|
411
|
+
this.#insertAdjacent(nodes, 0);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* 移除当前节点
|
|
416
|
+
* @complexity `n`
|
|
417
|
+
* @throws `Error` 不存在父节点
|
|
418
|
+
*/
|
|
419
|
+
remove() {
|
|
420
|
+
const {parentNode} = this;
|
|
421
|
+
if (!parentNode) {
|
|
422
|
+
throw new Error('不存在父节点!');
|
|
423
|
+
}
|
|
424
|
+
parentNode.removeChild(this);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* 将当前节点批量替换为新的节点
|
|
429
|
+
* @param {...this} nodes 插入节点
|
|
430
|
+
* @complexity `n`
|
|
431
|
+
*/
|
|
432
|
+
replaceWith(...nodes) {
|
|
433
|
+
this.after(...nodes);
|
|
434
|
+
this.remove();
|
|
274
435
|
}
|
|
275
436
|
|
|
276
437
|
/**
|
|
@@ -311,10 +472,10 @@ class AstNode {
|
|
|
311
472
|
for (const type of types) {
|
|
312
473
|
this.addEventListener(type, listener, options);
|
|
313
474
|
}
|
|
314
|
-
} else if (typeof types
|
|
315
|
-
this.typeError('addEventListener', 'String', 'Function');
|
|
316
|
-
} else {
|
|
475
|
+
} else if (typeof types === 'string' && typeof listener === 'function') {
|
|
317
476
|
this.#events[options?.once ? 'once' : 'on'](types, listener);
|
|
477
|
+
} else {
|
|
478
|
+
this.typeError('addEventListener', 'String', 'Function');
|
|
318
479
|
}
|
|
319
480
|
}
|
|
320
481
|
|
|
@@ -328,10 +489,10 @@ class AstNode {
|
|
|
328
489
|
for (const type of types) {
|
|
329
490
|
this.removeEventListener(type, listener);
|
|
330
491
|
}
|
|
331
|
-
} else if (typeof types
|
|
332
|
-
this.typeError('removeEventListener', 'String', 'Function');
|
|
333
|
-
} else {
|
|
492
|
+
} else if (typeof types === 'string' && typeof listener === 'function') {
|
|
334
493
|
this.#events.off(types, listener);
|
|
494
|
+
} else {
|
|
495
|
+
this.typeError('removeEventListener', 'String', 'Function');
|
|
335
496
|
}
|
|
336
497
|
}
|
|
337
498
|
|
|
@@ -344,10 +505,10 @@ class AstNode {
|
|
|
344
505
|
for (const type of types) {
|
|
345
506
|
this.removeAllEventListeners(type);
|
|
346
507
|
}
|
|
347
|
-
} else if (types
|
|
348
|
-
this.typeError('removeAllEventListeners', 'String');
|
|
349
|
-
} else {
|
|
508
|
+
} else if (types === undefined || typeof types === 'string') {
|
|
350
509
|
this.#events.removeAllListeners(types);
|
|
510
|
+
} else {
|
|
511
|
+
this.typeError('removeAllEventListeners', 'String');
|
|
351
512
|
}
|
|
352
513
|
}
|
|
353
514
|
|
|
@@ -363,7 +524,7 @@ class AstNode {
|
|
|
363
524
|
/**
|
|
364
525
|
* 触发事件
|
|
365
526
|
* @param {AstEvent} e 事件对象
|
|
366
|
-
* @param {
|
|
527
|
+
* @param {*} data 事件数据
|
|
367
528
|
*/
|
|
368
529
|
dispatchEvent(e, data) {
|
|
369
530
|
if (!(e instanceof Event)) {
|
|
@@ -386,198 +547,223 @@ class AstNode {
|
|
|
386
547
|
}
|
|
387
548
|
}
|
|
388
549
|
|
|
389
|
-
/**
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
node.setAttribute('parentNode');
|
|
399
|
-
this.setAttribute('childNodes', childNodes).dispatchEvent(e, {position: i, removed: node});
|
|
400
|
-
return node;
|
|
550
|
+
/** 获取所有祖先节点 */
|
|
551
|
+
getAncestors() {
|
|
552
|
+
const /** @type {this[]} */ ancestors = [];
|
|
553
|
+
let {parentNode} = this;
|
|
554
|
+
while (parentNode) {
|
|
555
|
+
ancestors.push(parentNode);
|
|
556
|
+
({parentNode} = parentNode);
|
|
557
|
+
}
|
|
558
|
+
return ancestors;
|
|
401
559
|
}
|
|
402
560
|
|
|
403
561
|
/**
|
|
404
|
-
*
|
|
405
|
-
* @param {this}
|
|
562
|
+
* 比较和另一个节点的相对位置
|
|
563
|
+
* @param {this} other 待比较的节点
|
|
406
564
|
* @complexity `n`
|
|
407
|
-
* @throws `
|
|
408
|
-
*/
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
|
|
565
|
+
* @throws `Error` 不在同一个语法树
|
|
566
|
+
*/
|
|
567
|
+
compareDocumentPosition(other) {
|
|
568
|
+
if (!(other instanceof AstNode)) {
|
|
569
|
+
this.typeError('compareDocumentPosition', 'AstNode');
|
|
570
|
+
} else if (this === other) {
|
|
571
|
+
return 0;
|
|
572
|
+
} else if (this.contains(other)) {
|
|
573
|
+
return -1;
|
|
574
|
+
} else if (other.contains(this)) {
|
|
575
|
+
return 1;
|
|
576
|
+
} else if (this.getRootNode() !== other.getRootNode()) {
|
|
577
|
+
throw new Error('不在同一个语法树!');
|
|
415
578
|
}
|
|
416
|
-
|
|
579
|
+
const aAncestors = [...this.getAncestors().reverse(), this],
|
|
580
|
+
bAncestors = [...other.getAncestors().reverse(), other],
|
|
581
|
+
depth = aAncestors.findIndex((ancestor, i) => bAncestors[i] !== ancestor),
|
|
582
|
+
commonAncestor = aAncestors[depth - 1],
|
|
583
|
+
{childNodes} = commonAncestor;
|
|
584
|
+
return childNodes.indexOf(aAncestors[depth]) - childNodes.indexOf(bAncestors[depth]);
|
|
417
585
|
}
|
|
418
586
|
|
|
419
587
|
/**
|
|
420
|
-
*
|
|
421
|
-
* @template {this} T
|
|
422
|
-
* @param {T} node 子节点
|
|
588
|
+
* 合并相邻的文本子节点
|
|
423
589
|
* @complexity `n`
|
|
424
590
|
*/
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
591
|
+
normalize() {
|
|
592
|
+
const AstText = require('./text');
|
|
593
|
+
const /** @type {AstText[]} */ childNodes = [...this.childNodes];
|
|
594
|
+
for (let i = childNodes.length - 1; i >= 0; i--) {
|
|
595
|
+
const {type, data} = childNodes[i];
|
|
596
|
+
if (this.getGaps(i - 1)) {
|
|
597
|
+
//
|
|
598
|
+
} else if (data === '') {
|
|
599
|
+
childNodes.splice(i, 1);
|
|
600
|
+
} else if (type === 'text' && childNodes[i - 1]?.type === 'text') {
|
|
601
|
+
childNodes[i - 1].setAttribute('data', childNodes[i - 1].data + data);
|
|
602
|
+
childNodes.splice(i, 1);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
this.setAttribute('childNodes', childNodes);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/** 获取根节点 */
|
|
609
|
+
getRootNode() {
|
|
610
|
+
let {parentNode} = this;
|
|
611
|
+
while (parentNode?.parentNode) {
|
|
612
|
+
({parentNode} = parentNode);
|
|
613
|
+
}
|
|
614
|
+
return parentNode ?? this;
|
|
428
615
|
}
|
|
429
616
|
|
|
430
617
|
/**
|
|
431
|
-
*
|
|
432
|
-
* @
|
|
433
|
-
* @param {T} node 待插入的子节点
|
|
434
|
-
* @param {number} i 插入位置
|
|
618
|
+
* 将字符位置转换为行列号
|
|
619
|
+
* @param {number} index 字符位置
|
|
435
620
|
* @complexity `n`
|
|
436
|
-
* @throws `RangeError` 不能插入祖先节点
|
|
437
621
|
*/
|
|
438
|
-
|
|
439
|
-
if (!(
|
|
440
|
-
this.typeError('
|
|
441
|
-
} else if (node.contains(this)) {
|
|
442
|
-
Parser.error('不能插入祖先节点!', node);
|
|
443
|
-
throw new RangeError('不能插入祖先节点!');
|
|
622
|
+
posFromIndex(index) {
|
|
623
|
+
if (!Number.isInteger(index)) {
|
|
624
|
+
this.typeError('posFromIndex', 'Number');
|
|
444
625
|
}
|
|
445
|
-
this
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if (j === -1) {
|
|
450
|
-
node.parentNode?.removeChild(node);
|
|
451
|
-
node.setAttribute('parentNode', this);
|
|
452
|
-
} else {
|
|
453
|
-
childNodes.splice(j, 1);
|
|
626
|
+
const str = String(this);
|
|
627
|
+
if (index >= -str.length && index <= str.length) {
|
|
628
|
+
const lines = str.slice(0, index).split('\n');
|
|
629
|
+
return {top: lines.length - 1, left: lines.at(-1).length};
|
|
454
630
|
}
|
|
455
|
-
|
|
456
|
-
this.setAttribute('childNodes', childNodes)
|
|
457
|
-
.dispatchEvent(e, {position: i < 0 ? i + this.childNodes.length - 1 : i, inserted: node});
|
|
458
|
-
return node;
|
|
631
|
+
return undefined;
|
|
459
632
|
}
|
|
460
633
|
|
|
461
634
|
/**
|
|
462
|
-
*
|
|
463
|
-
* @
|
|
464
|
-
* @param {
|
|
635
|
+
* 将行列号转换为字符位置
|
|
636
|
+
* @param {number} top 行号
|
|
637
|
+
* @param {number} left 列号
|
|
465
638
|
* @complexity `n`
|
|
466
639
|
*/
|
|
467
|
-
|
|
468
|
-
|
|
640
|
+
indexFromPos(top, left) {
|
|
641
|
+
if (!Number.isInteger(top) || !Number.isInteger(left)) {
|
|
642
|
+
this.typeError('indexFromPos', 'Number');
|
|
643
|
+
}
|
|
644
|
+
const lines = String(this).split('\n');
|
|
645
|
+
return top >= 0 && left >= 0 && lines.length >= top + 1 && lines[top].length >= left
|
|
646
|
+
? lines.slice(0, top).reduce((acc, curLine) => acc + curLine.length + 1, 0) + left
|
|
647
|
+
: undefined;
|
|
469
648
|
}
|
|
470
649
|
|
|
471
650
|
/**
|
|
472
|
-
*
|
|
473
|
-
* @template {this} T
|
|
474
|
-
* @param {T} child 插入节点
|
|
475
|
-
* @param {this} reference 指定位置处的子节点
|
|
651
|
+
* 获取行数和最后一行的列数
|
|
476
652
|
* @complexity `n`
|
|
477
653
|
*/
|
|
478
|
-
|
|
479
|
-
|
|
654
|
+
#getDimension() {
|
|
655
|
+
const lines = String(this).split('\n');
|
|
656
|
+
return {height: lines.length, width: lines.at(-1).length};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/** 第一个子节点前的间距 */
|
|
660
|
+
getPadding() {
|
|
661
|
+
return 0;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/** 子节点间距 */
|
|
665
|
+
getGaps() {
|
|
666
|
+
return 0;
|
|
480
667
|
}
|
|
481
668
|
|
|
482
669
|
/**
|
|
483
|
-
*
|
|
484
|
-
* @
|
|
485
|
-
* @param {this} newChild 新子节点
|
|
486
|
-
* @param {T} oldChild 原子节点
|
|
670
|
+
* 获取当前节点的相对字符位置,或其第`j`个子节点的相对字符位置
|
|
671
|
+
* @param {number|undefined} j 子节点序号
|
|
487
672
|
* @complexity `n`
|
|
488
673
|
*/
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
674
|
+
getRelativeIndex(j) {
|
|
675
|
+
let /** @type {this[]} */ childNodes;
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* 获取子节点相对于父节点的字符位置,使用前需要先给`childNodes`赋值
|
|
679
|
+
* @param {number} end 子节点序号
|
|
680
|
+
* @param {this} parent 父节点
|
|
681
|
+
* @returns {number}
|
|
682
|
+
*/
|
|
683
|
+
const getIndex = (end, parent) => childNodes.slice(0, end).reduce(
|
|
684
|
+
(acc, cur, i) => acc + String(cur).length + parent.getGaps(i),
|
|
685
|
+
0,
|
|
686
|
+
) + parent.getPadding();
|
|
687
|
+
if (j === undefined) {
|
|
688
|
+
const {parentNode} = this;
|
|
689
|
+
if (parentNode) {
|
|
690
|
+
({childNodes} = parentNode);
|
|
691
|
+
return getIndex(childNodes.indexOf(this), parentNode);
|
|
692
|
+
}
|
|
693
|
+
return 0;
|
|
694
|
+
}
|
|
695
|
+
this.getAttribute('verifyChild')(j, 1);
|
|
696
|
+
({childNodes} = this);
|
|
697
|
+
return getIndex(j, this);
|
|
494
698
|
}
|
|
495
699
|
|
|
496
700
|
/**
|
|
497
|
-
*
|
|
498
|
-
* @
|
|
499
|
-
* @param {number} offset 插入的相对位置
|
|
701
|
+
* 获取当前节点的绝对位置
|
|
702
|
+
* @returns {number}
|
|
500
703
|
* @complexity `n`
|
|
501
|
-
* @throws `Error` 不存在父节点
|
|
502
704
|
*/
|
|
503
|
-
|
|
705
|
+
getAbsoluteIndex() {
|
|
504
706
|
const {parentNode} = this;
|
|
505
|
-
|
|
506
|
-
throw new Error('不存在父节点!');
|
|
507
|
-
}
|
|
508
|
-
const i = parentNode.childNodes.indexOf(this) + offset;
|
|
509
|
-
for (let j = 0; j < nodes.length; j++) {
|
|
510
|
-
parentNode.insertAt(nodes[j], i + j);
|
|
511
|
-
}
|
|
707
|
+
return parentNode ? parentNode.getAbsoluteIndex() + this.getRelativeIndex() : 0;
|
|
512
708
|
}
|
|
513
709
|
|
|
514
710
|
/**
|
|
515
|
-
*
|
|
516
|
-
* @param {
|
|
711
|
+
* 获取当前节点的相对位置,或其第`j`个子节点的相对位置
|
|
712
|
+
* @param {number|undefined} j 子节点序号
|
|
517
713
|
* @complexity `n`
|
|
518
714
|
*/
|
|
519
|
-
|
|
520
|
-
|
|
715
|
+
#getPosition(j) {
|
|
716
|
+
return j === undefined
|
|
717
|
+
? this.parentNode?.posFromIndex(this.getRelativeIndex()) ?? {top: 0, left: 0}
|
|
718
|
+
: this.posFromIndex(this.getRelativeIndex(j));
|
|
521
719
|
}
|
|
522
720
|
|
|
523
721
|
/**
|
|
524
|
-
*
|
|
525
|
-
* @param {...this} nodes 插入节点
|
|
722
|
+
* 获取当前节点的行列位置和大小
|
|
526
723
|
* @complexity `n`
|
|
527
724
|
*/
|
|
528
|
-
|
|
529
|
-
this.#
|
|
725
|
+
getBoundingClientRect() {
|
|
726
|
+
return {...this.#getDimension(), ...this.getRootNode().posFromIndex(this.getAbsoluteIndex())};
|
|
530
727
|
}
|
|
531
728
|
|
|
532
729
|
/**
|
|
533
|
-
*
|
|
730
|
+
* 行数
|
|
534
731
|
* @complexity `n`
|
|
535
|
-
* @throws `Error` 不存在父节点
|
|
536
732
|
*/
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
if (!parentNode) {
|
|
540
|
-
throw new Error('不存在父节点!');
|
|
541
|
-
}
|
|
542
|
-
parentNode.removeChild(this);
|
|
733
|
+
get offsetHeight() {
|
|
734
|
+
return this.#getDimension().height;
|
|
543
735
|
}
|
|
544
736
|
|
|
545
737
|
/**
|
|
546
|
-
*
|
|
547
|
-
* @param {...this} nodes 插入节点
|
|
738
|
+
* 最后一行的列数
|
|
548
739
|
* @complexity `n`
|
|
549
740
|
*/
|
|
550
|
-
|
|
551
|
-
this
|
|
552
|
-
this.remove();
|
|
741
|
+
get offsetWidth() {
|
|
742
|
+
return this.#getDimension().width;
|
|
553
743
|
}
|
|
554
744
|
|
|
555
745
|
/**
|
|
556
|
-
*
|
|
746
|
+
* 位置、大小和padding
|
|
557
747
|
* @complexity `n`
|
|
558
748
|
*/
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const /** @type {AstText[]} */ childNodes = [...this.childNodes];
|
|
562
|
-
for (let i = childNodes.length - 1; i >= 0; i--) {
|
|
563
|
-
const {type, data} = childNodes[i];
|
|
564
|
-
if (data === '') {
|
|
565
|
-
childNodes.splice(i, 1);
|
|
566
|
-
} else if (type === 'text' && childNodes[i - 1]?.type === 'text') {
|
|
567
|
-
childNodes[i - 1].setAttribute('data', childNodes[i - 1].data + data);
|
|
568
|
-
childNodes.splice(i, 1);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
this.setAttribute('childNodes', childNodes);
|
|
749
|
+
get style() {
|
|
750
|
+
return {...this.#getPosition(), ...this.#getDimension(), padding: this.getPadding()};
|
|
572
751
|
}
|
|
573
752
|
|
|
574
|
-
/**
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
753
|
+
/**
|
|
754
|
+
* 相对于父容器的行号
|
|
755
|
+
* @complexity `n`
|
|
756
|
+
*/
|
|
757
|
+
get offsetTop() {
|
|
758
|
+
return this.#getPosition().top;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* 相对于父容器的列号
|
|
763
|
+
* @complexity `n`
|
|
764
|
+
*/
|
|
765
|
+
get offsetLeft() {
|
|
766
|
+
return this.#getPosition().left;
|
|
581
767
|
}
|
|
582
768
|
}
|
|
583
769
|
|