happy-dom 9.9.2 → 9.10.0
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.
Potentially problematic release.
This version of happy-dom might be problematic. Click here for more details.
- package/lib/css/declaration/utilities/CSSStyleDeclarationElementStyle.js +1 -1
- package/lib/css/declaration/utilities/CSSStyleDeclarationElementStyle.js.map +1 -1
- package/lib/nodes/element/Element.d.ts.map +1 -1
- package/lib/nodes/element/Element.js +2 -15
- package/lib/nodes/element/Element.js.map +1 -1
- package/lib/nodes/node/Node.d.ts +0 -8
- package/lib/nodes/node/Node.d.ts.map +1 -1
- package/lib/nodes/node/Node.js +1 -23
- package/lib/nodes/node/Node.js.map +1 -1
- package/lib/query-selector/ISelectorAttribute.d.ts +6 -0
- package/lib/query-selector/ISelectorAttribute.d.ts.map +1 -0
- package/lib/query-selector/ISelectorAttribute.js +3 -0
- package/lib/query-selector/ISelectorAttribute.js.map +1 -0
- package/lib/query-selector/ISelectorMatch.d.ts +4 -0
- package/lib/query-selector/ISelectorMatch.d.ts.map +1 -0
- package/lib/query-selector/ISelectorMatch.js +3 -0
- package/lib/query-selector/ISelectorMatch.js.map +1 -0
- package/lib/query-selector/QuerySelector.d.ts +21 -31
- package/lib/query-selector/QuerySelector.d.ts.map +1 -1
- package/lib/query-selector/QuerySelector.js +123 -131
- package/lib/query-selector/QuerySelector.js.map +1 -1
- package/lib/query-selector/SelectorCombinatorEnum.d.ts +7 -0
- package/lib/query-selector/SelectorCombinatorEnum.d.ts.map +1 -0
- package/lib/query-selector/SelectorCombinatorEnum.js +10 -0
- package/lib/query-selector/SelectorCombinatorEnum.js.map +1 -0
- package/lib/query-selector/SelectorItem.d.ts +41 -56
- package/lib/query-selector/SelectorItem.d.ts.map +1 -1
- package/lib/query-selector/SelectorItem.js +194 -220
- package/lib/query-selector/SelectorItem.js.map +1 -1
- package/lib/query-selector/SelectorParser.d.ts +21 -0
- package/lib/query-selector/SelectorParser.d.ts.map +1 -0
- package/lib/query-selector/SelectorParser.js +154 -0
- package/lib/query-selector/SelectorParser.js.map +1 -0
- package/package.json +1 -1
- package/src/css/declaration/utilities/CSSStyleDeclarationElementStyle.ts +2 -2
- package/src/nodes/element/Element.ts +3 -17
- package/src/nodes/node/Node.ts +1 -25
- package/src/query-selector/ISelectorAttribute.ts +5 -0
- package/src/query-selector/ISelectorMatch.ts +3 -0
- package/src/query-selector/QuerySelector.ts +187 -170
- package/src/query-selector/SelectorCombinatorEnum.ts +7 -0
- package/src/query-selector/SelectorItem.ts +238 -264
- package/src/query-selector/SelectorParser.ts +148 -0
@@ -1,12 +1,13 @@
|
|
1
|
-
import Element from '../nodes/element/Element';
|
2
1
|
import IElement from '../nodes/element/IElement';
|
3
|
-
import INode from '../nodes/node/INode';
|
4
|
-
import Node from '../nodes/node/Node';
|
5
2
|
import INodeList from '../nodes/node/INodeList';
|
6
3
|
import SelectorItem from './SelectorItem';
|
7
4
|
import NodeList from '../nodes/node/NodeList';
|
8
|
-
|
9
|
-
|
5
|
+
import NodeTypeEnum from '../nodes/node/NodeTypeEnum';
|
6
|
+
import SelectorCombinatorEnum from './SelectorCombinatorEnum';
|
7
|
+
import IDocument from '../nodes/document/IDocument';
|
8
|
+
import IDocumentFragment from '../nodes/document-fragment/IDocumentFragment';
|
9
|
+
import SelectorParser from './SelectorParser';
|
10
|
+
import ISelectorMatch from './ISelectorMatch';
|
10
11
|
|
11
12
|
/**
|
12
13
|
* Utility for query selection in an HTML element.
|
@@ -21,26 +22,36 @@ export default class QuerySelector {
|
|
21
22
|
* @param selector Selector.
|
22
23
|
* @returns HTML elements.
|
23
24
|
*/
|
24
|
-
public static querySelectorAll(
|
25
|
-
|
25
|
+
public static querySelectorAll(
|
26
|
+
node: IElement | IDocument | IDocumentFragment,
|
27
|
+
selector: string
|
28
|
+
): INodeList<IElement> {
|
29
|
+
const nodeList = new NodeList<IElement>();
|
30
|
+
const allMatches = {};
|
26
31
|
|
27
32
|
if (selector === '') {
|
28
33
|
throw new Error(
|
29
34
|
"Failed to execute 'querySelectorAll' on 'Element': The provided selector is empty."
|
30
35
|
);
|
31
36
|
}
|
37
|
+
|
32
38
|
if (selector === null || selector === undefined) {
|
33
|
-
return
|
39
|
+
return nodeList;
|
34
40
|
}
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
|
42
|
+
for (const items of SelectorParser.getSelectorGroups(selector)) {
|
43
|
+
const matches =
|
44
|
+
node.nodeType === NodeTypeEnum.elementNode
|
45
|
+
? this.findAll(<IElement>node, [<IElement>node], items)
|
46
|
+
: this.findAll(null, node.children, items);
|
47
|
+
Object.assign(allMatches, matches);
|
48
|
+
}
|
49
|
+
|
50
|
+
for (const key of Object.keys(allMatches).sort()) {
|
51
|
+
nodeList.push(allMatches[key]);
|
41
52
|
}
|
42
53
|
|
43
|
-
return
|
54
|
+
return nodeList;
|
44
55
|
}
|
45
56
|
|
46
57
|
/**
|
@@ -50,17 +61,25 @@ export default class QuerySelector {
|
|
50
61
|
* @param selector Selector.
|
51
62
|
* @returns HTML element.
|
52
63
|
*/
|
53
|
-
public static querySelector(
|
64
|
+
public static querySelector(
|
65
|
+
node: IElement | IDocument | IDocumentFragment,
|
66
|
+
selector: string
|
67
|
+
): IElement {
|
54
68
|
if (selector === '') {
|
55
69
|
throw new Error(
|
56
70
|
"Failed to execute 'querySelector' on 'Element': The provided selector is empty."
|
57
71
|
);
|
58
72
|
}
|
73
|
+
|
59
74
|
if (selector === null || selector === undefined) {
|
60
75
|
return null;
|
61
76
|
}
|
62
|
-
|
63
|
-
|
77
|
+
|
78
|
+
for (const items of SelectorParser.getSelectorGroups(selector)) {
|
79
|
+
const match =
|
80
|
+
node.nodeType === NodeTypeEnum.elementNode
|
81
|
+
? this.findFirst(<IElement>node, [<IElement>node], items)
|
82
|
+
: this.findFirst(null, node.children, items);
|
64
83
|
|
65
84
|
if (match) {
|
66
85
|
return match;
|
@@ -71,115 +90,153 @@ export default class QuerySelector {
|
|
71
90
|
}
|
72
91
|
|
73
92
|
/**
|
74
|
-
* Checks if
|
93
|
+
* Checks if an element matches a selector and returns priority weight.
|
75
94
|
*
|
76
|
-
* @param
|
77
|
-
* @param selector Selector.
|
95
|
+
* @param element Element to match.
|
96
|
+
* @param selector Selector to match with.
|
78
97
|
* @returns Result.
|
79
98
|
*/
|
80
|
-
public static match(
|
81
|
-
for (const
|
82
|
-
const result = this.
|
99
|
+
public static match(element: IElement, selector: string): ISelectorMatch | null {
|
100
|
+
for (const items of SelectorParser.getSelectorGroups(selector)) {
|
101
|
+
const result = this.matchSelector(element, element, items.reverse());
|
83
102
|
|
84
|
-
if (result
|
103
|
+
if (result) {
|
85
104
|
return result;
|
86
105
|
}
|
87
106
|
}
|
88
107
|
|
89
|
-
return
|
108
|
+
return null;
|
90
109
|
}
|
91
110
|
|
92
111
|
/**
|
93
112
|
* Checks if a node matches a selector.
|
94
113
|
*
|
95
|
-
* @param
|
96
|
-
* @param
|
97
|
-
* @param
|
114
|
+
* @param targetElement Target element.
|
115
|
+
* @param currentElement Current element.
|
116
|
+
* @param selectorItems Selector items.
|
98
117
|
* @param [priorityWeight] Priority weight.
|
99
118
|
* @returns Result.
|
100
119
|
*/
|
101
|
-
private static
|
102
|
-
|
103
|
-
|
104
|
-
|
120
|
+
private static matchSelector(
|
121
|
+
targetElement: IElement,
|
122
|
+
currentElement: IElement,
|
123
|
+
selectorItems: SelectorItem[],
|
105
124
|
priorityWeight = 0
|
106
|
-
): {
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
125
|
+
): ISelectorMatch | null {
|
126
|
+
const selectorItem = selectorItems[0];
|
127
|
+
const result = selectorItem.match(currentElement);
|
128
|
+
|
129
|
+
if (result) {
|
130
|
+
if (selectorItems.length === 1) {
|
131
|
+
return {
|
132
|
+
priorityWeight: priorityWeight + result.priorityWeight
|
133
|
+
};
|
115
134
|
}
|
116
|
-
}
|
117
135
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
136
|
+
switch (selectorItem.combinator) {
|
137
|
+
case SelectorCombinatorEnum.adjacentSibling:
|
138
|
+
if (currentElement.previousElementSibling) {
|
139
|
+
const match = this.matchSelector(
|
140
|
+
targetElement,
|
141
|
+
currentElement.previousElementSibling,
|
142
|
+
selectorItems.slice(1),
|
143
|
+
priorityWeight + result.priorityWeight
|
144
|
+
);
|
124
145
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
146
|
+
if (match) {
|
147
|
+
return match;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
break;
|
151
|
+
case SelectorCombinatorEnum.child:
|
152
|
+
case SelectorCombinatorEnum.descendant:
|
153
|
+
if (currentElement.parentElement) {
|
154
|
+
const match = this.matchSelector(
|
155
|
+
targetElement,
|
156
|
+
currentElement.parentElement,
|
157
|
+
selectorItems.slice(1),
|
158
|
+
priorityWeight + result.priorityWeight
|
159
|
+
);
|
160
|
+
if (match) {
|
161
|
+
return match;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
break;
|
165
|
+
}
|
130
166
|
}
|
131
167
|
|
132
|
-
if (
|
133
|
-
|
168
|
+
if (
|
169
|
+
selectorItem.combinator === SelectorCombinatorEnum.descendant &&
|
170
|
+
targetElement !== currentElement &&
|
171
|
+
currentElement.parentElement
|
172
|
+
) {
|
173
|
+
return this.matchSelector(
|
174
|
+
targetElement,
|
175
|
+
currentElement.parentElement,
|
176
|
+
selectorItems,
|
177
|
+
priorityWeight
|
178
|
+
);
|
134
179
|
}
|
135
180
|
|
136
|
-
return
|
137
|
-
isDirectChild ? currentNode.parentElement : targetNode,
|
138
|
-
currentNode.parentElement,
|
139
|
-
result.matches ? selectorParts.slice(1) : selectorParts,
|
140
|
-
priorityWeight + result.priorityWeight
|
141
|
-
);
|
181
|
+
return null;
|
142
182
|
}
|
143
183
|
|
144
184
|
/**
|
145
185
|
* Finds elements based on a query selector for a part of a list of selectors separated with comma.
|
146
186
|
*
|
147
|
-
* @param
|
148
|
-
* @param
|
149
|
-
* @param
|
150
|
-
* @param [
|
151
|
-
* @returns
|
187
|
+
* @param rootElement Root element.
|
188
|
+
* @param children Child elements.
|
189
|
+
* @param selectorItems Selector items.
|
190
|
+
* @param [documentPosition] Document position of the element.
|
191
|
+
* @returns Document position and element map.
|
152
192
|
*/
|
153
193
|
private static findAll(
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
):
|
159
|
-
const
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
if (
|
168
|
-
if (
|
169
|
-
if (
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
194
|
+
rootElement: IElement,
|
195
|
+
children: IElement[],
|
196
|
+
selectorItems: SelectorItem[],
|
197
|
+
documentPosition?: string
|
198
|
+
): { [documentPosition: string]: IElement } {
|
199
|
+
const selectorItem = selectorItems[0];
|
200
|
+
const nextSelectorItem = selectorItems[1];
|
201
|
+
const matched: { [documentPosition: string]: IElement } = {};
|
202
|
+
|
203
|
+
for (let i = 0, max = children.length; i < max; i++) {
|
204
|
+
const child = children[i];
|
205
|
+
const position = (documentPosition ? documentPosition + '>' : '') + i;
|
206
|
+
|
207
|
+
if (selectorItem.match(child)) {
|
208
|
+
if (!nextSelectorItem) {
|
209
|
+
if (rootElement !== child) {
|
210
|
+
matched[position] = child;
|
211
|
+
}
|
212
|
+
} else {
|
213
|
+
switch (nextSelectorItem.combinator) {
|
214
|
+
case SelectorCombinatorEnum.adjacentSibling:
|
215
|
+
if (child.nextElementSibling) {
|
216
|
+
Object.assign(
|
217
|
+
matched,
|
218
|
+
this.findAll(
|
219
|
+
rootElement,
|
220
|
+
[child.nextElementSibling],
|
221
|
+
selectorItems.slice(1),
|
222
|
+
position
|
223
|
+
)
|
224
|
+
);
|
225
|
+
}
|
226
|
+
break;
|
227
|
+
case SelectorCombinatorEnum.descendant:
|
228
|
+
case SelectorCombinatorEnum.child:
|
229
|
+
Object.assign(
|
230
|
+
matched,
|
231
|
+
this.findAll(rootElement, child.children, selectorItems.slice(1), position)
|
232
|
+
);
|
233
|
+
break;
|
177
234
|
}
|
178
235
|
}
|
179
236
|
}
|
180
237
|
|
181
|
-
if (
|
182
|
-
matched
|
238
|
+
if (selectorItem.combinator === SelectorCombinatorEnum.descendant && child.children.length) {
|
239
|
+
Object.assign(matched, this.findAll(rootElement, child.children, selectorItems, position));
|
183
240
|
}
|
184
241
|
}
|
185
242
|
|
@@ -189,99 +246,59 @@ export default class QuerySelector {
|
|
189
246
|
/**
|
190
247
|
* Finds an element based on a query selector for a part of a list of selectors separated with comma.
|
191
248
|
*
|
192
|
-
* @param
|
193
|
-
* @param
|
194
|
-
* @param
|
195
|
-
* @param selectorParts
|
196
|
-
* @param [selectorItem] Selector item.
|
249
|
+
* @param rootElement Root element.
|
250
|
+
* @param children Child elements.
|
251
|
+
* @param selectorItems Selector items.
|
197
252
|
* @returns HTML element.
|
198
253
|
*/
|
199
254
|
private static findFirst(
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
selectorItem?: SelectorItem
|
255
|
+
rootElement: IElement,
|
256
|
+
children: IElement[],
|
257
|
+
selectorItems: SelectorItem[]
|
204
258
|
): IElement {
|
205
|
-
const
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
if (selectorParts.length === 1) {
|
214
|
-
if (rootNode !== node) {
|
215
|
-
return <Element>node;
|
259
|
+
const selectorItem = selectorItems[0];
|
260
|
+
const nextSelectorItem = selectorItems[1];
|
261
|
+
|
262
|
+
for (const child of children) {
|
263
|
+
if (selectorItem.match(child)) {
|
264
|
+
if (!nextSelectorItem) {
|
265
|
+
if (rootElement !== child) {
|
266
|
+
return child;
|
216
267
|
}
|
217
268
|
} else {
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
269
|
+
switch (nextSelectorItem.combinator) {
|
270
|
+
case SelectorCombinatorEnum.adjacentSibling:
|
271
|
+
if (child.nextElementSibling) {
|
272
|
+
const match = this.findFirst(
|
273
|
+
rootElement,
|
274
|
+
[child.nextElementSibling],
|
275
|
+
selectorItems.slice(1)
|
276
|
+
);
|
277
|
+
if (match) {
|
278
|
+
return match;
|
279
|
+
}
|
280
|
+
}
|
281
|
+
break;
|
282
|
+
case SelectorCombinatorEnum.descendant:
|
283
|
+
case SelectorCombinatorEnum.child:
|
284
|
+
const match = this.findFirst(rootElement, child.children, selectorItems.slice(1));
|
285
|
+
if (match) {
|
286
|
+
return match;
|
287
|
+
}
|
288
|
+
break;
|
226
289
|
}
|
227
290
|
}
|
228
291
|
}
|
229
292
|
|
230
|
-
if (
|
231
|
-
const
|
293
|
+
if (selectorItem.combinator === SelectorCombinatorEnum.descendant && child.children.length) {
|
294
|
+
const match = this.findFirst(rootElement, child.children, selectorItems);
|
232
295
|
|
233
|
-
if (
|
234
|
-
return
|
296
|
+
if (match) {
|
297
|
+
return match;
|
235
298
|
}
|
236
299
|
}
|
237
300
|
}
|
238
301
|
|
239
302
|
return null;
|
240
303
|
}
|
241
|
-
|
242
|
-
/**
|
243
|
-
* Splits a selector string into groups and parts.
|
244
|
-
*
|
245
|
-
* @param selector Selector.
|
246
|
-
* @returns HTML element.
|
247
|
-
*/
|
248
|
-
private static getSelectorParts(selector: string): string[][] {
|
249
|
-
if (selector === '*' || (!selector.includes(',') && !selector.includes(' '))) {
|
250
|
-
return [[selector]];
|
251
|
-
}
|
252
|
-
|
253
|
-
const regexp = new RegExp(SELECTOR_PART_REGEXP);
|
254
|
-
const groups = [];
|
255
|
-
let currentSelector = '';
|
256
|
-
let parts = [];
|
257
|
-
let match;
|
258
|
-
|
259
|
-
while ((match = regexp.exec(selector))) {
|
260
|
-
if (match[2]) {
|
261
|
-
const trimmed = match[2].trim();
|
262
|
-
|
263
|
-
parts.push(currentSelector);
|
264
|
-
currentSelector = '';
|
265
|
-
|
266
|
-
if (trimmed === ',') {
|
267
|
-
groups.push(parts);
|
268
|
-
parts = [];
|
269
|
-
} else if (trimmed === '>') {
|
270
|
-
parts.push('>');
|
271
|
-
}
|
272
|
-
} else if (match[1]) {
|
273
|
-
currentSelector += match[1];
|
274
|
-
}
|
275
|
-
}
|
276
|
-
|
277
|
-
if (currentSelector !== '') {
|
278
|
-
parts.push(currentSelector);
|
279
|
-
}
|
280
|
-
|
281
|
-
if (parts.length > 0) {
|
282
|
-
groups.push(parts);
|
283
|
-
}
|
284
|
-
|
285
|
-
return groups;
|
286
|
-
}
|
287
304
|
}
|