happy-dom 7.2.0 → 7.3.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.

Files changed (49) hide show
  1. package/lib/index.d.ts +5 -1
  2. package/lib/index.js +5 -1
  3. package/lib/index.js.map +1 -1
  4. package/lib/nodes/attr/Attr.d.ts +1 -0
  5. package/lib/nodes/attr/Attr.js +1 -0
  6. package/lib/nodes/attr/Attr.js.map +1 -1
  7. package/lib/nodes/document/Document.d.ts +9 -0
  8. package/lib/nodes/document/Document.js +21 -0
  9. package/lib/nodes/document/Document.js.map +1 -1
  10. package/lib/nodes/document/IDocument.d.ts +9 -0
  11. package/lib/nodes/element/Element.d.ts +1 -0
  12. package/lib/nodes/element/Element.js +1 -0
  13. package/lib/nodes/element/Element.js.map +1 -1
  14. package/lib/nodes/element/IElement.d.ts +1 -0
  15. package/lib/nodes/node/INode.d.ts +8 -0
  16. package/lib/nodes/node/Node.d.ts +20 -0
  17. package/lib/nodes/node/Node.js +154 -0
  18. package/lib/nodes/node/Node.js.map +1 -1
  19. package/lib/nodes/node/NodeDocumentPositionEnum.d.ts +9 -0
  20. package/lib/nodes/node/NodeDocumentPositionEnum.js +13 -0
  21. package/lib/nodes/node/NodeDocumentPositionEnum.js.map +1 -0
  22. package/lib/nodes/node/NodeUtility.d.ts +16 -0
  23. package/lib/nodes/node/NodeUtility.js +100 -0
  24. package/lib/nodes/node/NodeUtility.js.map +1 -1
  25. package/lib/nodes/processing-instruction/IProcessingInstruction.d.ts +4 -0
  26. package/lib/nodes/processing-instruction/IProcessingInstruction.js +3 -0
  27. package/lib/nodes/processing-instruction/IProcessingInstruction.js.map +1 -0
  28. package/lib/nodes/processing-instruction/ProcessingInstruction.d.ts +12 -0
  29. package/lib/nodes/processing-instruction/ProcessingInstruction.js +20 -0
  30. package/lib/nodes/processing-instruction/ProcessingInstruction.js.map +1 -0
  31. package/lib/window/IWindow.d.ts +2 -0
  32. package/lib/window/Window.d.ts +2 -0
  33. package/lib/window/Window.js +2 -0
  34. package/lib/window/Window.js.map +1 -1
  35. package/package.json +2 -2
  36. package/src/index.ts +9 -1
  37. package/src/nodes/attr/Attr.ts +1 -0
  38. package/src/nodes/document/Document.ts +28 -0
  39. package/src/nodes/document/IDocument.ts +10 -0
  40. package/src/nodes/element/Element.ts +1 -0
  41. package/src/nodes/element/IElement.ts +1 -0
  42. package/src/nodes/node/INode.ts +8 -0
  43. package/src/nodes/node/Node.ts +191 -1
  44. package/src/nodes/node/NodeDocumentPositionEnum.ts +10 -0
  45. package/src/nodes/node/NodeUtility.ts +137 -0
  46. package/src/nodes/processing-instruction/IProcessingInstruction.ts +5 -0
  47. package/src/nodes/processing-instruction/ProcessingInstruction.ts +13 -0
  48. package/src/window/IWindow.ts +2 -0
  49. package/src/window/Window.ts +2 -0
@@ -11,6 +11,9 @@ import IHTMLBaseElement from '../html-base-element/IHTMLBaseElement';
11
11
  import INodeList from './INodeList';
12
12
  import NodeListFactory from './NodeListFactory';
13
13
  import NodeTypeEnum from './NodeTypeEnum';
14
+ import NodeDocumentPositionEnum from './NodeDocumentPositionEnum';
15
+ import NodeUtility from './NodeUtility';
16
+ import IAttr from '../attr/IAttr';
14
17
 
15
18
  /**
16
19
  * Node.
@@ -29,6 +32,13 @@ export default class Node extends EventTarget implements INode {
29
32
  public static readonly DOCUMENT_TYPE_NODE = NodeTypeEnum.documentTypeNode;
30
33
  public static readonly DOCUMENT_FRAGMENT_NODE = NodeTypeEnum.documentFragmentNode;
31
34
  public static readonly PROCESSING_INSTRUCTION_NODE = NodeTypeEnum.processingInstructionNode;
35
+ public static readonly DOCUMENT_POSITION_CONTAINED_BY = NodeDocumentPositionEnum.containedBy;
36
+ public static readonly DOCUMENT_POSITION_CONTAINS = NodeDocumentPositionEnum.contains;
37
+ public static readonly DOCUMENT_POSITION_DISCONNECTED = NodeDocumentPositionEnum.disconnect;
38
+ public static readonly DOCUMENT_POSITION_FOLLOWING = NodeDocumentPositionEnum.following;
39
+ public static readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC =
40
+ NodeDocumentPositionEnum.implementationSpecific;
41
+ public static readonly DOCUMENT_POSITION_PRECEDING = NodeDocumentPositionEnum.preceding;
32
42
  public readonly ELEMENT_NODE = NodeTypeEnum.elementNode;
33
43
  public readonly ATTRIBUTE_NODE = NodeTypeEnum.attributeNode;
34
44
  public readonly TEXT_NODE = NodeTypeEnum.textNode;
@@ -38,6 +48,13 @@ export default class Node extends EventTarget implements INode {
38
48
  public readonly DOCUMENT_TYPE_NODE = NodeTypeEnum.documentTypeNode;
39
49
  public readonly DOCUMENT_FRAGMENT_NODE = NodeTypeEnum.documentFragmentNode;
40
50
  public readonly PROCESSING_INSTRUCTION_NODE = NodeTypeEnum.processingInstructionNode;
51
+ public readonly DOCUMENT_POSITION_CONTAINED_BY = NodeDocumentPositionEnum.containedBy;
52
+ public readonly DOCUMENT_POSITION_CONTAINS = NodeDocumentPositionEnum.contains;
53
+ public readonly DOCUMENT_POSITION_DISCONNECTED = NodeDocumentPositionEnum.disconnect;
54
+ public readonly DOCUMENT_POSITION_FOLLOWING = NodeDocumentPositionEnum.following;
55
+ public readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC =
56
+ NodeDocumentPositionEnum.implementationSpecific;
57
+ public readonly DOCUMENT_POSITION_PRECEDING = NodeDocumentPositionEnum.preceding;
41
58
  public readonly ownerDocument: IDocument = null;
42
59
  public readonly parentNode: INode = null;
43
60
  public readonly nodeType: number;
@@ -444,7 +461,7 @@ export default class Node extends EventTarget implements INode {
444
461
  }
445
462
 
446
463
  // eslint-disable-next-line
447
- if(event.composed && (<any>this).host) {
464
+ if (event.composed && (<any>this).host) {
448
465
  // eslint-disable-next-line
449
466
  return (<any>this).host.dispatchEvent(event);
450
467
  }
@@ -528,4 +545,177 @@ export default class Node extends EventTarget implements INode {
528
545
  }
529
546
  }
530
547
  }
548
+
549
+ /**
550
+ * Reports the position of its argument node relative to the node on which it is called.
551
+ *
552
+ * @see https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
553
+ * @param otherNode Other node.
554
+ */
555
+ public compareDocumentPosition(otherNode: INode): number {
556
+ /**
557
+ * 1. If this is other, then return zero.
558
+ */
559
+ if (this === otherNode) {
560
+ return 0;
561
+ }
562
+
563
+ /**
564
+ * 2. Let node1 be other and node2 be this.
565
+ */
566
+ let node1: INode = otherNode;
567
+ let node2: INode = this;
568
+
569
+ /**
570
+ * 3. Let attr1 and attr2 be null.
571
+ */
572
+ let attr1 = null;
573
+ let attr2 = null;
574
+
575
+ /**
576
+ * 4. If node1 is an attribute, then set attr1 to node1 and node1 to attr1’s element.
577
+ */
578
+ if (node1.nodeType === Node.ATTRIBUTE_NODE) {
579
+ attr1 = node1;
580
+ node1 = (<IAttr>attr1).ownerElement;
581
+ }
582
+
583
+ /**
584
+ * 5. If node2 is an attribute, then:
585
+ * 5.1. Set attr2 to node2 and node2 to attr2’s element.
586
+ */
587
+ if (node2.nodeType === Node.ATTRIBUTE_NODE) {
588
+ attr2 = node2;
589
+ node2 = (<IAttr>attr2).ownerElement;
590
+
591
+ /**
592
+ * 5.2. If attr1 and node1 are non-null, and node2 is node1, then:
593
+ */
594
+ if (attr1 !== null && node1 !== null && node2 === node1) {
595
+ /**
596
+ * 5.2.1. For each attr in node2’s attribute list:
597
+ */
598
+ for (const attr of Object.values((<IElement>node2).attributes)) {
599
+ /**
600
+ * 5.2.1.1. If attr equals attr1, then return the result of adding DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC and DOCUMENT_POSITION_PRECEDING.
601
+ */
602
+ if (NodeUtility.nodeEquals(<IAttr>attr, attr1)) {
603
+ return (
604
+ Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | Node.DOCUMENT_POSITION_PRECEDING
605
+ );
606
+ }
607
+
608
+ /**
609
+ * 5.2.1.2. If attr equals attr2, then return the result of adding DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC and DOCUMENT_POSITION_FOLLOWING.
610
+ */
611
+ if (NodeUtility.nodeEquals(<IAttr>attr, attr2)) {
612
+ return (
613
+ Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | Node.DOCUMENT_POSITION_FOLLOWING
614
+ );
615
+ }
616
+ }
617
+ }
618
+ }
619
+
620
+ const node2Ancestors: INode[] = [];
621
+ let node2Ancestor: INode = node2;
622
+
623
+ while (node2Ancestor) {
624
+ /**
625
+ * 7. If node1 is an ancestor of node2 […] then return the result of adding DOCUMENT_POSITION_CONTAINS to DOCUMENT_POSITION_PRECEDING.
626
+ */
627
+ if (node2Ancestor === node1) {
628
+ return Node.DOCUMENT_POSITION_CONTAINS | Node.DOCUMENT_POSITION_PRECEDING;
629
+ }
630
+
631
+ node2Ancestors.push(node2Ancestor);
632
+ node2Ancestor = node2Ancestor.parentNode;
633
+ }
634
+
635
+ const node1Ancestors: INode[] = [];
636
+ let node1Ancestor: INode = node1;
637
+
638
+ while (node1Ancestor) {
639
+ /**
640
+ * 8. If node1 is a descendant of node2 […] then return the result of adding DOCUMENT_POSITION_CONTAINED_BY to DOCUMENT_POSITION_FOLLOWING.
641
+ */
642
+ if (node1Ancestor === node2) {
643
+ return Node.DOCUMENT_POSITION_CONTAINED_BY | Node.DOCUMENT_POSITION_FOLLOWING;
644
+ }
645
+
646
+ node1Ancestors.push(node1Ancestor);
647
+ node1Ancestor = node1Ancestor.parentNode;
648
+ }
649
+
650
+ const reverseArrayIndex = (array: INode[], reverseIndex: number): INode => {
651
+ return array[array.length - 1 - reverseIndex];
652
+ };
653
+
654
+ const root = reverseArrayIndex(node2Ancestors, 0);
655
+
656
+ /**
657
+ * 6. If node1 or node2 is null, or node1’s root is not node2’s root, then return the result of adding
658
+ * DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, and either
659
+ * DOCUMENT_POSITION_PRECEDING or DOCUMENT_POSITION_FOLLOWING, with the constraint that this is to be consistent, together.
660
+ */
661
+ if (!root || root !== reverseArrayIndex(node1Ancestors, 0)) {
662
+ return (
663
+ Node.DOCUMENT_POSITION_DISCONNECTED |
664
+ Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
665
+ Node.DOCUMENT_POSITION_FOLLOWING
666
+ );
667
+ }
668
+
669
+ // Find the lowest common ancestor
670
+ let commonAncestorIndex = 0;
671
+ const ancestorsMinLength = Math.min(node2Ancestors.length, node1Ancestors.length);
672
+
673
+ for (let i = 0; i < ancestorsMinLength; ++i) {
674
+ const node2Ancestor = reverseArrayIndex(node2Ancestors, i);
675
+ const node1Ancestor = reverseArrayIndex(node1Ancestors, i);
676
+
677
+ if (node2Ancestor !== node1Ancestor) {
678
+ break;
679
+ }
680
+
681
+ commonAncestorIndex = i;
682
+ }
683
+
684
+ const commonAncestor = reverseArrayIndex(node2Ancestors, commonAncestorIndex);
685
+
686
+ // Indexes within the common ancestor
687
+ let indexes = 0;
688
+ let node2Index = -1;
689
+ let node1Index = -1;
690
+ const node2Node = reverseArrayIndex(node2Ancestors, commonAncestorIndex + 1);
691
+ const node1Node = reverseArrayIndex(node1Ancestors, commonAncestorIndex + 1);
692
+
693
+ const computeNodeIndexes = (nodes: INode[]): void => {
694
+ for (const childNode of nodes) {
695
+ computeNodeIndexes(childNode.childNodes);
696
+
697
+ if (childNode === node2Node) {
698
+ node2Index = indexes;
699
+ } else if (childNode === node1Node) {
700
+ node1Index = indexes;
701
+ }
702
+
703
+ if (node2Index !== -1 && node1Index !== -1) {
704
+ break;
705
+ }
706
+
707
+ indexes++;
708
+ }
709
+ };
710
+
711
+ computeNodeIndexes(commonAncestor.childNodes);
712
+
713
+ /**
714
+ * 9. If node1 is preceding node2, then return DOCUMENT_POSITION_PRECEDING.
715
+ * 10. Return DOCUMENT_POSITION_FOLLOWING.
716
+ */
717
+ return node1Index < node2Index
718
+ ? Node.DOCUMENT_POSITION_PRECEDING
719
+ : Node.DOCUMENT_POSITION_FOLLOWING;
720
+ }
531
721
  }
@@ -0,0 +1,10 @@
1
+ enum NodeDocumentPositionEnum {
2
+ disconnect = 0x01,
3
+ preceding = 0x02,
4
+ following = 0x04,
5
+ contains = 0x08,
6
+ containedBy = 0x10,
7
+ implementationSpecific = 0x20
8
+ }
9
+
10
+ export default NodeDocumentPositionEnum;
@@ -2,6 +2,10 @@ import IText from '../text/IText';
2
2
  import IComment from '../comment/IComment';
3
3
  import INode from './INode';
4
4
  import NodeTypeEnum from './NodeTypeEnum';
5
+ import IElement from '../element/IElement';
6
+ import IDocumentType from '../document-type/IDocumentType';
7
+ import IAttr from '../attr/IAttr';
8
+ import IProcessingInstruction from '../processing-instruction/IProcessingInstruction';
5
9
 
6
10
  /**
7
11
  * Node utility.
@@ -136,4 +140,137 @@ export default class NodeUtility {
136
140
 
137
141
  return node.nextSibling;
138
142
  }
143
+
144
+ /**
145
+ * Needed by https://dom.spec.whatwg.org/#concept-node-equals
146
+ *
147
+ * @param elementA
148
+ * @param elementB
149
+ */
150
+ public static attributeListsEqual(elementA: IElement, elementB: IElement): boolean {
151
+ const listA = Object.values(elementA.attributes);
152
+ const listB = Object.values(elementB.attributes);
153
+
154
+ const lengthA = listA.length;
155
+ const lengthB = listB.length;
156
+
157
+ if (lengthA !== lengthB) {
158
+ return false;
159
+ }
160
+
161
+ for (let i = 0; i < lengthA; ++i) {
162
+ const attrA = listA[i];
163
+
164
+ if (
165
+ !listB.some((attrB) => {
166
+ return (
167
+ (typeof attrA === 'number' && typeof attrB === 'number' && attrA === attrB) ||
168
+ (typeof attrA === 'object' &&
169
+ typeof attrB === 'object' &&
170
+ NodeUtility.nodeEquals(attrA, attrB))
171
+ );
172
+ })
173
+ ) {
174
+ return false;
175
+ }
176
+ }
177
+
178
+ return true;
179
+ }
180
+
181
+ /**
182
+ * Check if node nodeA equals node nodeB.
183
+ * Reference: https://dom.spec.whatwg.org/#concept-node-equals
184
+ *
185
+ * @param nodeA Node A.
186
+ * @param nodeB Node B.
187
+ */
188
+ public static nodeEquals(nodeA: INode, nodeB: INode): boolean {
189
+ if (nodeA.nodeType !== nodeB.nodeType) {
190
+ return false;
191
+ }
192
+
193
+ switch (nodeA.nodeType) {
194
+ case NodeTypeEnum.documentTypeNode:
195
+ const documentTypeA = <IDocumentType>nodeA;
196
+ const documentTypeB = <IDocumentType>nodeB;
197
+
198
+ if (
199
+ documentTypeA.name !== documentTypeB.name ||
200
+ documentTypeA.publicId !== documentTypeB.publicId ||
201
+ documentTypeA.systemId !== documentTypeB.systemId
202
+ ) {
203
+ return false;
204
+ }
205
+ break;
206
+ case NodeTypeEnum.elementNode:
207
+ const elementA = <IElement>nodeA;
208
+ const elementB = <IElement>nodeB;
209
+
210
+ if (
211
+ elementA.namespaceURI !== elementB.namespaceURI ||
212
+ elementA.prefix !== elementB.prefix ||
213
+ elementA.localName !== elementB.localName ||
214
+ elementA.attributes.length !== elementB.attributes.length
215
+ ) {
216
+ return false;
217
+ }
218
+ break;
219
+ case NodeTypeEnum.attributeNode:
220
+ const attributeA = <IAttr>nodeA;
221
+ const attributeB = <IAttr>nodeB;
222
+
223
+ if (
224
+ attributeA.namespaceURI !== attributeB.namespaceURI ||
225
+ attributeA.localName !== attributeB.localName ||
226
+ attributeA.value !== attributeB.value
227
+ ) {
228
+ return false;
229
+ }
230
+ break;
231
+ case NodeTypeEnum.processingInstructionNode:
232
+ const processingInstructionA = <IProcessingInstruction>nodeA;
233
+ const processingInstructionB = <IProcessingInstruction>nodeB;
234
+
235
+ if (
236
+ processingInstructionA.target !== processingInstructionB.target ||
237
+ processingInstructionA.data !== processingInstructionB.data
238
+ ) {
239
+ return false;
240
+ }
241
+ break;
242
+ case NodeTypeEnum.textNode:
243
+ case NodeTypeEnum.commentNode:
244
+ type TextOrComment = IText | IComment;
245
+ const textOrCommentA = <TextOrComment>nodeA;
246
+ const textOrCommentB = <TextOrComment>nodeB;
247
+
248
+ if (textOrCommentA.data !== textOrCommentB.data) {
249
+ return false;
250
+ }
251
+ break;
252
+ }
253
+
254
+ if (
255
+ nodeA.nodeType === NodeTypeEnum.elementNode &&
256
+ !NodeUtility.attributeListsEqual(<IElement>nodeA, <IElement>nodeB)
257
+ ) {
258
+ return false;
259
+ }
260
+
261
+ if (nodeA.childNodes.length !== nodeB.childNodes.length) {
262
+ return false;
263
+ }
264
+
265
+ for (let i = 0; i < nodeA.childNodes.length; i++) {
266
+ const childNodeA = nodeA.childNodes[i];
267
+ const childNodeB = nodeB.childNodes[i];
268
+
269
+ if (!NodeUtility.nodeEquals(childNodeA, childNodeB)) {
270
+ return false;
271
+ }
272
+ }
273
+
274
+ return true;
275
+ }
139
276
  }
@@ -0,0 +1,5 @@
1
+ import ICharacterData from '../character-data/ICharacterData';
2
+
3
+ export default interface IProcessingInstruction extends ICharacterData {
4
+ target: string;
5
+ }
@@ -0,0 +1,13 @@
1
+ import IProcessingInstruction from './IProcessingInstruction';
2
+ import CharacterData from '../character-data/CharacterData';
3
+ import NodeTypeEnum from '../node/NodeTypeEnum';
4
+
5
+ /**
6
+ * Processing instruction node interface.
7
+ *
8
+ * Reference: https://developer.mozilla.org/en-US/docs/Web/API/ProcessingInstruction.
9
+ */
10
+ export default class ProcessingInstruction extends CharacterData implements IProcessingInstruction {
11
+ public readonly nodeType = NodeTypeEnum.processingInstructionNode;
12
+ public target: string;
13
+ }
@@ -98,6 +98,7 @@ import Attr from '../nodes/attr/Attr';
98
98
  import { URLSearchParams } from 'url';
99
99
  import { Performance } from 'perf_hooks';
100
100
  import IElement from '../nodes/element/IElement';
101
+ import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction';
101
102
 
102
103
  /**
103
104
  * Window without dependencies to server side specific packages.
@@ -142,6 +143,7 @@ export default interface IWindow extends IEventTarget, NodeJS.Global {
142
143
  readonly Element: typeof Element;
143
144
  readonly DocumentFragment: typeof DocumentFragment;
144
145
  readonly CharacterData: typeof CharacterData;
146
+ readonly ProcessingInstruction: typeof ProcessingInstruction;
145
147
  readonly NodeFilter: typeof NodeFilter;
146
148
  readonly TreeWalker: typeof TreeWalker;
147
149
  readonly DOMParser: typeof DOMParser;
@@ -109,6 +109,7 @@ import Base64 from '../base64/Base64';
109
109
  import IDocument from '../nodes/document/IDocument';
110
110
  import Attr from '../nodes/attr/Attr';
111
111
  import IElement from '../nodes/element/IElement';
112
+ import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction';
112
113
 
113
114
  const ORIGINAL_SET_TIMEOUT = setTimeout;
114
115
  const ORIGINAL_CLEAR_TIMEOUT = clearTimeout;
@@ -171,6 +172,7 @@ export default class Window extends EventTarget implements IWindow {
171
172
  public readonly Text = Text;
172
173
  public readonly Comment = Comment;
173
174
  public readonly ShadowRoot = ShadowRoot;
175
+ public readonly ProcessingInstruction = ProcessingInstruction;
174
176
  public readonly Element = Element;
175
177
  public readonly DocumentFragment = DocumentFragment;
176
178
  public readonly CharacterData = CharacterData;