Skip to content

Commit

Permalink
Merge branch '666-fixes-cookie-jar' of https://github.com/Mas0nShi/ha…
Browse files Browse the repository at this point in the history
…ppy-dom into 666-fixes-cookie-jar
  • Loading branch information
Mas0nShi committed Nov 30, 2022
2 parents 1592878 + d142006 commit 5499e59
Show file tree
Hide file tree
Showing 36 changed files with 2,001 additions and 344 deletions.
3 changes: 2 additions & 1 deletion packages/happy-dom/src/config/ElementTag.ts
Expand Up @@ -20,9 +20,10 @@ import HTMLDialogElement from '../nodes/html-dialog-element/HTMLDialogElement';
import HTMLButtonElement from '../nodes/html-button-element/HTMLButtonElement';
import HTMLAudioElement from '../nodes/html-audio-element/HTMLAudioElement';
import HTMLVideoElement from '../nodes/html-video-element/HTMLVideoElement';
import HTMLAnchorElement from '../nodes/html-anchor-element/HTMLAnchorElement';

export default {
A: HTMLElement,
A: HTMLAnchorElement,
ABBR: HTMLElement,
ADDRESS: HTMLElement,
AREA: HTMLElement,
Expand Down
Expand Up @@ -77,21 +77,17 @@ export default abstract class AbstractCSSStyleDeclaration {

if (this._ownerElement) {
const style = new CSSStyleDeclarationPropertyManager({ cssText });
if (!style.size()) {
delete this._ownerElement['_attributes']['style'];
} else {
if (!this._ownerElement['_attributes']['style']) {
Attr._ownerDocument = this._ownerElement.ownerDocument;
this._ownerElement['_attributes']['style'] = new Attr();
this._ownerElement['_attributes']['style'].name = 'style';
}

if (this._ownerElement.isConnected) {
this._ownerElement.ownerDocument['_cacheID']++;
}
if (!this._ownerElement['_attributes']['style']) {
Attr._ownerDocument = this._ownerElement.ownerDocument;
this._ownerElement['_attributes']['style'] = new Attr();
this._ownerElement['_attributes']['style'].name = 'style';
}

this._ownerElement['_attributes']['style'].value = style.toString();
if (this._ownerElement.isConnected) {
this._ownerElement.ownerDocument['_cacheID']++;
}

this._ownerElement['_attributes']['style'].value = style.toString();
} else {
this._style = new CSSStyleDeclarationPropertyManager({ cssText });
}
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/dom-token-list/DOMTokenList.ts
Expand Up @@ -199,7 +199,7 @@ export default class DOMTokenList implements IDOMTokenList {
* Updates indices.
*/
public _updateIndices(): void {
const attr = this._ownerElement.getAttribute('class');
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];

for (let i = list.length - 1, max = this.length; i < max; i++) {
Expand Down
70 changes: 70 additions & 0 deletions packages/happy-dom/src/named-node-map/INamedNodeMap.ts
@@ -0,0 +1,70 @@
import IAttr from '../nodes/attr/IAttr';

/**
* NamedNodeMap.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap.
*/
export default interface INamedNodeMap extends Iterable<IAttr> {
[index: number]: IAttr;
[Symbol.toStringTag]: string;
readonly length: number;

/**
* Returns attribute by index.
*
* @param index Index.
*/
item: (index: number) => IAttr;

/**
* Returns attribute by name.
*
* @param qualifiedName Name.
* @returns Attribute.
*/
getNamedItem: (qualifiedName: string) => IAttr;

/**
* Returns attribute by name and namespace.
*
* @param namespace Namespace.
* @param localName Local name of the attribute.
* @returns Attribute.
*/
getNamedItemNS: (namespace: string, localName: string) => IAttr;

/**
* Adds a new attribute node.
*
* @param attr Attribute.
* @returns Replaced attribute.
*/
setNamedItem: (attr: IAttr) => IAttr;

/**
* Adds a new namespaced attribute node.
*
* @param attr Attribute.
* @returns Replaced attribute.
*/
setNamedItemNS: (attr: IAttr) => IAttr;

/**
* Removes an attribute.
*
* @param qualifiedName Name of the attribute.
* @returns Removed attribute.
*/
removeNamedItem: (qualifiedName: string) => IAttr;

/**
* Removes a namespaced attribute.
*
* @param namespace Namespace.
* @param localName Local name of the attribute.
* @returns Removed attribute.
*/
removeNamedItemNS: (namespace: string, localName: string) => IAttr;
}
145 changes: 145 additions & 0 deletions packages/happy-dom/src/named-node-map/NamedNodeMap.ts
@@ -0,0 +1,145 @@
import type Element from '../nodes/element/Element';
import IAttr from '../nodes/attr/IAttr';
import INamedNodeMap from './INamedNodeMap';

/**
* NamedNodeMap.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap.
*/
export default class NamedNodeMap implements INamedNodeMap {
[index: number]: IAttr;

/**
* Reference to the element.
*/
#ownerElement: Element;

/**
* Constructor.
*
* @param element Associated element.
*/
constructor(element: Element) {
this.#ownerElement = element;
}

/**
* Returns string.
*
* @returns string.
*/
public get [Symbol.toStringTag](): string {
return this.constructor.name;
}

/**
* Length.
*
* @returns Length.
*/
public get length(): number {
return Object.keys(this.#ownerElement._attributes).length;
}

/**
* Returns attribute by index.
*
* @param index Index.
*/
public item(index: number): IAttr | null {
if (index < 0) {
return null;
}
const attr = Object.values(this.#ownerElement._attributes)[index];
return attr ? attr : null;
}

/**
* Returns attribute by name.
*
* @param qualifiedName Name.
* @returns Attribute.
*/
public getNamedItem(qualifiedName: string): IAttr | null {
return this.#ownerElement.getAttributeNode(qualifiedName);
}

/**
* Returns attribute by name and namespace.
*
* @param namespace Namespace.
* @param localName Local name of the attribute.
* @returns Attribute.
*/
public getNamedItemNS(namespace: string, localName: string): IAttr | null {
return this.#ownerElement.getAttributeNodeNS(namespace, localName);
}

/**
* Adds a new attribute node.
*
* @param attr Attribute.
* @returns Replaced attribute.
*/
public setNamedItem(attr: IAttr): IAttr {
return this.#ownerElement.setAttributeNode(attr);
}

/**
* Adds a new namespaced attribute node.
*
* @param attr Attribute.
* @returns Replaced attribute.
*/
public setNamedItemNS(attr: IAttr): IAttr {
return this.#ownerElement.setAttributeNodeNS(attr);
}

/**
* Removes an attribute.
*
* @param qualifiedName Name of the attribute.
* @returns Removed attribute.
*/
public removeNamedItem(qualifiedName: string): IAttr | null {
const attr = this.getNamedItem(qualifiedName);

if (attr) {
this.#ownerElement.removeAttributeNode(attr);
}
return attr;
}

/**
* Removes a namespaced attribute.
*
* @param namespace Namespace.
* @param localName Local name of the attribute.
* @returns Removed attribute.
*/
public removeNamedItemNS(namespace: string, localName: string): IAttr | null {
const attr = this.getNamedItemNS(namespace, localName);

if (attr) {
this.#ownerElement.removeAttributeNode(attr);
}
return attr;
}

/**
* Iterator.
*
* @returns Iterator.
*/
public [Symbol.iterator](): Iterator<IAttr> {
let index = -1;
return {
next: () => {
index++;
return { value: this.item(index), done: index >= this.length };
}
};
}
}
45 changes: 26 additions & 19 deletions packages/happy-dom/src/nodes/element/Element.ts
@@ -1,6 +1,7 @@
import Node from '../node/Node';
import ShadowRoot from '../shadow-root/ShadowRoot';
import Attr from '../attr/Attr';
import NamedNodeMap from '../../named-node-map/NamedNodeMap';
import DOMRect from './DOMRect';
import DOMTokenList from '../../dom-token-list/DOMTokenList';
import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
Expand All @@ -26,6 +27,8 @@ import IText from '../text/IText';
import IDOMRectList from './IDOMRectList';
import DOMRectListFactory from './DOMRectListFactory';
import IAttr from '../attr/IAttr';
import INamedNodeMap from '../../named-node-map/INamedNodeMap';

import Event from '../../event/Event';

/**
Expand Down Expand Up @@ -249,11 +252,8 @@ export default class Element extends Node implements IElement {
*
* @returns Attributes.
*/
public get attributes(): { [k: string | number]: IAttr } & { length: number } {
const attributes = Object.values(this._attributes);
return Object.assign({}, this._attributes, attributes, {
length: attributes.length
});
public get attributes(): INamedNodeMap {
return Object.assign(new NamedNodeMap(this), Object.values(this._attributes), this._attributes);
}

/**
Expand Down Expand Up @@ -840,7 +840,9 @@ export default class Element extends Node implements IElement {

this._attributes[name] = attribute;

this._updateDomListIndices();
if (attribute.name === 'class' && this._classList) {
this._classList._updateIndices();
}

if (
this.attributeChangedCallback &&
Expand Down Expand Up @@ -919,15 +921,26 @@ export default class Element extends Node implements IElement {
* Removes an Attr node.
*
* @param attribute Attribute.
* @returns Removed attribute.
*/
public removeAttributeNode(attribute: IAttr): void {
public removeAttributeNode(attribute: IAttr): IAttr {
const removedAttribute = this._attributes[attribute.name];

if (removedAttribute !== attribute) {
throw new DOMException(
`Failed to execute 'removeAttributeNode' on 'Element': The node provided is owned by another element.`
);
}

delete this._attributes[attribute.name];

if (this.isConnected) {
this.ownerDocument['_cacheID']++;
}

this._updateDomListIndices();
if (attribute.name === 'class' && this._classList) {
this._classList._updateIndices();
}

if (
this.attributeChangedCallback &&
Expand All @@ -954,15 +967,18 @@ export default class Element extends Node implements IElement {
}
}
}

return attribute;
}

/**
* Removes an Attr node.
*
* @param attribute Attribute.
* @returns Removed attribute.
*/
public removeAttributeNodeNS(attribute: IAttr): void {
this.removeAttributeNode(attribute);
public removeAttributeNodeNS(attribute: IAttr): IAttr {
return this.removeAttributeNode(attribute);
}

/**
Expand Down Expand Up @@ -1021,13 +1037,4 @@ export default class Element extends Node implements IElement {
}
return name.toLowerCase();
}

/**
* Updates DOM list indices.
*/
protected _updateDomListIndices(): void {
if (this._classList) {
this._classList._updateIndices();
}
}
}

0 comments on commit 5499e59

Please sign in to comment.