@asamuzakjp/dom-selector 7.0.1 → 7.0.3
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 +7 -8
- package/src/index.js +33 -17
- package/src/js/finder.js +165 -201
- package/src/js/matcher.js +36 -34
- package/src/js/parser.js +1 -2
- package/src/js/utility.js +13 -13
- package/types/js/finder.d.ts +1 -4
package/package.json
CHANGED
|
@@ -23,13 +23,12 @@
|
|
|
23
23
|
},
|
|
24
24
|
"./package.json": "./package.json"
|
|
25
25
|
},
|
|
26
|
-
"types": "types/index.d.ts",
|
|
27
26
|
"dependencies": {
|
|
28
27
|
"@asamuzakjp/nwsapi": "^2.3.9",
|
|
29
28
|
"bidi-js": "^1.0.3",
|
|
30
|
-
"css-tree": "^3.1
|
|
29
|
+
"css-tree": "^3.2.1",
|
|
31
30
|
"is-potential-custom-element-name": "^1.0.1",
|
|
32
|
-
"lru-cache": "^11.2.
|
|
31
|
+
"lru-cache": "^11.2.7"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"@types/css-tree": "^2.3.11",
|
|
@@ -37,18 +36,18 @@
|
|
|
37
36
|
"c8": "^11.0.0",
|
|
38
37
|
"chai": "^6.2.2",
|
|
39
38
|
"commander": "^14.0.3",
|
|
40
|
-
"eslint": "^9.39.
|
|
39
|
+
"eslint": "^9.39.4",
|
|
41
40
|
"eslint-config-prettier": "^10.1.8",
|
|
42
|
-
"eslint-plugin-jsdoc": "^62.
|
|
41
|
+
"eslint-plugin-jsdoc": "^62.8.0",
|
|
43
42
|
"eslint-plugin-prettier": "^5.5.5",
|
|
44
|
-
"eslint-plugin-regexp": "^3.
|
|
43
|
+
"eslint-plugin-regexp": "^3.1.0",
|
|
45
44
|
"eslint-plugin-unicorn": "^63.0.0",
|
|
46
45
|
"globals": "^17.4.0",
|
|
47
46
|
"jsdom": "^28.1.0",
|
|
48
47
|
"mocha": "^11.7.5",
|
|
49
48
|
"neostandard": "^0.13.0",
|
|
50
49
|
"prettier": "^3.8.1",
|
|
51
|
-
"sinon": "^21.0.
|
|
50
|
+
"sinon": "^21.0.2",
|
|
52
51
|
"typescript": "^5.9.3",
|
|
53
52
|
"wpt-runner": "^6.1.0"
|
|
54
53
|
},
|
|
@@ -72,5 +71,5 @@
|
|
|
72
71
|
"engines": {
|
|
73
72
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
74
73
|
},
|
|
75
|
-
"version": "7.0.
|
|
74
|
+
"version": "7.0.3"
|
|
76
75
|
}
|
package/src/index.js
CHANGED
|
@@ -125,9 +125,7 @@ export class DOMSelector {
|
|
|
125
125
|
opt.check = true;
|
|
126
126
|
opt.noexcept = true;
|
|
127
127
|
opt.warn = false;
|
|
128
|
-
this.#finder.setup(selector, node, opt);
|
|
129
|
-
const res = this.#finder.find(TARGET_SELF);
|
|
130
|
-
return res;
|
|
128
|
+
return this.#finder.setup(selector, node, opt).find(TARGET_SELF);
|
|
131
129
|
};
|
|
132
130
|
|
|
133
131
|
/**
|
|
@@ -163,8 +161,7 @@ export class DOMSelector {
|
|
|
163
161
|
if (filterMatches) {
|
|
164
162
|
try {
|
|
165
163
|
const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
|
|
166
|
-
|
|
167
|
-
return res;
|
|
164
|
+
return this.#nwsapi.match(selector, n);
|
|
168
165
|
} catch (e) {
|
|
169
166
|
// fall through
|
|
170
167
|
}
|
|
@@ -175,8 +172,7 @@ export class DOMSelector {
|
|
|
175
172
|
if (this.#idlUtils) {
|
|
176
173
|
node = this.#idlUtils.wrapperForImpl(node);
|
|
177
174
|
}
|
|
178
|
-
this.#finder.setup(selector, node, opt);
|
|
179
|
-
const nodes = this.#finder.find(TARGET_SELF);
|
|
175
|
+
const nodes = this.#finder.setup(selector, node, opt).find(TARGET_SELF);
|
|
180
176
|
res = nodes.size;
|
|
181
177
|
} catch (e) {
|
|
182
178
|
this.#finder.onError(e, opt);
|
|
@@ -217,8 +213,7 @@ export class DOMSelector {
|
|
|
217
213
|
if (filterMatches) {
|
|
218
214
|
try {
|
|
219
215
|
const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
|
|
220
|
-
|
|
221
|
-
return res;
|
|
216
|
+
return this.#nwsapi.closest(selector, n);
|
|
222
217
|
} catch (e) {
|
|
223
218
|
// fall through
|
|
224
219
|
}
|
|
@@ -229,8 +224,7 @@ export class DOMSelector {
|
|
|
229
224
|
if (this.#idlUtils) {
|
|
230
225
|
node = this.#idlUtils.wrapperForImpl(node);
|
|
231
226
|
}
|
|
232
|
-
this.#finder.setup(selector, node, opt);
|
|
233
|
-
const nodes = this.#finder.find(TARGET_LINEAL);
|
|
227
|
+
const nodes = this.#finder.setup(selector, node, opt).find(TARGET_LINEAL);
|
|
234
228
|
if (nodes.size) {
|
|
235
229
|
let refNode = node;
|
|
236
230
|
while (refNode) {
|
|
@@ -259,13 +253,37 @@ export class DOMSelector {
|
|
|
259
253
|
const e = new this.#window.TypeError(`Unexpected type ${getType(node)}`);
|
|
260
254
|
return this.#finder.onError(e, opt);
|
|
261
255
|
}
|
|
256
|
+
const document =
|
|
257
|
+
node.nodeType === DOCUMENT_NODE ? node : node.ownerDocument;
|
|
258
|
+
if (
|
|
259
|
+
document === this.#document &&
|
|
260
|
+
document.contentType === 'text/html' &&
|
|
261
|
+
document.documentElement &&
|
|
262
|
+
(node.nodeType !== DOCUMENT_FRAGMENT_NODE || !node.host)
|
|
263
|
+
) {
|
|
264
|
+
const cacheKey = `querySelector_${selector}`;
|
|
265
|
+
let filterMatches = false;
|
|
266
|
+
if (this.#cache.has(cacheKey)) {
|
|
267
|
+
filterMatches = this.#cache.get(cacheKey);
|
|
268
|
+
} else {
|
|
269
|
+
filterMatches = filterSelector(selector, TARGET_FIRST);
|
|
270
|
+
this.#cache.set(cacheKey, filterMatches);
|
|
271
|
+
}
|
|
272
|
+
if (filterMatches) {
|
|
273
|
+
try {
|
|
274
|
+
const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
|
|
275
|
+
return this.#nwsapi.first(selector, n);
|
|
276
|
+
} catch (e) {
|
|
277
|
+
// fall through
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
262
281
|
let res;
|
|
263
282
|
try {
|
|
264
283
|
if (this.#idlUtils) {
|
|
265
284
|
node = this.#idlUtils.wrapperForImpl(node);
|
|
266
285
|
}
|
|
267
|
-
this.#finder.setup(selector, node, opt);
|
|
268
|
-
const nodes = this.#finder.find(TARGET_FIRST);
|
|
286
|
+
const nodes = this.#finder.setup(selector, node, opt).find(TARGET_FIRST);
|
|
269
287
|
if (nodes.size) {
|
|
270
288
|
[res] = [...nodes];
|
|
271
289
|
}
|
|
@@ -307,8 +325,7 @@ export class DOMSelector {
|
|
|
307
325
|
if (filterMatches) {
|
|
308
326
|
try {
|
|
309
327
|
const n = this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
|
|
310
|
-
|
|
311
|
-
return res;
|
|
328
|
+
return this.#nwsapi.select(selector, n);
|
|
312
329
|
} catch (e) {
|
|
313
330
|
// fall through
|
|
314
331
|
}
|
|
@@ -319,8 +336,7 @@ export class DOMSelector {
|
|
|
319
336
|
if (this.#idlUtils) {
|
|
320
337
|
node = this.#idlUtils.wrapperForImpl(node);
|
|
321
338
|
}
|
|
322
|
-
this.#finder.setup(selector, node, opt);
|
|
323
|
-
const nodes = this.#finder.find(TARGET_ALL);
|
|
339
|
+
const nodes = this.#finder.setup(selector, node, opt).find(TARGET_ALL);
|
|
324
340
|
if (nodes.size) {
|
|
325
341
|
res = [...nodes];
|
|
326
342
|
}
|
package/src/js/finder.js
CHANGED
|
@@ -275,17 +275,18 @@ export class Finder {
|
|
|
275
275
|
* @returns {Array.<void>} An array of return values from addEventListener.
|
|
276
276
|
*/
|
|
277
277
|
_registerEventListeners = () => {
|
|
278
|
-
const opt = {
|
|
279
|
-
capture: true,
|
|
280
|
-
passive: true
|
|
281
|
-
};
|
|
282
278
|
const func = [];
|
|
283
279
|
for (const eventHandler of this.#eventHandlers) {
|
|
284
280
|
const { keys, handler } = eventHandler;
|
|
285
281
|
const l = keys.length;
|
|
286
282
|
for (let i = 0; i < l; i++) {
|
|
287
283
|
const key = keys[i];
|
|
288
|
-
func.push(
|
|
284
|
+
func.push(
|
|
285
|
+
this.#window.addEventListener(key, handler, {
|
|
286
|
+
capture: true,
|
|
287
|
+
passive: true
|
|
288
|
+
})
|
|
289
|
+
);
|
|
289
290
|
}
|
|
290
291
|
}
|
|
291
292
|
return func;
|
|
@@ -454,10 +455,10 @@ export class Finder {
|
|
|
454
455
|
* @private
|
|
455
456
|
* @param {object} parentNode - The parent element.
|
|
456
457
|
* @param {Array.<Array.<object>>} selectorBranches - The selector branches.
|
|
457
|
-
* @param {object}
|
|
458
|
+
* @param {object} opt - Options.
|
|
458
459
|
* @returns {Array.<object>} An array of child nodes.
|
|
459
460
|
*/
|
|
460
|
-
_getFilteredChildren = (parentNode, selectorBranches, opt
|
|
461
|
+
_getFilteredChildren = (parentNode, selectorBranches, opt) => {
|
|
461
462
|
const children = [];
|
|
462
463
|
const walker = this._createTreeWalker(parentNode, { force: true });
|
|
463
464
|
let childNode = walker.firstChild();
|
|
@@ -496,10 +497,10 @@ export class Finder {
|
|
|
496
497
|
* @param {boolean} [anb.reverse] - If true, reverses the order.
|
|
497
498
|
* @param {object} [anb.selector] - The AST.
|
|
498
499
|
* @param {object} node - The Element node.
|
|
499
|
-
* @param {object}
|
|
500
|
+
* @param {object} opt - Options.
|
|
500
501
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
501
502
|
*/
|
|
502
|
-
_collectNthChild = (anb, node, opt
|
|
503
|
+
_collectNthChild = (anb, node, opt) => {
|
|
503
504
|
const { a, b, selector } = anb;
|
|
504
505
|
const { parentNode } = node;
|
|
505
506
|
if (!parentNode) {
|
|
@@ -574,10 +575,10 @@ export class Finder {
|
|
|
574
575
|
* @param {object} ast - The AST.
|
|
575
576
|
* @param {object} node - The Element node.
|
|
576
577
|
* @param {string} nthName - The name of the nth pseudo-class.
|
|
577
|
-
* @param {object}
|
|
578
|
+
* @param {object} opt - Options.
|
|
578
579
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
579
580
|
*/
|
|
580
|
-
_matchAnPlusB = (ast, node, nthName, opt
|
|
581
|
+
_matchAnPlusB = (ast, node, nthName, opt) => {
|
|
581
582
|
const {
|
|
582
583
|
nth: { a, b, name: nthIdentName },
|
|
583
584
|
selector
|
|
@@ -662,8 +663,8 @@ export class Finder {
|
|
|
662
663
|
leaves: twigLeaves
|
|
663
664
|
};
|
|
664
665
|
opt.dir = DIR_NEXT;
|
|
665
|
-
const nodes = this.
|
|
666
|
-
if (nodes.
|
|
666
|
+
const nodes = this._collectCombinatorMatches(twig, node, opt, []);
|
|
667
|
+
if (nodes.length) {
|
|
667
668
|
if (leaves.length) {
|
|
668
669
|
let bool = false;
|
|
669
670
|
for (const nextNode of nodes) {
|
|
@@ -688,7 +689,7 @@ export class Finder {
|
|
|
688
689
|
* @param {object} [opt] - Options.
|
|
689
690
|
* @returns {?object} The matched node.
|
|
690
691
|
*/
|
|
691
|
-
_evaluateHasPseudo = (astData, node, opt) => {
|
|
692
|
+
_evaluateHasPseudo = (astData, node, opt = {}) => {
|
|
692
693
|
const { branches } = astData;
|
|
693
694
|
let bool = false;
|
|
694
695
|
const l = branches.length;
|
|
@@ -763,10 +764,7 @@ export class Finder {
|
|
|
763
764
|
const arr = [];
|
|
764
765
|
opt.dir = DIR_PREV;
|
|
765
766
|
for (const nextNode of nextNodes) {
|
|
766
|
-
|
|
767
|
-
if (m.size) {
|
|
768
|
-
arr.push(...m);
|
|
769
|
-
}
|
|
767
|
+
this._collectCombinatorMatches(twig, nextNode, opt, arr);
|
|
770
768
|
}
|
|
771
769
|
if (arr.length) {
|
|
772
770
|
if (j === 0) {
|
|
@@ -802,6 +800,8 @@ export class Finder {
|
|
|
802
800
|
* @param {object} ast - The AST.
|
|
803
801
|
* @param {object} node - The Element node.
|
|
804
802
|
* @param {object} [opt] - Options.
|
|
803
|
+
* @param {boolean} [opt.forgive] - Ignores unknown or invalid selectors.
|
|
804
|
+
* @param {boolean} [opt.warn] - If true, console warnings are enabled.
|
|
805
805
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
806
806
|
*/
|
|
807
807
|
_matchPseudoClassSelector(ast, node, opt = {}) {
|
|
@@ -1851,10 +1851,10 @@ export class Finder {
|
|
|
1851
1851
|
* @private
|
|
1852
1852
|
* @param {object} ast - The AST.
|
|
1853
1853
|
* @param {object} node - The Element node.
|
|
1854
|
-
* @param {object}
|
|
1854
|
+
* @param {object} opt - Options.
|
|
1855
1855
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
1856
1856
|
*/
|
|
1857
|
-
_matchSelectorForElement = (ast, node, opt
|
|
1857
|
+
_matchSelectorForElement = (ast, node, opt) => {
|
|
1858
1858
|
const { type: astType } = ast;
|
|
1859
1859
|
const astName = unescapeSelector(ast.name);
|
|
1860
1860
|
const matched = new Set();
|
|
@@ -1889,7 +1889,7 @@ export class Finder {
|
|
|
1889
1889
|
// PS_ELEMENT_SELECTOR is handled by default.
|
|
1890
1890
|
default: {
|
|
1891
1891
|
try {
|
|
1892
|
-
if (
|
|
1892
|
+
if (this.#check) {
|
|
1893
1893
|
const css = generateCSS(ast);
|
|
1894
1894
|
this.#pseudoElement.push(css);
|
|
1895
1895
|
matched.add(node);
|
|
@@ -1934,10 +1934,10 @@ export class Finder {
|
|
|
1934
1934
|
* @private
|
|
1935
1935
|
* @param {object} ast - The AST.
|
|
1936
1936
|
* @param {object} node - The Document, DocumentFragment, or Element node.
|
|
1937
|
-
* @param {object}
|
|
1937
|
+
* @param {object} opt - Options.
|
|
1938
1938
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
1939
1939
|
*/
|
|
1940
|
-
_matchSelector = (ast, node, opt
|
|
1940
|
+
_matchSelector = (ast, node, opt) => {
|
|
1941
1941
|
if (node.nodeType === ELEMENT_NODE) {
|
|
1942
1942
|
return this._matchSelectorForElement(ast, node, opt);
|
|
1943
1943
|
}
|
|
@@ -1956,10 +1956,10 @@ export class Finder {
|
|
|
1956
1956
|
* @private
|
|
1957
1957
|
* @param {Array.<object>} leaves - The AST leaves.
|
|
1958
1958
|
* @param {object} node - The node.
|
|
1959
|
-
* @param {object}
|
|
1959
|
+
* @param {object} opt - Options.
|
|
1960
1960
|
* @returns {boolean} The result.
|
|
1961
1961
|
*/
|
|
1962
|
-
_matchLeaves = (leaves, node, opt
|
|
1962
|
+
_matchLeaves = (leaves, node, opt) => {
|
|
1963
1963
|
const results = this.#invalidate ? this.#invalidateResults : this.#results;
|
|
1964
1964
|
let result = results.get(leaves);
|
|
1965
1965
|
if (result && result.has(node)) {
|
|
@@ -2012,10 +2012,10 @@ export class Finder {
|
|
|
2012
2012
|
* @private
|
|
2013
2013
|
* @param {object} baseNode - The base Element node or Element.shadowRoot.
|
|
2014
2014
|
* @param {Array.<object>} leaves - The AST leaves.
|
|
2015
|
-
* @param {object}
|
|
2015
|
+
* @param {object} opt - Options.
|
|
2016
2016
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
2017
2017
|
*/
|
|
2018
|
-
_traverseAllDescendants = (baseNode, leaves, opt
|
|
2018
|
+
_traverseAllDescendants = (baseNode, leaves, opt) => {
|
|
2019
2019
|
const walker = this._createTreeWalker(baseNode);
|
|
2020
2020
|
traverseNode(baseNode, walker);
|
|
2021
2021
|
let currentNode = walker.firstChild();
|
|
@@ -2034,10 +2034,10 @@ export class Finder {
|
|
|
2034
2034
|
* @private
|
|
2035
2035
|
* @param {Array.<object>} leaves - The AST leaves.
|
|
2036
2036
|
* @param {object} baseNode - The base Element node or Element.shadowRoot.
|
|
2037
|
-
* @param {object}
|
|
2037
|
+
* @param {object} opt - Options.
|
|
2038
2038
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
2039
2039
|
*/
|
|
2040
|
-
_findDescendantNodes = (leaves, baseNode, opt
|
|
2040
|
+
_findDescendantNodes = (leaves, baseNode, opt) => {
|
|
2041
2041
|
const [leaf, ...filterLeaves] = leaves;
|
|
2042
2042
|
const { type: leafType } = leaf;
|
|
2043
2043
|
switch (leafType) {
|
|
@@ -2080,108 +2080,85 @@ export class Finder {
|
|
|
2080
2080
|
};
|
|
2081
2081
|
|
|
2082
2082
|
/**
|
|
2083
|
-
*
|
|
2083
|
+
* Collects combinator matches into an array without creating intermediate sets.
|
|
2084
2084
|
* @private
|
|
2085
2085
|
* @param {object} twig - The twig object.
|
|
2086
2086
|
* @param {object} node - The Element node.
|
|
2087
2087
|
* @param {object} [opt] - Options.
|
|
2088
|
-
* @
|
|
2088
|
+
* @param {string} [opt.dir] - The find direction.
|
|
2089
|
+
* @param {Array.<object>} matched - The collector array.
|
|
2090
|
+
* @returns {Array.<object>} The collector array.
|
|
2089
2091
|
*/
|
|
2090
|
-
|
|
2091
|
-
const {
|
|
2092
|
-
|
|
2092
|
+
_collectCombinatorMatches = (twig, node, opt = {}, matched = []) => {
|
|
2093
|
+
const {
|
|
2094
|
+
combo: { name: comboName },
|
|
2095
|
+
leaves
|
|
2096
|
+
} = twig;
|
|
2093
2097
|
const { dir } = opt;
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2098
|
+
switch (comboName) {
|
|
2099
|
+
case '+': {
|
|
2100
|
+
const refNode =
|
|
2101
|
+
dir === DIR_NEXT
|
|
2102
|
+
? node.nextElementSibling
|
|
2103
|
+
: node.previousElementSibling;
|
|
2104
|
+
if (refNode && this._matchLeaves(leaves, refNode, opt)) {
|
|
2105
|
+
matched.push(refNode);
|
|
2106
|
+
}
|
|
2107
|
+
break;
|
|
2103
2108
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
* @param {object} node - The Element node.
|
|
2118
|
-
* @param {object} [opt] - Options.
|
|
2119
|
-
* @returns {Set.<object>} A collection of matched nodes.
|
|
2120
|
-
*/
|
|
2121
|
-
_matchChildCombinator = (twig, node, opt = {}) => {
|
|
2122
|
-
const { leaves } = twig;
|
|
2123
|
-
const { dir } = opt;
|
|
2124
|
-
const { parentNode } = node;
|
|
2125
|
-
const matched = new Set();
|
|
2126
|
-
if (dir === DIR_NEXT) {
|
|
2127
|
-
let refNode = node.firstElementChild;
|
|
2128
|
-
while (refNode) {
|
|
2129
|
-
if (this._matchLeaves(leaves, refNode, opt)) {
|
|
2130
|
-
matched.add(refNode);
|
|
2109
|
+
case '~': {
|
|
2110
|
+
let refNode =
|
|
2111
|
+
dir === DIR_NEXT
|
|
2112
|
+
? node.nextElementSibling
|
|
2113
|
+
: node.previousElementSibling;
|
|
2114
|
+
while (refNode) {
|
|
2115
|
+
if (this._matchLeaves(leaves, refNode, opt)) {
|
|
2116
|
+
matched.push(refNode);
|
|
2117
|
+
}
|
|
2118
|
+
refNode =
|
|
2119
|
+
dir === DIR_NEXT
|
|
2120
|
+
? refNode.nextElementSibling
|
|
2121
|
+
: refNode.previousElementSibling;
|
|
2131
2122
|
}
|
|
2132
|
-
|
|
2123
|
+
break;
|
|
2133
2124
|
}
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2125
|
+
case '>': {
|
|
2126
|
+
if (dir === DIR_NEXT) {
|
|
2127
|
+
let refNode = node.firstElementChild;
|
|
2128
|
+
while (refNode) {
|
|
2129
|
+
if (this._matchLeaves(leaves, refNode, opt)) {
|
|
2130
|
+
matched.push(refNode);
|
|
2131
|
+
}
|
|
2132
|
+
refNode = refNode.nextElementSibling;
|
|
2133
|
+
}
|
|
2134
|
+
} else {
|
|
2135
|
+
const { parentNode } = node;
|
|
2136
|
+
if (parentNode && this._matchLeaves(leaves, parentNode, opt)) {
|
|
2137
|
+
matched.push(parentNode);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
break;
|
|
2138
2141
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
matched.add(refNode);
|
|
2159
|
-
}
|
|
2160
|
-
return matched;
|
|
2161
|
-
};
|
|
2162
|
-
|
|
2163
|
-
/**
|
|
2164
|
-
* Matches the general sibling combinator '~'.
|
|
2165
|
-
* @private
|
|
2166
|
-
* @param {object} twig - The twig object.
|
|
2167
|
-
* @param {object} node - The Element node.
|
|
2168
|
-
* @param {object} [opt] - Options.
|
|
2169
|
-
* @returns {Set.<object>} A collection of matched nodes.
|
|
2170
|
-
*/
|
|
2171
|
-
_matchGeneralSiblingCombinator = (twig, node, opt = {}) => {
|
|
2172
|
-
const { leaves } = twig;
|
|
2173
|
-
const { dir } = opt;
|
|
2174
|
-
const matched = new Set();
|
|
2175
|
-
let refNode =
|
|
2176
|
-
dir === DIR_NEXT ? node.nextElementSibling : node.previousElementSibling;
|
|
2177
|
-
while (refNode) {
|
|
2178
|
-
if (this._matchLeaves(leaves, refNode, opt)) {
|
|
2179
|
-
matched.add(refNode);
|
|
2142
|
+
case ' ':
|
|
2143
|
+
default: {
|
|
2144
|
+
if (dir === DIR_NEXT) {
|
|
2145
|
+
for (const refNode of this._findDescendantNodes(leaves, node, opt)) {
|
|
2146
|
+
matched.push(refNode);
|
|
2147
|
+
}
|
|
2148
|
+
} else {
|
|
2149
|
+
const ancestors = [];
|
|
2150
|
+
let refNode = node.parentNode;
|
|
2151
|
+
while (refNode) {
|
|
2152
|
+
if (this._matchLeaves(leaves, refNode, opt)) {
|
|
2153
|
+
ancestors.push(refNode);
|
|
2154
|
+
}
|
|
2155
|
+
refNode = refNode.parentNode;
|
|
2156
|
+
}
|
|
2157
|
+
if (ancestors.length) {
|
|
2158
|
+
matched.push(...ancestors.reverse());
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2180
2161
|
}
|
|
2181
|
-
refNode =
|
|
2182
|
-
dir === DIR_NEXT
|
|
2183
|
-
? refNode.nextElementSibling
|
|
2184
|
-
: refNode.previousElementSibling;
|
|
2185
2162
|
}
|
|
2186
2163
|
return matched;
|
|
2187
2164
|
};
|
|
@@ -2191,44 +2168,26 @@ export class Finder {
|
|
|
2191
2168
|
* @private
|
|
2192
2169
|
* @param {object} twig - The twig object.
|
|
2193
2170
|
* @param {object} node - The Element node.
|
|
2194
|
-
* @param {object}
|
|
2171
|
+
* @param {object} opt - Options.
|
|
2195
2172
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
2196
2173
|
*/
|
|
2197
|
-
_matchCombinator = (twig, node, opt
|
|
2198
|
-
|
|
2199
|
-
combo: { name: comboName }
|
|
2200
|
-
} = twig;
|
|
2201
|
-
switch (comboName) {
|
|
2202
|
-
case '+': {
|
|
2203
|
-
return this._matchAdjacentSiblingCombinator(twig, node, opt);
|
|
2204
|
-
}
|
|
2205
|
-
case '~': {
|
|
2206
|
-
return this._matchGeneralSiblingCombinator(twig, node, opt);
|
|
2207
|
-
}
|
|
2208
|
-
case '>': {
|
|
2209
|
-
return this._matchChildCombinator(twig, node, opt);
|
|
2210
|
-
}
|
|
2211
|
-
case ' ':
|
|
2212
|
-
default: {
|
|
2213
|
-
return this._matchDescendantCombinator(twig, node, opt);
|
|
2214
|
-
}
|
|
2215
|
-
}
|
|
2216
|
-
};
|
|
2174
|
+
_matchCombinator = (twig, node, opt) =>
|
|
2175
|
+
new Set(this._collectCombinatorMatches(twig, node, opt));
|
|
2217
2176
|
|
|
2218
2177
|
/**
|
|
2219
2178
|
* Traverses with a TreeWalker and collects nodes matching the leaves.
|
|
2220
2179
|
* @private
|
|
2221
2180
|
* @param {TreeWalker} walker - The TreeWalker instance to use.
|
|
2222
2181
|
* @param {Array} leaves - The AST leaves to match against.
|
|
2223
|
-
* @param {object}
|
|
2224
|
-
* @param {Node}
|
|
2225
|
-
* @param {
|
|
2226
|
-
* @param {Node} [
|
|
2227
|
-
* @param {
|
|
2182
|
+
* @param {object} [opt] - Traversal options.
|
|
2183
|
+
* @param {Node} [opt.boundaryNode] - The node to stop traversal at.
|
|
2184
|
+
* @param {boolean} [opt.force] - Force traversal to the next node.
|
|
2185
|
+
* @param {Node} [opt.startNode] - The node to start traversal from.
|
|
2186
|
+
* @param {string} [opt.targetType] - The type of target ('all' or 'first').
|
|
2228
2187
|
* @returns {Array.<Node>} An array of matched nodes.
|
|
2229
2188
|
*/
|
|
2230
|
-
_traverseAndCollectNodes = (walker, leaves,
|
|
2231
|
-
const { boundaryNode, force, startNode, targetType } =
|
|
2189
|
+
_traverseAndCollectNodes = (walker, leaves, opt = {}) => {
|
|
2190
|
+
const { boundaryNode, force, startNode, targetType } = opt;
|
|
2232
2191
|
const collectedNodes = [];
|
|
2233
2192
|
let currentNode = traverseNode(startNode, walker, !!force);
|
|
2234
2193
|
if (!currentNode) {
|
|
@@ -2275,7 +2234,7 @@ export class Finder {
|
|
|
2275
2234
|
* @private
|
|
2276
2235
|
* @param {Array.<object>} leaves - The AST leaves.
|
|
2277
2236
|
* @param {object} node - The node to start from.
|
|
2278
|
-
* @param {object} opt - Options.
|
|
2237
|
+
* @param {object} [opt] - Options.
|
|
2279
2238
|
* @param {boolean} [opt.force] - If true, traverses only to the next node.
|
|
2280
2239
|
* @param {string} [opt.targetType] - The target type.
|
|
2281
2240
|
* @returns {Array.<object>} A collection of matched nodes.
|
|
@@ -2298,7 +2257,7 @@ export class Finder {
|
|
|
2298
2257
|
* @private
|
|
2299
2258
|
* @param {Array.<object>} leaves - The AST leaves.
|
|
2300
2259
|
* @param {object} node - The node to start from.
|
|
2301
|
-
* @param {object} opt - Options.
|
|
2260
|
+
* @param {object} [opt] - Options.
|
|
2302
2261
|
* @param {boolean} [opt.precede] - If true, finds preceding nodes.
|
|
2303
2262
|
* @returns {Array.<object>} A collection of matched nodes.
|
|
2304
2263
|
*/
|
|
@@ -2323,12 +2282,13 @@ export class Finder {
|
|
|
2323
2282
|
* Matches the node itself.
|
|
2324
2283
|
* @private
|
|
2325
2284
|
* @param {Array} leaves - The AST leaves.
|
|
2326
|
-
* @param {boolean} check - Indicates if running in internal check().
|
|
2327
2285
|
* @returns {Array} An array containing [nodes, filtered, pseudoElement].
|
|
2328
2286
|
*/
|
|
2329
|
-
_matchSelf =
|
|
2330
|
-
const
|
|
2331
|
-
|
|
2287
|
+
_matchSelf = leaves => {
|
|
2288
|
+
const matched = this._matchLeaves(leaves, this.#node, {
|
|
2289
|
+
check: this.#check,
|
|
2290
|
+
warn: this.#warn
|
|
2291
|
+
});
|
|
2332
2292
|
const nodes = matched ? [this.#node] : [];
|
|
2333
2293
|
return [nodes, matched, this.#pseudoElement];
|
|
2334
2294
|
};
|
|
@@ -2337,21 +2297,22 @@ export class Finder {
|
|
|
2337
2297
|
* Finds lineal nodes (self and ancestors).
|
|
2338
2298
|
* @private
|
|
2339
2299
|
* @param {Array} leaves - The AST leaves.
|
|
2340
|
-
* @param {object} opt - Options.
|
|
2300
|
+
* @param {object} [opt] - Options.
|
|
2301
|
+
* @param {boolean} [opt.complex] - If true, the selector is complex.
|
|
2341
2302
|
* @returns {Array} An array containing [nodes, filtered].
|
|
2342
2303
|
*/
|
|
2343
|
-
_findLineal = (leaves, opt) => {
|
|
2304
|
+
_findLineal = (leaves, opt = {}) => {
|
|
2344
2305
|
const { complex } = opt;
|
|
2345
2306
|
const nodes = [];
|
|
2346
|
-
const
|
|
2347
|
-
const selfMatched = this._matchLeaves(leaves, this.#node,
|
|
2307
|
+
const matchOpts = { warn: this.#warn };
|
|
2308
|
+
const selfMatched = this._matchLeaves(leaves, this.#node, matchOpts);
|
|
2348
2309
|
if (selfMatched) {
|
|
2349
2310
|
nodes.push(this.#node);
|
|
2350
2311
|
}
|
|
2351
2312
|
if (!selfMatched || complex) {
|
|
2352
2313
|
let currentNode = this.#node.parentNode;
|
|
2353
2314
|
while (currentNode) {
|
|
2354
|
-
if (this._matchLeaves(leaves, currentNode,
|
|
2315
|
+
if (this._matchLeaves(leaves, currentNode, matchOpts)) {
|
|
2355
2316
|
nodes.push(currentNode);
|
|
2356
2317
|
}
|
|
2357
2318
|
currentNode = currentNode.parentNode;
|
|
@@ -2376,7 +2337,7 @@ export class Finder {
|
|
|
2376
2337
|
const css = generateCSS(leaf);
|
|
2377
2338
|
this.#pseudoElement.push(css);
|
|
2378
2339
|
if (filterLeaves.length) {
|
|
2379
|
-
[nodes, filtered] = this._matchSelf(filterLeaves
|
|
2340
|
+
[nodes, filtered] = this._matchSelf(filterLeaves);
|
|
2380
2341
|
} else {
|
|
2381
2342
|
nodes.push(this.#node);
|
|
2382
2343
|
filtered = true;
|
|
@@ -2392,17 +2353,19 @@ export class Finder {
|
|
|
2392
2353
|
* @private
|
|
2393
2354
|
* @param {object} twig - The current twig from the AST branch.
|
|
2394
2355
|
* @param {string} targetType - The type of target to find.
|
|
2395
|
-
* @param {object} opt -
|
|
2356
|
+
* @param {object} [opt] - Options.
|
|
2357
|
+
* @param {boolean} [opt.complex] - If true, the selector is complex.
|
|
2358
|
+
* @param {boolean} [opt.precede] - If true, finds preceding nodes.
|
|
2396
2359
|
* @returns {object} The result { nodes, filtered, pending }.
|
|
2397
2360
|
*/
|
|
2398
|
-
_findEntryNodesForId = (twig, targetType, opt) => {
|
|
2361
|
+
_findEntryNodesForId = (twig, targetType, opt = {}) => {
|
|
2399
2362
|
const { leaves } = twig;
|
|
2400
2363
|
const [leaf, ...filterLeaves] = leaves;
|
|
2401
2364
|
const { complex, precede } = opt;
|
|
2402
2365
|
let nodes = [];
|
|
2403
2366
|
let filtered = false;
|
|
2404
2367
|
if (targetType === TARGET_SELF) {
|
|
2405
|
-
[nodes, filtered] = this._matchSelf(leaves
|
|
2368
|
+
[nodes, filtered] = this._matchSelf(leaves);
|
|
2406
2369
|
} else if (targetType === TARGET_LINEAL) {
|
|
2407
2370
|
[nodes, filtered] = this._findLineal(leaves, { complex });
|
|
2408
2371
|
} else if (
|
|
@@ -2433,15 +2396,17 @@ export class Finder {
|
|
|
2433
2396
|
* @private
|
|
2434
2397
|
* @param {Array.<object>} leaves - The AST leaves for the selector.
|
|
2435
2398
|
* @param {string} targetType - The type of target to find.
|
|
2436
|
-
* @param {object} opt -
|
|
2399
|
+
* @param {object} [opt] - Options.
|
|
2400
|
+
* @param {boolean} [opt.complex] - If true, the selector is complex.
|
|
2401
|
+
* @param {boolean} [opt.precede] - If true, finds preceding nodes.
|
|
2437
2402
|
* @returns {object} The result { nodes, filtered, pending }.
|
|
2438
2403
|
*/
|
|
2439
|
-
_findEntryNodesForClass = (leaves, targetType, opt) => {
|
|
2404
|
+
_findEntryNodesForClass = (leaves, targetType, opt = {}) => {
|
|
2440
2405
|
const { complex, precede } = opt;
|
|
2441
2406
|
let nodes = [];
|
|
2442
2407
|
let filtered = false;
|
|
2443
2408
|
if (targetType === TARGET_SELF) {
|
|
2444
|
-
[nodes, filtered] = this._matchSelf(leaves
|
|
2409
|
+
[nodes, filtered] = this._matchSelf(leaves);
|
|
2445
2410
|
} else if (targetType === TARGET_LINEAL) {
|
|
2446
2411
|
[nodes, filtered] = this._findLineal(leaves, { complex });
|
|
2447
2412
|
} else {
|
|
@@ -2456,15 +2421,17 @@ export class Finder {
|
|
|
2456
2421
|
* @private
|
|
2457
2422
|
* @param {Array.<object>} leaves - The AST leaves for the selector.
|
|
2458
2423
|
* @param {string} targetType - The type of target to find.
|
|
2459
|
-
* @param {object} opt -
|
|
2424
|
+
* @param {object} [opt] - Options.
|
|
2425
|
+
* @param {boolean} [opt.complex] - If true, the selector is complex.
|
|
2426
|
+
* @param {boolean} [opt.precede] - If true, finds preceding nodes.
|
|
2460
2427
|
* @returns {object} The result { nodes, filtered, pending }.
|
|
2461
2428
|
*/
|
|
2462
|
-
_findEntryNodesForType = (leaves, targetType, opt) => {
|
|
2429
|
+
_findEntryNodesForType = (leaves, targetType, opt = {}) => {
|
|
2463
2430
|
const { complex, precede } = opt;
|
|
2464
2431
|
let nodes = [];
|
|
2465
2432
|
let filtered = false;
|
|
2466
2433
|
if (targetType === TARGET_SELF) {
|
|
2467
|
-
[nodes, filtered] = this._matchSelf(leaves
|
|
2434
|
+
[nodes, filtered] = this._matchSelf(leaves);
|
|
2468
2435
|
} else if (targetType === TARGET_LINEAL) {
|
|
2469
2436
|
[nodes, filtered] = this._findLineal(leaves, { complex });
|
|
2470
2437
|
} else {
|
|
@@ -2479,10 +2446,12 @@ export class Finder {
|
|
|
2479
2446
|
* @private
|
|
2480
2447
|
* @param {object} twig - The current twig from the AST branch.
|
|
2481
2448
|
* @param {string} targetType - The type of target to find.
|
|
2482
|
-
* @param {object} opt -
|
|
2449
|
+
* @param {object} [opt] - Options.
|
|
2450
|
+
* @param {boolean} [opt.complex] - If true, the selector is complex.
|
|
2451
|
+
* @param {boolean} [opt.precede] - If true, finds preceding nodes.
|
|
2483
2452
|
* @returns {object} The result { nodes, filtered, pending }.
|
|
2484
2453
|
*/
|
|
2485
|
-
_findEntryNodesForOther = (twig, targetType, opt) => {
|
|
2454
|
+
_findEntryNodesForOther = (twig, targetType, opt = {}) => {
|
|
2486
2455
|
const { leaves } = twig;
|
|
2487
2456
|
const [leaf, ...filterLeaves] = leaves;
|
|
2488
2457
|
const { complex, precede } = opt;
|
|
@@ -2536,7 +2505,7 @@ export class Finder {
|
|
|
2536
2505
|
}
|
|
2537
2506
|
}
|
|
2538
2507
|
} else if (targetType === TARGET_SELF) {
|
|
2539
|
-
[nodes, filtered] = this._matchSelf(leaves
|
|
2508
|
+
[nodes, filtered] = this._matchSelf(leaves);
|
|
2540
2509
|
} else if (targetType === TARGET_LINEAL) {
|
|
2541
2510
|
[nodes, filtered] = this._findLineal(leaves, { complex });
|
|
2542
2511
|
} else if (targetType === TARGET_FIRST) {
|
|
@@ -2792,15 +2761,13 @@ export class Finder {
|
|
|
2792
2761
|
*/
|
|
2793
2762
|
_getCombinedNodes = (twig, nodes, dir) => {
|
|
2794
2763
|
const arr = [];
|
|
2795
|
-
const options = {
|
|
2796
|
-
dir,
|
|
2797
|
-
warn: this.#warn
|
|
2798
|
-
};
|
|
2799
2764
|
for (const node of nodes) {
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2765
|
+
this._collectCombinatorMatches(
|
|
2766
|
+
twig,
|
|
2767
|
+
node,
|
|
2768
|
+
{ dir, warn: this.#warn },
|
|
2769
|
+
arr
|
|
2770
|
+
);
|
|
2804
2771
|
}
|
|
2805
2772
|
return arr;
|
|
2806
2773
|
};
|
|
@@ -2810,21 +2777,24 @@ export class Finder {
|
|
|
2810
2777
|
* @private
|
|
2811
2778
|
* @param {Array} branch - The branch.
|
|
2812
2779
|
* @param {Set.<object>} nodes - A collection of Element nodes.
|
|
2813
|
-
* @param {object} opt - Options.
|
|
2814
|
-
* @param {object} opt.combo - The combo object.
|
|
2815
|
-
* @param {number} opt.index - The index.
|
|
2780
|
+
* @param {object} [opt] - Options.
|
|
2781
|
+
* @param {object} [opt.combo] - The combo object.
|
|
2782
|
+
* @param {number} [opt.index] - The index.
|
|
2816
2783
|
* @returns {?object} The matched node.
|
|
2817
2784
|
*/
|
|
2818
|
-
_matchNodeNext = (branch, nodes, opt) => {
|
|
2785
|
+
_matchNodeNext = (branch, nodes, opt = {}) => {
|
|
2819
2786
|
const { combo, index } = opt;
|
|
2820
2787
|
const { combo: nextCombo, leaves } = branch[index];
|
|
2821
2788
|
const twig = {
|
|
2822
2789
|
combo,
|
|
2823
2790
|
leaves
|
|
2824
2791
|
};
|
|
2825
|
-
const nextNodes =
|
|
2826
|
-
if (nextNodes.
|
|
2792
|
+
const nextNodes = this._getCombinedNodes(twig, nodes, DIR_NEXT);
|
|
2793
|
+
if (nextNodes.length) {
|
|
2827
2794
|
if (index === branch.length - 1) {
|
|
2795
|
+
if (nextNodes.length === 1) {
|
|
2796
|
+
return nextNodes[0];
|
|
2797
|
+
}
|
|
2828
2798
|
const [nextNode] = sortNodes(nextNodes);
|
|
2829
2799
|
return nextNode;
|
|
2830
2800
|
}
|
|
@@ -2841,16 +2811,15 @@ export class Finder {
|
|
|
2841
2811
|
* @private
|
|
2842
2812
|
* @param {Array} branch - The branch.
|
|
2843
2813
|
* @param {object} node - The Element node.
|
|
2844
|
-
* @param {object} opt - Options.
|
|
2845
|
-
* @param {number} opt.index - The index.
|
|
2814
|
+
* @param {object} [opt] - Options.
|
|
2815
|
+
* @param {number} [opt.index] - The index.
|
|
2846
2816
|
* @returns {?object} The node.
|
|
2847
2817
|
*/
|
|
2848
|
-
_matchNodePrev = (branch, node, opt) => {
|
|
2818
|
+
_matchNodePrev = (branch, node, opt = {}) => {
|
|
2849
2819
|
const { index } = opt;
|
|
2850
2820
|
const twig = branch[index];
|
|
2851
|
-
const
|
|
2852
|
-
|
|
2853
|
-
if (nextNodes.size) {
|
|
2821
|
+
const nextNodes = this._getCombinedNodes(twig, [node], DIR_PREV);
|
|
2822
|
+
if (nextNodes.length) {
|
|
2854
2823
|
if (index === 0) {
|
|
2855
2824
|
return node;
|
|
2856
2825
|
}
|
|
@@ -2887,7 +2856,7 @@ export class Finder {
|
|
|
2887
2856
|
const { combo: firstCombo } = branch[0];
|
|
2888
2857
|
for (const node of entryNodes) {
|
|
2889
2858
|
let combo = firstCombo;
|
|
2890
|
-
let nextNodes =
|
|
2859
|
+
let nextNodes = [node];
|
|
2891
2860
|
for (let j = 1; j < branchLen; j++) {
|
|
2892
2861
|
const { combo: nextCombo, leaves } = branch[j];
|
|
2893
2862
|
const twig = { combo, leaves };
|
|
@@ -2899,10 +2868,8 @@ export class Finder {
|
|
|
2899
2868
|
}
|
|
2900
2869
|
}
|
|
2901
2870
|
combo = nextCombo;
|
|
2902
|
-
nextNodes =
|
|
2871
|
+
nextNodes = nodesArr;
|
|
2903
2872
|
} else {
|
|
2904
|
-
// No further matches down this path.
|
|
2905
|
-
nextNodes.clear();
|
|
2906
2873
|
break;
|
|
2907
2874
|
}
|
|
2908
2875
|
}
|
|
@@ -2910,7 +2877,7 @@ export class Finder {
|
|
|
2910
2877
|
// DIR_PREV
|
|
2911
2878
|
} else {
|
|
2912
2879
|
for (const node of entryNodes) {
|
|
2913
|
-
let nextNodes =
|
|
2880
|
+
let nextNodes = [node];
|
|
2914
2881
|
for (let j = lastIndex - 1; j >= 0; j--) {
|
|
2915
2882
|
const twig = branch[j];
|
|
2916
2883
|
const nodesArr = this._getCombinedNodes(twig, nextNodes, dir);
|
|
@@ -2919,10 +2886,8 @@ export class Finder {
|
|
|
2919
2886
|
if (j === 0) {
|
|
2920
2887
|
matchedNodes.add(node);
|
|
2921
2888
|
}
|
|
2922
|
-
nextNodes =
|
|
2889
|
+
nextNodes = nodesArr;
|
|
2923
2890
|
} else {
|
|
2924
|
-
// No further matches down this path.
|
|
2925
|
-
nextNodes.clear();
|
|
2926
2891
|
break;
|
|
2927
2892
|
}
|
|
2928
2893
|
}
|
|
@@ -3152,7 +3117,6 @@ export class Finder {
|
|
|
3152
3117
|
* @returns {object} The AST for the selector.
|
|
3153
3118
|
*/
|
|
3154
3119
|
getAST = selector => {
|
|
3155
|
-
// TBD: get cached AST if possible.
|
|
3156
3120
|
return parseSelector(selector);
|
|
3157
3121
|
};
|
|
3158
3122
|
}
|
package/src/js/matcher.js
CHANGED
|
@@ -34,7 +34,6 @@ const KEYS_FORM_PS_DISABLED = new Set([
|
|
|
34
34
|
]);
|
|
35
35
|
const KEYS_INPUT_EDIT = new Set(INPUT_EDIT);
|
|
36
36
|
const REG_LANG_VALID = new RegExp(`^(?:\\*-)?${ALPHA_NUM}${LANG_PART}$`, 'i');
|
|
37
|
-
const REG_TAG_NAME = /[A-Z][\\w-]*/i;
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
39
|
* Validates a pseudo-element selector.
|
|
@@ -315,9 +314,8 @@ export const matchAttributeSelector = (ast, node, opt = {}) => {
|
|
|
315
314
|
return false;
|
|
316
315
|
}
|
|
317
316
|
// Determine case sensitivity based on document type and flags.
|
|
318
|
-
const contentType = node.ownerDocument.contentType;
|
|
319
317
|
let caseInsensitive;
|
|
320
|
-
if (contentType === 'text/html') {
|
|
318
|
+
if (node.ownerDocument.contentType === 'text/html') {
|
|
321
319
|
if (typeof astFlags === 'string' && /^s$/i.test(astFlags)) {
|
|
322
320
|
caseInsensitive = false;
|
|
323
321
|
} else {
|
|
@@ -345,6 +343,7 @@ export const matchAttributeSelector = (ast, node, opt = {}) => {
|
|
|
345
343
|
itemName = itemName.toLowerCase();
|
|
346
344
|
itemValue = itemValue.toLowerCase();
|
|
347
345
|
}
|
|
346
|
+
const colonIdx = itemName.indexOf(':');
|
|
348
347
|
switch (astPrefix) {
|
|
349
348
|
case '': {
|
|
350
349
|
if (astLocalName === itemName) {
|
|
@@ -353,9 +352,10 @@ export const matchAttributeSelector = (ast, node, opt = {}) => {
|
|
|
353
352
|
break;
|
|
354
353
|
}
|
|
355
354
|
case '*': {
|
|
356
|
-
if (
|
|
357
|
-
const
|
|
358
|
-
|
|
355
|
+
if (colonIdx > -1) {
|
|
356
|
+
const itemLocalName = itemName
|
|
357
|
+
.substring(colonIdx + 1)
|
|
358
|
+
.replace(/^:/, '');
|
|
359
359
|
if (itemLocalName === astLocalName) {
|
|
360
360
|
attrValues.add(itemValue);
|
|
361
361
|
}
|
|
@@ -376,9 +376,11 @@ export const matchAttributeSelector = (ast, node, opt = {}) => {
|
|
|
376
376
|
globalObject
|
|
377
377
|
);
|
|
378
378
|
}
|
|
379
|
-
if (
|
|
380
|
-
const
|
|
381
|
-
const itemLocalName =
|
|
379
|
+
if (colonIdx > -1) {
|
|
380
|
+
const itemPrefix = itemName.substring(0, colonIdx);
|
|
381
|
+
const itemLocalName = itemName
|
|
382
|
+
.substring(colonIdx + 1)
|
|
383
|
+
.replace(/^:/, '');
|
|
382
384
|
// Ignore the 'xml:lang' attribute.
|
|
383
385
|
if (itemPrefix === 'xml' && itemLocalName === 'lang') {
|
|
384
386
|
continue;
|
|
@@ -402,9 +404,12 @@ export const matchAttributeSelector = (ast, node, opt = {}) => {
|
|
|
402
404
|
itemName = itemName.toLowerCase();
|
|
403
405
|
itemValue = itemValue.toLowerCase();
|
|
404
406
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const
|
|
407
|
+
const colonIdx = itemName.indexOf(':');
|
|
408
|
+
if (colonIdx > -1) {
|
|
409
|
+
const itemPrefix = itemName.substring(0, colonIdx);
|
|
410
|
+
const itemLocalName = itemName
|
|
411
|
+
.substring(colonIdx + 1)
|
|
412
|
+
.replace(/^:/, '');
|
|
408
413
|
// The attribute is starting with ':'.
|
|
409
414
|
if (!itemPrefix && astAttrName === `:${itemLocalName}`) {
|
|
410
415
|
attrValues.add(itemValue);
|
|
@@ -427,15 +432,15 @@ export const matchAttributeSelector = (ast, node, opt = {}) => {
|
|
|
427
432
|
let attrValue;
|
|
428
433
|
if (astIdentValue) {
|
|
429
434
|
if (caseInsensitive) {
|
|
430
|
-
attrValue = astIdentValue.toLowerCase();
|
|
435
|
+
attrValue = astIdentValue.toLowerCase().replace(/\\(?!\\)/g, '');
|
|
431
436
|
} else {
|
|
432
|
-
attrValue = astIdentValue;
|
|
437
|
+
attrValue = astIdentValue.replace(/\\(?!\\)/g, '');
|
|
433
438
|
}
|
|
434
439
|
} else if (astStringValue) {
|
|
435
440
|
if (caseInsensitive) {
|
|
436
|
-
attrValue = astStringValue.toLowerCase();
|
|
441
|
+
attrValue = astStringValue.toLowerCase().replace(/\\(?!\\)/g, '');
|
|
437
442
|
} else {
|
|
438
|
-
attrValue = astStringValue;
|
|
443
|
+
attrValue = astStringValue.replace(/\\(?!\\)/g, '');
|
|
439
444
|
}
|
|
440
445
|
} else if (astStringValue === '') {
|
|
441
446
|
attrValue = astStringValue;
|
|
@@ -521,39 +526,39 @@ export const matchTypeSelector = (ast, node, opt = {}) => {
|
|
|
521
526
|
astName,
|
|
522
527
|
node
|
|
523
528
|
);
|
|
529
|
+
const firstChar = localName.charCodeAt(0);
|
|
530
|
+
const isAlphabet =
|
|
531
|
+
(firstChar >= 65 && firstChar <= 90) ||
|
|
532
|
+
(firstChar >= 97 && firstChar <= 122);
|
|
524
533
|
if (
|
|
525
534
|
node.ownerDocument.contentType === 'text/html' &&
|
|
526
535
|
(!namespaceURI || namespaceURI === 'http://www.w3.org/1999/xhtml') &&
|
|
527
|
-
|
|
536
|
+
isAlphabet
|
|
528
537
|
) {
|
|
529
538
|
astPrefix = astPrefix.toLowerCase();
|
|
530
539
|
astLocalName = astLocalName.toLowerCase();
|
|
531
540
|
}
|
|
532
541
|
let nodePrefix;
|
|
533
542
|
let nodeLocalName;
|
|
534
|
-
|
|
535
|
-
if (
|
|
536
|
-
|
|
543
|
+
const colonIdx = localName.indexOf(':');
|
|
544
|
+
if (colonIdx > -1) {
|
|
545
|
+
nodePrefix = localName.substring(0, colonIdx);
|
|
546
|
+
nodeLocalName = localName.substring(colonIdx + 1);
|
|
537
547
|
} else {
|
|
538
548
|
nodePrefix = prefix || '';
|
|
539
549
|
nodeLocalName = localName;
|
|
540
550
|
}
|
|
551
|
+
const isUniversal = astLocalName === '*';
|
|
541
552
|
switch (astPrefix) {
|
|
542
553
|
case '': {
|
|
543
|
-
|
|
554
|
+
return (
|
|
544
555
|
!nodePrefix &&
|
|
545
556
|
!namespaceURI &&
|
|
546
|
-
(
|
|
547
|
-
)
|
|
548
|
-
return true;
|
|
549
|
-
}
|
|
550
|
-
return false;
|
|
557
|
+
(isUniversal || astLocalName === nodeLocalName)
|
|
558
|
+
);
|
|
551
559
|
}
|
|
552
560
|
case '*': {
|
|
553
|
-
|
|
554
|
-
return true;
|
|
555
|
-
}
|
|
556
|
-
return false;
|
|
561
|
+
return isUniversal || astLocalName === nodeLocalName;
|
|
557
562
|
}
|
|
558
563
|
default: {
|
|
559
564
|
if (!check) {
|
|
@@ -570,10 +575,7 @@ export const matchTypeSelector = (ast, node, opt = {}) => {
|
|
|
570
575
|
const astNS = node.lookupNamespaceURI(astPrefix);
|
|
571
576
|
const nodeNS = node.lookupNamespaceURI(nodePrefix);
|
|
572
577
|
if (astNS === nodeNS && astPrefix === nodePrefix) {
|
|
573
|
-
|
|
574
|
-
return true;
|
|
575
|
-
}
|
|
576
|
-
return false;
|
|
578
|
+
return isUniversal || astLocalName === nodeLocalName;
|
|
577
579
|
} else if (!forgive && !astNS) {
|
|
578
580
|
throw generateException(
|
|
579
581
|
`Undeclared namespace ${astPrefix}`,
|
package/src/js/parser.js
CHANGED
package/src/js/utility.js
CHANGED
|
@@ -32,7 +32,6 @@ import {
|
|
|
32
32
|
RULE,
|
|
33
33
|
SCOPE,
|
|
34
34
|
SELECTOR_LIST,
|
|
35
|
-
SIBLING,
|
|
36
35
|
TARGET_ALL,
|
|
37
36
|
TARGET_FIRST,
|
|
38
37
|
TEXT_NODE,
|
|
@@ -59,11 +58,11 @@ const KEYS_NODE_FOCUSABLE_SVG = new Set([
|
|
|
59
58
|
'symbol',
|
|
60
59
|
'title'
|
|
61
60
|
]);
|
|
61
|
+
const REG_ATTR_SIMPLE = /^\[[A-Z\d-]{1,255}="?[A-Z\d\s-]{1,255}"?\]$/i;
|
|
62
62
|
const REG_EXCLUDE_BASIC =
|
|
63
63
|
/[|\\]|::|[^\u0021-\u007F\s]|\[\s*[\w$*=^|~-]+(?:(?:"[\w$*=^|~\s'-]+"|'[\w$*=^|~\s"-]+')?(?:\s+[\w$*=^|~-]+)+|"[^"\]]{1,255}|'[^'\]]{1,255})\s*\]|:(?:is|where)\(\s*\)/;
|
|
64
64
|
const REG_COMPLEX = new RegExp(`${COMPOUND_I}${COMBO}${COMPOUND_I}`, 'i');
|
|
65
65
|
const REG_DESCEND = new RegExp(`${COMPOUND_I}${DESCEND}${COMPOUND_I}`, 'i');
|
|
66
|
-
const REG_SIBLING = new RegExp(`${COMPOUND_I}${SIBLING}${COMPOUND_I}`, 'i');
|
|
67
66
|
const REG_LOGIC_COMPLEX = new RegExp(
|
|
68
67
|
`:(?!${PSEUDO_CLASS}|${N_TH}|${LOGIC_COMPLEX})`
|
|
69
68
|
);
|
|
@@ -1045,7 +1044,7 @@ export const initNwsapi = (window, document) => {
|
|
|
1045
1044
|
* @returns {boolean} - True if the selector is valid for nwsapi.
|
|
1046
1045
|
*/
|
|
1047
1046
|
export const filterSelector = (selector, target) => {
|
|
1048
|
-
const
|
|
1047
|
+
const isQuerySelectorAll = target === TARGET_ALL;
|
|
1049
1048
|
if (
|
|
1050
1049
|
!selector ||
|
|
1051
1050
|
typeof selector !== 'string' ||
|
|
@@ -1061,6 +1060,13 @@ export const filterSelector = (selector, target) => {
|
|
|
1061
1060
|
return false;
|
|
1062
1061
|
}
|
|
1063
1062
|
}
|
|
1063
|
+
// Match only simple attribute selector for TARGET_FIRST.
|
|
1064
|
+
if (target === TARGET_FIRST) {
|
|
1065
|
+
if (REG_ATTR_SIMPLE.test(selector)) {
|
|
1066
|
+
return true;
|
|
1067
|
+
}
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1064
1070
|
// Exclude various complex or unsupported selectors.
|
|
1065
1071
|
// - selectors containing '/'
|
|
1066
1072
|
// - namespaced selectors
|
|
@@ -1076,17 +1082,11 @@ export const filterSelector = (selector, target) => {
|
|
|
1076
1082
|
}
|
|
1077
1083
|
// Include pseudo-classes that are known to work correctly.
|
|
1078
1084
|
if (selector.includes(':')) {
|
|
1079
|
-
|
|
1080
|
-
if (target !== isQuerySelectorType) {
|
|
1081
|
-
complex = REG_COMPLEX.test(selector);
|
|
1082
|
-
}
|
|
1083
|
-
if (
|
|
1084
|
-
isQuerySelectorType &&
|
|
1085
|
-
REG_DESCEND.test(selector) &&
|
|
1086
|
-
!REG_SIBLING.test(selector)
|
|
1087
|
-
) {
|
|
1085
|
+
if (isQuerySelectorAll && REG_DESCEND.test(selector)) {
|
|
1088
1086
|
return false;
|
|
1089
|
-
}
|
|
1087
|
+
}
|
|
1088
|
+
const complex = isQuerySelectorAll ? false : REG_COMPLEX.test(selector);
|
|
1089
|
+
if (!isQuerySelectorAll && /:has\(/.test(selector)) {
|
|
1090
1090
|
if (!complex || REG_LOGIC_HAS_COMPOUND.test(selector)) {
|
|
1091
1091
|
return false;
|
|
1092
1092
|
}
|
package/types/js/finder.d.ts
CHANGED
|
@@ -34,10 +34,7 @@ export class Finder {
|
|
|
34
34
|
private _matchLeaves;
|
|
35
35
|
private _traverseAllDescendants;
|
|
36
36
|
private _findDescendantNodes;
|
|
37
|
-
private
|
|
38
|
-
private _matchChildCombinator;
|
|
39
|
-
private _matchAdjacentSiblingCombinator;
|
|
40
|
-
private _matchGeneralSiblingCombinator;
|
|
37
|
+
private _collectCombinatorMatches;
|
|
41
38
|
private _matchCombinator;
|
|
42
39
|
private _traverseAndCollectNodes;
|
|
43
40
|
private _findPrecede;
|