happy-dom 2.24.3 → 2.25.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.

Potentially problematic release.


This version of happy-dom might be problematic. Click here for more details.

Files changed (78) hide show
  1. package/lib/css/CSSStyleSheet.d.ts +5 -1
  2. package/lib/css/CSSStyleSheet.js +21 -10
  3. package/lib/css/CSSStyleSheet.js.map +1 -1
  4. package/lib/event/EventTarget.d.ts +9 -0
  5. package/lib/event/EventTarget.js +11 -0
  6. package/lib/event/EventTarget.js.map +1 -1
  7. package/lib/event/NonImplementedEventTypes.js +0 -1
  8. package/lib/event/NonImplementedEventTypes.js.map +1 -1
  9. package/lib/event/events/IPointerEventInit.d.ts +13 -0
  10. package/lib/event/events/IPointerEventInit.js +3 -0
  11. package/lib/event/events/IPointerEventInit.js.map +1 -0
  12. package/lib/event/events/PointerEvent.d.ts +24 -0
  13. package/lib/event/events/PointerEvent.js +64 -0
  14. package/lib/event/events/PointerEvent.js.map +1 -0
  15. package/lib/exception/DOMExceptionNameEnum.d.ts +4 -1
  16. package/lib/exception/DOMExceptionNameEnum.js +3 -0
  17. package/lib/exception/DOMExceptionNameEnum.js.map +1 -1
  18. package/lib/nodes/document/Document.d.ts +8 -1
  19. package/lib/nodes/document/Document.js +14 -1
  20. package/lib/nodes/document/Document.js.map +1 -1
  21. package/lib/nodes/document/IDocument.d.ts +1 -0
  22. package/lib/nodes/document-fragment/DocumentFragment.d.ts +1 -0
  23. package/lib/nodes/document-fragment/DocumentFragment.js +1 -0
  24. package/lib/nodes/document-fragment/DocumentFragment.js.map +1 -1
  25. package/lib/nodes/element/Element.d.ts +7 -0
  26. package/lib/nodes/element/Element.js +39 -3
  27. package/lib/nodes/element/Element.js.map +1 -1
  28. package/lib/nodes/element/IElement.d.ts +7 -0
  29. package/lib/nodes/html-element/HTMLElement.js +16 -4
  30. package/lib/nodes/html-element/HTMLElement.js.map +1 -1
  31. package/lib/nodes/html-input-element/HTMLInputElement.d.ts +9 -6
  32. package/lib/nodes/html-input-element/HTMLInputElement.js +18 -8
  33. package/lib/nodes/html-input-element/HTMLInputElement.js.map +1 -1
  34. package/lib/nodes/html-input-element/IHTMLInputElement.d.ts +0 -1
  35. package/lib/nodes/html-link-element/HTMLLinkElement.d.ts +5 -12
  36. package/lib/nodes/html-link-element/HTMLLinkElement.js +39 -68
  37. package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
  38. package/lib/nodes/html-script-element/HTMLScriptElement.d.ts +5 -12
  39. package/lib/nodes/html-script-element/HTMLScriptElement.js +21 -49
  40. package/lib/nodes/html-script-element/HTMLScriptElement.js.map +1 -1
  41. package/lib/nodes/html-style-element/HTMLStyleElement.js +1 -1
  42. package/lib/nodes/html-style-element/HTMLStyleElement.js.map +1 -1
  43. package/lib/nodes/node/INode.d.ts +1 -1
  44. package/lib/nodes/node/Node.d.ts +8 -13
  45. package/lib/nodes/node/Node.js +58 -64
  46. package/lib/nodes/node/Node.js.map +1 -1
  47. package/lib/nodes/shadow-root/ShadowRoot.d.ts +7 -0
  48. package/lib/nodes/shadow-root/ShadowRoot.js +16 -0
  49. package/lib/nodes/shadow-root/ShadowRoot.js.map +1 -1
  50. package/lib/query-selector/SelectorItem.d.ts +8 -0
  51. package/lib/query-selector/SelectorItem.js +29 -11
  52. package/lib/query-selector/SelectorItem.js.map +1 -1
  53. package/lib/window/Window.d.ts +2 -0
  54. package/lib/window/Window.js +2 -0
  55. package/lib/window/Window.js.map +1 -1
  56. package/package.json +2 -2
  57. package/src/css/CSSStyleSheet.ts +33 -4
  58. package/src/event/EventTarget.ts +12 -0
  59. package/src/event/NonImplementedEventTypes.ts +0 -1
  60. package/src/event/events/IPointerEventInit.ts +14 -0
  61. package/src/event/events/PointerEvent.ts +42 -0
  62. package/src/exception/DOMExceptionNameEnum.ts +4 -1
  63. package/src/nodes/document/Document.ts +11 -1
  64. package/src/nodes/document/IDocument.ts +1 -0
  65. package/src/nodes/document-fragment/DocumentFragment.ts +1 -0
  66. package/src/nodes/element/Element.ts +42 -3
  67. package/src/nodes/element/IElement.ts +8 -0
  68. package/src/nodes/html-element/HTMLElement.ts +21 -4
  69. package/src/nodes/html-input-element/HTMLInputElement.ts +21 -9
  70. package/src/nodes/html-input-element/IHTMLInputElement.ts +0 -1
  71. package/src/nodes/html-link-element/HTMLLinkElement.ts +46 -73
  72. package/src/nodes/html-script-element/HTMLScriptElement.ts +24 -49
  73. package/src/nodes/html-style-element/HTMLStyleElement.ts +1 -1
  74. package/src/nodes/node/INode.ts +1 -1
  75. package/src/nodes/node/Node.ts +61 -63
  76. package/src/nodes/shadow-root/ShadowRoot.ts +14 -0
  77. package/src/query-selector/SelectorItem.ts +38 -19
  78. package/src/window/Window.ts +2 -0
@@ -9,7 +9,6 @@ import IDocument from '../document/IDocument';
9
9
  import IElement from '../element/IElement';
10
10
  import INodeList from './INodeList';
11
11
  import NodeListFactory from './NodeListFactory';
12
- import { IShadowRoot } from '../..';
13
12
 
14
13
  /**
15
14
  * Node.
@@ -27,9 +26,8 @@ export default class Node extends EventTarget implements INode {
27
26
  public readonly parentNode: INode = null;
28
27
  public readonly nodeType: number;
29
28
  public readonly childNodes: INodeList<INode> = NodeListFactory.create();
30
-
31
- // Protected properties
32
- protected _isConnected = false;
29
+ public readonly isConnected: boolean = false;
30
+ public _rootNode: INode = null;
33
31
 
34
32
  // Custom Properties (not part of HTML standard)
35
33
  protected _observers: MutationObserverListener[] = [];
@@ -42,42 +40,6 @@ export default class Node extends EventTarget implements INode {
42
40
  this.ownerDocument = (<typeof Node>this.constructor).ownerDocument;
43
41
  }
44
42
 
45
- /**
46
- * Returns "true" if connected to DOM.
47
- *
48
- * @returns "true" if connected.
49
- */
50
- public get isConnected(): boolean {
51
- return this._isConnected;
52
- }
53
-
54
- /**
55
- * Sets the connected state.
56
- *
57
- * @param isConnected "true" if connected.
58
- */
59
- public set isConnected(isConnected) {
60
- if (this._isConnected !== isConnected) {
61
- this._isConnected = isConnected;
62
-
63
- if (isConnected && this.connectedCallback) {
64
- this.connectedCallback();
65
- } else if (!isConnected && this.disconnectedCallback) {
66
- this.disconnectedCallback();
67
- }
68
-
69
- for (const child of this.childNodes) {
70
- child.isConnected = isConnected;
71
- }
72
-
73
- // eslint-disable-next-line
74
- if ((<any>this).shadowRoot) {
75
- // eslint-disable-next-line
76
- (<any>this).shadowRoot.isConnected = isConnected;
77
- }
78
- }
79
- }
80
-
81
43
  /**
82
44
  * Get text value of children.
83
45
  *
@@ -176,7 +138,7 @@ export default class Node extends EventTarget implements INode {
176
138
  public get parentElement(): IElement {
177
139
  let parent = this.parentNode;
178
140
  while (parent && parent.nodeType !== Node.ELEMENT_NODE) {
179
- parent = this.parentNode;
141
+ parent = parent.parentNode;
180
142
  }
181
143
  return <IElement>parent;
182
144
  }
@@ -208,21 +170,15 @@ export default class Node extends EventTarget implements INode {
208
170
  * @returns Node.
209
171
  */
210
172
  public getRootNode(options?: { composed: boolean }): INode {
211
- // eslint-disable-next-line
212
- let parent: INode = this;
173
+ if (!this.isConnected) {
174
+ return this;
175
+ }
213
176
 
214
- while (!!parent) {
215
- if (!parent.parentNode) {
216
- if (!options?.composed || !(<IShadowRoot>parent).host) {
217
- return parent;
218
- }
219
- parent = (<IShadowRoot>parent).host;
220
- } else {
221
- parent = parent.parentNode;
222
- }
177
+ if (this._rootNode && !options?.composed) {
178
+ return this._rootNode;
223
179
  }
224
180
 
225
- return null;
181
+ return this.ownerDocument;
226
182
  }
227
183
 
228
184
  /**
@@ -234,8 +190,11 @@ export default class Node extends EventTarget implements INode {
234
190
  public cloneNode(deep = false): INode {
235
191
  const clone = new (<typeof Node>this.constructor)();
236
192
 
237
- for (const node of clone.childNodes.slice()) {
238
- node.parentNode.removeChild(node);
193
+ // Document has childNodes directly when it is created
194
+ if (clone.childNodes.length) {
195
+ for (const node of clone.childNodes.slice()) {
196
+ node.parentNode.removeChild(node);
197
+ }
239
198
  }
240
199
 
241
200
  if (deep) {
@@ -281,8 +240,7 @@ export default class Node extends EventTarget implements INode {
281
240
 
282
241
  this.childNodes.push(node);
283
242
 
284
- (<Node>node.parentNode) = this;
285
- node.isConnected = this.isConnected;
243
+ (<Node>node)._connectToNode(this);
286
244
 
287
245
  // MutationObserver
288
246
  if (this._observers.length > 0) {
@@ -318,8 +276,7 @@ export default class Node extends EventTarget implements INode {
318
276
 
319
277
  this.childNodes.splice(index, 1);
320
278
 
321
- (<Node>node.parentNode) = null;
322
- node.isConnected = false;
279
+ (<Node>node)._connectToNode(null);
323
280
 
324
281
  // MutationObserver
325
282
  if (this._observers.length > 0) {
@@ -384,8 +341,7 @@ export default class Node extends EventTarget implements INode {
384
341
 
385
342
  this.childNodes.splice(index, 0, newNode);
386
343
 
387
- (<Node>newNode.parentNode) = this;
388
- newNode.isConnected = this.isConnected;
344
+ (<Node>newNode)._connectToNode(this);
389
345
 
390
346
  // MutationObserver
391
347
  if (this._observers.length > 0) {
@@ -432,8 +388,16 @@ export default class Node extends EventTarget implements INode {
432
388
 
433
389
  const returnValue = super.dispatchEvent(event);
434
390
 
435
- if (event.bubbles && this.parentNode !== null && !event._propagationStopped) {
436
- return this.parentNode.dispatchEvent(event);
391
+ if (event.bubbles && !event._propagationStopped) {
392
+ if (this.parentNode) {
393
+ return this.parentNode.dispatchEvent(event);
394
+ }
395
+
396
+ // eslint-disable-next-line
397
+ if(event.composed && (<any>this).host) {
398
+ // eslint-disable-next-line
399
+ return (<any>this).host.dispatchEvent(event);
400
+ }
437
401
  }
438
402
 
439
403
  return returnValue;
@@ -480,4 +444,38 @@ export default class Node extends EventTarget implements INode {
480
444
  }
481
445
  }
482
446
  }
447
+
448
+ /**
449
+ * Connects this element to another element.
450
+ *
451
+ * @param parentNode Parent node.
452
+ */
453
+ public _connectToNode(parentNode: INode = null): void {
454
+ const isConnected = !!parentNode && parentNode.isConnected;
455
+
456
+ if (this.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
457
+ (<INode>this.parentNode) = parentNode;
458
+ (<Node>this)._rootNode = isConnected && parentNode ? (<Node>parentNode)._rootNode : null;
459
+ }
460
+
461
+ if (this.isConnected !== isConnected) {
462
+ (<boolean>this.isConnected) = isConnected;
463
+
464
+ if (isConnected && this.connectedCallback) {
465
+ this.connectedCallback();
466
+ } else if (!isConnected && this.disconnectedCallback) {
467
+ this.disconnectedCallback();
468
+ }
469
+
470
+ for (const child of this.childNodes) {
471
+ (<Node>child)._connectToNode(this);
472
+ }
473
+
474
+ // eslint-disable-next-line
475
+ if ((<any>this).shadowRoot) {
476
+ // eslint-disable-next-line
477
+ (<any>this).shadowRoot._connectToNode(this);
478
+ }
479
+ }
480
+ }
483
481
  }
@@ -4,6 +4,7 @@ import XMLSerializer from '../../xml-serializer/XMLSerializer';
4
4
  import IElement from '../element/IElement';
5
5
  import CSSStyleSheet from '../../css/CSSStyleSheet';
6
6
  import IShadowRoot from './IShadowRoot';
7
+ import IHTMLElement from '../../nodes/html-element/IHTMLElement';
7
8
 
8
9
  /**
9
10
  * ShadowRoot.
@@ -42,6 +43,19 @@ export default class ShadowRoot extends DocumentFragment implements IShadowRoot
42
43
  }
43
44
  }
44
45
 
46
+ /**
47
+ * Returns active element.
48
+ *
49
+ * @returns Active element.
50
+ */
51
+ public get activeElement(): IHTMLElement {
52
+ const activeElement: IHTMLElement = this.ownerDocument['_activeElement'];
53
+ if (activeElement && activeElement.getRootNode() === this) {
54
+ return activeElement;
55
+ }
56
+ return null;
57
+ }
58
+
45
59
  /**
46
60
  * Converts to string.
47
61
  *
@@ -3,7 +3,7 @@ import Element from '../nodes/element/Element';
3
3
 
4
4
  const ATTRIBUTE_REGEXP = /\[([a-zA-Z0-9-_]+)\]|\[([a-zA-Z0-9-_]+)([~|^$*]{0,1})[ ]*=[ ]*["']{0,1}([^"']+)["']{0,1}\]/g;
5
5
  const ATTRIBUTE_NAME_REGEXP = /[^a-zA-Z0-9-_$]/;
6
- const PSUEDO_REGEXP = /:([a-zA-Z-]+)\(([0-9n+-]+|odd|even)\)|:([a-zA-Z-]+)/g;
6
+ const PSUEDO_REGEXP = /:([a-zA-Z-]+)\(([0-9n+-]+|odd|even)\)|:not\(([^)]+)\)|:([a-zA-Z-]+)/g;
7
7
  const CLASS_REGEXP = /\.([a-zA-Z0-9-_$]+)/g;
8
8
  const TAG_NAME_REGEXP = /^[a-zA-Z0-9-]+/;
9
9
 
@@ -29,7 +29,8 @@ export default class SelectorItem {
29
29
  constructor(selector: string) {
30
30
  this.isAll = selector === '*';
31
31
  this.isID = !this.isAll ? selector.startsWith('#') : false;
32
- this.isAttribute = !this.isAll && !this.isID && selector.includes('[');
32
+ this.isAttribute =
33
+ !this.isAll && !this.isID && selector.includes('[') && !selector.includes(':not(');
33
34
  this.isPseudo = !this.isAll && !this.isID && selector.includes(':');
34
35
  this.isClass = !this.isAll && !this.isID && new RegExp(CLASS_REGEXP, 'g').test(selector);
35
36
  this.tagName = !this.isAll && !this.isID ? selector.match(TAG_NAME_REGEXP) : null;
@@ -84,24 +85,17 @@ export default class SelectorItem {
84
85
  while ((match = regexp.exec(selector))) {
85
86
  if (match[1] && !this.matchesNthChild(element, match[1], match[2])) {
86
87
  return false;
87
- } else if (match[3] && !this.matchesPsuedo(element, match[3])) {
88
+ } else if (match[3] && this.matchesAttribute(element, match[3])) {
89
+ return false;
90
+ } else if (match[4] && !this.matchesPsuedo(element, match[4])) {
88
91
  return false;
89
92
  }
90
93
  }
91
94
  }
92
95
 
93
96
  // Attribute match
94
- if (this.isAttribute) {
95
- const regexp = new RegExp(ATTRIBUTE_REGEXP, 'g');
96
-
97
- while ((match = regexp.exec(selector))) {
98
- if (
99
- (match[1] && !this.matchesAttributeName(element, match[1])) ||
100
- (match[2] && !this.matchesAttributeNameAndValue(element, match[2], match[4], match[3]))
101
- ) {
102
- return false;
103
- }
104
- }
97
+ if (this.isAttribute && !this.matchesAttribute(element, selector)) {
98
+ return false;
105
99
  }
106
100
 
107
101
  return true;
@@ -217,6 +211,31 @@ export default class SelectorItem {
217
211
  return false;
218
212
  }
219
213
 
214
+ /**
215
+ * Matches attribute.
216
+ *
217
+ * @param element Element.
218
+ * @param selector Selector.
219
+ * @returns True if it is a match.
220
+ */
221
+ private matchesAttribute(element: Element, selector: string): boolean {
222
+ const regexp = new RegExp(ATTRIBUTE_REGEXP, 'g');
223
+ let match;
224
+
225
+ while ((match = regexp.exec(selector))) {
226
+ const isPsuedo = match.index > 0 && selector[match.index] === '(';
227
+ if (
228
+ !isPsuedo &&
229
+ ((match[1] && !this.matchesAttributeName(element, match[1])) ||
230
+ (match[2] && !this.matchesAttributeNameAndValue(element, match[2], match[4], match[3])))
231
+ ) {
232
+ return false;
233
+ }
234
+ }
235
+
236
+ return true;
237
+ }
238
+
220
239
  /**
221
240
  * Matches attribute name only.
222
241
  *
@@ -224,7 +243,7 @@ export default class SelectorItem {
224
243
  * @param attributeName Attribute name.
225
244
  * @returns True if it is a match.
226
245
  */
227
- private matchesAttributeName(element, attributeName): boolean {
246
+ private matchesAttributeName(element: Element, attributeName: string): boolean {
228
247
  if (ATTRIBUTE_NAME_REGEXP.test(attributeName)) {
229
248
  throw new DOMException(`The selector "${this.selector}" is not valid.`);
230
249
  }
@@ -243,10 +262,10 @@ export default class SelectorItem {
243
262
  * @returns True if it is a match.
244
263
  */
245
264
  private matchesAttributeNameAndValue(
246
- element,
247
- attributeName,
248
- attributeValue,
249
- matchType = null
265
+ element: Element,
266
+ attributeName: string,
267
+ attributeValue: string,
268
+ matchType: string = null
250
269
  ): boolean {
251
270
  const attribute = element._attributes[attributeName.toLowerCase()];
252
271
  const value = attributeValue;
@@ -42,6 +42,7 @@ import FileReader from '../file/FileReader';
42
42
  import History from '../history/History';
43
43
  import CSSStyleDeclaration from '../css/CSSStyleDeclaration';
44
44
  import MouseEvent from '../event/events/MouseEvent';
45
+ import PointerEvent from '../event/events/PointerEvent';
45
46
  import FocusEvent from '../event/events/FocusEvent';
46
47
  import WheelEvent from '../event/events/WheelEvent';
47
48
  import DataTransfer from '../event/DataTransfer';
@@ -111,6 +112,7 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
111
112
  public readonly AnimationEvent = AnimationEvent;
112
113
  public readonly KeyboardEvent = KeyboardEvent;
113
114
  public readonly MouseEvent = MouseEvent;
115
+ public readonly PointerEvent = PointerEvent;
114
116
  public readonly FocusEvent = FocusEvent;
115
117
  public readonly WheelEvent = WheelEvent;
116
118
  public readonly InputEvent = InputEvent;