happy-dom 7.6.4 → 7.6.5

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 (27) hide show
  1. package/lib/nodes/element/Element.d.ts +4 -2
  2. package/lib/nodes/element/Element.js +8 -1
  3. package/lib/nodes/element/Element.js.map +1 -1
  4. package/lib/nodes/element/IElement.d.ts +4 -2
  5. package/lib/nodes/html-element/HTMLElement.d.ts +1 -12
  6. package/lib/nodes/html-element/HTMLElement.js +1 -11
  7. package/lib/nodes/html-element/HTMLElement.js.map +1 -1
  8. package/lib/nodes/html-option-element/HTMLOptionElement.d.ts +17 -0
  9. package/lib/nodes/html-option-element/HTMLOptionElement.js +55 -32
  10. package/lib/nodes/html-option-element/HTMLOptionElement.js.map +1 -1
  11. package/lib/nodes/html-option-element/HTMLOptionsCollection.d.ts +0 -1
  12. package/lib/nodes/html-option-element/HTMLOptionsCollection.js +2 -10
  13. package/lib/nodes/html-option-element/HTMLOptionsCollection.js.map +1 -1
  14. package/lib/nodes/html-select-element/HTMLSelectElement.d.ts +18 -2
  15. package/lib/nodes/html-select-element/HTMLSelectElement.js +99 -22
  16. package/lib/nodes/html-select-element/HTMLSelectElement.js.map +1 -1
  17. package/lib/nodes/svg-element/SVGElement.d.ts +1 -8
  18. package/lib/nodes/svg-element/SVGElement.js +1 -7
  19. package/lib/nodes/svg-element/SVGElement.js.map +1 -1
  20. package/package.json +2 -2
  21. package/src/nodes/element/Element.ts +15 -3
  22. package/src/nodes/element/IElement.ts +4 -2
  23. package/src/nodes/html-element/HTMLElement.ts +3 -12
  24. package/src/nodes/html-option-element/HTMLOptionElement.ts +69 -35
  25. package/src/nodes/html-option-element/HTMLOptionsCollection.ts +2 -9
  26. package/src/nodes/html-select-element/HTMLSelectElement.ts +109 -27
  27. package/src/nodes/svg-element/SVGElement.ts +3 -8
@@ -167,13 +167,14 @@ export default class HTMLSelectElement extends HTMLElement implements IHTMLSelec
167
167
  * @returns Value.
168
168
  */
169
169
  public get value(): string {
170
- if (this.options.selectedIndex === -1) {
171
- return '';
170
+ for (let i = 0, max = this.options.length; i < max; i++) {
171
+ const option = <HTMLOptionElement>this.options[i];
172
+ if (option._selectedness) {
173
+ return option.value;
174
+ }
172
175
  }
173
176
 
174
- const option = this.options[this.options.selectedIndex];
175
-
176
- return option instanceof HTMLOptionElement ? option.value : '';
177
+ return '';
177
178
  }
178
179
 
179
180
  /**
@@ -182,9 +183,15 @@ export default class HTMLSelectElement extends HTMLElement implements IHTMLSelec
182
183
  * @param value Value.
183
184
  */
184
185
  public set value(value: string) {
185
- this.options.selectedIndex = this.options.findIndex(
186
- (o) => o instanceof HTMLOptionElement && o.value === value
187
- );
186
+ for (let i = 0, max = this.options.length; i < max; i++) {
187
+ const option = <HTMLOptionElement>this.options[i];
188
+ if (option.value === value) {
189
+ option._selectedness = true;
190
+ option._dirtyness = true;
191
+ } else {
192
+ option._selectedness = false;
193
+ }
194
+ }
188
195
  }
189
196
 
190
197
  /**
@@ -193,16 +200,31 @@ export default class HTMLSelectElement extends HTMLElement implements IHTMLSelec
193
200
  * @returns Value.
194
201
  */
195
202
  public get selectedIndex(): number {
196
- return this.options.selectedIndex;
203
+ for (let i = 0, max = this.options.length; i < max; i++) {
204
+ if ((<HTMLOptionElement>this.options[i])._selectedness) {
205
+ return i;
206
+ }
207
+ }
208
+ return -1;
197
209
  }
198
210
 
199
211
  /**
200
212
  * Sets value.
201
213
  *
202
- * @param value Value.
214
+ * @param selectedIndex Selected index.
203
215
  */
204
- public set selectedIndex(value: number) {
205
- this.options.selectedIndex = value;
216
+ public set selectedIndex(selectedIndex: number) {
217
+ if (typeof selectedIndex === 'number' && !isNaN(selectedIndex)) {
218
+ for (let i = 0, max = this.options.length; i < max; i++) {
219
+ (<HTMLOptionElement>this.options[i])._selectedness = false;
220
+ }
221
+
222
+ const selectedOption = <HTMLOptionElement>this.options[selectedIndex];
223
+ if (selectedOption) {
224
+ selectedOption._selectedness = true;
225
+ selectedOption._dirtyness = true;
226
+ }
227
+ }
206
228
  }
207
229
 
208
230
  /**
@@ -287,13 +309,10 @@ export default class HTMLSelectElement extends HTMLElement implements IHTMLSelec
287
309
 
288
310
  if (element.tagName === 'OPTION' || element.tagName === 'OPTGROUP') {
289
311
  this.options.push(<IHTMLOptionElement | IHTMLOptGroupElement>element);
290
-
291
- if (this.options.length === 1) {
292
- this.options.selectedIndex = 0;
293
- }
294
312
  }
295
313
 
296
314
  this._updateIndexProperties(previousLength, this.options.length);
315
+ this._resetOptionSelectednes();
297
316
  }
298
317
 
299
318
  return super.appendChild(node);
@@ -332,15 +351,15 @@ export default class HTMLSelectElement extends HTMLElement implements IHTMLSelec
332
351
  } else {
333
352
  this.options.push(<IHTMLOptionElement | IHTMLOptGroupElement>newElement);
334
353
  }
335
-
336
- if (this.options.length === 1) {
337
- this.options.selectedIndex = 0;
338
- }
339
354
  }
340
355
 
341
356
  this._updateIndexProperties(previousLength, this.options.length);
342
357
  }
343
358
 
359
+ if (newNode.nodeType === NodeTypeEnum.elementNode) {
360
+ this._resetOptionSelectednes();
361
+ }
362
+
344
363
  return returnValue;
345
364
  }
346
365
 
@@ -358,20 +377,83 @@ export default class HTMLSelectElement extends HTMLElement implements IHTMLSelec
358
377
  if (index !== -1) {
359
378
  this.options.splice(index, 1);
360
379
  }
380
+ }
361
381
 
362
- if (this.options.selectedIndex >= this.options.length) {
363
- this.options.selectedIndex = this.options.length - 1;
382
+ this._updateIndexProperties(previousLength, this.options.length);
383
+ this._resetOptionSelectednes();
384
+ }
385
+
386
+ return super.removeChild(node);
387
+ }
388
+
389
+ /**
390
+ * Resets the option selectedness.
391
+ *
392
+ * Based on:
393
+ * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/nodes/HTMLSelectElement-impl.js
394
+ *
395
+ * @param [newOption] Optional new option element to be selected.
396
+ * @see https://html.spec.whatwg.org/multipage/form-elements.html#selectedness-setting-algorithm
397
+ */
398
+ public _resetOptionSelectednes(newOption?: IHTMLOptionElement): void {
399
+ if (this.hasAttributeNS(null, 'multiple')) {
400
+ return;
401
+ }
402
+
403
+ const selected: HTMLOptionElement[] = [];
404
+
405
+ for (let i = 0, max = this.options.length; i < max; i++) {
406
+ if (newOption) {
407
+ (<HTMLOptionElement>this.options[i])._selectedness = this.options[i] === newOption;
408
+ }
409
+
410
+ if ((<HTMLOptionElement>this.options[i])._selectedness) {
411
+ selected.push(<HTMLOptionElement>this.options[i]);
412
+ }
413
+ }
414
+
415
+ const size = this._getDisplaySize();
416
+
417
+ if (size === 1 && !selected.length) {
418
+ for (let i = 0, max = this.options.length; i < max; i++) {
419
+ const option = <HTMLOptionElement>this.options[i];
420
+
421
+ let disabled = option.hasAttributeNS(null, 'disabled');
422
+ const parentNode = <IHTMLElement>option.parentNode;
423
+ if (
424
+ parentNode &&
425
+ parentNode.nodeType === NodeTypeEnum.elementNode &&
426
+ parentNode.tagName === 'OPTGROUP' &&
427
+ parentNode.hasAttributeNS(null, 'disabled')
428
+ ) {
429
+ disabled = true;
364
430
  }
365
431
 
366
- if (!this.options.length) {
367
- this.options.selectedIndex = -1;
432
+ if (!disabled) {
433
+ option._selectedness = true;
434
+ break;
368
435
  }
369
436
  }
370
-
371
- this._updateIndexProperties(previousLength, this.options.length);
437
+ } else if (selected.length >= 2) {
438
+ for (let i = 0, max = this.options.length; i < max; i++) {
439
+ (<HTMLOptionElement>this.options[i])._selectedness = i === selected.length - 1;
440
+ }
372
441
  }
442
+ }
373
443
 
374
- return super.removeChild(node);
444
+ /**
445
+ * Returns display size.
446
+ *
447
+ * @returns Display size.
448
+ */
449
+ protected _getDisplaySize(): number {
450
+ if (this.hasAttributeNS(null, 'size')) {
451
+ const size = parseInt(this.getAttributeNS(null, 'size'));
452
+ if (!isNaN(size) && size >= 0) {
453
+ return size;
454
+ }
455
+ }
456
+ return this.hasAttributeNS(null, 'multiple') ? 4 : 1;
375
457
  }
376
458
 
377
459
  /**
@@ -74,11 +74,7 @@ export default class SVGElement extends Element implements ISVGElement {
74
74
  }
75
75
 
76
76
  /**
77
- * The setAttributeNode() method adds a new Attr node to the specified element.
78
- *
79
77
  * @override
80
- * @param attribute Attribute.
81
- * @returns Replaced attribute.
82
78
  */
83
79
  public setAttributeNode(attribute: IAttr): IAttr {
84
80
  const replacedAttribute = super.setAttributeNode(attribute);
@@ -91,16 +87,15 @@ export default class SVGElement extends Element implements ISVGElement {
91
87
  }
92
88
 
93
89
  /**
94
- * Removes an Attr node.
95
- *
96
90
  * @override
97
- * @param attribute Attribute.
98
91
  */
99
- public removeAttributeNode(attribute: IAttr): void {
92
+ public removeAttributeNode(attribute: IAttr): IAttr {
100
93
  super.removeAttributeNode(attribute);
101
94
 
102
95
  if (attribute.name === 'style' && this._style) {
103
96
  this._style.cssText = '';
104
97
  }
98
+
99
+ return attribute;
105
100
  }
106
101
  }