wikiparser-node 0.9.0-m → 0.9.1-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.
Files changed (77) hide show
  1. package/bundle/bundle.min.js +38 -0
  2. package/extensions/editor.css +62 -0
  3. package/extensions/editor.js +328 -0
  4. package/extensions/ui.css +119 -0
  5. package/package.json +12 -10
  6. package/config/default.json +0 -831
  7. package/config/llwiki.json +0 -595
  8. package/config/moegirl.json +0 -685
  9. package/config/zhwiki.json +0 -803
  10. package/index.js +0 -81
  11. package/lib/element.js +0 -136
  12. package/lib/node.js +0 -236
  13. package/lib/text.js +0 -150
  14. package/lib/title.js +0 -56
  15. package/mixin/hidden.js +0 -18
  16. package/parser/brackets.js +0 -125
  17. package/parser/commentAndExt.js +0 -58
  18. package/parser/converter.js +0 -45
  19. package/parser/externalLinks.js +0 -32
  20. package/parser/hrAndDoubleUnderscore.js +0 -37
  21. package/parser/html.js +0 -41
  22. package/parser/links.js +0 -93
  23. package/parser/list.js +0 -58
  24. package/parser/magicLinks.js +0 -40
  25. package/parser/quotes.js +0 -63
  26. package/parser/table.js +0 -113
  27. package/src/arg.js +0 -93
  28. package/src/atom/hidden.js +0 -11
  29. package/src/atom/index.js +0 -26
  30. package/src/attribute.js +0 -290
  31. package/src/attributes.js +0 -150
  32. package/src/converter.js +0 -70
  33. package/src/converterFlags.js +0 -97
  34. package/src/converterRule.js +0 -74
  35. package/src/extLink.js +0 -60
  36. package/src/gallery.js +0 -95
  37. package/src/hasNowiki/index.js +0 -32
  38. package/src/hasNowiki/pre.js +0 -28
  39. package/src/heading.js +0 -83
  40. package/src/html.js +0 -139
  41. package/src/imageParameter.js +0 -139
  42. package/src/imagemap.js +0 -140
  43. package/src/imagemapLink.js +0 -29
  44. package/src/index.js +0 -410
  45. package/src/link/category.js +0 -13
  46. package/src/link/file.js +0 -132
  47. package/src/link/galleryImage.js +0 -62
  48. package/src/link/index.js +0 -115
  49. package/src/magicLink.js +0 -66
  50. package/src/nested/choose.js +0 -23
  51. package/src/nested/combobox.js +0 -22
  52. package/src/nested/index.js +0 -66
  53. package/src/nested/references.js +0 -22
  54. package/src/nowiki/comment.js +0 -47
  55. package/src/nowiki/dd.js +0 -13
  56. package/src/nowiki/doubleUnderscore.js +0 -26
  57. package/src/nowiki/hr.js +0 -22
  58. package/src/nowiki/index.js +0 -34
  59. package/src/nowiki/list.js +0 -13
  60. package/src/nowiki/noinclude.js +0 -14
  61. package/src/nowiki/quote.js +0 -55
  62. package/src/onlyinclude.js +0 -39
  63. package/src/paramTag/index.js +0 -66
  64. package/src/paramTag/inputbox.js +0 -32
  65. package/src/parameter.js +0 -97
  66. package/src/syntax.js +0 -23
  67. package/src/table/index.js +0 -46
  68. package/src/table/td.js +0 -118
  69. package/src/table/tr.js +0 -74
  70. package/src/tagPair/ext.js +0 -128
  71. package/src/tagPair/include.js +0 -26
  72. package/src/tagPair/index.js +0 -77
  73. package/src/transclude.js +0 -336
  74. package/util/base.js +0 -17
  75. package/util/diff.js +0 -76
  76. package/util/lint.js +0 -55
  77. package/util/string.js +0 -75
package/parser/table.js DELETED
@@ -1,113 +0,0 @@
1
- 'use strict';
2
-
3
- const Parser = require('..'),
4
- AstText = require('../lib/text'),
5
- Token = require('../src'),
6
- TableToken = require('../src/table'),
7
- TrToken = require('../src/table/tr'),
8
- TdToken = require('../src/table/td'),
9
- DdToken = require('../src/nowiki/dd');
10
-
11
- /**
12
- * 解析表格,注意`tr`和`td`包含开头的换行
13
- * @param {Token & {firstChild: AstText}} root 根节点
14
- * @param {accum} accum
15
- */
16
- const parseTable = ({firstChild: {data}, type, name}, config = Parser.getConfig(), accum = []) => {
17
- const /** @type {TrToken[]} */ stack = [],
18
- lines = data.split('\n');
19
- let out = type === 'root' || type === 'parameter-value' || type === 'ext-inner' && name === 'poem'
20
- ? ''
21
- : `\n${lines.shift()}`;
22
-
23
- /**
24
- * 向表格中插入纯文本
25
- * @param {string} str 待插入的文本
26
- * @param {TrToken} top 当前解析的表格或表格行
27
- */
28
- const push = (str, top) => {
29
- if (!top) {
30
- out += str;
31
- return;
32
- }
33
- const /** @type {Token}} */ {lastChild} = top;
34
- if (lastChild.constructor === Token) {
35
- lastChild.setText(String(lastChild) + str);
36
- } else {
37
- const token = new Token(str, config, true, accum);
38
- token.type = 'table-inter';
39
- top.insertAt(token.setAttribute('stage', 3));
40
- }
41
- };
42
- for (const outLine of lines) {
43
- let top = stack.pop();
44
- const [spaces] = /^(?:\s|\0\d+c\x7F)*/u.exec(outLine),
45
- line = outLine.slice(spaces.length),
46
- matchesStart = /^(:*)((?:\s|\0\d+c\x7F)*)(\{\||\{(?:\0\d+c\x7F)*\0\d+!\x7F|\0\d+\{\x7F)(.*)$/u.exec(line);
47
- if (matchesStart) {
48
- while (top && top.type !== 'td') {
49
- top = stack.pop();
50
- }
51
- const [, indent, moreSpaces, tableSyntax, attr] = matchesStart;
52
- if (indent) {
53
- new DdToken(indent, config, accum);
54
- }
55
- push(`\n${spaces}${indent && `\0${accum.length - 1}d\x7F`}${moreSpaces}\0${accum.length}b\x7F`, top);
56
- const table = new TableToken(tableSyntax, attr, config, accum);
57
- stack.push(...top ? [top] : [], table);
58
- continue;
59
- } else if (!top) {
60
- out += `\n${outLine}`;
61
- continue;
62
- }
63
- const matches = // eslint-disable-line operator-linebreak
64
- /^(?:(\|\}|\0\d+!\x7F\}|\0\d+\}\x7F)|(\|-+|\0\d+!\x7F-+|\0\d+-\x7F-*)(?!-)|(!|(?:\||\0\d+!\x7F)\+?))(.*)$/u
65
- .exec(line);
66
- if (!matches) {
67
- push(`\n${outLine}`, top);
68
- stack.push(...top ? [top] : []);
69
- continue;
70
- }
71
- const [, closing, row, cell, attr] = matches;
72
- if (closing) {
73
- while (!(top instanceof TableToken)) {
74
- top = stack.pop();
75
- }
76
- top.close(`\n${spaces}${closing}`, true);
77
- push(attr, stack.at(-1));
78
- } else if (row) {
79
- if (top.type === 'td') {
80
- top = stack.pop();
81
- }
82
- if (top.type === 'tr') {
83
- top = stack.pop();
84
- }
85
- const tr = new TrToken(`\n${spaces}${row}`, attr, config, accum);
86
- stack.push(top, tr);
87
- top.insertAt(tr);
88
- } else {
89
- if (top.type === 'td') {
90
- top = stack.pop();
91
- }
92
- const regex = cell === '!'
93
- ? /!!|(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu
94
- : /(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu;
95
- let mt = regex.exec(attr),
96
- lastIndex = 0,
97
- lastSyntax = `\n${spaces}${cell}`;
98
- while (mt) {
99
- const td = new TdToken(lastSyntax, attr.slice(lastIndex, mt.index), config, accum);
100
- top.insertAt(td);
101
- ({lastIndex} = regex);
102
- [lastSyntax] = mt;
103
- mt = regex.exec(attr);
104
- }
105
- const td = new TdToken(lastSyntax, attr.slice(lastIndex), config, accum);
106
- stack.push(top, td);
107
- top.insertAt(td);
108
- }
109
- }
110
- return out.slice(1);
111
- };
112
-
113
- module.exports = parseTable;
package/src/arg.js DELETED
@@ -1,93 +0,0 @@
1
- 'use strict';
2
-
3
- const {text} = require('../util/string'),
4
- {generateForSelf, generateForChild} = require('../util/lint'),
5
- Parser = require('..'),
6
- Token = require('.');
7
-
8
- /**
9
- * `{{{}}}`包裹的参数
10
- * @classdesc `{childNodes: [AtomToken, ?Token, ...HiddenToken]}`
11
- */
12
- class ArgToken extends Token {
13
- type = 'arg';
14
-
15
- /** default */
16
- get default() {
17
- return this.childNodes[1]?.text() ?? false;
18
- }
19
-
20
- /**
21
- * @param {string[]} parts 以'|'分隔的各部分
22
- * @param {accum} accum
23
- * @complexity `n`
24
- */
25
- constructor(parts, config = Parser.getConfig(), accum = []) {
26
- super(undefined, config, true, accum, {
27
- });
28
- for (let i = 0; i < parts.length; i++) {
29
- if (i === 0 || i > 1) {
30
- const AtomToken = i === 0 ? require('./atom') : require('./atom/hidden');
31
- const token = new AtomToken(parts[i], i === 0 ? 'arg-name' : undefined, config, accum, {
32
- });
33
- super.insertAt(token);
34
- } else {
35
- const token = new Token(parts[i], config, true, accum);
36
- token.type = 'arg-default';
37
- super.insertAt(token.setAttribute('stage', 2));
38
- }
39
- }
40
- }
41
-
42
- /**
43
- * @override
44
- */
45
- toString(selector) {
46
- return `{{{${super.toString(selector, '|')}}}}`;
47
- }
48
-
49
- /**
50
- * @override
51
- * @returns {string}
52
- */
53
- text() {
54
- return `{{{${text(this.childNodes.slice(0, 2), '|')}}}}`;
55
- }
56
-
57
- /** @override */
58
- getPadding() {
59
- return 3;
60
- }
61
-
62
- /** @override */
63
- getGaps() {
64
- return 1;
65
- }
66
-
67
- /**
68
- * @override
69
- * @param {number} start 起始位置
70
- * @returns {LintError[]}
71
- */
72
- lint(start = this.getAbsoluteIndex()) {
73
- if (!this.getAttribute('include')) {
74
- return [generateForSelf(this, {start}, 'unexpected template argument')];
75
- }
76
- const {childNodes: [argName, argDefault, ...rest]} = this,
77
- errors = argName.lint(start + 3);
78
- if (argDefault) {
79
- errors.push(...argDefault.lint(start + 4 + String(argName).length));
80
- }
81
- if (rest.length > 0) {
82
- const rect = {start, ...this.getRootNode().posFromIndex(start)};
83
- errors.push(...rest.map(child => {
84
- const error = generateForChild(child, rect, 'invisible content inside triple brackets'),
85
- {startIndex, startCol, excerpt} = error;
86
- return {...error, startIndex: startIndex - 1, startCol: startCol - 1, excerpt: `|${excerpt}`};
87
- }));
88
- }
89
- return errors;
90
- }
91
- }
92
-
93
- module.exports = ArgToken;
@@ -1,11 +0,0 @@
1
- 'use strict';
2
-
3
- const hidden = require('../../mixin/hidden'),
4
- AtomToken = require('.');
5
-
6
- /** 不可见的节点 */
7
- class HiddenToken extends hidden(AtomToken) {
8
- type = 'hidden';
9
- }
10
-
11
- module.exports = HiddenToken;
package/src/atom/index.js DELETED
@@ -1,26 +0,0 @@
1
- 'use strict';
2
-
3
- const Parser = require('../..'),
4
- Token = require('..');
5
-
6
- /**
7
- * 不会被继续解析的plain Token
8
- * @classdesc `{childNodes: ...AstText|Token}`
9
- */
10
- class AtomToken extends Token {
11
- type = 'plain';
12
-
13
- /**
14
- * @param {string} wikitext wikitext
15
- * @param {string|undefined} type Token.type
16
- * @param {accum} accum
17
- */
18
- constructor(wikitext, type, config = Parser.getConfig(), accum = [], acceptable = undefined) {
19
- super(wikitext, config, true, accum, acceptable);
20
- if (type) {
21
- this.type = type;
22
- }
23
- }
24
- }
25
-
26
- module.exports = AtomToken;
package/src/attribute.js DELETED
@@ -1,290 +0,0 @@
1
- 'use strict';
2
-
3
- const {generateForChild} = require('../util/lint'),
4
- {removeComment} = require('../util/string'),
5
- Parser = require('..'),
6
- Token = require('.'),
7
- AtomToken = require('./atom');
8
-
9
- const commonHtmlAttrs = new Set([
10
- 'id',
11
- 'class',
12
- 'style',
13
- 'lang',
14
- 'dir',
15
- 'title',
16
- 'tabindex',
17
- 'aria-describedby',
18
- 'aria-flowto',
19
- 'aria-hidden',
20
- 'aria-label',
21
- 'aria-labelledby',
22
- 'aria-owns',
23
- 'role',
24
- 'about',
25
- 'property',
26
- 'resource',
27
- 'datatype',
28
- 'typeof',
29
- 'itemid',
30
- 'itemprop',
31
- 'itemref',
32
- 'itemscope',
33
- 'itemtype',
34
- ]),
35
- blockAttrs = new Set(['align']),
36
- citeAttrs = new Set(['cite']),
37
- citeAndAttrs = new Set(['cite', 'datetime']),
38
- widthAttrs = new Set(['width']),
39
- tdAttrs = new Set(
40
- ['align', 'valign', 'abbr', 'axis', 'headers', 'scope', 'rowspan', 'colspan', 'width', 'height', 'bgcolor'],
41
- ),
42
- typeAttrs = new Set(['type']),
43
- htmlAttrs = {
44
- div: blockAttrs,
45
- h1: blockAttrs,
46
- h2: blockAttrs,
47
- h3: blockAttrs,
48
- h4: blockAttrs,
49
- h5: blockAttrs,
50
- h6: blockAttrs,
51
- blockquote: citeAttrs,
52
- q: citeAttrs,
53
- p: blockAttrs,
54
- br: new Set(['clear']),
55
- pre: widthAttrs,
56
- ins: citeAndAttrs,
57
- del: citeAndAttrs,
58
- ul: typeAttrs,
59
- ol: new Set(['type', 'start', 'reversed']),
60
- li: new Set(['type', 'value']),
61
- table: new Set(
62
- ['summary', 'width', 'border', 'frame', 'rules', 'cellspacing', 'cellpadding', 'align', 'bgcolor'],
63
- ),
64
- caption: blockAttrs,
65
- tr: new Set(['bgcolor', 'align', 'valign']),
66
- td: tdAttrs,
67
- th: tdAttrs,
68
- img: new Set(['alt', 'src', 'width', 'height', 'srcset']),
69
- font: new Set(['size', 'color', 'face']),
70
- hr: widthAttrs,
71
- rt: new Set(['rbspan']),
72
- data: new Set(['value']),
73
- time: new Set(['datetime']),
74
- meta: new Set(['itemprop', 'content']),
75
- link: new Set(['itemprop', 'href', 'title']),
76
- gallery: new Set(['mode', 'showfilename', 'caption', 'perrow', 'widths', 'heights', 'showthumbnails', 'type']),
77
- poem: new Set(['compact', 'align']),
78
- categorytree: new Set([
79
- 'align',
80
- 'hideroot',
81
- 'onlyroot',
82
- 'depth',
83
- 'mode',
84
- 'hideprefix',
85
- 'namespaces',
86
- 'showcount',
87
- 'notranslations',
88
- ]),
89
- combooption: new Set(['name', 'for', 'inline', 'align']),
90
- },
91
- empty = new Set(),
92
- extAttrs = {
93
- nowiki: empty,
94
- indicator: new Set(['name']),
95
- langconvert: new Set(['from', 'to']),
96
- ref: new Set(['group', 'name', 'extends', 'follow', 'dir']),
97
- references: new Set(['group', 'responsive']),
98
- charinsert: new Set(['label']),
99
- choose: new Set(['uncached', 'before', 'after']),
100
- option: new Set(['weight']),
101
- imagemap: empty,
102
- inputbox: empty,
103
- templatestyles: new Set(['src', 'wrapper']),
104
- dynamicpagelist: empty,
105
- poll: new Set(['id', 'show-results-before-voting']),
106
- sm2: typeAttrs,
107
- flashmp3: typeAttrs,
108
- tab: new Set([
109
- 'nested',
110
- 'name',
111
- 'index',
112
- 'class',
113
- 'block',
114
- 'inline',
115
- 'openname',
116
- 'closename',
117
- 'collapsed',
118
- 'dropdown',
119
- 'style',
120
- 'bgcolor',
121
- 'container',
122
- 'id',
123
- 'title',
124
- ]),
125
- tabs: new Set(['plain', 'class', 'container', 'id', 'title', 'style']),
126
- combobox: new Set(['placeholder', 'value', 'id', 'class', 'text', 'dropdown', 'style']),
127
- },
128
- insecureStyle = new RegExp(
129
- `${
130
- 'expression'
131
- }|${
132
- '(?:filter|accelerator|-o-link(?:-source)?|-o-replace)\\s*:'
133
- }|${
134
- '(?:url|image(?:-set)?)\\s*\\('
135
- }|${
136
- 'attr\\s*\\([^)]+[\\s,]url'
137
- }`,
138
- 'u',
139
- );
140
-
141
- /**
142
- * 扩展和HTML标签属性
143
- * @classdesc `{childNodes: [AtomToken, Token|AtomToken]}`
144
- */
145
- class AttributeToken extends Token {
146
- #equal;
147
- #quotes;
148
- #tag;
149
-
150
- /** 引号是否匹配 */
151
- get balanced() {
152
- return !this.#equal || this.#quotes[0] === this.#quotes[1];
153
- }
154
-
155
- /** getValue()的getter */
156
- get value() {
157
- return this.getValue();
158
- }
159
-
160
- /** 标签名 */
161
- get tag() {
162
- return this.#tag;
163
- }
164
-
165
- /**
166
- * @param {'ext-attr'|'html-attr'|'table-attr'} type 标签类型
167
- * @param {string} tag 标签名
168
- * @param {string} key 属性名
169
- * @param {string} equal 等号
170
- * @param {string} value 属性值
171
- * @param {string[]} quotes 引号
172
- * @param {accum} accum
173
- */
174
- constructor(type, tag, key, equal = '', value = '', quotes = [], config = Parser.getConfig(), accum = []) {
175
- const keyToken = new AtomToken(key, 'attr-key', config, accum, {
176
- });
177
- let valueToken;
178
- if (key === 'title') {
179
- valueToken = new Token(value, config, true, accum, {
180
- }).setAttribute('type', 'attr-value').setAttribute('stage', Parser.MAX_STAGE - 1);
181
- } else if (tag === 'gallery' && key === 'caption') {
182
- /** @type {ParserConfig} */
183
- const newConfig = {...config, excludes: [...config.excludes, 'quote', 'extLink', 'magicLink', 'list']};
184
- valueToken = new Token(value, newConfig, true, accum, {
185
- }).setAttribute('type', 'attr-value').setAttribute('stage', 5);
186
- } else if (tag === 'choose' && (key === 'before' || key === 'after')) {
187
- /** @type {ParserConfig} */
188
- const newConfig = {...config, excludes: [...config.excludes, 'heading', 'html', 'table', 'hr', 'list']};
189
- valueToken = new Token(value, newConfig, true, accum, {
190
- }).setAttribute('type', 'attr-value').setAttribute('stage', 1);
191
- } else {
192
- valueToken = new AtomToken(value, 'attr-value', config, accum, {
193
- });
194
- }
195
- super(undefined, config, true, accum);
196
- this.type = type;
197
- this.append(keyToken, valueToken);
198
- this.#equal = equal;
199
- this.#quotes = quotes;
200
- this.#tag = tag;
201
- this.setAttribute('name', removeComment(key).trim().toLowerCase());
202
- }
203
-
204
- /** @override */
205
- afterBuild() {
206
- if (this.#equal.includes('\0')) {
207
- this.#equal = this.getAttribute('buildFromStr')(this.#equal, 'string');
208
- }
209
- if (this.parentNode) {
210
- this.#tag = this.parentNode.name;
211
- }
212
- this.setAttribute('name', this.firstChild.text().trim().toLowerCase());
213
- }
214
-
215
- /**
216
- * @override
217
- * @returns {string}
218
- */
219
- toString(selector) {
220
- const [quoteStart = '', quoteEnd = ''] = this.#quotes;
221
- return this.#equal
222
- ? `${super.toString(selector, `${this.#equal}${quoteStart}`)}${quoteEnd}`
223
- : this.firstChild.toString(selector);
224
- }
225
-
226
- /**
227
- * @override
228
- * @returns {string}
229
- */
230
- text() {
231
- return this.#equal ? `${super.text(`${this.#equal.trim()}"`)}"` : this.firstChild.text();
232
- }
233
-
234
- /** @override */
235
- getGaps() {
236
- return this.#equal ? this.#equal.length + (this.#quotes[0]?.length ?? 0) : 0;
237
- }
238
-
239
- /** @override */
240
- print() {
241
- const [quoteStart = '', quoteEnd = ''] = this.#quotes;
242
- return this.#equal ? super.print({sep: `${this.#equal}${quoteStart}`, post: quoteEnd}) : super.print();
243
- }
244
-
245
- /**
246
- * @override
247
- * @param {number} start 起始位置
248
- */
249
- lint(start = this.getAbsoluteIndex()) {
250
- const errors = super.lint(start),
251
- {balanced, firstChild, lastChild, type, name, parentNode, value} = this,
252
- tagName = parentNode?.name;
253
- let rect;
254
- if (!balanced) {
255
- const root = this.getRootNode();
256
- rect = {start, ...root.posFromIndex(start)};
257
- const e = generateForChild(lastChild, rect, 'unclosed quotes', 'warning'),
258
- startIndex = e.startIndex - 1,
259
- startCol = e.startCol - 1;
260
- errors.push({...e, startIndex, startCol, excerpt: String(root).slice(startIndex, startIndex + 50)});
261
- }
262
- if (!/\{\{[^{]+\}\}/u.test(name) && (
263
- type === 'ext-attr' && !(tagName in htmlAttrs) && extAttrs[tagName] && !extAttrs[tagName].has(name)
264
- || (type === 'html-attr' || type === 'table-attr' || tagName in htmlAttrs) && !htmlAttrs[tagName]?.has(name)
265
- && !/^(?:xmlns:[\w:.-]+|data-[^:]*)$/u.test(name)
266
- && (tagName === 'meta' || tagName === 'link' || !commonHtmlAttrs.has(name))
267
- )) {
268
- rect ||= {start, ...this.getRootNode().posFromIndex(start)};
269
- errors.push(generateForChild(firstChild, rect, 'illegal attribute name'));
270
- } else if (name === 'style' && typeof value === 'string' && insecureStyle.test(value)) {
271
- rect ||= {start, ...this.getRootNode().posFromIndex(start)};
272
- errors.push(generateForChild(lastChild, rect, 'insecure style'));
273
- }
274
- return errors;
275
- }
276
-
277
- /** 获取属性值 */
278
- getValue() {
279
- if (this.#equal) {
280
- const value = this.lastChild.text();
281
- if (this.#quotes[1]) {
282
- return value;
283
- }
284
- return this.#quotes[0] ? value.trimEnd() : value.trim();
285
- }
286
- return true;
287
- }
288
- }
289
-
290
- module.exports = AttributeToken;
package/src/attributes.js DELETED
@@ -1,150 +0,0 @@
1
- 'use strict';
2
-
3
- const {generateForSelf, generateForChild} = require('../util/lint'),
4
- {removeComment} = require('../util/string'),
5
- Parser = require('..'),
6
- Token = require('.'),
7
- AtomToken = require('./atom'),
8
- AttributeToken = require('./attribute');
9
-
10
- /**
11
- * 扩展和HTML标签属性
12
- * @classdesc `{childNodes: ...AtomToken|AttributeToken}`
13
- */
14
- class AttributesToken extends Token {
15
- /**
16
- * @param {string} attr 标签属性
17
- * @param {'ext-attrs'|'html-attrs'|'table-attrs'} type 标签类型
18
- * @param {string} name 标签名
19
- * @param {accum} accum
20
- */
21
- constructor(attr, type, name, config = Parser.getConfig(), accum = []) {
22
- super(undefined, config, true, accum, {
23
- });
24
- this.type = type;
25
- this.setAttribute('name', name);
26
- if (attr) {
27
- const regex = new RegExp(
28
- `([^\\s/](?:(?!\0\\d+~\x7F)[^\\s/=])*)` // 属性名
29
- + '(?:'
30
- + '((?:\\s|\0\\d+c\x7F)*' // `=`前的空白字符
31
- + '(?:=|\0\\d+~\x7F)' // `=`
32
- + '(?:\\s|\0\\d+c\x7F)*)' // `=`后的空白字符
33
- + `(?:(["'])(.*?)(\\3|$)|(\\S*))` // 属性值
34
- + ')?',
35
- 'gsu',
36
- );
37
- let out = '',
38
- mt = regex.exec(attr),
39
- lastIndex = 0;
40
- const insertDirty = /** 插入无效属性 */ () => {
41
- if (out) {
42
- super.insertAt(new AtomToken(out, `${type.slice(0, -1)}-dirty`, config, accum, {
43
- }));
44
- out = '';
45
- }
46
- };
47
- while (mt) {
48
- const {index, 0: full, 1: key, 2: equal, 3: quoteStart, 4: quoted, 5: quoteEnd, 6: unquoted} = mt;
49
- out += attr.slice(lastIndex, index);
50
- if (/^(?:[\w:]|\0\d+[t!~{}+-]\x7F)(?:[\w:.-]|\0\d+[t!~{}+-]\x7F)*$/u.test(removeComment(key).trim())) {
51
- const value = quoted ?? unquoted,
52
- quotes = [quoteStart, quoteEnd],
53
- token = new AttributeToken(type.slice(0, -1), name, key, equal, value, quotes, config, accum);
54
- insertDirty();
55
- super.insertAt(token);
56
- } else {
57
- out += full;
58
- }
59
- ({lastIndex} = regex);
60
- mt = regex.exec(attr);
61
- }
62
- out += attr.slice(lastIndex);
63
- insertDirty();
64
- }
65
- }
66
-
67
- /**
68
- * @override
69
- * @this {AttributesToken & {parentNode: TdToken}}
70
- */
71
- afterBuild() {
72
- const TdToken = require('./table/td');
73
- if (this.type === 'table-attrs') {
74
- this.setAttribute('name', this.parentNode?.subtype === 'caption' ? 'caption' : this.parentNode?.type);
75
- }
76
- }
77
-
78
- /**
79
- * 所有指定属性名的AttributeToken
80
- * @param {string} key 属性名
81
- * @returns {AttributeToken[]}
82
- */
83
- getAttrTokens(key) {
84
- return this.childNodes.filter(
85
- child => child instanceof AttributeToken && child.name === key.toLowerCase().trim(),
86
- );
87
- }
88
-
89
- /**
90
- * 制定属性名的最后一个AttributeToken
91
- * @param {string} key 属性名
92
- */
93
- getAttrToken(key) {
94
- const tokens = this.getAttrTokens(key);
95
- return tokens.at(-1);
96
- }
97
-
98
- /**
99
- * 获取标签属性
100
- * @param {string} key 属性键
101
- */
102
- getAttr(key) {
103
- return this.getAttrToken(key)?.getValue();
104
- }
105
-
106
- /**
107
- * @override
108
- * @this {AttributesToken & {parentNode: HtmlToken}}
109
- * @param {number} start 起始位置
110
- */
111
- lint(start = this.getAbsoluteIndex()) {
112
- const HtmlToken = require('./html');
113
- const errors = super.lint(start),
114
- {parentNode: {closing}, length, childNodes} = this,
115
- /** @type {Record<string, AttributeToken[]>} */ attrs = {},
116
- /** @type {Set<string>} */ duplicated = new Set();
117
- let rect;
118
- if (closing && this.text().trim()) {
119
- rect = {start, ...this.getRootNode().posFromIndex(start)};
120
- errors.push(generateForSelf(this, rect, 'attributes of a closing tag'));
121
- }
122
- for (let i = 0; i < length; i++) {
123
- const /** @type {AtomToken|AttributeToken} */ attr = childNodes[i];
124
- if (attr instanceof AtomToken && attr.text().trim()) {
125
- rect ||= {start, ...this.getRootNode().posFromIndex(start)};
126
- errors.push({
127
- ...generateForChild(attr, rect, 'containing invalid attribute'),
128
- excerpt: childNodes.slice(i).map(String).join('').slice(0, 50),
129
- });
130
- } else if (attr instanceof AttributeToken) {
131
- const {name} = attr;
132
- if (name in attrs) {
133
- duplicated.add(name);
134
- attrs[name].push(attr);
135
- } else if (name !== 'class') {
136
- attrs[name] = [attr];
137
- }
138
- }
139
- }
140
- if (duplicated.size > 0) {
141
- rect ||= {start, ...this.getRootNode().posFromIndex(start)};
142
- for (const key of duplicated) {
143
- errors.push(...attrs[key].map(attr => generateForChild(attr, rect, Parser.msg('duplicated $1 attribute', key))));
144
- }
145
- }
146
- return errors;
147
- }
148
- }
149
-
150
- module.exports = AttributesToken;