happy-dom 7.6.6 → 7.7.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 (35) hide show
  1. package/lib/config/ElementTag.d.ts +2 -1
  2. package/lib/config/ElementTag.js +2 -1
  3. package/lib/config/ElementTag.js.map +1 -1
  4. package/lib/dom-token-list/DOMTokenList.js +1 -1
  5. package/lib/dom-token-list/DOMTokenList.js.map +1 -1
  6. package/lib/nodes/element/Element.d.ts +0 -4
  7. package/lib/nodes/element/Element.js +6 -10
  8. package/lib/nodes/element/Element.js.map +1 -1
  9. package/lib/nodes/html-anchor-element/HTMLAnchorElement.d.ts +250 -0
  10. package/lib/nodes/html-anchor-element/HTMLAnchorElement.js +395 -0
  11. package/lib/nodes/html-anchor-element/HTMLAnchorElement.js.map +1 -0
  12. package/lib/nodes/html-anchor-element/HTMLAnchorElementUtility.d.ts +29 -0
  13. package/lib/nodes/html-anchor-element/HTMLAnchorElementUtility.js +45 -0
  14. package/lib/nodes/html-anchor-element/HTMLAnchorElementUtility.js.map +1 -0
  15. package/lib/nodes/html-anchor-element/IHTMLAnchorElement.d.ts +20 -0
  16. package/lib/nodes/html-anchor-element/IHTMLAnchorElement.js +3 -0
  17. package/lib/nodes/html-anchor-element/IHTMLAnchorElement.js.map +1 -0
  18. package/lib/nodes/html-anchor-element/IHTMLHyperlinkElementUtils.d.ts +19 -0
  19. package/lib/nodes/html-anchor-element/IHTMLHyperlinkElementUtils.js +3 -0
  20. package/lib/nodes/html-anchor-element/IHTMLHyperlinkElementUtils.js.map +1 -0
  21. package/lib/nodes/html-link-element/HTMLLinkElement.d.ts +3 -7
  22. package/lib/nodes/html-link-element/HTMLLinkElement.js +13 -13
  23. package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
  24. package/lib/nodes/html-option-element/HTMLOptionElement.js +1 -1
  25. package/lib/nodes/html-option-element/HTMLOptionElement.js.map +1 -1
  26. package/package.json +2 -2
  27. package/src/config/ElementTag.ts +2 -1
  28. package/src/dom-token-list/DOMTokenList.ts +1 -1
  29. package/src/nodes/element/Element.ts +6 -11
  30. package/src/nodes/html-anchor-element/HTMLAnchorElement.ts +439 -0
  31. package/src/nodes/html-anchor-element/HTMLAnchorElementUtility.ts +48 -0
  32. package/src/nodes/html-anchor-element/IHTMLAnchorElement.ts +21 -0
  33. package/src/nodes/html-anchor-element/IHTMLHyperlinkElementUtils.ts +19 -0
  34. package/src/nodes/html-link-element/HTMLLinkElement.ts +18 -16
  35. package/src/nodes/html-option-element/HTMLOptionElement.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-dom",
3
- "version": "7.6.6",
3
+ "version": "7.7.0",
4
4
  "license": "MIT",
5
5
  "homepage": "https://github.com/capricorn86/happy-dom",
6
6
  "repository": "https://github.com/capricorn86/happy-dom",
@@ -75,5 +75,5 @@
75
75
  "ts-jest": "^27.1.3",
76
76
  "typescript": "^4.6.2"
77
77
  },
78
- "gitHead": "e4072902f23440d68bd104027b65bf67685d4396"
78
+ "gitHead": "9eb21a6f922278fe17ff2d4b99f2a2898fe2053f"
79
79
  }
@@ -20,9 +20,10 @@ import HTMLDialogElement from '../nodes/html-dialog-element/HTMLDialogElement';
20
20
  import HTMLButtonElement from '../nodes/html-button-element/HTMLButtonElement';
21
21
  import HTMLAudioElement from '../nodes/html-audio-element/HTMLAudioElement';
22
22
  import HTMLVideoElement from '../nodes/html-video-element/HTMLVideoElement';
23
+ import HTMLAnchorElement from '../nodes/html-anchor-element/HTMLAnchorElement';
23
24
 
24
25
  export default {
25
- A: HTMLElement,
26
+ A: HTMLAnchorElement,
26
27
  ABBR: HTMLElement,
27
28
  ADDRESS: HTMLElement,
28
29
  AREA: HTMLElement,
@@ -199,7 +199,7 @@ export default class DOMTokenList implements IDOMTokenList {
199
199
  * Updates indices.
200
200
  */
201
201
  public _updateIndices(): void {
202
- const attr = this._ownerElement.getAttribute('class');
202
+ const attr = this._ownerElement.getAttribute(this._attributeName);
203
203
  const list = attr ? Array.from(new Set(attr.split(' '))) : [];
204
204
 
205
205
  for (let i = list.length - 1, max = this.length; i < max; i++) {
@@ -840,7 +840,9 @@ export default class Element extends Node implements IElement {
840
840
 
841
841
  this._attributes[name] = attribute;
842
842
 
843
- this._updateDomListIndices();
843
+ if (attribute.name === 'class' && this._classList) {
844
+ this._classList._updateIndices();
845
+ }
844
846
 
845
847
  if (
846
848
  this.attributeChangedCallback &&
@@ -936,7 +938,9 @@ export default class Element extends Node implements IElement {
936
938
  this.ownerDocument['_cacheID']++;
937
939
  }
938
940
 
939
- this._updateDomListIndices();
941
+ if (attribute.name === 'class' && this._classList) {
942
+ this._classList._updateIndices();
943
+ }
940
944
 
941
945
  if (
942
946
  this.attributeChangedCallback &&
@@ -1033,13 +1037,4 @@ export default class Element extends Node implements IElement {
1033
1037
  }
1034
1038
  return name.toLowerCase();
1035
1039
  }
1036
-
1037
- /**
1038
- * Updates DOM list indices.
1039
- */
1040
- protected _updateDomListIndices(): void {
1041
- if (this._classList) {
1042
- this._classList._updateIndices();
1043
- }
1044
- }
1045
1040
  }
@@ -0,0 +1,439 @@
1
+ import HTMLElement from '../html-element/HTMLElement';
2
+ import DOMTokenList from '../../dom-token-list/DOMTokenList';
3
+ import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
4
+ import IHTMLAnchorElement from './IHTMLAnchorElement';
5
+ import { URL } from 'url';
6
+ import IAttr from '../attr/IAttr';
7
+ import HTMLAnchorElementUtility from './HTMLAnchorElementUtility';
8
+
9
+ /**
10
+ * HTML Anchor Element.
11
+ *
12
+ * Reference:
13
+ * https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement.
14
+ */
15
+ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAnchorElement {
16
+ private _relList: DOMTokenList = null;
17
+ private _url: URL | null = null;
18
+
19
+ /**
20
+ * Returns download.
21
+ *
22
+ * @returns download.
23
+ */
24
+ public get download(): string {
25
+ return this.getAttribute('download') || '';
26
+ }
27
+
28
+ /**
29
+ * Sets download.
30
+ *
31
+ * @param download Download.
32
+ */
33
+ public set download(download: string) {
34
+ this.setAttributeNS(null, 'download', download);
35
+ }
36
+
37
+ /**
38
+ * Returns hash.
39
+ *
40
+ * @returns Hash.
41
+ */
42
+ public get hash(): string {
43
+ return this._url?.hash ?? '';
44
+ }
45
+
46
+ /**
47
+ * Sets hash.
48
+ *
49
+ * @param hash Hash.
50
+ */
51
+ public set hash(hash: string) {
52
+ if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
53
+ this._url.hash = hash;
54
+ this.setAttributeNS(null, 'href', this._url.toString());
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Returns href.
60
+ *
61
+ * @returns Href.
62
+ */
63
+ public get href(): string | null {
64
+ if (this._url) {
65
+ return this._url.toString();
66
+ }
67
+
68
+ return this.getAttributeNS(null, 'href') || '';
69
+ }
70
+
71
+ /**
72
+ * Sets href.
73
+ *
74
+ * @param href Href.
75
+ */
76
+ public set href(href: string) {
77
+ this.setAttributeNS(null, 'href', href);
78
+ }
79
+
80
+ /**
81
+ * Returns hreflang.
82
+ *
83
+ * @returns Hreflang.
84
+ */
85
+ public get hreflang(): string {
86
+ return this.getAttributeNS(null, 'hreflang') || '';
87
+ }
88
+
89
+ /**
90
+ * Sets hreflang.
91
+ *
92
+ * @param hreflang Hreflang.
93
+ */
94
+ public set hreflang(hreflang: string) {
95
+ this.setAttributeNS(null, 'hreflang', hreflang);
96
+ }
97
+
98
+ /**
99
+ * Returns the hyperlink's URL's origin.
100
+ *
101
+ * @returns Origin.
102
+ */
103
+ public get origin(): string {
104
+ return this._url?.origin ?? '';
105
+ }
106
+
107
+ /**
108
+ * Returns ping.
109
+ *
110
+ * @returns Ping.
111
+ */
112
+ public get ping(): string {
113
+ return this.getAttributeNS(null, 'ping') || '';
114
+ }
115
+
116
+ /**
117
+ * Sets ping.
118
+ *
119
+ * @param ping Ping.
120
+ */
121
+ public set ping(ping: string) {
122
+ this.setAttributeNS(null, 'ping', ping);
123
+ }
124
+
125
+ /**
126
+ * Returns protocol.
127
+ *
128
+ * @returns Protocol.
129
+ */
130
+ public get protocol(): string {
131
+ return this._url?.protocol ?? '';
132
+ }
133
+
134
+ /**
135
+ * Sets protocol.
136
+ *
137
+ * @param protocol Protocol.
138
+ */
139
+ public set protocol(protocol: string) {
140
+ if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
141
+ this._url.protocol = protocol;
142
+ this.setAttributeNS(null, 'href', this._url.toString());
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Returns username.
148
+ *
149
+ * @returns Username.
150
+ */
151
+ public get username(): string {
152
+ return this._url?.username ?? '';
153
+ }
154
+
155
+ /**
156
+ * Sets username.
157
+ *
158
+ * @param username Username.
159
+ */
160
+ public set username(username: string) {
161
+ if (
162
+ this._url &&
163
+ !HTMLAnchorElementUtility.isBlobURL(this._url) &&
164
+ this._url.host &&
165
+ this._url.protocol != 'file'
166
+ ) {
167
+ this._url.username = username;
168
+ this.setAttributeNS(null, 'href', this._url.toString());
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Returns password.
174
+ *
175
+ * @returns Password.
176
+ */
177
+ public get password(): string {
178
+ return this._url?.password ?? '';
179
+ }
180
+
181
+ /**
182
+ * Sets password.
183
+ *
184
+ * @param password Password.
185
+ */
186
+ public set password(password: string) {
187
+ if (
188
+ this._url &&
189
+ !HTMLAnchorElementUtility.isBlobURL(this._url) &&
190
+ this._url.host &&
191
+ this._url.protocol != 'file'
192
+ ) {
193
+ this._url.password = password;
194
+ this.setAttributeNS(null, 'href', this._url.toString());
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Returns pathname.
200
+ *
201
+ * @returns Pathname.
202
+ */
203
+ public get pathname(): string {
204
+ return this._url?.pathname ?? '';
205
+ }
206
+
207
+ /**
208
+ * Sets pathname.
209
+ *
210
+ * @param pathname Pathname.
211
+ */
212
+ public set pathname(pathname: string) {
213
+ if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
214
+ this._url.pathname = pathname;
215
+ this.setAttributeNS(null, 'href', this._url.toString());
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Returns port.
221
+ *
222
+ * @returns Port.
223
+ */
224
+ public get port(): string {
225
+ return this._url?.port ?? '';
226
+ }
227
+
228
+ /**
229
+ * Sets port.
230
+ *
231
+ * @param port Port.
232
+ */
233
+ public set port(port: string) {
234
+ if (
235
+ this._url &&
236
+ !HTMLAnchorElementUtility.isBlobURL(this._url) &&
237
+ this._url.host &&
238
+ this._url.protocol != 'file'
239
+ ) {
240
+ this._url.port = port;
241
+ this.setAttributeNS(null, 'href', this._url.toString());
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Returns host.
247
+ *
248
+ * @returns Host.
249
+ */
250
+ public get host(): string {
251
+ return this._url?.host ?? '';
252
+ }
253
+
254
+ /**
255
+ * Sets host.
256
+ *
257
+ * @param host Host.
258
+ */
259
+ public set host(host: string) {
260
+ if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
261
+ this._url.host = host;
262
+ this.setAttributeNS(null, 'href', this._url.toString());
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Returns hostname.
268
+ *
269
+ * @returns Hostname.
270
+ */
271
+ public get hostname(): string {
272
+ return this._url?.hostname ?? '';
273
+ }
274
+
275
+ /**
276
+ * Sets hostname.
277
+ *
278
+ * @param hostname Hostname.
279
+ */
280
+ public set hostname(hostname: string) {
281
+ if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
282
+ this._url.hostname = hostname;
283
+ this.setAttributeNS(null, 'href', this._url.toString());
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Returns referrerPolicy.
289
+ *
290
+ * @returns Referrer Policy.
291
+ */
292
+ public get referrerPolicy(): string {
293
+ return this.getAttributeNS(null, 'referrerPolicy') || '';
294
+ }
295
+
296
+ /**
297
+ * Sets referrerPolicy.
298
+ *
299
+ * @param referrerPolicy Referrer Policy.
300
+ */
301
+ public set referrerPolicy(referrerPolicy: string) {
302
+ this.setAttributeNS(null, 'referrerPolicy', referrerPolicy);
303
+ }
304
+
305
+ /**
306
+ * Returns rel.
307
+ *
308
+ * @returns Rel.
309
+ */
310
+ public get rel(): string {
311
+ return this.getAttributeNS(null, 'rel') || '';
312
+ }
313
+
314
+ /**
315
+ * Sets rel.
316
+ *
317
+ * @param rel Rel.
318
+ */
319
+ public set rel(rel: string) {
320
+ this.setAttributeNS(null, 'rel', rel);
321
+ }
322
+
323
+ /**
324
+ * Returns rel list.
325
+ *
326
+ * @returns Rel list.
327
+ */
328
+ public get relList(): IDOMTokenList {
329
+ if (!this._relList) {
330
+ this._relList = new DOMTokenList(this, 'rel');
331
+ }
332
+ return <IDOMTokenList>this._relList;
333
+ }
334
+
335
+ /**
336
+ * Returns search.
337
+ *
338
+ * @returns Search.
339
+ */
340
+ public get search(): string {
341
+ return this._url?.search ?? '';
342
+ }
343
+
344
+ /**
345
+ * Sets search.
346
+ *
347
+ * @param search Search.
348
+ */
349
+ public set search(search: string) {
350
+ if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
351
+ this._url.search = search;
352
+ this.setAttributeNS(null, 'href', this._url.toString());
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Returns target.
358
+ *
359
+ * @returns target.
360
+ */
361
+ public get target(): string {
362
+ return this.getAttributeNS(null, 'target') || '';
363
+ }
364
+
365
+ /**
366
+ * Sets target.
367
+ *
368
+ * @param target Target.
369
+ */
370
+ public set target(target: string) {
371
+ this.setAttributeNS(null, 'target', target);
372
+ }
373
+
374
+ /**
375
+ * Returns text.
376
+ *
377
+ * @returns text.
378
+ */
379
+ public get text(): string {
380
+ return this.textContent;
381
+ }
382
+
383
+ /**
384
+ * Sets text.
385
+ *
386
+ * @param text Text.
387
+ */
388
+ public set text(text: string) {
389
+ this.textContent = text;
390
+ }
391
+
392
+ /**
393
+ * Returns type.
394
+ *
395
+ * @returns Type.
396
+ */
397
+ public get type(): string {
398
+ return this.getAttributeNS(null, 'type') || '';
399
+ }
400
+
401
+ /**
402
+ * Sets type.
403
+ *
404
+ * @param type Type.
405
+ */
406
+ public set type(type: string) {
407
+ this.setAttributeNS(null, 'type', type);
408
+ }
409
+
410
+ /**
411
+ * @override
412
+ */
413
+ public override setAttributeNode(attribute: IAttr): IAttr {
414
+ const replacedAttribute = super.setAttributeNode(attribute);
415
+
416
+ if (attribute.name === 'rel' && this._relList) {
417
+ this._relList._updateIndices();
418
+ } else if (attribute.name === 'href') {
419
+ this._url = HTMLAnchorElementUtility.getUrl(this.ownerDocument, attribute.value);
420
+ }
421
+
422
+ return replacedAttribute;
423
+ }
424
+
425
+ /**
426
+ * @override
427
+ */
428
+ public override removeAttributeNode(attribute: IAttr): IAttr {
429
+ super.removeAttributeNode(attribute);
430
+
431
+ if (attribute.name === 'rel' && this._relList) {
432
+ this._relList._updateIndices();
433
+ } else if (attribute.name === 'href') {
434
+ this._url = null;
435
+ }
436
+
437
+ return attribute;
438
+ }
439
+ }
@@ -0,0 +1,48 @@
1
+ import IDocument from '../document/IDocument';
2
+ import { URL } from 'url';
3
+
4
+ /**
5
+ * HTML Anchor Element utility.
6
+ */
7
+ export default class HTMLAnchorElementUtility {
8
+ /**
9
+ * Returns "true" if it is a blob URL.
10
+ *
11
+ * According to spec, if element's url is non-null, its scheme is "blob", and it has an opaque path, then the process of updating properties on the URL should be terminated.
12
+ *
13
+ * @see https://html.spec.whatwg.org/multipage/links.html#reinitialise-url
14
+ * @param url
15
+ * @param url URL.
16
+ * @returns "true" if blob URL.
17
+ */
18
+ public static isBlobURL(url: URL): boolean {
19
+ return (
20
+ url && url.protocol === 'blob:' && url.pathname.length > 1 && url.pathname.includes('://')
21
+ );
22
+ }
23
+
24
+ /**
25
+ * Returns URL.
26
+ *
27
+ * @see https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-href
28
+ * @see https://html.spec.whatwg.org/multipage/links.html#hyperlink
29
+ * @param document Document.
30
+ * @param href Href.
31
+ * @returns URL.
32
+ */
33
+ public static getUrl(document: IDocument, href: string | null): URL {
34
+ if (!href) {
35
+ return null;
36
+ }
37
+
38
+ const documentUrl = document.location.href;
39
+
40
+ try {
41
+ return new URL(href.trim(), documentUrl);
42
+ } catch (TypeError) {
43
+ // Ignore error
44
+ }
45
+
46
+ return null;
47
+ }
48
+ }
@@ -0,0 +1,21 @@
1
+ import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
2
+ import IHTMLElement from '../html-element/IHTMLElement';
3
+ import IHTMLHyperlinkElementUtils from './IHTMLHyperlinkElementUtils';
4
+
5
+ /**
6
+ * HTML Anchor Element.
7
+ *
8
+ * Reference:
9
+ * https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement.
10
+ */
11
+ export default interface IHTMLAnchorElement extends IHTMLElement, IHTMLHyperlinkElementUtils {
12
+ readonly relList: IDOMTokenList;
13
+ download: string;
14
+ ping: string;
15
+ hreflang: string;
16
+ referrerPolicy: string;
17
+ rel: string;
18
+ target: string;
19
+ text: string;
20
+ type: string;
21
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * HTMLHyperlinkElementUtils.
3
+ *
4
+ * Reference:
5
+ * https://html.spec.whatwg.org/multipage/links.html#htmlhyperlinkelementutils.
6
+ */
7
+ export default interface IHTMLHyperlinkElementUtils {
8
+ readonly origin: string;
9
+ href: string;
10
+ protocol: string;
11
+ username: string;
12
+ password: string;
13
+ host: string;
14
+ hostname: string;
15
+ port: string;
16
+ pathname: string;
17
+ search: string;
18
+ hash: string;
19
+ }
@@ -180,17 +180,17 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
180
180
  }
181
181
 
182
182
  /**
183
- * The setAttributeNode() method adds a new Attr node to the specified element.
184
- *
185
183
  * @override
186
- * @param attribute Attribute.
187
- * @returns Replaced attribute.
188
184
  */
189
- public setAttributeNode(attribute: IAttr): IAttr {
185
+ public override setAttributeNode(attribute: IAttr): IAttr {
190
186
  const replacedAttribute = super.setAttributeNode(attribute);
191
187
  const rel = this.getAttributeNS(null, 'rel');
192
188
  const href = this.getAttributeNS(null, 'href');
193
189
 
190
+ if (attribute.name === 'rel' && this._relList) {
191
+ this._relList._updateIndices();
192
+ }
193
+
194
194
  if (
195
195
  (attribute.name === 'rel' || attribute.name === 'href') &&
196
196
  href !== null &&
@@ -233,6 +233,19 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
233
233
  return replacedAttribute;
234
234
  }
235
235
 
236
+ /**
237
+ * @override
238
+ */
239
+ public override removeAttributeNode(attribute: IAttr): IAttr {
240
+ super.removeAttributeNode(attribute);
241
+
242
+ if (attribute.name === 'rel' && this._relList) {
243
+ this._relList._updateIndices();
244
+ }
245
+
246
+ return attribute;
247
+ }
248
+
236
249
  /**
237
250
  * @override
238
251
  */
@@ -280,15 +293,4 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
280
293
  }
281
294
  }
282
295
  }
283
-
284
- /**
285
- * Updates DOM list indices.
286
- */
287
- protected _updateDomListIndices(): void {
288
- super._updateDomListIndices();
289
-
290
- if (this._relList) {
291
- this._relList._updateIndices();
292
- }
293
- }
294
296
  }
@@ -109,7 +109,7 @@ export default class HTMLOptionElement extends HTMLElement implements IHTMLOptio
109
109
  * @returns Value.
110
110
  */
111
111
  public get value(): string {
112
- return this.getAttributeNS(null, 'value') || '';
112
+ return this.getAttributeNS(null, 'value') || this.textContent;
113
113
  }
114
114
 
115
115
  /**