@asamuzakjp/dom-selector 0.19.4 → 0.19.6
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/package.json +12 -12
- package/src/js/constant.js +2 -0
- package/src/js/matcher.js +111 -115
- package/src/js/parser.js +4 -4
package/package.json
CHANGED
|
@@ -21,26 +21,26 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"css-tree": "^2.3.1",
|
|
23
23
|
"is-potential-custom-element-name": "^1.0.1",
|
|
24
|
-
"xpath": "^0.0.
|
|
24
|
+
"xpath": "^0.0.33"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/css-tree": "^2.3.
|
|
27
|
+
"@types/css-tree": "^2.3.2",
|
|
28
28
|
"benchmark": "^2.1.4",
|
|
29
|
-
"c8": "^8.0.
|
|
30
|
-
"chai": "^4.3.
|
|
29
|
+
"c8": "^8.0.1",
|
|
30
|
+
"chai": "^4.3.8",
|
|
31
31
|
"css2xpath": "^0.0.3",
|
|
32
|
-
"eslint": "^8.
|
|
32
|
+
"eslint": "^8.50.0",
|
|
33
33
|
"eslint-config-standard": "^17.1.0",
|
|
34
|
-
"eslint-plugin-import": "^2.
|
|
35
|
-
"eslint-plugin-jsdoc": "^46.
|
|
34
|
+
"eslint-plugin-import": "^2.28.1",
|
|
35
|
+
"eslint-plugin-jsdoc": "^46.8.2",
|
|
36
36
|
"eslint-plugin-regexp": "^1.15.0",
|
|
37
|
-
"eslint-plugin-unicorn": "^
|
|
37
|
+
"eslint-plugin-unicorn": "^48.0.1",
|
|
38
38
|
"jsdom": "^22.1.0",
|
|
39
|
-
"linkedom": "^0.
|
|
39
|
+
"linkedom": "^0.15.3",
|
|
40
40
|
"mocha": "^10.2.0",
|
|
41
41
|
"nwsapi": "^2.2.7",
|
|
42
|
-
"sinon": "^
|
|
43
|
-
"typescript": "^5.
|
|
42
|
+
"sinon": "^16.0.0",
|
|
43
|
+
"typescript": "^5.2.2"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"bench": "node benchmark/benchmark.js",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"test": "c8 --reporter=text mocha --exit test/**/*.test.js",
|
|
50
50
|
"tsc": "npx tsc"
|
|
51
51
|
},
|
|
52
|
-
"version": "0.19.
|
|
52
|
+
"version": "0.19.6"
|
|
53
53
|
}
|
package/src/js/constant.js
CHANGED
|
@@ -8,6 +8,7 @@ export const CLASS_SELECTOR = 'ClassSelector';
|
|
|
8
8
|
export const COMBINATOR = 'Combinator';
|
|
9
9
|
export const ID_SELECTOR = 'IdSelector';
|
|
10
10
|
export const IDENTIFIER = 'Identifier';
|
|
11
|
+
export const NOT_SUPPORTED_ERR = 'NotSupportedError';
|
|
11
12
|
export const NTH = 'Nth';
|
|
12
13
|
export const PSEUDO_CLASS_SELECTOR = 'PseudoClassSelector';
|
|
13
14
|
export const PSEUDO_ELEMENT_SELECTOR = 'PseudoElementSelector';
|
|
@@ -15,4 +16,5 @@ export const RAW = 'Raw';
|
|
|
15
16
|
export const SELECTOR = 'Selector';
|
|
16
17
|
export const SELECTOR_LIST = 'SelectorList';
|
|
17
18
|
export const STRING = 'String';
|
|
19
|
+
export const SYNTAX_ERR = 'SyntaxError';
|
|
18
20
|
export const TYPE_SELECTOR = 'TypeSelector';
|
package/src/js/matcher.js
CHANGED
|
@@ -10,7 +10,8 @@ import { generateCSS, parseSelector, walkAST } from './parser.js';
|
|
|
10
10
|
/* constants */
|
|
11
11
|
import {
|
|
12
12
|
ATTRIBUTE_SELECTOR, CLASS_SELECTOR, COMBINATOR, ID_SELECTOR,
|
|
13
|
-
PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR,
|
|
13
|
+
NOT_SUPPORTED_ERR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR,
|
|
14
|
+
SYNTAX_ERR, TYPE_SELECTOR
|
|
14
15
|
} from './constant.js';
|
|
15
16
|
const BIT_ATTRIBUTE_SELECTOR = 16;
|
|
16
17
|
const BIT_CLASS_SELECTOR = 2;
|
|
@@ -26,6 +27,10 @@ const DOCUMENT_POSITION_PRECEDING = 2;
|
|
|
26
27
|
const ELEMENT_NODE = 1;
|
|
27
28
|
const FILTER_SHOW_ELEMENT = 1;
|
|
28
29
|
const TEXT_NODE = 3;
|
|
30
|
+
const TYPE_ALL = 'all';
|
|
31
|
+
const TYPE_FIRST = 'first';
|
|
32
|
+
const TYPE_LINEAL = 'lineal';
|
|
33
|
+
const TYPE_SELF = 'self';
|
|
29
34
|
|
|
30
35
|
/* regexp */
|
|
31
36
|
const DIR_VALUE = /^(?:auto|ltr|rtl)$/;
|
|
@@ -48,7 +53,9 @@ const WHITESPACE = /^[\n\r\f]/;
|
|
|
48
53
|
export const isContentEditable = (node = {}) => {
|
|
49
54
|
let res;
|
|
50
55
|
if (node.nodeType === ELEMENT_NODE) {
|
|
51
|
-
if (node.
|
|
56
|
+
if (typeof node.isContentEditable === 'boolean') {
|
|
57
|
+
res = node.isContentEditable;
|
|
58
|
+
} else if (node.ownerDocument.designMode === 'on') {
|
|
52
59
|
res = true;
|
|
53
60
|
} else if (node.hasAttribute('contenteditable')) {
|
|
54
61
|
const attr = node.getAttribute('contenteditable');
|
|
@@ -176,14 +183,14 @@ export const parseASTName = (name, node) => {
|
|
|
176
183
|
[astPrefix, astNodeName] = name.split('|');
|
|
177
184
|
if (astPrefix && astPrefix !== '*' &&
|
|
178
185
|
node && !isNamespaceDeclared(astPrefix, node)) {
|
|
179
|
-
throw new DOMException(`invalid selector ${name}`,
|
|
186
|
+
throw new DOMException(`invalid selector ${name}`, SYNTAX_ERR);
|
|
180
187
|
}
|
|
181
188
|
} else {
|
|
182
189
|
astPrefix = '*';
|
|
183
190
|
astNodeName = name;
|
|
184
191
|
}
|
|
185
192
|
} else {
|
|
186
|
-
throw new DOMException(`invalid selector ${name}`,
|
|
193
|
+
throw new DOMException(`invalid selector ${name}`, SYNTAX_ERR);
|
|
187
194
|
}
|
|
188
195
|
return {
|
|
189
196
|
astNodeName,
|
|
@@ -249,7 +256,7 @@ export class Matcher {
|
|
|
249
256
|
this.#selector = selector;
|
|
250
257
|
this.#sort = !!sort;
|
|
251
258
|
this.#warn = !!warn;
|
|
252
|
-
this._prepare(selector);
|
|
259
|
+
[this.#list, this.#matrix] = this._prepare(selector);
|
|
253
260
|
}
|
|
254
261
|
|
|
255
262
|
/**
|
|
@@ -259,7 +266,7 @@ export class Matcher {
|
|
|
259
266
|
* @returns {void}
|
|
260
267
|
*/
|
|
261
268
|
_onError(e) {
|
|
262
|
-
if (e instanceof DOMException && e.name ===
|
|
269
|
+
if (e instanceof DOMException && e.name === NOT_SUPPORTED_ERR) {
|
|
263
270
|
if (this.#warn) {
|
|
264
271
|
console.warn(e.message);
|
|
265
272
|
}
|
|
@@ -354,8 +361,8 @@ export class Matcher {
|
|
|
354
361
|
_prepare(selector = this.#selector) {
|
|
355
362
|
const ast = parseSelector(selector);
|
|
356
363
|
const branches = walkAST(ast).values();
|
|
357
|
-
|
|
358
|
-
|
|
364
|
+
const list = [];
|
|
365
|
+
const matrix = [];
|
|
359
366
|
let i = 0;
|
|
360
367
|
for (const branchItem of branches) {
|
|
361
368
|
const [...items] = branchItem;
|
|
@@ -368,7 +375,7 @@ export class Matcher {
|
|
|
368
375
|
const [nextItem] = items;
|
|
369
376
|
if (nextItem.type === COMBINATOR) {
|
|
370
377
|
const msg = `invalid combinator, ${item.name}${nextItem.name}`;
|
|
371
|
-
throw new DOMException(msg,
|
|
378
|
+
throw new DOMException(msg, SYNTAX_ERR);
|
|
372
379
|
}
|
|
373
380
|
branch.push({
|
|
374
381
|
combo: item,
|
|
@@ -391,19 +398,19 @@ export class Matcher {
|
|
|
391
398
|
}
|
|
392
399
|
}
|
|
393
400
|
const branchLen = branch.length;
|
|
394
|
-
|
|
401
|
+
matrix[i] = [];
|
|
395
402
|
for (let j = 0; j < branchLen; j++) {
|
|
396
|
-
|
|
403
|
+
matrix[i][j] = new Set();
|
|
397
404
|
}
|
|
398
|
-
|
|
405
|
+
list.push({
|
|
399
406
|
branch,
|
|
400
407
|
skip: false
|
|
401
408
|
});
|
|
402
409
|
i++;
|
|
403
410
|
}
|
|
404
411
|
return [
|
|
405
|
-
|
|
406
|
-
|
|
412
|
+
list,
|
|
413
|
+
matrix
|
|
407
414
|
];
|
|
408
415
|
}
|
|
409
416
|
|
|
@@ -655,11 +662,11 @@ export class Matcher {
|
|
|
655
662
|
(!inputType ||
|
|
656
663
|
/^(?:(?:emai|te|ur)l|search|text)$/.test(inputType))))) {
|
|
657
664
|
throw new DOMException('Unsupported pseudo-class :dir()',
|
|
658
|
-
|
|
665
|
+
NOT_SUPPORTED_ERR);
|
|
659
666
|
// FIXME:
|
|
660
667
|
} else if (nodeDir === 'auto' || (localName === 'bdi' && !nodeDir)) {
|
|
661
668
|
throw new DOMException('Unsupported pseudo-class :dir()',
|
|
662
|
-
|
|
669
|
+
NOT_SUPPORTED_ERR);
|
|
663
670
|
} else if (!nodeDir) {
|
|
664
671
|
let parent = node.parentNode;
|
|
665
672
|
while (parent) {
|
|
@@ -863,10 +870,10 @@ export class Matcher {
|
|
|
863
870
|
case 'nth-col':
|
|
864
871
|
case 'nth-last-col':
|
|
865
872
|
throw new DOMException(`Unsupported pseudo-class :${astName}()`,
|
|
866
|
-
|
|
873
|
+
NOT_SUPPORTED_ERR);
|
|
867
874
|
default:
|
|
868
875
|
throw new DOMException(`Unknown pseudo-class :${astName}()`,
|
|
869
|
-
|
|
876
|
+
SYNTAX_ERR);
|
|
870
877
|
}
|
|
871
878
|
}
|
|
872
879
|
} else {
|
|
@@ -1172,7 +1179,7 @@ export class Matcher {
|
|
|
1172
1179
|
// FIXME:
|
|
1173
1180
|
if (isMultiple) {
|
|
1174
1181
|
throw new DOMException(`Unsupported pseudo-class :${astName}`,
|
|
1175
|
-
|
|
1182
|
+
NOT_SUPPORTED_ERR);
|
|
1176
1183
|
} else {
|
|
1177
1184
|
const firstOpt = parentNode.firstElementChild;
|
|
1178
1185
|
const defaultOpt = new Set();
|
|
@@ -1382,7 +1389,7 @@ export class Matcher {
|
|
|
1382
1389
|
case 'first-letter':
|
|
1383
1390
|
case 'first-line': {
|
|
1384
1391
|
throw new DOMException(`Unsupported pseudo-element ::${astName}`,
|
|
1385
|
-
|
|
1392
|
+
NOT_SUPPORTED_ERR);
|
|
1386
1393
|
}
|
|
1387
1394
|
case 'active':
|
|
1388
1395
|
case 'autofill':
|
|
@@ -1406,11 +1413,11 @@ export class Matcher {
|
|
|
1406
1413
|
case 'volume-locked':
|
|
1407
1414
|
case '-webkit-autofill': {
|
|
1408
1415
|
throw new DOMException(`Unsupported pseudo-class :${astName}`,
|
|
1409
|
-
|
|
1416
|
+
NOT_SUPPORTED_ERR);
|
|
1410
1417
|
}
|
|
1411
1418
|
default: {
|
|
1412
1419
|
throw new DOMException(`Unknown pseudo-class :${astName}`,
|
|
1413
|
-
|
|
1420
|
+
SYNTAX_ERR);
|
|
1414
1421
|
}
|
|
1415
1422
|
}
|
|
1416
1423
|
}
|
|
@@ -1428,7 +1435,7 @@ export class Matcher {
|
|
|
1428
1435
|
flags: astFlags, matcher: astMatcher, name: astName, value: astValue
|
|
1429
1436
|
} = ast;
|
|
1430
1437
|
if (typeof astFlags === 'string' && !/^[is]$/i.test(astFlags)) {
|
|
1431
|
-
throw new DOMException('invalid attribute selector',
|
|
1438
|
+
throw new DOMException('invalid attribute selector', SYNTAX_ERR);
|
|
1432
1439
|
}
|
|
1433
1440
|
const { attributes } = node;
|
|
1434
1441
|
let res;
|
|
@@ -1650,13 +1657,15 @@ export class Matcher {
|
|
|
1650
1657
|
|
|
1651
1658
|
/**
|
|
1652
1659
|
* match pseudo-element selector
|
|
1653
|
-
* NOTE: throws DOMException
|
|
1654
1660
|
* @param {object} ast - AST
|
|
1655
1661
|
* @param {object} node - Element node
|
|
1662
|
+
* @throws {DOMException}
|
|
1656
1663
|
* @returns {void}
|
|
1657
1664
|
*/
|
|
1658
1665
|
_matchPseudoElementSelector(ast, node) {
|
|
1659
1666
|
const astName = unescapeSelector(ast.name);
|
|
1667
|
+
let msg;
|
|
1668
|
+
let type;
|
|
1660
1669
|
switch (astName) {
|
|
1661
1670
|
case 'after':
|
|
1662
1671
|
case 'backdrop':
|
|
@@ -1672,14 +1681,16 @@ export class Matcher {
|
|
|
1672
1681
|
case 'selection':
|
|
1673
1682
|
case 'slotted':
|
|
1674
1683
|
case 'target-text': {
|
|
1675
|
-
|
|
1676
|
-
|
|
1684
|
+
msg = `Unsupported pseudo-element ::${astName}`;
|
|
1685
|
+
type = NOT_SUPPORTED_ERR;
|
|
1686
|
+
break;
|
|
1677
1687
|
}
|
|
1678
1688
|
default: {
|
|
1679
|
-
|
|
1680
|
-
|
|
1689
|
+
msg = `Unknown pseudo-element ::${astName}`;
|
|
1690
|
+
type = SYNTAX_ERR;
|
|
1681
1691
|
}
|
|
1682
1692
|
}
|
|
1693
|
+
throw new DOMException(msg, type);
|
|
1683
1694
|
}
|
|
1684
1695
|
|
|
1685
1696
|
/**
|
|
@@ -1972,10 +1983,10 @@ export class Matcher {
|
|
|
1972
1983
|
/**
|
|
1973
1984
|
* find nodes
|
|
1974
1985
|
* @param {object} twig - twig
|
|
1975
|
-
* @param {string}
|
|
1986
|
+
* @param {string} targetType - target type
|
|
1976
1987
|
* @returns {object} - result
|
|
1977
1988
|
*/
|
|
1978
|
-
_findNodes(twig,
|
|
1989
|
+
_findNodes(twig, targetType) {
|
|
1979
1990
|
const { leaves: [leaf, ...items] } = twig;
|
|
1980
1991
|
const len = items.length;
|
|
1981
1992
|
const { type: leafType } = leaf;
|
|
@@ -1986,12 +1997,12 @@ export class Matcher {
|
|
|
1986
1997
|
switch (leafType) {
|
|
1987
1998
|
case ID_SELECTOR: {
|
|
1988
1999
|
let node;
|
|
1989
|
-
if (
|
|
2000
|
+
if (targetType === TYPE_SELF) {
|
|
1990
2001
|
const bool = this._matchLeaves([leaf], this.#node);
|
|
1991
2002
|
if (bool) {
|
|
1992
2003
|
node = this.#node;
|
|
1993
2004
|
}
|
|
1994
|
-
} else if (
|
|
2005
|
+
} else if (targetType === TYPE_LINEAL) {
|
|
1995
2006
|
let refNode = this.#node;
|
|
1996
2007
|
while (refNode) {
|
|
1997
2008
|
const bool = this._matchLeaves([leaf], refNode);
|
|
@@ -2020,12 +2031,12 @@ export class Matcher {
|
|
|
2020
2031
|
}
|
|
2021
2032
|
case CLASS_SELECTOR: {
|
|
2022
2033
|
const arr = [];
|
|
2023
|
-
if (
|
|
2034
|
+
if (targetType === TYPE_SELF) {
|
|
2024
2035
|
if (this.#node.nodeType === ELEMENT_NODE &&
|
|
2025
2036
|
this.#node.classList.contains(leafName)) {
|
|
2026
2037
|
arr.push(this.#node);
|
|
2027
2038
|
}
|
|
2028
|
-
} else if (
|
|
2039
|
+
} else if (targetType === TYPE_LINEAL) {
|
|
2029
2040
|
let refNode = this.#node;
|
|
2030
2041
|
while (refNode) {
|
|
2031
2042
|
if (refNode.nodeType === ELEMENT_NODE) {
|
|
@@ -2038,15 +2049,13 @@ export class Matcher {
|
|
|
2038
2049
|
}
|
|
2039
2050
|
}
|
|
2040
2051
|
} else if (root.nodeType === DOCUMENT_FRAGMENT_NODE) {
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
arr.push(nextNode);
|
|
2052
|
+
const iterator = [...root.children].values();
|
|
2053
|
+
for (const node of iterator) {
|
|
2054
|
+
if (node.classList.contains(leafName)) {
|
|
2055
|
+
arr.push(node);
|
|
2046
2056
|
}
|
|
2047
|
-
const a = [...
|
|
2057
|
+
const a = [...node.getElementsByClassName(leafName)];
|
|
2048
2058
|
arr.push(...a);
|
|
2049
|
-
nextNode = walker.nextSibling();
|
|
2050
2059
|
}
|
|
2051
2060
|
} else {
|
|
2052
2061
|
if (root.nodeType === ELEMENT_NODE &&
|
|
@@ -2072,64 +2081,62 @@ export class Matcher {
|
|
|
2072
2081
|
break;
|
|
2073
2082
|
}
|
|
2074
2083
|
case TYPE_SELECTOR: {
|
|
2075
|
-
|
|
2084
|
+
const arr = [];
|
|
2085
|
+
if (targetType === TYPE_SELF) {
|
|
2086
|
+
const bool = this.#node.nodeType === ELEMENT_NODE &&
|
|
2087
|
+
this._matchLeaves([leaf], this.#node);
|
|
2088
|
+
if (bool) {
|
|
2089
|
+
arr.push(this.#node);
|
|
2090
|
+
}
|
|
2091
|
+
} else if (targetType === TYPE_LINEAL) {
|
|
2092
|
+
let refNode = this.#node;
|
|
2093
|
+
while (refNode) {
|
|
2094
|
+
if (refNode.nodeType === ELEMENT_NODE) {
|
|
2095
|
+
const bool = this._matchLeaves([leaf], refNode);
|
|
2096
|
+
if (bool) {
|
|
2097
|
+
arr.push(refNode);
|
|
2098
|
+
}
|
|
2099
|
+
refNode = refNode.parentNode;
|
|
2100
|
+
} else {
|
|
2101
|
+
break;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
} else if (document.contentType !== 'text/html' ||
|
|
2105
|
+
/[*|]/.test(leafName)) {
|
|
2076
2106
|
pending = true;
|
|
2077
2107
|
} else {
|
|
2078
2108
|
const tagName = leafName.toLowerCase();
|
|
2079
|
-
|
|
2080
|
-
if (range === 'self') {
|
|
2081
|
-
const bool = this.#node.nodeType === ELEMENT_NODE &&
|
|
2082
|
-
this._matchLeaves([leaf], this.#node);
|
|
2083
|
-
if (bool) {
|
|
2084
|
-
arr.push(this.#node);
|
|
2085
|
-
}
|
|
2086
|
-
} else if (range === 'lineal') {
|
|
2087
|
-
let refNode = this.#node;
|
|
2088
|
-
while (refNode) {
|
|
2089
|
-
if (refNode.nodeType === ELEMENT_NODE) {
|
|
2090
|
-
const bool = this._matchLeaves([leaf], refNode);
|
|
2091
|
-
if (bool) {
|
|
2092
|
-
arr.push(refNode);
|
|
2093
|
-
}
|
|
2094
|
-
refNode = refNode.parentNode;
|
|
2095
|
-
} else {
|
|
2096
|
-
break;
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
} else if (root.nodeType === DOCUMENT_NODE) {
|
|
2109
|
+
if (root.nodeType === DOCUMENT_NODE) {
|
|
2100
2110
|
const a = xpath.select(`//*[local-name()='${tagName}']`, root);
|
|
2101
2111
|
arr.push(...a);
|
|
2102
2112
|
} else if (root.nodeType === DOCUMENT_FRAGMENT_NODE) {
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
arr.push(nextNode);
|
|
2113
|
+
const iterator = [...root.children].values();
|
|
2114
|
+
for (const node of iterator) {
|
|
2115
|
+
if (node.localName === tagName) {
|
|
2116
|
+
arr.push(node);
|
|
2108
2117
|
}
|
|
2109
|
-
const a = [...
|
|
2118
|
+
const a = [...node.getElementsByTagName(leafName)];
|
|
2110
2119
|
arr.push(...a);
|
|
2111
|
-
nextNode = walker.nextSibling();
|
|
2112
2120
|
}
|
|
2113
|
-
} else {
|
|
2114
|
-
if (root.
|
|
2115
|
-
root.localName === tagName) {
|
|
2121
|
+
} else if (root.nodeType === ELEMENT_NODE) {
|
|
2122
|
+
if (root.localName === tagName) {
|
|
2116
2123
|
arr.push(root);
|
|
2117
2124
|
}
|
|
2118
2125
|
const a = [...root.getElementsByTagName(leafName)];
|
|
2119
2126
|
arr.push(...a);
|
|
2120
2127
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2128
|
+
}
|
|
2129
|
+
if (arr.length) {
|
|
2130
|
+
if (len) {
|
|
2131
|
+
const iterator = arr.values();
|
|
2132
|
+
for (const node of iterator) {
|
|
2133
|
+
const bool = this._matchLeaves(items, node);
|
|
2134
|
+
if (bool) {
|
|
2135
|
+
nodes.add(node);
|
|
2129
2136
|
}
|
|
2130
|
-
} else {
|
|
2131
|
-
nodes = new Set(arr);
|
|
2132
2137
|
}
|
|
2138
|
+
} else {
|
|
2139
|
+
nodes = new Set(arr);
|
|
2133
2140
|
}
|
|
2134
2141
|
}
|
|
2135
2142
|
break;
|
|
@@ -2139,12 +2146,12 @@ export class Matcher {
|
|
|
2139
2146
|
}
|
|
2140
2147
|
default: {
|
|
2141
2148
|
const arr = [];
|
|
2142
|
-
if (
|
|
2149
|
+
if (targetType === TYPE_SELF) {
|
|
2143
2150
|
const bool = this._matchLeaves([leaf], this.#node);
|
|
2144
2151
|
if (bool) {
|
|
2145
2152
|
arr.push(this.#node);
|
|
2146
2153
|
}
|
|
2147
|
-
} else if (
|
|
2154
|
+
} else if (targetType === TYPE_LINEAL) {
|
|
2148
2155
|
let refNode = this.#node;
|
|
2149
2156
|
while (refNode) {
|
|
2150
2157
|
const bool = this._matchLeaves([leaf], refNode); ;
|
|
@@ -2179,10 +2186,10 @@ export class Matcher {
|
|
|
2179
2186
|
|
|
2180
2187
|
/**
|
|
2181
2188
|
* collect nodes
|
|
2182
|
-
* @param {string}
|
|
2189
|
+
* @param {string} targetType - target type
|
|
2183
2190
|
* @returns {Array} - matrix
|
|
2184
2191
|
*/
|
|
2185
|
-
_collectNodes(
|
|
2192
|
+
_collectNodes(targetType) {
|
|
2186
2193
|
const pendingItems = new Set();
|
|
2187
2194
|
const listIterator = this.#list.values();
|
|
2188
2195
|
let i = 0;
|
|
@@ -2190,11 +2197,11 @@ export class Matcher {
|
|
|
2190
2197
|
const { branch } = list;
|
|
2191
2198
|
const branchLen = branch.length;
|
|
2192
2199
|
const lastIndex = branchLen - 1;
|
|
2193
|
-
if (
|
|
2200
|
+
if (targetType === TYPE_ALL) {
|
|
2194
2201
|
for (let j = 0; j < branchLen; j++) {
|
|
2195
2202
|
const twig = branch[j];
|
|
2196
2203
|
const { nodes, pending } =
|
|
2197
|
-
this._findNodes(twig, j === lastIndex ?
|
|
2204
|
+
this._findNodes(twig, j === lastIndex ? targetType : null);
|
|
2198
2205
|
if (nodes.size) {
|
|
2199
2206
|
this.#matrix[i][j] = nodes;
|
|
2200
2207
|
} else if (pending) {
|
|
@@ -2209,7 +2216,7 @@ export class Matcher {
|
|
|
2209
2216
|
}
|
|
2210
2217
|
} else {
|
|
2211
2218
|
const twig = branch[lastIndex];
|
|
2212
|
-
const { nodes, pending } = this._findNodes(twig,
|
|
2219
|
+
const { nodes, pending } = this._findNodes(twig, targetType);
|
|
2213
2220
|
if (nodes.size) {
|
|
2214
2221
|
this.#matrix[i][lastIndex] = nodes;
|
|
2215
2222
|
} else if (pending) {
|
|
@@ -2230,18 +2237,7 @@ export class Matcher {
|
|
|
2230
2237
|
let nextNode = iterator.nextNode();
|
|
2231
2238
|
while (nextNode) {
|
|
2232
2239
|
let bool;
|
|
2233
|
-
if (
|
|
2234
|
-
bool = nextNode === this.#node;
|
|
2235
|
-
} else if (range === 'lineal') {
|
|
2236
|
-
let refNode = this.#node;
|
|
2237
|
-
while (refNode) {
|
|
2238
|
-
bool = nextNode === refNode;
|
|
2239
|
-
if (bool) {
|
|
2240
|
-
break;
|
|
2241
|
-
}
|
|
2242
|
-
refNode = refNode.parentNode;
|
|
2243
|
-
}
|
|
2244
|
-
} else if (/^(?:all|first)$/.test(range)) {
|
|
2240
|
+
if (targetType === TYPE_ALL || targetType === TYPE_FIRST) {
|
|
2245
2241
|
if (this.#node.nodeType === ELEMENT_NODE) {
|
|
2246
2242
|
bool = isDescendant(nextNode, this.#node);
|
|
2247
2243
|
} else {
|
|
@@ -2270,10 +2266,10 @@ export class Matcher {
|
|
|
2270
2266
|
|
|
2271
2267
|
/**
|
|
2272
2268
|
* match nodes
|
|
2273
|
-
* @param {string}
|
|
2269
|
+
* @param {string} targetType - target type
|
|
2274
2270
|
* @returns {object} - collection of matched nodes
|
|
2275
2271
|
*/
|
|
2276
|
-
_matchNodes(
|
|
2272
|
+
_matchNodes(targetType) {
|
|
2277
2273
|
const [...branches] = this.#list;
|
|
2278
2274
|
const l = branches.length;
|
|
2279
2275
|
let nodes = new Set();
|
|
@@ -2286,7 +2282,7 @@ export class Matcher {
|
|
|
2286
2282
|
const lastIndex = branchLen - 1;
|
|
2287
2283
|
if (lastIndex === 0) {
|
|
2288
2284
|
const matched = this.#matrix[i][0];
|
|
2289
|
-
if (
|
|
2285
|
+
if ((targetType === TYPE_ALL || targetType === TYPE_FIRST) &&
|
|
2290
2286
|
this.#node.nodeType === ELEMENT_NODE) {
|
|
2291
2287
|
for (const node of matched) {
|
|
2292
2288
|
if (isDescendant(node, this.#node)) {
|
|
@@ -2296,7 +2292,7 @@ export class Matcher {
|
|
|
2296
2292
|
} else {
|
|
2297
2293
|
nodes = matched;
|
|
2298
2294
|
}
|
|
2299
|
-
} else if (
|
|
2295
|
+
} else if (targetType === TYPE_ALL) {
|
|
2300
2296
|
let { combo } = branch[0];
|
|
2301
2297
|
let prevNodes = this.#matrix[i][0];
|
|
2302
2298
|
for (let j = 1; j < branchLen; j++) {
|
|
@@ -2339,7 +2335,7 @@ export class Matcher {
|
|
|
2339
2335
|
}
|
|
2340
2336
|
if (matched.size) {
|
|
2341
2337
|
if (j === 0) {
|
|
2342
|
-
if (
|
|
2338
|
+
if (targetType === TYPE_FIRST &&
|
|
2343
2339
|
this.#node.nodeType === ELEMENT_NODE) {
|
|
2344
2340
|
if (isDescendant(node, this.#node)) {
|
|
2345
2341
|
nodes.add(node);
|
|
@@ -2368,12 +2364,12 @@ export class Matcher {
|
|
|
2368
2364
|
|
|
2369
2365
|
/**
|
|
2370
2366
|
* find matched nodes
|
|
2371
|
-
* @param {string}
|
|
2367
|
+
* @param {string} targetType - target type
|
|
2372
2368
|
* @returns {object} - collection of matched nodes
|
|
2373
2369
|
*/
|
|
2374
|
-
_find(
|
|
2375
|
-
this._collectNodes(
|
|
2376
|
-
const nodes = this._matchNodes(
|
|
2370
|
+
_find(targetType) {
|
|
2371
|
+
this._collectNodes(targetType);
|
|
2372
|
+
const nodes = this._matchNodes(targetType);
|
|
2377
2373
|
return nodes;
|
|
2378
2374
|
}
|
|
2379
2375
|
|
|
@@ -2407,7 +2403,7 @@ export class Matcher {
|
|
|
2407
2403
|
matches() {
|
|
2408
2404
|
let res;
|
|
2409
2405
|
try {
|
|
2410
|
-
const nodes = this._find(
|
|
2406
|
+
const nodes = this._find(TYPE_SELF);
|
|
2411
2407
|
res = nodes.has(this.#node);
|
|
2412
2408
|
} catch (e) {
|
|
2413
2409
|
this._onError(e);
|
|
@@ -2422,7 +2418,7 @@ export class Matcher {
|
|
|
2422
2418
|
closest() {
|
|
2423
2419
|
let res;
|
|
2424
2420
|
try {
|
|
2425
|
-
const nodes = this._find(
|
|
2421
|
+
const nodes = this._find(TYPE_LINEAL);
|
|
2426
2422
|
let node = this.#node;
|
|
2427
2423
|
while (node) {
|
|
2428
2424
|
if (nodes.has(node)) {
|
|
@@ -2444,7 +2440,7 @@ export class Matcher {
|
|
|
2444
2440
|
querySelector() {
|
|
2445
2441
|
let res;
|
|
2446
2442
|
try {
|
|
2447
|
-
const nodes = this._find(
|
|
2443
|
+
const nodes = this._find(TYPE_FIRST);
|
|
2448
2444
|
nodes.delete(this.#node);
|
|
2449
2445
|
if (nodes.size) {
|
|
2450
2446
|
[res] = this._sortNodes(nodes);
|
|
@@ -2463,7 +2459,7 @@ export class Matcher {
|
|
|
2463
2459
|
querySelectorAll() {
|
|
2464
2460
|
const res = [];
|
|
2465
2461
|
try {
|
|
2466
|
-
const nodes = this._find(
|
|
2462
|
+
const nodes = this._find(TYPE_ALL);
|
|
2467
2463
|
nodes.delete(this.#node);
|
|
2468
2464
|
if (nodes.size > 1 && this.#sort) {
|
|
2469
2465
|
res.push(...this._sortNodes(nodes));
|
package/src/js/parser.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { findAll, parse, toPlainObject, walk } from 'css-tree';
|
|
7
7
|
|
|
8
8
|
/* constants */
|
|
9
|
-
import { PSEUDO_CLASS_SELECTOR, SELECTOR } from './constant.js';
|
|
9
|
+
import { PSEUDO_CLASS_SELECTOR, SELECTOR, SYNTAX_ERR } from './constant.js';
|
|
10
10
|
const CODE_POINT_UNIT = parseInt('10000', 16);
|
|
11
11
|
const HEX = 16;
|
|
12
12
|
const PAIR = 2;
|
|
@@ -54,7 +54,7 @@ export const preprocess = (...args) => {
|
|
|
54
54
|
selector = Object.prototype.toString.call(selector)
|
|
55
55
|
.slice(TYPE_FROM, TYPE_TO).toLowerCase();
|
|
56
56
|
} else {
|
|
57
|
-
throw new DOMException(`invalid selector ${selector}`,
|
|
57
|
+
throw new DOMException(`invalid selector ${selector}`, SYNTAX_ERR);
|
|
58
58
|
}
|
|
59
59
|
return selector;
|
|
60
60
|
};
|
|
@@ -68,7 +68,7 @@ export const parseSelector = selector => {
|
|
|
68
68
|
selector = preprocess(selector);
|
|
69
69
|
// invalid selectors
|
|
70
70
|
if (selector === '' || /^\s*>/.test(selector) || /,\s*$/.test(selector)) {
|
|
71
|
-
throw new DOMException(`invalid selector ${selector}`,
|
|
71
|
+
throw new DOMException(`invalid selector ${selector}`, SYNTAX_ERR);
|
|
72
72
|
}
|
|
73
73
|
let res;
|
|
74
74
|
try {
|
|
@@ -83,7 +83,7 @@ export const parseSelector = selector => {
|
|
|
83
83
|
} else if (e.message === '")" is expected' && !selector.endsWith(')')) {
|
|
84
84
|
res = parseSelector(`${selector})`);
|
|
85
85
|
} else {
|
|
86
|
-
throw new DOMException(e.message,
|
|
86
|
+
throw new DOMException(e.message, SYNTAX_ERR);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
return res;
|