@lisn.js/bundles
Version: 
LISN.js browser bundles.
1,384 lines (1,321 loc) • 546 kB
JavaScript
/*!
 * LISN.js v1.2.1
 * (c) 2025 @AaylaSecura
 * Released under the MIT License.
 */
var LISN = (function (exports) {
  'use strict';
  /**
   * For minification optimization.
   *
   * @module
   * @ignore
   * @internal
   */
  const PREFIX = "lisn";
  const LOG_PREFIX = "[LISN.js]";
  const OBJECT = Object;
  const SYMBOL = Symbol;
  const ARRAY = Array;
  const STRING = String;
  const MATH = Math;
  const NUMBER = Number;
  const PROMISE = Promise;
  const PI = MATH.PI;
  const INFINITY = Infinity;
  const S_ABSOLUTE = "absolute";
  const S_FIXED = "fixed";
  const S_WIDTH = "width";
  const S_HEIGHT = "height";
  const S_TOP = "top";
  const S_BOTTOM = "bottom";
  const S_UP = "up";
  const S_DOWN = "down";
  const S_LEFT = "left";
  const S_RIGHT = "right";
  const S_AT = "at";
  const S_ABOVE = "above";
  const S_BELOW = "below";
  const S_IN = "in";
  const S_OUT = "out";
  const S_NONE = "none";
  const S_AMBIGUOUS = "ambiguous";
  const S_ADDED = "added";
  const S_REMOVED = "removed";
  const S_ATTRIBUTE = "attribute";
  const S_KEY = "key";
  const S_MOUSE = "mouse";
  const S_POINTER = "pointer";
  const S_TOUCH = "touch";
  const S_WHEEL = "wheel";
  const S_CLICK = "click";
  const S_HOVER = "hover";
  const S_PRESS = "press";
  const S_SCROLL = "scroll";
  const S_ZOOM = "zoom";
  const S_DRAG = "drag";
  const S_UNKNOWN = "unknown";
  const S_SCROLL_TOP = `${S_SCROLL}Top`;
  const S_SCROLL_LEFT = `${S_SCROLL}Left`;
  const S_SCROLL_WIDTH = `${S_SCROLL}Width`;
  const S_SCROLL_HEIGHT = `${S_SCROLL}Height`;
  const S_CLIENT_WIDTH = "clientWidth";
  const S_CLIENT_HEIGHT = "clientHeight";
  const S_SCROLL_TOP_FRACTION = `${S_SCROLL}TopFraction`;
  const S_SCROLL_LEFT_FRACTION = `${S_SCROLL}LeftFraction`;
  const S_HORIZONTAL = "horizontal";
  const S_VERTICAL = "vertical";
  const S_SKIP_INITIAL = "skipInitial";
  const S_DEBOUNCE_WINDOW = "debounceWindow";
  const S_TOGGLE = "toggle";
  const S_CANCEL = "cancel";
  const S_KEYDOWN = `${S_KEY}${S_DOWN}`;
  const S_MOUSEUP = `${S_MOUSE}${S_UP}`;
  const S_MOUSEDOWN = `${S_MOUSE}${S_DOWN}`;
  const S_POINTERUP = `${S_POINTER}${S_UP}`;
  const S_POINTERDOWN = `${S_POINTER}${S_DOWN}`;
  const S_POINTERENTER = `${S_POINTER}enter`;
  const S_POINTERLEAVE = `${S_POINTER}leave`;
  const S_POINTERMOVE = `${S_POINTER}move`;
  const S_POINTERCANCEL = `${S_POINTER}${S_CANCEL}`;
  const S_TOUCHSTART = `${S_TOUCH}start`;
  const S_TOUCHEND = `${S_TOUCH}end`;
  const S_TOUCHMOVE = `${S_TOUCH}move`;
  const S_TOUCHCANCEL = `${S_TOUCH}${S_CANCEL}`;
  const S_SELECTSTART = "selectstart";
  const S_ATTRIBUTES = "attributes";
  const S_CHILD_LIST = "childList";
  const S_REVERSE = "reverse";
  const S_DISABLED = "disabled";
  const S_ARROW = "arrow";
  const S_ROLE = "role";
  const S_AUTO = "auto";
  const S_VISIBLE = "visible";
  const ARIA_PREFIX = "aria-";
  const S_ARIA_CONTROLS = ARIA_PREFIX + "controls";
  const PREFIX_RELATIVE = `${PREFIX}-relative`;
  const PREFIX_WRAPPER$2 = `${PREFIX}-wrapper`;
  const PREFIX_WRAPPER_CONTENT = `${PREFIX_WRAPPER$2}-content`;
  const PREFIX_TRANSITION = `${PREFIX}-transition`;
  const PREFIX_TRANSITION_DISABLE = `${PREFIX_TRANSITION}__disable`;
  const PREFIX_HIDE = `${PREFIX}-hide`;
  const PREFIX_SHOW = `${PREFIX}-show`;
  const PREFIX_DISPLAY = `${PREFIX}-display`;
  const PREFIX_UNDISPLAY = `${PREFIX}-undisplay`;
  const PREFIX_ORIENTATION = `${PREFIX}-orientation`;
  const PREFIX_GHOST = `${PREFIX}-ghost`;
  const PREFIX_NO_SELECT = `${PREFIX}-no-select`;
  const PREFIX_NO_TOUCH_ACTION = `${PREFIX}-no-touch-action`;
  const PREFIX_NO_WRAP = `${PREFIX}-no-wrap`;
  const S_ANIMATE = "animate";
  const ANIMATE_PREFIX = `${PREFIX}-${S_ANIMATE}__`;
  const PREFIX_ANIMATE_DISABLE = `${ANIMATE_PREFIX}disable`;
  const PREFIX_ANIMATE_PAUSE = `${ANIMATE_PREFIX}pause`;
  const PREFIX_ANIMATE_REVERSE = `${ANIMATE_PREFIX}${S_REVERSE}`;
  /**
   * @module Errors
   */
  /**
   * Base error type emitted by LISN.
   */
  class LisnError extends Error {}
  /**
   * Error type emitted for invalid input or incorrect usage of a function.
   */
  class LisnUsageError extends LisnError {
    constructor(message = "") {
      super(`${LOG_PREFIX} Incorrect usage: ${message}`);
      this.name = "LisnUsageError";
    }
  }
  /**
   * Error type emitted if an assertion is wrong => report bug.
   */
  class LisnBugError extends LisnError {
    constructor(message = "") {
      super(`${LOG_PREFIX} Please report a bug: ${message}`);
      this.name = "LisnBugError";
    }
  }
  /**
   * For minification optimization
   *
   * @module
   * @ignore
   * @internal
   */
  // credit: underscore.js
  const root = typeof self === "object" && self.self === self && self || typeof global == "object" && global.global === global && global || Function("return this")() || {};
  const kebabToCamelCase$1 = str => str.replace(/-./g, m => toUpperCase(m.charAt(1)));
  const camelToKebabCase$1 = str => str.replace(/[A-Z][a-z]/g, m => "-" + toLowerCase(m)).replace(/[A-Z]+/, m => "-" + toLowerCase(m));
  const prefixName = name => `${PREFIX}-${name}`;
  const prefixCssVar = name => "--" + prefixName(name);
  const prefixCssJsVar = name => prefixCssVar("js--" + name);
  const toDataAttrName = name => `data-${camelToKebabCase$1(name)}`;
  const toLowerCase = s => s.toLowerCase();
  const toUpperCase = s => s.toUpperCase();
  const timeNow = Date.now.bind(Date);
  const timeSince = startTime => timeNow() - startTime;
  const hasDOM = () => typeof document !== "undefined";
  const getWindow = () => window;
  const getDoc = () => document;
  const getDocElement = () => getDoc().documentElement;
  const getDocScrollingElement = () => getDoc().scrollingElement;
  const getBody = () => getDoc().body;
  const getReadyState = () => getDoc().readyState;
  const getPointerType = event => isPointerEvent(event) ? event.pointerType : isMouseEvent(event) ? "mouse" : null;
  const onAnimationFrame = callback => requestAnimationFrame(callback);
  const createElement = (tagName, options) => getDoc().createElement(tagName, options);
  const createButton = (label = "", tag = "button") => {
    const btn = createElement(tag);
    setTabIndex(btn);
    setAttr(btn, S_ROLE, "button");
    setAttr(btn, ARIA_PREFIX + "label", label);
    return btn;
  };
  const isNullish = v => v === undefined || v === null;
  const isEmpty = v => isNullish(v) || v === "";
  const isOfType = (v, tag, checkLevelsUp = 0) => isInstanceOfByClassName(v, tag) || OBJECT.prototype.toString.call(v) === `[object ${tag}]` || checkLevelsUp > 0 && isOfType(getPrototypeOf(v), tag, checkLevelsUp - 1);
  // Not including function
  const isObject = v => v !== null && typeof v === "object";
  const isPlainObject = v => isObject(v) && (getPrototypeOf(v) === null || getPrototypeOf(getPrototypeOf(v)) === null);
  const isIterableObject = v => isObject(v) && SYMBOL.iterator in v;
  const isArray = ARRAY.isArray.bind(ARRAY);
  const isPrimitive = v => isLiteralString(v) || isSymbol(v) || isLiteralNumber(v) || isBoolean(v) || isNullish(v);
  // only primitive number
  const isLiteralNumber = v => typeof v === "number";
  const isNumber = v => isOfType(v, "Number");
  const isString = v => isOfType(v, "String");
  const isLiteralString = v => typeof v === "string";
  const isSymbol = v => typeof v === "symbol";
  const isBoolean = v => typeof v === "boolean";
  const isFunction = v => typeof v === "function" || isOfType(v, "Function");
  const isMap = v => isOfType(v, "Map");
  const isSet = v => isOfType(v, "Set");
  const isMouseEvent = event => isOfType(event, "MouseEvent");
  const isPointerEvent = event => isOfType(event, "PointerEvent");
  const isTouchPointerEvent = event => isPointerEvent(event) && getPointerType(event) === S_TOUCH;
  const isWheelEvent = event => isOfType(event, "WheelEvent");
  const isKeyboardEvent = event => isOfType(event, "KeyboardEvent");
  const isTouchEvent = event => isOfType(event, "TouchEvent");
  const isDoc = target => isOfType(target, "HTMLDocument");
  const isNode = target => typeof Node === "function" && isInstanceOf(target, Node) || target != null && typeof target.nodeType === "number" && typeof target.nodeName === "string";
  const isElement = target => isNode(target) && target.nodeType === 1;
  const isStyledElement = (target, namespace) => isElement(target) && isObject(target.style) && (!namespace || target.namespaceURI === namespace);
  const isHTMLElement = target => typeof HTMLElement === "function" && isInstanceOf(target, HTMLElement) || isStyledElement(target, HTML_NS);
  const isHTMLInputElement = target => typeof HTMLInputElement === "function" && isInstanceOf(target, HTMLInputElement) || isHTMLElement(target) && hasTagName(target, "input");
  const isAnimation = value => typeof Animation === "function" && isInstanceOf(value, Animation) || isOfType(value, "Animation", 2);
  const isCSSAnimation = value => typeof CSSAnimation === "function" && isInstanceOf(value, CSSAnimation) || isOfType(value, "CSSAnimation", 2);
  const isKeyframeEffect = value => typeof KeyframeEffect === "function" && isInstanceOf(value, KeyframeEffect) || isOfType(value, "KeyframeEffect");
  const isNodeBAfterA = (nodeA, nodeB) => (nodeA.compareDocumentPosition(nodeB) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0;
  const strReplace = (s, match, replacement) => s.replace(match, replacement);
  const setTimer = root.setTimeout.bind(root);
  const clearTimer = root.clearTimeout.bind(root);
  const closestParent = (element, selector) => element.closest(selector);
  const getBoundingClientRect = element => element.getBoundingClientRect();
  // Copy size properties explicitly to another object so they can be used with
  // the spread operator (DOMRect/DOMRectReadOnly's properties are not enumerable)
  const copyBoundingRectProps = rect => {
    return {
      x: rect.x,
      left: rect.left,
      right: rect.right,
      [S_WIDTH]: rect[S_WIDTH],
      y: rect.y,
      top: rect.top,
      bottom: rect.bottom,
      [S_HEIGHT]: rect[S_HEIGHT]
    };
  };
  const querySelector = (root, selector) => root.querySelector(selector);
  const querySelectorAll = (root, selector) => root.querySelectorAll(selector);
  const docQuerySelector = selector => querySelector(getDoc(), selector);
  const docQuerySelectorAll = selector => querySelectorAll(getDoc(), selector);
  const getElementById = id => getDoc().getElementById(id);
  const getAttr = (element, name) => element.getAttribute(name);
  const setAttr = (element, name, value = "true") => element.setAttribute(name, value);
  const unsetAttr = (element, name) => element.setAttribute(name, "false");
  const delAttr = (element, name) => element.removeAttribute(name);
  const includes = (arr, v, startAt) => arr.indexOf(v, startAt) >= 0;
  const some = (array, predicate) => array.some(predicate);
  const filter = (array, filterFn) => array.filter(filterFn);
  const filterBlank = array => {
    const result = array ? filter(array, v => !isEmpty(v)) : undefined;
    return lengthOf(result) ? result : undefined;
  };
  const sizeOf = obj => {
    var _obj$size;
    return (_obj$size = obj === null || obj === void 0 ? void 0 : obj.size) !== null && _obj$size !== void 0 ? _obj$size : 0;
  };
  const lengthOf = obj => {
    var _obj$length;
    return (_obj$length = obj === null || obj === void 0 ? void 0 : obj.length) !== null && _obj$length !== void 0 ? _obj$length : 0;
  };
  const lastOf = a => a === null || a === void 0 ? void 0 : a.slice(-1)[0];
  const tagName = element => element.tagName;
  // case-insensitive
  const hasTagName = (element, tag) => toUpperCase(tagName(element)) === toUpperCase(tag);
  const preventDefault = event => event.preventDefault();
  const arrayFrom = ARRAY.from.bind(ARRAY);
  const keysOf = obj => OBJECT.keys(obj);
  // use it in place of object spread
  const merge = (...a) => {
    return assign({}, ...a);
  };
  // implementation (wide) — callers see the overloads, not this signature
  function copyObject(obj) {
    return merge(obj);
  }
  const promiseResolve = PROMISE.resolve.bind(PROMISE);
  const promiseAll = PROMISE.all.bind(PROMISE);
  const assign = OBJECT.assign.bind(OBJECT);
  OBJECT.freeze.bind(OBJECT);
  const hasOwnProp = (o, prop) => OBJECT.prototype.hasOwnProperty.call(o, prop);
  const defineProperty = OBJECT.defineProperty.bind(OBJECT);
  const getPrototypeOf = OBJECT.getPrototypeOf.bind(OBJECT);
  const preventExtensions = OBJECT.preventExtensions.bind(OBJECT);
  const stringify = JSON.stringify.bind(JSON);
  const floor = MATH.floor.bind(MATH);
  const ceil = MATH.ceil.bind(MATH);
  const log2 = MATH.log2.bind(MATH);
  const sqrt = MATH.sqrt.bind(MATH);
  const max = MATH.max.bind(MATH);
  const min = MATH.min.bind(MATH);
  const abs = MATH.abs.bind(MATH);
  const round = MATH.round.bind(MATH);
  const pow = MATH.pow.bind(MATH);
  const exp = MATH.exp.bind(MATH);
  MATH.cos.bind(MATH);
  MATH.sin.bind(MATH);
  MATH.tan.bind(MATH);
  const parseFloat = NUMBER.parseFloat.bind(NUMBER);
  NUMBER.isNaN.bind(NUMBER);
  const isInstanceOf = (value, classOrName, checkLisnBrand = false) => isLiteralString(classOrName) ? isInstanceOfByClassName(value, classOrName) : isInstanceOfByClass(value, classOrName, checkLisnBrand);
  const constructorOf = obj => obj.constructor;
  const typeOf = obj => typeof obj;
  const typeOrClassOf = obj => {
    var _constructorOf;
    return isObject(obj) ? (_constructorOf = constructorOf(obj)) === null || _constructorOf === void 0 ? void 0 : _constructorOf.name : typeOf(obj);
  };
  const parentOf = element => {
    var _element$parentElemen;
    return (_element$parentElemen = element === null || element === void 0 ? void 0 : element.parentElement) !== null && _element$parentElemen !== void 0 ? _element$parentElemen : null;
  };
  const childrenOf = element => (element === null || element === void 0 ? void 0 : element.children) || [];
  function targetOf(obj) {
    return obj && "target" in obj ? obj.target : undefined;
  }
  function currentTargetOf(obj) {
    return obj && "currentTarget" in obj ? obj.currentTarget : undefined;
  }
  function classList(element) {
    return element === null || element === void 0 ? void 0 : element.classList;
  }
  const S_TABINDEX = "tabindex";
  const getTabIndex = element => getAttr(element, S_TABINDEX);
  const setTabIndex = (element, index = "0") => setAttr(element, S_TABINDEX, index);
  const unsetTabIndex = element => delAttr(element, S_TABINDEX);
  const remove = (obj, ...args) => obj === null || obj === void 0 ? void 0 : obj.remove(...args);
  const deleteObjKey = (obj, key) => delete obj[key];
  const deleteKey = (map, key) => map === null || map === void 0 ? void 0 : map.delete(key);
  const elScrollTo = (element, coords, behavior = "instant") => element.scrollTo(merge({
    behavior
  }, coords));
  const newPromise = executor => new Promise(executor);
  const newMap = entries => new Map(entries);
  const newWeakMap = entries => new WeakMap(entries);
  const newSet = values => new Set(values);
  const newWeakSet = values => new WeakSet(values);
  const newIntersectionObserver = (callback, options) => new IntersectionObserver(callback, options);
  const newResizeObserver = callback => typeof ResizeObserver === "undefined" ? null : new ResizeObserver(callback);
  const newMutationObserver = callback => new MutationObserver(callback);
  const usageError = msg => new LisnUsageError(msg);
  const bugError = msg => new LisnBugError(msg);
  const illegalConstructorError = useWhat => usageError(`Illegal constructor. Use ${useWhat}.`);
  const CONSOLE = console;
  CONSOLE.debug.bind(CONSOLE);
  CONSOLE.log.bind(CONSOLE);
  CONSOLE.info.bind(CONSOLE);
  const consoleWarn = CONSOLE.warn.bind(CONSOLE);
  const consoleError = CONSOLE.error.bind(CONSOLE);
  // --------------------
  const LISN_BRAND_PROP = SYMBOL.for("__lisn.js:brand");
  const HTML_NS = "http://www.w3.org/1999/xhtml";
  const isInstanceOfByClass = (value, Class, checkLisnBrand = false) => {
    if (!isFunction(Class) || !isObject(value)) {
      return false;
    }
    if (value instanceof Class) {
      return true;
    }
    return checkLisnBrand ? isInstanceOfLisnClass(value, Class) : false;
  };
  const isInstanceOfLisnClass = (value, Class) => {
    if (!isFunction(Class) || !isObject(value)) {
      return false;
    }
    const proto = Class.prototype;
    const clsBrandSym = isObject(proto) ? proto[LISN_BRAND_PROP] : undefined;
    if (!clsBrandSym) {
      return false;
    }
    const valBrandSym = value[LISN_BRAND_PROP];
    return valBrandSym == clsBrandSym || isInstanceOfLisnClass(getPrototypeOf(value), Class);
  };
  const isInstanceOfByClassName = (value, className) => isInstanceOfByClass(value, globalThis[className]);
  /**
   * @module Settings
   */
  // TODO add defaults for watchers' debounce window, thresholds
  /**
   * LISN's settings.
   * @readonly
   *
   * If you wish to modify them, then you need to do so immediately after loading
   * LISN before you instantiate any watchers, etc. For example:
   *
   * ```html
   * <!doctype html>
   * <html>
   *   <head>
   *     <meta charset="UTF-8" />
   *     <meta name="viewport" content="width=device-width" />
   *     <script src="lisn.js" charset="utf-8"></script>
   *     <script charset="utf-8">
   *       // modify LISN settings, for example:
   *       LISN.settings.deviceBreakpoints.desktop = 1024;
   *     </script>
   *   </head>
   *   <body>
   *   </body>
   * </html>
   * ```
   */
  const settings = preventExtensions({
    /**
     * A unique selector (preferably `#some-id`) for the element that holds the
     * main page content, if other than `document.body`.
     *
     * E.g. if your main content is inside a custom scrollable container, rather
     * than directly in `document.body`, then pass a selector for it here.
     *
     * The element must be scrollable, i.e. have a fixed size and `overflow: scroll`.
     *
     * **IMPORTANT:** You must set this before initializing any watchers, widgets,
     * etc. If you are using the HTML API, then you must set this before the
     * document `readyState` becomes interactive.
     *
     * @defaultValue null // document.scrollingElement
     * @category Generic
     */
    mainScrollableElementSelector: null,
    /**
     * This setting allows us to automatically wrap certain elements or groups of
     * elements into a single `div` or `span` element to allow for more reliable
     * or efficient working of certain features. In particular:
     *
     * 1. View tracking using relative offsets and a scrolling root **requires wrapping**
     *
     * When using view position tracking with a percentage offset specification
     * (e.g. `top: 50%`) _and_ a custom root element that is scrollable_ (and
     * obviously has a size smaller than the content), you **MUST** enable
     * content wrapping, otherwise the trigger offset elements cannot be
     * positioned relative to the scrolling _content size_.
     *
     * 2. Scroll tracking
     *
     * When using scroll tracking, including scrollbars, on a scrolling element
     * (that obviously has a size smaller than the content), it's recommended for
     * the content of the scrollable element to be wrapped in a single `div`
     * container, to allow for more efficient and reliable detection of changes
     * in the _scrollable content_ size.
     *
     * If content wrapping is disabled, when scroll tracking is used on a given
     * element (other than the root of the document), each of the immediate
     * children of the scrollable element have their sizes tracked, which could
     * lead to more resource usage.
     *
     * 3. Scrollbars on custom elements
     *
     * When you setup a {@link Widgets.Scrollbar} widget for a custom
     * scrollable element that may not be the main scrollable (and therefore
     * won't take up the full viewport all the time), then to be able to position
     * to scrollbar relative to the scrollable element, its content needs to be
     * wrapped.
     *
     * If this setting is OFF, then the scrollbars on custom elements have to
     * rely on position sticky which doesn't have as wide browser support as the
     * default option.
     *
     * 4. Animating on viewport enter/leave
     *
     * For elements that have transforms applied as part of an animation or
     * transition, if you wish to run or reverse the animation when the element
     * enters or leaves the viewport, then the transform can interfere with the
     * viewport tracking. For example, if undoing the animation as soon as the
     * element leaves the viewport makes it enter it again (because it's moved),
     * then this will result in a glitch.
     *
     * If content wrapping is disabled, then to get around such issues, a dummy
     * element is positioned on top of the actual element and is the one tracked
     * across the viewport instead. Either approach could cause issues depending
     * on your CSS, so it's your choice which one is applied.
     *
     * ----------
     *
     * **IMPORTANT:** Certain widgets always require wrapping of elements or their
     * children. This setting only applies in cases where wrapping is optional.
     * If you can, it's recommended to leave this setting ON. You can still try to
     * disable wrapping on a per-element basis by setting `data-lisn-no-wrap`
     * attribute on it. Alternatively, if the elements that need wrapping are
     * already wrapped in an element with a class `lisn-wrapper`, this will be
     * used as the wrapper.
     *
     * @defaultValue true
     * @category Generic
     */
    contentWrappingAllowed: true,
    // [TODO v2] rename this setting
    /**
     * The timeout in milliseconds for waiting for the `document.readyState` to
     * become `complete`. The timer begins _once the `readyState` becomes
     * `interactive`_.
     *
     * The page will be considered "ready" either when the `readyState` becomes
     * `complete` or this many milliseconds after it becomes `interactive`,
     * whichever is first.
     *
     * Set to 0 or a negative number to disable timeout.
     *
     * @defaultValue 2000 // i.e. 2s
     * @category Generic
     */
    pageLoadTimeout: 2000,
    /**
     * This enables LISN's HTML API. Then the page will be parsed (and watched
     * for dynamically added elements at any time) for any elements matching a
     * widget selector. Any element that has a matching CSS class or data
     * attribute will be setup according to the relevant widget, which may wrap,
     * clone or add attributes to the element.
     *
     * This is enabled by default for bundles, and disabled otherwise.
     *
     * **IMPORTANT:** You must set this before the document `readyState` becomes
     * interactive.
     *
     * @defaultValue `false` in general, but `true` in browser bundles
     * @category Widgets
     */
    autoWidgets: false,
    /**
     * Default lag value. Used by
     * - {@link Widgets.SmoothScroll}
     * - {@link Effects/FXController.FXController}
     * - {@link Watchers/ScrollWatcher.ScrollWatcher} (default scroll duration)
     *
     * @defaultValue 1000
     * @category Effects
     */
    effectLag: 1000,
    /**
     * Default setting for
     * {@link Widgets.ScrollbarConfig.hideNative | ScrollbarConfig.hideNative}.
     *
     * @defaultValue true
     * @category Widgets/Scrollbar
     */
    scrollbarHideNative: true,
    /**
     * Default setting for
     * {@link Widgets.ScrollbarConfig.onMobile | ScrollbarConfig.onMobile}.
     *
     * @defaultValue false
     * @category Widgets/Scrollbar
     */
    scrollbarOnMobile: false,
    /**
     * Default setting for
     * {@link Widgets.ScrollbarConfig.positionH | ScrollbarConfig.positionH}.
     *
     * @defaultValue "bottom"
     * @category Widgets/Scrollbar
     */
    scrollbarPositionH: "bottom",
    /**
     * Default setting for
     * {@link Widgets.ScrollbarConfig.positionV | ScrollbarConfig.positionV}.
     *
     * @defaultValue "right"
     * @category Widgets/Scrollbar
     */
    scrollbarPositionV: "right",
    /**
     * Default setting for
     * {@link Widgets.ScrollbarConfig.autoHide | ScrollbarConfig.autoHide}.
     *
     * @defaultValue -1
     * @category Widgets/Scrollbar
     */
    scrollbarAutoHide: -1,
    /**
     * Default setting for
     * {@link Widgets.ScrollbarConfig.clickScroll | ScrollbarConfig.clickScroll}.
     *
     * @defaultValue true
     * @category Widgets/Scrollbar
     */
    scrollbarClickScroll: true,
    /**
     * Default setting for
     * {@link Widgets.ScrollbarConfig.dragScroll | ScrollbarConfig.dragScroll}.
     *
     * @defaultValue true
     * @category Widgets/Scrollbar
     */
    scrollbarDragScroll: true,
    /**
     * Default setting for
     * {@link Widgets.ScrollbarConfig.useHandle | ScrollbarConfig.useHandle}.
     *
     * @defaultValue false
     * @category Widgets/Scrollbar
     */
    scrollbarUseHandle: false,
    /**
     * Default setting for
     * {@link Widgets.SameHeightConfig.diffTolerance | SameHeightConfig.diffTolerance}.
     *
     * @defaultValue 15
     * @category Widgets/SameHeight
     */
    sameHeightDiffTolerance: 15,
    /**
     * Default setting for
     * {@link Widgets.SameHeightConfig.resizeThreshold | SameHeightConfig.resizeThreshold}.
     *
     * @defaultValue 5
     * @category Widgets/SameHeight
     */
    sameHeightResizeThreshold: 5,
    /**
     * Default setting for
     * {@link Widgets.SameHeightConfig.debounceWindow | SameHeightConfig.debounceWindow}.
     *
     * @defaultValue 100
     * @category Widgets/SameHeight
     */
    sameHeightDebounceWindow: 100,
    /**
     * Default setting for
     * {@link Widgets.SameHeightConfig.minGap | SameHeightConfig.minGap}.
     *
     * @defaultValue 30
     * @category Widgets/SameHeight
     */
    sameHeightMinGap: 30,
    /**
     * Default setting for
     * {@link Widgets.SameHeightConfig.maxFreeR | SameHeightConfig.maxFreeR}.
     *
     * @defaultValue 0.4
     * @category Widgets/SameHeight
     */
    sameHeightMaxFreeR: 0.4,
    /**
     * Default setting for
     * {@link Widgets.SameHeightConfig.maxWidthR | SameHeightConfig.maxWidthR}.
     *
     * @defaultValue 1.7
     * @category Widgets/SameHeight
     */
    sameHeightMaxWidthR: 1.7,
    /**
     * Set custom device breakpoints as width in pixels.
     *
     * The value of each sets its lower limit, i.e. it specifies a device whose
     * width is larger than the given value (and up to the next larger one).
     *
     * If you specify only some of the below devices, then the other ones will
     * keep their default breakpoint values.
     *
     * Adding device types, other than the ones listed below is not supported.
     *
     * @category Device layouts
     */
    deviceBreakpoints: {
      /**
       * This should be left as 0 as it's the catch-all for anything narrower
       * than "mobile-wide".
       *
       * @defaultValue 0
       */
      mobile: 0,
      /**
       * Anything wider than the given value is "mobile-wide", up to the value of
       * "tablet".
       *
       * @defaultValue 576
       */
      "mobile-wide": 576,
      /**
       * Anything wider than the given value is "tablet", up to the value of
       * "desktop".
       *
       * @defaultValue 768
       */
      tablet: 768,
      // tablet is anything above this (up to desktop)
      /**
       * Anything wider than the given value is "desktop".
       *
       * @defaultValue 992
       */
      desktop: 992 // desktop is anything above this
    },
    /**
     * Set custom aspect ratio breakpoints (as ratio of width to height).
     *
     * The value of each sets its lower limit, i.e. it specifies an aspect ratio
     * that is wider than the given value (and up to the next wider one).
     *
     * If you specify only some of the below aspect ratios, then the other ones
     * will keep their default breakpoint values.
     *
     * Adding aspect ratio types, other than the ones listed below is not
     * supported.
     *
     * @category Device layouts
     */
    aspectRatioBreakpoints: {
      /**
       * This should be left as 0 as it's the catch-all for anything with
       * a narrower aspect ratio than "tall".
       *
       * @defaultValue 0
       */
      "very-tall": 0,
      // very tall is up to 9:16
      /**
       * Anything with a wider aspect ratio than the given value is "tall", up to
       * the value of "square".
       *
       * @defaultValue 9 / 16
       */
      tall: 9 / 16,
      // tall is between 9:16 and 3:4
      /**
       * Anything with a wider aspect ratio than the given value is "square", up
       * to the value of "wide".
       *
       * @defaultValue 3 / 4
       */
      square: 3 / 4,
      // square is between 3:4 and 4:3
      /**
       * Anything with a wider aspect ratio than the given value is "wide", up to
       * the value of "very-wide".
       *
       * @defaultValue 4 / 3
       */
      wide: 4 / 3,
      // wide is between 4:3 and 16:9
      /**
       * Anything with a wider aspect ratio than the given value is "very-wide".
       *
       * @defaultValue 16 / 9
       */
      "very-wide": 16 / 9 // very wide is above 16:9
    },
    /**
     * The CSS class that enables light theme.
     *
     * **IMPORTANT:** If you change this, you should also change the
     * `$light-theme-cls` variable in the SCSS configuration, or otherwise add the
     * following to your CSS:
     *
     * :root,
     * .custom-light-theme-cls {
     *   --lisn-color-fg: some-dark-color;
     *   --lisn-color-fg-t: some-dark-color-with-transparency;
     *   --lisn-color-bg: some-light-color;
     *   --lisn-color-bg-t: some-light-color-with-transparency;
     * }
     */
    lightThemeClassName: "light-theme",
    /**
     * The CSS class that enables dark theme.
     *
     * **IMPORTANT:** If you change this, you should also change the
     * `$dark-theme-cls` variable in the SCSS configuration, or otherwise add the
     * following to your CSS:
     *
     * .custom-dark-theme-cls {
     *   --lisn-color-fg: some-light-color;
     *   --lisn-color-fg-t: some-light-color-with-transparency;
     *   --lisn-color-bg: some-dark-color;
     *   --lisn-color-bg-t: some-dark-color-with-transparency;
     * }
     */
    darkThemeClassName: "dark-theme",
    /**
     * Used to determine the effective delta in pixels for gestures triggered by
     * some key (arrows) and wheel events (where the browser reports the delta
     * mode to be LINE).
     *
     * Value is in pixels.
     *
     * @defaultValue 40
     * @category Gestures
     */
    deltaLineHeight: 40,
    /**
     * Used to determine the effective delta in pixels for gestures triggered by
     * some wheel events (where the browser reports the delta mode to be PAGE).
     *
     * Value is in pixels.
     *
     * @defaultValue 1600
     * @category Gestures
     */
    deltaPageWidth: 1600,
    /**
     * Used to determine the effective delta in pixels for gestures triggered by
     * some key (PageUp/PageDown/Space) and wheel events (where the browser
     * reports the delta mode to be PAGE).
     *
     * Value is in pixels.
     *
     * @defaultValue 800
     * @category Gestures
     */
    deltaPageHeight: 800,
    /**
     * Controls the debugging verbosity level. Values from 0 (none) to 10 (insane)
     * are recognized.
     *
     * **Note:** Logging is not available in bundles except in the "debug" bundle.
     *
     * @defaultValue `0` except in the "debug" bundle where it defaults to 10
     * @category Logging
     */
    verbosityLevel: 0,
    /**
     * The URL of the remote logger to connect to. LISN uses
     * {@link https://socket.io/docs/v4/client-api/ | socket.io-client}
     * to talk to the client and emits messages on the following namespaces:
     *
     * - `console.debug`
     * - `console.log`
     * - `console.info`
     * - `console.warn`
     * - `console.error`
     *
     * There is a simple logging server that ships with LISN, see the source
     * code repository.
     *
     * You can always explicitly disable remote logging on a given page by
     * setting `disableRemoteLog=1` query parameter in the URL.
     *
     * **Note:** Logging is not available in bundles (except in the `debug` bundle).
     *
     * @defaultValue null
     * @category Logging
     */
    remoteLoggerURL: null,
    /**
     * Enable remote logging only on mobile devices.
     *
     * You can always disable remote logging for any page by setting
     * `disableRemoteLog=1` URL query parameter.
     *
     * **Note:** Logging is not available in bundles (except in the `debug` bundle).
     *
     * @defaultValue false
     * @category Logging
     */
    remoteLoggerOnMobileOnly: false
  });
  // --------------------
  /**
   * @module Utils
   */
  /**
   * Round a number to the given decimal precision.
   *
   * @category Math
   */
  const roundNumTo = (value, numDecimal = 0) => {
    const multiplicationFactor = pow(10, numDecimal);
    return round(value * multiplicationFactor) / multiplicationFactor;
  };
  /**
   * Returns true if the given value is a valid _finite_ number.
   *
   * @category Validation
   */
  const isValidNum = value => isNumber(value) && NUMBER.isFinite(value);
  /**
   * If the given value is a valid _finite_ number, it is returned, otherwise
   * the default is returned.
   *
   * @category Math
   */
  const toNum = (value, defaultValue = 0) => {
    const numValue = isLiteralString(value) ? parseFloat(value) : value;
    // parseFloat will strip trailing non-numeric characters, so we check that
    // the parsed number is equal to the string, if it was a string, using loose
    // equality, in order to make sure the entire string was a number.
    return isValidNum(numValue) && numValue == value ? numValue : defaultValue;
  };
  /**
   * If the given value is a valid _finite integer_ number, it is returned,
   * otherwise the default is returned.
   *
   * @category Math
   */
  const toInt = (value, defaultValue = 0) => {
    let numValue = toNum(value, null);
    numValue = numValue === null ? numValue : floor(numValue);
    // Ensure that the parsed int equaled the original by loose equality.
    return isValidNum(numValue) && numValue == value ? numValue : defaultValue;
  };
  /**
   * If the given value is a valid non-negative _finite_ number, it is returned,
   * otherwise the default is returned.
   *
   * @category Math
   */
  const toNonNegNum = (value, defaultValue = 0) => {
    const numValue = toNum(value, null);
    return numValue !== null && numValue >= 0 ? numValue : defaultValue;
  };
  /**
   * If the given value is a valid positive number, it is returned, otherwise the
   * default is returned.
   *
   * @category Math
   */
  const toPosNum = (value, defaultValue = 0) => {
    const numValue = toNum(value, null);
    return numValue !== null && numValue > 0 ? numValue : defaultValue;
  };
  /**
   * Returns the given number bound by min and/or max value.
   *
   * If the value is not a valid number, then `defaultValue` is returned if given
   * (_including if it is null_), otherwise `limits.min` if given and not null,
   * otherwise `limits.max` if given and not null, or finally 0.
   *
   * If the value is outside the bounds, then:
   * - if `defaultValue` is given, `defaultValue` is returned (_including if it
   *   is null_)
   * - otherwise, the min or the max value (whichever one is violated) is
   *   returned
   *
   * @category Math
   */
  const toNumWithBounds = (value, limits, defaultValue) => {
    var _limits$min, _limits$max;
    const numValue = toNum(value, null);
    const min = (_limits$min = limits === null || limits === void 0 ? void 0 : limits.min) !== null && _limits$min !== void 0 ? _limits$min : null;
    const max = (_limits$max = limits === null || limits === void 0 ? void 0 : limits.max) !== null && _limits$max !== void 0 ? _limits$max : null;
    let result;
    if (!isValidNum(numValue)) {
      var _ref;
      result = (_ref = min !== null && min !== void 0 ? min : max) !== null && _ref !== void 0 ? _ref : 0;
    } else if (min !== null && numValue < min) {
      result = min;
    } else if (max !== null && numValue > max) {
      result = max;
    } else {
      result = numValue;
    }
    return result;
  };
  /**
   * Returns the largest absolute value among the given ones.
   *
   * The result is always positive.
   *
   * @category Math
   */
  const maxAbs = (...values) => max(...values.map(v => abs(v)));
  /**
   * Returns the value with the largest absolute value among the given ones.
   *
   * The result can be negative.
   *
   * @category Math
   */
  const havingMaxAbs = (...values) => lengthOf(values) ? values.sort((a, b) => abs(b) - abs(a))[0] : -INFINITY;
  /**
   * Returns the angle (in radians) that the vector defined by the given x, y
   * makes with the positive horizontal axis.
   *
   * The angle returned is in the range -PI to PI, not including -PI.
   *
   * @category Math
   */
  const hAngle = (x, y) => normalizeAngle(MATH.atan2(y, x)); // ensure that -PI is transformed to +PI
  /**
   * Normalizes the given angle (in radians) so that it's in the range -PI to PI,
   * not including -PI.
   *
   * @category Math
   */
  const normalizeAngle = a => {
    // ensure it's positive in the range 0 to 2 PI
    while (a < 0 || a > PI * 2) {
      a += (a < 0 ? 1 : -1) * PI * 2;
    }
    // then, if > PI, offset by - 2PI
    return a > PI ? a - PI * 2 : a;
  };
  /**
   * Converts the given angle in degrees to radians.
   *
   * @category Math
   */
  const degToRad = a => a * PI / 180;
  /**
   * Returns true if the given vectors point in the same direction.
   *
   * @param angleDiffThreshold
   *                  Sets the threshold in degrees when comparing the angles of
   *                  two vectors. E.g. for 5 degrees threshold, directions
   *                  whose vectors are within 5 degrees of each other are
   *                  considered parallel.
   *                  It doesn't make sense for this value to be < 0 or >= 90
   *                  degrees. If it is, it's forced to be positive (absolute)
   *                  and <= 89.99.
   *
   * @category Math
   */
  const areParallel = (vA, vB, angleDiffThreshold = 0) => {
    const angleA = hAngle(vA[0], vA[1]);
    const angleB = hAngle(vB[0], vB[1]);
    angleDiffThreshold = min(89.99, abs(angleDiffThreshold));
    return abs(normalizeAngle(angleA - angleB)) <= degToRad(angleDiffThreshold);
  };
  /**
   * Returns true if the given vectors point in the opposite direction.
   *
   * @param angleDiffThreshold
   *                  Sets the threshold in degrees when comparing the angles of
   *                  two vectors. E.g. for 5 degrees threshold, directions
   *                  whose vectors are within 175-185 degrees of each other are
   *                  considered antiparallel.
   *                  It doesn't make sense for this value to be < 0 or >= 90
   *                  degrees. If it is, it's forced to be positive (absolute)
   *                  and <= 89.99.
   *
   * @category Math
   */
  const areAntiParallel = (vA, vB, angleDiffThreshold = 0) => areParallel(vA, [-vB[0], -vB[1]], angleDiffThreshold);
  /**
   * Returns the distance between two points on the screen.
   *
   * @category Math
   */
  const distanceBetween = (ptA, ptB) => sqrt(pow(ptA[0] - ptB[0], 2) + pow(ptA[1] - ptB[1], 2));
  /**
   * Returns an array of object's keys sorted by the numeric value they hold.
   *
   * @category Math
   */
  const sortedKeysByVal = (obj, descending = false) => {
    if (descending) {
      return keysOf(obj).sort((x, y) => obj[y] - obj[x]);
    }
    return keysOf(obj).sort((x, y) => obj[x] - obj[y]);
  };
  /**
   * Takes two integers and returns a bitmask that covers all values between
   * 1 << start and 1 << end, _including the starting and ending one_.
   *
   * If pStart > pEnd, they are reversed.
   *
   * getBitmask(start, start) always returns 1 << start
   * getBitmask(start, end) always returns same as getBitmask(end, start)
   *
   * @category Math
   */
  const getBitmask = (start, end) => start > end ? getBitmask(end, start) : -1 >>> 32 - end - 1 + start << start;
  /**
   * @module
   * @ignore
   * @internal
   */
  /**
   * Recursively copies the given value. Handles circular references.
   *
   * The following types are deeply copied:
   * - Plain objects (i.e. with prototype Object or null)
   * - Array
   * - ArrayBuffer, DataView and TypedArray
   * - Map
   * - Set
   * - Date
   * - RegExp
   *
   * Other values are returned as is
   *
   * @since v1.3.0
   */
  const deepCopy = (value, _seen = new WeakMap()) => {
    if (!isObject(value)) {
      // Primitive or function
      return value;
    }
    if (_seen.has(value)) {
      // Circular reference
      return _seen.get(value);
    }
    if (isArray(value)) {
      const out = new ARRAY(value.length);
      _seen.set(value, out);
      for (let i = 0; i < value.length; i++) {
        if (i in value) {
          out[i] = deepCopy(value[i], _seen);
        }
      }
      return out;
    }
    if (isMap(value)) {
      const out = newMap();
      _seen.set(value, out);
      for (const [k, v] of value) {
        const kCopy = deepCopy(k, _seen);
        const vCopy = deepCopy(v, _seen);
        out.set(kCopy, vCopy);
      }
      return out;
    }
    if (isSet(value)) {
      const out = newSet();
      _seen.set(value, out);
      for (const v of value) {
        out.add(deepCopy(v, _seen));
      }
      return out;
    }
    if (isOfType(value, "ArrayBuffer")) {
      return value.slice(0);
    }
    if (isOfType(value, "DataView")) {
      const buf = deepCopy(value.buffer, _seen);
      return new DataView(buf, value.byteOffset, value.byteLength);
    } else if (ArrayBuffer.isView(value)) {
      // DataView already handled above, so this is TypedArray:
      // Int8Array, Uint8Array, Float32Array, etc.
      const Ctor = value.constructor;
      return new Ctor(value);
    }
    if (isOfType(value, "Date")) {
      return new Date(value.getTime());
    }
    if (isOfType(value, "RegExp")) {
      const flags = value.flags !== undefined ? value.flags : (value.global ? "g" : "") + (value.ignoreCase ? "i" : "") + (value.multiline ? "m" : "") + (value.unicode ? "u" : "") + (value.sticky ? "y" : "") + (value.dotAll ? "s" : "");
      const copy = new RegExp(value.source, flags);
      copy.lastIndex = value.lastIndex;
      return copy;
    }
    if (!isPlainObject(value)) {
      // Non-clonable
      return value;
    }
    // Plain object (preserve prototype, if it's null & property descriptors,
    // including symbols)
    const out = OBJECT.create(getPrototypeOf(value));
    _seen.set(value, out);
    for (const key of Reflect.ownKeys(value)) {
      const desc = OBJECT.getOwnPropertyDescriptor(value, key);
      if (!desc) {
        continue;
      }
      if ("value" in desc) {
        // Data descriptor: deep copy the value
        desc.value = deepCopy(desc.value, _seen);
      }
      // Otherwise it's accessor descriptor: keep same getter/setter references
      // (cannot deep copy closures, so we redefine them as is)
      defineProperty(out, key, desc);
    }
    return out;
  };
  /**
   * For all keys present in `toObj`, if the key is also in `fromObj`, it copies
   * the value recursively from `fromObj` into `toObj` in place.
   *
   * Plain objects are recursed into, but other values are copied as is.
   *
   * @since v1.3.0 Was previously called copyExistingKeys
   */
  const copyExistingKeysTo = (fromObj, toObj) => {
    for (const key in toObj) {
      if (!hasOwnProp(toObj, key)) {
        continue;
      }
      if (key in fromObj) {
        const current = toObj[key];
        const updated = fromObj[key];
        if (isPlainObject(updated) && isPlainObject(current)) {
          copyExistingKeysTo(updated, current);
        } else if (updated !== undefined) {
          toObj[key] = updated;
        }
      }
    }
  };
  /**
   * Omits the keys in object `keysToRm` from `obj`. This is to avoid hardcording
   * the key names as a string so as to allow minifier to mangle them, and to
   * avoid using object spread.
   */
  const omitKeys = (obj, keysToRm) => {
    const res = {};
    for (const key in obj) {
      if (!(key in keysToRm)) {
        res[key] = obj[key];
      }
    }
    return res;
  };
  /**
   * Returns true if the two objects are equal. If values are numeric, it will
   * round to the given number of decimal places.
   */
  const compareValuesIn = (objA, objB, roundTo = 3) => {
    for (const key in objA) {
      if (!hasOwnProp(objA, key)) {
        continue;
      }
      const valA = objA[key];
      const valB = objB[key];
      if (isPlainObject(valA) && isPlainObject(valB)) {
        if (!compareValuesIn(valA, valB)) {
          return false;
        }
      } else if (isLiteralNumber(valA) && isLiteralNumber(valB)) {
        if (roundNumTo(valA, roundTo) !== roundNumTo(valB, roundTo)) {
          return false;
        }
      } else if (valA !== valB) {
        return false;
      }
    }
    return true;
  };
  const toArrayIfSingle = value => isArray(value) ? value : !isNullish(value) ? [value] : [];
  const toBoolean = value => value === true || value === "true" || value === "" ? true : isNullish(value) || value === false || value === "false" ? false : null;
  /**
   * @module Utils
   */
  /**
   * Formats an object as a string. It supports more meaningful formatting as
   * string for certain types rather than using the default string
   * representation.
   *
   * **NOTE:** This is not intended for serialization of data that needs to be
   * de-serialized. Only for debugging output.
   *
   * @param value    The value to format as string.
   * @param [maxLen] Maximum length of the returned string. If not given or
   *                 is <= 0, the string is not truncated. Otherwise, if the
   *                 result is longer than maxLen, it is truncated to
   *                 `maxLen - 3` and added a suffix of "...".
   *                 Note that if `maxLen` is > 0 but <= 3, the result is
   *                 always "..."
   *
   * @category Text
   */
  const formatAsString = (value, maxLen) => {
    const result = maybeConvertToString(value, false);
    return result;
  };
  /**
   * Join an array of values as string using separator. It uses
   * {@link formatAsString} rather than the default string representation as
   * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join | Array:join} would.
   *
   * @param separator The separator to use to delimit each argument.
   * @param args      Objects or values to convert to string and join.
   *
   * @category Text
   */
  const joinAsString = (separator, ...args) => args.map(a => formatAsString(a)).join(separator);
  /**
   * Similar to
   * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split | String.prototype.split}
   * except that
   * 1. `limit` is interpreted as the maximum number of splits, and the
   *   returned array contains `limit + 1` entries. Also if `limit` is given and
   *   the number of substrings is greater than the limit, all the remaining
   *   substrings are present in the final substring.
   * 2. If input is an empty string (or containing only whitespace), returns an
   *    empty array.
   *
   * @example
   * ```javascript
   * splitOn('foo, bar, baz', RegExp(',\\s*'), 0); // -> ['foo, bar, baz']
   * splitOn('foo, bar, baz', RegExp(',\\s*'), 1); // -> ['foo', 'bar, baz']
   * splitOn('foo, bar, baz', RegExp(',\\s*'), 2); // -> ['foo', 'bar', 'baz']
   * splitOn('foo, bar, baz', RegExp(',\\s*'), 3); // -> ['foo', 'bar', 'baz']
   * ```
   *
   * @param trim  If true, entries will be trimmed for whitespace after splitting.
   *
   * @param limit If not given or < 0, the string will be split on every
   *              occurrence of `separator`. Otherwise, it will be split on
   *              the first `limit` number of occurrences of `separator`.
   *
   * @category Text
   */
  const splitOn = (input, separator, trim, limit) => {
    if (!input.trim()) {
      return [];
    }
    limit !== null && limit !== void 0 ? limit : limit = -1;
    const output = [];
    const addEntry = s => output.push(trim ? s.trim() : s);
    while (limit--) {
      let matchIndex = -1,
        matchLength = 0;
      if (isLiteralString(separator)) {
        matchIndex = input.indexOf(separator);
        matchLength = lengthOf(separator);
      } else {
        var _match$index;
        const match = separator.exec(input);
        matchIndex = (_match$index = match === null || match === void 0 ? void 0 : match.index) !== null && _match$index !== void 0 ? _match$index : -1;
        matchLength = match ? lengthOf(match[0]) : 0;
      }
      if (matchIndex < 0) {
        break;
      }
      addEntry(input.slice(0, matchIndex));
      input = input.slice(matchIndex + matchLength);
    }
    addEntry(input);
    return output;
  };
  /**
   * Converts a kebab-cased-string to camelCase.
   * The result is undefined if the input string is not formatted in
   * kebab-case.
   *
   * @category Text
   */
  const kebabToCamelCase = kebabToCamelCase$1;
  /**
   * Converts a camelCasedString to kebab-case.
   * The result is undefined if the input string is not formatted in
   * camelCase.
   *
   * @category Text
   */
  const camelToKebabCase = camelToKebabCase$1;
  /**
   * Generates a random string of a fixed length.
   *
   * **IMPORTANT:** This is _not_ suitable for cryptographic applicati