happy-dom 2.40.1 → 2.41.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.

@@ -10,7 +10,36 @@ export default class CSSStyleDeclaration {
10
10
  private _attributes: { [k: string]: Attr } = null;
11
11
  private _computedStyleElement: { isConnected: boolean } = null;
12
12
 
13
- /* eslint-disable require-jsdoc */
13
+ /**
14
+ * Constructor.
15
+ *
16
+ * @param [attributes] Attributes.
17
+ * @param [computedStyleElement] Computed style element.
18
+ * @param computedStyleElement.isConnected
19
+ */
20
+ constructor(
21
+ attributes: { [k: string]: Attr } = {},
22
+ computedStyleElement: { isConnected: boolean } = null
23
+ ) {
24
+ const style = attributes['style'];
25
+ let index = 0;
26
+
27
+ this._attributes = attributes;
28
+ this._computedStyleElement = computedStyleElement;
29
+
30
+ if (style && style.value) {
31
+ const parts = style.value.split(';');
32
+ for (const part of parts) {
33
+ if (part) {
34
+ const [name] = part.trim().split(':');
35
+ this[index] = name;
36
+ index++;
37
+ }
38
+ }
39
+ }
40
+
41
+ (<number>this.length) = index;
42
+ }
14
43
 
15
44
  /**
16
45
  *
@@ -4809,8 +4838,6 @@ export default class CSSStyleDeclaration {
4809
4838
  this.setProperty('zoom', zoom);
4810
4839
  }
4811
4840
 
4812
- /* eslint-enable require-jsdoc */
4813
-
4814
4841
  /**
4815
4842
  * Returns the style decleration as a CSS text.
4816
4843
  *
@@ -4844,7 +4871,11 @@ export default class CSSStyleDeclaration {
4844
4871
  for (const part of parts) {
4845
4872
  if (part) {
4846
4873
  const [name, value] = part.trim().split(':');
4847
- newStyle.push(`${name}: ${value.trim()};`);
4874
+ if (value) {
4875
+ newStyle.push(`${name}: ${value.trim()};`);
4876
+ } else {
4877
+ newStyle.push(name);
4878
+ }
4848
4879
  this[index] = name;
4849
4880
  index++;
4850
4881
  }
@@ -4860,37 +4891,6 @@ export default class CSSStyleDeclaration {
4860
4891
  }
4861
4892
  }
4862
4893
 
4863
- /**
4864
- * Constructor.
4865
- *
4866
- * @param [attributes] Attributes.
4867
- * @param [computedStyleElement] Computed style element.
4868
- * @param computedStyleElement.isConnected
4869
- */
4870
- constructor(
4871
- attributes: { [k: string]: Attr } = {},
4872
- computedStyleElement: { isConnected: boolean } = null
4873
- ) {
4874
- const style = attributes['style'];
4875
- let index = 0;
4876
-
4877
- this._attributes = attributes;
4878
- this._computedStyleElement = computedStyleElement;
4879
-
4880
- if (style && style.value) {
4881
- const parts = style.value.split(';');
4882
- for (const part of parts) {
4883
- if (part) {
4884
- const [name] = part.trim().split(':');
4885
- this[index] = name;
4886
- index++;
4887
- }
4888
- }
4889
- }
4890
-
4891
- (<number>this.length) = index;
4892
- }
4893
-
4894
4894
  /**
4895
4895
  * Returns item.
4896
4896
  *
@@ -4932,8 +4932,10 @@ export default class CSSStyleDeclaration {
4932
4932
  if (name === propertyName) {
4933
4933
  newStyle.push(`${name}: ${value};`);
4934
4934
  isExisting = true;
4935
- } else {
4935
+ } else if (existingValue) {
4936
4936
  newStyle.push(`${name}: ${existingValue.trim()};`);
4937
+ } else {
4938
+ newStyle.push(`${name};`);
4937
4939
  }
4938
4940
 
4939
4941
  this[index] = name;
@@ -5011,6 +5013,9 @@ export default class CSSStyleDeclaration {
5011
5013
  if (part) {
5012
5014
  const [name, value] = part.trim().split(':');
5013
5015
  if (name === propertyName) {
5016
+ if (!value) {
5017
+ return '';
5018
+ }
5014
5019
  return value.trim();
5015
5020
  }
5016
5021
  }
package/src/index.ts CHANGED
@@ -106,6 +106,7 @@ import ChildLessElements from './config/ChildLessElements';
106
106
  import CSSStyleSheet from './css/CSSStyleSheet';
107
107
  import Storage from './storage/Storage';
108
108
  import URLSearchParams from './url-search-params/URLSearchParams';
109
+ import Selection from './selection/Selection';
109
110
 
110
111
  export {
111
112
  AsyncWindow,
@@ -215,5 +216,6 @@ export {
215
216
  ChildLessElements,
216
217
  CSSStyleSheet,
217
218
  Storage,
218
- URLSearchParams
219
+ URLSearchParams,
220
+ Selection
219
221
  };
@@ -35,6 +35,7 @@ import IHTMLStyleElement from '../html-style-element/IHTMLStyleElement';
35
35
  import DocumentReadyStateEnum from './DocumentReadyStateEnum';
36
36
  import DocumentReadyStateManager from './DocumentReadyStateManager';
37
37
  import Location from '../../location/Location';
38
+ import Selection from '../../selection/Selection';
38
39
 
39
40
  /**
40
41
  * Document.
@@ -699,6 +700,15 @@ export default class Document extends Node implements IDocument {
699
700
  return adopted;
700
701
  }
701
702
 
703
+ /**
704
+ * Returns selection.
705
+ *
706
+ * @returns Selection.
707
+ */
708
+ public getSelection(): Selection {
709
+ return new Selection();
710
+ }
711
+
702
712
  /**
703
713
  * @override
704
714
  */
@@ -11,6 +11,7 @@ import IParentNode from '../parent-node/IParentNode';
11
11
  import INode from '../node/INode';
12
12
  import ICharacterData from '../character-data/ICharacterData';
13
13
  import IDocumentFragment from '../document-fragment/IDocumentFragment';
14
+ import Selection from '../../selection/Selection';
14
15
 
15
16
  /**
16
17
  * Document.
@@ -152,4 +153,11 @@ export default interface IDocument extends IParentNode {
152
153
  * @returns Adopted node.
153
154
  */
154
155
  adoptNode(node: INode): INode;
156
+
157
+ /**
158
+ * Returns selection.
159
+ *
160
+ * @returns Selection.
161
+ */
162
+ getSelection(): Selection;
155
163
  }
@@ -4,6 +4,7 @@ import CSSStyleDeclaration from '../../css/CSSStyleDeclaration';
4
4
  import Attr from '../../attribute/Attr';
5
5
  import FocusEvent from '../../event/events/FocusEvent';
6
6
  import PointerEvent from '../../event/events/PointerEvent';
7
+ import Node from '../node/Node';
7
8
 
8
9
  /**
9
10
  * HTML Element.
@@ -49,21 +50,34 @@ export default class HTMLElement extends Element implements IHTMLElement {
49
50
  }
50
51
 
51
52
  /**
52
- * Returns inner text.
53
+ * Returns inner text, which is the rendered appearance of text.
53
54
  *
54
- * @returns Text.
55
+ * @returns Inner text.
55
56
  */
56
57
  public get innerText(): string {
57
- return this.textContent;
58
+ let result = '';
59
+ for (const childNode of this.childNodes) {
60
+ if (childNode instanceof HTMLElement) {
61
+ if (childNode.tagName !== 'SCRIPT' && childNode.tagName !== 'STYLE') {
62
+ result += childNode.innerText;
63
+ }
64
+ } else if (
65
+ childNode.nodeType === Node.ELEMENT_NODE ||
66
+ childNode.nodeType === Node.TEXT_NODE
67
+ ) {
68
+ result += childNode.textContent;
69
+ }
70
+ }
71
+ return result;
58
72
  }
59
73
 
60
74
  /**
61
- * Sets inner text.
75
+ * Sets the inner text, which is the rendered appearance of text.
62
76
  *
63
- * @param text Text.
77
+ * @param innerText Inner text.
64
78
  */
65
- public set innerText(text: string) {
66
- this.textContent = text;
79
+ public set innerText(innerText: string) {
80
+ this.textContent = innerText;
67
81
  }
68
82
 
69
83
  /**
@@ -0,0 +1,140 @@
1
+ import INode from '../nodes/node/INode';
2
+
3
+ /**
4
+ * Selection.
5
+ *
6
+ * Reference:
7
+ * https://developer.mozilla.org/en-US/docs/Web/API/Selection.
8
+ */
9
+ export default class Selection {
10
+ public readonly anchorNode: INode = null;
11
+ public readonly anchorOffset: number = 0;
12
+ public readonly baseNode: INode = null;
13
+ public readonly baseOffset: number = 0;
14
+ public readonly extentNode: INode = null;
15
+ public readonly extentOffset: number = 0;
16
+ public readonly focusNode: INode = null;
17
+ public readonly focusOffset: number = 0;
18
+ public readonly isCollapsed: boolean = true;
19
+ public readonly rangeCount: number = 0;
20
+ public readonly type: string = 'None';
21
+
22
+ /**
23
+ * Adds a range.
24
+ *
25
+ * @param _range Range.
26
+ */
27
+ public addRange(_range: object): void {
28
+ // Do nothing.
29
+ }
30
+
31
+ /**
32
+ * Collapses the current selection to a single point.
33
+ *
34
+ * @param _node Node.
35
+ * @param _offset Offset.
36
+ */
37
+ public collapse(_node: INode, _offset?: number): void {
38
+ // Do nothing.
39
+ }
40
+
41
+ /**
42
+ * Collapses the selection to the end.
43
+ */
44
+ public collapseToEnd(): void {
45
+ // Do nothing.
46
+ }
47
+
48
+ /**
49
+ * Collapses the selection to the start.
50
+ */
51
+ public collapseToStart(): void {
52
+ // Do nothing.
53
+ }
54
+
55
+ /**
56
+ * Indicates whether a specified node is part of the selection.
57
+ *
58
+ * @param _node Node.
59
+ * @param _partialContainer Partial container.
60
+ * @returns Always returns "true" for now.
61
+ */
62
+ public containsNode(_node: INode, _partialContainer?: INode): boolean {
63
+ return true;
64
+ }
65
+
66
+ /**
67
+ * Deletes the selected text from the document's DOM.
68
+ */
69
+ public deleteFromDocument(): void {
70
+ // Do nothing.
71
+ }
72
+
73
+ /**
74
+ * Moves the focus of the selection to a specified point.
75
+ *
76
+ * @param _node Node.
77
+ * @param _offset Offset.
78
+ */
79
+ public extend(_node: INode, _offset?: number): void {
80
+ // Do nothing.
81
+ }
82
+
83
+ /**
84
+ * Moves the focus of the selection to a specified point.
85
+ *
86
+ * @param _index Index.
87
+ */
88
+ public getRangeAt(_index: number): object {
89
+ throw new Error('Not a valid index.');
90
+ }
91
+
92
+ /**
93
+ * Removes a range from a selection.
94
+ *
95
+ * @param _range Range.
96
+ */
97
+ public removeRange(_range: object): void {
98
+ // Do nothing.
99
+ }
100
+
101
+ /**
102
+ * Removes all ranges.
103
+ */
104
+ public removeAllRanges(): void {
105
+ // Do nothing.
106
+ }
107
+
108
+ /**
109
+ * Selects all children.
110
+ *
111
+ * @param _parentNode Parent node.
112
+ */
113
+ public selectAllChildren(_parentNode: INode): void {
114
+ // Do nothing.
115
+ }
116
+
117
+ /**
118
+ * Sets the selection to be a range including all or parts of two specified DOM nodes, and any content located between them.
119
+ *
120
+ * @param _anchorNode Anchor node.
121
+ * @param _anchorOffset Anchor offset.
122
+ * @param _focusNode Focus node.
123
+ * @param _focusOffset Focus offset.
124
+ */
125
+ public setBaseAndExtent(
126
+ _anchorNode: INode,
127
+ _anchorOffset: number,
128
+ _focusNode: INode,
129
+ _focusOffset: number
130
+ ): void {
131
+ // Do nothing.
132
+ }
133
+
134
+ /**
135
+ * Returns string currently being represented by the selection object.
136
+ */
137
+ public toString(): string {
138
+ return '';
139
+ }
140
+ }
@@ -68,6 +68,7 @@ import Window from './Window';
68
68
  import URLSearchParams from '../url-search-params/URLSearchParams';
69
69
  import HTMLCollection from '../nodes/element/HTMLCollection';
70
70
  import NodeList from '../nodes/node/NodeList';
71
+ import Selection from '../selection/Selection';
71
72
 
72
73
  /**
73
74
  * Window.
@@ -148,6 +149,7 @@ export default interface IWindow {
148
149
  readonly NodeList: typeof NodeList;
149
150
  readonly CSSUnitValue: typeof CSSUnitValue;
150
151
  readonly CSS: CSS;
152
+ readonly Selection: typeof Selection;
151
153
 
152
154
  // Events
153
155
  onload: (event: Event) => void;
@@ -73,6 +73,7 @@ import URLSearchParams from '../url-search-params/URLSearchParams';
73
73
  import HTMLCollection from '../nodes/element/HTMLCollection';
74
74
  import NodeList from '../nodes/node/NodeList';
75
75
  import MediaQueryList from '../match-media/MediaQueryList';
76
+ import Selection from '../selection/Selection';
76
77
  import * as PerfHooks from 'perf_hooks';
77
78
 
78
79
  const FETCH_RESPONSE_TYPE_METHODS = ['blob', 'json', 'text'];
@@ -160,6 +161,7 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
160
161
  public readonly NodeList = NodeList;
161
162
  public readonly MediaQueryList = MediaQueryList;
162
163
  public readonly CSSUnitValue = CSSUnitValue;
164
+ public readonly Selection = Selection;
163
165
 
164
166
  // Events
165
167
  public onload: (event: Event) => void = null;
@@ -37,7 +37,6 @@ export default class XMLParser {
37
37
  let parentUnnestableTagName = null;
38
38
  let lastTextIndex = 0;
39
39
  let match: RegExpExecArray;
40
- let childLessIndex = 0;
41
40
 
42
41
  while ((match = markupRegexp.exec(data))) {
43
42
  const tagName = match[2].toLowerCase();
@@ -88,15 +87,18 @@ export default class XMLParser {
88
87
  } else {
89
88
  parent.appendChild(newElement);
90
89
  }
91
- lastTextIndex = childLessIndex = markupRegexp.lastIndex;
90
+ lastTextIndex = markupRegexp.lastIndex;
92
91
 
93
92
  // Tags which contain non-parsed content
94
93
  // For example: <script> JavaScript should not be parsed
95
94
  if (ChildLessElements.includes(tagName)) {
96
- while (markupRegexp.exec(data)[2].toLowerCase() != tagName) {
97
- childLessIndex = markupRegexp.lastIndex;
95
+ let childLessMatch = null;
96
+ while ((childLessMatch = markupRegexp.exec(data))) {
97
+ if (childLessMatch[2] === match[2] && childLessMatch[1]) {
98
+ markupRegexp.lastIndex -= childLessMatch[0].length;
99
+ break;
100
+ }
98
101
  }
99
- markupRegexp.lastIndex = childLessIndex;
100
102
  }
101
103
  } else {
102
104
  stack.pop();