UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

270 lines (244 loc) 6.3 kB
/** * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved. * Node module: @schukai/monster * * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html * * For those who do not wish to adhere to the AGPLv3, a commercial license is available. * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. * For more information about purchasing a commercial license, please contact schukai GmbH. * * SPDX-License-Identifier: AGPL-3.0 */ import { isIterable, isString } from "./is.mjs"; import { validateFunction, validateString } from "./validate.mjs"; import { Base } from "./base.mjs"; export { TokenList }; /** * A `TokenList` allows you to manage tokens (individual character strings such as css classes in an attribute string). * * The `TokenList` offers various functions to manipulate values. For example, you can add, remove or replace a class in a CSS list. * * This class implements the [iteration protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols). * * @license AGPLv3 * @since 1.2.0 * @copyright schukai GmbH */ class TokenList extends Base { /** * * @param {array|string|iteratable} init */ constructor(init) { super(); this.tokens = new Set(); if (typeof init !== "undefined") { this.add(init); } } /** * Iterator protocol * * @return {Symbol.iterator} */ getIterator() { return this[Symbol.iterator](); } /** * Iterator * * @return {{next: ((function(): ({value: *, done: boolean}))|*)}} */ [Symbol.iterator]() { // Use a new index for each iterator. This makes multiple // iterations over the iterable safe for non-trivial cases, // such as use of break or nested looping over the same iterable. let index = 0; const entries = this.entries(); return { next: () => { if (index < entries.length) { return { value: entries?.[index++], done: false }; } else { return { done: true }; } }, }; } /** * Returns true if it contains token, otherwise false * * @externalExample ../../example/types/tokenlist-2.mjs * @param {array|string|iteratable} value * @return {boolean} */ contains(value) { if (isString(value)) { value = value.trim(); let counter = 0; value.split(" ").forEach((token) => { if (this.tokens.has(token.trim()) === false) return false; counter++; }); return counter > 0 ? true : false; } if (isIterable(value)) { let counter = 0; for (const token of value) { validateString(token); if (this.tokens.has(token.trim()) === false) return false; counter++; } return counter > 0 ? true : false; } return false; } /** * Add tokens * * @externalExample ../../example/types/tokenlist-3.mjs * @param {array|string|iteratable} value * @return {TokenList} * @throws {TypeError} unsupported value */ add(value) { if (isString(value)) { value.split(" ").forEach((token) => { this.tokens.add(token.trim()); }); } else if (isIterable(value)) { for (const token of value) { validateString(token); this.tokens.add(token.trim()); } } else if (typeof value !== "undefined") { throw new TypeError("unsupported value"); } return this; } /** * remove all tokens * * @return {TokenList} */ clear() { this.tokens.clear(); return this; } /** * Removes token * * @externalExample ../../example/types/tokenlist-4.mjs * @param {array|string|iteratable} value * @return {TokenList} * @throws {TypeError} unsupported value */ remove(value) { if (isString(value)) { value.split(" ").forEach((token) => { this.tokens.delete(token.trim()); }); } else if (isIterable(value)) { for (const token of value) { validateString(token); this.tokens.delete(token.trim()); } } else if (typeof value !== "undefined") { throw new TypeError("unsupported value", "types/tokenlist.mjs"); } return this; } /** * this method replaces a token with a new token. * * if the passed token exists, it is replaced with newToken and TokenList is returned. * if the token does not exist, newToken is not set and TokenList is returned. * * @param {string} token * @param {string} newToken * @return {TokenList} */ replace(token, newToken) { validateString(token); validateString(newToken); if (!this.contains(token)) { return this; } const a = Array.from(this.tokens); const i = a.indexOf(token); if (i === -1) return this; a.splice(i, 1, newToken); this.tokens = new Set(); this.add(a); return this; } /** * Removes token from string. If token doesn't exist it's added. * * @externalExample ../../example/types/tokenlist-5.mjs * @param {array|string|iteratable} value * @return {boolean} * @throws {TypeError} unsupported value */ toggle(value) { if (isString(value)) { value.split(" ").forEach((token) => { toggleValue.call(this, token); }); } else if (isIterable(value)) { for (const token of value) { toggleValue.call(this, token); } } else if (typeof value !== "undefined") { throw new TypeError("unsupported value", "types/tokenlist.mjs"); } return this; } /** * returns an array with all tokens * * @return {array} */ entries() { return Array.from(this.tokens); } /** * executes the provided function with each value of the set * * @param {function} callback * @return {TokenList} */ forEach(callback) { validateFunction(callback); this.tokens.forEach(callback); return this; } /** * returns the individual tokens separated by a blank character * * @return {string} */ toString() { return this.entries().join(" "); } } /** * @private * @param token * @return {toggleValue} * @throws {Error} must be called with TokenList.call */ function toggleValue(token) { if (!(this instanceof TokenList)) throw Error("must be called with TokenList.call"); validateString(token); token = token.trim(); if (this.contains(token)) { this.remove(token); return this; } this.add(token); return this; }