happy-dom 9.8.2 → 9.8.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.
Potentially problematic release.
This version of happy-dom might be problematic. Click here for more details.
- package/lib/nodes/document/Document.d.ts +1 -1
- package/lib/nodes/document/Document.d.ts.map +1 -1
- package/lib/nodes/document/Document.js +6 -6
- package/lib/nodes/document/Document.js.map +1 -1
- package/lib/nodes/document-fragment/DocumentFragment.d.ts +1 -1
- package/lib/nodes/document-fragment/DocumentFragment.d.ts.map +1 -1
- package/lib/nodes/document-fragment/DocumentFragment.js +6 -6
- package/lib/nodes/document-fragment/DocumentFragment.js.map +1 -1
- package/lib/nodes/element/Element.js +6 -6
- package/lib/nodes/element/Element.js.map +1 -1
- package/lib/nodes/element/ElementUtility.d.ts +20 -9
- package/lib/nodes/element/ElementUtility.d.ts.map +1 -1
- package/lib/nodes/element/ElementUtility.js +54 -22
- package/lib/nodes/element/ElementUtility.js.map +1 -1
- package/lib/nodes/html-select-element/HTMLOptionsCollection.js.map +1 -1
- package/lib/nodes/node/Node.d.ts +1 -1
- package/lib/nodes/node/Node.d.ts.map +1 -1
- package/lib/nodes/node/Node.js +4 -111
- package/lib/nodes/node/Node.js.map +1 -1
- package/lib/nodes/node/NodeUtility.d.ts +37 -12
- package/lib/nodes/node/NodeUtility.d.ts.map +1 -1
- package/lib/nodes/node/NodeUtility.js +174 -30
- package/lib/nodes/node/NodeUtility.js.map +1 -1
- package/package.json +1 -1
- package/src/nodes/document/Document.ts +9 -9
- package/src/nodes/document-fragment/DocumentFragment.ts +9 -9
- package/src/nodes/element/Element.ts +6 -6
- package/src/nodes/element/ElementUtility.ts +77 -27
- package/src/nodes/html-select-element/HTMLOptionsCollection.ts +1 -1
- package/src/nodes/node/Node.ts +5 -142
- package/src/nodes/node/NodeUtility.ts +239 -37
@@ -6,63 +6,229 @@ import IElement from '../element/IElement';
|
|
6
6
|
import IDocumentType from '../document-type/IDocumentType';
|
7
7
|
import IAttr from '../attr/IAttr';
|
8
8
|
import IProcessingInstruction from '../processing-instruction/IProcessingInstruction';
|
9
|
-
import
|
9
|
+
import IShadowRoot from '../shadow-root/IShadowRoot';
|
10
|
+
import DOMException from '../../exception/DOMException';
|
11
|
+
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum';
|
12
|
+
import Node from './Node';
|
13
|
+
import MutationRecord from '../../mutation-observer/MutationRecord';
|
14
|
+
import MutationTypeEnum from '../../mutation-observer/MutationTypeEnum';
|
10
15
|
|
11
16
|
/**
|
12
17
|
* Node utility.
|
13
18
|
*/
|
14
19
|
export default class NodeUtility {
|
15
20
|
/**
|
16
|
-
*
|
21
|
+
* Append a child node to childNodes.
|
17
22
|
*
|
18
|
-
* @param
|
19
|
-
* @
|
23
|
+
* @param ancestorNode Ancestor node.
|
24
|
+
* @param node Node to append.
|
25
|
+
* @param [options] Options.
|
26
|
+
* @param [options.disableAncestorValidation] Disables validation for checking if the node is an ancestor of the ancestorNode.
|
27
|
+
* @returns Appended node.
|
20
28
|
*/
|
21
|
-
public static
|
22
|
-
|
29
|
+
public static appendChild(
|
30
|
+
ancestorNode: INode,
|
31
|
+
node: INode,
|
32
|
+
options?: { disableAncestorValidation?: boolean }
|
33
|
+
): INode {
|
34
|
+
if (node === ancestorNode) {
|
35
|
+
throw new DOMException(
|
36
|
+
"Failed to execute 'appendChild' on 'Node': Not possible to append a node as a child of itself."
|
37
|
+
);
|
38
|
+
}
|
39
|
+
|
40
|
+
if (!options?.disableAncestorValidation && this.isInclusiveAncestor(node, ancestorNode, true)) {
|
41
|
+
throw new DOMException(
|
42
|
+
"Failed to execute 'appendChild' on 'Node': The new node is a parent of the node to insert to.",
|
43
|
+
DOMExceptionNameEnum.domException
|
44
|
+
);
|
45
|
+
}
|
46
|
+
|
47
|
+
// If the type is DocumentFragment, then the child nodes of if it should be moved instead of the actual node.
|
48
|
+
// See: https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment
|
49
|
+
if (node.nodeType === NodeTypeEnum.documentFragmentNode) {
|
50
|
+
for (const child of node.childNodes.slice()) {
|
51
|
+
ancestorNode.appendChild(child);
|
52
|
+
}
|
53
|
+
return node;
|
54
|
+
}
|
55
|
+
|
56
|
+
// Remove the node from its previous parent if it has any.
|
57
|
+
if (node.parentNode) {
|
58
|
+
const index = node.parentNode.childNodes.indexOf(node);
|
59
|
+
if (index !== -1) {
|
60
|
+
node.parentNode.childNodes.splice(index, 1);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
if (ancestorNode.isConnected) {
|
65
|
+
(ancestorNode.ownerDocument || this)['_cacheID']++;
|
66
|
+
}
|
67
|
+
|
68
|
+
ancestorNode.childNodes.push(node);
|
69
|
+
|
70
|
+
(<Node>node)._connectToNode(ancestorNode);
|
71
|
+
|
72
|
+
// MutationObserver
|
73
|
+
if ((<Node>ancestorNode)._observers.length > 0) {
|
74
|
+
const record = new MutationRecord();
|
75
|
+
record.target = ancestorNode;
|
76
|
+
record.type = MutationTypeEnum.childList;
|
77
|
+
record.addedNodes = [node];
|
78
|
+
|
79
|
+
for (const observer of (<Node>ancestorNode)._observers) {
|
80
|
+
if (observer.options.subtree) {
|
81
|
+
(<Node>node)._observe(observer);
|
82
|
+
}
|
83
|
+
if (observer.options.childList) {
|
84
|
+
observer.callback([record]);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
return node;
|
23
90
|
}
|
24
91
|
|
25
92
|
/**
|
26
|
-
*
|
93
|
+
* Remove Child element from childNodes array.
|
27
94
|
*
|
28
|
-
* @param
|
29
|
-
* @param
|
30
|
-
* @
|
31
|
-
* @returns "true" if this node contains the other node.
|
95
|
+
* @param ancestorNode Ancestor node.
|
96
|
+
* @param node Node to remove.
|
97
|
+
* @returns Removed node.
|
32
98
|
*/
|
33
|
-
public static
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
if (otherNode === null) {
|
39
|
-
return false;
|
99
|
+
public static removeChild(ancestorNode: INode, node: INode): INode {
|
100
|
+
const index = ancestorNode.childNodes.indexOf(node);
|
101
|
+
|
102
|
+
if (index === -1) {
|
103
|
+
throw new DOMException('Failed to remove node. Node is not child of parent.');
|
40
104
|
}
|
41
105
|
|
42
|
-
if (
|
43
|
-
|
106
|
+
if (ancestorNode.isConnected) {
|
107
|
+
(ancestorNode.ownerDocument || this)['_cacheID']++;
|
44
108
|
}
|
45
109
|
|
46
|
-
|
47
|
-
|
110
|
+
ancestorNode.childNodes.splice(index, 1);
|
111
|
+
|
112
|
+
(<Node>node)._connectToNode(null);
|
113
|
+
|
114
|
+
// MutationObserver
|
115
|
+
if ((<Node>ancestorNode)._observers.length > 0) {
|
116
|
+
const record = new MutationRecord();
|
117
|
+
record.target = ancestorNode;
|
118
|
+
record.type = MutationTypeEnum.childList;
|
119
|
+
record.removedNodes = [node];
|
120
|
+
|
121
|
+
for (const observer of (<Node>ancestorNode)._observers) {
|
122
|
+
(<Node>node)._unobserve(observer);
|
123
|
+
if (observer.options.childList) {
|
124
|
+
observer.callback([record]);
|
125
|
+
}
|
126
|
+
}
|
48
127
|
}
|
49
128
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
129
|
+
return node;
|
130
|
+
}
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Inserts a node before another.
|
134
|
+
*
|
135
|
+
* @param ancestorNode Ancestor node.
|
136
|
+
* @param newNode Node to insert.
|
137
|
+
* @param referenceNode Node to insert before.
|
138
|
+
* @param [options] Options.
|
139
|
+
* @param [options.disableAncestorValidation] Disables validation for checking if the node is an ancestor of the ancestorNode.
|
140
|
+
* @returns Inserted node.
|
141
|
+
*/
|
142
|
+
public static insertBefore(
|
143
|
+
ancestorNode: INode,
|
144
|
+
newNode: INode,
|
145
|
+
referenceNode: INode | null,
|
146
|
+
options?: { disableAncestorValidation?: boolean }
|
147
|
+
): INode {
|
148
|
+
if (
|
149
|
+
!options?.disableAncestorValidation &&
|
150
|
+
this.isInclusiveAncestor(newNode, ancestorNode, true)
|
151
|
+
) {
|
152
|
+
throw new DOMException(
|
153
|
+
"Failed to execute 'insertBefore' on 'Node': The new node is a parent of the node to insert to.",
|
154
|
+
DOMExceptionNameEnum.domException
|
155
|
+
);
|
156
|
+
}
|
157
|
+
|
158
|
+
// If the type is DocumentFragment, then the child nodes of if it should be moved instead of the actual node.
|
159
|
+
// See: https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment
|
160
|
+
if (newNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
161
|
+
for (const child of newNode.childNodes.slice()) {
|
162
|
+
ancestorNode.insertBefore(child, referenceNode);
|
59
163
|
}
|
164
|
+
return newNode;
|
60
165
|
}
|
61
|
-
|
166
|
+
|
167
|
+
if (referenceNode === null) {
|
168
|
+
ancestorNode.appendChild(newNode);
|
169
|
+
return newNode;
|
170
|
+
}
|
171
|
+
|
172
|
+
if (!referenceNode) {
|
173
|
+
throw new DOMException(
|
174
|
+
"Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 1 present.",
|
175
|
+
'TypeError'
|
176
|
+
);
|
177
|
+
}
|
178
|
+
|
179
|
+
if (ancestorNode.childNodes.indexOf(referenceNode) === -1) {
|
180
|
+
throw new DOMException(
|
181
|
+
"Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node."
|
182
|
+
);
|
183
|
+
}
|
184
|
+
|
185
|
+
if (ancestorNode.isConnected) {
|
186
|
+
(ancestorNode.ownerDocument || this)['_cacheID']++;
|
187
|
+
}
|
188
|
+
|
189
|
+
if (newNode.parentNode) {
|
190
|
+
const index = newNode.parentNode.childNodes.indexOf(newNode);
|
191
|
+
if (index !== -1) {
|
192
|
+
newNode.parentNode.childNodes.splice(index, 1);
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
ancestorNode.childNodes.splice(ancestorNode.childNodes.indexOf(referenceNode), 0, newNode);
|
197
|
+
|
198
|
+
(<Node>newNode)._connectToNode(ancestorNode);
|
199
|
+
|
200
|
+
// MutationObserver
|
201
|
+
if ((<Node>ancestorNode)._observers.length > 0) {
|
202
|
+
const record = new MutationRecord();
|
203
|
+
record.target = ancestorNode;
|
204
|
+
record.type = MutationTypeEnum.childList;
|
205
|
+
record.addedNodes = [newNode];
|
206
|
+
|
207
|
+
for (const observer of (<Node>ancestorNode)._observers) {
|
208
|
+
if (observer.options.subtree) {
|
209
|
+
(<Node>newNode)._observe(observer);
|
210
|
+
}
|
211
|
+
if (observer.options.childList) {
|
212
|
+
observer.callback([record]);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
return newNode;
|
62
218
|
}
|
63
219
|
|
64
220
|
/**
|
65
|
-
* Returns
|
221
|
+
* Returns whether the passed node is a text node, and narrows its type.
|
222
|
+
*
|
223
|
+
* @param node The node to be tested.
|
224
|
+
* @returns "true" if the node is a text node.
|
225
|
+
*/
|
226
|
+
public static isTextNode(node: INode | null): node is IText {
|
227
|
+
return node?.nodeType === NodeTypeEnum.textNode;
|
228
|
+
}
|
229
|
+
|
230
|
+
/**
|
231
|
+
* Returns boolean indicating if "ancestorNode" is an inclusive ancestor of "referenceNode".
|
66
232
|
*
|
67
233
|
* Based on:
|
68
234
|
* https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/helpers/node.js
|
@@ -70,16 +236,52 @@ export default class NodeUtility {
|
|
70
236
|
* @see https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
|
71
237
|
* @param ancestorNode Ancestor node.
|
72
238
|
* @param referenceNode Reference node.
|
73
|
-
* @
|
239
|
+
* @param [includeShadowRoots = false] Include shadow roots.
|
240
|
+
* @returns "true" if inclusive ancestor.
|
74
241
|
*/
|
75
|
-
public static isInclusiveAncestor(
|
76
|
-
|
242
|
+
public static isInclusiveAncestor(
|
243
|
+
ancestorNode: INode,
|
244
|
+
referenceNode: INode,
|
245
|
+
includeShadowRoots = false
|
246
|
+
): boolean {
|
247
|
+
if (ancestorNode === null || referenceNode === null) {
|
248
|
+
return false;
|
249
|
+
}
|
250
|
+
|
251
|
+
if (ancestorNode === referenceNode) {
|
252
|
+
return true;
|
253
|
+
}
|
254
|
+
|
255
|
+
if (!ancestorNode.childNodes.length) {
|
256
|
+
return false;
|
257
|
+
}
|
258
|
+
|
259
|
+
if (includeShadowRoots && referenceNode.isConnected !== ancestorNode.isConnected) {
|
260
|
+
return false;
|
261
|
+
}
|
262
|
+
|
263
|
+
if (
|
264
|
+
includeShadowRoots &&
|
265
|
+
ancestorNode === referenceNode.ownerDocument &&
|
266
|
+
referenceNode.isConnected
|
267
|
+
) {
|
268
|
+
return true;
|
269
|
+
}
|
270
|
+
|
271
|
+
let parent: INode = referenceNode.parentNode;
|
272
|
+
|
77
273
|
while (parent) {
|
78
274
|
if (ancestorNode === parent) {
|
79
275
|
return true;
|
80
276
|
}
|
81
|
-
|
277
|
+
|
278
|
+
parent = parent.parentNode
|
279
|
+
? parent.parentNode
|
280
|
+
: includeShadowRoots && (<IShadowRoot>parent).host
|
281
|
+
? (<IShadowRoot>parent).host
|
282
|
+
: null;
|
82
283
|
}
|
284
|
+
|
83
285
|
return false;
|
84
286
|
}
|
85
287
|
|