Skip to content

Commit

Permalink
capricorn86#1168@patch: Normalize whitespace in processing of DOMToke…
Browse files Browse the repository at this point in the history
…nList.
  • Loading branch information
takenspc committed Nov 24, 2023
1 parent 9a0062b commit 5824775
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 18 deletions.
40 changes: 22 additions & 18 deletions packages/happy-dom/src/dom-token-list/DOMTokenList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ export default class DOMTokenList implements IDOMTokenList {
* @param newToken NewToken.
*/
public replace(token: string, newToken: string): boolean {
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];
const list = this._getTokenList();
const index = list.indexOf(token);
if (index === -1) {
return false;
Expand All @@ -81,17 +80,15 @@ export default class DOMTokenList implements IDOMTokenList {
* Returns an iterator, allowing you to go through all values of the key/value pairs contained in this object.
*/
public values(): IterableIterator<string> {
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];
const list = this._getTokenList();
return list.values();
}

/**
* Returns an iterator, allowing you to go through all key/value pairs contained in this object.
*/
public entries(): IterableIterator<[number, string]> {
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];
const list = this._getTokenList();
return list.entries();
}

Expand All @@ -102,8 +99,7 @@ export default class DOMTokenList implements IDOMTokenList {
* @param thisArg
*/
public forEach(callback: (currentValue, currentIndex, listObj) => void, thisArg?: this): void {
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];
const list = this._getTokenList();
return list.forEach(callback, thisArg);
}

Expand All @@ -112,8 +108,7 @@ export default class DOMTokenList implements IDOMTokenList {
*
*/
public keys(): IterableIterator<number> {
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];
const list = this._getTokenList();
return list.keys();
}

Expand All @@ -123,8 +118,7 @@ export default class DOMTokenList implements IDOMTokenList {
* @param tokens Tokens.
*/
public add(...tokens: string[]): void {
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];
const list = this._getTokenList();

for (const token of tokens) {
const index = list.indexOf(token);
Expand All @@ -144,8 +138,7 @@ export default class DOMTokenList implements IDOMTokenList {
* @param tokens Tokens.
*/
public remove(...tokens: string[]): void {
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];
const list = this._getTokenList();

for (const token of tokens) {
const index = list.indexOf(token);
Expand All @@ -164,8 +157,8 @@ export default class DOMTokenList implements IDOMTokenList {
* @returns TRUE if it contains.
*/
public contains(className: string): boolean {
const attr = this._ownerElement.getAttribute(this._attributeName);
return (attr ? attr.split(' ') : []).includes(className);
const list = this._getTokenList();
return list.includes(className);
}

/**
Expand Down Expand Up @@ -198,8 +191,7 @@ export default class DOMTokenList implements IDOMTokenList {
* Updates indices.
*/
public _updateIndices(): void {
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];
const list = this._getTokenList();

for (let i = list.length - 1, max = this.length; i < max; i++) {
delete this[i];
Expand All @@ -212,6 +204,18 @@ export default class DOMTokenList implements IDOMTokenList {
(<number>this.length) = list.length;
}

/**
* Returns token list from attribute value.
*
* @see https://infra.spec.whatwg.org/#split-on-ascii-whitespace
*/
private _getTokenList(): string[] {
let attr = this._ownerElement.getAttribute(this._attributeName) ?? '';
attr = attr.replace(/^[\t\n\f\r ]+|[\t\n\f\r ]+$/g, '');
attr = attr.replace(/[\t\n\f\r ]+/g, ' ');
return attr === '' ? [] : Array.from(new Set(attr.split(' ')));
}

/**
* Returns DOMTokenList value.
*/
Expand Down
8 changes: 8 additions & 0 deletions packages/happy-dom/test/dom-token-list/DOMTokenList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,12 @@ describe('DOMTokenList', () => {
expect(element.classList.toString()).toEqual('class1 class2 class3');
});
});

describe('whitespace handling', () => {
it('Normalizes whitespace to a single space', () => {
element.className = ' class1 class2\nclass3 ';
expect(Array.from(element.classList.values())).toEqual(['class1', 'class2', 'class3']);
expect(element.classList.toString()).toEqual(' class1 class2\nclass3 ');
});
});
});

0 comments on commit 5824775

Please sign in to comment.