@asamuzakjp/dom-selector 0.13.5 → 0.15.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/README.md +8 -12
- package/package.json +1 -1
- package/src/index.js +4 -8
- package/src/js/matcher.js +141 -109
- package/src/js/parser.js +29 -12
- package/types/js/matcher.d.ts +1 -1
package/README.md
CHANGED
|
@@ -29,52 +29,49 @@ const {
|
|
|
29
29
|
|
|
30
30
|
### matches(selector, node, opt)
|
|
31
31
|
|
|
32
|
-
matches - [Element.matches()][64]
|
|
32
|
+
matches - same functionality as [Element.matches()][64]
|
|
33
33
|
|
|
34
34
|
#### Parameters
|
|
35
35
|
|
|
36
36
|
- `selector` **[string][59]** CSS selector
|
|
37
37
|
- `node` **[object][60]** Element node
|
|
38
38
|
- `opt` **[object][60]?** options
|
|
39
|
-
- `opt.
|
|
40
|
-
- `opt.jsdom` **[boolean][61]?** is jsdom
|
|
39
|
+
- `opt.warn` **[boolean][61]?** console warn e.g. unsupported pseudo-class
|
|
41
40
|
|
|
42
41
|
Returns **[boolean][61]** result
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
### closest(selector, node, opt)
|
|
46
45
|
|
|
47
|
-
closest - [Element.closest()][65]
|
|
46
|
+
closest - same functionality as [Element.closest()][65]
|
|
48
47
|
|
|
49
48
|
#### Parameters
|
|
50
49
|
|
|
51
50
|
- `selector` **[string][59]** CSS selector
|
|
52
51
|
- `node` **[object][60]** Element node
|
|
53
52
|
- `opt` **[object][60]?** options
|
|
54
|
-
- `opt.
|
|
55
|
-
- `opt.jsdom` **[boolean][61]?** is jsdom
|
|
53
|
+
- `opt.warn` **[boolean][61]?** console warn e.g. unsupported pseudo-class
|
|
56
54
|
|
|
57
55
|
Returns **[object][60]?** matched node
|
|
58
56
|
|
|
59
57
|
|
|
60
58
|
### querySelector(selector, refPoint, opt)
|
|
61
59
|
|
|
62
|
-
querySelector - [Document.querySelector()][66], [DocumentFragment.querySelector()][67], [Element.querySelector()][68]
|
|
60
|
+
querySelector - same functionality as [Document.querySelector()][66], [DocumentFragment.querySelector()][67], [Element.querySelector()][68]
|
|
63
61
|
|
|
64
62
|
#### Parameters
|
|
65
63
|
|
|
66
64
|
- `selector` **[string][59]** CSS selector
|
|
67
65
|
- `refPoint` **[object][60]** Document, DocumentFragment or Element node
|
|
68
66
|
- `opt` **[object][60]?** options
|
|
69
|
-
- `opt.
|
|
70
|
-
- `opt.jsdom` **[boolean][61]?** is jsdom
|
|
67
|
+
- `opt.warn` **[boolean][61]?** console warn e.g. unsupported pseudo-class
|
|
71
68
|
|
|
72
69
|
Returns **[object][60]?** matched node
|
|
73
70
|
|
|
74
71
|
|
|
75
72
|
### querySelectorAll(selector, refPoint, opt)
|
|
76
73
|
|
|
77
|
-
querySelectorAll - [Document.querySelectorAll()][69], [Document.querySelectorAll()][70], [Element.querySelectorAll()][71]
|
|
74
|
+
querySelectorAll - same functionality as [Document.querySelectorAll()][69], [Document.querySelectorAll()][70], [Element.querySelectorAll()][71]
|
|
78
75
|
**NOTE**: returns Array, not NodeList
|
|
79
76
|
|
|
80
77
|
#### Parameters
|
|
@@ -82,8 +79,7 @@ querySelectorAll - [Document.querySelectorAll()][69], [Document.querySelectorAll
|
|
|
82
79
|
- `selector` **[string][59]** CSS selector
|
|
83
80
|
- `refPoint` **[object][60]** Document, DocumentFragment or Element node
|
|
84
81
|
- `opt` **[object][60]?** options
|
|
85
|
-
- `opt.
|
|
86
|
-
- `opt.jsdom` **[boolean][61]?** is jsdom
|
|
82
|
+
- `opt.warn` **[boolean][61]?** console warn e.g. unsupported pseudo-class
|
|
87
83
|
|
|
88
84
|
Returns **[Array][62]<([object][60] \| [undefined][63])>** array of matched nodes
|
|
89
85
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -14,8 +14,7 @@ const { Matcher } = require('./js/matcher.js');
|
|
|
14
14
|
* @param {string} selector - CSS selector
|
|
15
15
|
* @param {object} node - Element node
|
|
16
16
|
* @param {object} [opt] - options
|
|
17
|
-
* @param {object} [opt.
|
|
18
|
-
* @param {boolean} [opt.jsdom] - is jsdom
|
|
17
|
+
* @param {object} [opt.warn] - console warn e.g. unsupported pseudo-class
|
|
19
18
|
* @returns {boolean} - result
|
|
20
19
|
*/
|
|
21
20
|
const matches = (selector, node, opt) => {
|
|
@@ -28,8 +27,7 @@ const matches = (selector, node, opt) => {
|
|
|
28
27
|
* @param {string} selector - CSS selector
|
|
29
28
|
* @param {object} node - Element node
|
|
30
29
|
* @param {object} [opt] - options
|
|
31
|
-
* @param {object} [opt.
|
|
32
|
-
* @param {boolean} [opt.jsdom] - is jsdom
|
|
30
|
+
* @param {object} [opt.warn] - console warn e.g. unsupported pseudo-class
|
|
33
31
|
* @returns {?object} - matched node
|
|
34
32
|
*/
|
|
35
33
|
const closest = (selector, node, opt) => {
|
|
@@ -42,8 +40,7 @@ const closest = (selector, node, opt) => {
|
|
|
42
40
|
* @param {string} selector - CSS selector
|
|
43
41
|
* @param {object} refPoint - Document or Element node
|
|
44
42
|
* @param {object} [opt] - options
|
|
45
|
-
* @param {object} [opt.
|
|
46
|
-
* @param {boolean} [opt.jsdom] - is jsdom
|
|
43
|
+
* @param {object} [opt.warn] - console warn e.g. unsupported pseudo-class
|
|
47
44
|
* @returns {?object} - matched node
|
|
48
45
|
*/
|
|
49
46
|
const querySelector = (selector, refPoint, opt) => {
|
|
@@ -57,8 +54,7 @@ const querySelector = (selector, refPoint, opt) => {
|
|
|
57
54
|
* @param {string} selector - CSS selector
|
|
58
55
|
* @param {object} refPoint - Document or Element node
|
|
59
56
|
* @param {object} [opt] - options
|
|
60
|
-
* @param {object} [opt.
|
|
61
|
-
* @param {boolean} [opt.jsdom] - is jsdom
|
|
57
|
+
* @param {object} [opt.warn] - console warn e.g. unsupported pseudo-class
|
|
62
58
|
* @returns {Array.<object|undefined>} - array of matched nodes
|
|
63
59
|
*/
|
|
64
60
|
const querySelectorAll = (selector, refPoint, opt) => {
|
package/src/js/matcher.js
CHANGED
|
@@ -85,6 +85,27 @@ const isNamespaceDeclared = (ns = '', node = {}) => {
|
|
|
85
85
|
return !!res;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* is node attached to owner document
|
|
90
|
+
* @param {object} node - Element node
|
|
91
|
+
* @returns {boolean} - result
|
|
92
|
+
*/
|
|
93
|
+
const isAttached = (node = {}) => {
|
|
94
|
+
const { nodeType, ownerDocument } = node;
|
|
95
|
+
let res;
|
|
96
|
+
if (nodeType === ELEMENT_NODE && ownerDocument) {
|
|
97
|
+
const root = ownerDocument.documentElement;
|
|
98
|
+
if (node === root) {
|
|
99
|
+
res = true;
|
|
100
|
+
} else {
|
|
101
|
+
const posBit =
|
|
102
|
+
node.compareDocumentPosition(root) & DOCUMENT_POSITION_CONTAINS;
|
|
103
|
+
res = posBit;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return !!res;
|
|
107
|
+
};
|
|
108
|
+
|
|
88
109
|
/**
|
|
89
110
|
* unescape selector
|
|
90
111
|
* @param {string} selector - CSS selector
|
|
@@ -211,7 +232,8 @@ const collectNthChild = (anb = {}, node = {}) => {
|
|
|
211
232
|
const { a, b, reverse, selector } = anb;
|
|
212
233
|
const { nodeType, parentNode } = node;
|
|
213
234
|
const matched = [];
|
|
214
|
-
if (Number.isInteger(a) && Number.isInteger(b) &&
|
|
235
|
+
if (Number.isInteger(a) && Number.isInteger(b) &&
|
|
236
|
+
nodeType === ELEMENT_NODE && parentNode) {
|
|
215
237
|
const arr = [...parentNode.children];
|
|
216
238
|
if (reverse) {
|
|
217
239
|
arr.reverse();
|
|
@@ -293,7 +315,8 @@ const collectNthOfType = (anb = {}, node = {}) => {
|
|
|
293
315
|
const { a, b, reverse } = anb;
|
|
294
316
|
const { localName, nodeType, parentNode, prefix } = node;
|
|
295
317
|
const matched = [];
|
|
296
|
-
if (Number.isInteger(a) && Number.isInteger(b) &&
|
|
318
|
+
if (Number.isInteger(a) && Number.isInteger(b) &&
|
|
319
|
+
nodeType === ELEMENT_NODE && parentNode) {
|
|
297
320
|
const arr = [...parentNode.children];
|
|
298
321
|
if (reverse) {
|
|
299
322
|
arr.reverse();
|
|
@@ -357,67 +380,66 @@ const collectNthOfType = (anb = {}, node = {}) => {
|
|
|
357
380
|
* @param {object} node - Element node
|
|
358
381
|
* @returns {Array.<object|undefined>} - collection of matched nodes
|
|
359
382
|
*/
|
|
360
|
-
const matchAnPlusB = (nthName, ast = {}, node = {}) => {
|
|
383
|
+
const matchAnPlusB = (nthName, ast = { nth: {} }, node = {}) => {
|
|
384
|
+
const {
|
|
385
|
+
nth: {
|
|
386
|
+
a,
|
|
387
|
+
b,
|
|
388
|
+
name: nthIdentName
|
|
389
|
+
},
|
|
390
|
+
selector: astSelector,
|
|
391
|
+
type: astType
|
|
392
|
+
} = ast;
|
|
393
|
+
const identName = unescapeSelector(nthIdentName);
|
|
394
|
+
const { nodeType, parentNode } = node;
|
|
361
395
|
const matched = [];
|
|
362
|
-
if (typeof nthName === 'string'
|
|
396
|
+
if (typeof nthName === 'string' && astType === NTH &&
|
|
397
|
+
nodeType === ELEMENT_NODE && parentNode) {
|
|
363
398
|
nthName = nthName.trim();
|
|
364
399
|
if (PSEUDO_NTH.test(nthName)) {
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (
|
|
379
|
-
|
|
380
|
-
anbMap.set('a', 2);
|
|
381
|
-
anbMap.set('b', 0);
|
|
382
|
-
} else if (identName === 'odd') {
|
|
383
|
-
anbMap.set('a', 2);
|
|
384
|
-
anbMap.set('b', 1);
|
|
385
|
-
}
|
|
386
|
-
if (/last/.test(nthName)) {
|
|
387
|
-
anbMap.set('reverse', true);
|
|
388
|
-
}
|
|
400
|
+
const anbMap = new Map();
|
|
401
|
+
if (identName) {
|
|
402
|
+
if (identName === 'even') {
|
|
403
|
+
anbMap.set('a', 2);
|
|
404
|
+
anbMap.set('b', 0);
|
|
405
|
+
} else if (identName === 'odd') {
|
|
406
|
+
anbMap.set('a', 2);
|
|
407
|
+
anbMap.set('b', 1);
|
|
408
|
+
}
|
|
409
|
+
if (/last/.test(nthName)) {
|
|
410
|
+
anbMap.set('reverse', true);
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
if (typeof a === 'string' && /-?\d+/.test(a)) {
|
|
414
|
+
anbMap.set('a', a * 1);
|
|
389
415
|
} else {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
416
|
+
anbMap.set('a', 0);
|
|
417
|
+
}
|
|
418
|
+
if (typeof b === 'string' && /-?\d+/.test(b)) {
|
|
419
|
+
anbMap.set('b', b * 1);
|
|
420
|
+
} else {
|
|
421
|
+
anbMap.set('b', 0);
|
|
422
|
+
}
|
|
423
|
+
if (/last/.test(nthName)) {
|
|
424
|
+
anbMap.set('reverse', true);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (anbMap.has('a') && anbMap.has('b')) {
|
|
428
|
+
if (/^nth-(?:last-)?child$/.test(nthName)) {
|
|
429
|
+
if (astSelector) {
|
|
430
|
+
const css = generateCSS(astSelector);
|
|
431
|
+
anbMap.set('selector', css);
|
|
399
432
|
}
|
|
400
|
-
|
|
401
|
-
|
|
433
|
+
const anb = Object.fromEntries(anbMap);
|
|
434
|
+
const arr = collectNthChild(anb, node);
|
|
435
|
+
if (arr.length) {
|
|
436
|
+
matched.push(...arr);
|
|
402
437
|
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
anbMap.set('selector', css);
|
|
409
|
-
}
|
|
410
|
-
const anb = Object.fromEntries(anbMap);
|
|
411
|
-
const arr = collectNthChild(anb, node);
|
|
412
|
-
if (arr.length) {
|
|
413
|
-
matched.push(...arr);
|
|
414
|
-
}
|
|
415
|
-
} else if (/^nth-(?:last-)?of-type$/.test(nthName)) {
|
|
416
|
-
const anb = Object.fromEntries(anbMap);
|
|
417
|
-
const arr = collectNthOfType(anb, node);
|
|
418
|
-
if (arr.length) {
|
|
419
|
-
matched.push(...arr);
|
|
420
|
-
}
|
|
438
|
+
} else if (/^nth-(?:last-)?of-type$/.test(nthName)) {
|
|
439
|
+
const anb = Object.fromEntries(anbMap);
|
|
440
|
+
const arr = collectNthOfType(anb, node);
|
|
441
|
+
if (arr.length) {
|
|
442
|
+
matched.push(...arr);
|
|
421
443
|
}
|
|
422
444
|
}
|
|
423
445
|
}
|
|
@@ -600,7 +622,7 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
600
622
|
while (i < l) {
|
|
601
623
|
const { name: itemName, value: itemValue } = attributes.item(i);
|
|
602
624
|
switch (astAttrPrefix) {
|
|
603
|
-
case '':
|
|
625
|
+
case '': {
|
|
604
626
|
if (astAttrLocalName === itemName) {
|
|
605
627
|
if (caseInsensitive) {
|
|
606
628
|
attrValues.push(itemValue.toLowerCase());
|
|
@@ -609,7 +631,8 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
609
631
|
}
|
|
610
632
|
}
|
|
611
633
|
break;
|
|
612
|
-
|
|
634
|
+
}
|
|
635
|
+
case '*': {
|
|
613
636
|
if (/:/.test(itemName)) {
|
|
614
637
|
if (itemName.endsWith(`:${astAttrLocalName}`)) {
|
|
615
638
|
if (caseInsensitive) {
|
|
@@ -626,7 +649,8 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
626
649
|
}
|
|
627
650
|
}
|
|
628
651
|
break;
|
|
629
|
-
|
|
652
|
+
}
|
|
653
|
+
default: {
|
|
630
654
|
if (/:/.test(itemName)) {
|
|
631
655
|
const [itemNamePrefix, itemNameLocalName] = itemName.split(':');
|
|
632
656
|
if (astAttrPrefix === itemNamePrefix &&
|
|
@@ -638,6 +662,7 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
638
662
|
}
|
|
639
663
|
}
|
|
640
664
|
}
|
|
665
|
+
}
|
|
641
666
|
}
|
|
642
667
|
i++;
|
|
643
668
|
}
|
|
@@ -685,15 +710,17 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
685
710
|
attrValue = astAttrStringValue;
|
|
686
711
|
}
|
|
687
712
|
switch (astMatcher) {
|
|
688
|
-
case null:
|
|
713
|
+
case null: {
|
|
689
714
|
res = node;
|
|
690
715
|
break;
|
|
691
|
-
|
|
716
|
+
}
|
|
717
|
+
case '=': {
|
|
692
718
|
if (typeof attrValue === 'string' && attrValues.includes(attrValue)) {
|
|
693
719
|
res = node;
|
|
694
720
|
}
|
|
695
721
|
break;
|
|
696
|
-
|
|
722
|
+
}
|
|
723
|
+
case '~=': {
|
|
697
724
|
if (typeof attrValue === 'string') {
|
|
698
725
|
for (const item of attrValues) {
|
|
699
726
|
const arr = item.split(/\s+/);
|
|
@@ -704,7 +731,8 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
704
731
|
}
|
|
705
732
|
}
|
|
706
733
|
break;
|
|
707
|
-
|
|
734
|
+
}
|
|
735
|
+
case '|=': {
|
|
708
736
|
if (typeof attrValue === 'string') {
|
|
709
737
|
const item = attrValues.find(v =>
|
|
710
738
|
(v === attrValue || v.startsWith(`${attrValue}-`))
|
|
@@ -714,7 +742,8 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
714
742
|
}
|
|
715
743
|
}
|
|
716
744
|
break;
|
|
717
|
-
|
|
745
|
+
}
|
|
746
|
+
case '^=': {
|
|
718
747
|
if (typeof attrValue === 'string') {
|
|
719
748
|
const item = attrValues.find(v => v.startsWith(`${attrValue}`));
|
|
720
749
|
if (item) {
|
|
@@ -722,7 +751,8 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
722
751
|
}
|
|
723
752
|
}
|
|
724
753
|
break;
|
|
725
|
-
|
|
754
|
+
}
|
|
755
|
+
case '$=': {
|
|
726
756
|
if (typeof attrValue === 'string') {
|
|
727
757
|
const item = attrValues.find(v => v.endsWith(`${attrValue}`));
|
|
728
758
|
if (item) {
|
|
@@ -730,7 +760,8 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
730
760
|
}
|
|
731
761
|
}
|
|
732
762
|
break;
|
|
733
|
-
|
|
763
|
+
}
|
|
764
|
+
case '*=': {
|
|
734
765
|
if (typeof attrValue === 'string') {
|
|
735
766
|
const item = attrValues.find(v => v.includes(`${attrValue}`));
|
|
736
767
|
if (item) {
|
|
@@ -738,9 +769,11 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
|
|
|
738
769
|
}
|
|
739
770
|
}
|
|
740
771
|
break;
|
|
741
|
-
|
|
772
|
+
}
|
|
773
|
+
default: {
|
|
742
774
|
throw new DOMException(`Unknown matcher ${astMatcher}`,
|
|
743
775
|
'SyntaxError');
|
|
776
|
+
}
|
|
744
777
|
}
|
|
745
778
|
}
|
|
746
779
|
}
|
|
@@ -876,7 +909,7 @@ const matchDirectionPseudoClass = (ast = {}, node = {}) => {
|
|
|
876
909
|
const { type: astType } = ast;
|
|
877
910
|
const { dir: nodeDir, localName, nodeType, type: inputType } = node;
|
|
878
911
|
let res;
|
|
879
|
-
if (astType === IDENTIFIER && nodeType === ELEMENT_NODE) {
|
|
912
|
+
if (astType === IDENTIFIER && nodeType === ELEMENT_NODE && isAttached(node)) {
|
|
880
913
|
const astName = unescapeSelector(ast.name);
|
|
881
914
|
let dir;
|
|
882
915
|
if (/^(?:ltr|rtl)$/.test(nodeDir)) {
|
|
@@ -1407,47 +1440,54 @@ const matchPseudoClassSelector = (
|
|
|
1407
1440
|
break;
|
|
1408
1441
|
}
|
|
1409
1442
|
case 'first-of-type': {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1443
|
+
if (parentNode) {
|
|
1444
|
+
const [node1] = collectNthOfType({
|
|
1445
|
+
a: 0,
|
|
1446
|
+
b: 1
|
|
1447
|
+
}, node);
|
|
1448
|
+
if (node1) {
|
|
1449
|
+
matched.push(node1);
|
|
1450
|
+
}
|
|
1416
1451
|
}
|
|
1417
1452
|
break;
|
|
1418
1453
|
}
|
|
1419
1454
|
case 'last-of-type': {
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1455
|
+
if (parentNode) {
|
|
1456
|
+
const [node1] = collectNthOfType({
|
|
1457
|
+
a: 0,
|
|
1458
|
+
b: 1,
|
|
1459
|
+
reverse: true
|
|
1460
|
+
}, node);
|
|
1461
|
+
if (node1) {
|
|
1462
|
+
matched.push(node1);
|
|
1463
|
+
}
|
|
1427
1464
|
}
|
|
1428
1465
|
break;
|
|
1429
1466
|
}
|
|
1430
1467
|
case 'only-of-type': {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1468
|
+
if (parentNode) {
|
|
1469
|
+
const [node1] = collectNthOfType({
|
|
1470
|
+
a: 0,
|
|
1471
|
+
b: 1
|
|
1472
|
+
}, node);
|
|
1473
|
+
const [node2] = collectNthOfType({
|
|
1474
|
+
a: 0,
|
|
1475
|
+
b: 1,
|
|
1476
|
+
reverse: true
|
|
1477
|
+
}, node);
|
|
1478
|
+
if (node1 === node && node2 === node) {
|
|
1479
|
+
matched.push(node);
|
|
1480
|
+
}
|
|
1442
1481
|
}
|
|
1443
1482
|
break;
|
|
1444
1483
|
}
|
|
1445
|
-
// legacy pseudo-elements
|
|
1446
1484
|
case 'after':
|
|
1447
1485
|
case 'before':
|
|
1448
1486
|
case 'first-letter':
|
|
1449
|
-
case 'first-line':
|
|
1487
|
+
case 'first-line': {
|
|
1488
|
+
// legacy pseudo-elements
|
|
1450
1489
|
break;
|
|
1490
|
+
}
|
|
1451
1491
|
case 'active':
|
|
1452
1492
|
case 'autofill':
|
|
1453
1493
|
case 'blank':
|
|
@@ -1508,12 +1548,14 @@ const matchPseudoElementSelector = (ast = {}, node = {}) => {
|
|
|
1508
1548
|
case 'placeholder':
|
|
1509
1549
|
case 'selection':
|
|
1510
1550
|
case 'slotted':
|
|
1511
|
-
case 'target-text':
|
|
1551
|
+
case 'target-text': {
|
|
1512
1552
|
throw new DOMException(`Unsupported pseudo-element ${astName}`,
|
|
1513
1553
|
'NotSupportedError');
|
|
1514
|
-
|
|
1554
|
+
}
|
|
1555
|
+
default: {
|
|
1515
1556
|
throw new DOMException(`Unknown pseudo-element ${astName}`,
|
|
1516
1557
|
'SyntaxError');
|
|
1558
|
+
}
|
|
1517
1559
|
}
|
|
1518
1560
|
}
|
|
1519
1561
|
};
|
|
@@ -1543,17 +1585,6 @@ class Matcher {
|
|
|
1543
1585
|
this.#warn = !!warn;
|
|
1544
1586
|
}
|
|
1545
1587
|
|
|
1546
|
-
/**
|
|
1547
|
-
* node is attached to document or detached
|
|
1548
|
-
* @returns {boolean} - result
|
|
1549
|
-
*/
|
|
1550
|
-
_isAttached() {
|
|
1551
|
-
const root = this.#document.documentElement;
|
|
1552
|
-
const posBit =
|
|
1553
|
-
this.#node.compareDocumentPosition(root) & DOCUMENT_POSITION_CONTAINS;
|
|
1554
|
-
return !!posBit;
|
|
1555
|
-
};
|
|
1556
|
-
|
|
1557
1588
|
/**
|
|
1558
1589
|
* match AST and node
|
|
1559
1590
|
* @param {object} ast - AST
|
|
@@ -1684,7 +1715,7 @@ class Matcher {
|
|
|
1684
1715
|
let res;
|
|
1685
1716
|
try {
|
|
1686
1717
|
let node;
|
|
1687
|
-
if (this
|
|
1718
|
+
if (isAttached(this.#node)) {
|
|
1688
1719
|
node = this.#document;
|
|
1689
1720
|
} else {
|
|
1690
1721
|
node = this.#node;
|
|
@@ -1800,6 +1831,7 @@ module.exports = {
|
|
|
1800
1831
|
collectNthOfType,
|
|
1801
1832
|
createSelectorForNode,
|
|
1802
1833
|
groupASTLeaves,
|
|
1834
|
+
isAttached,
|
|
1803
1835
|
isContentEditable,
|
|
1804
1836
|
isNamespaceDeclared,
|
|
1805
1837
|
matchAnPlusB,
|
package/src/js/parser.js
CHANGED
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
/* import */
|
|
7
|
-
const { generate, parse, toPlainObject, walk } = require('css-tree');
|
|
7
|
+
const { generate, findAll, parse, toPlainObject, walk } = require('css-tree');
|
|
8
8
|
const DOMException = require('./domexception.js');
|
|
9
9
|
|
|
10
10
|
/* constants */
|
|
11
|
-
const { SELECTOR } = require('./constant.js');
|
|
11
|
+
const { PSEUDO_CLASS_SELECTOR, SELECTOR } = require('./constant.js');
|
|
12
12
|
const CODE_POINT_UNIT = parseInt('10000', 16);
|
|
13
13
|
const HEX = 16;
|
|
14
14
|
const PAIR = 2;
|
|
15
15
|
const TYPE_FROM = 8;
|
|
16
16
|
const TYPE_TO = -1;
|
|
17
17
|
|
|
18
|
+
/* regexp */
|
|
19
|
+
const PSEUDO_FUNC = /^(?:(?:ha|i)s|not|where)$/;
|
|
20
|
+
|
|
18
21
|
/**
|
|
19
22
|
* preprocess
|
|
20
23
|
* @see https://drafts.csswg.org/css-syntax-3/#input-preprocessing
|
|
@@ -94,20 +97,34 @@ const parseSelector = selector => {
|
|
|
94
97
|
const walkAST = (ast = {}) => {
|
|
95
98
|
const selectors = new Set();
|
|
96
99
|
const opt = {
|
|
100
|
+
visit: SELECTOR,
|
|
97
101
|
enter: branch => {
|
|
98
|
-
|
|
99
|
-
selectors.add(branch.children);
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
leave: branch => {
|
|
103
|
-
let skip;
|
|
104
|
-
if (branch.type === SELECTOR) {
|
|
105
|
-
skip = walkAST.skip;
|
|
106
|
-
}
|
|
107
|
-
return skip;
|
|
102
|
+
selectors.add(branch.children);
|
|
108
103
|
}
|
|
109
104
|
};
|
|
110
105
|
walk(ast, opt);
|
|
106
|
+
findAll(ast, (node, item, list) => {
|
|
107
|
+
if (node.type === PSEUDO_CLASS_SELECTOR && PSEUDO_FUNC.test(node.name) &&
|
|
108
|
+
item) {
|
|
109
|
+
for (const i of list) {
|
|
110
|
+
const { children, name, type } = i;
|
|
111
|
+
if (type === PSEUDO_CLASS_SELECTOR && PSEUDO_FUNC.test(name) &&
|
|
112
|
+
children) {
|
|
113
|
+
// SelectorList
|
|
114
|
+
for (const j of children) {
|
|
115
|
+
const { children: grandChildren } = j;
|
|
116
|
+
// Selector
|
|
117
|
+
for (const k of grandChildren) {
|
|
118
|
+
const { children: greatGrandChildren } = k;
|
|
119
|
+
if (selectors.has(greatGrandChildren)) {
|
|
120
|
+
selectors.delete(greatGrandChildren);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
111
128
|
return [...selectors];
|
|
112
129
|
};
|
|
113
130
|
|
package/types/js/matcher.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export class Matcher {
|
|
|
2
2
|
constructor(selector: string, refPoint: object, opt?: {
|
|
3
3
|
warn?: object;
|
|
4
4
|
});
|
|
5
|
-
_isAttached(): boolean;
|
|
6
5
|
_match(ast: object, node: object): Array<object | undefined>;
|
|
7
6
|
_getMatchedNodes(branch?: Array<object>, node?: object): Array<object | undefined>;
|
|
8
7
|
_find(ast: object, node: object): Array<object | undefined>;
|
|
@@ -25,6 +24,7 @@ export function collectNthOfType(anb?: {
|
|
|
25
24
|
}, node?: object): Array<object | undefined>;
|
|
26
25
|
export function createSelectorForNode(node?: object): string | null;
|
|
27
26
|
export function groupASTLeaves(branch?: Array<object>): Array<object>;
|
|
27
|
+
export function isAttached(node?: object): boolean;
|
|
28
28
|
export function isContentEditable(node?: object): boolean;
|
|
29
29
|
export function isNamespaceDeclared(ns?: string, node?: object): boolean;
|
|
30
30
|
export function matchAnPlusB(nthName: string, ast?: object, node?: object): Array<object | undefined>;
|