happy-dom 2.42.0 → 2.45.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 (67) hide show
  1. package/lib/css/CSSStyleDeclaration.js +1 -1
  2. package/lib/css/CSSStyleDeclaration.js.map +1 -1
  3. package/lib/{nodes/element → dom-token-list}/DOMTokenList.d.ts +32 -29
  4. package/lib/dom-token-list/DOMTokenList.js +209 -0
  5. package/lib/dom-token-list/DOMTokenList.js.map +1 -0
  6. package/lib/{nodes/element → dom-token-list}/IDOMTokenList.d.ts +6 -4
  7. package/lib/{nodes/element → dom-token-list}/IDOMTokenList.js +0 -0
  8. package/lib/dom-token-list/IDOMTokenList.js.map +1 -0
  9. package/lib/navigator/MimeType.d.ts +25 -0
  10. package/lib/navigator/MimeType.js +32 -0
  11. package/lib/navigator/MimeType.js.map +1 -0
  12. package/lib/navigator/MimeTypeArray.d.ts +31 -0
  13. package/lib/navigator/MimeTypeArray.js +45 -0
  14. package/lib/navigator/MimeTypeArray.js.map +1 -0
  15. package/lib/navigator/Navigator.d.ts +41 -0
  16. package/lib/navigator/Navigator.js +78 -0
  17. package/lib/navigator/Navigator.js.map +1 -0
  18. package/lib/navigator/Plugin.d.ts +40 -0
  19. package/lib/navigator/Plugin.js +55 -0
  20. package/lib/navigator/Plugin.js.map +1 -0
  21. package/lib/navigator/PluginArray.d.ts +38 -0
  22. package/lib/navigator/PluginArray.js +54 -0
  23. package/lib/navigator/PluginArray.js.map +1 -0
  24. package/lib/nodes/document/Document.d.ts +2 -1
  25. package/lib/nodes/document/Document.js.map +1 -1
  26. package/lib/nodes/document/IDocument.d.ts +13 -2
  27. package/lib/nodes/element/Element.d.ts +12 -2
  28. package/lib/nodes/element/Element.js +27 -2
  29. package/lib/nodes/element/Element.js.map +1 -1
  30. package/lib/nodes/element/IElement.d.ts +2 -2
  31. package/lib/nodes/html-label-element/HTMLLabelElement.d.ts +0 -1
  32. package/lib/nodes/html-label-element/HTMLLabelElement.js +2 -6
  33. package/lib/nodes/html-label-element/HTMLLabelElement.js.map +1 -1
  34. package/lib/nodes/html-link-element/HTMLLinkElement.d.ts +12 -0
  35. package/lib/nodes/html-link-element/HTMLLinkElement.js +26 -0
  36. package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
  37. package/lib/nodes/html-link-element/IHTMLLinkElement.d.ts +2 -0
  38. package/lib/query-selector/SelectorItem.d.ts +18 -2
  39. package/lib/query-selector/SelectorItem.js +54 -25
  40. package/lib/query-selector/SelectorItem.js.map +1 -1
  41. package/lib/window/IWindow.d.ts +13 -4
  42. package/lib/window/Window.d.ts +11 -3
  43. package/lib/window/Window.js +11 -1
  44. package/lib/window/Window.js.map +1 -1
  45. package/package.json +2 -2
  46. package/src/css/CSSStyleDeclaration.ts +1 -1
  47. package/src/dom-token-list/DOMTokenList.ts +219 -0
  48. package/src/{nodes/element → dom-token-list}/IDOMTokenList.ts +6 -4
  49. package/src/navigator/MimeType.ts +35 -0
  50. package/src/navigator/MimeTypeArray.ts +48 -0
  51. package/src/navigator/Navigator.ts +93 -0
  52. package/src/navigator/Plugin.ts +61 -0
  53. package/src/navigator/PluginArray.ts +58 -0
  54. package/src/nodes/document/Document.ts +3 -2
  55. package/src/nodes/document/IDocument.ts +13 -2
  56. package/src/nodes/element/Element.ts +29 -3
  57. package/src/nodes/element/IElement.ts +2 -2
  58. package/src/nodes/html-label-element/HTMLLabelElement.ts +1 -5
  59. package/src/nodes/html-link-element/HTMLLinkElement.ts +26 -0
  60. package/src/nodes/html-link-element/IHTMLLinkElement.ts +2 -0
  61. package/src/query-selector/SelectorItem.ts +62 -26
  62. package/src/window/IWindow.ts +13 -2
  63. package/src/window/Window.ts +11 -1
  64. package/lib/nodes/element/DOMTokenList.js +0 -205
  65. package/lib/nodes/element/DOMTokenList.js.map +0 -1
  66. package/lib/nodes/element/IDOMTokenList.js.map +0 -1
  67. package/src/nodes/element/DOMTokenList.ts +0 -201
@@ -0,0 +1,48 @@
1
+ import MimeType from './MimeType';
2
+
3
+ /**
4
+ * MimeTypeArray.
5
+ *
6
+ * Reference:
7
+ * https://developer.mozilla.org/en-US/docs/Web/API/MimeTypeArray.
8
+ */
9
+ export default class MimeTypeArray {
10
+ [n: number]: MimeType;
11
+ public readonly length: number;
12
+
13
+ /**
14
+ * Constructor.
15
+ *
16
+ * @param mimeTypes
17
+ */
18
+ constructor(mimeTypes: MimeType[]) {
19
+ for (let i = 0, max = mimeTypes.length; i < max; i++) {
20
+ this[i] = mimeTypes[i];
21
+ this[mimeTypes[i].type] = mimeTypes[i];
22
+ }
23
+ this.length = mimeTypes.length;
24
+ }
25
+
26
+ /**
27
+ * @param index
28
+ */
29
+ public item(index: number): MimeType {
30
+ return this[index] || null;
31
+ }
32
+
33
+ /**
34
+ * @param name
35
+ */
36
+ public namedItem(name: string): MimeType {
37
+ return this[name] || null;
38
+ }
39
+
40
+ /**
41
+ * Returns the object as a string.
42
+ *
43
+ * @returns String.
44
+ */
45
+ public toString(): string {
46
+ return '[object MimeTypeArray]';
47
+ }
48
+ }
@@ -0,0 +1,93 @@
1
+ import MimeTypeArray from './MimeTypeArray';
2
+ import PluginArray from './PluginArray';
3
+
4
+ /**
5
+ * Browser Navigator API.
6
+ *
7
+ * Mocked information is taken from FireFox.
8
+ *
9
+ * Reference:
10
+ * https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator.
11
+ */
12
+ export default class Navigator {
13
+ // False if setting a cookie will be ignored and true otherwise.
14
+ public readonly cookieEnabled: boolean = true;
15
+
16
+ // TODO: Not implemented.
17
+ public readonly credentials: string = null;
18
+
19
+ // TODO: Not implemented.
20
+ public readonly geolocation: string = null;
21
+
22
+ // String representing the preferred language of the user, usually the language of the browser UI.
23
+ public readonly language: string = 'en-US';
24
+
25
+ // Array of string representing the user's preferred languages.
26
+ public readonly languages: string[] = ['en-US', 'en'];
27
+
28
+ // TODO: Not implemented.
29
+ public readonly locks: string = null;
30
+
31
+ // Maximum number of simultaneous touch contact points are supported by the current device.
32
+ public readonly maxTouchPoints: number = 0;
33
+
34
+ // Number of logical processors available to run threads on the user's computer.
35
+ public readonly hardwareConcurrency: number = 8;
36
+
37
+ // Browser app code name.
38
+ public readonly appCodeName: string = 'Mozilla';
39
+
40
+ // Browser app name.
41
+ public readonly appName: string = 'Netscape';
42
+
43
+ // Browser app version.
44
+ public readonly appVersion: string = '5.0 (Windows)';
45
+
46
+ // Browser platform.
47
+ public readonly platform: string = 'Win32';
48
+
49
+ // Browser product.
50
+ public readonly product: string = 'Gecko';
51
+
52
+ // Browser product sub.
53
+ public readonly productSub: string = '20100101';
54
+
55
+ // Browser vendor.
56
+ public readonly vendor: string = '';
57
+
58
+ // Browser vendor sub.
59
+ public readonly vendorSub: string = '';
60
+
61
+ // Browser user agent.
62
+ // "appCodeName/appVersion number (Platform; Security; OS-or-CPU; Localization; rv: revision-version-number) product/productSub Application-Name Application-Name-version".
63
+ public readonly userAgent: string =
64
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0';
65
+
66
+ // Boolean value indicating whether the browser is working online.
67
+ public readonly onLine: boolean = true;
68
+
69
+ // TODO: Not implemented.
70
+ public readonly permissions: string = null;
71
+
72
+ // Boolean Indicates whether the user agent is controlled by automation.
73
+ public readonly webdriver: boolean = true;
74
+
75
+ // The user's Do Not Track setting, which indicates whether the user is requesting web sites and advertisers to not track them.
76
+ // The value of the property reflects that of the DNT HTTP header, i.e. Values of "1", "0", or "unspecified".
77
+ public readonly doNotTrack: string = 'unspecified';
78
+
79
+ // Browser mime-types.
80
+ public readonly mimeTypes: MimeTypeArray = new MimeTypeArray([]);
81
+
82
+ // Browser plugins.
83
+ public readonly plugins: PluginArray = new PluginArray([]);
84
+
85
+ /**
86
+ * Returns the object as a string.
87
+ *
88
+ * @returns String.
89
+ */
90
+ public toString(): string {
91
+ return '[object Navigator]';
92
+ }
93
+ }
@@ -0,0 +1,61 @@
1
+ import MimeType from './MimeType';
2
+
3
+ /**
4
+ * Plugin.
5
+ */
6
+ export default class Plugin {
7
+ [n: number]: MimeType;
8
+ public readonly length: number = 0;
9
+ public readonly description: string;
10
+ public readonly filename: string;
11
+ public readonly name: string;
12
+
13
+ /**
14
+ * Constructor.
15
+ *
16
+ * @param mimeTypes Mime types.
17
+ * @param description Description.
18
+ * @param filename Filename.
19
+ * @param name Name.
20
+ */
21
+ constructor(mimeTypes: MimeType[], description: string, filename: string, name: string) {
22
+ this.description = description;
23
+ this.filename = filename;
24
+ this.name = name;
25
+
26
+ for (let i = 0, max = mimeTypes.length; i < max; i++) {
27
+ this[i] = mimeTypes[i];
28
+ this[mimeTypes[i].type] = mimeTypes[i];
29
+ }
30
+ this.length = mimeTypes.length;
31
+ }
32
+
33
+ /**
34
+ * Item.
35
+ *
36
+ * @param index Number.
37
+ * @returns IMimeType.
38
+ */
39
+ public item(index: number): MimeType {
40
+ return this[index] || null;
41
+ }
42
+
43
+ /**
44
+ * NamedItem.
45
+ *
46
+ * @param name String.
47
+ * @returns IMimeType.
48
+ */
49
+ public namedItem(name: string): MimeType {
50
+ return this[name] || null;
51
+ }
52
+
53
+ /**
54
+ * Returns the object as a string.
55
+ *
56
+ * @returns String.
57
+ */
58
+ public toString(): string {
59
+ return '[object Plugin]';
60
+ }
61
+ }
@@ -0,0 +1,58 @@
1
+ import Plugin from './Plugin';
2
+
3
+ /**
4
+ * PluginArray.
5
+ */
6
+ export default class PluginArray {
7
+ [n: number]: Plugin;
8
+ public readonly length: number;
9
+
10
+ /**
11
+ * Constructor.
12
+ *
13
+ * @param plugins Plugins.
14
+ */
15
+ constructor(plugins: Plugin[]) {
16
+ for (let i = 0, max = plugins.length; i < max; i++) {
17
+ this[i] = plugins[i];
18
+ this[plugins[i].name] = plugins[i];
19
+ }
20
+ this.length = plugins.length;
21
+ }
22
+
23
+ /**
24
+ * Returns an item.
25
+ *
26
+ * @param index Index.
27
+ * @returns Plugin.
28
+ */
29
+ public item(index: number): Plugin {
30
+ return this[index] || null;
31
+ }
32
+
33
+ /**
34
+ * Returns an item.
35
+ *
36
+ * @param name Name.
37
+ * @returns Plugin.
38
+ */
39
+ public namedItem(name: string): Plugin {
40
+ return this[name] || null;
41
+ }
42
+
43
+ /**
44
+ * Refreshes the list.
45
+ */
46
+ public refresh(): void {
47
+ // Do nothing
48
+ }
49
+
50
+ /**
51
+ * Returns the object as a string.
52
+ *
53
+ * @returns String.
54
+ */
55
+ public toString(): string {
56
+ return '[object PluginArray]';
57
+ }
58
+ }
@@ -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';
@@ -245,8 +246,8 @@ export default class Document extends Node implements IDocument {
245
246
  *
246
247
  * @returns Scripts.
247
248
  */
248
- public get scripts(): IHTMLCollection<IElement> {
249
- return this.getElementsByTagName('script');
249
+ public get scripts(): IHTMLCollection<IHTMLScriptElement> {
250
+ return <IHTMLCollection<IHTMLScriptElement>>this.getElementsByTagName('script');
250
251
  }
251
252
 
252
253
  /**
@@ -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 DOMTokenList from './DOMTokenList';
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';
@@ -37,13 +38,25 @@ export default class Element extends Node implements IElement {
37
38
  public nodeType = Node.ELEMENT_NODE;
38
39
  public shadowRoot: IShadowRoot = null;
39
40
  public _attributes: { [k: string]: Attr } = {};
40
- public readonly classList = new DOMTokenList(this);
41
41
  public scrollTop = 0;
42
42
  public scrollLeft = 0;
43
43
  public children: IHTMLCollection<IElement> = HTMLCollectionFactory.create();
44
-
45
44
  public readonly namespaceURI: string = null;
46
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
+
47
60
  /**
48
61
  * Returns ID.
49
62
  *
@@ -770,6 +783,8 @@ export default class Element extends Node implements IElement {
770
783
 
771
784
  this._attributes[name] = attribute;
772
785
 
786
+ this._updateDomListIndices();
787
+
773
788
  if (
774
789
  this.attributeChangedCallback &&
775
790
  (<typeof Element>this.constructor)._observedAttributes &&
@@ -843,6 +858,8 @@ export default class Element extends Node implements IElement {
843
858
  public removeAttributeNode(attribute: Attr): void {
844
859
  delete this._attributes[attribute.name];
845
860
 
861
+ this._updateDomListIndices();
862
+
846
863
  if (
847
864
  this.attributeChangedCallback &&
848
865
  (<typeof Element>this.constructor)._observedAttributes &&
@@ -934,4 +951,13 @@ export default class Element extends Node implements IElement {
934
951
  }
935
952
  return name.toLowerCase();
936
953
  }
954
+
955
+ /**
956
+ * Updates DOM list indices.
957
+ */
958
+ protected _updateDomListIndices(): void {
959
+ if (this._classList) {
960
+ this._classList._updateIndices();
961
+ }
962
+ }
937
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 DOMTokenList from './DOMTokenList';
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: DOMTokenList;
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,17 @@ 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';
73
+ import Navigator from '../navigator/Navigator';
74
+ import MimeType from '../navigator/MimeType';
75
+ import MimeTypeArray from '../navigator/MimeTypeArray';
76
+ import Plugin from '../navigator/Plugin';
77
+ import PluginArray from '../navigator/PluginArray';
72
78
 
73
79
  /**
74
80
  * Window.
75
81
  */
76
- export default interface IWindow {
82
+ export default interface IWindow extends IEventTarget {
77
83
  // Public Properties
78
84
  readonly happyDOM: {
79
85
  whenAsyncComplete: () => Promise<void>;
@@ -150,6 +156,11 @@ export default interface IWindow {
150
156
  readonly CSSUnitValue: typeof CSSUnitValue;
151
157
  readonly CSS: CSS;
152
158
  readonly Selection: typeof Selection;
159
+ readonly Navigator: typeof Navigator;
160
+ readonly MimeType: typeof MimeType;
161
+ readonly MimeTypeArray: typeof MimeTypeArray;
162
+ readonly Plugin: typeof Plugin;
163
+ readonly PluginArray: typeof PluginArray;
153
164
 
154
165
  // Events
155
166
  onload: (event: Event) => void;
@@ -160,7 +171,7 @@ export default interface IWindow {
160
171
  readonly customElements: CustomElementRegistry;
161
172
  readonly location: Location;
162
173
  readonly history: History;
163
- readonly navigator: { userAgent: string };
174
+ readonly navigator: Navigator;
164
175
  readonly console: Console;
165
176
  readonly self: IWindow;
166
177
  readonly top: IWindow;