happy-dom 5.4.0 → 6.0.2

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 (141) hide show
  1. package/.eslintrc.js +2 -2
  2. package/lib/base64/Base64.d.ts +23 -0
  3. package/lib/base64/Base64.js +89 -0
  4. package/lib/base64/Base64.js.map +1 -0
  5. package/lib/config/NonImplemenetedElementClasses.js +0 -1
  6. package/lib/config/NonImplemenetedElementClasses.js.map +1 -1
  7. package/lib/dom-implementation/DOMImplementation.d.ts +7 -1
  8. package/lib/dom-implementation/DOMImplementation.js +10 -2
  9. package/lib/dom-implementation/DOMImplementation.js.map +1 -1
  10. package/lib/dom-parser/DOMParser.d.ts +9 -4
  11. package/lib/dom-parser/DOMParser.js +13 -2
  12. package/lib/dom-parser/DOMParser.js.map +1 -1
  13. package/lib/exception/DOMExceptionNameEnum.d.ts +5 -1
  14. package/lib/exception/DOMExceptionNameEnum.js +4 -0
  15. package/lib/exception/DOMExceptionNameEnum.js.map +1 -1
  16. package/lib/fetch/Request.d.ts +8 -0
  17. package/lib/fetch/Request.js +15 -6
  18. package/lib/fetch/Request.js.map +1 -1
  19. package/lib/fetch/Response.d.ts +5 -0
  20. package/lib/fetch/Response.js +12 -6
  21. package/lib/fetch/Response.js.map +1 -1
  22. package/lib/file/FileReader.d.ts +7 -2
  23. package/lib/file/FileReader.js +9 -3
  24. package/lib/file/FileReader.js.map +1 -1
  25. package/lib/index.d.ts +2 -1
  26. package/lib/index.js +3 -1
  27. package/lib/index.js.map +1 -1
  28. package/lib/nodes/comment/Comment.d.ts +1 -1
  29. package/lib/nodes/document/Document.d.ts +18 -15
  30. package/lib/nodes/document/Document.js +36 -33
  31. package/lib/nodes/document/Document.js.map +1 -1
  32. package/lib/nodes/document/IDocument.d.ts +7 -0
  33. package/lib/nodes/document-fragment/DocumentFragment.d.ts +1 -1
  34. package/lib/nodes/document-type/DocumentType.d.ts +1 -1
  35. package/lib/nodes/element/DOMRectListFactory.d.ts +23 -0
  36. package/lib/nodes/element/DOMRectListFactory.js +33 -0
  37. package/lib/nodes/element/DOMRectListFactory.js.map +1 -0
  38. package/lib/nodes/element/Element.d.ts +6 -5
  39. package/lib/nodes/element/Element.js +9 -11
  40. package/lib/nodes/element/Element.js.map +1 -1
  41. package/lib/nodes/element/IDOMRectList.d.ts +11 -0
  42. package/lib/nodes/element/IDOMRectList.js +3 -0
  43. package/lib/nodes/element/IDOMRectList.js.map +1 -0
  44. package/lib/nodes/element/IElement.d.ts +5 -4
  45. package/lib/nodes/html-dialog-element/HTMLDialogElement.d.ts +1 -6
  46. package/lib/nodes/html-dialog-element/HTMLDialogElement.js +7 -3
  47. package/lib/nodes/html-dialog-element/HTMLDialogElement.js.map +1 -1
  48. package/lib/nodes/html-dialog-element/IHTMLDialogElement.d.ts +11 -0
  49. package/lib/nodes/html-element/DatasetUtility.js +3 -1
  50. package/lib/nodes/html-element/DatasetUtility.js.map +1 -1
  51. package/lib/nodes/html-template-element/HTMLTemplateElement.d.ts +1 -10
  52. package/lib/nodes/html-template-element/HTMLTemplateElement.js +2 -15
  53. package/lib/nodes/html-template-element/HTMLTemplateElement.js.map +1 -1
  54. package/lib/nodes/node/INode.d.ts +8 -6
  55. package/lib/nodes/node/Node.d.ts +20 -17
  56. package/lib/nodes/node/Node.js +19 -15
  57. package/lib/nodes/node/Node.js.map +1 -1
  58. package/lib/nodes/node/NodeTypeEnum.d.ts +10 -0
  59. package/lib/nodes/node/NodeTypeEnum.js +14 -0
  60. package/lib/nodes/node/NodeTypeEnum.js.map +1 -0
  61. package/lib/nodes/node/NodeUtility.d.ts +59 -0
  62. package/lib/nodes/node/NodeUtility.js +123 -0
  63. package/lib/nodes/node/NodeUtility.js.map +1 -0
  64. package/lib/nodes/text/IText.d.ts +9 -0
  65. package/lib/nodes/text/Text.d.ts +10 -1
  66. package/lib/nodes/text/Text.js +24 -0
  67. package/lib/nodes/text/Text.js.map +1 -1
  68. package/lib/query-selector/SelectorItem.js +3 -2
  69. package/lib/query-selector/SelectorItem.js.map +1 -1
  70. package/lib/range/IRangeBoundaryPoint.d.ts +8 -0
  71. package/lib/range/IRangeBoundaryPoint.js +3 -0
  72. package/lib/range/IRangeBoundaryPoint.js.map +1 -0
  73. package/lib/range/Range.d.ts +249 -0
  74. package/lib/range/Range.js +820 -0
  75. package/lib/range/Range.js.map +1 -0
  76. package/lib/range/RangeHowEnum.d.ts +7 -0
  77. package/lib/range/RangeHowEnum.js +11 -0
  78. package/lib/range/RangeHowEnum.js.map +1 -0
  79. package/lib/range/RangeUtility.d.ts +46 -0
  80. package/lib/range/RangeUtility.js +92 -0
  81. package/lib/range/RangeUtility.js.map +1 -0
  82. package/lib/selection/Selection.d.ts +167 -44
  83. package/lib/selection/Selection.js +369 -58
  84. package/lib/selection/Selection.js.map +1 -1
  85. package/lib/selection/SelectionDirectionEnum.d.ts +6 -0
  86. package/lib/selection/SelectionDirectionEnum.js +10 -0
  87. package/lib/selection/SelectionDirectionEnum.js.map +1 -0
  88. package/lib/window/IWindow.d.ts +20 -0
  89. package/lib/window/Window.d.ts +23 -9
  90. package/lib/window/Window.js +83 -35
  91. package/lib/window/Window.js.map +1 -1
  92. package/lib/xml-parser/XMLParser.d.ts +2 -2
  93. package/lib/xml-parser/XMLParser.js +5 -3
  94. package/lib/xml-parser/XMLParser.js.map +1 -1
  95. package/lib/xml-serializer/XMLSerializer.js +4 -1
  96. package/lib/xml-serializer/XMLSerializer.js.map +1 -1
  97. package/package.json +2 -2
  98. package/src/base64/Base64.ts +97 -0
  99. package/src/config/NonImplemenetedElementClasses.ts +0 -1
  100. package/src/dom-implementation/DOMImplementation.ts +13 -2
  101. package/src/dom-parser/DOMParser.ts +20 -7
  102. package/src/exception/DOMExceptionNameEnum.ts +5 -1
  103. package/src/fetch/Request.ts +16 -6
  104. package/src/fetch/Response.ts +13 -6
  105. package/src/file/FileReader.ts +14 -4
  106. package/src/index.ts +2 -0
  107. package/src/nodes/document/Document.ts +47 -39
  108. package/src/nodes/document/IDocument.ts +8 -0
  109. package/src/nodes/element/DOMRectListFactory.ts +33 -0
  110. package/src/nodes/element/Element.ts +10 -11
  111. package/src/nodes/element/IDOMRectList.ts +11 -0
  112. package/src/nodes/element/IElement.ts +5 -4
  113. package/src/nodes/html-dialog-element/HTMLDialogElement.ts +4 -9
  114. package/src/nodes/html-dialog-element/IHTMLDialogElement.ts +14 -0
  115. package/src/nodes/html-element/DatasetUtility.ts +3 -1
  116. package/src/nodes/html-template-element/HTMLTemplateElement.ts +3 -21
  117. package/src/nodes/node/INode.ts +8 -6
  118. package/src/nodes/node/Node.ts +24 -19
  119. package/src/nodes/node/NodeTypeEnum.ts +11 -0
  120. package/src/nodes/node/NodeUtility.ts +139 -0
  121. package/src/nodes/text/IText.ts +10 -0
  122. package/src/nodes/text/Text.ts +33 -0
  123. package/src/query-selector/SelectorItem.ts +3 -2
  124. package/src/range/IRangeBoundaryPoint.ts +9 -0
  125. package/src/range/Range.ts +1057 -0
  126. package/src/range/RangeHowEnum.ts +8 -0
  127. package/src/range/RangeUtility.ts +114 -0
  128. package/src/selection/Selection.ts +444 -60
  129. package/src/selection/SelectionDirectionEnum.ts +7 -0
  130. package/src/window/IWindow.ts +22 -0
  131. package/src/window/Window.ts +102 -32
  132. package/src/xml-parser/XMLParser.ts +15 -7
  133. package/src/xml-serializer/XMLSerializer.ts +6 -1
  134. package/lib/nodes/element/Range.d.ts +0 -167
  135. package/lib/nodes/element/Range.js +0 -215
  136. package/lib/nodes/element/Range.js.map +0 -1
  137. package/lib/window/WindowBase64.d.ts +0 -19
  138. package/lib/window/WindowBase64.js +0 -88
  139. package/lib/window/WindowBase64.js.map +0 -1
  140. package/src/nodes/element/Range.ts +0 -237
  141. package/src/window/WindowBase64.ts +0 -95
@@ -0,0 +1,8 @@
1
+ enum RangeHowEnum {
2
+ endToEnd = 2,
3
+ endToStart = 3,
4
+ startToEnd = 1,
5
+ startToStart = 0
6
+ }
7
+
8
+ export default RangeHowEnum;
@@ -0,0 +1,114 @@
1
+ import DOMException from '../exception/DOMException';
2
+ import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum';
3
+ import NodeTypeEnum from '../nodes/node/NodeTypeEnum';
4
+ import INode from '../nodes/node/INode';
5
+ import NodeUtility from '../nodes/node/NodeUtility';
6
+ import Range from './Range';
7
+ import IRangeBoundaryPoint from './IRangeBoundaryPoint';
8
+
9
+ /**
10
+ * Range utility.
11
+ *
12
+ * Based on:
13
+ * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/range/boundary-point.js.
14
+ */
15
+ export default class RangeUtility {
16
+ /**
17
+ * Compares boundary points.
18
+ *
19
+ * Based on logic from:
20
+ * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/range/boundary-point.js
21
+ *
22
+ * @see https://dom.spec.whatwg.org/#concept-range-bp-after
23
+ * @param pointA Point A.
24
+ * @param pointB Point B.
25
+ * @returns A number, -1, 0, or 1, indicating whether the corresponding boundary-point of the Range is respectively before, equal to, or after the corresponding boundary-point of sourceRange.
26
+ */
27
+ public static compareBoundaryPointsPosition(
28
+ pointA: IRangeBoundaryPoint,
29
+ pointB: IRangeBoundaryPoint
30
+ ): number {
31
+ if (pointA.node === pointB.node) {
32
+ if (pointA.offset === pointB.offset) {
33
+ return 0;
34
+ } else if (pointA.offset < pointB.offset) {
35
+ return -1;
36
+ }
37
+
38
+ return 1;
39
+ }
40
+
41
+ if (NodeUtility.isFollowing(pointA.node, pointB.node)) {
42
+ return this.compareBoundaryPointsPosition(pointB, pointA) === -1 ? 1 : -1;
43
+ }
44
+
45
+ if (NodeUtility.isInclusiveAncestor(pointA.node, pointB.node)) {
46
+ let child = pointB.node;
47
+
48
+ while (child.parentNode !== pointA.node) {
49
+ child = child.parentNode;
50
+ }
51
+
52
+ if (child.parentNode.childNodes.indexOf(child) < pointA.offset) {
53
+ return 1;
54
+ }
55
+ }
56
+
57
+ return -1;
58
+ }
59
+
60
+ /**
61
+ * Validates a boundary point.
62
+ *
63
+ * @throws DOMException
64
+ * @param point Boundary point.
65
+ */
66
+ public static validateBoundaryPoint(point: IRangeBoundaryPoint): void {
67
+ if (point.node.nodeType === NodeTypeEnum.documentTypeNode) {
68
+ throw new DOMException(
69
+ `DocumentType Node can't be used as boundary point.`,
70
+ DOMExceptionNameEnum.invalidNodeTypeError
71
+ );
72
+ }
73
+
74
+ if (point.offset > NodeUtility.getNodeLength(point.node)) {
75
+ throw new DOMException(`Offset out of bound.`, DOMExceptionNameEnum.indexSizeError);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Returns "true" if contained.
81
+ *
82
+ * @param node Node.
83
+ * @param range Range.
84
+ * @returns "true" if contained.
85
+ */
86
+ public static isContained(node: INode, range: Range): boolean {
87
+ return (
88
+ this.compareBoundaryPointsPosition(
89
+ { node, offset: 0 },
90
+ { node: range.startContainer, offset: range.startOffset }
91
+ ) === 1 &&
92
+ this.compareBoundaryPointsPosition(
93
+ { node, offset: NodeUtility.getNodeLength(node) },
94
+ { node: range.endContainer, offset: range.endOffset }
95
+ ) === -1
96
+ );
97
+ }
98
+
99
+ /**
100
+ * Returns "true" if partially contained.
101
+ *
102
+ * @param node Node.
103
+ * @param range Range.
104
+ * @returns "true" if partially contained.
105
+ */
106
+ public static isPartiallyContained(node: INode, range: Range): boolean {
107
+ return (
108
+ (NodeUtility.isInclusiveAncestor(node, range.startContainer) &&
109
+ !NodeUtility.isInclusiveAncestor(node, range.endContainer)) ||
110
+ (!NodeUtility.isInclusiveAncestor(node, range.startContainer) &&
111
+ NodeUtility.isInclusiveAncestor(node, range.endContainer))
112
+ );
113
+ }
114
+ }
@@ -1,140 +1,524 @@
1
+ import Event from '../event/Event';
2
+ import DOMException from '../exception/DOMException';
3
+ import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum';
4
+ import IDocument from '../nodes/document/IDocument';
1
5
  import INode from '../nodes/node/INode';
6
+ import NodeTypeEnum from '../nodes/node/NodeTypeEnum';
7
+ import NodeUtility from '../nodes/node/NodeUtility';
8
+ import Range from '../range/Range';
9
+ import RangeUtility from '../range/RangeUtility';
10
+ import SelectionDirectionEnum from './SelectionDirectionEnum';
2
11
 
3
12
  /**
4
13
  * Selection.
5
14
  *
15
+ * Based on logic from:
16
+ * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/selection/Selection-impl.js
17
+ *
6
18
  * Reference:
7
19
  * https://developer.mozilla.org/en-US/docs/Web/API/Selection.
8
20
  */
9
21
  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';
22
+ private readonly _ownerDocument: IDocument = null;
23
+ private _range: Range = null;
24
+ private _direction: SelectionDirectionEnum = SelectionDirectionEnum.directionless;
21
25
 
22
26
  /**
23
- * Adds a range.
27
+ * Constructor.
24
28
  *
25
- * @param _range Range.
29
+ * @param ownerDocument Owner document.
26
30
  */
27
- public addRange(_range: object): void {
28
- // Do nothing.
31
+ constructor(ownerDocument: IDocument) {
32
+ this._ownerDocument = ownerDocument;
29
33
  }
30
34
 
31
35
  /**
32
- * Collapses the current selection to a single point.
36
+ * Returns range count.
33
37
  *
34
- * @param _node Node.
35
- * @param _offset Offset.
38
+ * @see https://w3c.github.io/selection-api/#dom-selection-rangecount
39
+ * @returns Range count.
36
40
  */
37
- public collapse(_node: INode, _offset?: number): void {
38
- // Do nothing.
41
+ public get rangeCount(): number {
42
+ return this._range ? 1 : 0;
39
43
  }
40
44
 
41
45
  /**
42
- * Collapses the selection to the end.
46
+ * Returns collapsed state.
47
+ *
48
+ * @see https://w3c.github.io/selection-api/#dom-selection-iscollapsed
49
+ * @returns "true" if collapsed.
43
50
  */
44
- public collapseToEnd(): void {
45
- // Do nothing.
51
+ public get isCollapsed(): boolean {
52
+ return this._range === null || this._range.collapsed;
46
53
  }
47
54
 
48
55
  /**
49
- * Collapses the selection to the start.
56
+ * Returns type.
57
+ *
58
+ * @see https://w3c.github.io/selection-api/#dom-selection-type
59
+ * @returns Type.
50
60
  */
51
- public collapseToStart(): void {
52
- // Do nothing.
61
+ public get type(): string {
62
+ if (!this._range) {
63
+ return 'None';
64
+ } else if (this._range.collapsed) {
65
+ return 'Caret';
66
+ }
67
+
68
+ return 'Range';
53
69
  }
54
70
 
55
71
  /**
56
- * Indicates whether a specified node is part of the selection.
72
+ * Returns anchor node.
57
73
  *
58
- * @param _node Node.
59
- * @param _partialContainer Partial container.
60
- * @returns Always returns "true" for now.
74
+ * @see https://w3c.github.io/selection-api/#dom-selection-anchornode
75
+ * @returns Node.
61
76
  */
62
- public containsNode(_node: INode, _partialContainer?: INode): boolean {
63
- return true;
77
+ public get anchorNode(): INode {
78
+ if (!this._range) {
79
+ return null;
80
+ }
81
+ return this._direction === SelectionDirectionEnum.forwards
82
+ ? this._range.startContainer
83
+ : this._range.endContainer;
64
84
  }
65
85
 
66
86
  /**
67
- * Deletes the selected text from the document's DOM.
87
+ * Returns anchor offset.
88
+ *
89
+ * @see https://w3c.github.io/selection-api/#dom-selection-anchoroffset
90
+ * @returns Node.
68
91
  */
69
- public deleteFromDocument(): void {
70
- // Do nothing.
92
+ public get anchorOffset(): number {
93
+ if (!this._range) {
94
+ return null;
95
+ }
96
+ return this._direction === SelectionDirectionEnum.forwards
97
+ ? this._range.startOffset
98
+ : this._range.endOffset;
71
99
  }
72
100
 
73
101
  /**
74
- * Moves the focus of the selection to a specified point.
102
+ * Returns anchor node.
75
103
  *
76
- * @param _node Node.
77
- * @param _offset Offset.
104
+ * @deprecated
105
+ * @alias anchorNode
106
+ * @returns Node.
78
107
  */
79
- public extend(_node: INode, _offset?: number): void {
80
- // Do nothing.
108
+ public get baseNode(): INode {
109
+ return this.anchorNode;
81
110
  }
82
111
 
83
112
  /**
84
- * Moves the focus of the selection to a specified point.
113
+ * Returns anchor offset.
114
+ *
115
+ * @deprecated
116
+ * @alias anchorOffset
117
+ * @returns Node.
118
+ */
119
+ public get baseOffset(): number {
120
+ return this.anchorOffset;
121
+ }
122
+
123
+ /**
124
+ * Returns focus node.
125
+ *
126
+ * @see https://w3c.github.io/selection-api/#dom-selection-focusnode
127
+ * @returns Node.
128
+ */
129
+ public get focusNode(): INode {
130
+ return this.anchorNode;
131
+ }
132
+
133
+ /**
134
+ * Returns focus offset.
135
+ *
136
+ * @see https://w3c.github.io/selection-api/#dom-selection-focusoffset
137
+ * @returns Node.
138
+ */
139
+ public get focusOffset(): number {
140
+ return this.anchorOffset;
141
+ }
142
+
143
+ /**
144
+ * Returns focus node.
145
+ *
146
+ * @deprecated
147
+ * @alias focusNode
148
+ * @returns Node.
149
+ */
150
+ public get extentNode(): INode {
151
+ return this.focusNode;
152
+ }
153
+
154
+ /**
155
+ * Returns focus offset.
156
+ *
157
+ * @deprecated
158
+ * @alias focusOffset
159
+ * @returns Node.
160
+ */
161
+ public get extentOffset(): number {
162
+ return this.focusOffset;
163
+ }
164
+
165
+ /**
166
+ * Adds a range.
85
167
  *
86
- * @param _index Index.
168
+ * @see https://w3c.github.io/selection-api/#dom-selection-addrange
169
+ * @param newRange Range.
87
170
  */
88
- public getRangeAt(_index: number): object {
89
- throw new Error('Not a valid index.');
171
+ public addRange(newRange: Range): void {
172
+ if (!newRange) {
173
+ throw new Error('Failed to execute addRange on Selection. Parameter 1 is not of type Range.');
174
+ }
175
+ if (!this._range && newRange._ownerDocument === this._ownerDocument) {
176
+ this._associateRange(newRange);
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Returns Range.
182
+ *
183
+ * @see https://w3c.github.io/selection-api/#dom-selection-getrangeat
184
+ * @param index Index.
185
+ * @returns Range.
186
+ */
187
+ public getRangeAt(index: number): Range {
188
+ if (!this._range || index !== 0) {
189
+ throw new DOMException('Invalid range index.', DOMExceptionNameEnum.indexSizeError);
190
+ }
191
+
192
+ return this._range;
90
193
  }
91
194
 
92
195
  /**
93
196
  * Removes a range from a selection.
94
197
  *
95
- * @param _range Range.
198
+ * @see https://w3c.github.io/selection-api/#dom-selection-removerange
199
+ * @param range Range.
96
200
  */
97
- public removeRange(_range: object): void {
98
- // Do nothing.
201
+ public removeRange(range: Range): void {
202
+ if (this._range !== range) {
203
+ throw new DOMException('Invalid range.', DOMExceptionNameEnum.notFoundError);
204
+ }
205
+ this._associateRange(null);
99
206
  }
100
207
 
101
208
  /**
102
209
  * Removes all ranges.
103
210
  */
104
211
  public removeAllRanges(): void {
105
- // Do nothing.
212
+ this._associateRange(null);
213
+ }
214
+
215
+ /**
216
+ * Removes all ranges.
217
+ *
218
+ * @alias removeAllRanges()
219
+ */
220
+ public empty(): void {
221
+ this.removeAllRanges();
222
+ }
223
+
224
+ /**
225
+ * Collapses the current selection to a single point.
226
+ *
227
+ * @see https://w3c.github.io/selection-api/#dom-selection-collapse
228
+ * @param node Node.
229
+ * @param offset Offset.
230
+ */
231
+ public collapse(node: INode, offset: number): void {
232
+ if (node === null) {
233
+ this.removeAllRanges();
234
+ return;
235
+ }
236
+
237
+ if (node.nodeType === NodeTypeEnum.documentTypeNode) {
238
+ throw new DOMException(
239
+ "DocumentType Node can't be used as boundary point.",
240
+ DOMExceptionNameEnum.invalidNodeTypeError
241
+ );
242
+ }
243
+
244
+ if (offset > NodeUtility.getNodeLength(node)) {
245
+ throw new DOMException('Invalid range index.', DOMExceptionNameEnum.indexSizeError);
246
+ }
247
+
248
+ if (node.ownerDocument !== this._ownerDocument) {
249
+ return;
250
+ }
251
+
252
+ const newRange = new Range();
253
+
254
+ newRange._start.node = node;
255
+ newRange._start.offset = offset;
256
+ newRange._end.node = node;
257
+ newRange._end.offset = offset;
258
+
259
+ this._associateRange(newRange);
260
+ }
261
+
262
+ /**
263
+ * Collapses the current selection to a single point.
264
+ *
265
+ * @see https://w3c.github.io/selection-api/#dom-selection-setposition
266
+ * @alias collapse()
267
+ * @param node Node.
268
+ * @param offset Offset.
269
+ */
270
+ public setPosition(node: INode, offset: number): void {
271
+ this.collapse(node, offset);
272
+ }
273
+
274
+ /**
275
+ * Collapses the selection to the end.
276
+ *
277
+ * @see https://w3c.github.io/selection-api/#dom-selection-collapsetoend
278
+ */
279
+ public collapseToEnd(): void {
280
+ if (this._range === null) {
281
+ throw new DOMException(
282
+ 'There is no selection to collapse.',
283
+ DOMExceptionNameEnum.invalidStateError
284
+ );
285
+ }
286
+
287
+ const { node, offset } = this._range._end;
288
+ const newRange = new Range();
289
+
290
+ newRange._start.node = node;
291
+ newRange._start.offset = offset;
292
+ newRange._end.node = node;
293
+ newRange._end.offset = offset;
294
+
295
+ this._associateRange(newRange);
296
+ }
297
+
298
+ /**
299
+ * Collapses the selection to the start.
300
+ *
301
+ * @see https://w3c.github.io/selection-api/#dom-selection-collapsetostart
302
+ */
303
+ public collapseToStart(): void {
304
+ if (!this._range) {
305
+ throw new DOMException(
306
+ 'There is no selection to collapse.',
307
+ DOMExceptionNameEnum.invalidStateError
308
+ );
309
+ }
310
+
311
+ const { node, offset } = this._range._start;
312
+ const newRange = new Range();
313
+
314
+ newRange._start.node = node;
315
+ newRange._start.offset = offset;
316
+ newRange._end.node = node;
317
+ newRange._end.offset = offset;
318
+
319
+ this._associateRange(newRange);
320
+ }
321
+
322
+ /**
323
+ * Indicates whether a specified node is part of the selection.
324
+ *
325
+ * @see https://w3c.github.io/selection-api/#dom-selection-containsnode
326
+ * @param node Node.
327
+ * @param [allowPartialContainment] Set to "true" to allow partial containment.
328
+ * @returns Always returns "true" for now.
329
+ */
330
+ public containsNode(node: INode, allowPartialContainment = false): boolean {
331
+ if (!this._range || node.ownerDocument !== this._ownerDocument) {
332
+ return false;
333
+ }
334
+
335
+ const { _start, _end } = this._range;
336
+
337
+ const startIsBeforeNode =
338
+ RangeUtility.compareBoundaryPointsPosition(_start, { node, offset: 0 }) === -1;
339
+ const endIsAfterNode =
340
+ RangeUtility.compareBoundaryPointsPosition(_end, {
341
+ node,
342
+ offset: NodeUtility.getNodeLength(node)
343
+ }) === 1;
344
+
345
+ return allowPartialContainment
346
+ ? startIsBeforeNode || endIsAfterNode
347
+ : startIsBeforeNode && endIsAfterNode;
348
+ }
349
+
350
+ /**
351
+ * Deletes the selected text from the document's DOM.
352
+ *
353
+ * @see https://w3c.github.io/selection-api/#dom-selection-deletefromdocument
354
+ */
355
+ public deleteFromDocument(): void {
356
+ if (this._range) {
357
+ this._range.deleteContents();
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Moves the focus of the selection to a specified point.
363
+ *
364
+ * @see https://w3c.github.io/selection-api/#dom-selection-extend
365
+ * @param node Node.
366
+ * @param offset Offset.
367
+ */
368
+ public extend(node: INode, offset: number): void {
369
+ if (node.ownerDocument !== this._ownerDocument) {
370
+ return;
371
+ }
372
+
373
+ if (!this._range) {
374
+ throw new DOMException(
375
+ 'There is no selection to extend.',
376
+ DOMExceptionNameEnum.invalidStateError
377
+ );
378
+ }
379
+
380
+ const anchorNode = this.anchorNode;
381
+ const anchorOffset = this.anchorOffset;
382
+ const newRange = new Range();
383
+ newRange._start.node = node;
384
+ newRange._start.offset = 0;
385
+ newRange._end.node = node;
386
+ newRange._end.offset = 0;
387
+
388
+ if (node.ownerDocument !== this._range._ownerDocument) {
389
+ newRange._start.offset = offset;
390
+ newRange._end.offset = offset;
391
+ } else if (
392
+ RangeUtility.compareBoundaryPointsPosition(
393
+ { node: anchorNode, offset: anchorOffset },
394
+ { node, offset }
395
+ ) <= 0
396
+ ) {
397
+ newRange._start.node = anchorNode;
398
+ newRange._start.offset = anchorOffset;
399
+ newRange._end.node = node;
400
+ newRange._end.offset = offset;
401
+ } else {
402
+ newRange._start.node = node;
403
+ newRange._start.offset = offset;
404
+ newRange._end.node = anchorNode;
405
+ newRange._end.offset = anchorOffset;
406
+ }
407
+
408
+ this._associateRange(newRange);
409
+ this._direction =
410
+ RangeUtility.compareBoundaryPointsPosition(
411
+ { node, offset },
412
+ { node: anchorNode, offset: anchorOffset }
413
+ ) === -1
414
+ ? SelectionDirectionEnum.backwards
415
+ : SelectionDirectionEnum.forwards;
106
416
  }
107
417
 
108
418
  /**
109
419
  * Selects all children.
110
420
  *
421
+ * @see https://w3c.github.io/selection-api/#dom-selection-selectallchildren
422
+ * @param node
111
423
  * @param _parentNode Parent node.
112
424
  */
113
- public selectAllChildren(_parentNode: INode): void {
114
- // Do nothing.
425
+ public selectAllChildren(node: INode): void {
426
+ if (node.nodeType === NodeTypeEnum.documentTypeNode) {
427
+ throw new DOMException(
428
+ "DocumentType Node can't be used as boundary point.",
429
+ DOMExceptionNameEnum.invalidNodeTypeError
430
+ );
431
+ }
432
+
433
+ if (node.ownerDocument !== this._ownerDocument) {
434
+ return;
435
+ }
436
+
437
+ const length = node.childNodes.length;
438
+ const newRange = new Range();
439
+
440
+ newRange._start.node = node;
441
+ newRange._start.offset = 0;
442
+ newRange._end.node = node;
443
+ newRange._end.offset = length;
444
+
445
+ this._associateRange(newRange);
115
446
  }
116
447
 
117
448
  /**
118
449
  * Sets the selection to be a range including all or parts of two specified DOM nodes, and any content located between them.
119
450
  *
120
- * @param _anchorNode Anchor node.
121
- * @param _anchorOffset Anchor offset.
122
- * @param _focusNode Focus node.
123
- * @param _focusOffset Focus offset.
451
+ * @see https://w3c.github.io/selection-api/#dom-selection-setbaseandextent
452
+ * @param anchorNode Anchor node.
453
+ * @param anchorOffset Anchor offset.
454
+ * @param focusNode Focus node.
455
+ * @param focusOffset Focus offset.
124
456
  */
125
457
  public setBaseAndExtent(
126
- _anchorNode: INode,
127
- _anchorOffset: number,
128
- _focusNode: INode,
129
- _focusOffset: number
458
+ anchorNode: INode,
459
+ anchorOffset: number,
460
+ focusNode: INode,
461
+ focusOffset: number
130
462
  ): void {
131
- // Do nothing.
463
+ if (
464
+ anchorOffset > NodeUtility.getNodeLength(anchorNode) ||
465
+ focusOffset > NodeUtility.getNodeLength(focusNode)
466
+ ) {
467
+ throw new DOMException(
468
+ 'Invalid anchor or focus offset.',
469
+ DOMExceptionNameEnum.indexSizeError
470
+ );
471
+ }
472
+
473
+ if (
474
+ anchorNode.ownerDocument !== this._ownerDocument ||
475
+ focusNode.ownerDocument !== this._ownerDocument
476
+ ) {
477
+ return;
478
+ }
479
+
480
+ const anchor = { node: anchorNode, offset: anchorOffset };
481
+ const focus = { node: focusNode, offset: focusOffset };
482
+ const newRange = new Range();
483
+
484
+ if (RangeUtility.compareBoundaryPointsPosition(anchor, focus) === -1) {
485
+ newRange._start = anchor;
486
+ newRange._end = focus;
487
+ } else {
488
+ newRange._start = focus;
489
+ newRange._end = anchor;
490
+ }
491
+
492
+ this._associateRange(newRange);
493
+ this._direction =
494
+ RangeUtility.compareBoundaryPointsPosition(focus, anchor) === -1
495
+ ? SelectionDirectionEnum.backwards
496
+ : SelectionDirectionEnum.forwards;
132
497
  }
133
498
 
134
499
  /**
135
500
  * Returns string currently being represented by the selection object.
501
+ *
502
+ * @returns Selection as string.
136
503
  */
137
504
  public toString(): string {
138
- return '';
505
+ return this._range ? this._range.toString() : '';
506
+ }
507
+
508
+ /**
509
+ * Sets the current range.
510
+ *
511
+ * @param range Range.
512
+ */
513
+ protected _associateRange(range: Range): void {
514
+ const oldRange = this._range;
515
+ this._range = range;
516
+ this._direction =
517
+ range === null ? SelectionDirectionEnum.directionless : SelectionDirectionEnum.forwards;
518
+
519
+ if (oldRange !== this._range) {
520
+ // https://w3c.github.io/selection-api/#selectionchange-event
521
+ this._ownerDocument.dispatchEvent(new Event('selectionchange'));
522
+ }
139
523
  }
140
524
  }