“use strict”;
const DOMException = require(“../web-idl/DOMException”); const orderedSetParser = require(“./helpers/ordered-set-parser”);
// dom.spec.whatwg.org/#domtokenlist
const INTERNAL = Symbol(“DOMTokenList internal”);
class DOMTokenList {
constructor() { throw new TypeError("Illegal constructor"); } item(index) { const length = this.length; return length <= index || index < 0 ? null : this[index]; } contains(token) { token = String(token); return indexOf(this, token) !== -1; } replace(token, newToken) { token = String(token); newToken = String(newToken); validateTokens(token, newToken); const tokenIndex = indexOf(this, token); if (tokenIndex === -1) { return; } const newTokenIndex = indexOf(this, newToken); if (newTokenIndex !== -1) { spliceLite(this, newTokenIndex, 1); } this[INTERNAL].tokens[tokenIndex] = newToken; update(this); } add(/* tokens... */) { for (let i = 0; i < arguments.length; i++) { const token = String(arguments[i]); validateTokens(token); if (indexOf(this, token) === -1) { push(this, token); } } update(this); } remove(/* tokens... */) { for (let i = 0; i < arguments.length; i++) { const token = String(arguments[i]); validateTokens(token); const index = indexOf(this, token); if (index !== -1) { spliceLite(this, index, 1); } } update(this); } // if force is true, this behaves like add // if force is false, this behaves like remove // if force is undefined, this behaves as one would expect toggle to // always returns whether classList contains token after toggling toggle(token, force) { token = String(token); force = force === undefined ? undefined : Boolean(force); validateTokens(token); const index = indexOf(this, token); if (index !== -1) { if (force === false || force === undefined) { spliceLite(this, index, 1); update(this); return false; } return true; } if (force === false) { return false; } push(this, token); update(this); return true; } get length() { return this[INTERNAL].tokens.length; } get value() { return serialize(this); } set value(v) { this[INTERNAL].element.setAttribute(this[INTERNAL].attribute, v); } toString() { return serialize(this); }
}
function serialize(list) {
const value = list[INTERNAL].element.getAttribute(list[INTERNAL].attribute); return value === null ? "" : value;
}
function validateTokens(/* tokens… */) {
for (let i = 0; i < arguments.length; i++) { const token = String(arguments[i]); if (token === "") { throw new DOMException(DOMException.SYNTAX_ERR, "The token provided must not be empty."); } } for (let i = 0; i < arguments.length; i++) { const token = String(arguments[i]); if (/\s/.test(token)) { const whitespaceMsg = "The token provided contains HTML space characters, which are not valid in tokens."; throw new DOMException(DOMException.INVALID_CHARACTER_ERR, whitespaceMsg); } }
}
function update(list) {
const attribute = list[INTERNAL].attribute; list[INTERNAL].element.setAttribute(attribute, list[INTERNAL].tokens.join(" "));
}
// calls indexOf on internal array function indexOf(dtl, token) {
return dtl[INTERNAL].tokens.indexOf(token);
}
// calls push on internal array, then manually adds indexed property to dtl function push(dtl, token) {
const len = dtl[INTERNAL].tokens.push(token); dtl[len - 1] = token; return len;
}
// calls splice on internal array then rewrites indexed properties of dtl // does not allow items to be added, only removed, so splice-lite function spliceLite(dtl, start, deleteCount) {
const tokens = dtl[INTERNAL].tokens; const removedTokens = tokens.splice(start, deleteCount); // remove indexed properties from list const re = /^\d+$/; for (const prop in dtl) { if (re.test(prop)) { delete dtl[prop]; } } // copy indexed properties from internal array const len = tokens.length; for (let i = 0; i < len; i++) { dtl[i] = tokens[i]; } return removedTokens;
}
exports.DOMTokenList = DOMTokenList;
// set dom token list without running update steps exports.reset = function resetDOMTokenList(list, value) {
const tokens = list[INTERNAL].tokens; spliceLite(list, 0, tokens.length); if (value) { for (const token of orderedSetParser(value)) { push(list, token); } }
};
exports.create = function createDOMTokenList(element, attribute) {
const list = Object.create(DOMTokenList.prototype); list[INTERNAL] = { element, attribute, tokens: [] }; exports.reset(list, element.getAttribute(attribute)); return list;
};
exports.contains = function domTokenListContains(list, token, options) {
const caseInsensitive = options && options.caseInsensitive; if (!caseInsensitive) { return indexOf(list, token) !== -1; } const tokens = list[INTERNAL].tokens; const lowerToken = token.toLowerCase(); for (let i = 0; i < tokens.length; ++i) { if (tokens[i].toLowerCase() === lowerToken) { return true; } } return false;
};