@asamuzakjp/dom-selector 7.0.5 → 7.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/js/finder.js +30 -10
- package/src/js/matcher.js +30 -12
- package/types/js/finder.d.ts +1 -0
package/package.json
CHANGED
package/src/js/finder.js
CHANGED
|
@@ -114,6 +114,7 @@ export class Finder {
|
|
|
114
114
|
#documentURL;
|
|
115
115
|
#event;
|
|
116
116
|
#eventHandlers;
|
|
117
|
+
#filterLeavesCache;
|
|
117
118
|
#focus;
|
|
118
119
|
#invalidate;
|
|
119
120
|
#invalidateResults;
|
|
@@ -160,6 +161,7 @@ export class Finder {
|
|
|
160
161
|
handler: this._handleMouseEvent
|
|
161
162
|
}
|
|
162
163
|
]);
|
|
164
|
+
this.#filterLeavesCache = new WeakMap();
|
|
163
165
|
this._registerEventListeners();
|
|
164
166
|
this.clearResults(true);
|
|
165
167
|
}
|
|
@@ -233,6 +235,7 @@ export class Finder {
|
|
|
233
235
|
this.#invalidateResults = new WeakMap();
|
|
234
236
|
if (all) {
|
|
235
237
|
this.#results = new WeakMap();
|
|
238
|
+
this.#filterLeavesCache = new WeakMap();
|
|
236
239
|
}
|
|
237
240
|
};
|
|
238
241
|
|
|
@@ -462,8 +465,7 @@ export class Finder {
|
|
|
462
465
|
*/
|
|
463
466
|
_getFilteredChildren = (parentNode, selectorBranches, opt) => {
|
|
464
467
|
const children = [];
|
|
465
|
-
|
|
466
|
-
let childNode = walker.firstChild();
|
|
468
|
+
let childNode = parentNode.firstElementChild;
|
|
467
469
|
while (childNode) {
|
|
468
470
|
if (selectorBranches) {
|
|
469
471
|
let isMatch = false;
|
|
@@ -485,7 +487,7 @@ export class Finder {
|
|
|
485
487
|
} else {
|
|
486
488
|
children.push(childNode);
|
|
487
489
|
}
|
|
488
|
-
childNode =
|
|
490
|
+
childNode = childNode.nextElementSibling;
|
|
489
491
|
}
|
|
490
492
|
return children;
|
|
491
493
|
};
|
|
@@ -555,8 +557,7 @@ export class Finder {
|
|
|
555
557
|
return new Set();
|
|
556
558
|
}
|
|
557
559
|
const typedSiblings = [];
|
|
558
|
-
|
|
559
|
-
let sibling = walker.firstChild();
|
|
560
|
+
let sibling = parentNode.firstElementChild;
|
|
560
561
|
while (sibling) {
|
|
561
562
|
if (
|
|
562
563
|
sibling.localName === node.localName &&
|
|
@@ -565,7 +566,7 @@ export class Finder {
|
|
|
565
566
|
) {
|
|
566
567
|
typedSiblings.push(sibling);
|
|
567
568
|
}
|
|
568
|
-
sibling =
|
|
569
|
+
sibling = sibling.nextElementSibling;
|
|
569
570
|
}
|
|
570
571
|
const matchedNodes = filterNodesByAnB(typedSiblings, anb);
|
|
571
572
|
return new Set(matchedNodes);
|
|
@@ -2009,6 +2010,21 @@ export class Finder {
|
|
|
2009
2010
|
return bool;
|
|
2010
2011
|
};
|
|
2011
2012
|
|
|
2013
|
+
/**
|
|
2014
|
+
* Returns a cached slice of the leaves array (excluding the first item).
|
|
2015
|
+
* @private
|
|
2016
|
+
* @param {Array.<object>} leaves - The original AST leaves array.
|
|
2017
|
+
* @returns {Array.<object>} The filtered leaves.
|
|
2018
|
+
*/
|
|
2019
|
+
_getFilterLeaves = leaves => {
|
|
2020
|
+
if (this.#filterLeavesCache.has(leaves)) {
|
|
2021
|
+
return this.#filterLeavesCache.get(leaves);
|
|
2022
|
+
}
|
|
2023
|
+
const filterLeaves = leaves.slice(1);
|
|
2024
|
+
this.#filterLeavesCache.set(leaves, filterLeaves);
|
|
2025
|
+
return filterLeaves;
|
|
2026
|
+
};
|
|
2027
|
+
|
|
2012
2028
|
/**
|
|
2013
2029
|
* Traverses all descendant nodes and collects matches.
|
|
2014
2030
|
* @private
|
|
@@ -2040,7 +2056,8 @@ export class Finder {
|
|
|
2040
2056
|
* @returns {Set.<object>} A collection of matched nodes.
|
|
2041
2057
|
*/
|
|
2042
2058
|
_findDescendantNodes = (leaves, baseNode, opt) => {
|
|
2043
|
-
const [leaf
|
|
2059
|
+
const [leaf] = leaves;
|
|
2060
|
+
const filterLeaves = this._getFilterLeaves(leaves);
|
|
2044
2061
|
const { type: leafType } = leaf;
|
|
2045
2062
|
switch (leafType) {
|
|
2046
2063
|
case ID_SELECTOR: {
|
|
@@ -2362,7 +2379,8 @@ export class Finder {
|
|
|
2362
2379
|
*/
|
|
2363
2380
|
_findEntryNodesForId = (twig, targetType, opt = {}) => {
|
|
2364
2381
|
const { leaves } = twig;
|
|
2365
|
-
const [leaf
|
|
2382
|
+
const [leaf] = leaves;
|
|
2383
|
+
const filterLeaves = this._getFilterLeaves(leaves);
|
|
2366
2384
|
const { complex, precede } = opt;
|
|
2367
2385
|
let nodes = [];
|
|
2368
2386
|
let filtered = false;
|
|
@@ -2455,7 +2473,8 @@ export class Finder {
|
|
|
2455
2473
|
*/
|
|
2456
2474
|
_findEntryNodesForOther = (twig, targetType, opt = {}) => {
|
|
2457
2475
|
const { leaves } = twig;
|
|
2458
|
-
const [leaf
|
|
2476
|
+
const [leaf] = leaves;
|
|
2477
|
+
const filterLeaves = this._getFilterLeaves(leaves);
|
|
2459
2478
|
const { complex, precede } = opt;
|
|
2460
2479
|
let nodes = [];
|
|
2461
2480
|
let filtered = false;
|
|
@@ -2531,7 +2550,8 @@ export class Finder {
|
|
|
2531
2550
|
*/
|
|
2532
2551
|
_findEntryNodes = (twig, targetType, opt = {}) => {
|
|
2533
2552
|
const { leaves } = twig;
|
|
2534
|
-
const [leaf
|
|
2553
|
+
const [leaf] = leaves;
|
|
2554
|
+
const filterLeaves = this._getFilterLeaves(leaves);
|
|
2535
2555
|
const { complex = false, dir = DIR_PREV } = opt;
|
|
2536
2556
|
const precede =
|
|
2537
2557
|
dir === DIR_NEXT &&
|
package/src/js/matcher.js
CHANGED
|
@@ -136,6 +136,22 @@ export const matchDirectionPseudoClass = (ast, node) => {
|
|
|
136
136
|
* @returns {boolean} - True if the language matches, otherwise false.
|
|
137
137
|
*/
|
|
138
138
|
export const matchLanguagePseudoClass = (ast, node) => {
|
|
139
|
+
// Get the effective language attribute for the current node.
|
|
140
|
+
const elementLang = getLanguageAttribute(node);
|
|
141
|
+
// If the element has no language, it cannot match a specific pattern.
|
|
142
|
+
if (elementLang === null) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
// Use cached regex.
|
|
146
|
+
if (ast._langRegex !== undefined) {
|
|
147
|
+
if (ast._langPattern === '*') {
|
|
148
|
+
return elementLang !== '';
|
|
149
|
+
}
|
|
150
|
+
if (ast._langRegex === null) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
return ast._langRegex.test(elementLang);
|
|
154
|
+
}
|
|
139
155
|
const { name, type, value } = ast;
|
|
140
156
|
let langPattern;
|
|
141
157
|
// Determine the language pattern from the AST.
|
|
@@ -144,35 +160,31 @@ export const matchLanguagePseudoClass = (ast, node) => {
|
|
|
144
160
|
} else if (type === IDENT && name) {
|
|
145
161
|
langPattern = unescapeSelector(name);
|
|
146
162
|
}
|
|
163
|
+
// Cache lang pattern.
|
|
164
|
+
ast._langPattern = langPattern;
|
|
147
165
|
// If no valid language pattern is provided, it cannot match.
|
|
148
166
|
if (typeof langPattern !== 'string') {
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
// Get the effective language attribute for the current node.
|
|
152
|
-
const elementLang = getLanguageAttribute(node);
|
|
153
|
-
// If the element has no language, it cannot match a specific pattern.
|
|
154
|
-
if (elementLang === null) {
|
|
167
|
+
ast._langRegex = null;
|
|
155
168
|
return false;
|
|
156
169
|
}
|
|
157
170
|
// Handle the universal selector '*' for :lang.
|
|
158
171
|
if (langPattern === '*') {
|
|
159
|
-
|
|
172
|
+
ast._langRegex = null;
|
|
160
173
|
return elementLang !== '';
|
|
161
174
|
}
|
|
162
175
|
// Validate the provided language pattern structure.
|
|
163
176
|
if (!REG_LANG_VALID.test(langPattern)) {
|
|
177
|
+
ast._langRegex = null;
|
|
164
178
|
return false;
|
|
165
179
|
}
|
|
166
180
|
// Build a regex for extended language range matching.
|
|
167
181
|
let matcherRegex;
|
|
168
182
|
if (langPattern.indexOf('-') > -1) {
|
|
169
|
-
// Handle complex patterns with wildcards and sub-tags (e.g., '*-US').
|
|
170
183
|
const [langMain, langSub, ...langRest] = langPattern.split('-');
|
|
171
184
|
const extendedMain =
|
|
172
185
|
langMain === '*' ? `${ALPHA_NUM}${LANG_PART}` : `${langMain}${LANG_PART}`;
|
|
173
186
|
const extendedSub = `-${langSub}${LANG_PART}`;
|
|
174
187
|
let extendedRest = '';
|
|
175
|
-
// Use a standard for loop for performance as per the rules.
|
|
176
188
|
for (let i = 0; i < langRest.length; i++) {
|
|
177
189
|
extendedRest += `-${langRest[i]}${LANG_PART}`;
|
|
178
190
|
}
|
|
@@ -181,9 +193,9 @@ export const matchLanguagePseudoClass = (ast, node) => {
|
|
|
181
193
|
'i'
|
|
182
194
|
);
|
|
183
195
|
} else {
|
|
184
|
-
// Handle simple language patterns (e.g., 'en').
|
|
185
196
|
matcherRegex = new RegExp(`^${langPattern}${LANG_PART}$`, 'i');
|
|
186
197
|
}
|
|
198
|
+
ast._langRegex = matcherRegex;
|
|
187
199
|
// Test the element's language against the constructed regex.
|
|
188
200
|
return matcherRegex.test(elementLang);
|
|
189
201
|
};
|
|
@@ -452,9 +464,15 @@ export const matchAttributeSelector = (ast, node, opt = {}) => {
|
|
|
452
464
|
}
|
|
453
465
|
case '~=': {
|
|
454
466
|
if (attrValue && typeof attrValue === 'string') {
|
|
467
|
+
if (/\s/.test(attrValue)) {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
if (ast._tildeTarget === undefined) {
|
|
471
|
+
ast._tildeTarget = ` ${attrValue} `;
|
|
472
|
+
}
|
|
473
|
+
const target = ast._tildeTarget;
|
|
455
474
|
for (const value of attrValues) {
|
|
456
|
-
|
|
457
|
-
if (item.has(attrValue)) {
|
|
475
|
+
if (` ${value.replace(/[\t\r\n\f]/g, ' ')} `.includes(target)) {
|
|
458
476
|
return true;
|
|
459
477
|
}
|
|
460
478
|
}
|
package/types/js/finder.d.ts
CHANGED