@asamuzakjp/dom-selector 5.2.2 → 5.3.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/dist/cjs/index.js +2 -2
- package/dist/cjs/index.js.map +3 -3
- package/dist/cjs/js/constant.js +1 -1
- package/dist/cjs/js/constant.js.map +3 -3
- package/dist/cjs/js/finder.js +1 -1
- package/dist/cjs/js/finder.js.map +3 -3
- package/dist/cjs/js/matcher.js +1 -1
- package/dist/cjs/js/matcher.js.map +2 -2
- package/dist/cjs/js/parser.js +2 -2
- package/dist/cjs/js/parser.js.map +3 -3
- package/dist/cjs/js/utility.js +1 -1
- package/dist/cjs/js/utility.js.map +3 -3
- package/package.json +1 -1
- package/src/index.js +3 -1
- package/src/js/constant.js +11 -45
- package/src/js/finder.js +59 -46
- package/src/js/matcher.js +7 -6
- package/src/js/parser.js +24 -22
- package/src/js/utility.js +17 -17
- package/types/js/constant.d.ts +11 -41
package/src/js/constant.js
CHANGED
|
@@ -3,28 +3,23 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/* string */
|
|
6
|
-
export const
|
|
6
|
+
export const ATTR_SELECTOR = 'AttributeSelector';
|
|
7
|
+
export const CLASS_SELECTOR = 'ClassSelector';
|
|
7
8
|
export const COMBINATOR = 'Combinator';
|
|
8
9
|
export const EMPTY = '__EMPTY__';
|
|
9
|
-
export const
|
|
10
|
+
export const IDENT = 'Identifier';
|
|
11
|
+
export const ID_SELECTOR = 'IdSelector';
|
|
10
12
|
export const NOT_SUPPORTED_ERR = 'NotSupportedError';
|
|
11
13
|
export const NTH = 'Nth';
|
|
12
|
-
export const
|
|
14
|
+
export const PS_CLASS_SELECTOR = 'PseudoClassSelector';
|
|
15
|
+
export const PS_ELEMENT_SELECTOR = 'PseudoElementSelector';
|
|
13
16
|
export const SELECTOR = 'Selector';
|
|
14
|
-
export const SELECTOR_ATTR = 'AttributeSelector';
|
|
15
|
-
export const SELECTOR_CLASS = 'ClassSelector';
|
|
16
|
-
export const SELECTOR_ID = 'IdSelector';
|
|
17
|
-
export const SELECTOR_LIST = 'SelectorList';
|
|
18
|
-
export const SELECTOR_PSEUDO_CLASS = 'PseudoClassSelector';
|
|
19
|
-
export const SELECTOR_PSEUDO_ELEMENT = 'PseudoElementSelector';
|
|
20
|
-
export const SELECTOR_TYPE = 'TypeSelector';
|
|
21
|
-
export const STRING = 'String';
|
|
22
17
|
export const SYNTAX_ERR = 'SyntaxError';
|
|
23
18
|
export const TARGET_ALL = 'all';
|
|
24
19
|
export const TARGET_FIRST = 'first';
|
|
25
20
|
export const TARGET_LINEAL = 'lineal';
|
|
26
21
|
export const TARGET_SELF = 'self';
|
|
27
|
-
export const
|
|
22
|
+
export const TYPE_SELECTOR = 'TypeSelector';
|
|
28
23
|
|
|
29
24
|
/* numeric */
|
|
30
25
|
export const BIT_01 = 1;
|
|
@@ -34,9 +29,9 @@ export const BIT_08 = 8;
|
|
|
34
29
|
export const BIT_16 = 0x10;
|
|
35
30
|
export const BIT_32 = 0x20;
|
|
36
31
|
export const BIT_FFFF = 0xFFFF;
|
|
37
|
-
export const BIT_HYPHEN = 0x2D;
|
|
38
32
|
export const DUO = 2;
|
|
39
33
|
export const HEX = 16;
|
|
34
|
+
export const HYPHEN = 0x2D;
|
|
40
35
|
export const TYPE_FROM = 8;
|
|
41
36
|
export const TYPE_TO = -1;
|
|
42
37
|
|
|
@@ -61,7 +56,7 @@ export const ALPHA_NUM = '[A-Z\\d]+';
|
|
|
61
56
|
export const CHILD_IDX = '(?:first|last|only)-(?:child|of-type)';
|
|
62
57
|
export const DIGIT = '(?:0|[1-9]\\d*)';
|
|
63
58
|
export const LANG_PART = `(?:-${ALPHA_NUM})*`;
|
|
64
|
-
export const
|
|
59
|
+
export const PSEUDO_CLASS =
|
|
65
60
|
`(?:any-)?link|${CHILD_IDX}|checked|empty|indeterminate|root|target|visited`;
|
|
66
61
|
export const ANB =
|
|
67
62
|
`[+-]?(?:${DIGIT}n?|n)|(?:[+-]?${DIGIT})?n\\s*[+-]\\s*${DIGIT}`;
|
|
@@ -95,35 +90,6 @@ export const LOGICAL_COMPOUND =
|
|
|
95
90
|
`${KEY_IS_NOT}\\(\\s*${COMPOUND_A}(?:\\s*,\\s*${COMPOUND_A})*\\s*\\)`;
|
|
96
91
|
|
|
97
92
|
/* regexp */
|
|
98
|
-
export const
|
|
99
|
-
export const
|
|
100
|
-
export const REG_DESCEND = new RegExp(`${DESCEND}${COMPOUND_I}`, 'i');
|
|
101
|
-
export const REG_DIR = /^(?:ltr|rtl)$/;
|
|
102
|
-
export const REG_FILTER_COMPLEX =
|
|
103
|
-
new RegExp(`:(?!${PSEUDO_CLASSES}|${N_TH}|${LOGICAL_COMPLEX})`);
|
|
104
|
-
export const REG_FILTER_COMPOUND =
|
|
105
|
-
new RegExp(`:(?!${PSEUDO_CLASSES}|${N_TH}|${LOGICAL_COMPOUND})`);
|
|
106
|
-
export const REG_FILTER_SIMPLE = new RegExp(`:(?!${PSEUDO_CLASSES}|${N_TH})`);
|
|
107
|
-
export const REG_FORM = /^(?:button|fieldset|form|input|select|textarea)$/;
|
|
108
|
-
export const REG_FORM_CTRL =
|
|
109
|
-
/^(?:button|fieldset|input|optgroup|option|select|textarea)$/;
|
|
110
|
-
export const REG_FORM_VALID = /^(?:button|form|input|select|textarea)$/;
|
|
111
|
-
export const REG_HEX = /^([\da-f]{1,6}\s?)/i;
|
|
112
|
-
export const REG_INTERACT = /^(?:details|dialog)$/;
|
|
113
|
-
export const REG_INVALID_SELECTOR = /^$|^\s*>|,\s*$/;
|
|
114
|
-
export const REG_LANG = new RegExp(`^(?:\\*-)?${ALPHA_NUM}${LANG_PART}$`, 'i');
|
|
115
|
-
export const REG_LANG_QUOTED = /(:lang\(\s*("[A-Za-z\d\-*]*")\s*\))/;
|
|
116
|
-
export const REG_LOGICAL_EMPTY = /(:(is|where)\(\s*\))/;
|
|
117
|
-
export const REG_LOGICAL_PSEUDO = /^(?:has|is|not|where)$/;
|
|
118
|
-
export const REG_SHADOW_HOST = /^host(?:-context)?$/;
|
|
119
|
-
export const REG_SHADOW_MODE = /^(?:close|open)$/;
|
|
120
|
-
export const REG_SHADOW_PSEUDO = /^part|slotted$/;
|
|
121
|
-
export const REG_TAG_NAME = /[A-Z][\\w-]*/i;
|
|
122
|
-
export const REG_TYPE_CHECK = /^(?:checkbox|radio)$/;
|
|
123
|
-
export const REG_TYPE_INPUT =
|
|
93
|
+
export const REG_LOGICAL = /^(?:has|is|not|where)$/;
|
|
94
|
+
export const REG_INPUT_TYPE =
|
|
124
95
|
/^(?:date(?:time-local)?|email|month|number|password|search|tel|text|time|url|week)$/;
|
|
125
|
-
export const REG_TYPE_RANGE =
|
|
126
|
-
/^(?:date(?:time-local)?|month|number|range|time|week)$/;
|
|
127
|
-
export const REG_TYPE_RESET = /^(?:button|reset)$/;
|
|
128
|
-
export const REG_TYPE_SUBMIT = /^(?:image|submit)$/;
|
|
129
|
-
export const REG_TYPE_TEXT = /^(?:email|number|password|search|tel|text|url)$/;
|
package/src/js/finder.js
CHANGED
|
@@ -14,18 +14,26 @@ import {
|
|
|
14
14
|
|
|
15
15
|
/* constants */
|
|
16
16
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
SELECTOR_PSEUDO_CLASS, SELECTOR_PSEUDO_ELEMENT, SELECTOR_TYPE, SHOW_ALL,
|
|
23
|
-
SYNTAX_ERR, TARGET_ALL, TARGET_FIRST, TARGET_LINEAL, TARGET_SELF, TEXT_NODE,
|
|
24
|
-
WALKER_FILTER
|
|
17
|
+
ATTR_SELECTOR, BIT_01, CLASS_SELECTOR, COMBINATOR, DOCUMENT_FRAGMENT_NODE,
|
|
18
|
+
DOCUMENT_NODE, ELEMENT_NODE, EMPTY, ID_SELECTOR, NOT_SUPPORTED_ERR,
|
|
19
|
+
PS_CLASS_SELECTOR, PS_ELEMENT_SELECTOR, REG_INPUT_TYPE, REG_LOGICAL,
|
|
20
|
+
SHOW_ALL, SYNTAX_ERR, TARGET_ALL, TARGET_FIRST, TARGET_LINEAL, TARGET_SELF,
|
|
21
|
+
TEXT_NODE, TYPE_SELECTOR, WALKER_FILTER
|
|
25
22
|
} from './constant.js';
|
|
26
23
|
const DIR_NEXT = 'next';
|
|
27
24
|
const DIR_PREV = 'prev';
|
|
28
25
|
const KEY_TAB = 'Tab';
|
|
26
|
+
const REG_ANCHOR = /^a(?:rea)?$/;
|
|
27
|
+
const REG_FORM_CTRL =
|
|
28
|
+
/^(?:button|fieldset|input|optgroup|option|select|textarea)$/;
|
|
29
|
+
const REG_FORM_VALID = /^(?:button|form|input|select|textarea)$/;
|
|
30
|
+
const REG_INTERACT = /^(?:details|dialog)$/;
|
|
31
|
+
const REG_SHADOW_HOST = /^host(?:-context)?$/;
|
|
32
|
+
const REG_TYPE_CHECK = /^(?:checkbox|radio)$/;
|
|
33
|
+
const REG_TYPE_RANGE = /^(?:date(?:time-local)?|month|number|range|time|week)$/;
|
|
34
|
+
const REG_TYPE_RESET = /^(?:button|reset)$/;
|
|
35
|
+
const REG_TYPE_SUBMIT = /^(?:image|submit)$/;
|
|
36
|
+
const REG_TYPE_TEXT = /^(?:email|number|password|search|tel|text|url)$/;
|
|
29
37
|
|
|
30
38
|
/**
|
|
31
39
|
* Finder
|
|
@@ -110,6 +118,8 @@ export class Finder {
|
|
|
110
118
|
} else {
|
|
111
119
|
throw new this.#window.DOMException(e.message, e.name);
|
|
112
120
|
}
|
|
121
|
+
} else if (e.name in this.#window) {
|
|
122
|
+
throw new this.#window[e.name](e.message);
|
|
113
123
|
} else {
|
|
114
124
|
throw e;
|
|
115
125
|
}
|
|
@@ -222,8 +232,10 @@ export class Finder {
|
|
|
222
232
|
} catch (e) {
|
|
223
233
|
this.onError(e);
|
|
224
234
|
}
|
|
225
|
-
const {
|
|
226
|
-
|
|
235
|
+
const {
|
|
236
|
+
branches, info: { hasHasPseudoFunc, hasNthChildOfSelector }
|
|
237
|
+
} = walkAST(cssAst);
|
|
238
|
+
let invalidate = !!(hasHasPseudoFunc || hasNthChildOfSelector);
|
|
227
239
|
let descendant = false;
|
|
228
240
|
let i = 0;
|
|
229
241
|
ast = [];
|
|
@@ -354,12 +366,9 @@ export class Finder {
|
|
|
354
366
|
if (this.#astCache.has(selector)) {
|
|
355
367
|
selectorBranches = this.#astCache.get(selector);
|
|
356
368
|
} else {
|
|
357
|
-
const { branches
|
|
369
|
+
const { branches } = walkAST(selector);
|
|
358
370
|
selectorBranches = branches;
|
|
359
371
|
this.#astCache.set(selector, selectorBranches);
|
|
360
|
-
if (info.hasLogicalPseudoFunc) {
|
|
361
|
-
this.#invalidate = true;
|
|
362
|
-
}
|
|
363
372
|
}
|
|
364
373
|
}
|
|
365
374
|
if (parentNode) {
|
|
@@ -828,7 +837,7 @@ export class Finder {
|
|
|
828
837
|
} = opt;
|
|
829
838
|
const matched = new Set();
|
|
830
839
|
// :has(), :is(), :not(), :where()
|
|
831
|
-
if (
|
|
840
|
+
if (REG_LOGICAL.test(astName)) {
|
|
832
841
|
let astData;
|
|
833
842
|
if (this.#astCache.has(ast)) {
|
|
834
843
|
astData = this.#astCache.get(ast);
|
|
@@ -1194,7 +1203,7 @@ export class Finder {
|
|
|
1194
1203
|
break;
|
|
1195
1204
|
}
|
|
1196
1205
|
case 'input': {
|
|
1197
|
-
if ((!node.type ||
|
|
1206
|
+
if ((!node.type || REG_INPUT_TYPE.test(node.type)) &&
|
|
1198
1207
|
(node.readonly || node.hasAttribute('readonly') ||
|
|
1199
1208
|
node.disabled || node.hasAttribute('disabled'))) {
|
|
1200
1209
|
matched.add(node);
|
|
@@ -1219,7 +1228,7 @@ export class Finder {
|
|
|
1219
1228
|
break;
|
|
1220
1229
|
}
|
|
1221
1230
|
case 'input': {
|
|
1222
|
-
if ((!node.type ||
|
|
1231
|
+
if ((!node.type || REG_INPUT_TYPE.test(node.type)) &&
|
|
1223
1232
|
!(node.readonly || node.hasAttribute('readonly') ||
|
|
1224
1233
|
node.disabled || node.hasAttribute('disabled'))) {
|
|
1225
1234
|
matched.add(node);
|
|
@@ -1476,7 +1485,7 @@ export class Finder {
|
|
|
1476
1485
|
if (node.hasAttribute('type')) {
|
|
1477
1486
|
const inputType = node.getAttribute('type');
|
|
1478
1487
|
if (inputType === 'file' || REG_TYPE_CHECK.test(inputType) ||
|
|
1479
|
-
|
|
1488
|
+
REG_INPUT_TYPE.test(inputType)) {
|
|
1480
1489
|
targetNode = node;
|
|
1481
1490
|
}
|
|
1482
1491
|
} else {
|
|
@@ -1497,7 +1506,7 @@ export class Finder {
|
|
|
1497
1506
|
if (node.hasAttribute('type')) {
|
|
1498
1507
|
const inputType = node.getAttribute('type');
|
|
1499
1508
|
if (inputType === 'file' || REG_TYPE_CHECK.test(inputType) ||
|
|
1500
|
-
|
|
1509
|
+
REG_INPUT_TYPE.test(inputType)) {
|
|
1501
1510
|
targetNode = node;
|
|
1502
1511
|
}
|
|
1503
1512
|
} else {
|
|
@@ -1767,23 +1776,23 @@ export class Finder {
|
|
|
1767
1776
|
}
|
|
1768
1777
|
if (node.nodeType === ELEMENT_NODE) {
|
|
1769
1778
|
switch (astType) {
|
|
1770
|
-
case
|
|
1779
|
+
case PS_ELEMENT_SELECTOR: {
|
|
1771
1780
|
this.#matcher.matchPseudoElementSelector(astName, opt);
|
|
1772
1781
|
break;
|
|
1773
1782
|
}
|
|
1774
|
-
case
|
|
1783
|
+
case ID_SELECTOR: {
|
|
1775
1784
|
if (node.id === astName) {
|
|
1776
1785
|
matched.add(node);
|
|
1777
1786
|
}
|
|
1778
1787
|
break;
|
|
1779
1788
|
}
|
|
1780
|
-
case
|
|
1789
|
+
case CLASS_SELECTOR: {
|
|
1781
1790
|
if (node.classList.contains(astName)) {
|
|
1782
1791
|
matched.add(node);
|
|
1783
1792
|
}
|
|
1784
1793
|
break;
|
|
1785
1794
|
}
|
|
1786
|
-
case
|
|
1795
|
+
case PS_CLASS_SELECTOR: {
|
|
1787
1796
|
const nodes = this._matchPseudoClassSelector(ast, node, opt);
|
|
1788
1797
|
return nodes;
|
|
1789
1798
|
}
|
|
@@ -1794,9 +1803,9 @@ export class Finder {
|
|
|
1794
1803
|
}
|
|
1795
1804
|
}
|
|
1796
1805
|
}
|
|
1797
|
-
} else if (this.#shadow && astType ===
|
|
1806
|
+
} else if (this.#shadow && astType === PS_CLASS_SELECTOR &&
|
|
1798
1807
|
node.nodeType === DOCUMENT_FRAGMENT_NODE) {
|
|
1799
|
-
if (astName !== 'has' &&
|
|
1808
|
+
if (astName !== 'has' && REG_LOGICAL.test(astName)) {
|
|
1800
1809
|
const nodes = this._matchPseudoClassSelector(ast, node, opt);
|
|
1801
1810
|
return nodes;
|
|
1802
1811
|
} else if (REG_SHADOW_HOST.test(astName)) {
|
|
@@ -1831,17 +1840,18 @@ export class Finder {
|
|
|
1831
1840
|
}
|
|
1832
1841
|
if (typeof bool !== 'boolean') {
|
|
1833
1842
|
let cacheable = true;
|
|
1834
|
-
if (node.nodeType === ELEMENT_NODE &&
|
|
1843
|
+
if (node.nodeType === ELEMENT_NODE &&
|
|
1844
|
+
/^(?:button|fieldset|form|input|select|textarea)$/.test(node.localName)) {
|
|
1835
1845
|
cacheable = false;
|
|
1836
1846
|
}
|
|
1837
1847
|
for (const leaf of leaves) {
|
|
1838
1848
|
switch (leaf.type) {
|
|
1839
|
-
case
|
|
1840
|
-
case
|
|
1849
|
+
case ATTR_SELECTOR:
|
|
1850
|
+
case ID_SELECTOR: {
|
|
1841
1851
|
cacheable = false;
|
|
1842
1852
|
break;
|
|
1843
1853
|
}
|
|
1844
|
-
case
|
|
1854
|
+
case PS_CLASS_SELECTOR: {
|
|
1845
1855
|
if (/^(?:(?:any-)?link|defined|dir)$/.test(leaf.name)) {
|
|
1846
1856
|
cacheable = false;
|
|
1847
1857
|
}
|
|
@@ -1921,11 +1931,11 @@ export class Finder {
|
|
|
1921
1931
|
pending = true;
|
|
1922
1932
|
} else {
|
|
1923
1933
|
switch (leafType) {
|
|
1924
|
-
case
|
|
1934
|
+
case PS_ELEMENT_SELECTOR: {
|
|
1925
1935
|
this.#matcher.matchPseudoElementSelector(leafName, opt);
|
|
1926
1936
|
break;
|
|
1927
1937
|
}
|
|
1928
|
-
case
|
|
1938
|
+
case ID_SELECTOR: {
|
|
1929
1939
|
if (this.#root.nodeType === ELEMENT_NODE) {
|
|
1930
1940
|
pending = true;
|
|
1931
1941
|
} else {
|
|
@@ -1943,7 +1953,7 @@ export class Finder {
|
|
|
1943
1953
|
}
|
|
1944
1954
|
break;
|
|
1945
1955
|
}
|
|
1946
|
-
case
|
|
1956
|
+
case CLASS_SELECTOR: {
|
|
1947
1957
|
const items = baseNode.getElementsByClassName(leafName);
|
|
1948
1958
|
nodes = this._matchHTMLCollection(items, {
|
|
1949
1959
|
compound,
|
|
@@ -1951,7 +1961,7 @@ export class Finder {
|
|
|
1951
1961
|
});
|
|
1952
1962
|
break;
|
|
1953
1963
|
}
|
|
1954
|
-
case
|
|
1964
|
+
case TYPE_SELECTOR: {
|
|
1955
1965
|
if (this.#document.contentType === 'text/html' &&
|
|
1956
1966
|
!/[*|]/.test(leafName)) {
|
|
1957
1967
|
const items = baseNode.getElementsByTagName(leafName);
|
|
@@ -2322,13 +2332,13 @@ export class Finder {
|
|
|
2322
2332
|
let filtered = false;
|
|
2323
2333
|
let pending = false;
|
|
2324
2334
|
switch (leafType) {
|
|
2325
|
-
case
|
|
2335
|
+
case PS_ELEMENT_SELECTOR: {
|
|
2326
2336
|
this.#matcher.matchPseudoElementSelector(leafName, {
|
|
2327
2337
|
warn: this.#warn
|
|
2328
2338
|
});
|
|
2329
2339
|
break;
|
|
2330
2340
|
}
|
|
2331
|
-
case
|
|
2341
|
+
case ID_SELECTOR: {
|
|
2332
2342
|
if (targetType === TARGET_SELF) {
|
|
2333
2343
|
[nodes, filtered] = this._matchSelf(leaves);
|
|
2334
2344
|
} else if (targetType === TARGET_LINEAL) {
|
|
@@ -2359,7 +2369,7 @@ export class Finder {
|
|
|
2359
2369
|
}
|
|
2360
2370
|
break;
|
|
2361
2371
|
}
|
|
2362
|
-
case
|
|
2372
|
+
case CLASS_SELECTOR: {
|
|
2363
2373
|
if (targetType === TARGET_SELF) {
|
|
2364
2374
|
[nodes, filtered] = this._matchSelf(leaves);
|
|
2365
2375
|
} else if (targetType === TARGET_LINEAL) {
|
|
@@ -2383,7 +2393,7 @@ export class Finder {
|
|
|
2383
2393
|
}
|
|
2384
2394
|
break;
|
|
2385
2395
|
}
|
|
2386
|
-
case
|
|
2396
|
+
case TYPE_SELECTOR: {
|
|
2387
2397
|
if (targetType === TARGET_SELF) {
|
|
2388
2398
|
[nodes, filtered] = this._matchSelf(leaves);
|
|
2389
2399
|
} else if (targetType === TARGET_LINEAL) {
|
|
@@ -2473,19 +2483,19 @@ export class Finder {
|
|
|
2473
2483
|
type: lastType
|
|
2474
2484
|
}]
|
|
2475
2485
|
} = lastTwig;
|
|
2476
|
-
if (lastType ===
|
|
2477
|
-
lastType ===
|
|
2486
|
+
if (lastType === PS_ELEMENT_SELECTOR ||
|
|
2487
|
+
lastType === ID_SELECTOR) {
|
|
2478
2488
|
dir = DIR_PREV;
|
|
2479
2489
|
twig = lastTwig;
|
|
2480
|
-
} else if (firstType ===
|
|
2481
|
-
firstType ===
|
|
2490
|
+
} else if (firstType === PS_ELEMENT_SELECTOR ||
|
|
2491
|
+
firstType === ID_SELECTOR) {
|
|
2482
2492
|
dir = DIR_NEXT;
|
|
2483
2493
|
twig = firstTwig;
|
|
2484
2494
|
} else if (targetType === TARGET_ALL) {
|
|
2485
|
-
if (firstName === '*' && firstType ===
|
|
2495
|
+
if (firstName === '*' && firstType === TYPE_SELECTOR) {
|
|
2486
2496
|
dir = DIR_PREV;
|
|
2487
2497
|
twig = lastTwig;
|
|
2488
|
-
} else if (lastName === '*' && lastType ===
|
|
2498
|
+
} else if (lastName === '*' && lastType === TYPE_SELECTOR) {
|
|
2489
2499
|
dir = DIR_NEXT;
|
|
2490
2500
|
twig = firstTwig;
|
|
2491
2501
|
} else if (branchLen === 2) {
|
|
@@ -2501,17 +2511,17 @@ export class Finder {
|
|
|
2501
2511
|
dir = DIR_NEXT;
|
|
2502
2512
|
twig = firstTwig;
|
|
2503
2513
|
}
|
|
2504
|
-
} else if (lastName === '*' && lastType ===
|
|
2514
|
+
} else if (lastName === '*' && lastType === TYPE_SELECTOR) {
|
|
2505
2515
|
dir = DIR_NEXT;
|
|
2506
2516
|
twig = firstTwig;
|
|
2507
|
-
} else if (firstName === '*' && firstType ===
|
|
2517
|
+
} else if (firstName === '*' && firstType === TYPE_SELECTOR) {
|
|
2508
2518
|
dir = DIR_PREV;
|
|
2509
2519
|
twig = lastTwig;
|
|
2510
2520
|
} else {
|
|
2511
2521
|
let bool;
|
|
2512
2522
|
for (const { combo, leaves: [leaf] } of branch) {
|
|
2513
2523
|
const { name: leafName, type: leafType } = leaf;
|
|
2514
|
-
if (leafType ===
|
|
2524
|
+
if (leafType === PS_CLASS_SELECTOR && leafName === 'dir') {
|
|
2515
2525
|
bool = false;
|
|
2516
2526
|
break;
|
|
2517
2527
|
}
|
|
@@ -2874,6 +2884,9 @@ export class Finder {
|
|
|
2874
2884
|
nodes = new Set(sortNodes(nodes));
|
|
2875
2885
|
}
|
|
2876
2886
|
}
|
|
2887
|
+
if (this.#invalidate) {
|
|
2888
|
+
this.#invalidateResults = new WeakMap();
|
|
2889
|
+
}
|
|
2877
2890
|
return nodes;
|
|
2878
2891
|
}
|
|
2879
2892
|
};
|
package/src/js/matcher.js
CHANGED
|
@@ -8,8 +8,8 @@ import { getDirectionality, getType, isNamespaceDeclared } from './utility.js';
|
|
|
8
8
|
|
|
9
9
|
/* constants */
|
|
10
10
|
import {
|
|
11
|
-
ALPHA_NUM, ELEMENT_NODE, EMPTY, LANG_PART, NOT_SUPPORTED_ERR,
|
|
12
|
-
|
|
11
|
+
ALPHA_NUM, ATTR_SELECTOR, ELEMENT_NODE, EMPTY, LANG_PART, NOT_SUPPORTED_ERR,
|
|
12
|
+
SYNTAX_ERR, TYPE_SELECTOR
|
|
13
13
|
} from './constant.js';
|
|
14
14
|
|
|
15
15
|
/* Matcher */
|
|
@@ -294,7 +294,7 @@ export class Matcher {
|
|
|
294
294
|
prefix: astPrefix, localName: astLocalName
|
|
295
295
|
} = parseAstName(astName, node);
|
|
296
296
|
if (node.ownerDocument.contentType === 'text/html' &&
|
|
297
|
-
|
|
297
|
+
/[A-Z][\\w-]*/i.test(localName)) {
|
|
298
298
|
astPrefix = astPrefix.toLowerCase();
|
|
299
299
|
astLocalName = astLocalName.toLowerCase();
|
|
300
300
|
}
|
|
@@ -393,7 +393,8 @@ export class Matcher {
|
|
|
393
393
|
}
|
|
394
394
|
}
|
|
395
395
|
} else if (astName) {
|
|
396
|
-
|
|
396
|
+
const reg = new RegExp(`^(?:\\*-)?${ALPHA_NUM}${LANG_PART}$`, 'i');
|
|
397
|
+
if (reg.test(astName)) {
|
|
397
398
|
let regExtendedLang;
|
|
398
399
|
if (astName.indexOf('-') > -1) {
|
|
399
400
|
const [langMain, langSub, ...langRest] = astName.split('-');
|
|
@@ -462,11 +463,11 @@ export class Matcher {
|
|
|
462
463
|
}
|
|
463
464
|
let matched;
|
|
464
465
|
switch (ast.type) {
|
|
465
|
-
case
|
|
466
|
+
case ATTR_SELECTOR: {
|
|
466
467
|
matched = this._matchAttributeSelector(ast, node);
|
|
467
468
|
break;
|
|
468
469
|
}
|
|
469
|
-
case
|
|
470
|
+
case TYPE_SELECTOR: {
|
|
470
471
|
matched = this._matchTypeSelector(ast, node, opt ?? {});
|
|
471
472
|
break;
|
|
472
473
|
}
|
package/src/js/parser.js
CHANGED
|
@@ -8,12 +8,14 @@ import { getType } from './utility.js';
|
|
|
8
8
|
|
|
9
9
|
/* constants */
|
|
10
10
|
import {
|
|
11
|
-
BIT_01, BIT_02, BIT_04, BIT_08, BIT_16, BIT_32, BIT_FFFF,
|
|
12
|
-
DUO, EMPTY, HEX,
|
|
13
|
-
|
|
14
|
-
SELECTOR_ATTR, SELECTOR_CLASS, SELECTOR_ID, SELECTOR_PSEUDO_CLASS,
|
|
15
|
-
SELECTOR_PSEUDO_ELEMENT, SELECTOR_TYPE, SYNTAX_ERR, U_FFFD
|
|
11
|
+
ATTR_SELECTOR, BIT_01, BIT_02, BIT_04, BIT_08, BIT_16, BIT_32, BIT_FFFF,
|
|
12
|
+
CLASS_SELECTOR, DUO, EMPTY, HEX, HYPHEN, ID_SELECTOR, NTH, PS_CLASS_SELECTOR,
|
|
13
|
+
PS_ELEMENT_SELECTOR, REG_LOGICAL, SELECTOR, SYNTAX_ERR, TYPE_SELECTOR
|
|
16
14
|
} from './constant.js';
|
|
15
|
+
const REG_LANG_QUOTED = /(:lang\(\s*("[A-Za-z\d\-*]*")\s*\))/;
|
|
16
|
+
const REG_LOGICAL_EMPTY = /(:(is|where)\(\s*\))/;
|
|
17
|
+
const REG_SHADOW_PSEUDO = /^part|slotted$/;
|
|
18
|
+
const U_FFFD = '\uFFFD';
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* unescape selector
|
|
@@ -29,7 +31,7 @@ export const unescapeSelector = (selector = '') => {
|
|
|
29
31
|
if (item === '' && i === l - 1) {
|
|
30
32
|
item = U_FFFD;
|
|
31
33
|
} else {
|
|
32
|
-
const hexExists =
|
|
34
|
+
const hexExists = /^([\da-f]{1,6}\s?)/i.exec(item);
|
|
33
35
|
if (hexExists) {
|
|
34
36
|
const [, hex] = hexExists;
|
|
35
37
|
let str;
|
|
@@ -88,7 +90,7 @@ export const preprocess = (...args) => {
|
|
|
88
90
|
throw new DOMException(`Invalid selector ${selector}`, SYNTAX_ERR);
|
|
89
91
|
}
|
|
90
92
|
const codePoint = postHash.codePointAt(0);
|
|
91
|
-
if (codePoint ===
|
|
93
|
+
if (codePoint === HYPHEN) {
|
|
92
94
|
if (/^\d$/.test(postHash.substring(1, 2))) {
|
|
93
95
|
throw new DOMException(`Invalid selector ${selector}`, SYNTAX_ERR);
|
|
94
96
|
}
|
|
@@ -126,7 +128,7 @@ export const preprocess = (...args) => {
|
|
|
126
128
|
export const parseSelector = selector => {
|
|
127
129
|
selector = preprocess(selector);
|
|
128
130
|
// invalid selectors
|
|
129
|
-
if (
|
|
131
|
+
if (/^$|^\s*>|,\s*$/.test(selector)) {
|
|
130
132
|
throw new DOMException(`Invalid selector ${selector}`, SYNTAX_ERR);
|
|
131
133
|
}
|
|
132
134
|
let res;
|
|
@@ -191,8 +193,8 @@ export const walkAST = (ast = {}) => {
|
|
|
191
193
|
branches.add(node.children);
|
|
192
194
|
break;
|
|
193
195
|
}
|
|
194
|
-
case
|
|
195
|
-
if (
|
|
196
|
+
case PS_CLASS_SELECTOR: {
|
|
197
|
+
if (REG_LOGICAL.test(node.name)) {
|
|
196
198
|
info.set('hasNestedSelector', true);
|
|
197
199
|
info.set('hasLogicalPseudoFunc', true);
|
|
198
200
|
if (node.name === 'has') {
|
|
@@ -201,7 +203,7 @@ export const walkAST = (ast = {}) => {
|
|
|
201
203
|
}
|
|
202
204
|
break;
|
|
203
205
|
}
|
|
204
|
-
case
|
|
206
|
+
case PS_ELEMENT_SELECTOR: {
|
|
205
207
|
if (REG_SHADOW_PSEUDO.test(node.name)) {
|
|
206
208
|
info.set('hasNestedSelector', true);
|
|
207
209
|
}
|
|
@@ -210,6 +212,7 @@ export const walkAST = (ast = {}) => {
|
|
|
210
212
|
case NTH: {
|
|
211
213
|
if (node.selector) {
|
|
212
214
|
info.set('hasNestedSelector', true);
|
|
215
|
+
info.set('hasNthChildOfSelector', true);
|
|
213
216
|
}
|
|
214
217
|
break;
|
|
215
218
|
}
|
|
@@ -221,12 +224,11 @@ export const walkAST = (ast = {}) => {
|
|
|
221
224
|
if (info.get('hasNestedSelector')) {
|
|
222
225
|
findAll(ast, (node, item, list) => {
|
|
223
226
|
if (list) {
|
|
224
|
-
if (node.type ===
|
|
225
|
-
REG_LOGICAL_PSEUDO.test(node.name)) {
|
|
227
|
+
if (node.type === PS_CLASS_SELECTOR && REG_LOGICAL.test(node.name)) {
|
|
226
228
|
const itemList = list.filter(i => {
|
|
227
229
|
const { name, type } = i;
|
|
228
230
|
const res =
|
|
229
|
-
type ===
|
|
231
|
+
type === PS_CLASS_SELECTOR && REG_LOGICAL.test(name);
|
|
230
232
|
return res;
|
|
231
233
|
});
|
|
232
234
|
for (const { children } of itemList) {
|
|
@@ -240,12 +242,12 @@ export const walkAST = (ast = {}) => {
|
|
|
240
242
|
}
|
|
241
243
|
}
|
|
242
244
|
}
|
|
243
|
-
} else if (node.type ===
|
|
245
|
+
} else if (node.type === PS_ELEMENT_SELECTOR &&
|
|
244
246
|
REG_SHADOW_PSEUDO.test(node.name)) {
|
|
245
247
|
const itemList = list.filter(i => {
|
|
246
248
|
const { name, type } = i;
|
|
247
249
|
const res =
|
|
248
|
-
type ===
|
|
250
|
+
type === PS_ELEMENT_SELECTOR && REG_SHADOW_PSEUDO.test(name);
|
|
249
251
|
return res;
|
|
250
252
|
});
|
|
251
253
|
for (const { children } of itemList) {
|
|
@@ -290,12 +292,12 @@ export const sortAST = asts => {
|
|
|
290
292
|
const arr = [...asts];
|
|
291
293
|
if (arr.length > 1) {
|
|
292
294
|
const order = new Map([
|
|
293
|
-
[
|
|
294
|
-
[
|
|
295
|
-
[
|
|
296
|
-
[
|
|
297
|
-
[
|
|
298
|
-
[
|
|
295
|
+
[PS_ELEMENT_SELECTOR, BIT_01],
|
|
296
|
+
[ID_SELECTOR, BIT_02],
|
|
297
|
+
[CLASS_SELECTOR, BIT_04],
|
|
298
|
+
[TYPE_SELECTOR, BIT_08],
|
|
299
|
+
[ATTR_SELECTOR, BIT_16],
|
|
300
|
+
[PS_CLASS_SELECTOR, BIT_32]
|
|
299
301
|
]);
|
|
300
302
|
arr.sort((a, b) => {
|
|
301
303
|
const { type: typeA } = a;
|
package/src/js/utility.js
CHANGED
|
@@ -10,10 +10,15 @@ import isCustomElementName from 'is-potential-custom-element-name';
|
|
|
10
10
|
/* constants */
|
|
11
11
|
import {
|
|
12
12
|
DOCUMENT_FRAGMENT_NODE, DOCUMENT_NODE, DOCUMENT_POSITION_CONTAINS,
|
|
13
|
-
DOCUMENT_POSITION_PRECEDING, ELEMENT_NODE,
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
DOCUMENT_POSITION_PRECEDING, ELEMENT_NODE, N_TH, LOGICAL_COMPLEX,
|
|
14
|
+
LOGICAL_COMPOUND, PSEUDO_CLASS, REG_INPUT_TYPE, TEXT_NODE, TYPE_FROM,
|
|
15
|
+
TYPE_TO, WALKER_FILTER
|
|
16
16
|
} from './constant.js';
|
|
17
|
+
const REG_LOGICAL_COMPLEX =
|
|
18
|
+
new RegExp(`:(?!${PSEUDO_CLASS}|${N_TH}|${LOGICAL_COMPLEX})`);
|
|
19
|
+
const REG_LOGICAL_COMPOUND =
|
|
20
|
+
new RegExp(`:(?!${PSEUDO_CLASS}|${N_TH}|${LOGICAL_COMPOUND})`);
|
|
21
|
+
const REG_WO_LOGICAL = new RegExp(`:(?!${PSEUDO_CLASS}|${N_TH})`);
|
|
17
22
|
|
|
18
23
|
/**
|
|
19
24
|
* get type
|
|
@@ -171,7 +176,7 @@ export const isInShadowTree = node => {
|
|
|
171
176
|
while (refNode) {
|
|
172
177
|
const { host, mode, nodeType, parentNode } = refNode;
|
|
173
178
|
if (host && mode && nodeType === DOCUMENT_FRAGMENT_NODE &&
|
|
174
|
-
|
|
179
|
+
/^(?:close|open)$/.test(mode)) {
|
|
175
180
|
bool = true;
|
|
176
181
|
break;
|
|
177
182
|
}
|
|
@@ -221,7 +226,7 @@ export const getDirectionality = node => {
|
|
|
221
226
|
if (node.nodeType === ELEMENT_NODE) {
|
|
222
227
|
const { dir: nodeDir, localName, parentNode } = node;
|
|
223
228
|
const { getEmbeddingLevels } = bidiFactory();
|
|
224
|
-
if (
|
|
229
|
+
if (/^(?:ltr|rtl)$/.test(nodeDir)) {
|
|
225
230
|
res = nodeDir;
|
|
226
231
|
} else if (nodeDir === 'auto') {
|
|
227
232
|
let text;
|
|
@@ -253,7 +258,7 @@ export const getDirectionality = node => {
|
|
|
253
258
|
text = itemTextContent.trim();
|
|
254
259
|
} else if (itemNodeType === ELEMENT_NODE) {
|
|
255
260
|
if (!/^(?:bdi|script|style|textarea)$/.test(itemLocalName) &&
|
|
256
|
-
(!itemDir ||
|
|
261
|
+
(!itemDir || !/^(?:ltr|rtl)$/.test(itemDir))) {
|
|
257
262
|
if (itemLocalName === 'slot') {
|
|
258
263
|
text = getSlottedTextContent(item);
|
|
259
264
|
} else {
|
|
@@ -394,7 +399,7 @@ export const isFocusVisible = node => {
|
|
|
394
399
|
const { localName, type } = node;
|
|
395
400
|
switch (localName) {
|
|
396
401
|
case 'input': {
|
|
397
|
-
if (!type ||
|
|
402
|
+
if (!type || REG_INPUT_TYPE.test(type)) {
|
|
398
403
|
res = true;
|
|
399
404
|
}
|
|
400
405
|
break;
|
|
@@ -596,21 +601,16 @@ export const filterSelector = (selector, opt = {}) => {
|
|
|
596
601
|
// filter pseudo-classes
|
|
597
602
|
if (selector.includes(':')) {
|
|
598
603
|
const { complex, descend } = opt;
|
|
599
|
-
let reg;
|
|
600
604
|
if (/:(?:is|not)\(/.test(selector)) {
|
|
601
605
|
if (complex) {
|
|
602
|
-
|
|
606
|
+
return !REG_LOGICAL_COMPLEX.test(selector);
|
|
603
607
|
} else {
|
|
604
|
-
|
|
608
|
+
return !REG_LOGICAL_COMPOUND.test(selector);
|
|
605
609
|
}
|
|
606
|
-
} else {
|
|
607
|
-
if (descend) {
|
|
608
|
-
return false;
|
|
609
|
-
}
|
|
610
|
-
reg = REG_FILTER_SIMPLE;
|
|
611
|
-
}
|
|
612
|
-
if (reg.test(selector)) {
|
|
610
|
+
} else if (descend) {
|
|
613
611
|
return false;
|
|
612
|
+
} else {
|
|
613
|
+
return !REG_WO_LOGICAL.test(selector);
|
|
614
614
|
}
|
|
615
615
|
}
|
|
616
616
|
return true;
|