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.
- package/bundle/bundle.min.js +38 -0
- package/extensions/editor.css +62 -0
- package/extensions/editor.js +328 -0
- package/extensions/ui.css +119 -0
- package/package.json +12 -10
- package/config/default.json +0 -831
- package/config/llwiki.json +0 -595
- package/config/moegirl.json +0 -685
- package/config/zhwiki.json +0 -803
- package/index.js +0 -81
- package/lib/element.js +0 -136
- package/lib/node.js +0 -236
- package/lib/text.js +0 -150
- package/lib/title.js +0 -56
- package/mixin/hidden.js +0 -18
- package/parser/brackets.js +0 -125
- package/parser/commentAndExt.js +0 -58
- package/parser/converter.js +0 -45
- package/parser/externalLinks.js +0 -32
- package/parser/hrAndDoubleUnderscore.js +0 -37
- package/parser/html.js +0 -41
- package/parser/links.js +0 -93
- package/parser/list.js +0 -58
- package/parser/magicLinks.js +0 -40
- package/parser/quotes.js +0 -63
- package/parser/table.js +0 -113
- package/src/arg.js +0 -93
- package/src/atom/hidden.js +0 -11
- package/src/atom/index.js +0 -26
- package/src/attribute.js +0 -290
- package/src/attributes.js +0 -150
- package/src/converter.js +0 -70
- package/src/converterFlags.js +0 -97
- package/src/converterRule.js +0 -74
- package/src/extLink.js +0 -60
- package/src/gallery.js +0 -95
- package/src/hasNowiki/index.js +0 -32
- package/src/hasNowiki/pre.js +0 -28
- package/src/heading.js +0 -83
- package/src/html.js +0 -139
- package/src/imageParameter.js +0 -139
- package/src/imagemap.js +0 -140
- package/src/imagemapLink.js +0 -29
- package/src/index.js +0 -410
- package/src/link/category.js +0 -13
- package/src/link/file.js +0 -132
- package/src/link/galleryImage.js +0 -62
- package/src/link/index.js +0 -115
- package/src/magicLink.js +0 -66
- package/src/nested/choose.js +0 -23
- package/src/nested/combobox.js +0 -22
- package/src/nested/index.js +0 -66
- package/src/nested/references.js +0 -22
- package/src/nowiki/comment.js +0 -47
- package/src/nowiki/dd.js +0 -13
- package/src/nowiki/doubleUnderscore.js +0 -26
- package/src/nowiki/hr.js +0 -22
- package/src/nowiki/index.js +0 -34
- package/src/nowiki/list.js +0 -13
- package/src/nowiki/noinclude.js +0 -14
- package/src/nowiki/quote.js +0 -55
- package/src/onlyinclude.js +0 -39
- package/src/paramTag/index.js +0 -66
- package/src/paramTag/inputbox.js +0 -32
- package/src/parameter.js +0 -97
- package/src/syntax.js +0 -23
- package/src/table/index.js +0 -46
- package/src/table/td.js +0 -118
- package/src/table/tr.js +0 -74
- package/src/tagPair/ext.js +0 -128
- package/src/tagPair/include.js +0 -26
- package/src/tagPair/index.js +0 -77
- package/src/transclude.js +0 -336
- package/util/base.js +0 -17
- package/util/diff.js +0 -76
- package/util/lint.js +0 -55
- 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;
|
package/src/atom/hidden.js
DELETED
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;
|