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/src/parameter.js
CHANGED
|
@@ -14,7 +14,7 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
14
14
|
|
|
15
15
|
/** 是否是匿名参数 */
|
|
16
16
|
get anon() {
|
|
17
|
-
return this.
|
|
17
|
+
return this.firstChild.childNodes.length === 0;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/** getValue()的getter */
|
|
@@ -26,6 +26,19 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
26
26
|
this.setValue(value);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* 是否是重复参数
|
|
31
|
+
* @this {ParameterToken & {parentNode: TranscludeToken}}
|
|
32
|
+
*/
|
|
33
|
+
get duplicated() {
|
|
34
|
+
const TranscludeToken = require('./transclude');
|
|
35
|
+
try {
|
|
36
|
+
return Boolean(this.parentNode?.getDuplicatedArgs()?.some(([key]) => key === this.name));
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
29
42
|
/**
|
|
30
43
|
* @param {string|number} key 参数名
|
|
31
44
|
* @param {string} value 参数值
|
|
@@ -42,34 +55,23 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
42
55
|
this.append(keyToken, token.setAttribute('stage', 2));
|
|
43
56
|
}
|
|
44
57
|
|
|
45
|
-
/** @override */
|
|
46
|
-
cloneNode() {
|
|
47
|
-
const [key, value] = this.cloneChildNodes(),
|
|
48
|
-
config = this.getAttribute('config');
|
|
49
|
-
return Parser.run(() => {
|
|
50
|
-
const token = new ParameterToken(this.anon ? Number(this.name) : undefined, undefined, config);
|
|
51
|
-
token.firstElementChild.safeReplaceWith(key);
|
|
52
|
-
token.lastElementChild.safeReplaceWith(value);
|
|
53
|
-
return token.afterBuild();
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
58
|
/** @override */
|
|
58
59
|
afterBuild() {
|
|
59
60
|
if (!this.anon) {
|
|
60
|
-
const
|
|
61
|
+
const TranscludeToken = require('./transclude');
|
|
62
|
+
const name = this.firstChild.text().trim(),
|
|
61
63
|
{parentNode} = this;
|
|
62
64
|
this.setAttribute('name', name);
|
|
63
|
-
if (parentNode && parentNode instanceof
|
|
65
|
+
if (parentNode && parentNode instanceof TranscludeToken) {
|
|
64
66
|
parentNode.getAttribute('keys').add(name);
|
|
65
67
|
parentNode.getArgs(name, false, false).add(this);
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
const /** @type {AstListener} */ parameterListener = ({prevTarget}, data) => {
|
|
69
71
|
if (!this.anon) { // 匿名参数不管怎么变动还是匿名
|
|
70
|
-
const {
|
|
71
|
-
if (prevTarget ===
|
|
72
|
-
const newKey =
|
|
72
|
+
const {firstChild, name} = this;
|
|
73
|
+
if (prevTarget === firstChild) {
|
|
74
|
+
const newKey = firstChild.text().trim();
|
|
73
75
|
data.oldKey = name;
|
|
74
76
|
data.newKey = newKey;
|
|
75
77
|
this.setAttribute('name', newKey);
|
|
@@ -87,7 +89,7 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
87
89
|
*/
|
|
88
90
|
toString(selector) {
|
|
89
91
|
return this.anon && !(selector && this.matches(selector))
|
|
90
|
-
? this.
|
|
92
|
+
? this.lastChild.toString(selector)
|
|
91
93
|
: super.toString(selector, '=');
|
|
92
94
|
}
|
|
93
95
|
|
|
@@ -96,12 +98,29 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
96
98
|
return this.anon ? 0 : 1;
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
/** @override */
|
|
102
|
+
print() {
|
|
103
|
+
return super.print({sep: this.anon ? '' : '='});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @override */
|
|
107
|
+
cloneNode() {
|
|
108
|
+
const [key, value] = this.cloneChildNodes(),
|
|
109
|
+
config = this.getAttribute('config');
|
|
110
|
+
return Parser.run(() => {
|
|
111
|
+
const token = new ParameterToken(this.anon ? Number(this.name) : undefined, undefined, config);
|
|
112
|
+
token.firstChild.safeReplaceWith(key);
|
|
113
|
+
token.lastChild.safeReplaceWith(value);
|
|
114
|
+
return token.afterBuild();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
99
118
|
/**
|
|
100
119
|
* @override
|
|
101
120
|
* @returns {string}
|
|
102
121
|
*/
|
|
103
122
|
text() {
|
|
104
|
-
return this.anon ? this.
|
|
123
|
+
return this.anon ? this.lastChild.text() : super.text('=');
|
|
105
124
|
}
|
|
106
125
|
|
|
107
126
|
/**
|
|
@@ -114,37 +133,46 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
114
133
|
return this.replaceWith(token);
|
|
115
134
|
}
|
|
116
135
|
|
|
117
|
-
/**
|
|
136
|
+
/**
|
|
137
|
+
* 获取参数值
|
|
138
|
+
* @this {ParameterToken & {parentNode: TranscludeToken}}
|
|
139
|
+
*/
|
|
118
140
|
getValue() {
|
|
119
|
-
const
|
|
120
|
-
|
|
141
|
+
const TranscludeToken = require('./transclude');
|
|
142
|
+
const value = this.lastChild.text();
|
|
143
|
+
return this.anon && this.parentNode?.isTemplate() ? value : value.trim();
|
|
121
144
|
}
|
|
122
145
|
|
|
123
146
|
/**
|
|
124
147
|
* 设置参数值
|
|
148
|
+
* @this {ParameterToken & {parentNode: TranscludeToken}}
|
|
125
149
|
* @param {string} value 参数值
|
|
126
150
|
* @throws `SyntaxError` 非法的模板参数
|
|
127
151
|
*/
|
|
128
152
|
setValue(value) {
|
|
129
153
|
value = String(value);
|
|
130
|
-
const
|
|
154
|
+
const TranscludeToken = require('./transclude');
|
|
155
|
+
const templateLike = this.parentNode?.isTemplate(),
|
|
131
156
|
wikitext = `{{${templateLike ? ':T|' : 'lc:'}${this.anon ? '' : '1='}${value}}}`,
|
|
132
157
|
root = Parser.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
133
|
-
{
|
|
134
|
-
/** @type {ParameterToken} */
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
158
|
+
{length, firstChild: transclude} = root,
|
|
159
|
+
/** @type {Token & {lastChild: ParameterToken}} */
|
|
160
|
+
{lastChild: parameter, type, name, length: transcludeLength} = transclude,
|
|
161
|
+
targetType = templateLike ? 'template' : 'magic-word',
|
|
162
|
+
targetName = templateLike ? 'T' : 'lc';
|
|
163
|
+
if (length !== 1 || type !== targetType || name !== targetName || transcludeLength !== 2
|
|
164
|
+
|| parameter.anon !== this.anon || parameter.name !== '1'
|
|
138
165
|
) {
|
|
139
166
|
throw new SyntaxError(`非法的模板参数:${noWrap(value)}`);
|
|
140
167
|
}
|
|
141
|
-
const {lastChild} =
|
|
142
|
-
|
|
143
|
-
this.
|
|
168
|
+
const {lastChild} = parameter;
|
|
169
|
+
parameter.destroy(true);
|
|
170
|
+
this.lastChild.safeReplaceWith(lastChild);
|
|
144
171
|
}
|
|
145
172
|
|
|
146
173
|
/**
|
|
147
174
|
* 修改参数名
|
|
175
|
+
* @this {ParameterToken & {parentNode: TranscludeToken}}
|
|
148
176
|
* @param {string} key 新参数名
|
|
149
177
|
* @param {boolean} force 是否无视冲突命名
|
|
150
178
|
* @throws `Error` 仅用于模板参数
|
|
@@ -155,31 +183,30 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
155
183
|
if (typeof key !== 'string') {
|
|
156
184
|
this.typeError('rename', 'String');
|
|
157
185
|
}
|
|
186
|
+
const TranscludeToken = require('./transclude');
|
|
158
187
|
const {parentNode} = this;
|
|
159
188
|
// 必须检测是否是TranscludeToken
|
|
160
|
-
if (!parentNode || !parentNode
|
|
161
|
-
|| !(parentNode instanceof require('./transclude'))
|
|
162
|
-
) {
|
|
189
|
+
if (!parentNode?.isTemplate() || !(parentNode instanceof TranscludeToken)) {
|
|
163
190
|
throw new Error(`${this.constructor.name}.rename 方法仅用于模板参数!`);
|
|
164
191
|
}
|
|
165
192
|
const root = Parser.parse(`{{:T|${key}=}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
166
|
-
{
|
|
167
|
-
|
|
193
|
+
{length, firstChild: template} = root,
|
|
194
|
+
{type, name, lastChild: parameter, length: templateLength} = template;
|
|
195
|
+
if (length !== 1 || type !== 'template' || name !== 'T' || templateLength !== 2) {
|
|
168
196
|
throw new SyntaxError(`非法的模板参数名:${key}`);
|
|
169
197
|
}
|
|
170
|
-
const {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
} else if (parentNode.hasArg(name)) {
|
|
198
|
+
const {name: parameterName, firstChild} = parameter;
|
|
199
|
+
if (this.name === parameterName) {
|
|
200
|
+
Parser.warn('未改变实际参数名', parameterName);
|
|
201
|
+
} else if (parentNode.hasArg(parameterName)) {
|
|
175
202
|
if (force) {
|
|
176
|
-
Parser.warn('参数更名造成重复参数',
|
|
203
|
+
Parser.warn('参数更名造成重复参数', parameterName);
|
|
177
204
|
} else {
|
|
178
|
-
throw new RangeError(`参数更名造成重复参数:${
|
|
205
|
+
throw new RangeError(`参数更名造成重复参数:${parameterName}`);
|
|
179
206
|
}
|
|
180
207
|
}
|
|
181
|
-
|
|
182
|
-
this.
|
|
208
|
+
parameter.destroy(true);
|
|
209
|
+
this.firstChild.safeReplaceWith(firstChild);
|
|
183
210
|
}
|
|
184
211
|
}
|
|
185
212
|
|
package/src/syntax.js
CHANGED
|
@@ -7,7 +7,7 @@ const {undo} = require('../util/debug'),
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* 满足特定语法格式的plain Token
|
|
10
|
-
* @classdesc `{childNodes:
|
|
10
|
+
* @classdesc `{childNodes: ...AstText|Token}`
|
|
11
11
|
*/
|
|
12
12
|
class SyntaxToken extends Token {
|
|
13
13
|
#pattern;
|
|
@@ -65,6 +65,14 @@ class SyntaxToken extends Token {
|
|
|
65
65
|
return key === 'pattern' ? this.#pattern : super.getAttribute(key);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @override
|
|
70
|
+
* @param {PropertyKey} key 属性键
|
|
71
|
+
*/
|
|
72
|
+
hasAttribute(key) {
|
|
73
|
+
return key === 'pattern' || super.hasAttribute(key);
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
/**
|
|
69
77
|
* @override
|
|
70
78
|
* @param {...Token} elements 待替换的子节点
|
package/src/table/index.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const assert = require('assert/strict'),
|
|
4
4
|
{noWrap} = require('../../util/string'),
|
|
5
|
+
{generateForChild} = require('../../util/lint'),
|
|
6
|
+
{isPlainObject} = require('../../util/base'),
|
|
5
7
|
Parser = require('../..'),
|
|
6
8
|
Token = require('..'),
|
|
7
9
|
TrToken = require('./tr'),
|
|
8
10
|
TdToken = require('./td'),
|
|
9
|
-
SyntaxToken = require('../syntax')
|
|
10
|
-
AttributeToken = require('../attribute');
|
|
11
|
+
SyntaxToken = require('../syntax');
|
|
11
12
|
|
|
12
13
|
const openingPattern = /^(?:\{\||\{\{\{\s*!\s*\}\}|\{\{\s*\(!\s*\}\})$/u,
|
|
13
14
|
closingPattern = /^\n[^\S\n]*(?:\|\}|\{\{\s*!\s*\}\}\}|\{\{\s*!\)\s*\}\})$/u;
|
|
@@ -123,7 +124,7 @@ class TableToken extends TrToken {
|
|
|
123
124
|
|
|
124
125
|
/** 表格是否闭合 */
|
|
125
126
|
get closed() {
|
|
126
|
-
return this.
|
|
127
|
+
return this.lastChild.type === 'table-syntax';
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
set closed(closed) {
|
|
@@ -154,11 +155,11 @@ class TableToken extends TrToken {
|
|
|
154
155
|
* @throws `SyntaxError` 表格的闭合部分非法
|
|
155
156
|
*/
|
|
156
157
|
insertAt(token, i = this.childNodes.length) {
|
|
157
|
-
const previous = this.
|
|
158
|
+
const previous = this.childNodes.at(i - 1);
|
|
158
159
|
if (token.type === 'td' && previous.type === 'tr') {
|
|
159
160
|
Parser.warn('改为将单元格插入当前行。');
|
|
160
|
-
return previous.
|
|
161
|
-
} else if (
|
|
161
|
+
return previous.insertAt(token);
|
|
162
|
+
} else if (i > 0 && i === this.childNodes.length && token instanceof SyntaxToken
|
|
162
163
|
&& (token.getAttribute('pattern') !== closingPattern || !closingPattern.test(token.text()))
|
|
163
164
|
) {
|
|
164
165
|
throw new SyntaxError(`表格的闭合部分不符合语法!${noWrap(String(token))}`);
|
|
@@ -166,6 +167,18 @@ class TableToken extends TrToken {
|
|
|
166
167
|
return super.insertAt(token, i);
|
|
167
168
|
}
|
|
168
169
|
|
|
170
|
+
/**
|
|
171
|
+
* @override
|
|
172
|
+
* @param {number} start 起始位置
|
|
173
|
+
*/
|
|
174
|
+
lint(start = 0) {
|
|
175
|
+
const errors = super.lint(start);
|
|
176
|
+
if (!this.closed) {
|
|
177
|
+
errors.push(generateForChild(this.firstChild, this.getRootNode().posFromIndex(start), '未闭合的表格'));
|
|
178
|
+
}
|
|
179
|
+
return errors;
|
|
180
|
+
}
|
|
181
|
+
|
|
169
182
|
/**
|
|
170
183
|
* 闭合表格语法
|
|
171
184
|
* @complexity `n`
|
|
@@ -177,13 +190,13 @@ class TableToken extends TrToken {
|
|
|
177
190
|
const config = this.getAttribute('config'),
|
|
178
191
|
accum = this.getAttribute('accum'),
|
|
179
192
|
inner = !halfParsed && Parser.parse(syntax, this.getAttribute('include'), 2, config),
|
|
180
|
-
{
|
|
193
|
+
{lastChild} = this;
|
|
181
194
|
if (!halfParsed && !closingPattern.test(inner.text())) {
|
|
182
195
|
throw new SyntaxError(`表格的闭合部分不符合语法!${noWrap(syntax)}`);
|
|
183
|
-
} else if (
|
|
184
|
-
|
|
196
|
+
} else if (lastChild instanceof SyntaxToken) {
|
|
197
|
+
lastChild.replaceChildren(...inner.childNodes);
|
|
185
198
|
} else {
|
|
186
|
-
|
|
199
|
+
super.insertAt(Parser.run(() => {
|
|
187
200
|
const token = new SyntaxToken(syntax, closingPattern, 'table-syntax', config, accum, {
|
|
188
201
|
'Stage-1': ':', '!ExtToken': '', TranscludeToken: ':',
|
|
189
202
|
});
|
|
@@ -202,7 +215,7 @@ class TableToken extends TrToken {
|
|
|
202
215
|
*/
|
|
203
216
|
getRowCount() {
|
|
204
217
|
return super.getRowCount()
|
|
205
|
-
+ this.
|
|
218
|
+
+ this.childNodes.filter(child => child.type === 'tr' && child.getRowCount()).length;
|
|
206
219
|
}
|
|
207
220
|
|
|
208
221
|
/** @override */
|
|
@@ -228,7 +241,7 @@ class TableToken extends TrToken {
|
|
|
228
241
|
* @throws `RangeError` 不存在该行
|
|
229
242
|
*/
|
|
230
243
|
getNthRow(n, force, insert) {
|
|
231
|
-
if (
|
|
244
|
+
if (!Number.isInteger(n)) {
|
|
232
245
|
this.typeError('getNthRow', 'Number');
|
|
233
246
|
}
|
|
234
247
|
const nRows = this.getRowCount(),
|
|
@@ -241,7 +254,7 @@ class TableToken extends TrToken {
|
|
|
241
254
|
} else if (isRow) {
|
|
242
255
|
n--;
|
|
243
256
|
}
|
|
244
|
-
for (const child of this.
|
|
257
|
+
for (const child of this.childNodes.slice(2)) {
|
|
245
258
|
if (child.type === 'tr' && child.getRowCount()) {
|
|
246
259
|
n--;
|
|
247
260
|
if (n < 0) {
|
|
@@ -262,7 +275,7 @@ class TableToken extends TrToken {
|
|
|
262
275
|
getAllRows() {
|
|
263
276
|
return [
|
|
264
277
|
...super.getRowCount() ? [this] : [],
|
|
265
|
-
...this.
|
|
278
|
+
...this.childNodes.filter(child => child.type === 'tr' && child.getRowCount()),
|
|
266
279
|
];
|
|
267
280
|
}
|
|
268
281
|
|
|
@@ -295,7 +308,7 @@ class TableToken extends TrToken {
|
|
|
295
308
|
let j = 0,
|
|
296
309
|
k = 0,
|
|
297
310
|
last;
|
|
298
|
-
for (const cell of rows[i].
|
|
311
|
+
for (const cell of rows[i].childNodes.slice(2)) {
|
|
299
312
|
if (cell instanceof TdToken) {
|
|
300
313
|
if (cell.isIndependent()) {
|
|
301
314
|
last = cell.subtype !== 'caption';
|
|
@@ -344,7 +357,7 @@ class TableToken extends TrToken {
|
|
|
344
357
|
* @complexity `n`
|
|
345
358
|
*/
|
|
346
359
|
toRenderedCoords({row, column}) {
|
|
347
|
-
if (
|
|
360
|
+
if (!Number.isInteger(row) || !Number.isInteger(column)) {
|
|
348
361
|
this.typeError('toRenderedCoords', 'Number');
|
|
349
362
|
}
|
|
350
363
|
const rowLayout = this.getLayout({row, column})[row],
|
|
@@ -358,19 +371,19 @@ class TableToken extends TrToken {
|
|
|
358
371
|
* @complexity `n`
|
|
359
372
|
*/
|
|
360
373
|
toRawCoords({x, y}) {
|
|
361
|
-
if (
|
|
374
|
+
if (!Number.isInteger(x) || !Number.isInteger(y)) {
|
|
362
375
|
this.typeError('toRawCoords', 'Number');
|
|
363
376
|
}
|
|
364
377
|
const rowLayout = this.getLayout({x, y})[y],
|
|
365
378
|
coords = rowLayout?.[x];
|
|
366
379
|
if (coords) {
|
|
367
380
|
return {...coords, start: coords.row === y && rowLayout[x - 1] !== coords};
|
|
368
|
-
} else if (
|
|
369
|
-
return
|
|
381
|
+
} else if (rowLayout || y > 0) {
|
|
382
|
+
return x === rowLayout?.length
|
|
383
|
+
? {row: y, column: (rowLayout.findLast(({row}) => row === y)?.column ?? -1) + 1, start: true}
|
|
384
|
+
: undefined;
|
|
370
385
|
}
|
|
371
|
-
return
|
|
372
|
-
? {row: y, column: (rowLayout.findLast(({row}) => row === y)?.column ?? -1) + 1, start: true}
|
|
373
|
-
: undefined;
|
|
386
|
+
return {row: 0, column: 0, start: true};
|
|
374
387
|
}
|
|
375
388
|
|
|
376
389
|
/**
|
|
@@ -379,7 +392,7 @@ class TableToken extends TrToken {
|
|
|
379
392
|
* @complexity `n²`
|
|
380
393
|
*/
|
|
381
394
|
getFullRow(y) {
|
|
382
|
-
if (
|
|
395
|
+
if (!Number.isInteger(y)) {
|
|
383
396
|
this.typeError('getFullRow', 'Number');
|
|
384
397
|
}
|
|
385
398
|
const rows = this.getAllRows();
|
|
@@ -394,7 +407,7 @@ class TableToken extends TrToken {
|
|
|
394
407
|
* @complexity `n`
|
|
395
408
|
*/
|
|
396
409
|
getFullCol(x) {
|
|
397
|
-
if (
|
|
410
|
+
if (!Number.isInteger(x)) {
|
|
398
411
|
this.typeError('getFullCol', 'Number');
|
|
399
412
|
}
|
|
400
413
|
const layout = this.getLayout(),
|
|
@@ -474,7 +487,7 @@ class TableToken extends TrToken {
|
|
|
474
487
|
if (coords.column === undefined) {
|
|
475
488
|
const {x, y} = coords;
|
|
476
489
|
coords = this.toRawCoords(coords);
|
|
477
|
-
if (!coords?.start) {
|
|
490
|
+
if (!coords?.start) {
|
|
478
491
|
throw new RangeError(`指定的坐标不是单元格起始点:(${x}, ${y})`);
|
|
479
492
|
}
|
|
480
493
|
}
|
|
@@ -490,16 +503,16 @@ class TableToken extends TrToken {
|
|
|
490
503
|
*/
|
|
491
504
|
#prependTableRow() {
|
|
492
505
|
const row = Parser.run(() => new TrToken('\n|-', undefined, this.getAttribute('config'))),
|
|
493
|
-
{
|
|
494
|
-
[,, plain] =
|
|
495
|
-
start = plain?.
|
|
496
|
-
/** @type {TdToken[]} */ tdChildren =
|
|
506
|
+
{childNodes} = this,
|
|
507
|
+
[,, plain] = childNodes,
|
|
508
|
+
start = plain?.constructor === Token ? 3 : 2,
|
|
509
|
+
/** @type {TdToken[]} */ tdChildren = childNodes.slice(start),
|
|
497
510
|
index = tdChildren.findIndex(({type}) => type !== 'td');
|
|
498
511
|
this.insertAt(row, index === -1 ? -1 : index + start);
|
|
499
512
|
Parser.run(() => {
|
|
500
513
|
for (const cell of tdChildren.slice(0, index === -1 ? undefined : index)) {
|
|
501
514
|
if (cell.subtype !== 'caption') {
|
|
502
|
-
row.
|
|
515
|
+
row.insertAt(cell);
|
|
503
516
|
}
|
|
504
517
|
}
|
|
505
518
|
});
|
|
@@ -516,10 +529,11 @@ class TableToken extends TrToken {
|
|
|
516
529
|
* @complexity `n`
|
|
517
530
|
*/
|
|
518
531
|
insertTableRow(y, attr = {}, inner = undefined, subtype = 'td', innerAttr = {}) {
|
|
519
|
-
if (
|
|
532
|
+
if (!isPlainObject(attr)) {
|
|
520
533
|
this.typeError('insertTableRow', 'Object');
|
|
521
534
|
}
|
|
522
535
|
let reference = this.getNthRow(y, false, true);
|
|
536
|
+
const AttributeToken = require('../attribute');
|
|
523
537
|
/** @type {TrToken & AttributeToken}} */
|
|
524
538
|
const token = Parser.run(() => new TrToken('\n|-', undefined, this.getAttribute('config')));
|
|
525
539
|
for (const [k, v] of Object.entries(attr)) {
|
|
@@ -539,7 +553,7 @@ class TableToken extends TrToken {
|
|
|
539
553
|
for (let i = 0; i < maxCol; i++) {
|
|
540
554
|
const coords = rowLayout[i];
|
|
541
555
|
if (!coords) {
|
|
542
|
-
token.
|
|
556
|
+
token.insertAt(td.cloneNode());
|
|
543
557
|
} else if (!set.has(coords)) {
|
|
544
558
|
set.add(coords);
|
|
545
559
|
if (coords.row < y) {
|
|
@@ -562,7 +576,7 @@ class TableToken extends TrToken {
|
|
|
562
576
|
* @throws `RangeError` 列号过大
|
|
563
577
|
*/
|
|
564
578
|
insertTableCol(x, inner, subtype = 'td', attr = {}) {
|
|
565
|
-
if (
|
|
579
|
+
if (!Number.isInteger(x)) {
|
|
566
580
|
this.typeError('insertTableCol', 'Number');
|
|
567
581
|
}
|
|
568
582
|
const layout = this.getLayout(),
|
|
@@ -631,11 +645,11 @@ class TableToken extends TrToken {
|
|
|
631
645
|
*/
|
|
632
646
|
removeTableCol(x) {
|
|
633
647
|
for (const [token, start] of this.getFullCol(x)) {
|
|
634
|
-
const {colspan,
|
|
648
|
+
const {colspan, lastChild} = token;
|
|
635
649
|
if (colspan > 1) {
|
|
636
650
|
token.colspan = colspan - 1;
|
|
637
651
|
if (start) {
|
|
638
|
-
|
|
652
|
+
lastChild.replaceChildren();
|
|
639
653
|
}
|
|
640
654
|
} else {
|
|
641
655
|
token.remove();
|
|
@@ -651,7 +665,7 @@ class TableToken extends TrToken {
|
|
|
651
665
|
* @throws `RangeError` 待合并区域与外侧区域有重叠
|
|
652
666
|
*/
|
|
653
667
|
mergeCells(xlim, ylim) {
|
|
654
|
-
if ([...xlim, ...ylim].
|
|
668
|
+
if (![...xlim, ...ylim].every(Number.isInteger)) {
|
|
655
669
|
this.typeError('mergeCells', 'Number');
|
|
656
670
|
}
|
|
657
671
|
const layout = this.getLayout(),
|
|
@@ -664,7 +678,7 @@ class TableToken extends TrToken {
|
|
|
664
678
|
if ([...layout[ymin - 1] ?? [], ...layout[ymax] ?? []].some(coords => set.has(coords))
|
|
665
679
|
|| layout.some(rowLayout => set.has(rowLayout[xmin - 1]) || set.has(rowLayout[xmax]))
|
|
666
680
|
) {
|
|
667
|
-
throw new RangeError(
|
|
681
|
+
throw new RangeError('待合并区域与外侧区域有重叠!');
|
|
668
682
|
}
|
|
669
683
|
const corner = layout[ymin][xmin],
|
|
670
684
|
rows = this.getAllRows(),
|
|
@@ -703,7 +717,7 @@ class TableToken extends TrToken {
|
|
|
703
717
|
if (x !== undefined) {
|
|
704
718
|
coords = this.toRawCoords(coords);
|
|
705
719
|
}
|
|
706
|
-
if (coords.start === false || x === undefined) {
|
|
720
|
+
if (coords.start === false || x === undefined) {
|
|
707
721
|
({x, y} = this.toRenderedCoords(coords));
|
|
708
722
|
}
|
|
709
723
|
const splitting = {rowspan: 1, colspan: 1};
|
|
@@ -804,7 +818,7 @@ class TableToken extends TrToken {
|
|
|
804
818
|
* @throws `RangeError` 无法移动
|
|
805
819
|
*/
|
|
806
820
|
moveTableRowBefore(y, before) {
|
|
807
|
-
if (
|
|
821
|
+
if (!Number.isInteger(y) || !Number.isInteger(before)) {
|
|
808
822
|
this.typeError('moveTableRowBefore', 'Number');
|
|
809
823
|
}
|
|
810
824
|
const layout = this.getLayout();
|
|
@@ -844,13 +858,13 @@ class TableToken extends TrToken {
|
|
|
844
858
|
* @throws `RangeError` 无法移动
|
|
845
859
|
*/
|
|
846
860
|
moveTableRowAfter(y, after) {
|
|
847
|
-
if (
|
|
861
|
+
if (!Number.isInteger(y) || !Number.isInteger(after)) {
|
|
848
862
|
this.typeError('moveTableRowAfter', 'Number');
|
|
849
863
|
}
|
|
850
864
|
const layout = this.getLayout(),
|
|
851
865
|
afterToken = this.getNthRow(after),
|
|
852
866
|
/** @type {TdToken[]} */
|
|
853
|
-
cells = afterToken.
|
|
867
|
+
cells = afterToken.childNodes.filter(child => child instanceof TdToken && child.subtype !== 'caption');
|
|
854
868
|
|
|
855
869
|
/**
|
|
856
870
|
* @type {(i: number, oneRow?: boolean) => number[]}
|
|
@@ -897,7 +911,7 @@ class TableToken extends TrToken {
|
|
|
897
911
|
* @throws `RangeError` 无法移动
|
|
898
912
|
*/
|
|
899
913
|
#moveCol(x, reference, after = false) {
|
|
900
|
-
if (
|
|
914
|
+
if (!Number.isInteger(x) || !Number.isInteger(reference)) {
|
|
901
915
|
this.typeError(`moveTableCol${after ? 'After' : 'Before'}`, 'Number');
|
|
902
916
|
}
|
|
903
917
|
const layout = this.getLayout();
|
|
@@ -926,7 +940,7 @@ class TableToken extends TrToken {
|
|
|
926
940
|
if (start) {
|
|
927
941
|
const original = token;
|
|
928
942
|
token = token.cloneNode();
|
|
929
|
-
original.
|
|
943
|
+
original.lastChild.replaceChildren();
|
|
930
944
|
token.colspan = 1;
|
|
931
945
|
}
|
|
932
946
|
}
|
|
@@ -934,7 +948,7 @@ class TableToken extends TrToken {
|
|
|
934
948
|
const col = rowLayout.slice(reference + Number(after)).find(({row}) => row === i)?.column;
|
|
935
949
|
rowToken.insertBefore(
|
|
936
950
|
token, col === undefined && rowToken.type === 'table'
|
|
937
|
-
? rowToken.
|
|
951
|
+
? rowToken.childNodes.slice(2).find(isRowEnd)
|
|
938
952
|
: col !== undefined && rowToken.getNthCol(col),
|
|
939
953
|
);
|
|
940
954
|
}
|