happy-dom 2.41.0 → 2.43.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 (48) hide show
  1. package/lib/css/CSSStyleDeclaration.js +1 -1
  2. package/lib/css/CSSStyleDeclaration.js.map +1 -1
  3. package/lib/dom-token-list/DOMTokenList.d.ts +104 -0
  4. package/lib/dom-token-list/DOMTokenList.js +209 -0
  5. package/lib/dom-token-list/DOMTokenList.js.map +1 -0
  6. package/lib/dom-token-list/IDOMTokenList.d.ts +18 -0
  7. package/lib/dom-token-list/IDOMTokenList.js +3 -0
  8. package/lib/dom-token-list/IDOMTokenList.js.map +1 -0
  9. package/lib/nodes/document/Document.d.ts +7 -0
  10. package/lib/nodes/document/Document.js +12 -0
  11. package/lib/nodes/document/Document.js.map +1 -1
  12. package/lib/nodes/document/IDocument.d.ts +13 -2
  13. package/lib/nodes/element/Element.d.ts +15 -5
  14. package/lib/nodes/element/Element.js +28 -3
  15. package/lib/nodes/element/Element.js.map +1 -1
  16. package/lib/nodes/element/IElement.d.ts +2 -2
  17. package/lib/nodes/html-label-element/HTMLLabelElement.d.ts +0 -1
  18. package/lib/nodes/html-label-element/HTMLLabelElement.js +2 -6
  19. package/lib/nodes/html-label-element/HTMLLabelElement.js.map +1 -1
  20. package/lib/nodes/html-link-element/HTMLLinkElement.d.ts +12 -0
  21. package/lib/nodes/html-link-element/HTMLLinkElement.js +26 -0
  22. package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
  23. package/lib/nodes/html-link-element/IHTMLLinkElement.d.ts +2 -0
  24. package/lib/query-selector/SelectorItem.d.ts +18 -2
  25. package/lib/query-selector/SelectorItem.js +54 -25
  26. package/lib/query-selector/SelectorItem.js.map +1 -1
  27. package/lib/window/IWindow.d.ts +3 -1
  28. package/lib/window/Window.d.ts +1 -0
  29. package/lib/window/Window.js +1 -0
  30. package/lib/window/Window.js.map +1 -1
  31. package/package.json +2 -2
  32. package/src/css/CSSStyleDeclaration.ts +1 -1
  33. package/src/dom-token-list/DOMTokenList.ts +219 -0
  34. package/src/dom-token-list/IDOMTokenList.ts +19 -0
  35. package/src/nodes/document/Document.ts +10 -0
  36. package/src/nodes/document/IDocument.ts +13 -2
  37. package/src/nodes/element/Element.ts +30 -3
  38. package/src/nodes/element/IElement.ts +2 -2
  39. package/src/nodes/html-label-element/HTMLLabelElement.ts +1 -5
  40. package/src/nodes/html-link-element/HTMLLinkElement.ts +26 -0
  41. package/src/nodes/html-link-element/IHTMLLinkElement.ts +2 -0
  42. package/src/query-selector/SelectorItem.ts +62 -26
  43. package/src/window/IWindow.ts +3 -1
  44. package/src/window/Window.ts +1 -0
  45. package/lib/nodes/element/ClassList.d.ts +0 -40
  46. package/lib/nodes/element/ClassList.js +0 -99
  47. package/lib/nodes/element/ClassList.js.map +0 -1
  48. package/src/nodes/element/ClassList.ts +0 -92
@@ -0,0 +1,219 @@
1
+ import DOMException from '../exception/DOMException';
2
+ import Element from '../nodes/element/Element';
3
+ import IDOMTokenList from './IDOMTokenList';
4
+
5
+ /**
6
+ * DOM Token List.
7
+ *
8
+ * Reference:
9
+ * https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList.
10
+ */
11
+ export default class DOMTokenList implements IDOMTokenList {
12
+ public readonly length = 0;
13
+ private _ownerElement: Element;
14
+ private _attributeName: string;
15
+
16
+ /**
17
+ * Constructor.
18
+ *
19
+ * @param ownerElement Owner element.
20
+ * @param attributeName Attribute name.
21
+ */
22
+ constructor(ownerElement: Element, attributeName) {
23
+ this._ownerElement = ownerElement;
24
+ this._attributeName = attributeName;
25
+ this._updateIndices();
26
+ }
27
+
28
+ /**
29
+ * Set value.
30
+ *
31
+ * @param value Value.
32
+ */
33
+ public set value(value: string) {
34
+ this._ownerElement.setAttributeNS(null, this._attributeName, value);
35
+ }
36
+
37
+ /**
38
+ * Get value.
39
+ */
40
+ public get value(): string {
41
+ return this._ownerElement.getAttributeNS(null, this._attributeName);
42
+ }
43
+
44
+ /**
45
+ * Get ClassName.
46
+ *
47
+ * @param index Index.
48
+ * */
49
+ public item(index: number | string): string {
50
+ index = typeof index === 'number' ? index : 0;
51
+ return index >= 0 && this[index] ? this[index] : null;
52
+ }
53
+
54
+ /**
55
+ * Replace Token.
56
+ *
57
+ * @param token Token.
58
+ * @param newToken NewToken.
59
+ */
60
+ public replace(token: string, newToken: string): boolean {
61
+ const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
62
+ const list = attr ? attr.split(' ') : [];
63
+ const index = list.indexOf(token);
64
+ if (index === -1) {
65
+ return false;
66
+ }
67
+ list[index] = newToken;
68
+ this._ownerElement.setAttributeNS(null, this._attributeName, list.join(' '));
69
+ return true;
70
+ }
71
+
72
+ /**
73
+ * Supports.
74
+ *
75
+ * @param token Token.
76
+ */
77
+ public supports(token: string): boolean {
78
+ // TODO: Only implemented for classList, which does not have any supported tokens
79
+ throw new DOMException(
80
+ `Failed to execute '${token}' on 'DOMTokenList': DOMTokenList has no supported tokens.`,
81
+ 'TypeError'
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Returns an iterator, allowing you to go through all values of the key/value pairs contained in this object.
87
+ *
88
+ *
89
+ */
90
+ public values(): IterableIterator<string> {
91
+ const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
92
+ return (attr ? attr.split(' ') : []).values();
93
+ }
94
+
95
+ /**
96
+ * Returns an iterator, allowing you to go through all key/value pairs contained in this object.
97
+ *
98
+ *
99
+ */
100
+ public entries(): IterableIterator<[number, string]> {
101
+ const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
102
+ return (attr ? attr.split(' ') : []).entries();
103
+ }
104
+
105
+ /**
106
+ * Executes a provided callback function once for each DOMTokenList element.
107
+ *
108
+ * @param callback
109
+ * @param thisArg
110
+ */
111
+ public forEach(callback: (currentValue, currentIndex, listObj) => void, thisArg?: this): void {
112
+ const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
113
+ return (attr ? attr.split(' ') : []).forEach(callback, thisArg);
114
+ }
115
+
116
+ /**
117
+ * Returns an iterator, allowing you to go through all keys of the key/value pairs contained in this object.
118
+ *
119
+ */
120
+ public keys(): IterableIterator<number> {
121
+ const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
122
+ return (attr ? attr.split(' ') : []).keys();
123
+ }
124
+
125
+ /**
126
+ * Adds tokens.
127
+ *
128
+ * @param tokens Tokens.
129
+ */
130
+ public add(...tokens: string[]): void {
131
+ const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
132
+ const list = attr ? attr.split(' ') : [];
133
+
134
+ for (const token of tokens) {
135
+ const index = list.indexOf(token);
136
+ if (index === -1) {
137
+ list.push(token);
138
+ } else {
139
+ list[index] = token;
140
+ }
141
+ }
142
+
143
+ this._ownerElement.setAttributeNS(null, this._attributeName, list.join(' '));
144
+ }
145
+
146
+ /**
147
+ * Removes tokens.
148
+ *
149
+ * @param tokens Tokens.
150
+ */
151
+ public remove(...tokens: string[]): void {
152
+ const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
153
+ const list = attr ? attr.split(' ') : [];
154
+
155
+ for (const token of tokens) {
156
+ const index = list.indexOf(token);
157
+ if (index !== -1) {
158
+ list.splice(index, 1);
159
+ }
160
+ }
161
+
162
+ this._ownerElement.setAttributeNS(null, this._attributeName, list.join(' '));
163
+ }
164
+
165
+ /**
166
+ * Check if the list contains a class.
167
+ *
168
+ * @param className Class name.
169
+ * @returns TRUE if it contains.
170
+ */
171
+ public contains(className: string): boolean {
172
+ const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
173
+ return (attr ? attr.split(' ') : []).includes(className);
174
+ }
175
+
176
+ /**
177
+ * Toggle a class name.
178
+ *
179
+ * @param token A string representing the class name you want to toggle.
180
+ * @param [force] If included, turns the toggle into a one way-only operation. If set to `false`, then class name will only be removed, but not added. If set to `true`, then class name will only be added, but not removed.
181
+ * @returns A boolean value, `true` or `false`, indicating whether class name is in the list after the call or not.
182
+ */
183
+ public toggle(token: string, force?: boolean): boolean {
184
+ let shouldAdd: boolean;
185
+
186
+ if (force !== undefined) {
187
+ shouldAdd = force;
188
+ } else {
189
+ shouldAdd = !this.contains(token);
190
+ }
191
+
192
+ if (shouldAdd) {
193
+ this.add(token);
194
+ return true;
195
+ }
196
+
197
+ this.remove(token);
198
+
199
+ return false;
200
+ }
201
+
202
+ /**
203
+ * Updates indices.
204
+ */
205
+ public _updateIndices(): void {
206
+ const attr = this._ownerElement.getAttribute('class');
207
+ const list = attr ? Array.from(new Set(attr.split(' '))) : [];
208
+
209
+ for (let i = list.length - 1, max = this.length; i < max; i++) {
210
+ delete this[i];
211
+ }
212
+
213
+ for (let i = 0, max = list.length; i < max; i++) {
214
+ this[i] = list[i];
215
+ }
216
+
217
+ (<number>this.length) = list.length;
218
+ }
219
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * IDOMTokenList.
3
+ */
4
+ export default interface IDOMTokenList {
5
+ value: string;
6
+ readonly length: number;
7
+ item(index: number | string): string;
8
+ contains(token: string): boolean;
9
+ add(...tokens: string[]): void;
10
+ remove(...tokens: string[]): void;
11
+ toggle(token: string, force?: boolean): boolean;
12
+ replace(token: string, newToken: string): boolean;
13
+ supports(token: string): boolean;
14
+
15
+ values(): IterableIterator<string>;
16
+ entries(): IterableIterator<[number, string]>;
17
+ forEach(callback: (currentValue, currentIndex, listObj) => void, thisArg?: this): void;
18
+ keys(): IterableIterator<number>;
19
+ }
@@ -21,6 +21,7 @@ import CSSStyleSheet from '../../css/CSSStyleSheet';
21
21
  import DOMException from '../../exception/DOMException';
22
22
  import CookieUtility from '../../cookie/CookieUtility';
23
23
  import IElement from '../element/IElement';
24
+ import IHTMLScriptElement from '../html-script-element/IHTMLScriptElement';
24
25
  import IHTMLElement from '../html-element/IHTMLElement';
25
26
  import IDocumentType from '../document-type/IDocumentType';
26
27
  import INode from '../node/INode';
@@ -240,6 +241,15 @@ export default class Document extends Node implements IDocument {
240
241
  return this._defaultView.location;
241
242
  }
242
243
 
244
+ /**
245
+ * Returns scripts.
246
+ *
247
+ * @returns Scripts.
248
+ */
249
+ public get scripts(): IHTMLCollection<IHTMLScriptElement> {
250
+ return <IHTMLCollection<IHTMLScriptElement>>this.getElementsByTagName('script');
251
+ }
252
+
243
253
  /**
244
254
  * Inserts a set of Node objects or DOMString objects after the last child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
245
255
  *
@@ -12,6 +12,11 @@ import INode from '../node/INode';
12
12
  import ICharacterData from '../character-data/ICharacterData';
13
13
  import IDocumentFragment from '../document-fragment/IDocumentFragment';
14
14
  import Selection from '../../selection/Selection';
15
+ import IHTMLCollection from '../element/IHTMLCollection';
16
+ import IHTMLScriptElement from '../html-script-element/IHTMLScriptElement';
17
+ import CSSStyleSheet from '../../css/CSSStyleSheet';
18
+ import Location from '../../location/Location';
19
+ import DocumentReadyStateEnum from './DocumentReadyStateEnum';
15
20
 
16
21
  /**
17
22
  * Document.
@@ -24,7 +29,13 @@ export default interface IDocument extends IParentNode {
24
29
  readonly doctype: IDocumentType;
25
30
  readonly body: IHTMLElement;
26
31
  readonly head: IHTMLElement;
32
+ readonly scripts: IHTMLCollection<IHTMLScriptElement>;
27
33
  readonly activeElement: IHTMLElement;
34
+ readonly styleSheets: CSSStyleSheet[];
35
+ readonly scrollingElement: IHTMLElement;
36
+ readonly location: Location;
37
+ readonly readyState: DocumentReadyStateEnum;
38
+ cookie: string;
28
39
 
29
40
  /**
30
41
  * Replaces the document HTML with new HTML.
@@ -69,7 +80,7 @@ export default interface IDocument extends IParentNode {
69
80
  * @param data Text data.
70
81
  * @returns Text node.
71
82
  */
72
- createTextNode(data: string): ICharacterData;
83
+ createTextNode(data?: string): ICharacterData;
73
84
 
74
85
  /**
75
86
  * Creates a comment node.
@@ -77,7 +88,7 @@ export default interface IDocument extends IParentNode {
77
88
  * @param data Text data.
78
89
  * @returns Text node.
79
90
  */
80
- createComment(data: string): ICharacterData;
91
+ createComment(data?: string): ICharacterData;
81
92
 
82
93
  /**
83
94
  * Creates a document fragment.
@@ -3,7 +3,8 @@ import ShadowRoot from '../shadow-root/ShadowRoot';
3
3
  import Attr from '../../attribute/Attr';
4
4
  import DOMRect from './DOMRect';
5
5
  import Range from './Range';
6
- import ClassList from './ClassList';
6
+ import DOMTokenList from '../../dom-token-list/DOMTokenList';
7
+ import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
7
8
  import QuerySelector from '../../query-selector/QuerySelector';
8
9
  import SelectorItem from '../../query-selector/SelectorItem';
9
10
  import MutationRecord from '../../mutation-observer/MutationRecord';
@@ -36,13 +37,26 @@ export default class Element extends Node implements IElement {
36
37
  public tagName: string = null;
37
38
  public nodeType = Node.ELEMENT_NODE;
38
39
  public shadowRoot: IShadowRoot = null;
39
- public readonly classList = new ClassList(this);
40
+ public _attributes: { [k: string]: Attr } = {};
40
41
  public scrollTop = 0;
41
42
  public scrollLeft = 0;
42
43
  public children: IHTMLCollection<IElement> = HTMLCollectionFactory.create();
43
- public _attributes: { [k: string]: Attr } = {};
44
44
  public readonly namespaceURI: string = null;
45
45
 
46
+ private _classList: DOMTokenList = null;
47
+
48
+ /**
49
+ * Returns class list.
50
+ *
51
+ * @returns Class list.
52
+ */
53
+ public get classList(): IDOMTokenList {
54
+ if (!this._classList) {
55
+ this._classList = new DOMTokenList(this, 'class');
56
+ }
57
+ return <IDOMTokenList>this._classList;
58
+ }
59
+
46
60
  /**
47
61
  * Returns ID.
48
62
  *
@@ -769,6 +783,8 @@ export default class Element extends Node implements IElement {
769
783
 
770
784
  this._attributes[name] = attribute;
771
785
 
786
+ this._updateDomListIndices();
787
+
772
788
  if (
773
789
  this.attributeChangedCallback &&
774
790
  (<typeof Element>this.constructor)._observedAttributes &&
@@ -842,6 +858,8 @@ export default class Element extends Node implements IElement {
842
858
  public removeAttributeNode(attribute: Attr): void {
843
859
  delete this._attributes[attribute.name];
844
860
 
861
+ this._updateDomListIndices();
862
+
845
863
  if (
846
864
  this.attributeChangedCallback &&
847
865
  (<typeof Element>this.constructor)._observedAttributes &&
@@ -933,4 +951,13 @@ export default class Element extends Node implements IElement {
933
951
  }
934
952
  return name.toLowerCase();
935
953
  }
954
+
955
+ /**
956
+ * Updates DOM list indices.
957
+ */
958
+ protected _updateDomListIndices(): void {
959
+ if (this._classList) {
960
+ this._classList._updateIndices();
961
+ }
962
+ }
936
963
  }
@@ -2,7 +2,7 @@ import IShadowRoot from '../shadow-root/IShadowRoot';
2
2
  import Attr from '../../attribute/Attr';
3
3
  import DOMRect from './DOMRect';
4
4
  import Range from './Range';
5
- import ClassList from './ClassList';
5
+ import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
6
6
  import INode from './../node/INode';
7
7
  import IChildNode from '../child-node/IChildNode';
8
8
  import IParentNode from '../parent-node/IParentNode';
@@ -16,7 +16,7 @@ export type TInsertAdjacentPositions = 'beforebegin' | 'afterbegin' | 'beforeend
16
16
  export default interface IElement extends IChildNode, INonDocumentTypeChildNode, IParentNode {
17
17
  readonly tagName: string;
18
18
  readonly shadowRoot: IShadowRoot;
19
- readonly classList: ClassList;
19
+ readonly classList: IDOMTokenList;
20
20
  readonly namespaceURI: string;
21
21
  scrollTop: number;
22
22
  scrollLeft: number;
@@ -10,8 +10,6 @@ import IHTMLLabelElement from './IHTMLLabelElement';
10
10
  * https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement.
11
11
  */
12
12
  export default class HTMLLabelElement extends HTMLElement implements IHTMLLabelElement {
13
- public _htmlFor: string = null;
14
-
15
13
  /**
16
14
  * Returns a string containing the ID of the labeled control. This reflects the "for" attribute.
17
15
  *
@@ -73,8 +71,6 @@ export default class HTMLLabelElement extends HTMLElement implements IHTMLLabelE
73
71
  * @returns Cloned node.
74
72
  */
75
73
  public cloneNode(deep = false): IHTMLLabelElement {
76
- const clone = <HTMLLabelElement>super.cloneNode(deep);
77
- clone._htmlFor = this._htmlFor;
78
- return clone;
74
+ return <HTMLLabelElement>super.cloneNode(deep);
79
75
  }
80
76
  }
@@ -7,6 +7,8 @@ import IHTMLLinkElement from './IHTMLLinkElement';
7
7
  import Event from '../../event/Event';
8
8
  import ErrorEvent from '../../event/events/ErrorEvent';
9
9
  import INode from '../../nodes/node/INode';
10
+ import DOMTokenList from '../../dom-token-list/DOMTokenList';
11
+ import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
10
12
 
11
13
  /**
12
14
  * HTML Link Element.
@@ -19,6 +21,19 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
19
21
  public onload: (event: Event) => void = null;
20
22
  public readonly sheet: CSSStyleSheet = null;
21
23
  public _evaluateCSS = true;
24
+ private _relList: DOMTokenList = null;
25
+
26
+ /**
27
+ * Returns rel list.
28
+ *
29
+ * @returns Rel list.
30
+ */
31
+ public get relList(): IDOMTokenList {
32
+ if (!this._relList) {
33
+ this._relList = new DOMTokenList(this, 'rel');
34
+ }
35
+ return <IDOMTokenList>this._relList;
36
+ }
22
37
 
23
38
  /**
24
39
  * Returns as.
@@ -259,4 +274,15 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
259
274
  }
260
275
  }
261
276
  }
277
+
278
+ /**
279
+ * Updates DOM list indices.
280
+ */
281
+ protected _updateDomListIndices(): void {
282
+ super._updateDomListIndices();
283
+
284
+ if (this._relList) {
285
+ this._relList._updateIndices();
286
+ }
287
+ }
262
288
  }
@@ -1,4 +1,5 @@
1
1
  import CSSStyleSheet from '../../css/CSSStyleSheet';
2
+ import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
2
3
  import IHTMLElement from '../html-element/IHTMLElement';
3
4
 
4
5
  /**
@@ -9,6 +10,7 @@ import IHTMLElement from '../html-element/IHTMLElement';
9
10
  */
10
11
  export default interface IHTMLLinkElement extends IHTMLElement {
11
12
  readonly sheet: CSSStyleSheet;
13
+ readonly relList: IDOMTokenList;
12
14
  as: string;
13
15
  crossOrigin: string;
14
16
  href: string;
@@ -27,10 +27,11 @@ export default class SelectorItem {
27
27
  * @param selector Selector.
28
28
  */
29
29
  constructor(selector: string) {
30
+ const isNotPseudo = selector.includes(':not(');
31
+
30
32
  this.isAll = selector === '*';
31
33
  this.isID = !this.isAll ? selector.startsWith('#') : false;
32
- this.isAttribute =
33
- !this.isAll && !this.isID && selector.includes('[') && !selector.includes(':not(');
34
+ this.isAttribute = !this.isAll && !this.isID && selector.includes('[') && !isNotPseudo;
34
35
  this.isPseudo = !this.isAll && !this.isID && selector.includes(':');
35
36
  this.isClass = !this.isAll && !this.isID && new RegExp(CLASS_REGEXP, 'g').test(selector);
36
37
  this.tagName = !this.isAll && !this.isID ? selector.match(TAG_NAME_REGEXP) : null;
@@ -48,7 +49,6 @@ export default class SelectorItem {
48
49
  */
49
50
  public match(element: Element): boolean {
50
51
  const selector = this.selector;
51
- let match;
52
52
 
53
53
  // Is all (*)
54
54
  if (this.isAll) {
@@ -68,29 +68,13 @@ export default class SelectorItem {
68
68
  }
69
69
 
70
70
  // Class match
71
- if (this.isClass) {
72
- const regexp = new RegExp(CLASS_REGEXP, 'g');
73
-
74
- while ((match = regexp.exec(selector))) {
75
- if (!element.classList.contains(match[1])) {
76
- return false;
77
- }
78
- }
71
+ if (this.isClass && !this.matchesClass(element, selector)) {
72
+ return false;
79
73
  }
80
74
 
81
75
  // Pseudo match
82
- if (this.isPseudo) {
83
- const regexp = new RegExp(PSUEDO_REGEXP, 'g');
84
-
85
- while ((match = regexp.exec(selector))) {
86
- if (match[1] && !this.matchesNthChild(element, match[1], match[2])) {
87
- return false;
88
- } else if (match[3] && this.matchesAttribute(element, match[3])) {
89
- return false;
90
- } else if (match[4] && !this.matchesPsuedo(element, match[4])) {
91
- return false;
92
- }
93
- }
76
+ if (this.isPseudo && !this.matchesPsuedo(element, selector)) {
77
+ return false;
94
78
  }
95
79
 
96
80
  // Attribute match
@@ -101,6 +85,35 @@ export default class SelectorItem {
101
85
  return true;
102
86
  }
103
87
 
88
+ /**
89
+ * Matches a psuedo selector.
90
+ *
91
+ * @param element Element.
92
+ * @param selector Selector.
93
+ * @returns True if it is a match.
94
+ */
95
+ private matchesPsuedo(element: Element, selector: string): boolean {
96
+ const regexp = new RegExp(PSUEDO_REGEXP, 'g');
97
+ let match: RegExpMatchArray;
98
+
99
+ while ((match = regexp.exec(selector))) {
100
+ const isNotClass = match[3] && match[3].trim()[0] === '.';
101
+ if (match[1] && !this.matchesNthChild(element, match[1], match[2])) {
102
+ return false;
103
+ } else if (
104
+ match[3] &&
105
+ ((isNotClass && this.matchesClass(element, match[3])) ||
106
+ (!isNotClass && this.matchesAttribute(element, match[3])))
107
+ ) {
108
+ return false;
109
+ } else if (match[4] && !this.matchesPsuedoExpression(element, match[4])) {
110
+ return false;
111
+ }
112
+ }
113
+
114
+ return true;
115
+ }
116
+
104
117
  /**
105
118
  * Matches a nth-child selector.
106
119
  *
@@ -158,13 +171,13 @@ export default class SelectorItem {
158
171
  }
159
172
 
160
173
  /**
161
- * Matches a psuedo selector.
174
+ * Matches a psuedo selector expression.
162
175
  *
163
176
  * @param element Element.
164
177
  * @param psuedo Psuedo name.
165
178
  * @returns True if it is a match.
166
179
  */
167
- private matchesPsuedo(element: Element, psuedo: string): boolean {
180
+ private matchesPsuedoExpression(element: Element, psuedo: string): boolean {
168
181
  const parent = <Element>element.parentNode;
169
182
 
170
183
  if (!parent) {
@@ -220,7 +233,7 @@ export default class SelectorItem {
220
233
  */
221
234
  private matchesAttribute(element: Element, selector: string): boolean {
222
235
  const regexp = new RegExp(ATTRIBUTE_REGEXP, 'g');
223
- let match;
236
+ let match: RegExpMatchArray;
224
237
 
225
238
  while ((match = regexp.exec(selector))) {
226
239
  const isPsuedo = match.index > 0 && selector[match.index] === '(';
@@ -236,6 +249,29 @@ export default class SelectorItem {
236
249
  return true;
237
250
  }
238
251
 
252
+ /**
253
+ * Matches class.
254
+ *
255
+ * @param element Element.
256
+ * @param selector Selector.
257
+ * @returns True if it is a match.
258
+ */
259
+ private matchesClass(element: Element, selector: string): boolean {
260
+ const regexp = new RegExp(CLASS_REGEXP, 'g');
261
+ const classList = element.className.split(' ');
262
+ let previousIndex = 0;
263
+ let match: RegExpMatchArray;
264
+
265
+ while ((match = regexp.exec(selector)) && match.index === previousIndex) {
266
+ if (!classList.includes(match[1])) {
267
+ return false;
268
+ }
269
+ previousIndex = match.index + match[0].length - 1;
270
+ }
271
+
272
+ return true;
273
+ }
274
+
239
275
  /**
240
276
  * Matches attribute name only.
241
277
  *
@@ -69,11 +69,12 @@ import URLSearchParams from '../url-search-params/URLSearchParams';
69
69
  import HTMLCollection from '../nodes/element/HTMLCollection';
70
70
  import NodeList from '../nodes/node/NodeList';
71
71
  import Selection from '../selection/Selection';
72
+ import IEventTarget from '../event/IEventTarget';
72
73
 
73
74
  /**
74
75
  * Window.
75
76
  */
76
- export default interface IWindow {
77
+ export default interface IWindow extends IEventTarget {
77
78
  // Public Properties
78
79
  readonly happyDOM: {
79
80
  whenAsyncComplete: () => Promise<void>;
@@ -166,6 +167,7 @@ export default interface IWindow {
166
167
  readonly top: IWindow;
167
168
  readonly parent: IWindow;
168
169
  readonly window: IWindow;
170
+ readonly globalThis: IWindow;
169
171
  readonly screen: Screen;
170
172
  readonly innerWidth: number;
171
173
  readonly innerHeight: number;
@@ -178,6 +178,7 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
178
178
  public readonly top = this;
179
179
  public readonly parent = this;
180
180
  public readonly window = this;
181
+ public readonly globalThis = this;
181
182
  public readonly screen = new Screen();
182
183
  public readonly innerWidth = 1024;
183
184
  public readonly innerHeight = 768;
@@ -1,40 +0,0 @@
1
- import Element from './Element';
2
- /**
3
- * Class list.
4
- */
5
- export default class ClassList {
6
- private _ownerElement;
7
- /**
8
- * Adds class names.
9
- *
10
- * @param ownerElement Owner element.
11
- */
12
- constructor(ownerElement: Element);
13
- /**
14
- * Adds class names.
15
- *
16
- * @param classNames Class names.
17
- */
18
- add(...classNames: string[]): void;
19
- /**
20
- * Adds class names.
21
- *
22
- * @param classNames Class names.
23
- */
24
- remove(...classNames: string[]): void;
25
- /**
26
- * Check if the list contains a class.
27
- *
28
- * @param className Class name.
29
- * @returns TRUE if it contains.
30
- */
31
- contains(className: string): boolean;
32
- /**
33
- * Toggle a class name.
34
- *
35
- * @param className A string representing the class name you want to toggle.
36
- * @param force If included, turns the toggle into a one way-only operation. If set to `false`, then class name will only be removed, but not added. If set to `true`, then class name will only be added, but not removed.
37
- * @returns A boolean value, `true` or `false`, indicating whether class name is in the list after the call or not.
38
- */
39
- toggle(className: string, force?: boolean): boolean;
40
- }