// @ts-check
///
///
///
///
///
///
"use strict";
/**
* @name Celestra
* @version 6.6.0 browser
* @author Ferenc Czigler
* @see https://github.com/Serrin/Celestra/
* @license MIT https://opensource.org/licenses/MIT
*/
const VERSION = "Celestra v6.6.0 browser";
/** TS types */
/**
* @description Map-like object with string or number or symbol keys.
*
* @private
* */
type MapLike = Record;
/**
* @description Number-like object.
*
* @private
* */
type NumberLike = number | bigint;
/**
* @description Any iterable or iterator.
*
* @private
*/
type IterableLike = Iterable | Iterator | IterableIterator;
/**
* @description Any iterable, iterator, or array-like structure.
*
* @private
*/
type IterableLikeAndArrayLike =
| Iterable
| Iterator
| IterableIterator
| ArrayLike;
/**
* @description Iterable and Iterator and Generator types.
*
* @private
*/
type IteratorReturn =
| Iterable
| Iterator
| Generator;
/**
* @description Type for undefined and null values.
*
* @private
*/
type Nullish = undefined | null;
/*
built-in type:
type NonNullable = number | boolean | string | symbol | object | Function;
*/
/**
* @description Not null or undefined or object or function.
*
* @private
*/
type NonNullablePrimitive = number | bigint | boolean | string | symbol;
/**
* @description Not object or function.
*
* @private
*/
type Primitive = null | undefined | number | bigint | boolean | string | symbol;
/**
* Generic comparable types.
*
* @private
*/
type Comparable = number | bigint | string | boolean | Date;
/**
* @description Object key type.
*
* @private
*/
type PropertyKey = string | symbol;
/**
* @description Type AsyncFunction.
*
* @private
*/
type AsyncFunction = (...args: ReadonlyArray) => Promise;
/**
* @description Type ArrowFunction.
*
* @private
*/
type ArrowFunction =
(this: void, ...args: Args) => R;
/**
* @description TypedArray types.
*
* @private
*/
type TypedArray = Exclude;
/**
* @description ClearCookiesOptions object type.
*
* @private
*/
type ClearCookiesOptions = {
path?: string | undefined;
domain?: string | undefined;
secure?: boolean | undefined;
SameSite?: string | undefined;
HttpOnly?: boolean | undefined;
};
/** polyfills **/
/* globalThis; */
(function (global) {
if (!global.globalThis) {
if (Object.defineProperty) {
Object.defineProperty(global, "globalThis", {
configurable: true, enumerable: false, value: global, writable: true
});
} else {
global.globalThis = global;
}
}
})(typeof this === "object" ? this : Function("return this")());
/* Math.sumPrecise(); */
if (!("sumPrecise" in Math)) {
// @ts-ignore
Math.sumPrecise = function sumPrecise ([...array]): number {
/* empty iterator */
if (array.length === 0) { return -0; }
/* iterator with items */
if (array.every((value: unknown): boolean => typeof value === "number")) {
/* return NaN + Infinity + -Infinity */
let inf = array.indexOf(Infinity) >- 1;
let negInf = array.indexOf(-Infinity) > -1;
if (array.some((value: unknown): boolean => value !== value)
|| (inf && negInf)) { return NaN; }
if (inf) { return Infinity; }
if (negInf) { return -Infinity; }
/* sum hi */
let hi = array.filter((value: unknown): boolean =>
(value === 1e20 || value === -1e20))
.reduce((acc, value): number => acc + value, 0);
/* sum lo - Kahan sum */
let lo: number = 0.0;
let c: number = 0.0;
for (let item of array.filter((value: unknown): boolean =>
(value !== 1e20 && value !== -1e20))) {
let y = item - c; let t = lo + y; c = (t - lo) - y; lo = t;
}
/* return sum values */
/*
if (lo === 0 && hi !== 0) { return hi; }
if (lo > 0 && hi > 0) { return hi; }
if (lo < 0 && hi < 0) { return hi; }
if (lo > 0 && hi < 0) { return lo + hi; }
if (lo < 0 && hi > 0) { return lo + hi; }
if (lo === 0 && hi === 0) { return lo; }
if (lo !== 0 && hi === 0) { return lo; }
*/
if ((lo === 0 && hi !== 0) || (lo > 0 && hi > 0) || (lo < 0 && hi < 0)) {
return hi;
}
if ((lo > 0 && hi < 0) || (lo < 0 && hi > 0)) { return lo + hi; }
return lo;
}
/* not number items -> TypeError */
throw new TypeError("values passed to Math.sumPrecise must be numbers");
};
}
/* Error.isError(); */
if (!("isError" in Error)) {
// @ts-ignore
Error.isError = function isError (value: unknown) {
let className =
Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
return (className === "error" || className === "domexception");
};
}
/* crypto.randomUUID(); */
if (("crypto" in globalThis) && !("randomUUID" in globalThis.crypto)) {
// @ts-ignore
globalThis.crypto.randomUUID = function randomUUID () {
// @ts-ignore
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,
(c: any): any =>
(c^crypto.getRandomValues(new Uint8Array(1))[0]&15 >> c/4).toString(16)
);
};
}
/* globalThis.GeneratorFunction; */
// @ts-ignore
if (!globalThis.GeneratorFunction) {
// @ts-ignore
globalThis.GeneratorFunction =
Object.getPrototypeOf(function*(){}).constructor;
}
/* globalThis.AsyncFunction; */
// @ts-ignore
if (!globalThis.AsyncFunction) {
// @ts-ignore
globalThis.AsyncFunction =
Object.getPrototypeOf(async function(){}).constructor;
}
/* globalThis.AsyncGeneratorFunction; */
// @ts-ignore
if (!globalThis.AsyncGeneratorFunction) {
// @ts-ignore
globalThis.AsyncGeneratorFunction =
Object.getPrototypeOf(async function* () {}).constructor;
}
/** Core API **/
/* Alphabet constans */
const BASE16 = "0123456789ABCDEF";
const BASE32 = "234567ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const WORDSAFEALPHABET = "23456789CFGHJMPQRVWXcfghjmpqvwx"; /* 31 characters */
/**
* @description Ensures that `condition` is truthy. Throws an `Error` if falsy.
*
* @param {unknown} condition The value to check.
* @param {unknown} [message] - Optional message or Error to throw.
* @throws {Error} If assertion is failed.
*/
function assert (condition: unknown, message?: unknown): asserts condition {
if (!condition) {
// @ts-ignore
if (Error.isError(message)) { throw message; }
let errorMessage =
`[assert] Assertion failed: ${condition} should be truly${message ? ` - ${message}` : ""}`;
throw new Error(errorMessage, {cause: errorMessage});
}
}
/**
* @description SameValueZero equality (like `Object.is`, but +0 === -0).
*
* @param {unknown} value1
* @param {unknown} value2
* @returns {boolean}
*/
const eq = (value1: unknown, value2: unknown): boolean =>
value1 === value2 || (value1 !== value1 && value2 !== value2);
/**
* @description Greater than.
*
* @param {any} value1
* @param {any} value2
* @returns {boolean}
*/
function gt (value1: Comparable, value2: Comparable): boolean {
const _typeOf = (value: unknown): string =>
value === null ? "null" : typeof value;
return _typeOf(value1) === _typeOf(value2) && value1 > value2;
}
/**
* @description Greater than or equal (SameValueZero).
*
* @param {any} value1
* @param {any} value2
* @returns {boolean}
*/
function gte (value1: Comparable, value2: Comparable): boolean {
const _typeOf = (value: unknown): string =>
value === null ? "null" : typeof value;
return _typeOf(value1) === _typeOf(value2)
&& (value1 > value2
|| value1 === value2
|| (value1 !== value1 && value2 !== value2));
}
/**
* @description Less than.
*
* @param {any} value1
* @param {any} value2
* @returns {boolean}
*/
function lt (value1: Comparable, value2: Comparable): boolean {
const _typeOf = (value: unknown): string =>
value === null ? "null" : typeof value;
return _typeOf(value1) === _typeOf(value2) && value1 < value2;
}
/**
* @description Less than or equal (SameValueZero).
*
* @param {any} value1
* @param {any} value2
* @returns {boolean}
*/
function lte (value1: Comparable, value2: Comparable): boolean {
const _typeOf = (value: unknown): string =>
value === null ? "null" : typeof value;
return _typeOf(value1) === _typeOf(value2)
&& (value1 < value2
|| value1 === value2
|| (value1 !== value1 && value2 !== value2));
}
/**
* @description Calls `callback` with the given `value` and then returns `value`.
*
* @param {Function} callback
* @returns {Function}
*/
function tap (callback: Function): any {
return function (value: unknown): any { callback(value); return value; };
}
/**
* @description Creates a function that is restricted to invoking `callback` once.
*
* @param {Function} callback
* @returns {Function}
*/
function once (callback: Function): Function {
let called: boolean = false;
let result: any;
return function (...args: any[]): any {
if (!called) {
called = true;
result = callback(...args);
}
return result;
};
}
/**
* @description Transforms a function of N arguments into N functions of one argument.
*
* @param {Function} callback
* @returns {Function}
*/
function curry (callback: Function): Function {
const curried = (...args: any[]): any =>
args.length >= callback.length
? callback(...args)
: (...rest: any[]): any => curried(...args, ...rest);
return curried;
}
/**
* @description Creates a function that is the composition of the provided functions.
*
* @param {Function} functions
* @returns {Function}
*/
const pipe = (...functions: Function[]): Function =>
(first: any): any =>
functions.reduce((value: unknown, callback: Function): any =>
callback(value), first);
/**
* @description Creates a function that is the composition of the provided functions.
*
* @param {Function} functions
* @returns {Function}
*/
const compose = (...functions: Function[]): Function =>
(first: any): any => functions.reduceRight(
(value, callback): any => callback(value),
first
);
/**
* @description Creates a new object composed of the picked `object` properties.
*
* @param {object} obj
* @param {string[]} keys
*/
const pick = (obj: MapLike, keys: string[]): MapLike =>
keys.reduce(function (acc: MapLike, key: string) {
if (key in obj) { acc[key] = obj[key]; }
return acc;
}, {});
/**
* @description Creates a new object composed of the `object` properties except for those omitted.
*
* @param {object} obj
* @param {string[]} keys
* @returns {object}
*/
const omit = (obj: MapLike, keys: string[]): MapLike =>
Object.keys(obj).reduce(function (acc: MapLike, key: string) {
// @ts-ignore
if (!keys.includes(key)) { acc[key] = obj[key]; }
return acc;
}, {});
/**
* @description Returns a new object with the specified key-value pair added or updated.
*
* @param {object} obj
* @param {string} key
* @param {object} value
*/
const assoc = (obj: MapLike, key: string, value: unknown): MapLike =>
({...obj, [key]: value});
/**
* @description An asynchronous no-operation function that returns a resolved Promise.
*
* @returns {Promise}
*/
// @ts-ignore
function asyncNoop (): Promise {
return new Promise(function (resolve: Function) { resolve(); });
}
/**
* @description Asynchronous function that returns a resolved Promise with `true`.
*
* @returns {Promise}
*/
async function asyncT (): Promise { return true; }
/**
* @description Asynchronous function that returns a resolved Promise with `false`.
*
* @returns {Promise}
*/
async function asyncF (): Promise { return false; }
/**
* @description Creates an asynchronous function that returns a resolved Promise with the specified value.
*
* @param {unknown} value
* @returns {Function}
*/
function asyncConstant (value: unknown): Function {
return async function() { return value; };
}
/**
* @description Asynchronous identity function that returns a resolved Promise with the given value.
*
* @param {unknown} value
* @returns {Promise}
*/
async function asyncIdentity (value: unknown): Promise { return value; }
/**
* @description Creates a polyfill method on an object if it does not already exist.
*
* @param {Object} obj - The object on which to create the method.
* @param {string} property - The name of the method to create.
* @param {Function} value - The function to assign as the method.
* @returns {boolean} - Returns true if the method was created or already exists with the same value.
*/
function createPolyfillMethod (
obj: Object,
property: string,
value: Function): boolean {
if (!(Object.hasOwn(obj, property))) {
Object.defineProperty(obj, property, {
writable: true, enumerable: false, configurable: true, value: value
});
}
// @ts-ignore
return (obj[property] === value);
}
/**
* @description Creates a polyfill property on an object if it does not already exist.
*
* @param {Object} obj - The object on which to create the property.
* @param {string} property - The name of the property to create.
* @param {unknown} value - The value to assign to the property.
* @returns {boolean} - Returns true if the property was created or already exists with the same value.
*/
function createPolyfillProperty (
obj: object,
property: string,
value: unknown): boolean {
if (!(Object.hasOwn(obj, property))) {
Object.defineProperty(obj, property, {
writable: true, enumerable: true, configurable: true, value: value
});
}
// @ts-ignore
return (obj[property] === value);
}
/**
* @description Generates a random UUID version 7 or UUID version 7 with version 4 ID.
*
* @param {boolean} [v4=false] - If true, generates a UUID version 4; otherwise, generates version 7.
* @returns {string} A randomly generated UUID string.
*/
function randomUUIDv7 (v4: boolean = false): string {
let ts = Date.now().toString(16).padStart(12,"0") + (v4 ? "4" : "7");
// @ts-ignore
let uuid = Array.from(([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,
(c: number): any =>
(c^crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
));
let index: number = 0;
let pos: number = 0;
while (index < 13) {
if (pos === 8 || pos === 13) { pos++; }
uuid[pos] = ts[index];
pos++;
index++;
}
return uuid.join("");
}
/**
* @description Returns a Promise that resolves after a specified delay in milliseconds.
*
* @param {number} milisec - The delay duration in milliseconds.
* @returns {Promise} A Promise that resolves after the specified delay.
*/
const delay = (milisec: number): Promise =>
new Promise(resolve => setTimeout(resolve, milisec));
/**
* @description Generates a random boolean value.
*
* @returns {boolean} A randomly generated boolean value.
*/
const randomBoolean = (): boolean => !Math.round(Math.random());
/**
* @description Parses URL query parameters into an object.
*
* @param {string} [str=location.search] - The URL query string to parse. Defaults to the current location's search string.
* @returns {Object} An object containing the parsed query parameters as key-value pairs.
*/
const getUrlVars = (str: string = location.search): Object =>
[...new URLSearchParams(str).entries()]
// @ts-ignore
.reduce(function (obj, item) { obj[item[0]] = item[1]; return obj; }, {});
/**
* @description Create a parsable string from an object.
*
* @param {object} obj
* @returns {string}
*/
const obj2string = (obj: any): string => Object.keys(obj).reduce(
(str, key): string =>
str += encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]) + "&",
""
).slice(0, -1);
/**
* @description Deep assign of an object (Object, Array, etc.)
*
* @returns any
*/
function extend (target: T, source: U): T & U;
function extend (target: T, s1: U, s2: V): T & U & V;
function extend (deep: true, target: T, ...sources: any[]): T;
function extend (deep: false, target: T, ...sources: any[]): T;
function extend (target: object, ...sources: any[]): object;
function extend (...args: any[]): any {
/* Arguments checking */
let deep: boolean = false;
let target: any;
let i = 0;
if (args[0] === true) {
deep = true;
target = args[1] || {};
i = 2;
} else {
target = args[0] || {};
i = 1;
}
/* Helper functions */
const _isPlainObject = (obj: any): obj is Record =>
obj != null
&& typeof obj === "object"
&& (obj.constructor === Object || obj.constructor == null);
function merge(target: any, source: any): any {
/* Identical or non-object -> direct assign */
if (Object.is(source, target) || source == null || typeof source !== "object") {
return source;
}
/* Date -> clone */
if (source instanceof Date) { return new Date(source.getTime()); }
/* RegExp -> clone */
if (source instanceof RegExp) { return new RegExp(source); }
/* Map -> deep merge entries */
if (source instanceof Map) {
if (target instanceof Map) { target = new Map(); }
for (let [key, value] of source) {
const tv = target.get(key);
target.set(key, deep ? merge(tv, value) : value);
}
return target;
}
/* Set -> deep union */
if (source instanceof Set) {
if (target instanceof Set) { target = new Set(); }
for (let item of source) {
if (deep) {
if (target.has(item)) { continue; }
}
target.add(item);
}
return target;
}
/* Array -> deep merge by index */
if (Array.isArray(source)) {
if (!Array.isArray(target)) { target = []; }
const srcLength = source.length;
for (let i = 0; i < srcLength; i++) {
let sv = source[i];
let tv = target[i];
target[i] = deep ? merge(tv, sv) : sv;
}
return target;
}
/* Plain object -> deep merge keys */
if (_isPlainObject(source)) {
if (!_isPlainObject(target)) { target = {}; }
for (let key in source) {
let sv = source[key];
let tv = target[key];
target[key] = deep ? merge(tv, sv) : sv;
}
return target;
}
/* Fallback: copy by reference */
return source;
}
/* Clone all sources */
const length = args.length;
for (; i < length; i++) { merge(target, args[i]); }
return target;
}
/**
* @description Returns the number of own properties (including symbols) of an object.
*
* @param {object} obj
* @returns {number}
*/
const sizeIn = (obj: object): number =>
Object.getOwnPropertyNames(obj).length
+ Object.getOwnPropertySymbols(obj).length;
/**
* @description Creates a function that invokes `callback` with its `this` binding removed.
*
* @param {Function} callback
* @returns {Function}
*/
const unBind = (callback: Function): Function =>
Function.prototype.call.bind(callback);
/**
* @description Creates a function that invokes `callback` with its `this` binding set to the provided context.
*
* @param {Function} callback
* @param {Function} context
*/
const bind = Function.prototype.call.bind(Function.prototype.bind);
/**
* @description Returns a function that always returns the same value.
*
* @param {unknown} value
* @returns {unknown}
*/
const constant = (value: T): (() => T) => () => value;
/**
* @description Returns value unchanged.
*
* @param {unknown} value
* @returns {unknown}
*/
const identity = (value: T): T => value;
/**
* @description A function that does nothing.
*
* @returns {void}
*/
function noop (): void {}
/**
* @description Always returns true.
*
* @returns {true}
*/
const T = (): boolean => true;
/**
* @description Always returns false.
*
* @returns {false}
*/
const F = (): boolean => false;
/**
* @description Generates a random string ID of specified size using the provided alphabet.
*
* @param {number} [size=21] - The length of the generated ID.
* @param {string} [alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"] - The set of characters to use for generating the ID.
*/
function nanoid (
size: number = 21,
alphabet: string =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
): string {
let result: string = "";
let dl: number = alphabet.length;
let pos: number;
let index: number = size;
while (index--) {
do { pos = crypto.getRandomValues(new Uint8Array(1))[0]; } while
(pos >= dl);
result += alphabet[pos];
}
return result;
}
/**
* @description Generates a timestamp-based string ID of specified size using the provided alphabet.
*
* @param {number} [size=21] - The total length of the generated ID, including the timestamp.
* @param {string} [alphabet="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"] - The set of characters to use for generating the ID.
* @returns {string} The generated timestamp-based ID.
*/
function timestampID (
size: number = 21,
alphabet: string =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
): string {
let result: string = Date.now().toString(36).padStart(10, "0") + "-";
let dl: number = alphabet.length;
let pos: number;
let index: number = ((size > 11) ? size : 12) - 11;
while (index--) {
do { pos = crypto.getRandomValues(new Uint8Array(1))[0]; } while
(pos >= dl);
result += alphabet[pos];
}
return result;
}
/** String API **/
/**
* @description Encodes a string to Base64 format.
*
* @param {any} str - The string to encode.
* @returns {string} The Base64 encoded string.
*/
function b64Encode (str: any): string {
return btoa(encodeURIComponent(String(str)).replace(/%([0-9A-F]{2})/g,
function toSolidBytes (_match, p1): string {
// @ts-ignore
return String.fromCharCode("0x" + p1);
}
));
}
/**
* @description Decodes a Base64 encoded string.
*
* @param {string} str - The Base64 encoded string to decode.
* @returns {string} The decoded string.
*/
function b64Decode (str: any): string {
return decodeURIComponent(atob(String(str)).split("").map(function (c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
}).join(""));
}
/**
* @description Counts the occurrences of a substring in a string.
*
* @param {unknown} str - The string to search in.
* @param {unknown} substr - The substring to search for.
* @returns {number} The number of occurrences of the substring.
*/
function strCount (str: unknown, substr: unknown): number {
let count = (String(str)?.split(String(substr)) ?? []).length - 1;
return count < 0 ? 0 : count;
}
/**
* @description Truncates a string to a specified length, optionally adding an omission string.
*
* @param {string} str - The string to truncate.
* @param {number} newLength - The maximum length of the truncated string.
* @param {string} [omission=""] - The string to append to the truncated string.
* @returns {string} The truncated string.
*/
function strTruncate (
str: any,
newLength: number,
omission: string = ""): string {
str = String(str);
omission = String(omission);
let strUC = Array.from(str);
if (newLength >= strUC.length) { return str; }
return strUC.slice(0, newLength-Array.from(omission).length).join("")
+ omission;
}
/**
* @description Converts the first character of each word in a string to uppercase and the rest to lowercase.
*
* @param {any} str - The string to convert.
* @returns {string} The converted string.
*/
const strPropercase = (str: any): string =>
String(str).trim().split(" ").map(function (value: string) {
let chars = Array.from(value).map( (c: string): string => c.toLowerCase() );
if (chars.length) { chars[0] = chars[0].toUpperCase(); }
return chars.join("");
}).join(" ");
/**
* @description Converts the first character of each word in a string to uppercase and the rest to lowercase.
*
* @param {any} str - The string to convert.
* @returns {string} The converted string.
*/
const strTitlecase = (str: any): string =>
String(str).trim().split(" ").map(function (value: string) {
let chars = Array.from(value).map( (c: string): string => c.toLowerCase() );
if (chars.length) { chars[0] = chars[0].toUpperCase(); }
return chars.join("");
}).join(" ");
/**
* @description Capitalizes the first character of a string and converts the rest to lowercase.
*
* @param {any} str - The string to capitalize.
* @returns {string} The capitalized string.
*/
function strCapitalize (str: any): string {
let chars = [...String(str).trim().toLowerCase()];
if (chars.length) { chars[0] = chars[0].toUpperCase(); }
return chars.join("");
}
/**
* @description Converts the first character of a string to uppercase.
*
* @param {any} str - The string to modify.
* @returns {string} The modified string.
*/
function strUpFirst (str: any): string {
let chars = [...String(str).trim()];
if (chars.length) { chars[0] = chars[0].toUpperCase(); }
return chars.join("");
}
/**
* @description Converts the first character of a string to lowercase.
*
* @param {any} str - The string to modify.
* @returns {string} The modified string.
*/
function strDownFirst (str: any): string {
let chars = [...String(str).trim()];
if (chars.length) { chars[0] = chars[0].toLowerCase(); }
return chars.join("");
}
/**
* @description Reverses the characters in a string.
*
* @param {any} str - The string to reverse.
* @returns {string} The reversed string.
*/
const strReverse = (str: unknown): string =>
Array.from(String(str)).reverse().join("");
/**
* @description Returns an array of Unicode code points for each character in a string.
*
* @param {any} str - The string to process.
* @returns {number[]} An array of Unicode code points.
*/
const strCodePoints = (str: any): any[] =>
Array.from(String(str), (value: string): number | undefined =>
value.codePointAt(0));
/**
* @description Creates a string from an array or iterable of Unicode code points.
*
* @param {Iterable} iterator - An array or iterable of Unicode code points.
* @returns {string} The constructed string.
*/
const strFromCodePoints = ([...array]: Iterable): string =>
String.fromCodePoint(...array);
/**
* @description Gets or sets a unicode character at a specified index in a string.
*
* @param {string} str - The string to modify.
* @param {number} index - The index of the character to get or set.
*/
function strAt (str: string, index: number, newChar?: string): string {
let chars: string[] = Array.from(String(str));
if (newChar == null) { return chars.at(index) || ""; }
index = index < 0 ? chars.length + index : index;
if (index > chars.length) { return chars.join(""); }
chars[index] = newChar;
return chars.join("");
}
/**
* @description Splices a string by removing a specified number of characters at a given index and optionally adding new characters.
*
* @param {string} str - The string to splice.
* @param {number} index - The index at which to start splicing.
* @param {number} count - The number of characters to remove.
* @param {string} [add] - The string to add at the splice index.
* @returns {string} The spliced string.
*/
const strSplice = (str: string, index: number,count: number, ...add: any[]): string =>
Array.from(str).toSpliced(index, count, add.join("")).join("");
/**
* @description Removes HTML tags from a string.
*
* @param {any} str - The string from which to remove HTML tags.
* @returns {string} The string without HTML tags.
*/
const strHTMLRemoveTags = (str: any): string =>
String(str).trim().replace(/<[^>]*>/g, " ").replace(/\s{2,}/g, " ").trim();
/**
* @description Escapes special HTML characters in a string.
*
* @param {any} str - The string to escape.
* @returns {string} The escaped string.
*/
const strHTMLEscape = (str: any): string =>
String(str).trim()
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
/**
* @description Unescapes special HTML characters in a string.
*
* @param {any} str - The string to unescape.
* @returns {string} The unescaped string.
*/
const strHTMLUnEscape = (str: string): string =>
String(str).trim()
.replace(/&/g, "&").replace(/&/g, "&")
.replace(/</g, "<").replace(/</g, "<")
.replace(/>/g, ">").replace(/>/g, ">")
.replace(/"/g, '"').replace(/"/g, '"')
.replace(/'/g, "'").replace(/'/g, "'");
/** DOM API **/
/**
* @description Selects all elements matching the specified CSS selector within the given context.
*
* @param {string} str - The CSS selector to match.
* @param {Document | HTMLElement} [context=document] - The context in which to search for elements.
* @returns {any[]} An array of matching elements.
*/
const qsa = (str: string, context: Document | HTMLElement = document): any[] =>
Array.from(context.querySelectorAll(str));
/**
* @description Selects the first element matching the specified CSS selector within the given context.
*
* @param {string} str - The CSS selector to match.
* @param {Document | HTMLElement} [context=document] - The context in which to search for the element.
* @returns {HTMLElement | null} The first matching element, or null if no match is found.
*/
const qs = (str: string, context: Document | Element = document): HTMLElement | null =>
context.querySelector(str);
/**
* @description Executes a callback function when the DOM is fully loaded.
*
* @param {Function} callback - The callback function to execute.
* @returns {void}
*/
function domReady (callback: Function): void {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", function (_event) { callback(); });
}
}
/* domCreate(type: string[, properties: object[, innerHTML: string]]):
element */
/* domCreate(element descriptive object): element */
/**
* @description Creates a new DOM element with specified properties and inner HTML.
*
* @param {string | object} elementType - The type of element to create or an object describing the element.
* @param {object} [properties] - An object containing properties to set on the element.
* @param {string} [innerHTML] - The inner HTML content to set for the element.
* @returns {HTMLElement} The newly created DOM element.
*/
function domCreate (
elementType: string | MapLike,
properties: object,
innerHTML: string): HTMLElement {
if (arguments.length === 1 && typeof elementType === "object") {
let obj = elementType;
elementType = obj.elementType;
properties = {};
for (let key in obj) {
// @ts-ignore
if (key !== "elementType") { properties[key] = obj[key]; }
}
}
let element: HTMLElement = document.createElement(elementType as string);
if (properties) {
for (let key in properties) {
// @ts-ignore
if (key !== "style" || typeof properties[key] === "string") {
// @ts-ignore
element[key] = properties[key];
} else {
// @ts-ignore
Object.assign(element.style, properties[key]);
}
}
}
if (innerHTML) { element.innerHTML = innerHTML; }
return element;
}
/**
* @description Converts an HTML string to a DOM element.
*
* @param {string} str - The HTML string to convert.
* @returns {Element | null} The resulting DOM element, or null if conversion fails.
*/
function domToElement (str: string): Element | null {
let element: HTMLElement = document.createElement("div");
element.innerHTML = str;
return element.firstElementChild;
}
/**
* @description Gets the computed CSS property value of a DOM element.
*
* @param {Element} element - The DOM element to retrieve the CSS property from.
* @param {string | number} [property] - The CSS property name to retrieve. If omitted, returns the full CSSStyleDeclaration.
* @returns {string | CSSStyleDeclaration} The computed CSS property value or the full CSSStyleDeclaration.
*/
const domGetCSS = (element: Element, property: string | number): string | CSSStyleDeclaration =>
// @ts-ignore
(property ? globalThis.getComputedStyle(element, null)[property] :
globalThis.getComputedStyle(element, null));
/* domSetCSS(element, property: string, value: string): undefined */
/* domSetCSS(element, properties: object): undefined */
/**
* @description Sets CSS property values on a DOM element.
*
* @param {HTMLElement} element - The DOM element to set the CSS properties on.
* @param {string | object} property - The CSS property name to set or an object containing multiple properties and their values.
* @param {string} [value] - The value to set for the specified CSS property (if `property` is a string).
* @returns {void}
*/
function domSetCSS (
element: HTMLElement,
property: string | object,
value: string): void {
if (typeof property === "string") {
// @ts-ignore
element.style[property] = value;
} else if (typeof property === "object") {
Object.keys(property).forEach((key: string): void =>
// @ts-ignore
element.style[key] = property[key]
);
}
}
/**
* @description Fades in a DOM element over a specified duration.
*
* @param {HTMLElement} element - The DOM element to fade in.
* @param {number} [duration=500] - The duration of the fade-in effect in milliseconds.
* @param {string} [display=""] - The CSS display value to set when the element is shown.
* @returns {void}
*/
function domFadeIn (
element: HTMLElement,
duration: number,
display: string): void {
let s = element.style;
let step: number = 25/(duration || 500);
s.opacity = (s.opacity ?? 0);
s.display = (display || "");
(function fade () {
// @ts-ignore
(s.opacity=parseFloat(s.opacity)+step)>1 ? s.opacity=1 :setTimeout(fade,25);
})();
}
/**
* @description Fades out a DOM element over a specified duration.
*
* @param {HTMLElement} element - The DOM element to fade out.
* @param {number} [duration=500] - The duration of the fade-out effect in milliseconds.
* @returns {void}
*/
function domFadeOut (
element: HTMLElement, duration: number): void {
let style = element.style;
let step: number = 25/(duration || 500);
// @ts-ignore
style.opacity = (style.opacity || 1);
(function fade () {
// @ts-ignore
(style.opacity -= step) < 0 ? style.display = "none" : setTimeout(fade, 25);
})();
}
/**
* @description Toggles the fade in/out effect of a DOM element over a specified duration.
*
* @param {HTMLElement} element - The DOM element to toggle fade effect on.
* @param {number} [duration=500] - The duration of the fade effect in milliseconds.
* @param {string} [display=""] - The CSS display value to set when the element is shown.
* @returns {void}
*/
function domFadeToggle (
element: HTMLElement, duration: number, display: string = ""): void {
if (globalThis.getComputedStyle(element, null).display === "none") {
/* same as domFadeIn(); */
let style = element.style;
let step: number = 25/(duration || 500);
style.opacity = (style.opacity ?? 0);
style.display = (display || "");
(function fade () {
// @ts-ignore
(style.opacity = parseFloat(style.opacity) + step) > 1 ? style.opacity = 1 :
setTimeout(fade, 25);
})();
} else {
/* same as domFadeOut(); */
let style = element.style;
let step: number = 25/(duration || 500);
style.opacity = (style.opacity ?? 1);
(function fade () {
// @ts-ignore
(style.opacity -= step) < 0 ? style.display = "none" : setTimeout(fade, 25);
})();
}
}
/**
* @description Hides a DOM element by setting its display style to "none".
*
* @param {HTMLElement} element - The DOM element to hide.
* @returns {void}
*/
const domHide = (element: HTMLElement): any => element.style.display = "none";
/**
* @description Shows a DOM element by setting its display style.
*
* @param {HTMLElement} element - The DOM element to show.
* @param {string} [display=""] - The CSS display value to set when showing the element.
* @returns {void}
*/
const domShow = (element: HTMLElement, display: string = ""): any =>
element.style.display = display;
/**
* @description Toggles the visibility of a DOM element by changing its display style.
*
* @param {HTMLElement} element - The DOM element to toggle.
* @param {string} [display=""] - The CSS display value to set when showing the element.
* @returns {void}
*/
function domToggle (element: HTMLElement, display: string = ""): void {
if (globalThis.getComputedStyle(element, null).display === "none") {
element.style.display = display;
} else {
element.style.display = "none";
}
}
/**
* @description Checks if a DOM element is hidden (i.e., has display style set to "none").
*
* @param {Element} element - The DOM element to check.
* @returns {boolean} True if the element is hidden, false otherwise.
*/
const domIsHidden = (element: Element): boolean =>
(globalThis.getComputedStyle(element,null).display === "none");
/**
* @description Retrieves all sibling elements of a given DOM element.
*
* @param {Element} element - The DOM element whose siblings are to be retrieved.
* @returns {Element[]} An array of sibling elements.
*/
const domSiblings = (element: Element): Element[] =>
// @ts-ignore
Array.prototype.filter.call(element.parentNode.children,
(item: Element): boolean => (item !== element)
);
/**
* @description Retrieves all sibling elements before a given DOM element.
*
* @param {Element} element - The DOM element whose previous siblings are to be retrieved.
* @returns {Element[]} An array of previous sibling elements.
*/
const domSiblingsPrev = (element: Element): Element[] =>
Array.prototype.slice.call(
// @ts-ignore
element.parentNode.children,
0,
// @ts-ignore
Array.prototype.indexOf.call(element.parentNode.children, element)
);
/**
* @description Retrieves all sibling elements to the left of a given DOM element.
*
* @param {Element} element - The DOM element whose left siblings are to be retrieved.
* @returns {Element[]} An array of left sibling elements.
*/
const domSiblingsLeft = (element: Element): Element[] =>
Array.prototype.slice.call(
// @ts-ignore
element.parentNode.children,
0,
// @ts-ignore
Array.prototype.indexOf.call(element.parentNode.children, element)
);
/**
* @description Retrieves all sibling elements after a given DOM element.
*
* @param {Element} element - The DOM element whose next siblings are to be retrieved.
* @returns {Element[]} An array of next sibling elements.
*/
const domSiblingsNext = (element: Element): Element[] =>
Array.prototype.slice.call(
// @ts-ignore
element.parentNode.children,
// @ts-ignore
Array.prototype.indexOf.call(element.parentNode.children, element) + 1,
// @ts-ignore
element.parentNode.children.length
);
/**
* @description Retrieves all sibling elements to the right of a given DOM element.
*
* @param {Element} element - The DOM element whose right siblings are to be retrieved.
* @returns {Element[]} An array of right sibling elements.
*/
const domSiblingsRight = (element: HTMLElement): Element[] =>
Array.prototype.slice.call(
// @ts-ignore
element.parentNode.children,
// @ts-ignore
Array.prototype.indexOf.call(element.parentNode.children, element) + 1,
// @ts-ignore
element.parentNode.children.length
);
/**
* @description Dynamically imports one or more JavaScript files into the document.
*
* @param {string[]} scripts - The URLs of the JavaScript files to import.
* @returns {void}
*/
function importScript (...scripts: string[]): void {
for (let item of scripts) {
let element: HTMLScriptElement = document.createElement("script");
element.type = "text\/javascript";
element.src = item;
// @ts-ignore
element.onerror = function (error: Error): void {
throw new URIError(
// @ts-ignore
`Loading failed for the script with source ${error.target.src}`
);
};
(document.head||document.getElementsByTagName("head")[0])
.appendChild(element);
}
}
/**
* @description Dynamically imports one or more CSS stylesheets into the document.
*
* @param {string[]} styles - The URLs of the CSS stylesheets to import.
* @returns {void}
*/
function importStyle (...styles: string[]): void {
for (let item of styles) {
let element: HTMLLinkElement = document.createElement("link");
element.rel = "stylesheet";
element.type = "text\/css";
element.href = item;
element.onerror = function (error) {
throw new URIError(
// @ts-ignore
`Loading failed for the style with source ${error.target.href}`
);
};
(document.head ||document.getElementsByTagName("head")[0])
.appendChild(element);
}
}
/**
* @description Converts a form element into an array of key-value pairs.
*
* @param {HTMLFormElement} form - The form element to convert.
* @returns {object[]} An array of objects representing the form data.
*/
function form2array (form: HTMLFormElement): object[] {
let field: any;
let result = Array