var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var require_sequencer = __commonJS({
  "sequencer.js"(exports, module) {
    const CONSTANTS = {
      MODULE_NAME: "sequencer",
      EFFECTS_FLAG_NAME: "effects",
      SOUNDS_FLAG_NAME: "sounds",
      IS_V12: false,
      COLOR: {
        PRIMARY: 15615023,
        SECONDARY: 6298186
      },
      SHAPES: {
        POLY: "polygon",
        RECT: "rectangle",
        CIRC: "circle",
        ELIP: "ellipse",
        RREC: "roundedRect"
      },
      FEET_REGEX: new RegExp(/\.[0-9]+ft\.*/g),
      ARRAY_REGEX: new RegExp(/\.[0-9]$/g),
      STATUS: {
        READY: 0,
        RUNNING: 1,
        COMPLETE: 2,
        SKIPPED: 3,
        ABORTED: 4
      },
      PLACEMENT_RESTRICTIONS: {
        ANYWHERE: "anywhere",
        LINE_OF_SIGHT: "lineOfSight",
        NO_COLLIDABLES: "noCollidables"
      },
      CALLBACKS: {
        SHOW: "show",
        MOUSE_MOVE: "mouseMove",
        MOVE: "move",
        COLLIDE: "collide",
        STOP_COLLIDING: "stopColliding",
        INVALID_PLACEMENT: "invalidPlacement",
        PLACED: "placed",
        CANCEL: "cancel"
      }
    };
    CONSTANTS.INTEGRATIONS = {};
    CONSTANTS.INTEGRATIONS.ISOMETRIC = {};
    CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE = false;
    CONSTANTS.INTEGRATIONS.ISOMETRIC.MODULE_NAME = "grape_juice-isometrics";
    CONSTANTS.INTEGRATIONS.ISOMETRIC.SCENE_ENABLED = `flags.${CONSTANTS.INTEGRATIONS.ISOMETRIC.MODULE_NAME}.is_isometric`;
    CONSTANTS.INTEGRATIONS.ISOMETRIC.PROJECTION_FLAG = `flags.${CONSTANTS.INTEGRATIONS.ISOMETRIC.MODULE_NAME}.original_image_projection_type`;
    CONSTANTS.INTEGRATIONS.ISOMETRIC.PROJECTION_TYPES = {
      TOPDOWN: "topdown",
      TRUE: "true_isometric",
      DIAMETRIC: "diametric"
    };
    CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION = Math.sqrt(3);
    CONSTANTS.INTEGRATIONS.ISOMETRIC.DIMETRIC_CONVERSION = 2 / CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION;
    CONSTANTS.INTEGRATIONS.ISOMETRIC.DUNGEON_BUILDER_CONVERSION = 278 / 154 / CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION;
    CONSTANTS.EFFECTS_FLAG = `flags.${CONSTANTS.MODULE_NAME}.${CONSTANTS.EFFECTS_FLAG_NAME}`;
    CONSTANTS.REMOVE_EFFECTS_FLAG = `flags.${CONSTANTS.MODULE_NAME}.-=${CONSTANTS.EFFECTS_FLAG_NAME}`;
    CONSTANTS.SOUNDS_FLAG = `flags.${CONSTANTS.MODULE_NAME}.${CONSTANTS.SOUNDS_FLAG_NAME}`;
    function registerEase(easeName, easeFunction, overwrite = false) {
      if (typeof easeName !== "string")
        throw custom_error("registerEase", "easeName must be of type string");
      if (!is_function$1(easeFunction))
        throw custom_error(
          "registerEase",
          "easeFunction must be of type function"
        );
      if (easeFunctions[easeName] !== void 0 && !overwrite) return;
      debug(`registerEase | Registered ease function: ${easeName}`);
      easeFunctions[easeName] = easeFunction;
    }
    const easeFunctions = {
      linear,
      easeInSine,
      easeOutSine,
      easeInOutSine,
      easeInQuad,
      easeOutQuad,
      easeInOutQuad,
      easeInCubic,
      easeOutCubic,
      easeInOutCubic,
      easeInQuart,
      easeOutQuart,
      easeInOutQuart,
      easeInQuint,
      easeOutQuint,
      easeInOutQuint,
      easeInExpo,
      easeOutExpo,
      easeInOutExpo,
      easeInCirc,
      easeOutCirc,
      easeInOutCirc,
      easeInBack,
      easeOutBack,
      easeInOutBack,
      easeInElastic,
      easeOutElastic,
      easeInOutElastic,
      easeInBounce,
      easeOutBounce,
      easeInOutBounce
    };
    const EASE = {
      LINEAR: "linear",
      SINE_IN: "easeInSine",
      SINE_OUT: "easeOutSine",
      SINE_IN_OUT: "easeInOutSine",
      QUAD_IN: "easeInQuad",
      QUAD_OUT: "easeOutQuad",
      QUAD_IN_OUT: "easeInOutQuad",
      CUBIC_IN: "easeInCubic",
      CUBIC_OUT: "easeOutCubic",
      CUBIC_IN_OUT: "easeInOutCubic",
      QUART_IN: "easeInQuart",
      QUART_OUT: "easeOutQuart",
      QUART_IN_OUT: "easeInOutQuart",
      QUINT_IN: "easeInQuint",
      QUINT_OUT: "easeOutQuint",
      QUINT_IN_OUT: "easeInOutQuint",
      EXPO_IN: "easeInExpo",
      EXPO_OUT: "easeOutExpo",
      EXPO_IN_OUT: "easeInOutExpo",
      CIRC_IN: "easeInCirc",
      CIRC_OUT: "easeOutCirc",
      CIRC_IN_OUT: "easeInOutCirc",
      BACK_IN: "easeInBack",
      BACK_OUT: "easeOutBack",
      BACK_IN_OUT: "easeInOutBack",
      ELASTIC_IN: "easeInElastic",
      ELASTIC_OUT: "easeOutElastic",
      ELASTIC_IN_OUT: "easeInOutElastic",
      BOUNCE_IN: "easeInBounce",
      BOUNCE_OUT: "easeOutBounce",
      BOUNCE_IN_OUT: "easeInOutBounce"
    };
    function linear(x) {
      return x;
    }
    function easeInSine(x) {
      return 1 - Math.cos(x * Math.PI / 2);
    }
    function easeOutSine(x) {
      return Math.sin(x * Math.PI / 2);
    }
    function easeInOutSine(x) {
      return -(Math.cos(Math.PI * x) - 1) / 2;
    }
    function easeInQuad(x) {
      return x * x;
    }
    function easeOutQuad(x) {
      return 1 - (1 - x) * (1 - x);
    }
    function easeInOutQuad(x) {
      return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
    }
    function easeInCubic(x) {
      return x * x * x;
    }
    function easeOutCubic(x) {
      return 1 - Math.pow(1 - x, 3);
    }
    function easeInOutCubic(x) {
      return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
    }
    function easeInQuart(x) {
      return x * x * x * x;
    }
    function easeOutQuart(x) {
      return 1 - Math.pow(1 - x, 4);
    }
    function easeInOutQuart(x) {
      return x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2;
    }
    function easeInQuint(x) {
      return x * x * x * x * x;
    }
    function easeOutQuint(x) {
      return 1 - Math.pow(1 - x, 5);
    }
    function easeInOutQuint(x) {
      return x < 0.5 ? 16 * x * x * x * x * x : 1 - Math.pow(-2 * x + 2, 5) / 2;
    }
    function easeInExpo(x) {
      return x === 0 ? 0 : Math.pow(2, 10 * x - 10);
    }
    function easeOutExpo(x) {
      return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
    }
    function easeInOutExpo(x) {
      return x === 0 ? 0 : x === 1 ? 1 : x < 0.5 ? Math.pow(2, 20 * x - 10) / 2 : (2 - Math.pow(2, -20 * x + 10)) / 2;
    }
    function easeInCirc(x) {
      return 1 - Math.sqrt(1 - Math.pow(x, 2));
    }
    function easeOutCirc(x) {
      return Math.sqrt(1 - Math.pow(x - 1, 2));
    }
    function easeInOutCirc(x) {
      return x < 0.5 ? (1 - Math.sqrt(1 - Math.pow(2 * x, 2))) / 2 : (Math.sqrt(1 - Math.pow(-2 * x + 2, 2)) + 1) / 2;
    }
    function easeInBack(x) {
      const c1 = 1.70158;
      const c3 = c1 + 1;
      return c3 * x * x * x - c1 * x * x;
    }
    function easeOutBack(x) {
      const c1 = 1.70158;
      const c3 = c1 + 1;
      return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
    }
    function easeInOutBack(x) {
      const c1 = 1.70158;
      const c2 = c1 * 1.525;
      return x < 0.5 ? Math.pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2) / 2 : (Math.pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
    }
    function easeInElastic(x) {
      const c4 = 2 * Math.PI / 3;
      return x === 0 ? 0 : x === 1 ? 1 : -Math.pow(2, 10 * x - 10) * Math.sin((x * 10 - 10.75) * c4);
    }
    function easeOutElastic(x) {
      const c4 = 2 * Math.PI / 3;
      return x === 0 ? 0 : x === 1 ? 1 : Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1;
    }
    function easeInOutElastic(x) {
      const c5 = 2 * Math.PI / 4.5;
      return x === 0 ? 0 : x === 1 ? 1 : x < 0.5 ? -(Math.pow(2, 20 * x - 10) * Math.sin((20 * x - 11.125) * c5)) / 2 : Math.pow(2, -20 * x + 10) * Math.sin((20 * x - 11.125) * c5) / 2 + 1;
    }
    function easeInBounce(x) {
      return 1 - easeOutBounce(1 - x);
    }
    function easeOutBounce(x) {
      const n1 = 7.5625;
      const d1 = 2.75;
      if (x < 1 / d1) {
        return n1 * x * x;
      } else if (x < 2 / d1) {
        return n1 * (x -= 1.5 / d1) * x + 0.75;
      } else if (x < 2.5 / d1) {
        return n1 * (x -= 2.25 / d1) * x + 0.9375;
      } else {
        return n1 * (x -= 2.625 / d1) * x + 0.984375;
      }
    }
    function easeInOutBounce(x) {
      return x < 0.5 ? (1 - easeOutBounce(1 - 2 * x)) / 2 : (1 + easeOutBounce(2 * x - 1)) / 2;
    }
    async function getFiles(inFile, { applyWildCard = false, softFail = false } = {}) {
      let source = "data";
      const browseOptions = { wildcard: applyWildCard };
      if (/\.s3\./.test(inFile)) {
        source = "s3";
        const { bucket, keyPrefix } = foundry.utils.parseS3URL(inFile);
        if (bucket) {
          browseOptions.bucket = bucket;
          inFile = keyPrefix;
        }
      }
      try {
        return (await FilePicker.browse(source, inFile, browseOptions)).files;
      } catch (err) {
        if (softFail) return false;
        throw custom_error("Sequencer", `getFiles | ${err}`);
      }
    }
    function interpolate(p1, p2, t, ease = "linear") {
      const easeFunction = is_function$1(ease) ? ease : easeFunctions[ease];
      return p1 + (p2 - p1) * easeFunction(t);
    }
    function random_float_between(min, max, twister = false) {
      const random = twister ? twister.random() : Math.random();
      const _max = Math.max(max, min);
      const _min = Math.min(max, min);
      return random * (_max - _min) + _min;
    }
    function random_int_between(min, max, twister = false) {
      return Math.floor(random_float_between(min, max, twister));
    }
    function flip_negate(num_1, num_2) {
      if (num_1 > 0) {
        num_1 -= num_2;
      } else {
        num_1 += num_2;
      }
      return num_1;
    }
    function shuffle_array(inArray, twister = false) {
      let shuffled = [...inArray];
      const randomMethod = twister?.random ?? Math.random;
      for (let i = shuffled.length - 1; i > 0; i--) {
        let j = Math.floor(randomMethod() * (i + 1));
        let temp = shuffled[i];
        shuffled[i] = shuffled[j];
        shuffled[j] = temp;
      }
      return shuffled;
    }
    function is_function$1(inFunc) {
      return inFunc && ({}.toString.call(inFunc) === "[object Function]" || {}.toString.call(inFunc) === "[object AsyncFunction]");
    }
    function random_array_element(inArray, { recurse = false, twister = false, index = false } = {}) {
      const chosenIndex = random_int_between(0, inArray.length, twister);
      let choice = inArray[chosenIndex];
      if (recurse && Array.isArray(choice)) {
        return random_array_element(choice, { recurse, twister, index });
      }
      if (index) {
        return chosenIndex;
      }
      return choice;
    }
    function random_object_element(inObject, { recurse = false, twister = false } = {}) {
      let keys = Object.keys(inObject).filter((k) => !k.startsWith("_"));
      let choice = inObject[random_array_element(keys, { twister })];
      if (typeof choice === "object" && recurse) {
        return random_object_element(choice, { recurse: true });
      }
      return choice;
    }
    function is_real_number(inNumber) {
      return !isNaN(inNumber) && typeof inNumber === "number" && isFinite(inNumber);
    }
    function deep_get(obj, path) {
      if (!Array.isArray(path)) path = path.split(".");
      try {
        let i;
        for (i = 0; i < path.length - 1; i++) {
          obj = obj[path[i]];
        }
        return obj[path[i]];
      } catch (err) {
      }
    }
    function deep_set(obj, path, value) {
      if (!Array.isArray(path)) path = path.split(".");
      try {
        let i;
        for (i = 0; i < path.length - 1; i++) {
          obj = obj[path[i]];
        }
        if (is_function$1(obj[path[i]])) {
          obj[path[i]](value);
        } else {
          obj[path[i]] = value;
        }
      } catch (err) {
      }
    }
    function flatten_object(obj) {
      let toReturn = [];
      for (let i in obj) {
        if (i.startsWith("_")) continue;
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == "object") {
          let flatObject = flatten_object(obj[i]);
          for (let x in flatObject) {
            if (!flatObject.hasOwnProperty(x)) continue;
            toReturn[i + "." + x] = flatObject[x];
          }
        } else {
          toReturn[i] = obj[i];
        }
      }
      return toReturn;
    }
    function wait$1(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }
    function clamp$1(num, min, max) {
      const _max = Math.max(min, max);
      const _min = Math.min(min, max);
      return Math.max(_min, Math.min(_max, num));
    }
    function is_UUID(inId) {
      return typeof inId === "string" && (inId.startsWith("Scene") || inId.startsWith("Actor") || inId.startsWith("Item")) && (inId.match(/\./g) || []).length && !inId.endsWith(".");
    }
    function get_object_from_scene(inObjectId, inSceneId = game.user.viewedScene) {
      let tryUUID = is_UUID(inObjectId);
      if (tryUUID) {
        const obj = fromUuidSync(inObjectId);
        if (obj) return obj;
        tryUUID = false;
      }
      const allDocs = get_all_documents_from_scene(inSceneId);
      return allDocs.find((obj) => {
        return get_object_identifier(obj, tryUUID) === inObjectId;
      });
    }
    function get_all_documents_from_scene(inSceneId = false) {
      const scene = inSceneId ? game.scenes.get(inSceneId) : game.scenes.get(game.user?.viewedScene);
      if (!scene) return [];
      return [
        ...canvas.layers.map((layer2) => layer2?.children ?? [].concat(layer2?.preview?.children ?? [])),
        ...Array.from(scene?.tokens ?? []),
        ...Array.from(scene?.lights ?? []),
        ...Array.from(scene?.sounds ?? []),
        ...Array.from(scene?.templates ?? []),
        ...Array.from(scene?.tiles ?? []),
        ...Array.from(scene?.walls ?? []),
        ...Array.from(scene?.drawings ?? [])
      ].deepFlatten().filter(Boolean);
    }
    function validate_document(inObject) {
      const document2 = inObject?.document ?? inObject;
      return is_UUID(document2?.uuid) ? document2 : inObject;
    }
    function get_object_identifier(inObject, tryUUID = true) {
      const uuid = tryUUID && is_UUID(inObject?.uuid) ? inObject?.uuid : void 0;
      return uuid ?? inObject?.id ?? inObject?.document?.name ?? inObject?.name ?? (inObject?.tag !== "" ? inObject?.tag : void 0) ?? (inObject?.label !== "" ? inObject?.label : void 0) ?? inObject?.sound_id;
    }
    function make_array_unique(inArray) {
      return Array.from(new Set(inArray));
    }
    function debug(msg, args = "") {
      if (game.settings.get(CONSTANTS.MODULE_NAME, "debug"))
        console.log(`DEBUG | Sequencer | ${msg}`, args);
    }
    function debug_error(msg, args) {
      if (game.settings.get(CONSTANTS.MODULE_NAME, "debug"))
        console.error(`DEBUG | Sequencer | ${msg}`, args);
    }
    function custom_warning(inClassName, warning, notify = false) {
      inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
      warning = `${inClassName} | ${warning}`;
      if (notify) ui.notifications.warn(warning, { console: false });
      console.warn(warning.replace("<br>", "\n"));
    }
    const throttledWarnings = {};
    function throttled_custom_warning(inClassName, warning, delay = 1e4, notify = false) {
      inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
      warning = `${inClassName} | ${warning}`;
      if (throttledWarnings[warning]) return;
      throttledWarnings[warning] = true;
      if (notify) ui.notifications.warn(warning, { console: false });
      console.warn(warning.replace("<br>", "\n"));
      setTimeout(() => {
        delete throttledWarnings[warning];
      }, delay);
    }
    function custom_error(inClassName, error, notify = true) {
      inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
      error = `${inClassName} | ${error}`;
      if (notify) ui.notifications.error(error, { console: false });
      return new Error(error.replace("<br>", "\n"));
    }
    function user_can_do(inSetting) {
      return game.user.role > game.settings.get(CONSTANTS.MODULE_NAME, inSetting);
    }
    function group_by(xs, key) {
      return xs.reduce(function(acc, obj) {
        let property = foundry.utils.getProperty(obj, key);
        acc[property] = acc[property] || [];
        acc[property].push(obj);
        return acc;
      }, {});
    }
    function objHasProperty(obj, prop) {
      return obj.constructor.prototype.hasOwnProperty(prop);
    }
    function sequence_proxy_wrap(inSequence) {
      return new Proxy(inSequence, {
        get: function(target, prop) {
          if (!objHasProperty(target, prop)) {
            if (Sequencer.SectionManager.externalSections[prop] === void 0) {
              const section = target.sections[target.sections.length - 1];
              if (section && objHasProperty(section, prop)) {
                const targetProperty = Reflect.get(section, prop);
                return is_function$1(targetProperty) ? targetProperty.bind(section) : targetProperty;
              }
              return Reflect.get(target, prop);
            }
            target.sectionToCreate = Sequencer.SectionManager.externalSections[prop];
            return Reflect.get(target, "_createCustomSection");
          }
          return Reflect.get(target, prop);
        }
      });
    }
    function section_proxy_wrap(inClass) {
      return new Proxy(inClass, {
        get: function(target, prop) {
          if (!objHasProperty(target, prop) && objHasProperty(target.sequence, prop)) {
            const targetProperty = Reflect.get(target.sequence, prop);
            return is_function$1(targetProperty) ? targetProperty.bind(target.sequence) : targetProperty;
          }
          return Reflect.get(target, prop);
        }
      });
    }
    function str_to_search_regex_str(str) {
      return str.trim().replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*+/g, ".*?");
    }
    function safe_str(str) {
      return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
    }
    function get_hash(input) {
      let hash2 = 0;
      const len = input.length;
      for (let i = 0; i < len; i++) {
        hash2 = (hash2 << 5) - hash2 + input.charCodeAt(i);
        hash2 |= 0;
      }
      return hash2;
    }
    function parseColor(inColor) {
      return {
        hexadecimal: is_real_number(inColor) ? inColor.toString(16) : inColor,
        decimal: typeof inColor === "string" && inColor.startsWith("#") ? parseInt(inColor.slice(1), 16) : inColor
      };
    }
    function getCanvasMouse() {
      return canvas?.app?.renderer?.events?.pointer ?? canvas?.app?.renderer?.plugins?.interaction?.pointer;
    }
    function createMersenneTwister(seed) {
      const twister = foundry?.dice?.MersenneTwister ?? MersenneTwister;
      return new twister(seed);
    }
    function deepMerge(target, ...sourceObj) {
      if (Object.prototype.toString.call(target) !== "[object Object]") {
        throw new TypeError(`deepMerge error: 'target' is not an object.`);
      }
      if (sourceObj.length === 0) {
        throw new TypeError(`deepMerge error: 'sourceObj' is not an object.`);
      }
      for (let cntr = 0; cntr < sourceObj.length; cntr++) {
        if (Object.prototype.toString.call(sourceObj[cntr]) !== "[object Object]") {
          throw new TypeError(`deepMerge error: 'sourceObj[${cntr}]' is not an object.`);
        }
      }
      if (sourceObj.length === 1) {
        const stack = [];
        for (const obj of sourceObj) {
          stack.push({ target, source: obj });
        }
        while (stack.length > 0) {
          const { target: target2, source } = stack.pop();
          for (const prop in source) {
            if (Object.hasOwn(source, prop)) {
              const sourceValue = source[prop];
              const targetValue = target2[prop];
              if (Object.hasOwn(target2, prop) && targetValue?.constructor === Object && sourceValue?.constructor === Object) {
                stack.push({ target: targetValue, source: sourceValue });
              } else {
                target2[prop] = sourceValue;
              }
            }
          }
        }
      } else {
        const stack = [{ target, sources: sourceObj }];
        while (stack.length > 0) {
          const { target: target2, sources } = stack.pop();
          for (const source of sources) {
            for (const prop in source) {
              if (Object.hasOwn(source, prop)) {
                const sourceValue = source[prop];
                const targetValue = target2[prop];
                if (Object.hasOwn(target2, prop) && targetValue?.constructor === Object && sourceValue?.constructor === Object) {
                  target2[prop] = Object.assign({}, targetValue);
                  stack.push({ target: target2[prop], sources: [sourceValue] });
                } else {
                  target2[prop] = sourceValue;
                }
              }
            }
          }
        }
      }
      return target;
    }
    function hasGetter(object, accessor) {
      if (typeof object !== "object" || object === null || object === void 0) {
        return false;
      }
      const iDescriptor = Object.getOwnPropertyDescriptor(object, accessor);
      if (iDescriptor !== void 0 && iDescriptor.get !== void 0) {
        return true;
      }
      for (let o = Object.getPrototypeOf(object); o; o = Object.getPrototypeOf(o)) {
        const descriptor = Object.getOwnPropertyDescriptor(o, accessor);
        if (descriptor !== void 0 && descriptor.get !== void 0) {
          return true;
        }
      }
      return false;
    }
    function isIterable(value) {
      if (value === null || value === void 0 || typeof value !== "object") {
        return false;
      }
      return Symbol.iterator in value;
    }
    function isObject(value) {
      return value !== null && typeof value === "object" && !Array.isArray(value);
    }
    function isPlainObject(value) {
      if (Object.prototype.toString.call(value) !== "[object Object]") {
        return false;
      }
      const prototype = Object.getPrototypeOf(value);
      return prototype === null || prototype === Object.prototype;
    }
    function safeAccess(data, accessor, defaultValue) {
      if (typeof data !== "object" || data === null) {
        return defaultValue;
      }
      if (typeof accessor !== "string") {
        return defaultValue;
      }
      const keys = accessor.split(".");
      let result = data;
      for (let cntr = 0; cntr < keys.length; cntr++) {
        if (result[keys[cntr]] === void 0 || result[keys[cntr]] === null) {
          return defaultValue;
        }
        result = result[keys[cntr]];
      }
      return result;
    }
    function safeSet(data, accessor, value, { operation = "set", createMissing = false } = {}) {
      if (typeof data !== "object" || data === null) {
        throw new TypeError(`safeSet error: 'data' is not an object.`);
      }
      if (typeof accessor !== "string") {
        throw new TypeError(`safeSet error: 'accessor' is not a string.`);
      }
      if (typeof operation !== "string") {
        throw new TypeError(`safeSet error: 'options.operation' is not a string.`);
      }
      if (operation !== "add" && operation !== "div" && operation !== "mult" && operation !== "set" && operation !== "set-undefined" && operation !== "sub") {
        throw new Error(`safeSet error: Unknown 'options.operation'.`);
      }
      if (typeof createMissing !== "boolean") {
        throw new TypeError(`safeSet error: 'options.createMissing' is not a boolean.`);
      }
      const access = accessor.split(".");
      let result = false;
      if (access.length === 1 && !createMissing && !(access[0] in data)) {
        return false;
      }
      for (let cntr = 0; cntr < access.length; cntr++) {
        if (Array.isArray(data)) {
          const number = +access[cntr];
          if (!Number.isInteger(number) || number < 0) {
            return false;
          }
        }
        if (cntr === access.length - 1) {
          switch (operation) {
            case "add":
              data[access[cntr]] += value;
              result = true;
              break;
            case "div":
              data[access[cntr]] /= value;
              result = true;
              break;
            case "mult":
              data[access[cntr]] *= value;
              result = true;
              break;
            case "set":
              data[access[cntr]] = value;
              result = true;
              break;
            case "set-undefined":
              if (data[access[cntr]] === void 0) {
                data[access[cntr]] = value;
              }
              result = true;
              break;
            case "sub":
              data[access[cntr]] -= value;
              result = true;
              break;
          }
        } else {
          if (createMissing && data[access[cntr]] === void 0) {
            data[access[cntr]] = {};
          }
          if (data[access[cntr]] === null || typeof data[access[cntr]] !== "object") {
            return false;
          }
          data = data[access[cntr]];
        }
      }
      return result;
    }
    function noop() {
    }
    const identity = (x) => x;
    function assign(tar, src) {
      for (const k in src) tar[k] = src[k];
      return (
        /** @type {T & S} */
        tar
      );
    }
    function run(fn) {
      return fn();
    }
    function blank_object() {
      return /* @__PURE__ */ Object.create(null);
    }
    function run_all(fns) {
      fns.forEach(run);
    }
    function is_function(thing) {
      return typeof thing === "function";
    }
    function safe_not_equal(a, b) {
      return a != a ? b == b : a !== b || a && typeof a === "object" || typeof a === "function";
    }
    let src_url_equal_anchor;
    function src_url_equal(element_src, url) {
      if (element_src === url) return true;
      if (!src_url_equal_anchor) {
        src_url_equal_anchor = document.createElement("a");
      }
      src_url_equal_anchor.href = url;
      return element_src === src_url_equal_anchor.href;
    }
    function is_empty(obj) {
      return Object.keys(obj).length === 0;
    }
    function subscribe(store, ...callbacks) {
      if (store == null) {
        for (const callback of callbacks) {
          callback(void 0);
        }
        return noop;
      }
      const unsub = store.subscribe(...callbacks);
      return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
    }
    function get_store_value(store) {
      let value;
      subscribe(store, (_) => value = _)();
      return value;
    }
    function component_subscribe(component, store, callback) {
      component.$$.on_destroy.push(subscribe(store, callback));
    }
    function create_slot(definition, ctx, $$scope, fn) {
      if (definition) {
        const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);
        return definition[0](slot_ctx);
      }
    }
    function get_slot_context(definition, ctx, $$scope, fn) {
      return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx;
    }
    function get_slot_changes(definition, $$scope, dirty, fn) {
      if (definition[2] && fn) {
        const lets = definition[2](fn(dirty));
        if ($$scope.dirty === void 0) {
          return lets;
        }
        if (typeof lets === "object") {
          const merged = [];
          const len = Math.max($$scope.dirty.length, lets.length);
          for (let i = 0; i < len; i += 1) {
            merged[i] = $$scope.dirty[i] | lets[i];
          }
          return merged;
        }
        return $$scope.dirty | lets;
      }
      return $$scope.dirty;
    }
    function update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn) {
      if (slot_changes) {
        const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
        slot.p(slot_context, slot_changes);
      }
    }
    function get_all_dirty_from_scope($$scope) {
      if ($$scope.ctx.length > 32) {
        const dirty = [];
        const length = $$scope.ctx.length / 32;
        for (let i = 0; i < length; i++) {
          dirty[i] = -1;
        }
        return dirty;
      }
      return -1;
    }
    function exclude_internal_props(props) {
      const result = {};
      for (const k in props) if (k[0] !== "$") result[k] = props[k];
      return result;
    }
    function compute_slots(slots) {
      const result = {};
      for (const key in slots) {
        result[key] = true;
      }
      return result;
    }
    function set_store_value(store, ret, value) {
      store.set(value);
      return ret;
    }
    function action_destroyer(action_result) {
      return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
    }
    const is_client = typeof window !== "undefined";
    let now = is_client ? () => window.performance.now() : () => Date.now();
    let raf = is_client ? (cb) => requestAnimationFrame(cb) : noop;
    const tasks = /* @__PURE__ */ new Set();
    function run_tasks(now2) {
      tasks.forEach((task) => {
        if (!task.c(now2)) {
          tasks.delete(task);
          task.f();
        }
      });
      if (tasks.size !== 0) raf(run_tasks);
    }
    function loop(callback) {
      let task;
      if (tasks.size === 0) raf(run_tasks);
      return {
        promise: new Promise((fulfill) => {
          tasks.add(task = { c: callback, f: fulfill });
        }),
        abort() {
          tasks.delete(task);
        }
      };
    }
    function append(target, node) {
      target.appendChild(node);
    }
    function get_root_for_style(node) {
      if (!node) return document;
      const root = node.getRootNode ? node.getRootNode() : node.ownerDocument;
      if (root && /** @type {ShadowRoot} */
      root.host) {
        return (
          /** @type {ShadowRoot} */
          root
        );
      }
      return node.ownerDocument;
    }
    function append_empty_stylesheet(node) {
      const style_element = element("style");
      style_element.textContent = "/* empty */";
      append_stylesheet(get_root_for_style(node), style_element);
      return style_element.sheet;
    }
    function append_stylesheet(node, style) {
      append(
        /** @type {Document} */
        node.head || node,
        style
      );
      return style.sheet;
    }
    function insert(target, node, anchor) {
      target.insertBefore(node, anchor || null);
    }
    function detach(node) {
      if (node.parentNode) {
        node.parentNode.removeChild(node);
      }
    }
    function destroy_each(iterations, detaching) {
      for (let i = 0; i < iterations.length; i += 1) {
        if (iterations[i]) iterations[i].d(detaching);
      }
    }
    function element(name2) {
      return document.createElement(name2);
    }
    function svg_element(name2) {
      return document.createElementNS("http://www.w3.org/2000/svg", name2);
    }
    function text$1(data) {
      return document.createTextNode(data);
    }
    function space() {
      return text$1(" ");
    }
    function empty() {
      return text$1("");
    }
    function listen(node, event, handler, options) {
      node.addEventListener(event, handler, options);
      return () => node.removeEventListener(event, handler, options);
    }
    function prevent_default(fn) {
      return function(event) {
        event.preventDefault();
        return fn.call(this, event);
      };
    }
    function stop_propagation(fn) {
      return function(event) {
        event.stopPropagation();
        return fn.call(this, event);
      };
    }
    function attr(node, attribute, value) {
      if (value == null) node.removeAttribute(attribute);
      else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value);
    }
    function to_number(value) {
      return value === "" ? null : +value;
    }
    function children(element2) {
      return Array.from(element2.childNodes);
    }
    function set_data(text2, data) {
      data = "" + data;
      if (text2.data === data) return;
      text2.data = /** @type {string} */
      data;
    }
    function set_input_value(input, value) {
      input.value = value == null ? "" : value;
    }
    function set_style(node, key, value, important) {
      if (value == null) {
        node.style.removeProperty(key);
      } else {
        node.style.setProperty(key, value, "");
      }
    }
    function select_option(select, value, mounting) {
      for (let i = 0; i < select.options.length; i += 1) {
        const option = select.options[i];
        if (option.__value === value) {
          option.selected = true;
          return;
        }
      }
      if (!mounting || value !== void 0) {
        select.selectedIndex = -1;
      }
    }
    function select_options(select, value) {
      for (let i = 0; i < select.options.length; i += 1) {
        const option = select.options[i];
        option.selected = ~value.indexOf(option.__value);
      }
    }
    function select_value(select) {
      const selected_option = select.querySelector(":checked");
      return selected_option && selected_option.__value;
    }
    function select_multiple_value(select) {
      return [].map.call(select.querySelectorAll(":checked"), (option) => option.__value);
    }
    function toggle_class(element2, name2, toggle) {
      element2.classList.toggle(name2, !!toggle);
    }
    function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
      return new CustomEvent(type, { detail, bubbles, cancelable });
    }
    class HtmlTag {
      /**
       * @private
       * @default false
       */
      is_svg = false;
      /** parent for creating node */
      e = void 0;
      /** html tag nodes */
      n = void 0;
      /** target */
      t = void 0;
      /** anchor */
      a = void 0;
      constructor(is_svg = false) {
        this.is_svg = is_svg;
        this.e = this.n = null;
      }
      /**
       * @param {string} html
       * @returns {void}
       */
      c(html) {
        this.h(html);
      }
      /**
       * @param {string} html
       * @param {HTMLElement | SVGElement} target
       * @param {HTMLElement | SVGElement} anchor
       * @returns {void}
       */
      m(html, target, anchor = null) {
        if (!this.e) {
          if (this.is_svg)
            this.e = svg_element(
              /** @type {keyof SVGElementTagNameMap} */
              target.nodeName
            );
          else
            this.e = element(
              /** @type {keyof HTMLElementTagNameMap} */
              target.nodeType === 11 ? "TEMPLATE" : target.nodeName
            );
          this.t = target.tagName !== "TEMPLATE" ? target : (
            /** @type {HTMLTemplateElement} */
            target.content
          );
          this.c(html);
        }
        this.i(anchor);
      }
      /**
       * @param {string} html
       * @returns {void}
       */
      h(html) {
        this.e.innerHTML = html;
        this.n = Array.from(
          this.e.nodeName === "TEMPLATE" ? this.e.content.childNodes : this.e.childNodes
        );
      }
      /**
       * @returns {void} */
      i(anchor) {
        for (let i = 0; i < this.n.length; i += 1) {
          insert(this.t, this.n[i], anchor);
        }
      }
      /**
       * @param {string} html
       * @returns {void}
       */
      p(html) {
        this.d();
        this.h(html);
        this.i(this.a);
      }
      /**
       * @returns {void} */
      d() {
        this.n.forEach(detach);
      }
    }
    function construct_svelte_component(component, props) {
      return new component(props);
    }
    const managed_styles = /* @__PURE__ */ new Map();
    let active = 0;
    function hash(str) {
      let hash2 = 5381;
      let i = str.length;
      while (i--) hash2 = (hash2 << 5) - hash2 ^ str.charCodeAt(i);
      return hash2 >>> 0;
    }
    function create_style_information(doc, node) {
      const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
      managed_styles.set(doc, info);
      return info;
    }
    function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {
      const step = 16.666 / duration;
      let keyframes = "{\n";
      for (let p = 0; p <= 1; p += step) {
        const t = a + (b - a) * ease(p);
        keyframes += p * 100 + `%{${fn(t, 1 - t)}}
`;
      }
      const rule = keyframes + `100% {${fn(b, 1 - b)}}
}`;
      const name2 = `__svelte_${hash(rule)}_${uid}`;
      const doc = get_root_for_style(node);
      const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
      if (!rules[name2]) {
        rules[name2] = true;
        stylesheet.insertRule(`@keyframes ${name2} ${rule}`, stylesheet.cssRules.length);
      }
      const animation2 = node.style.animation || "";
      node.style.animation = `${animation2 ? `${animation2}, ` : ""}${name2} ${duration}ms linear ${delay}ms 1 both`;
      active += 1;
      return name2;
    }
    function delete_rule(node, name2) {
      const previous = (node.style.animation || "").split(", ");
      const next = previous.filter(
        name2 ? (anim) => anim.indexOf(name2) < 0 : (anim) => anim.indexOf("__svelte") === -1
        // remove all Svelte animations
      );
      const deleted = previous.length - next.length;
      if (deleted) {
        node.style.animation = next.join(", ");
        active -= deleted;
        if (!active) clear_rules();
      }
    }
    function clear_rules() {
      raf(() => {
        if (active) return;
        managed_styles.forEach((info) => {
          const { ownerNode } = info.stylesheet;
          if (ownerNode) detach(ownerNode);
        });
        managed_styles.clear();
      });
    }
    let current_component;
    function set_current_component(component) {
      current_component = component;
    }
    function get_current_component() {
      if (!current_component) throw new Error("Function called outside component initialization");
      return current_component;
    }
    function onMount(fn) {
      get_current_component().$$.on_mount.push(fn);
    }
    function afterUpdate(fn) {
      get_current_component().$$.after_update.push(fn);
    }
    function onDestroy(fn) {
      get_current_component().$$.on_destroy.push(fn);
    }
    function createEventDispatcher() {
      const component = get_current_component();
      return (type, detail, { cancelable = false } = {}) => {
        const callbacks = component.$$.callbacks[type];
        if (callbacks) {
          const event = custom_event(
            /** @type {string} */
            type,
            detail,
            { cancelable }
          );
          callbacks.slice().forEach((fn) => {
            fn.call(component, event);
          });
          return !event.defaultPrevented;
        }
        return true;
      };
    }
    function setContext(key, context) {
      get_current_component().$$.context.set(key, context);
      return context;
    }
    function getContext(key) {
      return get_current_component().$$.context.get(key);
    }
    const dirty_components = [];
    const binding_callbacks = [];
    let render_callbacks = [];
    const flush_callbacks = [];
    const resolved_promise = /* @__PURE__ */ Promise.resolve();
    let update_scheduled = false;
    function schedule_update() {
      if (!update_scheduled) {
        update_scheduled = true;
        resolved_promise.then(flush);
      }
    }
    function add_render_callback(fn) {
      render_callbacks.push(fn);
    }
    function add_flush_callback(fn) {
      flush_callbacks.push(fn);
    }
    const seen_callbacks = /* @__PURE__ */ new Set();
    let flushidx = 0;
    function flush() {
      if (flushidx !== 0) {
        return;
      }
      const saved_component = current_component;
      do {
        try {
          while (flushidx < dirty_components.length) {
            const component = dirty_components[flushidx];
            flushidx++;
            set_current_component(component);
            update(component.$$);
          }
        } catch (e) {
          dirty_components.length = 0;
          flushidx = 0;
          throw e;
        }
        set_current_component(null);
        dirty_components.length = 0;
        flushidx = 0;
        while (binding_callbacks.length) binding_callbacks.pop()();
        for (let i = 0; i < render_callbacks.length; i += 1) {
          const callback = render_callbacks[i];
          if (!seen_callbacks.has(callback)) {
            seen_callbacks.add(callback);
            callback();
          }
        }
        render_callbacks.length = 0;
      } while (dirty_components.length);
      while (flush_callbacks.length) {
        flush_callbacks.pop()();
      }
      update_scheduled = false;
      seen_callbacks.clear();
      set_current_component(saved_component);
    }
    function update($$) {
      if ($$.fragment !== null) {
        $$.update();
        run_all($$.before_update);
        const dirty = $$.dirty;
        $$.dirty = [-1];
        $$.fragment && $$.fragment.p($$.ctx, dirty);
        $$.after_update.forEach(add_render_callback);
      }
    }
    function flush_render_callbacks(fns) {
      const filtered = [];
      const targets = [];
      render_callbacks.forEach((c) => fns.indexOf(c) === -1 ? filtered.push(c) : targets.push(c));
      targets.forEach((c) => c());
      render_callbacks = filtered;
    }
    let promise;
    function wait() {
      if (!promise) {
        promise = Promise.resolve();
        promise.then(() => {
          promise = null;
        });
      }
      return promise;
    }
    function dispatch(node, direction, kind) {
      node.dispatchEvent(custom_event(`${direction ? "intro" : "outro"}${kind}`));
    }
    const outroing = /* @__PURE__ */ new Set();
    let outros;
    function group_outros() {
      outros = {
        r: 0,
        c: [],
        p: outros
        // parent group
      };
    }
    function check_outros() {
      if (!outros.r) {
        run_all(outros.c);
      }
      outros = outros.p;
    }
    function transition_in(block, local) {
      if (block && block.i) {
        outroing.delete(block);
        block.i(local);
      }
    }
    function transition_out(block, local, detach2, callback) {
      if (block && block.o) {
        if (outroing.has(block)) return;
        outroing.add(block);
        outros.c.push(() => {
          outroing.delete(block);
          if (callback) {
            if (detach2) block.d(1);
            callback();
          }
        });
        block.o(local);
      } else if (callback) {
        callback();
      }
    }
    const null_transition = { duration: 0 };
    function create_in_transition(node, fn, params) {
      const options = { direction: "in" };
      let config = fn(node, params, options);
      let running = false;
      let animation_name;
      let task;
      let uid = 0;
      function cleanup() {
        if (animation_name) delete_rule(node, animation_name);
      }
      function go() {
        const {
          delay = 0,
          duration = 300,
          easing = identity,
          tick = noop,
          css
        } = config || null_transition;
        if (css) animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++);
        tick(0, 1);
        const start_time = now() + delay;
        const end_time = start_time + duration;
        if (task) task.abort();
        running = true;
        add_render_callback(() => dispatch(node, true, "start"));
        task = loop((now2) => {
          if (running) {
            if (now2 >= end_time) {
              tick(1, 0);
              dispatch(node, true, "end");
              cleanup();
              return running = false;
            }
            if (now2 >= start_time) {
              const t = easing((now2 - start_time) / duration);
              tick(t, 1 - t);
            }
          }
          return running;
        });
      }
      let started = false;
      return {
        start() {
          if (started) return;
          started = true;
          delete_rule(node);
          if (is_function(config)) {
            config = config(options);
            wait().then(go);
          } else {
            go();
          }
        },
        invalidate() {
          started = false;
        },
        end() {
          if (running) {
            cleanup();
            running = false;
          }
        }
      };
    }
    function create_out_transition(node, fn, params) {
      const options = { direction: "out" };
      let config = fn(node, params, options);
      let running = true;
      let animation_name;
      const group = outros;
      group.r += 1;
      let original_inert_value;
      function go() {
        const {
          delay = 0,
          duration = 300,
          easing = identity,
          tick = noop,
          css
        } = config || null_transition;
        if (css) animation_name = create_rule(node, 1, 0, duration, delay, easing, css);
        const start_time = now() + delay;
        const end_time = start_time + duration;
        add_render_callback(() => dispatch(node, false, "start"));
        if ("inert" in node) {
          original_inert_value = /** @type {HTMLElement} */
          node.inert;
          node.inert = true;
        }
        loop((now2) => {
          if (running) {
            if (now2 >= end_time) {
              tick(0, 1);
              dispatch(node, false, "end");
              if (!--group.r) {
                run_all(group.c);
              }
              return false;
            }
            if (now2 >= start_time) {
              const t = easing((now2 - start_time) / duration);
              tick(1 - t, t);
            }
          }
          return running;
        });
      }
      if (is_function(config)) {
        wait().then(() => {
          config = config(options);
          go();
        });
      } else {
        go();
      }
      return {
        end(reset) {
          if (reset && "inert" in node) {
            node.inert = original_inert_value;
          }
          if (reset && config.tick) {
            config.tick(1, 0);
          }
          if (running) {
            if (animation_name) delete_rule(node, animation_name);
            running = false;
          }
        }
      };
    }
    function ensure_array_like(array_like_or_iterator) {
      return array_like_or_iterator?.length !== void 0 ? array_like_or_iterator : Array.from(array_like_or_iterator);
    }
    function destroy_block(block, lookup) {
      block.d(1);
      lookup.delete(block.key);
    }
    function outro_and_destroy_block(block, lookup) {
      transition_out(block, 1, 1, () => {
        lookup.delete(block.key);
      });
    }
    function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block2, next, get_context) {
      let o = old_blocks.length;
      let n = list.length;
      let i = o;
      const old_indexes = {};
      while (i--) old_indexes[old_blocks[i].key] = i;
      const new_blocks = [];
      const new_lookup = /* @__PURE__ */ new Map();
      const deltas = /* @__PURE__ */ new Map();
      const updates = [];
      i = n;
      while (i--) {
        const child_ctx = get_context(ctx, list, i);
        const key = get_key(child_ctx);
        let block = lookup.get(key);
        if (!block) {
          block = create_each_block2(key, child_ctx);
          block.c();
        } else {
          updates.push(() => block.p(child_ctx, dirty));
        }
        new_lookup.set(key, new_blocks[i] = block);
        if (key in old_indexes) deltas.set(key, Math.abs(i - old_indexes[key]));
      }
      const will_move = /* @__PURE__ */ new Set();
      const did_move = /* @__PURE__ */ new Set();
      function insert2(block) {
        transition_in(block, 1);
        block.m(node, next);
        lookup.set(block.key, block);
        next = block.first;
        n--;
      }
      while (o && n) {
        const new_block = new_blocks[n - 1];
        const old_block = old_blocks[o - 1];
        const new_key = new_block.key;
        const old_key = old_block.key;
        if (new_block === old_block) {
          next = new_block.first;
          o--;
          n--;
        } else if (!new_lookup.has(old_key)) {
          destroy(old_block, lookup);
          o--;
        } else if (!lookup.has(new_key) || will_move.has(new_key)) {
          insert2(new_block);
        } else if (did_move.has(old_key)) {
          o--;
        } else if (deltas.get(new_key) > deltas.get(old_key)) {
          did_move.add(new_key);
          insert2(new_block);
        } else {
          will_move.add(old_key);
          o--;
        }
      }
      while (o--) {
        const old_block = old_blocks[o];
        if (!new_lookup.has(old_block.key)) destroy(old_block, lookup);
      }
      while (n) insert2(new_blocks[n - 1]);
      run_all(updates);
      return new_blocks;
    }
    function get_spread_update(levels, updates) {
      const update2 = {};
      const to_null_out = {};
      const accounted_for = { $$scope: 1 };
      let i = levels.length;
      while (i--) {
        const o = levels[i];
        const n = updates[i];
        if (n) {
          for (const key in o) {
            if (!(key in n)) to_null_out[key] = 1;
          }
          for (const key in n) {
            if (!accounted_for[key]) {
              update2[key] = n[key];
              accounted_for[key] = 1;
            }
          }
          levels[i] = n;
        } else {
          for (const key in o) {
            accounted_for[key] = 1;
          }
        }
      }
      for (const key in to_null_out) {
        if (!(key in update2)) update2[key] = void 0;
      }
      return update2;
    }
    function get_spread_object(spread_props) {
      return typeof spread_props === "object" && spread_props !== null ? spread_props : {};
    }
    function bind(component, name2, callback) {
      const index = component.$$.props[name2];
      if (index !== void 0) {
        component.$$.bound[index] = callback;
        callback(component.$$.ctx[index]);
      }
    }
    function create_component(block) {
      block && block.c();
    }
    function mount_component(component, target, anchor) {
      const { fragment, after_update } = component.$$;
      fragment && fragment.m(target, anchor);
      add_render_callback(() => {
        const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
        if (component.$$.on_destroy) {
          component.$$.on_destroy.push(...new_on_destroy);
        } else {
          run_all(new_on_destroy);
        }
        component.$$.on_mount = [];
      });
      after_update.forEach(add_render_callback);
    }
    function destroy_component(component, detaching) {
      const $$ = component.$$;
      if ($$.fragment !== null) {
        flush_render_callbacks($$.after_update);
        run_all($$.on_destroy);
        $$.fragment && $$.fragment.d(detaching);
        $$.on_destroy = $$.fragment = null;
        $$.ctx = [];
      }
    }
    function make_dirty(component, i) {
      if (component.$$.dirty[0] === -1) {
        dirty_components.push(component);
        schedule_update();
        component.$$.dirty.fill(0);
      }
      component.$$.dirty[i / 31 | 0] |= 1 << i % 31;
    }
    function init(component, options, instance2, create_fragment2, not_equal, props, append_styles = null, dirty = [-1]) {
      const parent_component = current_component;
      set_current_component(component);
      const $$ = component.$$ = {
        fragment: null,
        ctx: [],
        // state
        props,
        update: noop,
        not_equal,
        bound: blank_object(),
        // lifecycle
        on_mount: [],
        on_destroy: [],
        on_disconnect: [],
        before_update: [],
        after_update: [],
        context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
        // everything else
        callbacks: blank_object(),
        dirty,
        skip_bound: false,
        root: options.target || parent_component.$$.root
      };
      append_styles && append_styles($$.root);
      let ready = false;
      $$.ctx = instance2 ? instance2(component, options.props || {}, (i, ret, ...rest) => {
        const value = rest.length ? rest[0] : ret;
        if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
          if (!$$.skip_bound && $$.bound[i]) $$.bound[i](value);
          if (ready) make_dirty(component, i);
        }
        return ret;
      }) : [];
      $$.update();
      ready = true;
      run_all($$.before_update);
      $$.fragment = create_fragment2 ? create_fragment2($$.ctx) : false;
      if (options.target) {
        if (options.hydrate) {
          const nodes = children(options.target);
          $$.fragment && $$.fragment.l(nodes);
          nodes.forEach(detach);
        } else {
          $$.fragment && $$.fragment.c();
        }
        if (options.intro) transition_in(component.$$.fragment);
        mount_component(component, options.target, options.anchor);
        flush();
      }
      set_current_component(parent_component);
    }
    class SvelteComponent {
      /**
       * ### PRIVATE API
       *
       * Do not use, may change at any time
       *
       * @type {any}
       */
      $$ = void 0;
      /**
       * ### PRIVATE API
       *
       * Do not use, may change at any time
       *
       * @type {any}
       */
      $$set = void 0;
      /** @returns {void} */
      $destroy() {
        destroy_component(this, 1);
        this.$destroy = noop;
      }
      /**
       * @template {Extract<keyof Events, string>} K
       * @param {K} type
       * @param {((e: Events[K]) => void) | null | undefined} callback
       * @returns {() => void}
       */
      $on(type, callback) {
        if (!is_function(callback)) {
          return noop;
        }
        const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []);
        callbacks.push(callback);
        return () => {
          const index = callbacks.indexOf(callback);
          if (index !== -1) callbacks.splice(index, 1);
        };
      }
      /**
       * @param {Partial<Props>} props
       * @returns {void}
       */
      $set(props) {
        if (this.$$set && !is_empty(props)) {
          this.$$.skip_bound = true;
          this.$$set(props);
          this.$$.skip_bound = false;
        }
      }
    }
    const PUBLIC_VERSION = "4";
    const subscriber_queue = [];
    function readable(value, start) {
      return {
        subscribe: writable(value, start).subscribe
      };
    }
    function writable(value, start = noop) {
      let stop;
      const subscribers = /* @__PURE__ */ new Set();
      function set(new_value) {
        if (safe_not_equal(value, new_value)) {
          value = new_value;
          if (stop) {
            const run_queue = !subscriber_queue.length;
            for (const subscriber of subscribers) {
              subscriber[1]();
              subscriber_queue.push(subscriber, value);
            }
            if (run_queue) {
              for (let i = 0; i < subscriber_queue.length; i += 2) {
                subscriber_queue[i][0](subscriber_queue[i + 1]);
              }
              subscriber_queue.length = 0;
            }
          }
        }
      }
      function update2(fn) {
        set(fn(value));
      }
      function subscribe2(run2, invalidate = noop) {
        const subscriber = [run2, invalidate];
        subscribers.add(subscriber);
        if (subscribers.size === 1) {
          stop = start(set, update2) || noop;
        }
        run2(value);
        return () => {
          subscribers.delete(subscriber);
          if (subscribers.size === 0 && stop) {
            stop();
            stop = null;
          }
        };
      }
      return { set, update: update2, subscribe: subscribe2 };
    }
    function derived(stores, fn, initial_value) {
      const single = !Array.isArray(stores);
      const stores_array = single ? [stores] : stores;
      if (!stores_array.every(Boolean)) {
        throw new Error("derived() expects stores as input, got a falsy value");
      }
      const auto = fn.length < 2;
      return readable(initial_value, (set, update2) => {
        let started = false;
        const values = [];
        let pending = 0;
        let cleanup = noop;
        const sync = () => {
          if (pending) {
            return;
          }
          cleanup();
          const result = fn(single ? values[0] : values, set, update2);
          if (auto) {
            set(result);
          } else {
            cleanup = is_function(result) ? result : noop;
          }
        };
        const unsubscribers = stores_array.map(
          (store, i) => subscribe(
            store,
            (value) => {
              values[i] = value;
              pending &= ~(1 << i);
              if (started) {
                sync();
              }
            },
            () => {
              pending |= 1 << i;
            }
          )
        );
        started = true;
        sync();
        return function stop() {
          run_all(unsubscribers);
          cleanup();
          started = false;
        };
      });
    }
    function isWritableStore(store) {
      if (store === null || store === void 0) {
        return false;
      }
      switch (typeof store) {
        case "function":
        case "object":
          return typeof store.subscribe === "function" && typeof store.set === "function" && typeof store.update === "function";
      }
      return false;
    }
    function subscribeIgnoreFirst(store, update2) {
      let firedFirst = false;
      return store.subscribe((value) => {
        if (!firedFirst) {
          firedFirst = true;
        } else {
          update2(value);
        }
      });
    }
    class CrossWindow {
      /**
       * @private
       */
      constructor() {
      }
      // eslint-disable-line no-useless-constructor
      /**
       * Class names for all focusable element types.
       *
       * @type {string[]}
       */
      static #FocusableElementClassNames = [
        "HTMLAnchorElement",
        "HTMLButtonElement",
        "HTMLDetailsElement",
        "HTMLEmbedElement",
        "HTMLIFrameElement",
        "HTMLInputElement",
        "HTMLObjectElement",
        "HTMLSelectElement",
        "HTMLTextAreaElement"
      ];
      /**
       * DOM nodes with defined `ownerDocument` property.
       *
       * @type {Set<number>}
       */
      static #NodesWithOwnerDocument = /* @__PURE__ */ new Set([
        Node.ELEMENT_NODE,
        Node.TEXT_NODE,
        Node.COMMENT_NODE,
        Node.DOCUMENT_FRAGMENT_NODE
      ]);
      // Various UI Event sets for duck typing by constructor name.
      /**
       * Duck typing class names for pointer events.
       *
       * @type {Set<string>}
       */
      static #PointerEventSet = /* @__PURE__ */ new Set(["MouseEvent", "PointerEvent"]);
      /**
       * Duck typing class names for all UIEvents.
       *
       * @type {Set<string>}
       */
      static #UIEventSet = /* @__PURE__ */ new Set([
        "UIEvent",
        "FocusEvent",
        "MouseEvent",
        "WheelEvent",
        "KeyboardEvent",
        "PointerEvent",
        "TouchEvent",
        "InputEvent",
        "CompositionEvent",
        "DragEvent"
      ]);
      /**
       * Duck typing class names for events considered as user input.
       *
       * @type {Set<string>}
       */
      static #UserInputEventSet = /* @__PURE__ */ new Set(["KeyboardEvent", "MouseEvent", "PointerEvent"]);
      /**
       * Internal options used by `#checkDOMInstanceType` when retrieving the Window reference from a Node that doesn't
       * define `ownerDocument`.
       *
       * @type {{throws: boolean}}
       */
      static #optionsInternalCheckDOM = { throws: false };
      // DOM Querying ---------------------------------------------------------------------------------------------------
      /**
       * Convenience method to retrieve the `document.activeElement` value in the current Window context of a DOM Node /
       * Element, EventTarget, Document, or Window.
       *
       * @param {Document | EventTarget | Node | UIEvent | Window}  target - DOM Node / Element, EventTarget, Document,
       *        UIEvent or Window to query.
       *
       * @param {object} [options] - Options.
       *
       * @param {boolean} [options.throws=true] - When `true` and target is invalid throw an exception. If `false` and the
       *        target is invalid `undefined` is returned; default: `true`.
       *
       * @returns {Element | null} Active element or `undefined` when `throws` option is `false` and the target is invalid.
       *
       * @throws {@link TypeError} Target must be a DOM Node / Element, Document, UIEvent, or Window.
       */
      static getActiveElement(target, { throws = true } = {}) {
        if (this.#NodesWithOwnerDocument.has(target?.nodeType)) {
          return target?.ownerDocument?.activeElement ?? null;
        }
        if (this.isUIEvent(target) && isObject(target?.view)) {
          return target?.view?.document?.activeElement ?? null;
        }
        if (isObject(target?.defaultView)) {
          return target?.activeElement ?? null;
        }
        if (isObject(target?.document) && isObject(target?.location)) {
          return target?.document?.activeElement ?? null;
        }
        if (throws) {
          throw new TypeError(`'target' must be a DOM Node / Element, Document, UIEvent, or Window.`);
        }
        return void 0;
      }
      /**
       * Convenience method to retrieve the `Document` value in the current context of a DOM Node / Element, EventTarget,
       * Document, UIEvent, or Window.
       *
       * @param {Document | EventTarget | Node | UIEvent | Window}  target - DOM Node / Element, EventTarget, Document,
       *        UIEvent or Window to query.
       *
       * @param {object} [options] - Options.
       *
       * @param {boolean} [options.throws=true] - When `true` and target is invalid throw an exception. If `false` and the
       *        target is invalid `undefined` is returned; default: `true`.
       *
       * @returns {Document} Active document or `undefined` when `throws` option is `false` and the target is invalid.
       *
       * @throws {@link TypeError} Target must be a DOM Node / Element, Document, UIEvent, or Window.
       */
      static getDocument(target, { throws = true } = {}) {
        if (this.#NodesWithOwnerDocument.has(target?.nodeType)) {
          return target?.ownerDocument;
        }
        if (this.isUIEvent(target) && isObject(target?.view)) {
          return target?.view?.document;
        }
        if (isObject(target?.defaultView)) {
          return target;
        }
        if (isObject(target?.document) && isObject(target?.location)) {
          return target?.document;
        }
        if (throws) {
          throw new TypeError(`'target' must be a DOM Node / Element, Document, UIEvent, or Window.`);
        }
        return void 0;
      }
      /**
       * Convenience method to retrieve the `Window` value in the current context of a DOM Node / Element, EventTarget,
       * Document, or Window.
       *
       * @param {Document | EventTarget | Node | UIEvent | Window}  target - DOM Node / Element, EventTarget, Document,
       *        UIEvent or Window to query.
       *
       * @param {object} [options] - Options.
       *
       * @param {boolean} [options.throws=true] - When `true` and target is invalid throw an exception. If `false` and the
       *        target is invalid `undefined` is returned; default: `true`.
       *
       * @returns {Window} Active window or `undefined` when `throws` option is `false` and the target is invalid.
       *
       * @throws {@link TypeError} Target must be a DOM Node / Element, Document, UIEvent, or Window.
       */
      static getWindow(target, { throws = true } = {}) {
        if (this.#NodesWithOwnerDocument.has(target?.nodeType)) {
          return target.ownerDocument?.defaultView ?? globalThis;
        }
        if (this.isUIEvent(target) && isObject(target?.view)) {
          return target.view ?? globalThis;
        }
        if (isObject(target?.defaultView)) {
          return target.defaultView ?? globalThis;
        }
        if (isObject(target?.document) && isObject(target?.location)) {
          return target;
        }
        if (throws) {
          throw new TypeError(`'target' must be a DOM Node / Element, Document, UIEvent, or Window.`);
        }
        return void 0;
      }
      // ES / Browser API basic prototype tests -------------------------------------------------------------------------
      /**
       * Provides basic prototype string type checking if `target` is a Document.
       *
       * @param {unknown}  target - A potential Document to test.
       *
       * @returns {target is Document} Is `target` a Document.
       */
      static isDocument(target) {
        return isObject(target) && Object.prototype.toString.call(target) === "[object Document]";
      }
      /**
       * Provides basic prototype string type checking if `target` is a Map.
       *
       * @param {unknown}  target - A potential Map to test.
       *
       * @returns {target is Map} Is `target` a Map.
       */
      static isMap(target) {
        return isObject(target) && Object.prototype.toString.call(target) === "[object Map]";
      }
      /**
       * Provides basic prototype string type checking if `target` is a Promise.
       *
       * @param {unknown}  target - A potential Promise to test.
       *
       * @returns {target is Promise} Is `target` a Promise.
       */
      static isPromise(target) {
        return isObject(target) && Object.prototype.toString.call(target) === "[object Promise]";
      }
      /**
       * Provides basic prototype string type checking if `target` is a RegExp.
       *
       * @param {unknown}  target - A potential RegExp to test.
       *
       * @returns {target is RegExp} Is `target` a RegExp.
       */
      static isRegExp(target) {
        return isObject(target) && Object.prototype.toString.call(target) === "[object RegExp]";
      }
      /**
       * Provides basic prototype string type checking if `target` is a Set.
       *
       * @param {unknown}  target - A potential Set to test.
       *
       * @returns {target is Set} Is `target` a Set.
       */
      static isSet(target) {
        return isObject(target) && Object.prototype.toString.call(target) === "[object Set]";
      }
      /**
       * Provides basic prototype string type checking if `target` is a URL.
       *
       * @param {unknown}  target - A potential URL to test.
       *
       * @returns {target is URL} Is `target` a URL.
       */
      static isURL(target) {
        return isObject(target) && Object.prototype.toString.call(target) === "[object URL]";
      }
      /**
       * Provides basic prototype string type checking if `target` is a Window.
       *
       * @param {unknown}  target - A potential Window to test.
       *
       * @returns {target is Window} Is `target` a Window.
       */
      static isWindow(target) {
        return isObject(target) && Object.prototype.toString.call(target) === "[object Window]";
      }
      // DOM Element typing ---------------------------------------------------------------------------------------------
      /**
       * Ensures that the given target is an `instanceof` all known DOM elements that are focusable. Please note that
       * additional checks are required regarding focusable state; use {@link A11yHelper.isFocusable} for a complete check.
       *
       * @param {unknown}  target - Target to test for `instanceof` focusable HTML element.
       *
       * @returns {boolean} Is target an `instanceof` a focusable DOM element.
       */
      static isFocusableHTMLElement(target) {
        for (let cntr = this.#FocusableElementClassNames.length; --cntr >= 0; ) {
          if (this.#checkDOMInstanceType(target, Node.ELEMENT_NODE, this.#FocusableElementClassNames[cntr])) {
            return true;
          }
        }
        return false;
      }
      /**
       * Provides precise type checking if `target` is a DocumentFragment.
       *
       * @param {unknown}  target - A potential DocumentFragment to test.
       *
       * @returns {target is DocumentFragment} Is `target` a DocumentFragment.
       */
      static isDocumentFragment(target) {
        return this.#checkDOMInstanceType(target, Node.DOCUMENT_FRAGMENT_NODE, "DocumentFragment");
      }
      /**
       * Provides precise type checking if `target` is an Element.
       *
       * @param {unknown}  target - A potential Element to test.
       *
       * @returns {target is Element} Is `target` an Element.
       */
      static isElement(target) {
        return this.#checkDOMInstanceType(target, Node.ELEMENT_NODE, "Element");
      }
      /**
       * Provides precise type checking if `target` is a HTMLAnchorElement.
       *
       * @param {unknown}  target - A potential HTMLAnchorElement to test.
       *
       * @returns {target is HTMLAnchorElement} Is `target` a HTMLAnchorElement.
       */
      static isHTMLAnchorElement(target) {
        return this.#checkDOMInstanceType(target, Node.ELEMENT_NODE, "HTMLAnchorElement");
      }
      /**
       * Provides precise type checking if `target` is a HTMLElement.
       *
       * @param {unknown}  target - A potential HTMLElement to test.
       *
       * @returns {target is HTMLElement} Is `target` a HTMLElement.
       */
      static isHTMLElement(target) {
        return this.#checkDOMInstanceType(target, Node.ELEMENT_NODE, "HTMLElement");
      }
      /**
       * Provides precise type checking if `target` is a Node.
       *
       * @param {unknown}  target - A potential Node to test.
       *
       * @returns {target is Node} Is `target` a DOM Node.
       */
      static isNode(target) {
        if (typeof target?.nodeType !== "number") {
          return false;
        }
        if (target instanceof globalThis.Node) {
          return true;
        }
        const activeWindow = this.getWindow(target, this.#optionsInternalCheckDOM);
        const TargetNode = activeWindow?.Node;
        return TargetNode && target instanceof TargetNode;
      }
      /**
       * Provides precise type checking if `target` is a ShadowRoot.
       *
       * @param {unknown}  target - A potential ShadowRoot to test.
       *
       * @returns {target is ShadowRoot} Is `target` a ShadowRoot.
       */
      static isShadowRoot(target) {
        return this.#checkDOMInstanceType(target, Node.DOCUMENT_FRAGMENT_NODE, "ShadowRoot");
      }
      /**
       * Provides precise type checking if `target` is a SVGElement.
       *
       * @param {unknown}  target - A potential SVGElement to test.
       *
       * @returns {target is SVGElement} Is `target` a SVGElement.
       */
      static isSVGElement(target) {
        return this.#checkDOMInstanceType(target, Node.ELEMENT_NODE, "SVGElement");
      }
      // Event typing ---------------------------------------------------------------------------------------------------
      /**
       * Provides basic duck type checking for `Event` signature and optional constructor name(s).
       *
       * @param {unknown}  target - A potential DOM event to test.
       *
       * @param {string | Set<string>} [types] Specific constructor name or Set of constructor names to match.
       *
       * @returns {target is Event} Is `target` an Event with optional constructor name check.
       */
      static isEvent(target, types) {
        if (typeof target?.type !== "string" || typeof target?.defaultPrevented !== "boolean" || typeof target?.stopPropagation !== "function") {
          return false;
        }
        return types !== void 0 ? this.isCtorName(target, types) : true;
      }
      /**
       * Provides basic duck type checking for `Event` signature for standard mouse / pointer events including
       * `MouseEvent` and `PointerEvent`.
       *
       * @param {unknown}  target - A potential DOM event to test.
       *
       * @returns {target is PointerEvent} Is `target` a MouseEvent or PointerEvent.
       */
      static isPointerEvent(target) {
        return this.isEvent(target, this.#PointerEventSet);
      }
      /**
       * Provides basic duck type checking for `Event` signature for all UI events.
       *
       * @param {unknown}  target - A potential DOM event to test.
       *
       * @returns {target is UIEvent} Is `target` a UIEvent.
       * @see https://developer.mozilla.org/en-US/docs/Web/API/UIEvent
       */
      static isUIEvent(target) {
        return this.isEvent(target, this.#UIEventSet);
      }
      /**
       * Provides basic duck type checking for `Event` signature for standard user input events including `KeyboardEvent`,
       * `MouseEvent`, and `PointerEvent`.
       *
       * @param {unknown}  target - A potential DOM event to test.
       *
       * @returns {target is KeyboardEvent | MouseEvent | PointerEvent} Is `target` a Keyboard, MouseEvent, or
       *          PointerEvent.
       */
      static isUserInputEvent(target) {
        return this.isEvent(target, this.#UserInputEventSet);
      }
      // Generic typing -------------------------------------------------------------------------------------------------
      /**
       * Provides basic type checking by constructor name(s) for objects. This can be useful when checking multiple
       * constructor names against a provided Set.
       *
       * @param {unknown}  target - Object to test for constructor name.
       *
       * @param {string | Set<string>} types Specific constructor name or Set of constructor names to match.
       *
       * @returns {boolean} Does the provided object constructor name match the types provided.
       */
      static isCtorName(target, types) {
        if (!isObject(target)) {
          return false;
        }
        if (typeof types === "string" && target?.constructor?.name === types) {
          return true;
        }
        return !!types?.has(target?.constructor?.name);
      }
      // Internal implementation ----------------------------------------------------------------------------------------
      /**
       * Internal generic DOM `instanceof` check. First will attempt to find the class name by `globalThis` falling back
       * to the {@link Window} associated with the DOM node.
       *
       * @param {unknown}  target - Target to test.
       *
       * @param {number}   nodeType - Node type constant.
       *
       * @param {string}   className - DOM class name for instanceof check.
       *
       * @returns {boolean} Is the target the given nodeType and instance of class name.
       */
      static #checkDOMInstanceType(target, nodeType, className) {
        if (!isObject(target)) {
          return false;
        }
        if (target.nodeType !== nodeType) {
          return false;
        }
        const GlobalClass = globalThis[className];
        if (GlobalClass && target instanceof GlobalClass) {
          return true;
        }
        const activeWindow = this.#NodesWithOwnerDocument.has(target.nodeType) ? target?.ownerDocument?.defaultView : this.getWindow(target, this.#optionsInternalCheckDOM);
        const TargetClass = activeWindow?.[className];
        return TargetClass && target instanceof TargetClass;
      }
    }
    function storeGenerator({ storage, serialize = JSON.stringify, deserialize = JSON.parse }) {
      function isSimpleDeriver(deriver) {
        return deriver.length < 2;
      }
      function storageReadable(key, value, start) {
        return {
          subscribe: storageWritable(key, value, start).subscribe
        };
      }
      function storageWritable(key, value, start) {
        function wrap_start(ogSet) {
          return start(function wrap_set(new_value) {
            if (storage) {
              storage.setItem(key, serialize(new_value));
            }
            return ogSet(new_value);
          }, function wrap_update(fn) {
            set(fn(get_store_value(ogStore)));
          });
        }
        if (storage) {
          const storageValue = storage.getItem(key);
          try {
            if (storageValue) {
              value = deserialize(storageValue);
            }
          } catch (err) {
          }
          storage.setItem(key, serialize(value));
        }
        const ogStore = writable(value, start ? wrap_start : void 0);
        function set(new_value) {
          if (storage) {
            storage.setItem(key, serialize(new_value));
          }
          ogStore.set(new_value);
        }
        function update2(fn) {
          set(fn(get_store_value(ogStore)));
        }
        function subscribe2(run2, invalidate) {
          return ogStore.subscribe(run2, invalidate);
        }
        return { set, update: update2, subscribe: subscribe2 };
      }
      function storageDerived(key, stores, fn, initial_value) {
        const single = !Array.isArray(stores);
        const stores_array = single ? [stores] : stores;
        if (storage && storage.getItem(key)) {
          try {
            initial_value = deserialize(storage.getItem(key));
          } catch (err) {
          }
        }
        return storageReadable(key, initial_value, (set, update2) => {
          let inited = false;
          const values = [];
          let pending = 0;
          let cleanup;
          const sync = () => {
            if (pending) {
              return;
            }
            cleanup?.();
            const input = single ? values[0] : values;
            if (isSimpleDeriver(fn)) {
              set(fn(input));
            } else {
              const result = fn(input, set, update2);
              if (typeof result === "function") {
                cleanup = result;
              }
            }
          };
          const unsubscribers = stores_array.map((store, i) => store.subscribe((value) => {
            values[i] = value;
            pending &= ~(1 << i);
            if (inited) {
              sync();
            }
          }, () => {
            pending |= 1 << i;
          }));
          inited = true;
          sync();
          return function stop() {
            unsubscribers.forEach((unsubscriber) => unsubscriber());
            cleanup?.();
          };
        });
      }
      return {
        readable: storageReadable,
        writable: storageWritable,
        derived: storageDerived,
        storage,
        serialize,
        deserialize
      };
    }
    const sessionStores = storeGenerator({ storage: globalThis?.sessionStorage });
    class TJSWebStorage {
      /** @type {import('./').StorageStores} */
      #storageStores;
      /**
       * @type {(Map<string, {
       *    store: import('svelte/store').Writable,
       *    deserialize?: (value: string, ...rest: any[]) => any,
       *    serialize?: (value: any, ...rest: any[]) => string
       * }>)}
       */
      #stores = /* @__PURE__ */ new Map();
      /**
       * @param {import('./').StorageStores} storageStores - Provides a complete set of
       *        storage API store helper functions and the associated storage API instance and serializations strategy.
       */
      constructor(storageStores) {
        this.#storageStores = storageStores;
      }
      /**
       * Creates a new store for the given key.
       *
       * @template T
       *
       * @param {string}   key - Key to lookup in stores map.
       *
       * @param {T}        [defaultValue] - A default value to set for the store.
       *
       * @param {import('./').StorageStores} [storageStores] - Additional store creation options.
       *
       * @returns {import('svelte/store').Writable<T>} The new store.
       */
      #createStore(key, defaultValue = void 0, storageStores) {
        try {
          const value = this.#storageStores.storage.getItem(key);
          if (value !== null) {
            const deserialize = storageStores?.deserialize ?? this.#storageStores.deserialize;
            defaultValue = deserialize(value);
          }
        } catch (err) {
        }
        const writable2 = storageStores?.writable ?? this.#storageStores.writable;
        return writable2(key, defaultValue);
      }
      /**
       * @param {string}   key - Storage key.
       *
       * @returns {(value: string, ...rest: any[]) => any} Deserialize function.
       */
      #getDeserialize(key) {
        return this.#stores.get(key)?.deserialize ?? this.#storageStores.deserialize;
      }
      /**
       * @param {string}   key - Storage key.
       *
       * @returns {(value: any, ...rest: any[]) => string} Serialize function.
       */
      #getSerialize(key) {
        return this.#stores.get(key)?.serialize ?? this.#storageStores.serialize;
      }
      /**
       * Gets a store from the `stores` Map or creates a new store for the key and a given default value.
       *
       * @template T
       *
       * @param {string}   key - Key to lookup in stores map.
       *
       * @param {T}        [defaultValue] - A default value to set for the store.
       *
       * @param {import('./').StorageStores} [storageStores] - Additional store creation options.
       *
       * @returns {import('svelte/store').Writable<T>} The store for the given key.
       */
      #getStore(key, defaultValue = void 0, storageStores) {
        const storeEntry = this.#stores.get(key);
        if (storeEntry) {
          return storeEntry.store;
        }
        const store = this.#createStore(key, defaultValue, storageStores);
        this.#stores.set(key, {
          store,
          deserialize: storageStores?.deserialize,
          serialize: storageStores?.serialize
        });
        return store;
      }
      /**
       * Get value from the storage API.
       *
       * @param {string}   key - Key to lookup in storage API.
       *
       * @param {*}        [defaultValue] - A default value to return if key not present in session storage.
       *
       * @returns {*} Value from session storage or if not defined any default value provided.
       */
      getItem(key, defaultValue) {
        let value = defaultValue;
        const storageValue = this.#storageStores.storage.getItem(key);
        if (storageValue !== null) {
          try {
            value = this.#getDeserialize(key)(storageValue);
          } catch (err) {
            value = defaultValue;
          }
        } else if (defaultValue !== void 0) {
          try {
            const newValue = this.#getSerialize(key)(defaultValue);
            this.#storageStores.storage.setItem(key, newValue);
          } catch (err) {
          }
        }
        return value;
      }
      /**
       * Returns the backing Svelte store for the given key; potentially sets a default value if the key
       * is not already set.
       *
       * @template T
       *
       * @param {string}   key - Key to lookup in storage API.
       *
       * @param {T}        [defaultValue] - A default value to return if key not present in session storage.
       *
       * @param {import('./').StorageStores} [storageStores] - Additional store creation options.
       *
       * @returns {import('svelte/store').Writable<T>} The Svelte store for this key.
       */
      getStore(key, defaultValue, storageStores) {
        return this.#getStore(key, defaultValue, storageStores);
      }
      /**
       * Returns whether a store has already been created for the given key.
       *
       * @param {string}   key - Key to lookup in storage API.
       */
      hasStore(key) {
        return this.#stores.has(key);
      }
      /**
       * Sets the value for the given key in storage API.
       *
       * @param {string}   key - Key to lookup in storage API.
       *
       * @param {*}        value - A value to set for this key.
       */
      setItem(key, value) {
        const store = this.#getStore(key);
        store.set(value);
      }
      /**
       * Convenience method to swap a boolean value stored in storage API updating the associated store value.
       *
       * @param {string}   key - Key to lookup in storage API.
       *
       * @param {boolean}  [defaultValue] - A default value to return if key not present in session storage.
       *
       * @returns {boolean} The boolean swap for the given key.
       */
      swapItemBoolean(key, defaultValue) {
        const store = this.#getStore(key, defaultValue);
        let currentValue = false;
        try {
          currentValue = !!this.#getDeserialize(key)(this.#storageStores.storage.getItem(key));
        } catch (err) {
        }
        const newValue = typeof currentValue === "boolean" ? !currentValue : false;
        store.set(newValue);
        return newValue;
      }
      // Iterators ------------------------------------------------------------------------------------------------------
      /**
       * @template T
       *
       * Returns an iterable for the session storage keys and stores.
       *
       * @param {RegExp} [regex] - Optional regular expression to filter by storage keys.
       *
       * @returns {IterableIterator<[string, import('svelte/store').Writable<T>]>} Iterable iterator of keys and stores.
       * @yields {import('svelte/store').Writable<[string, Writable<T>]>}
       */
      *entries(regex = void 0) {
        if (regex !== void 0 && !CrossWindow.isRegExp(regex)) {
          throw new TypeError(`'regex' is not a RegExp`);
        }
        if (!this.#stores.size) {
          return void 0;
        }
        if (regex) {
          for (const key of this.#stores.keys()) {
            if (regex.test(key)) {
              yield [key, this.getStore(key)];
            }
          }
        } else {
          for (const key of this.#stores.keys()) {
            yield [key, this.getStore(key)];
          }
        }
      }
      /**
       * Returns an iterable for the session storage keys from existing stores.
       *
       * @param {RegExp} [regex] - Optional regular expression to filter by storage keys.
       *
       * @returns {IterableIterator<string>} Iterable iterator of session storage keys.
       * @yields {string}
       */
      *keys(regex = void 0) {
        if (regex !== void 0 && !CrossWindow.isRegExp(regex)) {
          throw new TypeError(`'regex' is not a RegExp`);
        }
        if (!this.#stores.size) {
          return void 0;
        }
        if (regex) {
          for (const key of this.#stores.keys()) {
            if (regex.test(key)) {
              yield key;
            }
          }
        } else {
          for (const key of this.#stores.keys()) {
            yield key;
          }
        }
      }
      /**
       * @template T
       *
       * Returns an iterable for the session storage stores.
       *
       * @param {RegExp} [regex] - Optional regular expression to filter by storage keys.
       *
       * @returns {IterableIterator<import('svelte/store').Writable<T>>} Iterable iterator of stores.
       * @yields {import('svelte/store').Writable<T>}
       */
      *stores(regex = void 0) {
        if (regex !== void 0 && !CrossWindow.isRegExp(regex)) {
          throw new TypeError(`'regex' is not a RegExp`);
        }
        if (!this.#stores.size) {
          return void 0;
        }
        if (regex) {
          for (const key of this.#stores.keys()) {
            if (regex.test(key)) {
              yield this.getStore(key);
            }
          }
        } else {
          for (const key of this.#stores.keys()) {
            yield this.getStore(key);
          }
        }
      }
    }
    class TJSSessionStorage extends TJSWebStorage {
      constructor() {
        super(sessionStores);
      }
    }
    function writableDerived(origins, derive, reflect, initial) {
      var childDerivedSetter, originValues, blockNextDerive = false;
      var reflectOldValues = reflect.length >= 2;
      var wrappedDerive = (got, set, update3) => {
        childDerivedSetter = set;
        if (reflectOldValues) {
          originValues = got;
        }
        if (!blockNextDerive) {
          let returned = derive(got, set, update3);
          if (derive.length < 2) {
            set(returned);
          } else {
            return returned;
          }
        }
        blockNextDerive = false;
      };
      var childDerived = derived(origins, wrappedDerive, initial);
      var singleOrigin = !Array.isArray(origins);
      function doReflect(reflecting) {
        var setWith = reflect(reflecting, originValues);
        if (singleOrigin) {
          blockNextDerive = true;
          origins.set(setWith);
        } else {
          setWith.forEach((value, i) => {
            blockNextDerive = true;
            origins[i].set(value);
          });
        }
        blockNextDerive = false;
      }
      var tryingSet = false;
      function update2(fn) {
        var isUpdated, mutatedBySubscriptions, oldValue, newValue;
        if (tryingSet) {
          newValue = fn(get_store_value(childDerived));
          childDerivedSetter(newValue);
          return;
        }
        var unsubscribe = childDerived.subscribe((value) => {
          if (!tryingSet) {
            oldValue = value;
          } else if (!isUpdated) {
            isUpdated = true;
          } else {
            mutatedBySubscriptions = true;
          }
        });
        newValue = fn(oldValue);
        tryingSet = true;
        childDerivedSetter(newValue);
        unsubscribe();
        tryingSet = false;
        if (mutatedBySubscriptions) {
          newValue = get_store_value(childDerived);
        }
        if (isUpdated) {
          doReflect(newValue);
        }
      }
      return {
        subscribe: childDerived.subscribe,
        set(value) {
          update2(() => value);
        },
        update: update2
      };
    }
    function propertyStore(origin, propName) {
      if (!Array.isArray(propName)) {
        return writableDerived(
          origin,
          (object) => object[propName],
          (reflecting, object) => {
            object[propName] = reflecting;
            return object;
          }
        );
      } else {
        let props = propName.concat();
        return writableDerived(
          origin,
          (value) => {
            for (let i = 0; i < props.length; ++i) {
              value = value[props[i]];
            }
            return value;
          },
          (reflecting, object) => {
            let target = object;
            for (let i = 0; i < props.length - 1; ++i) {
              target = target[props[i]];
            }
            target[props[props.length - 1]] = reflecting;
            return object;
          }
        );
      }
    }
    class APIConfig {
      constructor() {
      }
      /**
       * Validates `config` argument whether it is a valid {@link TJSSvelte.Config.Dynamic} or
       * {@link TJSSvelte.Config.Standard} configuration object suitable for parsing by
       * {@link TJSSvelte.API.Config.parseConfig}.
       *
       * @param config - The potential config object to validate.
       *
       * @param [options] - Options.
       *
       * @param [options.raiseException=false] - If validation fails raise an exception.
       *
       * @returns Is the config a valid TJSSvelte.Config.Dynamic or TJSSvelte.Config.Standard configuration object.
       *
       * @throws {TypeError}  Any validation error when `raiseException` is enabled.
       */
      static isConfig(config, { raiseException = false } = {}) {
        if (!isObject(config)) {
          if (raiseException) {
            throw new TypeError(`TJSSvelte.config.isConfig error: 'config' is not an object.`);
          }
          return false;
        }
        if (!TJSSvelte.util.isComponent(config.class)) {
          if (raiseException) {
            throw new TypeError(`TJSSvelte.config.isConfig error: 'config.class' is not a Svelte component constructor.`);
          }
          return false;
        }
        return true;
      }
      /**
       * Validates `config` argument whether it is a valid {@link TJSSvelte.Config.Embed} configuration object
       * suitable for directly mounting via the `<svelte:component>` directive.
       *
       * @param config - The potential config object to validate.
       *
       * @param [options] - Options.
       *
       * @param [options.raiseException=false] - If validation fails raise an exception.
       *
       * @returns Is the config a valid TJSSvelte.Config.Embed configuration object.
       *
       * @throws {TypeError}  Any validation error when `raiseException` is enabled.
       */
      static isConfigEmbed(config, { raiseException = false } = {}) {
        if (!isObject(config)) {
          if (raiseException) {
            throw new TypeError(`TJSSvelte.config.isConfigEmbed error: 'config' is not an object.`);
          }
          return false;
        }
        if (!TJSSvelte.util.isComponent(config.class)) {
          if (raiseException) {
            throw new TypeError(`TJSSvelte.config.isConfigEmbed error: 'config.class' is not a Svelte component constructor.`);
          }
          return false;
        }
        if (config.props !== void 0 && !isObject(config.props)) {
          if (raiseException) {
            throw new TypeError(`TJSSvelte.config.isConfigEmbed error: 'config.props' is not an object.`);
          }
          return false;
        }
        return true;
      }
      /**
       * Parses a TyphonJS Svelte dynamic or standard config object ensuring that the class specified is a Svelte
       * component, loads any dynamic defined `context` or `props` preparing the config object for loading into the
       * Svelte component.
       *
       * @param config - Svelte config object.
       *
       * @param [options] - Options.
       *
       * @param [options.contextExternal] - When true any context data provided will be loaded into `#external`
       *        context separating it from any internal context created by the component.
       *
       * @param [options.thisArg] - `This` reference to set for invoking any `context` or `props` defined as
       *        functions for {@link Config.Dynamic} configuration objects.
       *
       * @returns The processed Svelte config object turned with parsed `props` & `context` converted into the format
       *          supported by Svelte.
       */
      static parseConfig(config, { contextExternal = false, thisArg = void 0 } = {}) {
        if (!isObject(config)) {
          throw new TypeError(`TJSSvelte.config.parseConfig - 'config' is not an object:
${JSON.stringify(config)}.`);
        }
        if (!TJSSvelte.util.isComponent(config.class)) {
          throw new TypeError(`TJSSvelte.config.parseConfig - 'class' is not a Svelte component constructor for config:
${JSON.stringify(config)}.`);
        }
        if (config.hydrate !== void 0 && typeof config.hydrate !== "boolean") {
          throw new TypeError(`TJSSvelte.config.parseConfig - 'hydrate' is not a boolean for config:
${JSON.stringify(config)}.`);
        }
        if (config.intro !== void 0 && typeof config.intro !== "boolean") {
          throw new TypeError(`TJSSvelte.config.parseConfig - 'intro' is not a boolean for config:
${JSON.stringify(config)}.`);
        }
        if (config.target !== void 0 && !CrossWindow.isElement(config.target) && !CrossWindow.isShadowRoot(config.target) && !CrossWindow.isDocumentFragment(config.target)) {
          throw new TypeError(`TJSSvelte.config.parseConfig - 'target' is not a Element, ShadowRoot, or DocumentFragment for config:
${JSON.stringify(config)}.`);
        }
        if (config.anchor !== void 0 && !CrossWindow.isElement(config.anchor) && !CrossWindow.isShadowRoot(config.anchor) && !CrossWindow.isDocumentFragment(config.anchor)) {
          throw new TypeError(`TJSSvelte.config.parseConfig - 'anchor' is not a string, Element for config:
${JSON.stringify(config)}.`);
        }
        if (config.context !== void 0 && typeof config.context !== "function" && !isObject(config.context)) {
          throw new TypeError(`TJSSvelte.config.parseConfig - 'context' is not a function or object for config:
${JSON.stringify(config)}.`);
        }
        const svelteConfig = { ...config };
        let context = {};
        if (typeof svelteConfig.context === "function") {
          const contextFunc = svelteConfig.context;
          delete svelteConfig.context;
          const result = contextFunc.call(thisArg);
          if (isObject(result)) {
            context = { ...result };
          } else {
            throw new Error(`TJSSvelte.config.parseConfig - 'context' is a function that did not return an object for config:
${JSON.stringify(config)}`);
          }
        } else if (isObject(svelteConfig.context)) {
          context = svelteConfig.context;
          delete svelteConfig.context;
        }
        svelteConfig.props = this.#processProps(svelteConfig.props, thisArg, config);
        if (contextExternal) {
          svelteConfig.context = /* @__PURE__ */ new Map();
          svelteConfig.context.set("#external", context);
        } else {
          svelteConfig.context = new Map(Object.entries(context));
        }
        return svelteConfig;
      }
      // Internal implementation ----------------------------------------------------------------------------------------
      /**
       * Processes Svelte props. Potentially props can be a function to invoke with `thisArg`.
       *
       * @param props - Svelte props.
       *
       * @param thisArg - `This` reference to set for invoking any props function.
       *
       * @param config - Svelte config
       *
       * @returns Svelte props.
       */
      static #processProps(props, thisArg, config) {
        if (typeof props === "function") {
          const result = props.call(thisArg);
          if (isObject(result)) {
            return result;
          } else {
            throw new Error(`TJSSvelte.config.parseConfig - 'props' is a function that did not return an object for config:
${JSON.stringify(config)}`);
          }
        } else if (isObject(props)) {
          return props;
        } else if (props !== void 0) {
          throw new Error(`TJSSvelte.config.parseConfig - 'props' is not a function or an object for config:
${JSON.stringify(config)}`);
        }
        return {};
      }
    }
    Object.seal(APIConfig);
    class APIUtil {
      constructor() {
      }
      /**
       * Provides basic duck typing to determine if the provided function is a constructor function for a Svelte
       * component.
       *
       * @param comp - Data to check as a Svelte component.
       *
       * @returns Whether basic duck typing succeeds.
       */
      static isComponent(comp) {
        if (comp === null || comp === void 0 || typeof comp !== "function") {
          return false;
        }
        const prototypeName = comp?.prototype?.constructor?.name;
        if (typeof prototypeName === "string" && (prototypeName.startsWith("Proxy<") || prototypeName === "ProxyComponent")) {
          return true;
        }
        return typeof window !== "undefined" ? typeof comp?.prototype?.$destroy === "function" && typeof comp?.prototype?.$on === "function" : (
          // client-side
          typeof comp?.prototype?.render === "function"
        );
      }
      /**
       * Provides basic duck typing to determine if the provided object is a HMR ProxyComponent instance or class.
       *
       * @param {unknown}  comp - Data to check as a HMR proxy component.
       *
       * @returns {boolean} Whether basic duck typing succeeds.
       */
      static isHMRProxy(comp) {
        const instanceName = comp?.constructor?.name;
        if (typeof instanceName === "string" && (instanceName.startsWith("Proxy<") || instanceName === "ProxyComponent")) {
          return true;
        }
        const prototypeName = comp?.prototype?.constructor?.name;
        return typeof prototypeName === "string" && (prototypeName.startsWith("Proxy<") || prototypeName === "ProxyComponent");
      }
      /**
       * Runs outro transition then destroys Svelte component.
       *
       * Workaround for https://github.com/sveltejs/svelte/issues/4056
       *
       * @param instance - A Svelte component.
       *
       * @returns Promise returned after outro transition completed and component destroyed.
       */
      static async outroAndDestroy(instance2) {
        if (instance2 === void 0 || instance2 === null) {
          return Promise.resolve();
        }
        return new Promise((resolve) => {
          if (instance2?.$$?.fragment && instance2?.$$?.fragment?.o) {
            group_outros();
            transition_out(instance2.$$.fragment, 0, 0, () => {
              instance2?.$destroy?.();
              resolve();
            });
            check_outros();
          } else {
            instance2?.$destroy?.();
            resolve();
          }
        });
      }
    }
    Object.seal(APIUtil);
    class TJSSvelte {
      constructor() {
      }
      static get config() {
        return APIConfig;
      }
      /**
       * @returns The utility API.
       */
      static get util() {
        return APIUtil;
      }
    }
    Object.seal(TJSSvelte);
    function backInOut(t) {
      const s = 1.70158 * 1.525;
      if ((t *= 2) < 1) return 0.5 * (t * t * ((s + 1) * t - s));
      return 0.5 * ((t -= 2) * t * ((s + 1) * t + s) + 2);
    }
    function backIn(t) {
      const s = 1.70158;
      return t * t * ((s + 1) * t - s);
    }
    function backOut(t) {
      const s = 1.70158;
      return --t * t * ((s + 1) * t + s) + 1;
    }
    function bounceOut(t) {
      const a = 4 / 11;
      const b = 8 / 11;
      const c = 9 / 10;
      const ca = 4356 / 361;
      const cb = 35442 / 1805;
      const cc = 16061 / 1805;
      const t2 = t * t;
      return t < a ? 7.5625 * t2 : t < b ? 9.075 * t2 - 9.9 * t + 3.4 : t < c ? ca * t2 - cb * t + cc : 10.8 * t * t - 20.52 * t + 10.72;
    }
    function bounceInOut(t) {
      return t < 0.5 ? 0.5 * (1 - bounceOut(1 - t * 2)) : 0.5 * bounceOut(t * 2 - 1) + 0.5;
    }
    function bounceIn(t) {
      return 1 - bounceOut(1 - t);
    }
    function circInOut(t) {
      if ((t *= 2) < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1);
      return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
    }
    function circIn(t) {
      return 1 - Math.sqrt(1 - t * t);
    }
    function circOut(t) {
      return Math.sqrt(1 - --t * t);
    }
    function cubicInOut(t) {
      return t < 0.5 ? 4 * t * t * t : 0.5 * Math.pow(2 * t - 2, 3) + 1;
    }
    function cubicIn(t) {
      return t * t * t;
    }
    function cubicOut(t) {
      const f = t - 1;
      return f * f * f + 1;
    }
    function elasticInOut(t) {
      return t < 0.5 ? 0.5 * Math.sin(13 * Math.PI / 2 * 2 * t) * Math.pow(2, 10 * (2 * t - 1)) : 0.5 * Math.sin(-13 * Math.PI / 2 * (2 * t - 1 + 1)) * Math.pow(2, -10 * (2 * t - 1)) + 1;
    }
    function elasticIn(t) {
      return Math.sin(13 * t * Math.PI / 2) * Math.pow(2, 10 * (t - 1));
    }
    function elasticOut(t) {
      return Math.sin(-13 * (t + 1) * Math.PI / 2) * Math.pow(2, -10 * t) + 1;
    }
    function expoInOut(t) {
      return t === 0 || t === 1 ? t : t < 0.5 ? 0.5 * Math.pow(2, 20 * t - 10) : -0.5 * Math.pow(2, 10 - t * 20) + 1;
    }
    function expoIn(t) {
      return t === 0 ? t : Math.pow(2, 10 * (t - 1));
    }
    function expoOut(t) {
      return t === 1 ? t : 1 - Math.pow(2, -10 * t);
    }
    function quadInOut(t) {
      t /= 0.5;
      if (t < 1) return 0.5 * t * t;
      t--;
      return -0.5 * (t * (t - 2) - 1);
    }
    function quadIn(t) {
      return t * t;
    }
    function quadOut(t) {
      return -t * (t - 2);
    }
    function quartInOut(t) {
      return t < 0.5 ? 8 * Math.pow(t, 4) : -8 * Math.pow(t - 1, 4) + 1;
    }
    function quartIn(t) {
      return Math.pow(t, 4);
    }
    function quartOut(t) {
      return Math.pow(t - 1, 3) * (1 - t) + 1;
    }
    function quintInOut(t) {
      if ((t *= 2) < 1) return 0.5 * t * t * t * t * t;
      return 0.5 * ((t -= 2) * t * t * t * t + 2);
    }
    function quintIn(t) {
      return t * t * t * t * t;
    }
    function quintOut(t) {
      return --t * t * t * t * t + 1;
    }
    function sineInOut(t) {
      return -0.5 * (Math.cos(Math.PI * t) - 1);
    }
    function sineIn(t) {
      const v = Math.cos(t * Math.PI * 0.5);
      if (Math.abs(v) < 1e-14) return 1;
      else return 1 - v;
    }
    function sineOut(t) {
      return Math.sin(t * Math.PI / 2);
    }
    const svelteEasingFunc = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
      __proto__: null,
      backIn,
      backInOut,
      backOut,
      bounceIn,
      bounceInOut,
      bounceOut,
      circIn,
      circInOut,
      circOut,
      cubicIn,
      cubicInOut,
      cubicOut,
      elasticIn,
      elasticInOut,
      elasticOut,
      expoIn,
      expoInOut,
      expoOut,
      linear: identity,
      quadIn,
      quadInOut,
      quadOut,
      quartIn,
      quartInOut,
      quartOut,
      quintIn,
      quintInOut,
      quintOut,
      sineIn,
      sineInOut,
      sineOut
    }, Symbol.toStringTag, { value: "Module" }));
    const easingFunc = svelteEasingFunc;
    function getEasingFunc(easingRef, options) {
      if (typeof easingRef === "function") {
        return easingRef;
      }
      const easingFn = easingFunc[easingRef];
      return easingFn ? easingFn : easingFunc[options?.default ?? "linear"];
    }
    class A11yHelper {
      /**
       * You can set global focus debugging enabled by setting `A11yHelper.debug = true`.
       *
       * @type {boolean}
       */
      static #globalDebug = false;
      /**
       * @returns {boolean} Global debugging enabled.
       */
      static get debug() {
        return this.#globalDebug;
      }
      /**
       * @param {boolean}  debug - Global debug enabled
       */
      static set debug(debug2) {
        if (typeof debug2 !== "boolean") {
          throw new TypeError(`'debug' is not a boolean.`);
        }
        this.#globalDebug = debug2;
      }
      /**
       * Runs a media query to determine if the user / OS configuration is set up for reduced motion / animation.
       *
       * @returns {boolean} User prefers reduced motion.
       */
      static get prefersReducedMotion() {
        return globalThis?.matchMedia("(prefers-reduced-motion: reduce)")?.matches ?? false;
      }
      /**
       * Apply focus to the HTMLElement / SVGElement targets in a given A11yFocusSource data object. An iterable list
       * `options.focusEl` can contain HTMLElement / SVGElements or selector strings. If multiple focus targets are
       * provided in a list then the first valid target found will be focused. If focus target is a string then a lookup
       * via `document.querySelector` is performed. In this case you should provide a unique selector for the desired
       * focus target.
       *
       * Note: The body of this method is postponed to the next clock tick to allow any changes in the DOM to occur that
       * might alter focus targets before applying.
       *
       * @param {A11yFocusSource | { focusSource: A11yFocusSource }}   options - The focus options instance to apply.
       */
      static applyFocusSource(options) {
        if (!isObject(options)) {
          return;
        }
        const focusOpts = isObject(options?.focusSource) ? options.focusSource : options;
        setTimeout(() => {
          const debug2 = typeof focusOpts.debug === "boolean" ? this.debug || focusOpts.debug : this.debug;
          if (isIterable(focusOpts.focusEl)) {
            if (debug2) {
              console.debug(
                `A11yHelper.applyFocusSource debug - Attempting to apply focus target: `,
                focusOpts.focusEl
              );
            }
            for (const target of focusOpts.focusEl) {
              if (target?.nodeType === Node.ELEMENT_NODE && target?.isConnected) {
                target?.focus();
                if (debug2) {
                  console.debug(`A11yHelper.applyFocusSource debug - Applied focus to target: `, target);
                }
                break;
              } else if (typeof target === "string") {
                const element2 = document.querySelector(target);
                if (element2?.nodeType === Node.ELEMENT_NODE && element2?.isConnected) {
                  element2?.focus();
                  if (debug2) {
                    console.debug(`A11yHelper.applyFocusSource debug - Applied focus to target: `, element2);
                  }
                  break;
                } else if (debug2) {
                  console.debug(`A11yHelper.applyFocusSource debug - Could not query selector: `, target);
                }
              }
            }
          } else if (debug2) {
            console.debug(`A11yHelper.applyFocusSource debug - No focus targets defined.`);
          }
        }, 0);
      }
      /**
       * Returns first focusable element within a specified element.
       *
       * @param {Element | Document} [element=document] - Optional element to start query.
       *
       * @param {FocusableElementOptions} [options] - Optional parameters.
       *
       * @returns {FocusableElement} First focusable child element.
       */
      static getFirstFocusableElement(element2 = document, options) {
        const focusableElements = this.getFocusableElements(element2, options);
        return focusableElements.length > 0 ? focusableElements[0] : void 0;
      }
      /**
       * Returns all focusable elements within a specified element.
       *
       * @param {Element | Document} [element=document] Optional element to start query.
       *
       * @param {FocusableElementOptions} [options] - Optional parameters.
       *
       * @returns {Array<FocusableElement>} Child keyboard focusable elements.
       */
      static getFocusableElements(element2 = document, {
        anchorHref = true,
        ignoreClasses,
        ignoreElements,
        parentHidden = false,
        selectors
      } = {}) {
        if (element2?.nodeType !== Node.ELEMENT_NODE && element2?.nodeType !== Node.DOCUMENT_NODE) {
          throw new TypeError(`'element' is not a HTMLElement, SVGElement, or Document instance.`);
        }
        if (typeof anchorHref !== "boolean") {
          throw new TypeError(`'anchorHref' is not a boolean.`);
        }
        if (ignoreClasses !== void 0 && !isIterable(ignoreClasses)) {
          throw new TypeError(`'ignoreClasses' is not an iterable list.`);
        }
        if (ignoreElements !== void 0 && !CrossWindow.isSet(ignoreElements)) {
          throw new TypeError(`'ignoreElements' is not a Set.`);
        }
        if (selectors !== void 0 && typeof selectors !== "string") {
          throw new TypeError(`'selectors' is not a string.`);
        }
        const selectorQuery = selectors ?? this.#getFocusableSelectors(anchorHref);
        let allElements = [...element2.querySelectorAll(selectorQuery)];
        if (ignoreElements && ignoreClasses) {
          allElements = allElements.filter((el) => {
            let hasIgnoreClass = false;
            for (const ignoreClass of ignoreClasses) {
              if (el.classList.contains(ignoreClass)) {
                hasIgnoreClass = true;
                break;
              }
            }
            return !hasIgnoreClass && !ignoreElements.has(el) && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
          });
        } else if (ignoreClasses) {
          allElements = allElements.filter((el) => {
            let hasIgnoreClass = false;
            for (const ignoreClass of ignoreClasses) {
              if (el.classList.contains(ignoreClass)) {
                hasIgnoreClass = true;
                break;
              }
            }
            return !hasIgnoreClass && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
          });
        } else if (ignoreElements) {
          allElements = allElements.filter((el) => {
            return !ignoreElements.has(el) && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
          });
        } else {
          allElements = allElements.filter((el) => {
            return el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
          });
        }
        if (parentHidden) {
          allElements = allElements.filter((el) => {
            return !this.isParentHidden(el, element2);
          });
        }
        return allElements;
      }
      /**
       * Returns the default focusable selectors query.
       *
       * @param {boolean}  [anchorHref=true] - When true anchors must have an HREF.
       *
       * @returns {string} Focusable selectors for `querySelectorAll`.
       */
      static #getFocusableSelectors(anchorHref = true) {
        return `button, [contenteditable=""], [contenteditable="true"], details summary:not([tabindex="-1"]), embed, a${anchorHref ? "[href]" : ""}, iframe, object, input:not([type=hidden]), select, textarea, [tabindex]:not([tabindex="-1"])`;
      }
      /**
       * Gets a A11yFocusSource object from the given DOM event allowing for optional X / Y screen space overrides.
       * Browsers (Firefox / Chrome) forwards a mouse event for the context menu keyboard button. Provides detection of
       * when the context menu event is from the keyboard. Firefox as of (1/23) does not provide the correct screen space
       * coordinates, so for keyboard context menu presses coordinates are generated from the centroid point of the
       * element.
       *
       * A default fallback element or selector string may be provided to provide the focus target. If the event comes from
       * the keyboard however the source focused element is inserted as the target with the fallback value appended to the
       * list of focus targets. When A11yFocusSource is applied by {@link A11yHelper.applyFocusSource} the target focus
       * list is iterated through until a connected target is found and focus applied.
       *
       * @param {object} options - Options
       *
       * @param {KeyboardEvent | MouseEvent}   [options.event] - The source DOM event.
       *
       * @param {boolean} [options.debug] - When true {@link A11yHelper.applyFocusSource} logs focus target data.
       *
       * @param {FocusableElement | string} [options.focusEl] - A specific HTMLElement / SVGElement or selector
       *        string as the focus target.
       *
       * @param {number}   [options.x] - Used when an event isn't provided; integer of event source in screen space.
       *
       * @param {number}   [options.y] - Used when an event isn't provided; integer of event source in screen space.
       *
       * @returns {A11yFocusSource} A A11yFocusSource object.
       *
       * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1426671
       * @see https://bugzilla.mozilla.org/show_bug.cgi?id=314314
       *
       * @privateRemarks
       * TODO: Evaluate / test against touch input devices.
       */
      static getFocusSource({ event, x, y, focusEl, debug: debug2 = false }) {
        if (focusEl !== void 0 && !this.isFocusSource(focusEl)) {
          throw new TypeError(
            `A11yHelper.getFocusSource error: 'focusEl' is not a HTMLElement, SVGElement, or string.`
          );
        }
        if (debug2 !== void 0 && typeof debug2 !== "boolean") {
          throw new TypeError(`A11yHelper.getFocusSource error: 'debug' is not a boolean.`);
        }
        const debugEnabled = typeof debug2 === "boolean" ? this.debug || debug2 : this.debug;
        if (event === void 0) {
          if (typeof x !== "number") {
            throw new TypeError(`A11yHelper.getFocusSource error: 'event' not defined and 'x' is not a number.`);
          }
          if (typeof y !== "number") {
            throw new TypeError(`A11yHelper.getFocusSource error: 'event' not defined and 'y' is not a number.`);
          }
          const result2 = {
            debug: debug2,
            focusEl: focusEl !== void 0 ? [focusEl] : void 0,
            x,
            y
          };
          if (debugEnabled) {
            console.debug(`A11yHelper.getFocusSource debug: generated 'focusSource' without event: `, result2);
          }
          return result2;
        }
        if (event !== void 0 && !CrossWindow.isUserInputEvent(event)) {
          throw new TypeError(
            `A11yHelper.getFocusSource error: 'event' is not a KeyboardEvent, MouseEvent, or PointerEvent.`
          );
        }
        if (x !== void 0 && !Number.isInteger(x)) {
          throw new TypeError(`A11yHelper.getFocusSource error: 'x' is not a number.`);
        }
        if (y !== void 0 && !Number.isInteger(y)) {
          throw new TypeError(`A11yHelper.getFocusSource error: 'y' is not a number.`);
        }
        let targetEl;
        if (event) {
          if (A11yHelper.isFocusable(event.target)) {
            targetEl = event.target;
            if (debugEnabled) {
              console.debug(`A11yHelper.getFocusSource debug: 'targetEl' set to event.target: `, targetEl);
            }
          } else if (A11yHelper.isFocusable(event.currentTarget)) {
            targetEl = event.currentTarget;
            if (debugEnabled) {
              console.debug(`A11yHelper.getFocusSource debug: 'targetEl' set to event.currentTarget: `, targetEl);
            }
          } else {
            if (debugEnabled) {
              console.debug(
                `A11yHelper.getFocusSource debug: 'event.target' / 'event.currentTarget' are not focusable.`
              );
              console.debug(`A11yHelper.getFocusSource debug: 'event.target': `, event.target);
              console.debug(`A11yHelper.getFocusSource debug: 'event.currentTarget': `, event.currentTarget);
            }
          }
          if (targetEl) {
            if (targetEl?.nodeType !== Node.ELEMENT_NODE && typeof targetEl?.focus !== "function") {
              throw new TypeError(`A11yHelper.getFocusSource error: 'targetEl' is not an HTMLElement or SVGElement.`);
            }
          }
        }
        const result = { debug: debug2 };
        if (CrossWindow.isPointerEvent(event)) {
          if (event?.button !== 2 && event.type === "contextmenu") {
            const rectTarget = targetEl ?? event.target;
            const rect = rectTarget.getBoundingClientRect();
            result.source = "keyboard";
            result.x = x ?? rect.left + rect.width / 2;
            result.y = y ?? rect.top + rect.height / 2;
            result.focusEl = targetEl ? [targetEl] : [];
            if (focusEl) {
              result.focusEl.push(focusEl);
            }
          } else {
            result.source = "pointer";
            result.x = x ?? event.pageX;
            result.y = y ?? event.pageY;
            result.focusEl = targetEl ? [targetEl] : [];
            if (focusEl) {
              result.focusEl.push(focusEl);
            }
          }
        } else {
          const rectTarget = targetEl ?? event?.target;
          if (rectTarget) {
            const rect = rectTarget.getBoundingClientRect();
            result.source = "keyboard";
            result.x = x ?? rect.left + rect.width / 2;
            result.y = y ?? rect.top + rect.height / 2;
            result.focusEl = targetEl ? [targetEl] : [];
          }
          if (focusEl) {
            result.focusEl.push(focusEl);
          }
        }
        if (debugEnabled) {
          console.debug(`A11yHelper.getFocusSource debug: generated 'focusSource' with event: `, result);
        }
        return result;
      }
      /**
       * Returns first focusable element within a specified element.
       *
       * @param {Element | Document} [element=document] - Optional element to start query.
       *
       * @param {FocusableElementOptions} [options] - Optional parameters.
       *
       * @returns {FocusableElement} Last focusable child element.
       */
      static getLastFocusableElement(element2 = document, options) {
        const focusableElements = this.getFocusableElements(element2, options);
        return focusableElements.length > 0 ? focusableElements[focusableElements.length - 1] : void 0;
      }
      /**
       * Tests if the given element is focusable.
       *
       * @param {unknown} el - Element to test.
       *
       * @param {object} [options] - Optional parameters.
       *
       * @param {boolean} [options.anchorHref=true] - When true anchors must have an HREF.
       *
       * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
       *
       * @returns {boolean} Element is focusable.
       */
      static isFocusable(el, { anchorHref = true, ignoreClasses } = {}) {
        if (el === void 0 || el === null || el?.hidden || !el?.isConnected || el?.nodeType !== Node.ELEMENT_NODE || typeof el?.focus !== "function") {
          return false;
        }
        if (typeof anchorHref !== "boolean") {
          throw new TypeError(`'anchorHref' is not a boolean.`);
        }
        if (ignoreClasses !== void 0 && !isIterable(ignoreClasses)) {
          throw new TypeError(`'ignoreClasses' is not an iterable list.`);
        }
        const contenteditableAttr = el.getAttribute("contenteditable");
        const contenteditableFocusable = typeof contenteditableAttr === "string" && (contenteditableAttr === "" || contenteditableAttr === "true");
        const tabindexAttr = globalThis.parseInt(el.getAttribute("tabindex"));
        const tabindexFocusable = Number.isInteger(tabindexAttr) && tabindexAttr >= 0;
        if (contenteditableFocusable || tabindexFocusable || CrossWindow.isFocusableHTMLElement(el)) {
          if (anchorHref && !tabindexFocusable && CrossWindow.isHTMLAnchorElement(el) && typeof el.getAttribute("href") !== "string") {
            return false;
          }
          return el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
        }
        return false;
      }
      /**
       * Convenience method to check if the given data is a valid focus source.
       *
       * @param {Element | EventTarget | string}   data - Either an HTMLElement, SVGElement, or selector string.
       *
       * @returns {boolean} Is valid focus source.
       */
      static isFocusSource(data) {
        return typeof data === "string" || data?.nodeType === Node.ELEMENT_NODE && typeof data?.focus === "function";
      }
      /**
       * Tests if the given `element` is a Element node and has a `focus` method.
       *
       * @param {unknown}  element - Element to test for focus method.
       *
       * @returns {element is FocusableElement} Whether the element has a focus method.
       */
      static isFocusTarget(element2) {
        return element2 !== void 0 && element2 !== null && element2?.nodeType === Node.ELEMENT_NODE && typeof element2?.focus === "function";
      }
      /**
       * Perform a parent traversal from the current active element attempting to match the given element to test whether
       * current active element is within that element.
       *
       * @param {Element}  element - An element to match in parent traversal from the active element.
       *
       * @returns {boolean} Whether there is focus within the given element.
       */
      static isFocusWithin(element2) {
        if (!isObject(element2) || element2?.hidden || !element2?.isConnected) {
          return false;
        }
        let active2 = CrossWindow.getActiveElement(element2);
        if (!active2) {
          return false;
        }
        while (active2) {
          if (active2 === element2) {
            return true;
          }
          active2 = active2.parentElement;
        }
        return false;
      }
      /**
       * Traverses the given element's parent elements to check if any parent has `offsetWidth` and `offsetHeight` of 0,
       * indicating that a parent element is hidden. If a parent element is hidden, the given element is also considered
       * hidden. This is a reasonably efficient check and can be enabled as a filter step in conjunction with focusable
       * element detection methods like {@link A11yHelper.getFocusableElements}.
       *
       * @param {Element}  element - The starting element to check.
       *
       * @param {Element}  [stopElement] - The stopping parent element for traversal. If not defined, `document.body` is
       *        used as the stopping element.
       *
       * @returns {boolean} `true` if a parent element of the given element is hidden; otherwise, `false`.
       */
      static isParentHidden(element2, stopElement) {
        if (!CrossWindow.isElement(element2)) {
          throw new TypeError(`'element' is not an Element.`);
        }
        stopElement = stopElement ?? CrossWindow.getDocument(element2)?.body;
        if (!CrossWindow.isElement(stopElement)) {
          throw new TypeError(`'stopElement' must be an Element.`);
        }
        let current = element2.parentElement;
        while (current) {
          if (current === stopElement) {
            break;
          }
          if (current.offsetWidth === 0 && current.offsetHeight === 0) {
            return true;
          }
          current = current.parentElement;
        }
        return false;
      }
    }
    function clamp(value = 0, min = 0, max = 0) {
      return Math.min(Math.max(value, min), max);
    }
    function degToRad(deg) {
      return deg * (Math.PI / 180);
    }
    function radToDeg(rad) {
      return rad * (180 / Math.PI);
    }
    function lerp(start, end, amount) {
      return (1 - amount) * start + amount * end;
    }
    // @license MIT (https://github.com/toji/gl-matrix/blob/master/LICENSE.md)
    var GLM_EPSILON = 1e-6;
    var Mat4 = class _Mat4 extends Float32Array {
      static #IDENTITY_4X4 = new Float32Array([
        1,
        0,
        0,
        0,
        0,
        1,
        0,
        0,
        0,
        0,
        1,
        0,
        0,
        0,
        0,
        1
      ]);
      /**
       * Temporary variable to prevent repeated allocations in the algorithms within Mat4.
       * These are declared as TypedArrays to aid in tree-shaking.
       */
      static #TMP_VEC3 = new Float32Array(3);
      /**
       * Create a {@link Mat4}.
       *
       * @category Constructor
       */
      constructor(...values) {
        switch (values.length) {
          case 16:
            super(values);
            break;
          case 2:
            super(values[0], values[1], 16);
            break;
          case 1:
            const v = values[0];
            if (typeof v === "number") {
              super([
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v,
                v
              ]);
            } else {
              super(v, 0, 16);
            }
            break;
          default:
            super(_Mat4.#IDENTITY_4X4);
            break;
        }
      }
      // ============
      // Accessors
      // ============
      /**
       * A string representation of `this`
       * Equivalent to `Mat4.str(this);`
       *
       * @category Accessors
       */
      get str() {
        return _Mat4.str(this);
      }
      // ===================
      // Instance methods
      // ===================
      /**
       * Copy the values from another {@link Mat4} into `this`.
       * @category Methods
       *
       * @param a the source vector
       * @returns `this`
       */
      copy(a) {
        this.set(a);
        return this;
      }
      /**
       * Set `this` to the identity matrix
       * Equivalent to Mat4.identity(this)
       * @category Methods
       *
       * @returns `this`
       */
      identity() {
        this.set(_Mat4.#IDENTITY_4X4);
        return this;
      }
      /**
       * Multiplies this {@link Mat4} against another one
       * Equivalent to `Mat4.multiply(this, this, b);`
       * @category Methods
       *
       * @param b - The second operand
       * @returns `this`
       */
      multiply(b) {
        return _Mat4.multiply(this, this, b);
      }
      /**
       * Alias for {@link Mat4.multiply}
       * @category Methods
       */
      mul(b) {
        return this;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Transpose this {@link Mat4}
       * Equivalent to `Mat4.transpose(this, this);`
       * @category Methods
       *
       * @returns `this`
       */
      transpose() {
        return _Mat4.transpose(this, this);
      }
      /**
       * Inverts this {@link Mat4}
       * Equivalent to `Mat4.invert(this, this);`
       * @category Methods
       *
       * @returns `this`
       */
      invert() {
        return _Mat4.invert(this, this);
      }
      /**
       * Translate this {@link Mat4} by the given vector
       * Equivalent to `Mat4.translate(this, this, v);`
       * @category Methods
       *
       * @param v - The {@link Vec3} to translate by
       * @returns `this`
       */
      translate(v) {
        return _Mat4.translate(this, this, v);
      }
      /**
       * Rotates this {@link Mat4} by the given angle around the given axis
       * Equivalent to `Mat4.rotate(this, this, rad, axis);`
       * @category Methods
       *
       * @param rad - the angle to rotate the matrix by
       * @param axis - the axis to rotate around
       * @returns `this`
       */
      rotate(rad, axis) {
        return _Mat4.rotate(this, this, rad, axis);
      }
      /**
       * Scales this {@link Mat4} by the dimensions in the given vec3 not using vectorization
       * Equivalent to `Mat4.scale(this, this, v);`
       * @category Methods
       *
       * @param v - The {@link Vec3} to scale the matrix by
       * @returns `this`
       */
      scale(v) {
        return _Mat4.scale(this, this, v);
      }
      /**
       * Rotates this {@link Mat4} by the given angle around the X axis
       * Equivalent to `Mat4.rotateX(this, this, rad);`
       * @category Methods
       *
       * @param rad - the angle to rotate the matrix by
       * @returns `this`
       */
      rotateX(rad) {
        return _Mat4.rotateX(this, this, rad);
      }
      /**
       * Rotates this {@link Mat4} by the given angle around the Y axis
       * Equivalent to `Mat4.rotateY(this, this, rad);`
       * @category Methods
       *
       * @param rad - the angle to rotate the matrix by
       * @returns `this`
       */
      rotateY(rad) {
        return _Mat4.rotateY(this, this, rad);
      }
      /**
       * Rotates this {@link Mat4} by the given angle around the Z axis
       * Equivalent to `Mat4.rotateZ(this, this, rad);`
       * @category Methods
       *
       * @param rad - the angle to rotate the matrix by
       * @returns `this`
       */
      rotateZ(rad) {
        return _Mat4.rotateZ(this, this, rad);
      }
      /**
       * Generates a perspective projection matrix with the given bounds.
       * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
       * which matches WebGL/OpenGL's clip volume.
       * Passing null/undefined/no value for far will generate infinite projection matrix.
       * Equivalent to `Mat4.perspectiveNO(this, fovy, aspect, near, far);`
       * @category Methods
       *
       * @param fovy - Vertical field of view in radians
       * @param aspect - Aspect ratio. typically viewport width/height
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum, can be null or Infinity
       * @returns `this`
       */
      perspectiveNO(fovy, aspect, near, far) {
        return _Mat4.perspectiveNO(this, fovy, aspect, near, far);
      }
      /**
       * Generates a perspective projection matrix suitable for WebGPU with the given bounds.
       * The near/far clip planes correspond to a normalized device coordinate Z range of [0, 1],
       * which matches WebGPU/Vulkan/DirectX/Metal's clip volume.
       * Passing null/undefined/no value for far will generate infinite projection matrix.
       * Equivalent to `Mat4.perspectiveZO(this, fovy, aspect, near, far);`
       * @category Methods
       *
       * @param fovy - Vertical field of view in radians
       * @param aspect - Aspect ratio. typically viewport width/height
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum, can be null or Infinity
       * @returns `this`
       */
      perspectiveZO(fovy, aspect, near, far) {
        return _Mat4.perspectiveZO(this, fovy, aspect, near, far);
      }
      /**
       * Generates a orthogonal projection matrix with the given bounds.
       * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
       * which matches WebGL/OpenGL's clip volume.
       * Equivalent to `Mat4.orthoNO(this, left, right, bottom, top, near, far);`
       * @category Methods
       *
       * @param left - Left bound of the frustum
       * @param right - Right bound of the frustum
       * @param bottom - Bottom bound of the frustum
       * @param top - Top bound of the frustum
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum
       * @returns `this`
       */
      orthoNO(left, right, bottom, top, near, far) {
        return _Mat4.orthoNO(this, left, right, bottom, top, near, far);
      }
      /**
       * Generates a orthogonal projection matrix with the given bounds.
       * The near/far clip planes correspond to a normalized device coordinate Z range of [0, 1],
       * which matches WebGPU/Vulkan/DirectX/Metal's clip volume.
       * Equivalent to `Mat4.orthoZO(this, left, right, bottom, top, near, far);`
       * @category Methods
       *
       * @param left - Left bound of the frustum
       * @param right - Right bound of the frustum
       * @param bottom - Bottom bound of the frustum
       * @param top - Top bound of the frustum
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum
       * @returns `this`
       */
      orthoZO(left, right, bottom, top, near, far) {
        return _Mat4.orthoZO(this, left, right, bottom, top, near, far);
      }
      // ===================
      // Static accessors
      // ===================
      /**
       * @category Static
       *
       * @returns The number of bytes in a {@link Mat4}.
       */
      static get BYTE_LENGTH() {
        return 16 * Float32Array.BYTES_PER_ELEMENT;
      }
      // ===================
      // Static methods
      // ===================
      /**
       * Creates a new, identity {@link Mat4}
       * @category Static
       *
       * @returns A new {@link Mat4}
       */
      static create() {
        return new _Mat4();
      }
      /**
       * Creates a new {@link Mat4} initialized with values from an existing matrix
       * @category Static
       *
       * @param a - Matrix to clone
       * @returns A new {@link Mat4}
       */
      static clone(a) {
        return new _Mat4(a);
      }
      /**
       * Copy the values from one {@link Mat4} to another
       * @category Static
       *
       * @param out - The receiving Matrix
       * @param a - Matrix to copy
       * @returns `out`
       */
      static copy(out, a) {
        out[0] = a[0];
        out[1] = a[1];
        out[2] = a[2];
        out[3] = a[3];
        out[4] = a[4];
        out[5] = a[5];
        out[6] = a[6];
        out[7] = a[7];
        out[8] = a[8];
        out[9] = a[9];
        out[10] = a[10];
        out[11] = a[11];
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
        return out;
      }
      /**
       * Create a new mat4 with the given values
       * @category Static
       *
       * @param values - Matrix components
       * @returns A new {@link Mat4}
       */
      static fromValues(...values) {
        return new _Mat4(...values);
      }
      /**
       * Set the components of a mat4 to the given values
       * @category Static
       *
       * @param out - The receiving matrix
       * @param values - Matrix components
       * @returns `out`
       */
      static set(out, ...values) {
        out[0] = values[0];
        out[1] = values[1];
        out[2] = values[2];
        out[3] = values[3];
        out[4] = values[4];
        out[5] = values[5];
        out[6] = values[6];
        out[7] = values[7];
        out[8] = values[8];
        out[9] = values[9];
        out[10] = values[10];
        out[11] = values[11];
        out[12] = values[12];
        out[13] = values[13];
        out[14] = values[14];
        out[15] = values[15];
        return out;
      }
      /**
       * Set a {@link Mat4} to the identity matrix
       * @category Static
       *
       * @param out - The receiving Matrix
       * @returns `out`
       */
      static identity(out) {
        out[0] = 1;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = 1;
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[10] = 1;
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Transpose the values of a {@link Mat4}
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the source matrix
       * @returns `out`
       */
      static transpose(out, a) {
        if (out === a) {
          const a01 = a[1], a02 = a[2], a03 = a[3];
          const a12 = a[6], a13 = a[7];
          const a23 = a[11];
          out[1] = a[4];
          out[2] = a[8];
          out[3] = a[12];
          out[4] = a01;
          out[6] = a[9];
          out[7] = a[13];
          out[8] = a02;
          out[9] = a12;
          out[11] = a[14];
          out[12] = a03;
          out[13] = a13;
          out[14] = a23;
        } else {
          out[0] = a[0];
          out[1] = a[4];
          out[2] = a[8];
          out[3] = a[12];
          out[4] = a[1];
          out[5] = a[5];
          out[6] = a[9];
          out[7] = a[13];
          out[8] = a[2];
          out[9] = a[6];
          out[10] = a[10];
          out[11] = a[14];
          out[12] = a[3];
          out[13] = a[7];
          out[14] = a[11];
          out[15] = a[15];
        }
        return out;
      }
      /**
       * Inverts a {@link Mat4}
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the source matrix
       * @returns `out` or `null` if the matrix is not invertible
       */
      static invert(out, a) {
        const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
        const a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
        const a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
        const a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
        const b00 = a00 * a11 - a01 * a10;
        const b01 = a00 * a12 - a02 * a10;
        const b02 = a00 * a13 - a03 * a10;
        const b03 = a01 * a12 - a02 * a11;
        const b04 = a01 * a13 - a03 * a11;
        const b05 = a02 * a13 - a03 * a12;
        const b06 = a20 * a31 - a21 * a30;
        const b07 = a20 * a32 - a22 * a30;
        const b08 = a20 * a33 - a23 * a30;
        const b09 = a21 * a32 - a22 * a31;
        const b10 = a21 * a33 - a23 * a31;
        const b11 = a22 * a33 - a23 * a32;
        let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
        if (!det) {
          return null;
        }
        det = 1 / det;
        out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
        out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
        out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
        out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
        out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
        out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
        out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
        out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
        out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
        out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
        out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
        out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
        out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
        out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
        out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
        out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
        return out;
      }
      /**
       * Calculates the adjugate of a {@link Mat4}
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the source matrix
       * @returns `out`
       */
      static adjoint(out, a) {
        const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
        const a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
        const a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
        const a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
        const b00 = a00 * a11 - a01 * a10;
        const b01 = a00 * a12 - a02 * a10;
        const b02 = a00 * a13 - a03 * a10;
        const b03 = a01 * a12 - a02 * a11;
        const b04 = a01 * a13 - a03 * a11;
        const b05 = a02 * a13 - a03 * a12;
        const b06 = a20 * a31 - a21 * a30;
        const b07 = a20 * a32 - a22 * a30;
        const b08 = a20 * a33 - a23 * a30;
        const b09 = a21 * a32 - a22 * a31;
        const b10 = a21 * a33 - a23 * a31;
        const b11 = a22 * a33 - a23 * a32;
        out[0] = a11 * b11 - a12 * b10 + a13 * b09;
        out[1] = a02 * b10 - a01 * b11 - a03 * b09;
        out[2] = a31 * b05 - a32 * b04 + a33 * b03;
        out[3] = a22 * b04 - a21 * b05 - a23 * b03;
        out[4] = a12 * b08 - a10 * b11 - a13 * b07;
        out[5] = a00 * b11 - a02 * b08 + a03 * b07;
        out[6] = a32 * b02 - a30 * b05 - a33 * b01;
        out[7] = a20 * b05 - a22 * b02 + a23 * b01;
        out[8] = a10 * b10 - a11 * b08 + a13 * b06;
        out[9] = a01 * b08 - a00 * b10 - a03 * b06;
        out[10] = a30 * b04 - a31 * b02 + a33 * b00;
        out[11] = a21 * b02 - a20 * b04 - a23 * b00;
        out[12] = a11 * b07 - a10 * b09 - a12 * b06;
        out[13] = a00 * b09 - a01 * b07 + a02 * b06;
        out[14] = a31 * b01 - a30 * b03 - a32 * b00;
        out[15] = a20 * b03 - a21 * b01 + a22 * b00;
        return out;
      }
      /**
       * Calculates the determinant of a {@link Mat4}
       * @category Static
       *
       * @param a - the source matrix
       * @returns determinant of a
       */
      static determinant(a) {
        const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
        const a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
        const a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
        const a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
        const b0 = a00 * a11 - a01 * a10;
        const b1 = a00 * a12 - a02 * a10;
        const b2 = a01 * a12 - a02 * a11;
        const b3 = a20 * a31 - a21 * a30;
        const b4 = a20 * a32 - a22 * a30;
        const b5 = a21 * a32 - a22 * a31;
        const b6 = a00 * b5 - a01 * b4 + a02 * b3;
        const b7 = a10 * b5 - a11 * b4 + a12 * b3;
        const b8 = a20 * b2 - a21 * b1 + a22 * b0;
        const b9 = a30 * b2 - a31 * b1 + a32 * b0;
        return a13 * b6 - a03 * b7 + a33 * b8 - a23 * b9;
      }
      /**
       * Multiplies two {@link Mat4}s
       * @category Static
       *
       * @param out - The receiving Matrix
       * @param a - The first operand
       * @param b - The second operand
       * @returns `out`
       */
      static multiply(out, a, b) {
        const a00 = a[0];
        const a01 = a[1];
        const a02 = a[2];
        const a03 = a[3];
        const a10 = a[4];
        const a11 = a[5];
        const a12 = a[6];
        const a13 = a[7];
        const a20 = a[8];
        const a21 = a[9];
        const a22 = a[10];
        const a23 = a[11];
        const a30 = a[12];
        const a31 = a[13];
        const a32 = a[14];
        const a33 = a[15];
        let b0 = b[0];
        let b1 = b[1];
        let b2 = b[2];
        let b3 = b[3];
        out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
        out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
        out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
        out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
        b0 = b[4];
        b1 = b[5];
        b2 = b[6];
        b3 = b[7];
        out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
        out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
        out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
        out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
        b0 = b[8];
        b1 = b[9];
        b2 = b[10];
        b3 = b[11];
        out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
        out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
        out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
        out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
        b0 = b[12];
        b1 = b[13];
        b2 = b[14];
        b3 = b[15];
        out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
        out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
        out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
        out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
        return out;
      }
      /**
       * Alias for {@link Mat4.multiply}
       * @category Static
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static mul(out, a, b) {
        return out;
      }
      /**
       * Translate a {@link Mat4} by the given vector
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the matrix to translate
       * @param v - vector to translate by
       * @returns `out`
       */
      static translate(out, a, v) {
        const x = v[0];
        const y = v[1];
        const z = v[2];
        if (a === out) {
          out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
          out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
          out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
          out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
        } else {
          const a00 = a[0];
          const a01 = a[1];
          const a02 = a[2];
          const a03 = a[3];
          const a10 = a[4];
          const a11 = a[5];
          const a12 = a[6];
          const a13 = a[7];
          const a20 = a[8];
          const a21 = a[9];
          const a22 = a[10];
          const a23 = a[11];
          out[0] = a00;
          out[1] = a01;
          out[2] = a02;
          out[3] = a03;
          out[4] = a10;
          out[5] = a11;
          out[6] = a12;
          out[7] = a13;
          out[8] = a20;
          out[9] = a21;
          out[10] = a22;
          out[11] = a23;
          out[12] = a00 * x + a10 * y + a20 * z + a[12];
          out[13] = a01 * x + a11 * y + a21 * z + a[13];
          out[14] = a02 * x + a12 * y + a22 * z + a[14];
          out[15] = a03 * x + a13 * y + a23 * z + a[15];
        }
        return out;
      }
      /**
       * Scales the {@link Mat4} by the dimensions in the given {@link Vec3} not using vectorization
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the matrix to scale
       * @param v - the {@link Vec3} to scale the matrix by
       * @returns `out`
       **/
      static scale(out, a, v) {
        const x = v[0];
        const y = v[1];
        const z = v[2];
        out[0] = a[0] * x;
        out[1] = a[1] * x;
        out[2] = a[2] * x;
        out[3] = a[3] * x;
        out[4] = a[4] * y;
        out[5] = a[5] * y;
        out[6] = a[6] * y;
        out[7] = a[7] * y;
        out[8] = a[8] * z;
        out[9] = a[9] * z;
        out[10] = a[10] * z;
        out[11] = a[11] * z;
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
        return out;
      }
      /**
       * Rotates a {@link Mat4} by the given angle around the given axis
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the matrix to rotate
       * @param rad - the angle to rotate the matrix by
       * @param axis - the axis to rotate around
       * @returns `out` or `null` if axis has a length of 0
       */
      static rotate(out, a, rad, axis) {
        let x = axis[0];
        let y = axis[1];
        let z = axis[2];
        let len = Math.sqrt(x * x + y * y + z * z);
        if (len < GLM_EPSILON) {
          return null;
        }
        len = 1 / len;
        x *= len;
        y *= len;
        z *= len;
        const s = Math.sin(rad);
        const c = Math.cos(rad);
        const t = 1 - c;
        const a00 = a[0];
        const a01 = a[1];
        const a02 = a[2];
        const a03 = a[3];
        const a10 = a[4];
        const a11 = a[5];
        const a12 = a[6];
        const a13 = a[7];
        const a20 = a[8];
        const a21 = a[9];
        const a22 = a[10];
        const a23 = a[11];
        const b00 = x * x * t + c;
        const b01 = y * x * t + z * s;
        const b02 = z * x * t - y * s;
        const b10 = x * y * t - z * s;
        const b11 = y * y * t + c;
        const b12 = z * y * t + x * s;
        const b20 = x * z * t + y * s;
        const b21 = y * z * t - x * s;
        const b22 = z * z * t + c;
        out[0] = a00 * b00 + a10 * b01 + a20 * b02;
        out[1] = a01 * b00 + a11 * b01 + a21 * b02;
        out[2] = a02 * b00 + a12 * b01 + a22 * b02;
        out[3] = a03 * b00 + a13 * b01 + a23 * b02;
        out[4] = a00 * b10 + a10 * b11 + a20 * b12;
        out[5] = a01 * b10 + a11 * b11 + a21 * b12;
        out[6] = a02 * b10 + a12 * b11 + a22 * b12;
        out[7] = a03 * b10 + a13 * b11 + a23 * b12;
        out[8] = a00 * b20 + a10 * b21 + a20 * b22;
        out[9] = a01 * b20 + a11 * b21 + a21 * b22;
        out[10] = a02 * b20 + a12 * b21 + a22 * b22;
        out[11] = a03 * b20 + a13 * b21 + a23 * b22;
        if (a !== out) {
          out[12] = a[12];
          out[13] = a[13];
          out[14] = a[14];
          out[15] = a[15];
        }
        return out;
      }
      /**
       * Rotates a matrix by the given angle around the X axis
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the matrix to rotate
       * @param rad - the angle to rotate the matrix by
       * @returns `out`
       */
      static rotateX(out, a, rad) {
        const s = Math.sin(rad);
        const c = Math.cos(rad);
        const a10 = a[4];
        const a11 = a[5];
        const a12 = a[6];
        const a13 = a[7];
        const a20 = a[8];
        const a21 = a[9];
        const a22 = a[10];
        const a23 = a[11];
        if (a !== out) {
          out[0] = a[0];
          out[1] = a[1];
          out[2] = a[2];
          out[3] = a[3];
          out[12] = a[12];
          out[13] = a[13];
          out[14] = a[14];
          out[15] = a[15];
        }
        out[4] = a10 * c + a20 * s;
        out[5] = a11 * c + a21 * s;
        out[6] = a12 * c + a22 * s;
        out[7] = a13 * c + a23 * s;
        out[8] = a20 * c - a10 * s;
        out[9] = a21 * c - a11 * s;
        out[10] = a22 * c - a12 * s;
        out[11] = a23 * c - a13 * s;
        return out;
      }
      /**
       * Rotates a matrix by the given angle around the Y axis
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the matrix to rotate
       * @param rad - the angle to rotate the matrix by
       * @returns `out`
       */
      static rotateY(out, a, rad) {
        const s = Math.sin(rad);
        const c = Math.cos(rad);
        const a00 = a[0];
        const a01 = a[1];
        const a02 = a[2];
        const a03 = a[3];
        const a20 = a[8];
        const a21 = a[9];
        const a22 = a[10];
        const a23 = a[11];
        if (a !== out) {
          out[4] = a[4];
          out[5] = a[5];
          out[6] = a[6];
          out[7] = a[7];
          out[12] = a[12];
          out[13] = a[13];
          out[14] = a[14];
          out[15] = a[15];
        }
        out[0] = a00 * c - a20 * s;
        out[1] = a01 * c - a21 * s;
        out[2] = a02 * c - a22 * s;
        out[3] = a03 * c - a23 * s;
        out[8] = a00 * s + a20 * c;
        out[9] = a01 * s + a21 * c;
        out[10] = a02 * s + a22 * c;
        out[11] = a03 * s + a23 * c;
        return out;
      }
      /**
       * Rotates a matrix by the given angle around the Z axis
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the matrix to rotate
       * @param rad - the angle to rotate the matrix by
       * @returns `out`
       */
      static rotateZ(out, a, rad) {
        const s = Math.sin(rad);
        const c = Math.cos(rad);
        const a00 = a[0];
        const a01 = a[1];
        const a02 = a[2];
        const a03 = a[3];
        const a10 = a[4];
        const a11 = a[5];
        const a12 = a[6];
        const a13 = a[7];
        if (a !== out) {
          out[8] = a[8];
          out[9] = a[9];
          out[10] = a[10];
          out[11] = a[11];
          out[12] = a[12];
          out[13] = a[13];
          out[14] = a[14];
          out[15] = a[15];
        }
        out[0] = a00 * c + a10 * s;
        out[1] = a01 * c + a11 * s;
        out[2] = a02 * c + a12 * s;
        out[3] = a03 * c + a13 * s;
        out[4] = a10 * c - a00 * s;
        out[5] = a11 * c - a01 * s;
        out[6] = a12 * c - a02 * s;
        out[7] = a13 * c - a03 * s;
        return out;
      }
      /**
       * Creates a {@link Mat4} from a vector translation
       * This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.translate(dest, dest, vec);
       * ```
       * @category Static
       *
       * @param out - {@link Mat4} receiving operation result
       * @param v - Translation vector
       * @returns `out`
       */
      static fromTranslation(out, v) {
        out[0] = 1;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = 1;
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[10] = 1;
        out[11] = 0;
        out[12] = v[0];
        out[13] = v[1];
        out[14] = v[2];
        out[15] = 1;
        return out;
      }
      /**
       * Creates a {@link Mat4} from a vector scaling
       * This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.scale(dest, dest, vec);
       * ```
       * @category Static
       *
       * @param out - {@link Mat4} receiving operation result
       * @param v - Scaling vector
       * @returns `out`
       */
      static fromScaling(out, v) {
        out[0] = v[0];
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = v[1];
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[10] = v[2];
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Creates a {@link Mat4} from a given angle around a given axis
       * This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.rotate(dest, dest, rad, axis);
       * ```
       * @category Static
       *
       * @param out - {@link Mat4} receiving operation result
       * @param rad - the angle to rotate the matrix by
       * @param axis - the axis to rotate around
       * @returns `out` or `null` if `axis` has a length of 0
       */
      static fromRotation(out, rad, axis) {
        let x = axis[0];
        let y = axis[1];
        let z = axis[2];
        let len = Math.sqrt(x * x + y * y + z * z);
        if (len < GLM_EPSILON) {
          return null;
        }
        len = 1 / len;
        x *= len;
        y *= len;
        z *= len;
        const s = Math.sin(rad);
        const c = Math.cos(rad);
        const t = 1 - c;
        out[0] = x * x * t + c;
        out[1] = y * x * t + z * s;
        out[2] = z * x * t - y * s;
        out[3] = 0;
        out[4] = x * y * t - z * s;
        out[5] = y * y * t + c;
        out[6] = z * y * t + x * s;
        out[7] = 0;
        out[8] = x * z * t + y * s;
        out[9] = y * z * t - x * s;
        out[10] = z * z * t + c;
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Creates a matrix from the given angle around the X axis
       * This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.rotateX(dest, dest, rad);
       * ```
       * @category Static
       *
       * @param out - mat4 receiving operation result
       * @param rad - the angle to rotate the matrix by
       * @returns `out`
       */
      static fromXRotation(out, rad) {
        const s = Math.sin(rad);
        const c = Math.cos(rad);
        out[0] = 1;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = c;
        out[6] = s;
        out[7] = 0;
        out[8] = 0;
        out[9] = -s;
        out[10] = c;
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Creates a matrix from the given angle around the Y axis
       * This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.rotateY(dest, dest, rad);
       * ```
       * @category Static
       *
       * @param out - mat4 receiving operation result
       * @param rad - the angle to rotate the matrix by
       * @returns `out`
       */
      static fromYRotation(out, rad) {
        const s = Math.sin(rad);
        const c = Math.cos(rad);
        out[0] = c;
        out[1] = 0;
        out[2] = -s;
        out[3] = 0;
        out[4] = 0;
        out[5] = 1;
        out[6] = 0;
        out[7] = 0;
        out[8] = s;
        out[9] = 0;
        out[10] = c;
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Creates a matrix from the given angle around the Z axis
       * This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.rotateZ(dest, dest, rad);
       * ```
       * @category Static
       *
       * @param out - mat4 receiving operation result
       * @param rad - the angle to rotate the matrix by
       * @returns `out`
       */
      static fromZRotation(out, rad) {
        const s = Math.sin(rad);
        const c = Math.cos(rad);
        out[0] = c;
        out[1] = s;
        out[2] = 0;
        out[3] = 0;
        out[4] = -s;
        out[5] = c;
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[10] = 1;
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Creates a matrix from a quaternion rotation and vector translation
       * This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.translate(dest, vec);
       *   let quatMat = mat4.create();
       *   quat4.toMat4(quat, quatMat);
       *   mat4.multiply(dest, quatMat);
       * ```
       * @category Static
       *
       * @param out - mat4 receiving operation result
       * @param q - Rotation quaternion
       * @param v - Translation vector
       * @returns `out`
       */
      static fromRotationTranslation(out, q, v) {
        const x = q[0];
        const y = q[1];
        const z = q[2];
        const w = q[3];
        const x2 = x + x;
        const y2 = y + y;
        const z2 = z + z;
        const xx = x * x2;
        const xy = x * y2;
        const xz = x * z2;
        const yy = y * y2;
        const yz = y * z2;
        const zz = z * z2;
        const wx = w * x2;
        const wy = w * y2;
        const wz = w * z2;
        out[0] = 1 - (yy + zz);
        out[1] = xy + wz;
        out[2] = xz - wy;
        out[3] = 0;
        out[4] = xy - wz;
        out[5] = 1 - (xx + zz);
        out[6] = yz + wx;
        out[7] = 0;
        out[8] = xz + wy;
        out[9] = yz - wx;
        out[10] = 1 - (xx + yy);
        out[11] = 0;
        out[12] = v[0];
        out[13] = v[1];
        out[14] = v[2];
        out[15] = 1;
        return out;
      }
      /**
       * Sets a {@link Mat4} from a {@link Quat2}.
       * @category Static
       *
       * @param out - Matrix
       * @param a - Dual Quaternion
       * @returns `out`
       */
      static fromQuat2(out, a) {
        const bx = -a[0];
        const by = -a[1];
        const bz = -a[2];
        const bw = a[3];
        const ax = a[4];
        const ay = a[5];
        const az = a[6];
        const aw = a[7];
        const magnitude = bx * bx + by * by + bz * bz + bw * bw;
        if (magnitude > 0) {
          _Mat4.#TMP_VEC3[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude;
          _Mat4.#TMP_VEC3[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude;
          _Mat4.#TMP_VEC3[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude;
        } else {
          _Mat4.#TMP_VEC3[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
          _Mat4.#TMP_VEC3[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
          _Mat4.#TMP_VEC3[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
        }
        _Mat4.fromRotationTranslation(out, a, _Mat4.#TMP_VEC3);
        return out;
      }
      /**
       * Calculates a {@link Mat4} normal matrix (transpose inverse) from a {@link Mat4}
       * @category Static
       *
       * @param out - Matrix receiving operation result
       * @param a - Mat4 to derive the normal matrix from
       * @returns `out` or `null` if the matrix is not invertible
       */
      static normalFromMat4(out, a) {
        const a00 = a[0];
        const a01 = a[1];
        const a02 = a[2];
        const a03 = a[3];
        const a10 = a[4];
        const a11 = a[5];
        const a12 = a[6];
        const a13 = a[7];
        const a20 = a[8];
        const a21 = a[9];
        const a22 = a[10];
        const a23 = a[11];
        const a30 = a[12];
        const a31 = a[13];
        const a32 = a[14];
        const a33 = a[15];
        const b00 = a00 * a11 - a01 * a10;
        const b01 = a00 * a12 - a02 * a10;
        const b02 = a00 * a13 - a03 * a10;
        const b03 = a01 * a12 - a02 * a11;
        const b04 = a01 * a13 - a03 * a11;
        const b05 = a02 * a13 - a03 * a12;
        const b06 = a20 * a31 - a21 * a30;
        const b07 = a20 * a32 - a22 * a30;
        const b08 = a20 * a33 - a23 * a30;
        const b09 = a21 * a32 - a22 * a31;
        const b10 = a21 * a33 - a23 * a31;
        const b11 = a22 * a33 - a23 * a32;
        let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
        if (!det) {
          return null;
        }
        det = 1 / det;
        out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
        out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
        out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
        out[3] = 0;
        out[4] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
        out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
        out[6] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
        out[7] = 0;
        out[8] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
        out[9] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
        out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Calculates a {@link Mat4} normal matrix (transpose inverse) from a {@link Mat4}
       * This version omits the calculation of the constant factor (1/determinant), so
       * any normals transformed with it will need to be renormalized.
       * From https://stackoverflow.com/a/27616419/25968
       * @category Static
       *
       * @param out - Matrix receiving operation result
       * @param a - Mat4 to derive the normal matrix from
       * @returns `out`
       */
      static normalFromMat4Fast(out, a) {
        const ax = a[0];
        const ay = a[1];
        const az = a[2];
        const bx = a[4];
        const by = a[5];
        const bz = a[6];
        const cx = a[8];
        const cy = a[9];
        const cz = a[10];
        out[0] = by * cz - cz * cy;
        out[1] = bz * cx - cx * cz;
        out[2] = bx * cy - cy * cx;
        out[3] = 0;
        out[4] = cy * az - cz * ay;
        out[5] = cz * ax - cx * az;
        out[6] = cx * ay - cy * ax;
        out[7] = 0;
        out[8] = ay * bz - az * by;
        out[9] = az * bx - ax * bz;
        out[10] = ax * by - ay * bx;
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Returns the translation vector component of a transformation
       * matrix. If a matrix is built with fromRotationTranslation,
       * the returned vector will be the same as the translation vector
       * originally supplied.
       * @category Static
       *
       * @param  {vec3} out Vector to receive translation component
       * @param  {ReadonlyMat4} mat Matrix to be decomposed (input)
       * @return {vec3} out
       */
      static getTranslation(out, mat) {
        out[0] = mat[12];
        out[1] = mat[13];
        out[2] = mat[14];
        return out;
      }
      /**
       * Returns the scaling factor component of a transformation
       * matrix. If a matrix is built with fromRotationTranslationScale
       * with a normalized Quaternion parameter, the returned vector will be
       * the same as the scaling vector
       * originally supplied.
       * @category Static
       *
       * @param  {vec3} out Vector to receive scaling factor component
       * @param  {ReadonlyMat4} mat Matrix to be decomposed (input)
       * @return {vec3} out
       */
      static getScaling(out, mat) {
        const m11 = mat[0];
        const m12 = mat[1];
        const m13 = mat[2];
        const m21 = mat[4];
        const m22 = mat[5];
        const m23 = mat[6];
        const m31 = mat[8];
        const m32 = mat[9];
        const m33 = mat[10];
        out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);
        out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);
        out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);
        return out;
      }
      /**
       * Returns a quaternion representing the rotational component
       * of a transformation matrix. If a matrix is built with
       * fromRotationTranslation, the returned quaternion will be the
       * same as the quaternion originally supplied.
       * @category Static
       *
       * @param out - Quaternion to receive the rotation component
       * @param mat - Matrix to be decomposed (input)
       * @return `out`
       */
      static getRotation(out, mat) {
        _Mat4.getScaling(_Mat4.#TMP_VEC3, mat);
        const is1 = 1 / _Mat4.#TMP_VEC3[0];
        const is2 = 1 / _Mat4.#TMP_VEC3[1];
        const is3 = 1 / _Mat4.#TMP_VEC3[2];
        const sm11 = mat[0] * is1;
        const sm12 = mat[1] * is2;
        const sm13 = mat[2] * is3;
        const sm21 = mat[4] * is1;
        const sm22 = mat[5] * is2;
        const sm23 = mat[6] * is3;
        const sm31 = mat[8] * is1;
        const sm32 = mat[9] * is2;
        const sm33 = mat[10] * is3;
        const trace = sm11 + sm22 + sm33;
        let S = 0;
        if (trace > 0) {
          S = Math.sqrt(trace + 1) * 2;
          out[3] = 0.25 * S;
          out[0] = (sm23 - sm32) / S;
          out[1] = (sm31 - sm13) / S;
          out[2] = (sm12 - sm21) / S;
        } else if (sm11 > sm22 && sm11 > sm33) {
          S = Math.sqrt(1 + sm11 - sm22 - sm33) * 2;
          out[3] = (sm23 - sm32) / S;
          out[0] = 0.25 * S;
          out[1] = (sm12 + sm21) / S;
          out[2] = (sm31 + sm13) / S;
        } else if (sm22 > sm33) {
          S = Math.sqrt(1 + sm22 - sm11 - sm33) * 2;
          out[3] = (sm31 - sm13) / S;
          out[0] = (sm12 + sm21) / S;
          out[1] = 0.25 * S;
          out[2] = (sm23 + sm32) / S;
        } else {
          S = Math.sqrt(1 + sm33 - sm11 - sm22) * 2;
          out[3] = (sm12 - sm21) / S;
          out[0] = (sm31 + sm13) / S;
          out[1] = (sm23 + sm32) / S;
          out[2] = 0.25 * S;
        }
        return out;
      }
      /**
       * Decomposes a transformation matrix into its rotation, translation
       * and scale components. Returns only the rotation component
       * @category Static
       *
       * @param out_r - Quaternion to receive the rotation component
       * @param out_t - Vector to receive the translation vector
       * @param out_s - Vector to receive the scaling factor
       * @param mat - Matrix to be decomposed (input)
       * @returns `out_r`
       */
      static decompose(out_r, out_t, out_s, mat) {
        out_t[0] = mat[12];
        out_t[1] = mat[13];
        out_t[2] = mat[14];
        const m11 = mat[0];
        const m12 = mat[1];
        const m13 = mat[2];
        const m21 = mat[4];
        const m22 = mat[5];
        const m23 = mat[6];
        const m31 = mat[8];
        const m32 = mat[9];
        const m33 = mat[10];
        out_s[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);
        out_s[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);
        out_s[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);
        const is1 = 1 / out_s[0];
        const is2 = 1 / out_s[1];
        const is3 = 1 / out_s[2];
        const sm11 = m11 * is1;
        const sm12 = m12 * is2;
        const sm13 = m13 * is3;
        const sm21 = m21 * is1;
        const sm22 = m22 * is2;
        const sm23 = m23 * is3;
        const sm31 = m31 * is1;
        const sm32 = m32 * is2;
        const sm33 = m33 * is3;
        const trace = sm11 + sm22 + sm33;
        let S = 0;
        if (trace > 0) {
          S = Math.sqrt(trace + 1) * 2;
          out_r[3] = 0.25 * S;
          out_r[0] = (sm23 - sm32) / S;
          out_r[1] = (sm31 - sm13) / S;
          out_r[2] = (sm12 - sm21) / S;
        } else if (sm11 > sm22 && sm11 > sm33) {
          S = Math.sqrt(1 + sm11 - sm22 - sm33) * 2;
          out_r[3] = (sm23 - sm32) / S;
          out_r[0] = 0.25 * S;
          out_r[1] = (sm12 + sm21) / S;
          out_r[2] = (sm31 + sm13) / S;
        } else if (sm22 > sm33) {
          S = Math.sqrt(1 + sm22 - sm11 - sm33) * 2;
          out_r[3] = (sm31 - sm13) / S;
          out_r[0] = (sm12 + sm21) / S;
          out_r[1] = 0.25 * S;
          out_r[2] = (sm23 + sm32) / S;
        } else {
          S = Math.sqrt(1 + sm33 - sm11 - sm22) * 2;
          out_r[3] = (sm12 - sm21) / S;
          out_r[0] = (sm31 + sm13) / S;
          out_r[1] = (sm23 + sm32) / S;
          out_r[2] = 0.25 * S;
        }
        return out_r;
      }
      /**
       * Creates a matrix from a quaternion rotation, vector translation and vector scale
       * This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.translate(dest, vec);
       *   let quatMat = mat4.create();
       *   quat4.toMat4(quat, quatMat);
       *   mat4.multiply(dest, quatMat);
       *   mat4.scale(dest, scale);
       * ```
       * @category Static
       *
       * @param out - mat4 receiving operation result
       * @param q - Rotation quaternion
       * @param v - Translation vector
       * @param s - Scaling vector
       * @returns `out`
       */
      static fromRotationTranslationScale(out, q, v, s) {
        const x = q[0];
        const y = q[1];
        const z = q[2];
        const w = q[3];
        const x2 = x + x;
        const y2 = y + y;
        const z2 = z + z;
        const xx = x * x2;
        const xy = x * y2;
        const xz = x * z2;
        const yy = y * y2;
        const yz = y * z2;
        const zz = z * z2;
        const wx = w * x2;
        const wy = w * y2;
        const wz = w * z2;
        const sx = s[0];
        const sy = s[1];
        const sz = s[2];
        out[0] = (1 - (yy + zz)) * sx;
        out[1] = (xy + wz) * sx;
        out[2] = (xz - wy) * sx;
        out[3] = 0;
        out[4] = (xy - wz) * sy;
        out[5] = (1 - (xx + zz)) * sy;
        out[6] = (yz + wx) * sy;
        out[7] = 0;
        out[8] = (xz + wy) * sz;
        out[9] = (yz - wx) * sz;
        out[10] = (1 - (xx + yy)) * sz;
        out[11] = 0;
        out[12] = v[0];
        out[13] = v[1];
        out[14] = v[2];
        out[15] = 1;
        return out;
      }
      /**
       * Creates a matrix from a quaternion rotation, vector translation and vector scale, rotating and scaling around the
       * given origin. This is equivalent to (but much faster than):
       * ```js
       *   mat4.identity(dest);
       *   mat4.translate(dest, vec);
       *   mat4.translate(dest, origin);
       *   let quatMat = mat4.create();
       *   quat4.toMat4(quat, quatMat);
       *   mat4.multiply(dest, quatMat);
       *   mat4.scale(dest, scale)
       *   mat4.translate(dest, negativeOrigin);
       * ```
       * @category Static
       *
       * @param out - mat4 receiving operation result
       * @param q - Rotation quaternion
       * @param v - Translation vector
       * @param s - Scaling vector
       * @param o - The origin vector around which to scale and rotate
       * @returns `out`
       */
      static fromRotationTranslationScaleOrigin(out, q, v, s, o) {
        const x = q[0];
        const y = q[1];
        const z = q[2];
        const w = q[3];
        const x2 = x + x;
        const y2 = y + y;
        const z2 = z + z;
        const xx = x * x2;
        const xy = x * y2;
        const xz = x * z2;
        const yy = y * y2;
        const yz = y * z2;
        const zz = z * z2;
        const wx = w * x2;
        const wy = w * y2;
        const wz = w * z2;
        const sx = s[0];
        const sy = s[1];
        const sz = s[2];
        const ox = o[0];
        const oy = o[1];
        const oz = o[2];
        const out0 = (1 - (yy + zz)) * sx;
        const out1 = (xy + wz) * sx;
        const out2 = (xz - wy) * sx;
        const out4 = (xy - wz) * sy;
        const out5 = (1 - (xx + zz)) * sy;
        const out6 = (yz + wx) * sy;
        const out8 = (xz + wy) * sz;
        const out9 = (yz - wx) * sz;
        const out10 = (1 - (xx + yy)) * sz;
        out[0] = out0;
        out[1] = out1;
        out[2] = out2;
        out[3] = 0;
        out[4] = out4;
        out[5] = out5;
        out[6] = out6;
        out[7] = 0;
        out[8] = out8;
        out[9] = out9;
        out[10] = out10;
        out[11] = 0;
        out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz);
        out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz);
        out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz);
        out[15] = 1;
        return out;
      }
      /**
       * Calculates a 4x4 matrix from the given quaternion
       * @category Static
       *
       * @param out - mat4 receiving operation result
       * @param q - Quaternion to create matrix from
       * @returns `out`
       */
      static fromQuat(out, q) {
        const x = q[0];
        const y = q[1];
        const z = q[2];
        const w = q[3];
        const x2 = x + x;
        const y2 = y + y;
        const z2 = z + z;
        const xx = x * x2;
        const yx = y * x2;
        const yy = y * y2;
        const zx = z * x2;
        const zy = z * y2;
        const zz = z * z2;
        const wx = w * x2;
        const wy = w * y2;
        const wz = w * z2;
        out[0] = 1 - yy - zz;
        out[1] = yx + wz;
        out[2] = zx - wy;
        out[3] = 0;
        out[4] = yx - wz;
        out[5] = 1 - xx - zz;
        out[6] = zy + wx;
        out[7] = 0;
        out[8] = zx + wy;
        out[9] = zy - wx;
        out[10] = 1 - xx - yy;
        out[11] = 0;
        out[12] = 0;
        out[13] = 0;
        out[14] = 0;
        out[15] = 1;
        return out;
      }
      /**
       * Generates a frustum matrix with the given bounds
       * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
       * which matches WebGL/OpenGL's clip volume.
       * Passing null/undefined/no value for far will generate infinite projection matrix.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param left - Left bound of the frustum
       * @param right - Right bound of the frustum
       * @param bottom - Bottom bound of the frustum
       * @param top - Top bound of the frustum
       * @param near - Near bound of the frustum
       * @param far -  Far bound of the frustum, can be null or Infinity
       * @returns `out`
       */
      static frustumNO(out, left, right, bottom, top, near, far = Infinity) {
        const rl = 1 / (right - left);
        const tb = 1 / (top - bottom);
        out[0] = near * 2 * rl;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = near * 2 * tb;
        out[6] = 0;
        out[7] = 0;
        out[8] = (right + left) * rl;
        out[9] = (top + bottom) * tb;
        out[11] = -1;
        out[12] = 0;
        out[13] = 0;
        out[15] = 0;
        if (far != null && far !== Infinity) {
          const nf = 1 / (near - far);
          out[10] = (far + near) * nf;
          out[14] = 2 * far * near * nf;
        } else {
          out[10] = -1;
          out[14] = -2 * near;
        }
        return out;
      }
      /**
       * Alias for {@link Mat4.frustumNO}
       * @category Static
       * @deprecated Use {@link Mat4.frustumNO} or {@link Mat4.frustumZO} explicitly
       */
      static frustum(out, left, right, bottom, top, near, far = Infinity) {
        return out;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Generates a frustum matrix with the given bounds
       * The near/far clip planes correspond to a normalized device coordinate Z range of [0, 1],
       * which matches WebGPU/Vulkan/DirectX/Metal's clip volume.
       * Passing null/undefined/no value for far will generate infinite projection matrix.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param left - Left bound of the frustum
       * @param right - Right bound of the frustum
       * @param bottom - Bottom bound of the frustum
       * @param top - Top bound of the frustum
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum, can be null or Infinity
       * @returns `out`
       */
      static frustumZO(out, left, right, bottom, top, near, far = Infinity) {
        const rl = 1 / (right - left);
        const tb = 1 / (top - bottom);
        out[0] = near * 2 * rl;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = near * 2 * tb;
        out[6] = 0;
        out[7] = 0;
        out[8] = (right + left) * rl;
        out[9] = (top + bottom) * tb;
        out[11] = -1;
        out[12] = 0;
        out[13] = 0;
        out[15] = 0;
        if (far != null && far !== Infinity) {
          const nf = 1 / (near - far);
          out[10] = far * nf;
          out[14] = far * near * nf;
        } else {
          out[10] = -1;
          out[14] = -near;
        }
        return out;
      }
      /**
       * Generates a perspective projection matrix with the given bounds.
       * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
       * which matches WebGL/OpenGL's clip volume.
       * Passing null/undefined/no value for far will generate infinite projection matrix.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param fovy - Vertical field of view in radians
       * @param aspect - Aspect ratio. typically viewport width/height
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum, can be null or Infinity
       * @returns `out`
       */
      static perspectiveNO(out, fovy, aspect, near, far = Infinity) {
        const f = 1 / Math.tan(fovy / 2);
        out[0] = f / aspect;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = f;
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[11] = -1;
        out[12] = 0;
        out[13] = 0;
        out[15] = 0;
        if (far != null && far !== Infinity) {
          const nf = 1 / (near - far);
          out[10] = (far + near) * nf;
          out[14] = 2 * far * near * nf;
        } else {
          out[10] = -1;
          out[14] = -2 * near;
        }
        return out;
      }
      /**
       * Alias for {@link Mat4.perspectiveNO}
       * @category Static
       * @deprecated Use {@link Mat4.perspectiveNO} or {@link Mat4.perspectiveZO} explicitly
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static perspective(out, fovy, aspect, near, far = Infinity) {
        return out;
      }
      /**
       * Generates a perspective projection matrix suitable for WebGPU with the given bounds.
       * The near/far clip planes correspond to a normalized device coordinate Z range of [0, 1],
       * which matches WebGPU/Vulkan/DirectX/Metal's clip volume.
       * Passing null/undefined/no value for far will generate infinite projection matrix.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param fovy - Vertical field of view in radians
       * @param aspect - Aspect ratio. typically viewport width/height
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum, can be null or Infinity
       * @returns `out`
       */
      static perspectiveZO(out, fovy, aspect, near, far = Infinity) {
        const f = 1 / Math.tan(fovy / 2);
        out[0] = f / aspect;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = f;
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[11] = -1;
        out[12] = 0;
        out[13] = 0;
        out[15] = 0;
        if (far != null && far !== Infinity) {
          const nf = 1 / (near - far);
          out[10] = far * nf;
          out[14] = far * near * nf;
        } else {
          out[10] = -1;
          out[14] = -near;
        }
        return out;
      }
      /**
       * Generates a perspective projection matrix with the given field of view. This is primarily useful for generating
       * projection matrices to be used with the still experimental WebVR API.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param fov - Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum
       * @returns `out`
       * @deprecated
       */
      static perspectiveFromFieldOfView(out, fov, near, far) {
        const upTan = Math.tan(fov.upDegrees * Math.PI / 180);
        const downTan = Math.tan(fov.downDegrees * Math.PI / 180);
        const leftTan = Math.tan(fov.leftDegrees * Math.PI / 180);
        const rightTan = Math.tan(fov.rightDegrees * Math.PI / 180);
        const xScale = 2 / (leftTan + rightTan);
        const yScale = 2 / (upTan + downTan);
        out[0] = xScale;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = yScale;
        out[6] = 0;
        out[7] = 0;
        out[8] = -((leftTan - rightTan) * xScale * 0.5);
        out[9] = (upTan - downTan) * yScale * 0.5;
        out[10] = far / (near - far);
        out[11] = -1;
        out[12] = 0;
        out[13] = 0;
        out[14] = far * near / (near - far);
        out[15] = 0;
        return out;
      }
      /**
       * Generates an orthogonal projection matrix with the given bounds. The near / far clip planes correspond to a
       * normalized device coordinate Z range of [-1, 1], which matches WebGL / OpenGLs clip volume.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param left - Left bound of the frustum
       * @param right - Right bound of the frustum
       * @param bottom - Bottom bound of the frustum
       * @param top - Top bound of the frustum
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum
       * @returns `out`
       */
      static orthoNO(out, left, right, bottom, top, near, far) {
        const lr = 1 / (left - right);
        const bt = 1 / (bottom - top);
        const nf = 1 / (near - far);
        out[0] = -2 * lr;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = -2 * bt;
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[10] = 2 * nf;
        out[11] = 0;
        out[12] = (left + right) * lr;
        out[13] = (top + bottom) * bt;
        out[14] = (far + near) * nf;
        out[15] = 1;
        return out;
      }
      /**
       * Alias for {@link Mat4.orthoNO}
       * @category Static
       * @deprecated Use {@link Mat4.orthoNO} or {@link Mat4.orthoZO} explicitly
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static ortho(out, left, right, bottom, top, near, far) {
        return out;
      }
      /**
       * Generates a orthogonal projection matrix with the given bounds. The near / far clip planes correspond to a
       * normalized device coordinate Z range of [0, 1], which matches WebGPU / Vulkan / DirectX / Metal's clip volume.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param left - Left bound of the frustum
       * @param right - Right bound of the frustum
       * @param bottom - Bottom bound of the frustum
       * @param top - Top bound of the frustum
       * @param near - Near bound of the frustum
       * @param far - Far bound of the frustum
       * @returns `out`
       */
      static orthoZO(out, left, right, bottom, top, near, far) {
        const lr = 1 / (left - right);
        const bt = 1 / (bottom - top);
        const nf = 1 / (near - far);
        out[0] = -2 * lr;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = -2 * bt;
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[10] = nf;
        out[11] = 0;
        out[12] = (left + right) * lr;
        out[13] = (top + bottom) * bt;
        out[14] = near * nf;
        out[15] = 1;
        return out;
      }
      /**
       * Generates a look-at matrix with the given eye position, focal point, and up axis. If you want a matrix that
       * actually makes an object look at another object, you should use targetTo instead.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param eye - Position of the viewer
       * @param center - Point the viewer is looking at
       * @param up - vec3 pointing up
       * @returns `out`
       */
      static lookAt(out, eye, center, up) {
        const eyex = eye[0];
        const eyey = eye[1];
        const eyez = eye[2];
        const upx = up[0];
        const upy = up[1];
        const upz = up[2];
        const centerx = center[0];
        const centery = center[1];
        const centerz = center[2];
        if (Math.abs(eyex - centerx) < GLM_EPSILON && Math.abs(eyey - centery) < GLM_EPSILON && Math.abs(eyez - centerz) < GLM_EPSILON) {
          return _Mat4.identity(out);
        }
        let z0 = eyex - centerx;
        let z1 = eyey - centery;
        let z2 = eyez - centerz;
        let len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
        z0 *= len;
        z1 *= len;
        z2 *= len;
        let x0 = upy * z2 - upz * z1;
        let x1 = upz * z0 - upx * z2;
        let x2 = upx * z1 - upy * z0;
        len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
        if (!len) {
          x0 = 0;
          x1 = 0;
          x2 = 0;
        } else {
          len = 1 / len;
          x0 *= len;
          x1 *= len;
          x2 *= len;
        }
        let y0 = z1 * x2 - z2 * x1;
        let y1 = z2 * x0 - z0 * x2;
        let y2 = z0 * x1 - z1 * x0;
        len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
        if (!len) {
          y0 = 0;
          y1 = 0;
          y2 = 0;
        } else {
          len = 1 / len;
          y0 *= len;
          y1 *= len;
          y2 *= len;
        }
        out[0] = x0;
        out[1] = y0;
        out[2] = z0;
        out[3] = 0;
        out[4] = x1;
        out[5] = y1;
        out[6] = z1;
        out[7] = 0;
        out[8] = x2;
        out[9] = y2;
        out[10] = z2;
        out[11] = 0;
        out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
        out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
        out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
        out[15] = 1;
        return out;
      }
      /**
       * Generates a matrix that makes something look at something else.
       * @category Static
       *
       * @param out - mat4 frustum matrix will be written into
       * @param eye - Position of the viewer
       * @param target - Point the viewer is looking at
       * @param up - vec3 pointing up
       * @returns `out`
       */
      static targetTo(out, eye, target, up) {
        const eyex = eye[0];
        const eyey = eye[1];
        const eyez = eye[2];
        const upx = up[0];
        const upy = up[1];
        const upz = up[2];
        let z0 = eyex - target[0];
        let z1 = eyey - target[1];
        let z2 = eyez - target[2];
        let len = z0 * z0 + z1 * z1 + z2 * z2;
        if (len > 0) {
          len = 1 / Math.sqrt(len);
          z0 *= len;
          z1 *= len;
          z2 *= len;
        }
        let x0 = upy * z2 - upz * z1;
        let x1 = upz * z0 - upx * z2;
        let x2 = upx * z1 - upy * z0;
        len = x0 * x0 + x1 * x1 + x2 * x2;
        if (len > 0) {
          len = 1 / Math.sqrt(len);
          x0 *= len;
          x1 *= len;
          x2 *= len;
        }
        out[0] = x0;
        out[1] = x1;
        out[2] = x2;
        out[3] = 0;
        out[4] = z1 * x2 - z2 * x1;
        out[5] = z2 * x0 - z0 * x2;
        out[6] = z0 * x1 - z1 * x0;
        out[7] = 0;
        out[8] = z0;
        out[9] = z1;
        out[10] = z2;
        out[11] = 0;
        out[12] = eyex;
        out[13] = eyey;
        out[14] = eyez;
        out[15] = 1;
        return out;
      }
      /**
       * Returns Frobenius norm of a {@link Mat4}
       * @category Static
       *
       * @param a - the matrix to calculate Frobenius norm of
       * @returns Frobenius norm
       */
      static frob(a) {
        return Math.sqrt(
          a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3] + a[4] * a[4] + a[5] * a[5] + a[6] * a[6] + a[7] * a[7] + a[8] * a[8] + a[9] * a[9] + a[10] * a[10] + a[11] * a[11] + a[12] * a[12] + a[13] * a[13] + a[14] * a[14] + a[15] * a[15]
        );
      }
      /**
       * Adds two {@link Mat4}'s
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the first operand
       * @param b - the second operand
       * @returns `out`
       */
      static add(out, a, b) {
        out[0] = a[0] + b[0];
        out[1] = a[1] + b[1];
        out[2] = a[2] + b[2];
        out[3] = a[3] + b[3];
        out[4] = a[4] + b[4];
        out[5] = a[5] + b[5];
        out[6] = a[6] + b[6];
        out[7] = a[7] + b[7];
        out[8] = a[8] + b[8];
        out[9] = a[9] + b[9];
        out[10] = a[10] + b[10];
        out[11] = a[11] + b[11];
        out[12] = a[12] + b[12];
        out[13] = a[13] + b[13];
        out[14] = a[14] + b[14];
        out[15] = a[15] + b[15];
        return out;
      }
      /**
       * Subtracts matrix b from matrix a
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the first operand
       * @param b - the second operand
       * @returns `out`
       */
      static subtract(out, a, b) {
        out[0] = a[0] - b[0];
        out[1] = a[1] - b[1];
        out[2] = a[2] - b[2];
        out[3] = a[3] - b[3];
        out[4] = a[4] - b[4];
        out[5] = a[5] - b[5];
        out[6] = a[6] - b[6];
        out[7] = a[7] - b[7];
        out[8] = a[8] - b[8];
        out[9] = a[9] - b[9];
        out[10] = a[10] - b[10];
        out[11] = a[11] - b[11];
        out[12] = a[12] - b[12];
        out[13] = a[13] - b[13];
        out[14] = a[14] - b[14];
        out[15] = a[15] - b[15];
        return out;
      }
      /**
       * Alias for {@link Mat4.subtract}
       * @category Static
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static sub(out, a, b) {
        return out;
      }
      /**
       * Multiply each element of the matrix by a scalar.
       * @category Static
       *
       * @param out - the receiving matrix
       * @param a - the matrix to scale
       * @param b - amount to scale the matrix's elements by
       * @returns `out`
       */
      static multiplyScalar(out, a, b) {
        out[0] = a[0] * b;
        out[1] = a[1] * b;
        out[2] = a[2] * b;
        out[3] = a[3] * b;
        out[4] = a[4] * b;
        out[5] = a[5] * b;
        out[6] = a[6] * b;
        out[7] = a[7] * b;
        out[8] = a[8] * b;
        out[9] = a[9] * b;
        out[10] = a[10] * b;
        out[11] = a[11] * b;
        out[12] = a[12] * b;
        out[13] = a[13] * b;
        out[14] = a[14] * b;
        out[15] = a[15] * b;
        return out;
      }
      /**
       * Adds two mat4's after multiplying each element of the second operand by a scalar value.
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @param scale - the amount to scale b's elements by before adding
       * @returns `out`
       */
      static multiplyScalarAndAdd(out, a, b, scale2) {
        out[0] = a[0] + b[0] * scale2;
        out[1] = a[1] + b[1] * scale2;
        out[2] = a[2] + b[2] * scale2;
        out[3] = a[3] + b[3] * scale2;
        out[4] = a[4] + b[4] * scale2;
        out[5] = a[5] + b[5] * scale2;
        out[6] = a[6] + b[6] * scale2;
        out[7] = a[7] + b[7] * scale2;
        out[8] = a[8] + b[8] * scale2;
        out[9] = a[9] + b[9] * scale2;
        out[10] = a[10] + b[10] * scale2;
        out[11] = a[11] + b[11] * scale2;
        out[12] = a[12] + b[12] * scale2;
        out[13] = a[13] + b[13] * scale2;
        out[14] = a[14] + b[14] * scale2;
        out[15] = a[15] + b[15] * scale2;
        return out;
      }
      /**
       * Returns whether two {@link Mat4}s have exactly the same elements in the same position (when compared with ===).
       * @category Static
       *
       * @param a - The first matrix.
       * @param b - The second matrix.
       * @returns True if the matrices are equal, false otherwise.
       */
      static exactEquals(a, b) {
        return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] && a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15];
      }
      /**
       * Returns whether two {@link Mat4}s have approximately the same elements in the same position.
       * @category Static
       *
       * @param a - The first matrix.
       * @param b - The second matrix.
       * @returns True if the matrices are equal, false otherwise.
       */
      static equals(a, b) {
        const a0 = a[0];
        const a1 = a[1];
        const a2 = a[2];
        const a3 = a[3];
        const a4 = a[4];
        const a5 = a[5];
        const a6 = a[6];
        const a7 = a[7];
        const a8 = a[8];
        const a9 = a[9];
        const a10 = a[10];
        const a11 = a[11];
        const a12 = a[12];
        const a13 = a[13];
        const a14 = a[14];
        const a15 = a[15];
        const b0 = b[0];
        const b1 = b[1];
        const b2 = b[2];
        const b3 = b[3];
        const b4 = b[4];
        const b5 = b[5];
        const b6 = b[6];
        const b7 = b[7];
        const b8 = b[8];
        const b9 = b[9];
        const b10 = b[10];
        const b11 = b[11];
        const b12 = b[12];
        const b13 = b[13];
        const b14 = b[14];
        const b15 = b[15];
        return Math.abs(a0 - b0) <= GLM_EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= GLM_EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= GLM_EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= GLM_EPSILON * Math.max(1, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= GLM_EPSILON * Math.max(1, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= GLM_EPSILON * Math.max(1, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= GLM_EPSILON * Math.max(1, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= GLM_EPSILON * Math.max(1, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= GLM_EPSILON * Math.max(1, Math.abs(a8), Math.abs(b8)) && Math.abs(a9 - b9) <= GLM_EPSILON * Math.max(1, Math.abs(a9), Math.abs(b9)) && Math.abs(a10 - b10) <= GLM_EPSILON * Math.max(1, Math.abs(a10), Math.abs(b10)) && Math.abs(a11 - b11) <= GLM_EPSILON * Math.max(1, Math.abs(a11), Math.abs(b11)) && Math.abs(a12 - b12) <= GLM_EPSILON * Math.max(1, Math.abs(a12), Math.abs(b12)) && Math.abs(a13 - b13) <= GLM_EPSILON * Math.max(1, Math.abs(a13), Math.abs(b13)) && Math.abs(a14 - b14) <= GLM_EPSILON * Math.max(1, Math.abs(a14), Math.abs(b14)) && Math.abs(a15 - b15) <= GLM_EPSILON * Math.max(1, Math.abs(a15), Math.abs(b15));
      }
      /**
       * Returns a string representation of a {@link Mat4}
       * @category Static
       *
       * @param a - matrix to represent as a string
       * @returns string representation of the matrix
       */
      static str(a) {
        return `Mat4(${a.join(", ")})`;
      }
    };
    Mat4.prototype.mul = Mat4.prototype.multiply;
    Mat4.sub = Mat4.subtract;
    Mat4.mul = Mat4.multiply;
    Mat4.frustum = Mat4.frustumNO;
    Mat4.perspective = Mat4.perspectiveNO;
    Mat4.ortho = Mat4.orthoNO;
    var Vec3 = class _Vec3 extends Float32Array {
      /**
       * Create a {@link Vec3}.
       *
       * @category Constructor
       */
      constructor(...values) {
        switch (values.length) {
          case 3:
            super(values);
            break;
          case 2:
            super(values[0], values[1], 3);
            break;
          case 1: {
            const v = values[0];
            if (typeof v === "number") {
              super([v, v, v]);
            } else {
              super(v, 0, 3);
            }
            break;
          }
          default:
            super(3);
            break;
        }
      }
      // ============
      // Accessors
      // ============
      // Getters and setters to make component access read better.
      // These are likely to be a little bit slower than direct array access.
      /**
       * The x component of the vector. Equivalent to `this[0];`
       * @category Vector Components
       */
      get x() {
        return this[0];
      }
      set x(value) {
        this[0] = value;
      }
      /**
       * The y component of the vector. Equivalent to `this[1];`
       * @category Vector Components
       */
      get y() {
        return this[1];
      }
      set y(value) {
        this[1] = value;
      }
      /**
       * The z component of the vector. Equivalent to `this[2];`
       * @category Vector Components
       */
      get z() {
        return this[2];
      }
      set z(value) {
        this[2] = value;
      }
      // Alternate set of getters and setters in case this is being used to define
      // a color.
      /**
       * The r component of the vector. Equivalent to `this[0];`
       * @category Color Components
       */
      get r() {
        return this[0];
      }
      set r(value) {
        this[0] = value;
      }
      /**
       * The g component of the vector. Equivalent to `this[1];`
       * @category Color Components
       */
      get g() {
        return this[1];
      }
      set g(value) {
        this[1] = value;
      }
      /**
       * The b component of the vector. Equivalent to `this[2];`
       * @category Color Components
       */
      get b() {
        return this[2];
      }
      set b(value) {
        this[2] = value;
      }
      /**
       * The magnitude (length) of this.
       * Equivalent to `Vec3.magnitude(this);`
       *
       * Magnitude is used because the `length` attribute is already defined by
       * TypedArrays to mean the number of elements in the array.
       *
       * @category Accessors
       */
      get magnitude() {
        const x = this[0];
        const y = this[1];
        const z = this[2];
        return Math.sqrt(x * x + y * y + z * z);
      }
      /**
       * Alias for {@link Vec3.magnitude}
       *
       * @category Accessors
       */
      get mag() {
        return this.magnitude;
      }
      /**
       * The squared magnitude (length) of `this`.
       * Equivalent to `Vec3.squaredMagnitude(this);`
       *
       * @category Accessors
       */
      get squaredMagnitude() {
        const x = this[0];
        const y = this[1];
        const z = this[2];
        return x * x + y * y + z * z;
      }
      /**
       * Alias for {@link Vec3.squaredMagnitude}
       *
       * @category Accessors
       */
      get sqrMag() {
        return this.squaredMagnitude;
      }
      /**
       * A string representation of `this`
       * Equivalent to `Vec3.str(this);`
       *
       * @category Accessors
       */
      get str() {
        return _Vec3.str(this);
      }
      // ===================
      // Instances methods
      // ===================
      /**
       * Copy the values from another {@link Vec3} into `this`.
       * @category Methods
       *
       * @param a the source vector
       * @returns `this`
       */
      copy(a) {
        this.set(a);
        return this;
      }
      /**
       * Adds a {@link Vec3} to `this`.
       * Equivalent to `Vec3.add(this, this, b);`
       * @category Methods
       *
       * @param b - The vector to add to `this`
       * @returns `this`
       */
      add(b) {
        this[0] += b[0];
        this[1] += b[1];
        this[2] += b[2];
        return this;
      }
      /**
       * Subtracts a {@link Vec3} from `this`.
       * Equivalent to `Vec3.subtract(this, this, b);`
       * @category Methods
       *
       * @param b - The vector to subtract from `this`
       * @returns `this`
       */
      subtract(b) {
        this[0] -= b[0];
        this[1] -= b[1];
        this[2] -= b[2];
        return this;
      }
      /**
       * Alias for {@link Vec3.subtract}
       * @category Methods
       */
      sub(b) {
        return this;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Multiplies `this` by a {@link Vec3}.
       * Equivalent to `Vec3.multiply(this, this, b);`
       * @category Methods
       *
       * @param b - The vector to multiply `this` by
       * @returns `this`
       */
      multiply(b) {
        this[0] *= b[0];
        this[1] *= b[1];
        this[2] *= b[2];
        return this;
      }
      /**
       * Alias for {@link Vec3.multiply}
       * @category Methods
       */
      mul(b) {
        return this;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Divides `this` by a {@link Vec3}.
       * Equivalent to `Vec3.divide(this, this, b);`
       * @category Methods
       *
       * @param b - The vector to divide `this` by
       * @returns `this`
       */
      divide(b) {
        this[0] /= b[0];
        this[1] /= b[1];
        this[2] /= b[2];
        return this;
      }
      /**
       * Alias for {@link Vec3.divide}
       * @category Methods
       */
      div(b) {
        return this;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Scales `this` by a scalar number.
       * Equivalent to `Vec3.scale(this, this, b);`
       * @category Methods
       *
       * @param b - Amount to scale `this` by
       * @returns `this`
       */
      scale(b) {
        this[0] *= b;
        this[1] *= b;
        this[2] *= b;
        return this;
      }
      /**
       * Calculates `this` scaled by a scalar value then adds the result to `this`.
       * Equivalent to `Vec3.scaleAndAdd(this, this, b, scale);`
       * @category Methods
       *
       * @param b - The vector to add to `this`
       * @param scale - The amount to scale `b` by before adding
       * @returns `this`
       */
      scaleAndAdd(b, scale2) {
        this[0] += b[0] * scale2;
        this[1] += b[1] * scale2;
        this[2] += b[2] * scale2;
        return this;
      }
      /**
       * Calculates the Euclidean distance between another {@link Vec3} and `this`.
       * Equivalent to `Vec3.distance(this, b);`
       * @category Methods
       *
       * @param b - The vector to calculate the distance to
       * @returns Distance between `this` and `b`
       */
      distance(b) {
        return _Vec3.distance(this, b);
      }
      /**
       * Alias for {@link Vec3.distance}
       * @category Methods
       */
      dist(b) {
        return 0;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Calculates the squared Euclidean distance between another {@link Vec3} and `this`.
       * Equivalent to `Vec3.squaredDistance(this, b);`
       * @category Methods
       *
       * @param b The vector to calculate the squared distance to
       * @returns Squared distance between `this` and `b`
       */
      squaredDistance(b) {
        return _Vec3.squaredDistance(this, b);
      }
      /**
       * Alias for {@link Vec3.squaredDistance}
       * @category Methods
       */
      sqrDist(b) {
        return 0;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Negates the components of `this`.
       * Equivalent to `Vec3.negate(this, this);`
       * @category Methods
       *
       * @returns `this`
       */
      negate() {
        this[0] *= -1;
        this[1] *= -1;
        this[2] *= -1;
        return this;
      }
      /**
       * Inverts the components of `this`.
       * Equivalent to `Vec3.inverse(this, this);`
       * @category Methods
       *
       * @returns `this`
       */
      invert() {
        this[0] = 1 / this[0];
        this[1] = 1 / this[1];
        this[2] = 1 / this[2];
        return this;
      }
      /**
       * Sets each component of `this` to its absolute value.
       * Equivalent to `Vec3.abs(this, this);`
       * @category Methods
       *
       * @returns `this`
       */
      abs() {
        this[0] = Math.abs(this[0]);
        this[1] = Math.abs(this[1]);
        this[2] = Math.abs(this[2]);
        return this;
      }
      /**
       * Calculates the dot product of this and another {@link Vec3}.
       * Equivalent to `Vec3.dot(this, b);`
       * @category Methods
       *
       * @param b - The second operand
       * @returns Dot product of `this` and `b`
       */
      dot(b) {
        return this[0] * b[0] + this[1] * b[1] + this[2] * b[2];
      }
      /**
       * Normalize `this`.
       * Equivalent to `Vec3.normalize(this, this);`
       * @category Methods
       *
       * @returns `this`
       */
      normalize() {
        return _Vec3.normalize(this, this);
      }
      // ===================
      // Static accessors
      // ===================
      /**
       * @category Static
       *
       * @returns The number of bytes in a {@link Vec3}.
       */
      static get BYTE_LENGTH() {
        return 3 * Float32Array.BYTES_PER_ELEMENT;
      }
      // ===================
      // Static methods
      // ===================
      /**
       * Creates a new, empty vec3
       * @category Static
       *
       * @returns a new 3D vector
       */
      static create() {
        return new _Vec3();
      }
      /**
       * Creates a new vec3 initialized with values from an existing vector
       * @category Static
       *
       * @param a - vector to clone
       * @returns a new 3D vector
       */
      static clone(a) {
        return new _Vec3(a);
      }
      /**
       * Calculates the magnitude (length) of a {@link Vec3}
       * @category Static
       *
       * @param a - Vector to calculate magnitude of
       * @returns Magnitude of a
       */
      static magnitude(a) {
        const x = a[0];
        const y = a[1];
        const z = a[2];
        return Math.sqrt(x * x + y * y + z * z);
      }
      /**
       * Alias for {@link Vec3.magnitude}
       * @category Static
       */
      static mag(a) {
        return 0;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Alias for {@link Vec3.magnitude}
       * @category Static
       * @deprecated Use {@link Vec3.magnitude} to avoid conflicts with builtin `length` methods/attribs
       *
       * @param a - vector to calculate length of
       * @returns length of a
       */
      // Length conflicts with Function.length
      static length(a) {
        return 0;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Alias for {@link Vec3.magnitude}
       * @category Static
       * @deprecated Use {@link Vec3.mag}
       */
      static len(a) {
        return 0;
      }
      // eslint-disable-line @typescript-eslint/no-unused-vars
      /**
       * Creates a new vec3 initialized with the given values
       * @category Static
       *
       * @param x - X component
       * @param y - Y component
       * @param z - Z component
       * @returns a new 3D vector
       */
      static fromValues(x, y, z) {
        return new _Vec3(x, y, z);
      }
      /**
       * Copy the values from one vec3 to another
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the source vector
       * @returns `out`
       */
      static copy(out, a) {
        out[0] = a[0];
        out[1] = a[1];
        out[2] = a[2];
        return out;
      }
      /**
       * Set the components of a vec3 to the given values
       * @category Static
       *
       * @param out - the receiving vector
       * @param x - X component
       * @param y - Y component
       * @param z - Z component
       * @returns `out`
       */
      static set(out, x, y, z) {
        out[0] = x;
        out[1] = y;
        out[2] = z;
        return out;
      }
      /**
       * Adds two {@link Vec3}s
       * @category Static
       *
       * @param out - The receiving vector
       * @param a - The first operand
       * @param b - The second operand
       * @returns `out`
       */
      static add(out, a, b) {
        out[0] = a[0] + b[0];
        out[1] = a[1] + b[1];
        out[2] = a[2] + b[2];
        return out;
      }
      /**
       * Subtracts vector b from vector a
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @returns `out`
       */
      static subtract(out, a, b) {
        out[0] = a[0] - b[0];
        out[1] = a[1] - b[1];
        out[2] = a[2] - b[2];
        return out;
      }
      /**
       * Alias for {@link Vec3.subtract}
       * @category Static
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static sub(out, a, b) {
        return [0, 0, 0];
      }
      /**
       * Multiplies two vec3's
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @returns `out`
       */
      static multiply(out, a, b) {
        out[0] = a[0] * b[0];
        out[1] = a[1] * b[1];
        out[2] = a[2] * b[2];
        return out;
      }
      /**
       * Alias for {@link Vec3.multiply}
       * @category Static
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static mul(out, a, b) {
        return [0, 0, 0];
      }
      /**
       * Divides two vec3's
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @returns `out`
       */
      static divide(out, a, b) {
        out[0] = a[0] / b[0];
        out[1] = a[1] / b[1];
        out[2] = a[2] / b[2];
        return out;
      }
      /**
       * Alias for {@link Vec3.divide}
       * @category Static
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static div(out, a, b) {
        return [0, 0, 0];
      }
      /**
       * Math.ceil the components of a vec3
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - vector to ceil
       * @returns `out`
       */
      static ceil(out, a) {
        out[0] = Math.ceil(a[0]);
        out[1] = Math.ceil(a[1]);
        out[2] = Math.ceil(a[2]);
        return out;
      }
      /**
       * Math.floor the components of a vec3
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - vector to floor
       * @returns `out`
       */
      static floor(out, a) {
        out[0] = Math.floor(a[0]);
        out[1] = Math.floor(a[1]);
        out[2] = Math.floor(a[2]);
        return out;
      }
      /**
       * Returns the minimum of two vec3's
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @returns `out`
       */
      static min(out, a, b) {
        out[0] = Math.min(a[0], b[0]);
        out[1] = Math.min(a[1], b[1]);
        out[2] = Math.min(a[2], b[2]);
        return out;
      }
      /**
       * Returns the maximum of two vec3's
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @returns `out`
       */
      static max(out, a, b) {
        out[0] = Math.max(a[0], b[0]);
        out[1] = Math.max(a[1], b[1]);
        out[2] = Math.max(a[2], b[2]);
        return out;
      }
      /**
       * symmetric round the components of a vec3
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - vector to round
       * @returns `out`
       */
      /*
        static round(out: Vec3Like, a: Readonly<Vec3Like>): Vec3Like {
        out[0] = glMatrix.round(a[0]);
        out[1] = glMatrix.round(a[1]);
        out[2] = glMatrix.round(a[2]);
        return out;
      }*/
      /**
       * Scales a vec3 by a scalar number
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the vector to scale
       * @param scale - amount to scale the vector by
       * @returns `out`
       */
      static scale(out, a, scale2) {
        out[0] = a[0] * scale2;
        out[1] = a[1] * scale2;
        out[2] = a[2] * scale2;
        return out;
      }
      /**
       * Adds two vec3's after scaling the second operand by a scalar value
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @param scale - the amount to scale b by before adding
       * @returns `out`
       */
      static scaleAndAdd(out, a, b, scale2) {
        out[0] = a[0] + b[0] * scale2;
        out[1] = a[1] + b[1] * scale2;
        out[2] = a[2] + b[2] * scale2;
        return out;
      }
      /**
       * Calculates the Euclidean distance between two vec3's
       * @category Static
       *
       * @param a - the first operand
       * @param b - the second operand
       * @returns distance between a and b
       */
      static distance(a, b) {
        const x = b[0] - a[0];
        const y = b[1] - a[1];
        const z = b[2] - a[2];
        return Math.sqrt(x * x + y * y + z * z);
      }
      /**
       * Alias for {@link Vec3.distance}
       * @category Static
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static dist(a, b) {
        return 0;
      }
      /**
       * Calculates the squared Euclidean distance between two vec3's
       * @category Static
       *
       * @param a - the first operand
       * @param b - the second operand
       * @returns squared distance between a and b
       */
      static squaredDistance(a, b) {
        const x = b[0] - a[0];
        const y = b[1] - a[1];
        const z = b[2] - a[2];
        return x * x + y * y + z * z;
      }
      /**
       * Alias for {@link Vec3.squaredDistance}
       * @category Static
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static sqrDist(a, b) {
        return 0;
      }
      /**
       * Calculates the squared length of a vec3
       * @category Static
       *
       * @param a - vector to calculate squared length of
       * @returns squared length of a
       */
      static squaredLength(a) {
        const x = a[0];
        const y = a[1];
        const z = a[2];
        return x * x + y * y + z * z;
      }
      /**
       * Alias for {@link Vec3.squaredLength}
       * @category Static
       */
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      static sqrLen(a, b) {
        return 0;
      }
      /**
       * Negates the components of a vec3
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - vector to negate
       * @returns `out`
       */
      static negate(out, a) {
        out[0] = -a[0];
        out[1] = -a[1];
        out[2] = -a[2];
        return out;
      }
      /**
       * Returns the inverse of the components of a vec3
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - vector to invert
       * @returns `out`
       */
      static inverse(out, a) {
        out[0] = 1 / a[0];
        out[1] = 1 / a[1];
        out[2] = 1 / a[2];
        return out;
      }
      /**
       * Returns the absolute value of the components of a {@link Vec3}
       * @category Static
       *
       * @param out - The receiving vector
       * @param a - Vector to compute the absolute values of
       * @returns `out`
       */
      static abs(out, a) {
        out[0] = Math.abs(a[0]);
        out[1] = Math.abs(a[1]);
        out[2] = Math.abs(a[2]);
        return out;
      }
      /**
       * Normalize a vec3
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - vector to normalize
       * @returns `out`
       */
      static normalize(out, a) {
        const x = a[0];
        const y = a[1];
        const z = a[2];
        let len = x * x + y * y + z * z;
        if (len > 0) {
          len = 1 / Math.sqrt(len);
        }
        out[0] = a[0] * len;
        out[1] = a[1] * len;
        out[2] = a[2] * len;
        return out;
      }
      /**
       * Calculates the dot product of two vec3's
       * @category Static
       *
       * @param a - the first operand
       * @param b - the second operand
       * @returns dot product of a and b
       */
      static dot(a, b) {
        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
      }
      /**
       * Computes the cross product of two vec3's
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @returns `out`
       */
      static cross(out, a, b) {
        const ax = a[0], ay = a[1], az = a[2];
        const bx = b[0], by = b[1], bz = b[2];
        out[0] = ay * bz - az * by;
        out[1] = az * bx - ax * bz;
        out[2] = ax * by - ay * bx;
        return out;
      }
      /**
       * Performs a linear interpolation between two vec3's
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @param t - interpolation amount, in the range [0-1], between the two inputs
       * @returns `out`
       */
      static lerp(out, a, b, t) {
        const ax = a[0];
        const ay = a[1];
        const az = a[2];
        out[0] = ax + t * (b[0] - ax);
        out[1] = ay + t * (b[1] - ay);
        out[2] = az + t * (b[2] - az);
        return out;
      }
      /**
       * Performs a spherical linear interpolation between two vec3's
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @param t - interpolation amount, in the range [0-1], between the two inputs
       * @returns `out`
       */
      static slerp(out, a, b, t) {
        const angle = Math.acos(Math.min(Math.max(_Vec3.dot(a, b), -1), 1));
        const sinTotal = Math.sin(angle);
        const ratioA = Math.sin((1 - t) * angle) / sinTotal;
        const ratioB = Math.sin(t * angle) / sinTotal;
        out[0] = ratioA * a[0] + ratioB * b[0];
        out[1] = ratioA * a[1] + ratioB * b[1];
        out[2] = ratioA * a[2] + ratioB * b[2];
        return out;
      }
      /**
       * Performs a hermite interpolation with two control points
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @param c - the third operand
       * @param d - the fourth operand
       * @param t - interpolation amount, in the range [0-1], between the two inputs
       * @returns `out`
       */
      static hermite(out, a, b, c, d, t) {
        const factorTimes2 = t * t;
        const factor1 = factorTimes2 * (2 * t - 3) + 1;
        const factor2 = factorTimes2 * (t - 2) + t;
        const factor3 = factorTimes2 * (t - 1);
        const factor4 = factorTimes2 * (3 - 2 * t);
        out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
        out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
        out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
        return out;
      }
      /**
       * Performs a bezier interpolation with two control points
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the first operand
       * @param b - the second operand
       * @param c - the third operand
       * @param d - the fourth operand
       * @param t - interpolation amount, in the range [0-1], between the two inputs
       * @returns `out`
       */
      static bezier(out, a, b, c, d, t) {
        const inverseFactor = 1 - t;
        const inverseFactorTimesTwo = inverseFactor * inverseFactor;
        const factorTimes2 = t * t;
        const factor1 = inverseFactorTimesTwo * inverseFactor;
        const factor2 = 3 * t * inverseFactorTimesTwo;
        const factor3 = 3 * factorTimes2 * inverseFactor;
        const factor4 = factorTimes2 * t;
        out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
        out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
        out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
        return out;
      }
      /**
       * Generates a random vector with the given scale
       * @category Static
       *
       * @param out - the receiving vector
       * @param {Number} [scale] Length of the resulting vector. If omitted, a unit vector will be returned
       * @returns `out`
       */
      /*
          static random(out: Vec3Like, scale) {
          scale = scale === undefined ? 1.0 : scale;
      
          let r = glMatrix.RANDOM() * 2.0 * Math.PI;
          let z = glMatrix.RANDOM() * 2.0 - 1.0;
          let zScale = Math.sqrt(1.0 - z * z) * scale;
      
          out[0] = Math.cos(r) * zScale;
          out[1] = Math.sin(r) * zScale;
          out[2] = z * scale;
          return out;
        }*/
      /**
       * Transforms the vec3 with a mat4.
       * 4th vector component is implicitly '1'
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the vector to transform
       * @param m - matrix to transform with
       * @returns `out`
       */
      static transformMat4(out, a, m) {
        const x = a[0], y = a[1], z = a[2];
        const w = m[3] * x + m[7] * y + m[11] * z + m[15] || 1;
        out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
        out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
        out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
        return out;
      }
      /**
       * Transforms the vec3 with a mat3.
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the vector to transform
       * @param m - the 3x3 matrix to transform with
       * @returns `out`
       */
      static transformMat3(out, a, m) {
        const x = a[0], y = a[1], z = a[2];
        out[0] = x * m[0] + y * m[3] + z * m[6];
        out[1] = x * m[1] + y * m[4] + z * m[7];
        out[2] = x * m[2] + y * m[5] + z * m[8];
        return out;
      }
      /**
       * Transforms the vec3 with a quat
       * Can also be used for dual quaternions. (Multiply it with the real part)
       * @category Static
       *
       * @param out - the receiving vector
       * @param a - the vector to transform
       * @param q - quaternion to transform with
       * @returns `out`
       */
      static transformQuat(out, a, q) {
        const qx = q[0];
        const qy = q[1];
        const qz = q[2];
        const w2 = q[3] * 2;
        const x = a[0];
        const y = a[1];
        const z = a[2];
        const uvx = qy * z - qz * y;
        const uvy = qz * x - qx * z;
        const uvz = qx * y - qy * x;
        const uuvx = (qy * uvz - qz * uvy) * 2;
        const uuvy = (qz * uvx - qx * uvz) * 2;
        const uuvz = (qx * uvy - qy * uvx) * 2;
        out[0] = x + uvx * w2 + uuvx;
        out[1] = y + uvy * w2 + uuvy;
        out[2] = z + uvz * w2 + uuvz;
        return out;
      }
      /**
       * Rotate a 3D vector around the x-axis
       * @category Static
       *
       * @param out - The receiving vec3
       * @param a - The vec3 point to rotate
       * @param b - The origin of the rotation
       * @param rad - The angle of rotation in radians
       * @returns `out`
       */
      static rotateX(out, a, b, rad) {
        const by = b[1];
        const bz = b[2];
        const py = a[1] - by;
        const pz = a[2] - bz;
        out[0] = a[0];
        out[1] = py * Math.cos(rad) - pz * Math.sin(rad) + by;
        out[2] = py * Math.sin(rad) + pz * Math.cos(rad) + bz;
        return out;
      }
      /**
       * Rotate a 3D vector around the y-axis
       * @category Static
       *
       * @param out - The receiving vec3
       * @param a - The vec3 point to rotate
       * @param b - The origin of the rotation
       * @param rad - The angle of rotation in radians
       * @returns `out`
       */
      static rotateY(out, a, b, rad) {
        const bx = b[0];
        const bz = b[2];
        const px = a[0] - bx;
        const pz = a[2] - bz;
        out[0] = pz * Math.sin(rad) + px * Math.cos(rad) + bx;
        out[1] = a[1];
        out[2] = pz * Math.cos(rad) - px * Math.sin(rad) + bz;
        return out;
      }
      /**
       * Rotate a 3D vector around the z-axis
       * @category Static
       *
       * @param out - The receiving vec3
       * @param a - The vec3 point to rotate
       * @param b - The origin of the rotation
       * @param rad - The angle of rotation in radians
       * @returns `out`
       */
      static rotateZ(out, a, b, rad) {
        const bx = b[0];
        const by = b[1];
        const px = a[0] - bx;
        const py = a[1] - by;
        out[0] = px * Math.cos(rad) - py * Math.sin(rad) + bx;
        out[1] = px * Math.sin(rad) + py * Math.cos(rad) + by;
        out[2] = b[2];
        return out;
      }
      /**
       * Get the angle between two 3D vectors
       * @category Static
       *
       * @param a - The first operand
       * @param b - The second operand
       * @returns The angle in radians
       */
      static angle(a, b) {
        const ax = a[0];
        const ay = a[1];
        const az = a[2];
        const bx = b[0];
        const by = b[1];
        const bz = b[2];
        const mag = Math.sqrt((ax * ax + ay * ay + az * az) * (bx * bx + by * by + bz * bz));
        const cosine = mag && _Vec3.dot(a, b) / mag;
        return Math.acos(Math.min(Math.max(cosine, -1), 1));
      }
      /**
       * Set the components of a vec3 to zero
       * @category Static
       *
       * @param out - the receiving vector
       * @returns `out`
       */
      static zero(out) {
        out[0] = 0;
        out[1] = 0;
        out[2] = 0;
        return out;
      }
      /**
       * Returns a string representation of a vector
       * @category Static
       *
       * @param a - vector to represent as a string
       * @returns string representation of the vector
       */
      static str(a) {
        return `Vec3(${a.join(", ")})`;
      }
      /**
       * Returns whether the vectors have exactly the same elements in the same position (when compared with ===)
       * @category Static
       *
       * @param a - The first vector.
       * @param b - The second vector.
       * @returns True if the vectors are equal, false otherwise.
       */
      static exactEquals(a, b) {
        return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
      }
      /**
       * Returns whether the vectors have approximately the same elements in the same position.
       * @category Static
       *
       * @param a - The first vector.
       * @param b - The second vector.
       * @returns True if the vectors are equal, false otherwise.
       */
      static equals(a, b) {
        const a0 = a[0];
        const a1 = a[1];
        const a2 = a[2];
        const b0 = b[0];
        const b1 = b[1];
        const b2 = b[2];
        return Math.abs(a0 - b0) <= GLM_EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= GLM_EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= GLM_EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2));
      }
    };
    Vec3.prototype.sub = Vec3.prototype.subtract;
    Vec3.prototype.mul = Vec3.prototype.multiply;
    Vec3.prototype.div = Vec3.prototype.divide;
    Vec3.prototype.dist = Vec3.prototype.distance;
    Vec3.prototype.sqrDist = Vec3.prototype.squaredDistance;
    Vec3.sub = Vec3.subtract;
    Vec3.mul = Vec3.multiply;
    Vec3.div = Vec3.divide;
    Vec3.dist = Vec3.distance;
    Vec3.sqrDist = Vec3.squaredDistance;
    Vec3.sqrLen = Vec3.squaredLength;
    Vec3.mag = Vec3.magnitude;
    Vec3.length = Vec3.magnitude;
    Vec3.len = Vec3.magnitude;
    class StyleParse {
      static #regexPixels = /(\d+)\s*px/;
      /**
       * Parses a pixel string / computed styles. Ex. `100px` returns `100`.
       *
       * @param {string}   value - Value to parse.
       *
       * @returns {number|undefined} The integer component of a pixel string.
       */
      static pixels(value) {
        if (typeof value !== "string") {
          return void 0;
        }
        const isPixels = this.#regexPixels.test(value);
        const number = parseInt(value);
        return isPixels && Number.isFinite(number) ? number : void 0;
      }
      /**
       * Returns the pixel value for `1rem` based on the root document element. You may apply an optional multiplier.
       *
       * @param {number} [multiplier=1] - Optional multiplier to apply to `rem` pixel value; default: 1.
       *
       * @param {object} [options] - Optional parameters.
       *
       * @param {Document} [options.targetDocument=document] The target DOM {@link Document} if different from the main
       *        browser global `document`.
       *
       * @returns {number} The pixel value for `1rem` with or without a multiplier based on the root document element.
       */
      static remPixels(multiplier = 1, { targetDocument = document } = {}) {
        return targetDocument?.documentElement ? multiplier * parseFloat(globalThis.getComputedStyle(targetDocument.documentElement).fontSize) : void 0;
      }
    }
    class TJSStyleManager {
      /** @type {CSSStyleRule} */
      #cssRule;
      /** @type {string} */
      #docKey;
      /** @type {string} */
      #selector;
      /** @type {HTMLStyleElement} */
      #styleElement;
      /** @type {number} */
      #version;
      /**
       *
       * @param {object}   opts - Options.
       *
       * @param {string}   opts.docKey - Required key providing a link to a specific style sheet element.
       *
       * @param {string}   [opts.selector=:root] - Selector element.
       *
       * @param {Document} [opts.document] - Target document to load styles into.
       *
       * @param {number}   [opts.version] - An integer representing the version / level of styles being managed.
       */
      constructor({ docKey, selector = ":root", document: document2 = globalThis.document, version } = {}) {
        if (typeof docKey !== "string") {
          throw new TypeError(`StyleManager error: 'docKey' is not a string.`);
        }
        if (Object.prototype.toString.call(document2) !== "[object HTMLDocument]") {
          throw new TypeError(`TJSStyleManager error: 'document' is not an instance of HTMLDocument.`);
        }
        if (typeof selector !== "string") {
          throw new TypeError(`StyleManager error: 'selector' is not a string.`);
        }
        if (version !== void 0 && !Number.isSafeInteger(version) && version < 1) {
          throw new TypeError(`StyleManager error: 'version' is defined and is not a positive integer >= 1.`);
        }
        this.#selector = selector;
        this.#docKey = docKey;
        this.#version = version;
        if (document2[this.#docKey] === void 0) {
          this.#styleElement = document2.createElement("style");
          document2.head.append(this.#styleElement);
          this.#styleElement._STYLE_MANAGER_VERSION = version;
          this.#styleElement.sheet.insertRule(`${selector} {}`, 0);
          this.#cssRule = this.#styleElement.sheet.cssRules[0];
          document2[docKey] = this.#styleElement;
        } else {
          this.#styleElement = document2[docKey];
          this.#cssRule = this.#styleElement.sheet.cssRules[0];
          if (version) {
            const existingVersion = this.#styleElement._STYLE_MANAGER_VERSION ?? 0;
            if (version > existingVersion) {
              this.#cssRule.style.cssText = "";
            }
          }
        }
      }
      /**
       * @returns {string} Provides an accessor to get the `cssText` for the style sheet.
       */
      get cssText() {
        return this.#cssRule.style.cssText;
      }
      /**
       * @returns {number} Returns the version of this instance.
       */
      get version() {
        return this.#version;
      }
      /**
       * Provides a copy constructor to duplicate an existing TJSStyleManager instance into a new document.
       *
       * Note: This is used to support the `PopOut` module.
       *
       * @param {Document} [document] Target browser document to clone into.
       *
       * @returns {TJSStyleManager} New style manager instance.
       */
      clone(document2 = globalThis.document) {
        const newStyleManager = new TJSStyleManager({
          selector: this.#selector,
          docKey: this.#docKey,
          document: document2,
          version: this.#version
        });
        newStyleManager.#cssRule.style.cssText = this.#cssRule.style.cssText;
        return newStyleManager;
      }
      get() {
        const cssText = this.#cssRule.style.cssText;
        const result = {};
        if (cssText !== "") {
          for (const entry of cssText.split(";")) {
            if (entry !== "") {
              const values = entry.split(":");
              result[values[0].trim()] = values[1];
            }
          }
        }
        return result;
      }
      /**
       * Gets a particular CSS variable.
       *
       * @param {string}   key - CSS variable property key.
       *
       * @returns {string} Returns CSS variable value.
       */
      getProperty(key) {
        if (typeof key !== "string") {
          throw new TypeError(`StyleManager error: 'key' is not a string.`);
        }
        return this.#cssRule.style.getPropertyValue(key);
      }
      /**
       * Set rules by property / value; useful for CSS variables.
       *
       * @param {{ [key: string]: string }}  rules - An object with property / value string pairs to load.
       *
       * @param {boolean}                 [overwrite=true] - When true overwrites any existing values.
       */
      setProperties(rules, overwrite = true) {
        if (!isObject(rules)) {
          throw new TypeError(`StyleManager error: 'rules' is not an object.`);
        }
        if (typeof overwrite !== "boolean") {
          throw new TypeError(`StyleManager error: 'overwrite' is not a boolean.`);
        }
        if (overwrite) {
          for (const [key, value] of Object.entries(rules)) {
            this.#cssRule.style.setProperty(key, value);
          }
        } else {
          for (const [key, value] of Object.entries(rules)) {
            if (this.#cssRule.style.getPropertyValue(key) === "") {
              this.#cssRule.style.setProperty(key, value);
            }
          }
        }
      }
      /**
       * Sets a particular property.
       *
       * @param {string}   key - CSS variable property key.
       *
       * @param {string}   value - CSS variable value.
       *
       * @param {boolean}  [overwrite=true] - Overwrite any existing value.
       */
      setProperty(key, value, overwrite = true) {
        if (typeof key !== "string") {
          throw new TypeError(`StyleManager error: 'key' is not a string.`);
        }
        if (typeof value !== "string") {
          throw new TypeError(`StyleManager error: 'value' is not a string.`);
        }
        if (typeof overwrite !== "boolean") {
          throw new TypeError(`StyleManager error: 'overwrite' is not a boolean.`);
        }
        if (overwrite) {
          this.#cssRule.style.setProperty(key, value);
        } else {
          if (this.#cssRule.style.getPropertyValue(key) === "") {
            this.#cssRule.style.setProperty(key, value);
          }
        }
      }
      /**
       * Removes the property keys specified. If `keys` is an iterable list then all property keys in the list are removed.
       *
       * @param {Iterable<string>} keys - The property keys to remove.
       */
      removeProperties(keys) {
        if (!isIterable(keys)) {
          throw new TypeError(`StyleManager error: 'keys' is not an iterable list.`);
        }
        for (const key of keys) {
          if (typeof key === "string") {
            this.#cssRule.style.removeProperty(key);
          }
        }
      }
      /**
       * Removes a particular CSS variable.
       *
       * @param {string}   key - CSS variable property key.
       *
       * @returns {string} CSS variable value when removed.
       */
      removeProperty(key) {
        if (typeof key !== "string") {
          throw new TypeError(`StyleManager error: 'key' is not a string.`);
        }
        return this.#cssRule.style.removeProperty(key);
      }
    }
    async function nextAnimationFrame(cntr = 1) {
      if (!Number.isInteger(cntr) || cntr < 1) {
        throw new TypeError(`nextAnimationFrame error: 'cntr' must be a positive integer greater than 0.`);
      }
      let currentTime;
      for (; --cntr >= 0; ) {
        currentTime = await new Promise((resolve) => requestAnimationFrame(resolve));
      }
      return currentTime;
    }
    function draggable(node, { position, enabled = true, button = 0, storeDragging = void 0, tween = false, tweenOptions = { duration: 1, ease: "cubicOut" }, hasTargetClassList, ignoreTargetClassList }) {
      if (hasTargetClassList !== void 0 && !isIterable(hasTargetClassList)) {
        throw new TypeError(`'hasTargetClassList' is not iterable.`);
      }
      if (ignoreTargetClassList !== void 0 && !isIterable(ignoreTargetClassList)) {
        throw new TypeError(`'ignoreTargetClassList' is not iterable.`);
      }
      const positionData = { left: 0, top: 0 };
      let actualPosition = position?.position ?? position;
      let initialPosition = null;
      let initialDragPoint = { x: 0, y: 0 };
      let dragging = false;
      let quickTo = actualPosition.animate.quickTo(["top", "left"], tweenOptions);
      const handlers = {
        dragDown: ["pointerdown", onDragPointerDown, false],
        dragMove: ["pointermove", onDragPointerChange, false],
        dragUp: ["pointerup", onDragPointerUp, false]
      };
      function activateListeners() {
        node.addEventListener(...handlers.dragDown);
        node.classList.add("draggable");
      }
      function removeListeners() {
        if (typeof storeDragging?.set === "function") {
          storeDragging.set(false);
        }
        node.removeEventListener(...handlers.dragDown);
        node.removeEventListener(...handlers.dragMove);
        node.removeEventListener(...handlers.dragUp);
        node.classList.remove("draggable");
      }
      if (enabled) {
        activateListeners();
      }
      function onDragPointerDown(event) {
        if (event.button !== button || !event.isPrimary) {
          return;
        }
        if (!actualPosition.enabled) {
          return;
        }
        if (ignoreTargetClassList !== void 0 && A11yHelper.isFocusTarget(event.target)) {
          for (const targetClass of ignoreTargetClassList) {
            if (event.target.classList.contains(targetClass)) {
              return;
            }
          }
        }
        if (hasTargetClassList !== void 0 && A11yHelper.isFocusTarget(event.target)) {
          let foundTarget = false;
          for (const targetClass of hasTargetClassList) {
            if (event.target.classList.contains(targetClass)) {
              foundTarget = true;
              break;
            }
          }
          if (!foundTarget) {
            return;
          }
        }
        event.preventDefault();
        dragging = false;
        initialPosition = actualPosition.get();
        initialDragPoint = { x: event.clientX, y: event.clientY };
        node.addEventListener(...handlers.dragMove);
        node.addEventListener(...handlers.dragUp);
        node.setPointerCapture(event.pointerId);
      }
      function onDragPointerChange(event) {
        if ((event.buttons & 1) === 0) {
          onDragPointerUp(event);
          return;
        }
        if (event.button !== -1 || !event.isPrimary) {
          return;
        }
        event.preventDefault();
        if (!dragging && typeof storeDragging?.set === "function") {
          dragging = true;
          storeDragging.set(true);
        }
        const newLeft = initialPosition?.left + (event.clientX - initialDragPoint.x);
        const newTop = initialPosition?.top + (event.clientY - initialDragPoint.y);
        if (tween) {
          quickTo(newTop, newLeft);
        } else {
          positionData.left = newLeft;
          positionData.top = newTop;
          actualPosition.set(positionData);
        }
      }
      function onDragPointerUp(event) {
        event.preventDefault();
        dragging = false;
        if (typeof storeDragging?.set === "function") {
          storeDragging.set(false);
        }
        node.removeEventListener(...handlers.dragMove);
        node.removeEventListener(...handlers.dragUp);
      }
      return {
        // The default of enabled being true won't automatically add listeners twice.
        update: (options) => {
          if (options.position !== void 0) {
            const newPosition = options.position?.position ?? options.position;
            if (newPosition !== actualPosition) {
              actualPosition = newPosition;
              quickTo = actualPosition.animate.quickTo(["top", "left"], tweenOptions);
            }
          }
          if (typeof options.enabled === "boolean") {
            enabled = options.enabled;
            if (enabled) {
              activateListeners();
            } else {
              removeListeners();
            }
          }
          if (typeof options.button === "number") {
            button = options.button;
          }
          if (typeof options.tween === "boolean") {
            tween = options.tween;
          }
          if (isObject(options.tweenOptions)) {
            tweenOptions = options.tweenOptions;
            quickTo.options(tweenOptions);
          }
          if (options.hasTargetClassList !== void 0) {
            if (!isIterable(options.hasTargetClassList)) {
              throw new TypeError(`'hasTargetClassList' is not iterable.`);
            } else {
              hasTargetClassList = options.hasTargetClassList;
            }
          }
          if (options.ignoreTargetClassList !== void 0) {
            if (!isIterable(options.ignoreTargetClassList)) {
              throw new TypeError(`'ignoreTargetClassList' is not iterable.`);
            } else {
              ignoreTargetClassList = options.ignoreTargetClassList;
            }
          }
        },
        destroy: () => removeListeners()
      };
    }
    class DraggableOptionsStore {
      tween;
      tweenOptions;
      #initialTween;
      /**
       */
      #initialTweenOptions;
      #tween = false;
      /**
       */
      #tweenOptions = { duration: 1, ease: "cubicOut" };
      /**
       * Stores the subscribers.
       */
      #subscribers = [];
      /**
       * @param [opts] - Optional parameters.
       *
       * @param [opts.tween = false] - Tween enabled.
       *
       * @param [opts.tweenOptions] - Quick tween options.
       */
      constructor({ tween = false, tweenOptions } = {}) {
        Object.defineProperty(this, "tween", {
          get: () => {
            return this.#tween;
          },
          set: (newTween) => {
            if (typeof newTween !== "boolean") {
              throw new TypeError(`'tween' is not a boolean.`);
            }
            this.#tween = newTween;
            this.#updateSubscribers();
          },
          enumerable: true
        });
        Object.defineProperty(this, "tweenOptions", {
          get: () => {
            return this.#tweenOptions;
          },
          set: (newTweenOptions) => {
            if (!isObject(newTweenOptions)) {
              throw new TypeError(`'tweenOptions' is not an object.`);
            }
            if (newTweenOptions.duration !== void 0) {
              if (!Number.isFinite(newTweenOptions.duration)) {
                throw new TypeError(`'tweenOptions.duration' is not a finite number.`);
              }
              if (newTweenOptions.duration < 0) {
                this.#tweenOptions.duration = 0;
              } else {
                this.#tweenOptions.duration = newTweenOptions.duration;
              }
            }
            if (newTweenOptions.ease !== void 0) {
              const easeFn = getEasingFunc(newTweenOptions.ease);
              if (typeof easeFn !== "function") {
                throw new TypeError(`'tweenOptions.ease' is not a function or Svelte easing function name.`);
              }
              this.#tweenOptions.ease = newTweenOptions.ease;
            }
            this.#updateSubscribers();
          },
          enumerable: true
        });
        if (tween !== void 0) {
          this.tween = tween;
        }
        if (tweenOptions !== void 0) {
          this.tweenOptions = tweenOptions;
        }
        this.#initialTween = this.#tween;
        this.#initialTweenOptions = Object.assign({}, this.#tweenOptions);
      }
      /**
       * @returns Get tween duration.
       */
      get tweenDuration() {
        return this.#tweenOptions.duration;
      }
      /**
       * @returns Get easing function or easing function name.
       */
      get tweenEase() {
        return this.#tweenOptions.ease;
      }
      /**
       * @param duration - Set tween duration.
       */
      set tweenDuration(duration) {
        if (!Number.isFinite(duration)) {
          throw new TypeError(`'duration' is not a finite number.`);
        }
        if (duration < 0) {
          duration = 0;
        }
        this.#tweenOptions.duration = duration;
        this.#updateSubscribers();
      }
      /**
       * @param ease - Set easing function by name or direct function.
       */
      set tweenEase(ease) {
        const easeFn = getEasingFunc(ease);
        if (typeof easeFn !== "function") {
          throw new TypeError(`'ease' is not a function or Svelte easing function name.`);
        }
        this.#tweenOptions.ease = ease;
        this.#updateSubscribers();
      }
      /**
       * Resets all options data to initial values.
       */
      reset() {
        this.#tween = this.#initialTween;
        this.#tweenOptions = Object.assign({}, this.#initialTweenOptions);
        this.#updateSubscribers();
      }
      /**
       * Resets tween enabled state to initial value.
       */
      resetTween() {
        this.#tween = this.#initialTween;
        this.#updateSubscribers();
      }
      /**
       * Resets tween options to initial values.
       */
      resetTweenOptions() {
        this.#tweenOptions = Object.assign({}, this.#initialTweenOptions);
        this.#updateSubscribers();
      }
      /**
       * Store subscribe method.
       *
       * @param handler - Callback function that is invoked on update / changes. Receives the DraggableOptionsStore
       *        instance.
       *
       * @returns Unsubscribe function.
       */
      subscribe(handler) {
        const currentIdx = this.#subscribers.findIndex((entry) => entry === handler);
        if (currentIdx === -1) {
          this.#subscribers.push(handler);
          handler(this);
        }
        return () => {
          const existingIdx = this.#subscribers.findIndex((entry) => entry === handler);
          if (existingIdx !== -1) {
            this.#subscribers.splice(existingIdx, 1);
          }
        };
      }
      #updateSubscribers() {
        const subscriptions = this.#subscribers;
        if (subscriptions.length > 0) {
          for (let cntr = 0; cntr < subscriptions.length; cntr++) {
            subscriptions[cntr](this);
          }
        }
      }
    }
    draggable.options = (options) => new DraggableOptionsStore(options);
    class AnimationControl {
      /**
       */
      #animationData;
      /**
       */
      #finishedPromise;
      /**
       */
      #willFinish;
      /**
       * Defines a static empty / void animation control.
       */
      static #voidControl = new AnimationControl(null);
      /**
       * Provides a static void / undefined AnimationControl that is automatically resolved.
       */
      static get voidControl() {
        return this.#voidControl;
      }
      /**
       * @param [animationData] - Animation data.
       *
       * @param [willFinish] - Promise that tracks animation finished state.
       */
      constructor(animationData, willFinish = false) {
        this.#animationData = animationData;
        this.#willFinish = willFinish;
        if (isObject(animationData)) {
          animationData.control = this;
        }
      }
      /**
       * Get a promise that resolves when animation is finished.
       *
       * @returns Animation finished Promise.
       */
      get finished() {
        if (!CrossWindow.isPromise(this.#finishedPromise)) {
          this.#finishedPromise = this.#willFinish ? new Promise((resolve) => this.#animationData.resolve = resolve) : Promise.resolve({ cancelled: false });
        }
        return this.#finishedPromise;
      }
      /**
       * Returns whether this animation is currently active / animating.
       *
       * Note: a delayed animation may not be started / active yet. Use {@link AnimationControl.isFinished} to determine
       * if an animation is actually finished.
       *
       * @returns Animation active state.
       */
      get isActive() {
        return this.#animationData?.active ?? false;
      }
      /**
       * Returns whether this animation is completely finished.
       *
       * @returns Animation finished state.
       */
      get isFinished() {
        return this.#animationData?.finished ?? true;
      }
      /**
       * Cancels the animation.
       */
      cancel() {
        const animationData = this.#animationData;
        if (animationData === null || animationData === void 0) {
          return;
        }
        animationData.cancelled = true;
      }
    }
    class AnimationManager {
      /**
       * Cancels all animations except `quickTo` animations.
       */
      static cancelFn = (data) => data?.quickTo !== true;
      /**
       * Cancels all animations.
       */
      static cancelAllFn = () => true;
      /**
       * Defines the options used for {@link TJSPosition.set}.
       */
      static #tjsPositionSetOptions = Object.freeze({ immediateElementUpdate: true });
      /**
       */
      static #activeList = [];
      /**
       * Provides the `this` context for {@link AnimationManager.animate} to be scheduled on rAF.
       */
      static #animateBound = (timeFrame) => this.animate(timeFrame);
      /**
       */
      static #pendingList = [];
      /**
       * Tracks whether a requestAnimationFrame callback is pending via {@link AnimationManager.add};
       */
      static #rafPending = false;
      /**
       * Time of last `rAF` callback.
       */
      static #timeFrame;
      /**
       * Time of `performance.now()` at last `rAF` callback.
       */
      static #timeNow;
      /**
       * @returns Time of last `rAF` callback.
       */
      static get timeFrame() {
        return this.#timeFrame;
      }
      /**
       * @returns Time of `performance.now()` at last `rAF` callback.
       */
      static get timeNow() {
        return this.#timeNow;
      }
      /**
       * Add animation data.
       *
       * @param data -
       */
      static add(data) {
        if (data.cancelled) {
          this.#cleanupData(data);
          return;
        }
        AnimationManager.#pendingList.push(data);
        if (!AnimationManager.#rafPending) {
          AnimationManager.#rafPending = true;
          globalThis.requestAnimationFrame(this.#animateBound);
        }
      }
      /**
       * Manage all animation.
       *
       * @param timeFrame - rAF callback time.
       */
      static animate(timeFrame) {
        AnimationManager.#rafPending = false;
        AnimationManager.#timeNow = globalThis.performance.now();
        AnimationManager.#timeFrame = timeFrame;
        if (AnimationManager.#activeList.length === 0 && AnimationManager.#pendingList.length === 0) {
          return;
        }
        if (AnimationManager.#pendingList.length) {
          for (let cntr = AnimationManager.#pendingList.length; --cntr >= 0; ) {
            const data = AnimationManager.#pendingList[cntr];
            if (data.cancelled || data.el !== void 0 && !data.el.isConnected) {
              AnimationManager.#pendingList.splice(cntr, 1);
              this.#cleanupData(data);
            }
            if (data.active) {
              if (data.transformOrigin) {
                data.position.set({ transformOrigin: data.transformOrigin });
              }
              data.start = AnimationManager.#timeFrame;
              AnimationManager.#pendingList.splice(cntr, 1);
              AnimationManager.#activeList.push(data);
            }
          }
        }
        for (let cntr = AnimationManager.#activeList.length; --cntr >= 0; ) {
          const data = AnimationManager.#activeList[cntr];
          if (data.cancelled || data.el !== void 0 && !data.el.isConnected) {
            AnimationManager.#activeList.splice(cntr, 1);
            this.#cleanupData(data);
            continue;
          }
          data.current = timeFrame - data.start;
          if (data.current >= data.duration) {
            for (let dataCntr = data.keys.length; --dataCntr >= 0; ) {
              const key = data.keys[dataCntr];
              data.newData[key] = data.destination[key];
            }
            data.position.set(data.newData, AnimationManager.#tjsPositionSetOptions);
            AnimationManager.#activeList.splice(cntr, 1);
            this.#cleanupData(data);
            continue;
          }
          const easedTime = data.ease(data.current / data.duration);
          for (let dataCntr = data.keys.length; --dataCntr >= 0; ) {
            const key = data.keys[dataCntr];
            data.newData[key] = data.interpolate(data.initial[key], data.destination[key], easedTime);
          }
          data.position.set(data.newData, AnimationManager.#tjsPositionSetOptions);
        }
        globalThis.requestAnimationFrame(this.#animateBound);
      }
      /**
       * Cancels all animations for given TJSPosition instance.
       *
       * @param position - TJSPosition instance.
       *
       * @param [cancelFn] - An optional function to control cancelling animations.
       */
      static cancel(position, cancelFn = AnimationManager.cancelFn) {
        for (let cntr = AnimationManager.#activeList.length; --cntr >= 0; ) {
          const data = AnimationManager.#activeList[cntr];
          if (data.position === position && cancelFn(data)) {
            AnimationManager.#activeList.splice(cntr, 1);
            data.cancelled = true;
            this.#cleanupData(data);
          }
        }
        for (let cntr = AnimationManager.#pendingList.length; --cntr >= 0; ) {
          const data = AnimationManager.#pendingList[cntr];
          if (data.position === position && cancelFn(data)) {
            AnimationManager.#pendingList.splice(cntr, 1);
            data.cancelled = true;
            this.#cleanupData(data);
          }
        }
      }
      /**
       * Cancels all active and delayed animations.
       */
      static cancelAll() {
        for (let cntr = AnimationManager.#activeList.length; --cntr >= 0; ) {
          const data = AnimationManager.#activeList[cntr];
          data.cancelled = true;
          this.#cleanupData(data);
        }
        for (let cntr = AnimationManager.#pendingList.length; --cntr >= 0; ) {
          const data = AnimationManager.#pendingList[cntr];
          data.cancelled = true;
          this.#cleanupData(data);
        }
        AnimationManager.#activeList.length = 0;
        AnimationManager.#pendingList.length = 0;
      }
      /**
       * @param data - Animation data to cleanup.
       */
      static #cleanupData(data) {
        data.active = false;
        data.finished = true;
        if (data.transformOriginInitial) {
          data.position.set({ transformOrigin: data.transformOriginInitial });
        }
        if (typeof data.cleanup === "function") {
          data.cleanup(data);
        }
        if (typeof data.resolve === "function") {
          data.resolve({ cancelled: data.cancelled });
        }
        if (!data.quickTo) {
          data.cleanup = void 0;
          data.control = void 0;
          data.destination = void 0;
          data.el = void 0;
          data.ease = void 0;
          data.initial = void 0;
          data.interpolate = void 0;
          data.keys = void 0;
          data.newData = void 0;
          data.position = void 0;
          data.resolve = void 0;
        }
      }
      /**
       * Gets all {@link AnimationControl} instances for a given TJSPosition instance.
       *
       * @param position - TJSPosition instance.
       *
       * @returns All scheduled AnimationControl instances for the given TJSPosition instance.
       */
      static getScheduled(position) {
        const results = [];
        for (let cntr = AnimationManager.#activeList.length; --cntr >= 0; ) {
          const data = AnimationManager.#activeList[cntr];
          if (data.position === position && data.control) {
            results.push(data.control);
          }
        }
        for (let cntr = AnimationManager.#pendingList.length; --cntr >= 0; ) {
          const data = AnimationManager.#pendingList[cntr];
          if (data.position === position && data.control) {
            results.push(data.control);
          }
        }
        return results;
      }
      /**
       * Returns the status of any scheduled or pending animations for the given {@link TJSPosition} instance.
       *
       * @param position - TJSPosition instance.
       *
       * @param [options] - Scheduling options.
       *
       * @returns True if scheduled / false if not.
       */
      static isScheduled(position, { active: active2 = true, pending = true } = {}) {
        if (active2) {
          for (let cntr = AnimationManager.#activeList.length; --cntr >= 0; ) {
            if (AnimationManager.#activeList[cntr].position === position) {
              return true;
            }
          }
        }
        if (pending) {
          for (let cntr = AnimationManager.#pendingList.length; --cntr >= 0; ) {
            if (AnimationManager.#pendingList[cntr].position === position) {
              return true;
            }
          }
        }
        return false;
      }
    }
    class TJSPositionData {
      height;
      left;
      maxHeight;
      maxWidth;
      minHeight;
      minWidth;
      rotateX;
      rotateY;
      rotateZ;
      scale;
      top;
      transformOrigin;
      translateX;
      translateY;
      translateZ;
      width;
      zIndex;
      /**
       * @param [opts] - Options.
       *
       * @param [opts.height] -
       *
       * @param [opts.left] -
       *
       * @param [opts.maxHeight] -
       *
       * @param [opts.maxWidth] -
       *
       * @param [opts.minHeight] -
       *
       * @param [opts.minWidth] -
       *
       * @param [opts.rotateX] -
       *
       * @param [opts.rotateY] -
       *
       * @param [opts.rotateZ] -
       *
       * @param [opts.scale] -
       *
       * @param [opts.translateX] -
       *
       * @param [opts.translateY] -
       *
       * @param [opts.translateZ] -
       *
       * @param [opts.top] -
       *
       * @param [opts.transformOrigin] -
       *
       * @param [opts.width] -
       *
       * @param [opts.zIndex] -
       *
       * @param [opts.rotation] - Alias for `rotateZ`.
       */
      constructor({ height = null, left = null, maxHeight = null, maxWidth = null, minHeight = null, minWidth = null, rotateX = null, rotateY = null, rotateZ = null, scale: scale2 = null, translateX = null, translateY = null, translateZ = null, top = null, transformOrigin = null, width: width2 = null, zIndex = null } = {}) {
        this.height = height;
        this.left = left;
        this.maxHeight = maxHeight;
        this.maxWidth = maxWidth;
        this.minHeight = minHeight;
        this.minWidth = minWidth;
        this.rotateX = rotateX;
        this.rotateY = rotateY;
        this.rotateZ = rotateZ;
        this.scale = scale2;
        this.top = top;
        this.transformOrigin = transformOrigin;
        this.translateX = translateX;
        this.translateY = translateY;
        this.translateZ = translateZ;
        this.width = width2;
        this.zIndex = zIndex;
      }
    }
    class TJSPositionDataUtil {
      /**
       * Stores the TJSPositionData properties that can be animated.
       */
      static #animateKeys = Object.freeze(/* @__PURE__ */ new Set([
        // Main keys
        "left",
        "top",
        "maxWidth",
        "maxHeight",
        "minWidth",
        "minHeight",
        "width",
        "height",
        "rotateX",
        "rotateY",
        "rotateZ",
        "scale",
        "translateX",
        "translateY",
        "translateZ",
        "zIndex",
        // Aliases
        "rotation"
      ]));
      /**
       * Stores the TJSPositionData property aliases that can be animated.
       */
      static #animateKeyAliases = Object.freeze(/* @__PURE__ */ new Map([["rotation", "rotateZ"]]));
      /**
       * Provides numeric defaults for all parameters. This is used by {@link TJSPosition.get} to optionally provide
       * numeric defaults.
       */
      static #numericDefaults = Object.freeze({
        // Other keys
        height: 0,
        left: 0,
        maxHeight: null,
        maxWidth: null,
        minHeight: null,
        minWidth: null,
        top: 0,
        transformOrigin: null,
        width: 0,
        zIndex: null,
        rotateX: 0,
        rotateY: 0,
        rotateZ: 0,
        scale: 1,
        translateX: 0,
        translateY: 0,
        translateZ: 0
      });
      /**
       * Convenience to copy from source to target of two TJSPositionData like objects. If a target is not supplied a new
       * {@link TJSPositionData} instance is created.
       *
       * @param source - The source instance to copy from.
       *
       * @param [target] - Target TJSPositionData like object; if one is not provided a new instance is created.
       *
       * @returns The target instance with all TJSPositionData fields.
       */
      static copyData(source, target = new TJSPositionData()) {
        target.height = source.height ?? null;
        target.left = source.left ?? null;
        target.maxHeight = source.maxHeight ?? null;
        target.maxWidth = source.maxWidth ?? null;
        target.minHeight = source.minHeight ?? null;
        target.minWidth = source.minWidth ?? null;
        target.rotateX = source.rotateX ?? null;
        target.rotateY = source.rotateY ?? null;
        target.rotateZ = source.rotateZ ?? null;
        target.scale = source.scale ?? null;
        target.top = source.top ?? null;
        target.transformOrigin = source.transformOrigin ?? null;
        target.translateX = source.translateX ?? null;
        target.translateY = source.translateY ?? null;
        target.translateZ = source.translateZ ?? null;
        target.width = source.width ?? null;
        target.zIndex = source.zIndex ?? null;
        return target;
      }
      /**
       * Returns the non-aliased animation key.
       *
       * @param key - Animation key / possibly aliased key.
       *
       * @returns Actual non-aliased animation key.
       */
      static getAnimationKey(key) {
        return this.#animateKeyAliases.get(key) ?? key;
      }
      /**
       * Queries an object by the given key or otherwise returns any numeric default.
       *
       * @param data - An object to query for the given animation key.
       *
       * @param key - Animation key.
       *
       * @returns Data at key or numeric default.
       */
      static getDataOrDefault(data, key) {
        key = this.#animateKeyAliases.get(key) ?? key;
        return data[key] ?? this.#numericDefaults[key];
      }
      /**
       * Tests if the given key is an animation key.
       *
       * @param key - A potential animation key.
       *
       * @returns Is animation key.
       */
      static isAnimationKey(key) {
        return this.#animateKeys.has(key);
      }
      /**
       * Sets numeric defaults for a {@link TJSPositionData} like object.
       *
       * @param data - A TJSPositionData like object.
       */
      static setNumericDefaults(data) {
        if (data.rotateX === null) {
          data.rotateX = 0;
        }
        if (data.rotateY === null) {
          data.rotateY = 0;
        }
        if (data.rotateZ === null) {
          data.rotateZ = 0;
        }
        if (data.translateX === null) {
          data.translateX = 0;
        }
        if (data.translateY === null) {
          data.translateY = 0;
        }
        if (data.translateZ === null) {
          data.translateZ = 0;
        }
        if (data.scale === null) {
          data.scale = 1;
        }
      }
    }
    class ConvertStringData {
      /**
       * Animation keys for different processing categories.
       */
      static #animKeyTypes = {
        // Animation keys that can be specified in `px` converted to a number.
        numPx: Object.freeze(/* @__PURE__ */ new Set([
          "left",
          "top",
          "maxWidth",
          "maxHeight",
          "minWidth",
          "minHeight",
          "width",
          "height",
          "translateX",
          "translateY",
          "translateZ"
        ])),
        // Animation keys that can be specified in percentage of parent element constraint.
        percentParent: Object.freeze(/* @__PURE__ */ new Set([
          "left",
          "top",
          "maxWidth",
          "maxHeight",
          "minWidth",
          "minHeight",
          "width",
          "height"
        ])),
        // Only rotation animation keys can be specified in `rad` / `turn` converted to a number.
        rotationRadTurn: Object.freeze(/* @__PURE__ */ new Set(["rotateX", "rotateY", "rotateZ", "rotation"]))
      };
      /**
       * Parses string data values. Relative values must start with leading values '+=', '-=', or '*=' followed by a
       * float / numeric value. IE `+=45` or for percentage '+=10%'. Also handles exact percent value such as `10` or
       * `10%`. Percentage values are based on the current value, parent element constraints, or constraints of the type
       * of value like rotation being bound by 360 degrees.
       *
       * @privateRemarks
       * TODO: In the future support more specific CSS unit types.
       */
      static #regexStringData = /^(?<operation>[-+*]=)?(?<value>-?\d*\.?\d+)(?<unit>%|%~|px|rad|turn)?$/;
      /**
       * Stores the results for match groups from `regexStringData`;
       */
      static #matchResults = Object.seal({
        operation: void 0,
        value: 0,
        unit: void 0
      });
      /**
       * Converts any relative string values for animatable keys to actual updates performed against current data.
       *
       * @param data - position data.
       *
       * @param position - The source position data.
       *
       * @param el - Target positioned element.
       *
       * @returns Converted data.
       */
      static process(data, position, el) {
        let parentClientHeight = Number.NaN;
        let parentClientWidth = Number.NaN;
        for (const key in data) {
          if (TJSPositionDataUtil.isAnimationKey(key)) {
            const value = data[key];
            if (typeof value !== "string") {
              continue;
            }
            if (value === "auto" || value === "inherit") {
              continue;
            }
            const animKey = key;
            const regexResults = this.#regexStringData.exec(value);
            let handled = false;
            if (regexResults && regexResults.groups) {
              const results = this.#matchResults;
              results.operation = regexResults.groups.operation;
              results.value = parseFloat(regexResults.groups.value);
              results.unit = regexResults.groups.unit;
              const current = TJSPositionDataUtil.getDataOrDefault(position, key);
              switch (results.unit) {
                case "%": {
                  if (this.#animKeyTypes.percentParent.has(key) && (Number.isNaN(parentClientHeight) || Number.isNaN(parentClientWidth))) {
                    if (el?.parentElement?.isConnected) {
                      parentClientHeight = el.parentElement.clientHeight;
                      parentClientWidth = el.parentElement.clientWidth;
                    } else {
                      parentClientHeight = 0;
                      parentClientWidth = 0;
                      console.warn(`TJSPosition - ConvertStringData warning: could not determine parent constraints for key '${key}' with value '${value}'.`);
                      data[key] = void 0;
                      continue;
                    }
                  }
                  handled = this.#handlePercent(animKey, current, data, results, parentClientHeight, parentClientWidth);
                  break;
                }
                case "%~":
                  handled = this.#handleRelativePercent(animKey, current, data, results);
                  break;
                case "px":
                  handled = this.#animKeyTypes.numPx.has(key) ? this.#applyResultsValue(animKey, current, data, results) : false;
                  break;
                case "rad":
                case "turn":
                  handled = this.#animKeyTypes.rotationRadTurn.has(key) ? this.#handleRotationRadTurn(animKey, current, data, results) : false;
                  break;
                default:
                  handled = this.#applyResultsValue(animKey, current, data, results);
                  break;
              }
            }
            if (!regexResults || !handled) {
              console.warn(`TJSPosition - ConvertStringData warning: malformed key '${key}' with value '${value}'.`);
              data[key] = void 0;
            }
          }
        }
        return data;
      }
      // Internal implementation ----------------------------------------------------------------------------------------
      /**
       * Provides the common update to source data after `results.value` has been converted to the proper value
       * respectively.
       *
       * @param key - Animation key.
       *
       * @param current - Current value
       *
       * @param data - Source data to convert.
       *
       * @param results - Match results.
       *
       * @returns Adjustment successful.
       */
      static #applyResultsValue(key, current, data, results) {
        if (!results.operation) {
          data[key] = results.value;
          return true;
        }
        switch (results.operation) {
          case "-=":
            data[key] = current - results.value;
            break;
          case "+=":
            data[key] = current + results.value;
            break;
          case "*=":
            data[key] = current * results.value;
            break;
          default:
            return false;
        }
        return true;
      }
      /**
       * Handles the `%` unit type where values are adjusted against the parent element client width / height or in the
       * case of rotation the percentage of 360 degrees.
       *
       * @param key - Animation key.
       *
       * @param current - Current value
       *
       * @param data - Source data to convert.
       *
       * @param results - Match results.
       *
       * @param parentClientHeight - Parent element client height.
       *
       * @param parentClientWidth - Parent element client width.
       *
       * @returns Adjustment successful.
       */
      static #handlePercent(key, current, data, results, parentClientHeight, parentClientWidth) {
        switch (key) {
          case "left":
          case "maxWidth":
          case "minWidth":
          case "width":
          case "translateX":
            results.value = parentClientWidth * (results.value / 100);
            break;
          case "top":
          case "maxHeight":
          case "minHeight":
          case "height":
          case "translateY":
            results.value = parentClientHeight * (results.value / 100);
            break;
          case "rotateX":
          case "rotateY":
          case "rotateZ":
          case "rotation":
            results.value = 360 * (results.value / 100);
            break;
          default:
            return false;
        }
        return this.#applyResultsValue(key, current, data, results);
      }
      /**
       * Handles the `%~` unit type where values are adjusted against the current value for the given key.
       *
       * @param key - Animation key.
       *
       * @param current - Current value
       *
       * @param data - Source data to convert.
       *
       * @param results - Match results.
       *
       * @returns Adjustment successful.
       */
      static #handleRelativePercent(key, current, data, results) {
        results.value = results.value / 100;
        if (!results.operation) {
          data[key] = current * results.value;
          return true;
        }
        switch (results.operation) {
          case "-=":
            data[key] = current - current * results.value;
            break;
          case "+=":
            data[key] = current + current * results.value;
            break;
          case "*=":
            data[key] = current * (current * results.value);
            break;
          default:
            return false;
        }
        return true;
      }
      /**
       * Handles the `rad` / `turn` unit types for rotation animation keys.
       *
       * @param key - Animation key.
       *
       * @param current - Current value
       *
       * @param data - Source data to convert.
       *
       * @param results - Match results.
       *
       * @returns Adjustment successful.
       */
      static #handleRotationRadTurn(key, current, data, results) {
        switch (results.unit) {
          case "rad":
            results.value = radToDeg(results.value);
            break;
          case "turn":
            results.value = 360 * results.value;
            break;
        }
        return this.#applyResultsValue(key, current, data, results);
      }
    }
    class TJSTransformData {
      constructor() {
        Object.seal(this);
      }
      /**
       * Stores the calculated bounding rectangle.
       */
      #boundingRect = new DOMRect();
      /**
       * Stores the individual transformed corner points of the window in screen space clockwise from:
       * top left -> top right -> bottom right -> bottom left.
       */
      #corners = [new Vec3(), new Vec3(), new Vec3(), new Vec3()];
      /**
       * Stores the current gl-matrix Mat4 data.
       */
      #mat4 = new Mat4();
      /**
       * Stores the pre-origin & post-origin translations to apply to matrix transforms.
       */
      #originTranslations = [new Mat4(), new Mat4()];
      /**
       * @returns The bounding rectangle.
       */
      get boundingRect() {
        return this.#boundingRect;
      }
      /**
       * @returns The transformed corner points as Vec3 in screen space.
       */
      get corners() {
        return this.#corners;
      }
      /**
       * @returns Returns the CSS style string for the transform matrix.
       */
      get css() {
        return `matrix3d(${this.mat4.join(",")})`;
      }
      /**
       * @returns The transform matrix.
       */
      get mat4() {
        return this.#mat4;
      }
      /**
       * @returns The pre / post translation matrices for origin translation.
       */
      get originTranslations() {
        return this.#originTranslations;
      }
    }
    class NumberGuard {
      constructor() {
      }
      static isFinite(value) {
        return typeof value === "number" && Number.isFinite(value);
      }
      static isFiniteOrNull(value) {
        return value === null || typeof value === "number" && Number.isFinite(value);
      }
    }
    class TJSPositionStyleCache {
      el;
      computed;
      marginLeft;
      marginTop;
      maxHeight;
      maxWidth;
      minHeight;
      minWidth;
      hasWillChange;
      stores;
      resizeObserved;
      constructor() {
        this.el = void 0;
        this.computed = void 0;
        this.marginLeft = void 0;
        this.marginTop = void 0;
        this.maxHeight = void 0;
        this.maxWidth = void 0;
        this.minHeight = void 0;
        this.minWidth = void 0;
        this.hasWillChange = false;
        this.resizeObserved = Object.seal({
          contentHeight: void 0,
          contentWidth: void 0,
          offsetHeight: void 0,
          offsetWidth: void 0
        });
        const storeResizeObserved = writable(this.resizeObserved);
        this.stores = {
          element: writable(this.el),
          resizeContentHeight: propertyStore(storeResizeObserved, "contentHeight"),
          resizeContentWidth: propertyStore(storeResizeObserved, "contentWidth"),
          resizeObserved: storeResizeObserved,
          resizeObservable: writable(false),
          resizeOffsetHeight: propertyStore(storeResizeObserved, "offsetHeight"),
          resizeOffsetWidth: propertyStore(storeResizeObserved, "offsetWidth")
        };
      }
      /**
       * Returns the cached offsetHeight from any attached `resizeObserver` action otherwise gets the offsetHeight from
       * the element directly. The more optimized path is using `resizeObserver` as getting it from the element
       * directly is more expensive and alters the execution order of an animation frame.
       *
       * @returns The element offsetHeight.
       */
      get offsetHeight() {
        if (this.el !== void 0 && A11yHelper.isFocusTarget(this.el)) {
          return this.resizeObserved.offsetHeight !== void 0 ? this.resizeObserved.offsetHeight : this.el.offsetHeight;
        }
        throw new Error(`TJSPositionStyleCache - get offsetHeight error: no element assigned.`);
      }
      /**
       * Returns the cached offsetWidth from any attached `resizeObserver` action otherwise gets the offsetWidth from
       * the element directly. The more optimized path is using `resizeObserver` as getting it from the element
       * directly is more expensive and alters the execution order of an animation frame.
       *
       * @returns The element offsetHeight.
       */
      get offsetWidth() {
        if (this.el !== void 0 && A11yHelper.isFocusTarget(this.el)) {
          return this.resizeObserved.offsetWidth !== void 0 ? this.resizeObserved.offsetWidth : this.el.offsetWidth;
        }
        throw new Error(`TJSPositionStyleCache - get offsetWidth error: no element assigned.`);
      }
      /**
       * @param el -
       *
       * @returns Does element match cached element.
       */
      hasData(el) {
        return this.el === el;
      }
      /**
       * Resets the style cache.
       */
      reset() {
        if (this.el !== void 0 && A11yHelper.isFocusTarget(this.el) && this.el.isConnected && !this.hasWillChange) {
          this.el.style.willChange = "";
        }
        this.el = void 0;
        this.computed = void 0;
        this.marginLeft = void 0;
        this.marginTop = void 0;
        this.maxHeight = void 0;
        this.maxWidth = void 0;
        this.minHeight = void 0;
        this.minWidth = void 0;
        this.hasWillChange = false;
        this.resizeObserved.contentHeight = void 0;
        this.resizeObserved.contentWidth = void 0;
        this.resizeObserved.offsetHeight = void 0;
        this.resizeObserved.offsetWidth = void 0;
        this.stores.element.set(void 0);
      }
      /**
       * Updates the style cache with new data from the given element.
       *
       * @param el - An HTML element.
       */
      update(el) {
        this.el = el;
        this.computed = globalThis.getComputedStyle(el);
        this.marginLeft = StyleParse.pixels(el.style.marginLeft) ?? StyleParse.pixels(this.computed.marginLeft);
        this.marginTop = StyleParse.pixels(el.style.marginTop) ?? StyleParse.pixels(this.computed.marginTop);
        this.maxHeight = StyleParse.pixels(el.style.maxHeight) ?? StyleParse.pixels(this.computed.maxHeight);
        this.maxWidth = StyleParse.pixels(el.style.maxWidth) ?? StyleParse.pixels(this.computed.maxWidth);
        this.minHeight = StyleParse.pixels(el.style.minHeight) ?? StyleParse.pixels(this.computed.minHeight);
        this.minWidth = StyleParse.pixels(el.style.minWidth) ?? StyleParse.pixels(this.computed.minWidth);
        const willChange = el.style.willChange !== "" ? el.style.willChange : this.computed.willChange ?? "";
        this.hasWillChange = willChange !== "" && willChange !== "auto";
        this.stores.element.set(el);
      }
    }
    class TJSTransforms {
      /**
       * Stores transform data.
       */
      #data = {};
      /**
       * Stores the transform keys in the order added.
       */
      #orderList = [];
      /**
       * Defines the keys of TJSPositionData that are transform keys.
       */
      static #transformKeys = Object.freeze([
        "rotateX",
        "rotateY",
        "rotateZ",
        "scale",
        "translateX",
        "translateY",
        "translateZ"
      ]);
      /**
       * Validates that a given key is a transform key.
       *
       * @param key - A potential transform key.
       */
      static #isTransformKey(key) {
        return this.#transformKeys.includes(key);
      }
      /**
       * Defines bitwise keys for transforms used in {@link TJSTransforms.getMat4}.
       */
      static #transformKeysBitwise = Object.freeze({
        rotateX: 1,
        rotateY: 2,
        rotateZ: 4,
        scale: 8,
        translateX: 16,
        translateY: 32,
        translateZ: 64
      });
      /**
       * Defines the default transform origin.
       */
      static #transformOriginDefault = "top left";
      /**
       * Defines the valid transform origins.
       */
      static #transformOrigins = Object.freeze([
        "top left",
        "top center",
        "top right",
        "center left",
        "center",
        "center right",
        "bottom left",
        "bottom center",
        "bottom right"
      ]);
      /**
       * Defines a valid Set of transform origins.
       */
      static #transformOriginsSet = Object.freeze(new Set(this.#transformOrigins));
      // Temporary variables --------------------------------------------------------------------------------------------
      /**
       */
      static #mat4Result = new Mat4();
      /**
       */
      static #mat4Temp = new Mat4();
      /**
       */
      static #vec3Temp = new Vec3();
      /**
       */
      static #vectorScale = [1, 1, 1];
      /**
       */
      static #vectorTranslate = [0, 0, 0];
      /**
       * Returns a list of supported transform origins.
       *
       * @returns The supported transform origin strings.
       */
      static get transformOrigins() {
        return this.#transformOrigins;
      }
      /**
       * Returns whether the given string is a {@link TransformAPI.TransformOrigin}.
       *
       * @param origin - A potential transform origin string.
       *
       * @returns True if origin is a TransformOrigin string.
       */
      static isTransformOrigin(origin) {
        return this.#transformOriginsSet.has(origin);
      }
      /**
       * @returns Whether there are active transforms in local data.
       */
      get isActive() {
        return this.#orderList.length > 0;
      }
      /**
       * @returns Any local rotateX data.
       */
      get rotateX() {
        return this.#data.rotateX;
      }
      /**
       * @returns Any local rotateY data.
       */
      get rotateY() {
        return this.#data.rotateY;
      }
      /**
       * @returns Any local rotateZ data.
       */
      get rotateZ() {
        return this.#data.rotateZ;
      }
      /**
       * @returns Any local rotateZ scale.
       */
      get scale() {
        return this.#data.scale;
      }
      /**
       * @returns Any local translateZ data.
       */
      get translateX() {
        return this.#data.translateX;
      }
      /**
       * @returns Any local translateZ data.
       */
      get translateY() {
        return this.#data.translateY;
      }
      /**
       * @returns Any local translateZ data.
       */
      get translateZ() {
        return this.#data.translateZ;
      }
      /**
       * Sets the local rotateX data if the value is a finite number otherwise removes the local data.
       *
       * @param value - A value to set.
       */
      set rotateX(value) {
        if (Number.isFinite(value)) {
          if (this.#data.rotateX === void 0) {
            this.#orderList.push("rotateX");
          }
          this.#data.rotateX = value;
        } else {
          if (this.#data.rotateX !== void 0) {
            const index = this.#orderList.findIndex((entry) => entry === "rotateX");
            if (index >= 0) {
              this.#orderList.splice(index, 1);
            }
          }
          delete this.#data.rotateX;
        }
      }
      /**
       * Sets the local rotateY data if the value is a finite number otherwise removes the local data.
       *
       * @param value - A value to set.
       */
      set rotateY(value) {
        if (Number.isFinite(value)) {
          if (this.#data.rotateY === void 0) {
            this.#orderList.push("rotateY");
          }
          this.#data.rotateY = value;
        } else {
          if (this.#data.rotateY !== void 0) {
            const index = this.#orderList.findIndex((entry) => entry === "rotateY");
            if (index >= 0) {
              this.#orderList.splice(index, 1);
            }
          }
          delete this.#data.rotateY;
        }
      }
      /**
       * Sets the local rotateZ data if the value is a finite number otherwise removes the local data.
       *
       * @param value - A value to set.
       */
      set rotateZ(value) {
        if (Number.isFinite(value)) {
          if (this.#data.rotateZ === void 0) {
            this.#orderList.push("rotateZ");
          }
          this.#data.rotateZ = value;
        } else {
          if (this.#data.rotateZ !== void 0) {
            const index = this.#orderList.findIndex((entry) => entry === "rotateZ");
            if (index >= 0) {
              this.#orderList.splice(index, 1);
            }
          }
          delete this.#data.rotateZ;
        }
      }
      /**
       * Sets the local scale data if the value is a finite number otherwise removes the local data.
       *
       * @param value - A value to set.
       */
      set scale(value) {
        if (Number.isFinite(value)) {
          if (this.#data.scale === void 0) {
            this.#orderList.push("scale");
          }
          this.#data.scale = value;
        } else {
          if (this.#data.scale !== void 0) {
            const index = this.#orderList.findIndex((entry) => entry === "scale");
            if (index >= 0) {
              this.#orderList.splice(index, 1);
            }
          }
          delete this.#data.scale;
        }
      }
      /**
       * Sets the local translateX data if the value is a finite number otherwise removes the local data.
       *
       * @param value - A value to set.
       */
      set translateX(value) {
        if (Number.isFinite(value)) {
          if (this.#data.translateX === void 0) {
            this.#orderList.push("translateX");
          }
          this.#data.translateX = value;
        } else {
          if (this.#data.translateX !== void 0) {
            const index = this.#orderList.findIndex((entry) => entry === "translateX");
            if (index >= 0) {
              this.#orderList.splice(index, 1);
            }
          }
          delete this.#data.translateX;
        }
      }
      /**
       * Sets the local translateY data if the value is a finite number otherwise removes the local data.
       *
       * @param value - A value to set.
       */
      set translateY(value) {
        if (Number.isFinite(value)) {
          if (this.#data.translateY === void 0) {
            this.#orderList.push("translateY");
          }
          this.#data.translateY = value;
        } else {
          if (this.#data.translateY !== void 0) {
            const index = this.#orderList.findIndex((entry) => entry === "translateY");
            if (index >= 0) {
              this.#orderList.splice(index, 1);
            }
          }
          delete this.#data.translateY;
        }
      }
      /**
       * Sets the local translateZ data if the value is a finite number otherwise removes the local data.
       *
       * @param value - A value to set.
       */
      set translateZ(value) {
        if (Number.isFinite(value)) {
          if (this.#data.translateZ === void 0) {
            this.#orderList.push("translateZ");
          }
          this.#data.translateZ = value;
        } else {
          if (this.#data.translateZ !== void 0) {
            const index = this.#orderList.findIndex((entry) => entry === "translateZ");
            if (index >= 0) {
              this.#orderList.splice(index, 1);
            }
          }
          delete this.#data.translateZ;
        }
      }
      /**
       * Returns the `matrix3d` CSS transform for the given position / transform data.
       *
       * @param [data] - Optional position data otherwise use local stored transform data.
       *
       * @returns The CSS `matrix3d` string.
       */
      getCSS(data = this.#data) {
        return `matrix3d(${this.getMat4(data, TJSTransforms.#mat4Result).join(",")})`;
      }
      /**
       * Returns the `matrix3d` CSS transform for the given position / transform data.
       *
       * @param [data] - Optional position data otherwise use local stored transform data.
       *
       * @returns The CSS `matrix3d` string.
       */
      getCSSOrtho(data = this.#data) {
        return `matrix3d(${this.getMat4Ortho(data, TJSTransforms.#mat4Result).join(",")})`;
      }
      /**
       * Collects all data including a bounding rect, transform matrix, and points array of the given
       * {@link TJSPositionData} instance with the applied local transform data.
       *
       * @param position - The position data to process.
       *
       * @param [output] - Optional TJSTransformData output instance.
       *
       * @param [validationData] - Optional validation data for adjustment parameters.
       *
       * @returns The output TJSTransformData instance.
       */
      getData(position, output = new TJSTransformData(), validationData) {
        const valWidth = validationData?.width ?? 0;
        const valHeight = validationData?.height ?? 0;
        const valOffsetTop = validationData?.offsetTop ?? validationData?.marginTop ?? 0;
        const valOffsetLeft = validationData?.offsetLeft ?? validationData?.marginLeft ?? 0;
        position.top += valOffsetTop;
        position.left += valOffsetLeft;
        const width2 = NumberGuard.isFinite(position.width) ? position.width : valWidth;
        const height = NumberGuard.isFinite(position.height) ? position.height : valHeight;
        const rect = output.corners;
        if (this.hasTransform(position)) {
          rect[0][0] = rect[0][1] = rect[0][2] = 0;
          rect[1][0] = width2;
          rect[1][1] = rect[1][2] = 0;
          rect[2][0] = width2;
          rect[2][1] = height;
          rect[2][2] = 0;
          rect[3][0] = 0;
          rect[3][1] = height;
          rect[3][2] = 0;
          const matrix = this.getMat4(position, output.mat4);
          const translate = TJSTransforms.#getOriginTranslation(position.transformOrigin, width2, height, output.originTranslations);
          if (TJSTransforms.#transformOriginDefault === position.transformOrigin) {
            Vec3.transformMat4(rect[0], rect[0], matrix);
            Vec3.transformMat4(rect[1], rect[1], matrix);
            Vec3.transformMat4(rect[2], rect[2], matrix);
            Vec3.transformMat4(rect[3], rect[3], matrix);
          } else {
            Vec3.transformMat4(rect[0], rect[0], translate[0]);
            Vec3.transformMat4(rect[0], rect[0], matrix);
            Vec3.transformMat4(rect[0], rect[0], translate[1]);
            Vec3.transformMat4(rect[1], rect[1], translate[0]);
            Vec3.transformMat4(rect[1], rect[1], matrix);
            Vec3.transformMat4(rect[1], rect[1], translate[1]);
            Vec3.transformMat4(rect[2], rect[2], translate[0]);
            Vec3.transformMat4(rect[2], rect[2], matrix);
            Vec3.transformMat4(rect[2], rect[2], translate[1]);
            Vec3.transformMat4(rect[3], rect[3], translate[0]);
            Vec3.transformMat4(rect[3], rect[3], matrix);
            Vec3.transformMat4(rect[3], rect[3], translate[1]);
          }
          rect[0][0] = position.left + rect[0][0];
          rect[0][1] = position.top + rect[0][1];
          rect[1][0] = position.left + rect[1][0];
          rect[1][1] = position.top + rect[1][1];
          rect[2][0] = position.left + rect[2][0];
          rect[2][1] = position.top + rect[2][1];
          rect[3][0] = position.left + rect[3][0];
          rect[3][1] = position.top + rect[3][1];
        } else {
          rect[0][0] = position.left;
          rect[0][1] = position.top;
          rect[1][0] = position.left + width2;
          rect[1][1] = position.top;
          rect[2][0] = position.left + width2;
          rect[2][1] = position.top + height;
          rect[3][0] = position.left;
          rect[3][1] = position.top + height;
          Mat4.identity(output.mat4);
        }
        let maxX = Number.MIN_SAFE_INTEGER;
        let maxY = Number.MIN_SAFE_INTEGER;
        let minX = Number.MAX_SAFE_INTEGER;
        let minY = Number.MAX_SAFE_INTEGER;
        for (let cntr = 4; --cntr >= 0; ) {
          if (rect[cntr][0] > maxX) {
            maxX = rect[cntr][0];
          }
          if (rect[cntr][0] < minX) {
            minX = rect[cntr][0];
          }
          if (rect[cntr][1] > maxY) {
            maxY = rect[cntr][1];
          }
          if (rect[cntr][1] < minY) {
            minY = rect[cntr][1];
          }
        }
        const boundingRect = output.boundingRect;
        boundingRect.x = minX;
        boundingRect.y = minY;
        boundingRect.width = maxX - minX;
        boundingRect.height = maxY - minY;
        position.top -= valOffsetTop;
        position.left -= valOffsetLeft;
        return output;
      }
      /**
       * Creates a transform matrix based on local data applied in order it was added.
       *
       * If no data object is provided then the source is the local transform data. If another data object is supplied
       * then the stored local transform order is applied then all remaining transform keys are applied. This allows the
       * construction of a transform matrix in advance of setting local data and is useful in collision detection.
       *
       * @param [data] - TJSPositionData instance or local transform data.
       *
       * @param [output] - The output mat4 instance.
       *
       * @returns Transform matrix.
       */
      getMat4(data = this.#data, output = new Mat4()) {
        const matrix = Mat4.identity(output);
        let seenKeys = 0;
        const orderList = this.#orderList;
        for (let cntr = 0; cntr < orderList.length; cntr++) {
          const key = orderList[cntr];
          switch (key) {
            case "rotateX":
              seenKeys |= TJSTransforms.#transformKeysBitwise.rotateX;
              Mat4.multiply(matrix, matrix, Mat4.fromXRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
              break;
            case "rotateY":
              seenKeys |= TJSTransforms.#transformKeysBitwise.rotateY;
              Mat4.multiply(matrix, matrix, Mat4.fromYRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
              break;
            case "rotateZ":
              seenKeys |= TJSTransforms.#transformKeysBitwise.rotateZ;
              Mat4.multiply(matrix, matrix, Mat4.fromZRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
              break;
            case "scale":
              seenKeys |= TJSTransforms.#transformKeysBitwise.scale;
              TJSTransforms.#vectorScale[0] = TJSTransforms.#vectorScale[1] = data[key] ?? 0;
              Mat4.multiply(matrix, matrix, Mat4.fromScaling(TJSTransforms.#mat4Temp, TJSTransforms.#vectorScale));
              break;
            case "translateX":
              seenKeys |= TJSTransforms.#transformKeysBitwise.translateX;
              TJSTransforms.#vectorTranslate[0] = data.translateX ?? 0;
              TJSTransforms.#vectorTranslate[1] = 0;
              TJSTransforms.#vectorTranslate[2] = 0;
              Mat4.multiply(matrix, matrix, Mat4.fromTranslation(TJSTransforms.#mat4Temp, TJSTransforms.#vectorTranslate));
              break;
            case "translateY":
              seenKeys |= TJSTransforms.#transformKeysBitwise.translateY;
              TJSTransforms.#vectorTranslate[0] = 0;
              TJSTransforms.#vectorTranslate[1] = data.translateY ?? 0;
              TJSTransforms.#vectorTranslate[2] = 0;
              Mat4.multiply(matrix, matrix, Mat4.fromTranslation(TJSTransforms.#mat4Temp, TJSTransforms.#vectorTranslate));
              break;
            case "translateZ":
              seenKeys |= TJSTransforms.#transformKeysBitwise.translateZ;
              TJSTransforms.#vectorTranslate[0] = 0;
              TJSTransforms.#vectorTranslate[1] = 0;
              TJSTransforms.#vectorTranslate[2] = data.translateZ ?? 0;
              Mat4.multiply(matrix, matrix, Mat4.fromTranslation(TJSTransforms.#mat4Temp, TJSTransforms.#vectorTranslate));
              break;
          }
        }
        if (data !== this.#data) {
          for (let cntr = 0; cntr < TJSTransforms.#transformKeys.length; cntr++) {
            const key = TJSTransforms.#transformKeys[cntr];
            if (data[key] === null || (seenKeys & TJSTransforms.#transformKeysBitwise[key]) > 0) {
              continue;
            }
            const value = data[key];
            switch (key) {
              case "rotateX":
                Mat4.multiply(matrix, matrix, Mat4.fromXRotation(TJSTransforms.#mat4Temp, degToRad(value)));
                break;
              case "rotateY":
                Mat4.multiply(matrix, matrix, Mat4.fromYRotation(TJSTransforms.#mat4Temp, degToRad(value)));
                break;
              case "rotateZ":
                Mat4.multiply(matrix, matrix, Mat4.fromZRotation(TJSTransforms.#mat4Temp, degToRad(value)));
                break;
              case "scale":
                TJSTransforms.#vectorScale[0] = TJSTransforms.#vectorScale[1] = value;
                Mat4.multiply(matrix, matrix, Mat4.fromScaling(TJSTransforms.#mat4Temp, TJSTransforms.#vectorScale));
                break;
              case "translateX":
                TJSTransforms.#vectorTranslate[0] = value;
                TJSTransforms.#vectorTranslate[1] = 0;
                TJSTransforms.#vectorTranslate[2] = 0;
                Mat4.multiply(matrix, matrix, Mat4.fromTranslation(TJSTransforms.#mat4Temp, TJSTransforms.#vectorTranslate));
                break;
              case "translateY":
                TJSTransforms.#vectorTranslate[0] = 0;
                TJSTransforms.#vectorTranslate[1] = value;
                TJSTransforms.#vectorTranslate[2] = 0;
                Mat4.multiply(matrix, matrix, Mat4.fromTranslation(TJSTransforms.#mat4Temp, TJSTransforms.#vectorTranslate));
                break;
              case "translateZ":
                TJSTransforms.#vectorTranslate[0] = 0;
                TJSTransforms.#vectorTranslate[1] = 0;
                TJSTransforms.#vectorTranslate[2] = value;
                Mat4.multiply(matrix, matrix, Mat4.fromTranslation(TJSTransforms.#mat4Temp, TJSTransforms.#vectorTranslate));
                break;
            }
          }
        }
        return matrix;
      }
      /**
       * Provides an orthographic enhancement to convert left / top positional data to a translate operation.
       *
       * This transform matrix takes into account that the remaining operations are , but adds any left / top attributes
       * from passed in data to translate X / Y.
       *
       * If no data object is provided then the source is the local transform data. If another data object is supplied
       * then the stored local transform order is applied then all remaining transform keys are applied. This allows the
       * construction of a transform matrix in advance of setting local data and is useful in collision detection.
       *
       * @param [data] - TJSPositionData instance or local transform data.
       *
       * @param [output] - The output mat4 instance.
       *
       * @returns Transform matrix.
       */
      getMat4Ortho(data = this.#data, output = new Mat4()) {
        const matrix = Mat4.identity(output);
        TJSTransforms.#vectorTranslate[0] = (data.left ?? 0) + (data.translateX ?? 0);
        TJSTransforms.#vectorTranslate[1] = (data.top ?? 0) + (data.translateY ?? 0);
        TJSTransforms.#vectorTranslate[2] = data.translateZ ?? 0;
        Mat4.multiply(matrix, matrix, Mat4.fromTranslation(TJSTransforms.#mat4Temp, TJSTransforms.#vectorTranslate));
        if (data.scale !== null && data.scale !== void 0) {
          TJSTransforms.#vectorScale[0] = TJSTransforms.#vectorScale[1] = data.scale;
          Mat4.multiply(matrix, matrix, Mat4.fromScaling(TJSTransforms.#mat4Temp, TJSTransforms.#vectorScale));
        }
        if (data.rotateX === null && data.rotateY === null && data.rotateZ === null) {
          return matrix;
        }
        let seenKeys = 0;
        const orderList = this.#orderList;
        for (let cntr = 0; cntr < orderList.length; cntr++) {
          const key = orderList[cntr];
          switch (key) {
            case "rotateX":
              seenKeys |= TJSTransforms.#transformKeysBitwise.rotateX;
              Mat4.multiply(matrix, matrix, Mat4.fromXRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
              break;
            case "rotateY":
              seenKeys |= TJSTransforms.#transformKeysBitwise.rotateY;
              Mat4.multiply(matrix, matrix, Mat4.fromYRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
              break;
            case "rotateZ":
              seenKeys |= TJSTransforms.#transformKeysBitwise.rotateZ;
              Mat4.multiply(matrix, matrix, Mat4.fromZRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
              break;
          }
        }
        if (data !== this.#data) {
          for (let cntr = 0; cntr < TJSTransforms.#transformKeys.length; cntr++) {
            const key = TJSTransforms.#transformKeys[cntr];
            if (data[key] === null || (seenKeys & TJSTransforms.#transformKeysBitwise[key]) > 0) {
              continue;
            }
            switch (key) {
              case "rotateX":
                Mat4.multiply(matrix, matrix, Mat4.fromXRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
                break;
              case "rotateY":
                Mat4.multiply(matrix, matrix, Mat4.fromYRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
                break;
              case "rotateZ":
                Mat4.multiply(matrix, matrix, Mat4.fromZRotation(TJSTransforms.#mat4Temp, degToRad(data[key] ?? 0)));
                break;
            }
          }
        }
        return matrix;
      }
      /**
       * Tests an object if it contains transform keys and the values are finite numbers.
       *
       * @param data - An object to test for transform data.
       *
       * @returns Whether the given TJSPositionData has transforms.
       */
      hasTransform(data) {
        for (const key of TJSTransforms.#transformKeys) {
          if (Number.isFinite(data[key])) {
            return true;
          }
        }
        return false;
      }
      /**
       * Resets internal data from the given object containing valid transform keys.
       *
       * @param data - An object with transform data.
       */
      reset(data) {
        for (const key in data) {
          if (TJSTransforms.#isTransformKey(key)) {
            const value = data[key];
            if (NumberGuard.isFinite(value)) {
              this.#data[key] = value;
            } else {
              const index = this.#orderList.findIndex((entry) => entry === key);
              if (index >= 0) {
                this.#orderList.splice(index, 1);
              }
              delete this.#data[key];
            }
          }
        }
      }
      // Internal implementation ----------------------------------------------------------------------------------------
      /**
       * Returns the translations necessary to translate a matrix operation based on the `transformOrigin` parameter of the
       * given position instance. The first entry / index 0 is the pre-translation and last entry / index 1 is the post-
       * translation.
       *
       * This method is used internally, but may be useful if you need the origin translation matrices to transform
       * bespoke points based on any `transformOrigin` set in {@link TJSPositionData}.
       *
       * @param transformOrigin - The transform origin attribute from TJSPositionData.
       *
       * @param width - The TJSPositionData width or validation data width when 'auto'.
       *
       * @param height - The TJSPositionData height or validation data height when 'auto'.
       *
       * @param output - Output Mat4 array.
       *
       * @returns Output Mat4 array.
       */
      static #getOriginTranslation(transformOrigin, width2, height, output) {
        const vector = TJSTransforms.#vec3Temp;
        switch (transformOrigin) {
          case "top left":
            vector[0] = vector[1] = 0;
            Mat4.fromTranslation(output[0], vector);
            Mat4.fromTranslation(output[1], vector);
            break;
          case "top center":
            vector[0] = -width2 * 0.5;
            vector[1] = 0;
            Mat4.fromTranslation(output[0], vector);
            vector[0] = width2 * 0.5;
            Mat4.fromTranslation(output[1], vector);
            break;
          case "top right":
            vector[0] = -width2;
            vector[1] = 0;
            Mat4.fromTranslation(output[0], vector);
            vector[0] = width2;
            Mat4.fromTranslation(output[1], vector);
            break;
          case "center left":
            vector[0] = 0;
            vector[1] = -height * 0.5;
            Mat4.fromTranslation(output[0], vector);
            vector[1] = height * 0.5;
            Mat4.fromTranslation(output[1], vector);
            break;
          case null:
          case "center":
            vector[0] = -width2 * 0.5;
            vector[1] = -height * 0.5;
            Mat4.fromTranslation(output[0], vector);
            vector[0] = width2 * 0.5;
            vector[1] = height * 0.5;
            Mat4.fromTranslation(output[1], vector);
            break;
          case "center right":
            vector[0] = -width2;
            vector[1] = -height * 0.5;
            Mat4.fromTranslation(output[0], vector);
            vector[0] = width2;
            vector[1] = height * 0.5;
            Mat4.fromTranslation(output[1], vector);
            break;
          case "bottom left":
            vector[0] = 0;
            vector[1] = -height;
            Mat4.fromTranslation(output[0], vector);
            vector[1] = height;
            Mat4.fromTranslation(output[1], vector);
            break;
          case "bottom center":
            vector[0] = -width2 * 0.5;
            vector[1] = -height;
            Mat4.fromTranslation(output[0], vector);
            vector[0] = width2 * 0.5;
            vector[1] = height;
            Mat4.fromTranslation(output[1], vector);
            break;
          case "bottom right":
            vector[0] = -width2;
            vector[1] = -height;
            Mat4.fromTranslation(output[0], vector);
            vector[0] = width2;
            vector[1] = height;
            Mat4.fromTranslation(output[1], vector);
            break;
          default:
            Mat4.identity(output[0]);
            Mat4.identity(output[1]);
            break;
        }
        return output;
      }
    }
    class AnimationScheduler {
      /**
       * Used to copy data from a TJSPosition instance.
       */
      static #data = {};
      static #getEaseOptions = Object.freeze({ default: false });
      /**
       * Adds / schedules an animation w/ the AnimationManager. This contains the final steps common to all tweens.
       *
       * @param position -
       *
       * @param initial -
       *
       * @param destination -
       *
       * @param duration -
       *
       * @param el -
       *
       * @param delay -
       *
       * @param ease -
       *
       * @param [interpolate=lerp] -
       *
       * @param [transformOrigin] -
       *
       * @param [transformOriginInitial] -
       *
       * @param [cleanup] -
       *
       * @returns An AnimationControl instance or null if none created.
       */
      static #addAnimation(position, initial, destination, duration, el, delay, ease, interpolate2 = lerp, transformOrigin, transformOriginInitial, cleanup) {
        TJSPositionDataUtil.setNumericDefaults(initial);
        TJSPositionDataUtil.setNumericDefaults(destination);
        for (const key in initial) {
          if (!Number.isFinite(initial[key])) {
            delete initial[key];
          }
        }
        const keys = Object.keys(initial);
        const newData = Object.assign({}, initial);
        if (keys.length === 0) {
          return null;
        }
        const animationData = {
          active: true,
          cleanup,
          cancelled: false,
          control: void 0,
          current: 0,
          destination,
          duration: duration * 1e3,
          // Internally the AnimationManager works in ms.
          ease,
          el,
          finished: false,
          initial,
          interpolate: interpolate2,
          keys,
          newData,
          position,
          resolve: void 0,
          start: void 0,
          transformOrigin,
          transformOriginInitial,
          quickTo: false
        };
        if (delay > 0) {
          animationData.active = false;
          setTimeout(() => animationData.active = true, delay * 1e3);
        }
        AnimationManager.add(animationData);
        return new AnimationControl(animationData, true);
      }
      /**
       * Provides a tween from given position data to the current position.
       *
       * @param position - The target position instance.
       *
       * @param fromData - The starting position.
       *
       * @param options - Tween options.
       *
       * @param [cleanup] - Custom animation cleanup function.
       *
       * @returns An AnimationControl instance or null if none created.
       */
      static from(position, fromData, options = {}, cleanup) {
        if (!isObject(fromData)) {
          throw new TypeError(`AnimationAPI.from error: 'fromData' is not an object.`);
        }
        const parent = position.parent;
        if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
          return null;
        }
        let { delay = 0, duration = 1, ease = "cubicOut", strategy, transformOrigin } = options;
        if (strategy !== void 0) {
          if (this.#handleStrategy(position, strategy) === null) {
            return null;
          }
        }
        const targetEl = A11yHelper.isFocusTarget(parent) ? parent : parent?.elementTarget;
        const el = A11yHelper.isFocusTarget(targetEl) && targetEl.isConnected ? targetEl : void 0;
        if (!Number.isFinite(delay) || delay < 0) {
          throw new TypeError(`AnimationScheduler.from error: 'delay' is not a positive number.`);
        }
        if (!Number.isFinite(duration) || duration < 0) {
          throw new TypeError(`AnimationScheduler.from error: 'duration' is not a positive number.`);
        }
        ease = getEasingFunc(ease, this.#getEaseOptions);
        if (typeof ease !== "function") {
          throw new TypeError(`AnimationScheduler.from error: 'ease' is not a function or valid Svelte easing function name.`);
        }
        const initial = {};
        const destination = {};
        position.get(this.#data);
        transformOrigin = TJSTransforms.isTransformOrigin(transformOrigin) ? transformOrigin : void 0;
        const transformOriginInitial = transformOrigin !== void 0 ? this.#data.transformOrigin : void 0;
        for (const key in fromData) {
          const animKey = TJSPositionDataUtil.getAnimationKey(key);
          if (this.#data[animKey] !== void 0 && fromData[key] !== this.#data[animKey]) {
            initial[key] = fromData[key];
            destination[key] = this.#data[animKey];
          }
        }
        ConvertStringData.process(initial, this.#data, el);
        return this.#addAnimation(position, initial, destination, duration, el, delay, ease, lerp, transformOrigin, transformOriginInitial, cleanup);
      }
      /**
       * Provides a tween from given position data to the given position.
       *
       * @param position - The target position instance.
       *
       * @param fromData - The starting position.
       *
       * @param toData - The ending position.
       *
       * @param options - Tween options.
       *
       * @param [cleanup] - Custom animation cleanup function.
       *
       * @returns An AnimationControl instance or null if none created.
       */
      static fromTo(position, fromData, toData, options = {}, cleanup) {
        if (!isObject(fromData)) {
          throw new TypeError(`AnimationScheduler.fromTo error: 'fromData' is not an object.`);
        }
        if (!isObject(toData)) {
          throw new TypeError(`AnimationScheduler.fromTo error: 'toData' is not an object.`);
        }
        const parent = position.parent;
        if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
          return null;
        }
        let { delay = 0, duration = 1, ease = "cubicOut", strategy, transformOrigin } = options;
        if (strategy !== void 0) {
          if (this.#handleStrategy(position, strategy) === null) {
            return null;
          }
        }
        const targetEl = A11yHelper.isFocusTarget(parent) ? parent : parent?.elementTarget;
        const el = A11yHelper.isFocusTarget(targetEl) && targetEl.isConnected ? targetEl : void 0;
        if (!Number.isFinite(delay) || delay < 0) {
          throw new TypeError(`AnimationScheduler.fromTo error: 'delay' is not a positive number.`);
        }
        if (!Number.isFinite(duration) || duration < 0) {
          throw new TypeError(`AnimationScheduler.fromTo error: 'duration' is not a positive number.`);
        }
        ease = getEasingFunc(ease, this.#getEaseOptions);
        if (typeof ease !== "function") {
          throw new TypeError(`AnimationScheduler.fromTo error: 'ease' is not a function or valid Svelte easing function name.`);
        }
        const initial = {};
        const destination = {};
        position.get(this.#data);
        transformOrigin = TJSTransforms.isTransformOrigin(transformOrigin) ? transformOrigin : void 0;
        const transformOriginInitial = transformOrigin !== void 0 ? this.#data.transformOrigin : void 0;
        for (const key in fromData) {
          if (toData[key] === void 0) {
            console.warn(`AnimationScheduler.fromTo warning: skipping key ('${key}') from 'fromData' as it is missing in 'toData'.`);
            continue;
          }
          const animKey = TJSPositionDataUtil.getAnimationKey(key);
          if (this.#data[animKey] !== void 0) {
            initial[key] = fromData[key];
            destination[key] = toData[key];
          }
        }
        ConvertStringData.process(initial, this.#data, el);
        ConvertStringData.process(destination, this.#data, el);
        return this.#addAnimation(position, initial, destination, duration, el, delay, ease, lerp, transformOrigin, transformOriginInitial, cleanup);
      }
      /**
       * Provides a tween to given position data from the current position.
       *
       * @param position - The target position instance.
       *
       * @param toData - The destination position.
       *
       * @param options - Tween options.
       *
       * @param [cleanup] - Custom animation cleanup function.
       *
       * @returns An AnimationControl instance or null if none created.
       */
      static to(position, toData, options, cleanup) {
        if (!isObject(toData)) {
          throw new TypeError(`AnimationScheduler.to error: 'toData' is not an object.`);
        }
        const parent = position.parent;
        if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
          return null;
        }
        let { delay = 0, duration = 1, ease = "cubicOut", strategy, transformOrigin } = options;
        if (strategy !== void 0) {
          if (this.#handleStrategy(position, strategy) === null) {
            return null;
          }
        }
        const targetEl = A11yHelper.isFocusTarget(parent) ? parent : parent?.elementTarget;
        const el = A11yHelper.isFocusTarget(targetEl) && targetEl.isConnected ? targetEl : void 0;
        if (!Number.isFinite(delay) || delay < 0) {
          throw new TypeError(`AnimationScheduler.to error: 'delay' is not a positive number.`);
        }
        if (!Number.isFinite(duration) || duration < 0) {
          throw new TypeError(`AnimationScheduler.to error: 'duration' is not a positive number.`);
        }
        ease = getEasingFunc(ease, this.#getEaseOptions);
        if (typeof ease !== "function") {
          throw new TypeError(`AnimationScheduler.to error: 'ease' is not a function or valid Svelte easing function name.`);
        }
        const initial = {};
        const destination = {};
        position.get(this.#data);
        transformOrigin = TJSTransforms.isTransformOrigin(transformOrigin) ? transformOrigin : void 0;
        const transformOriginInitial = transformOrigin !== void 0 ? this.#data.transformOrigin : void 0;
        for (const key in toData) {
          const animKey = TJSPositionDataUtil.getAnimationKey(key);
          if (this.#data[animKey] !== void 0 && toData[key] !== this.#data[animKey]) {
            destination[key] = toData[key];
            initial[key] = this.#data[animKey];
          }
        }
        ConvertStringData.process(destination, this.#data, el);
        return this.#addAnimation(position, initial, destination, duration, el, delay, ease, lerp, transformOrigin, transformOriginInitial, cleanup);
      }
      // Internal implementation ----------------------------------------------------------------------------------------
      /**
       * Handle any defined scheduling strategy allowing existing scheduled animations for the same position instance
       * to be controlled.
       *
       * @param position - The target position instance.
       *
       * @param strategy - A scheduling strategy to apply.
       *
       * @returns Returns null to abort scheduling current animation.
       */
      static #handleStrategy(position, strategy) {
        switch (strategy) {
          case "cancel":
            if (AnimationManager.isScheduled(position)) {
              AnimationManager.cancel(position);
            }
            break;
          case "cancelAll":
            if (AnimationManager.isScheduled(position)) {
              AnimationManager.cancel(position, AnimationManager.cancelAllFn);
            }
            break;
          case "exclusive":
            if (AnimationManager.isScheduled(position)) {
              return null;
            }
            break;
          default:
            console.warn(`AnimationScheduler error: 'strategy' is not 'cancel', 'cancelAll', or 'exclusive'.`);
            return null;
        }
      }
    }
    class AnimationAPIImpl {
      static #getEaseOptions = Object.freeze({ default: false });
      /**
       */
      #data;
      #position;
      /**
       * @param position -
       *
       * @param data -
       */
      constructor(position, data) {
        this.#position = position;
        this.#data = data;
        Object.seal(this);
      }
      /**
       * Returns if there are scheduled animations whether active or pending for this TJSPosition instance.
       *
       * @returns Are there scheduled animations.
       */
      get isScheduled() {
        return AnimationManager.isScheduled(this.#position);
      }
      /**
       * Cancels all animation instances for this TJSPosition instance.
       */
      cancel() {
        AnimationManager.cancel(this.#position, AnimationManager.cancelAllFn);
      }
      /**
       * Returns all currently scheduled AnimationControl instances for this TJSPosition instance.
       *
       * @returns All currently scheduled animation controls for this TJSPosition instance.
       */
      getScheduled() {
        return AnimationManager.getScheduled(this.#position);
      }
      /**
       * Provides a tween from given position data to the current position.
       *
       * @param fromData - The starting position.
       *
       * @param [options] - Optional tween parameters.
       *
       * @returns A control object that can cancel animation and provides a `finished` Promise.
       */
      from(fromData, options) {
        const animationControl = AnimationScheduler.from(this.#position, fromData, options);
        return animationControl ? animationControl : AnimationControl.voidControl;
      }
      /**
       * Provides a tween from given position data to the given position.
       *
       * @param fromData - The starting position.
       *
       * @param toData - The ending position.
       *
       * @param [options] - Optional tween parameters.
       *
       * @returns A control object that can cancel animation and provides a `finished` Promise.
       */
      fromTo(fromData, toData, options) {
        const animationControl = AnimationScheduler.fromTo(this.#position, fromData, toData, options);
        return animationControl ? animationControl : AnimationControl.voidControl;
      }
      /**
       * Provides a tween to given position data from the current position.
       *
       * @param toData - The destination position.
       *
       * @param [options] - Optional tween parameters.
       *
       * @returns A control object that can cancel animation and provides a `finished` Promise.
       */
      to(toData, options) {
        const animationControl = AnimationScheduler.to(this.#position, toData, options);
        return animationControl ? animationControl : AnimationControl.voidControl;
      }
      /**
       * Returns a function that provides an optimized way to constantly update a to-tween.
       *
       * @param keys - The keys for quickTo.
       *
       * @param [options] - Optional quick tween parameters.
       *
       * @returns quick-to tween function.
       */
      quickTo(keys, options = {}) {
        if (!isIterable(keys)) {
          throw new TypeError(`AnimationAPI.quickTo error: 'keys' is not an iterable list.`);
        }
        const parent = this.#position.parent;
        if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
          throw new Error(`AnimationAPI.quickTo error: 'parent' is not positionable.`);
        }
        let { duration = 1, ease = "cubicOut" } = options;
        if (!Number.isFinite(duration) || duration < 0) {
          throw new TypeError(`AnimationAPI.quickTo error: 'duration' is not a positive number.`);
        }
        ease = getEasingFunc(ease, AnimationAPIImpl.#getEaseOptions);
        if (typeof ease !== "function") {
          throw new TypeError(`AnimationAPI.quickTo error: 'ease' is not a function or valid Svelte easing function name.`);
        }
        const initial = {};
        const destination = {};
        const data = this.#data;
        for (const key of keys) {
          if (typeof key !== "string") {
            throw new TypeError(`AnimationAPI.quickTo error: key ('${key}') is not a string.`);
          }
          if (!TJSPositionDataUtil.isAnimationKey(key)) {
            throw new Error(`AnimationAPI.quickTo error: key ('${key}') is not animatable.`);
          }
          const value = TJSPositionDataUtil.getDataOrDefault(data, key);
          if (value !== null) {
            destination[key] = value;
            initial[key] = value;
          }
        }
        const keysArray = [...keys];
        Object.freeze(keysArray);
        const newData = Object.assign({}, initial);
        const animationData = {
          active: true,
          cancelled: false,
          control: void 0,
          current: 0,
          destination,
          duration: duration * 1e3,
          // Internally the AnimationManager works in ms.
          ease,
          el: void 0,
          finished: true,
          // Note: start in finished state to add to AnimationManager on first callback.
          initial,
          interpolate: lerp,
          keys: keysArray,
          newData,
          position: this.#position,
          resolve: void 0,
          start: 0,
          quickTo: true
        };
        const quickToCB = (...args) => {
          const argsLength = args.length;
          if (argsLength === 0) {
            return;
          }
          for (let cntr = keysArray.length; --cntr >= 0; ) {
            const key = keysArray[cntr];
            const animKey = TJSPositionDataUtil.getAnimationKey(key);
            if (data[animKey] !== void 0) {
              initial[key] = data[animKey];
            }
          }
          if (isObject(args[0])) {
            const objData = args[0];
            for (const key in objData) {
              if (destination[key] !== void 0) {
                destination[key] = objData[key];
              }
            }
          } else {
            for (let cntr = 0; cntr < argsLength && cntr < keysArray.length; cntr++) {
              const key = keysArray[cntr];
              if (destination[key] !== void 0) {
                destination[key] = args[cntr];
              }
            }
          }
          TJSPositionDataUtil.setNumericDefaults(initial);
          TJSPositionDataUtil.setNumericDefaults(destination);
          const targetEl = A11yHelper.isFocusTarget(parent) ? parent : parent?.elementTarget;
          animationData.el = A11yHelper.isFocusTarget(targetEl) && targetEl.isConnected ? targetEl : void 0;
          ConvertStringData.process(destination, data, animationData.el);
          if (animationData.finished) {
            animationData.cancelled = false;
            animationData.finished = false;
            animationData.active = true;
            animationData.current = 0;
            AnimationManager.add(animationData);
          } else {
            const now2 = globalThis.performance.now();
            animationData.cancelled = false;
            animationData.current = 0;
            animationData.start = now2 + (AnimationManager.timeNow - now2);
          }
        };
        Object.defineProperty(quickToCB, "keys", {
          value: keysArray,
          writable: false,
          configurable: false
        });
        Object.defineProperty(quickToCB, "options", {
          value: (optionsCB) => {
            let { duration: duration2, ease: ease2 } = optionsCB;
            if (duration2 !== void 0 && (!Number.isFinite(duration2) || duration2 < 0)) {
              throw new TypeError(`AnimationAPI.quickTo.options error: 'duration' is not a positive number.`);
            }
            ease2 = getEasingFunc(ease2, AnimationAPIImpl.#getEaseOptions);
            if (ease2 !== void 0 && typeof ease2 !== "function") {
              throw new TypeError(`AnimationAPI.quickTo.options error: 'ease' is not a function or valid Svelte easing function name.`);
            }
            if (NumberGuard.isFinite(duration2) && duration2 >= 0) {
              animationData.duration = duration2 * 1e3;
            }
            if (ease2) {
              animationData.ease = ease2;
            }
            return quickToCB;
          },
          writable: false,
          configurable: false
        });
        return quickToCB;
      }
    }
    class AnimationGroupControl {
      /**
       */
      #animationControls;
      /**
       */
      #finishedPromise;
      /**
       * Defines a static empty / void animation control.
       */
      static #voidControl = new AnimationGroupControl(null);
      /**
       * Provides a static void / undefined AnimationGroupControl that is automatically resolved.
       */
      static get voidControl() {
        return this.#voidControl;
      }
      /**
       * @param animationControls - A Set of AnimationControl instances.
       */
      constructor(animationControls) {
        this.#animationControls = animationControls;
      }
      /**
       * Get a promise that resolves when all animations are finished.
       *
       * @returns Finished Promise for all animations.
       */
      get finished() {
        const animationControls = this.#animationControls;
        if (!CrossWindow.isPromise(this.#finishedPromise)) {
          if (animationControls === null || animationControls === void 0 || animationControls.size === 0) {
            this.#finishedPromise = Promise.resolve({ cancelled: false });
          } else {
            const promises = [];
            for (const animationControl of animationControls) {
              promises.push(animationControl.finished);
            }
            this.#finishedPromise = Promise.allSettled(promises).then((results) => {
              const anyCancelled = results.some((result) => result.status === "rejected" || result.status === "fulfilled" && result.value.cancelled);
              return { cancelled: anyCancelled };
            });
          }
        }
        return this.#finishedPromise;
      }
      /**
       * Returns whether there are active animation instances for this group.
       *
       * Note: a delayed animation may not be started / active yet. Use {@link AnimationGroupControl.isFinished} to
       * determine if all animations in the group are finished.
       *
       * @returns Are there active animation instances.
       */
      get isActive() {
        const animationControls = this.#animationControls;
        if (animationControls === null || animationControls === void 0 || animationControls.size === 0) {
          return false;
        }
        for (const animationControl of animationControls) {
          if (animationControl.isActive) {
            return true;
          }
        }
        return false;
      }
      /**
       * Returns whether all animations in the group are finished.
       *
       * @returns Are all animation instances finished.
       */
      get isFinished() {
        const animationControls = this.#animationControls;
        if (animationControls === null || animationControls === void 0 || animationControls.size === 0) {
          return true;
        }
        for (const animationControl of animationControls) {
          if (!animationControl.isFinished) {
            return false;
          }
        }
        return true;
      }
      /**
       * Cancels the all animations.
       */
      cancel() {
        const animationControls = this.#animationControls;
        if (animationControls === null || animationControls === void 0 || animationControls.size === 0) {
          return;
        }
        for (const animationControl of animationControls) {
          animationControl.cancel();
        }
      }
    }
    class AnimationGroupAPIImpl {
      constructor() {
      }
      /**
       * Returns the TJSPosition instance for the possible given positionable by checking the instance by checking for
       * AnimationAPI.
       *
       * @param positionable - Possible position group entry.
       *
       * @returns Returns actual TJSPosition instance.
       */
      static #getPosition(positionable) {
        if (!isObject(positionable)) {
          return null;
        }
        if (positionable.animate instanceof AnimationAPIImpl) {
          return positionable;
        }
        if (positionable.position?.animate instanceof AnimationAPIImpl) {
          return positionable.position;
        }
        return null;
      }
      /**
       * Cancels any animation for given TJSPosition.PositionGroup data.
       *
       * @param positionGroup - The position group to cancel.
       */
      static cancel(positionGroup) {
        if (isIterable(positionGroup)) {
          let index = -1;
          for (const entry of positionGroup) {
            index++;
            const actualPosition = this.#getPosition(entry);
            if (!actualPosition) {
              console.warn(`AnimationGroupAPI.cancel warning: No TJSPosition instance found at index: ${index}.`);
              continue;
            }
            AnimationManager.cancel(actualPosition);
          }
        } else {
          const actualPosition = this.#getPosition(positionGroup);
          if (!actualPosition) {
            console.warn(`AnimationGroupAPI.cancel warning: No TJSPosition instance found.`);
            return;
          }
          AnimationManager.cancel(actualPosition);
        }
      }
      /**
       * Cancels all TJSPosition animation.
       */
      static cancelAll() {
        AnimationManager.cancelAll();
      }
      /**
       * Gets all animation controls for the given position group data.
       *
       * @param positionGroup - A position group.
       *
       * @returns Results array.
       */
      static getScheduled(positionGroup) {
        const results = [];
        if (isIterable(positionGroup)) {
          let index = -1;
          for (const entry of positionGroup) {
            index++;
            const actualPosition = this.#getPosition(entry);
            if (!actualPosition) {
              console.warn(`AnimationGroupAPI.getScheduled warning: No TJSPosition instance found at index: ${index}.`);
              continue;
            }
            const controls = AnimationManager.getScheduled(actualPosition);
            results.push({
              position: actualPosition,
              entry: actualPosition !== entry ? entry : void 0,
              controls
            });
          }
        } else {
          const actualPosition = this.#getPosition(positionGroup);
          if (!actualPosition) {
            console.warn(`AnimationGroupAPI.getScheduled warning: No TJSPosition instance found.`);
            return results;
          }
          const controls = AnimationManager.getScheduled(actualPosition);
          results.push({
            position: actualPosition,
            entry: actualPosition !== positionGroup ? positionGroup : void 0,
            controls
          });
        }
        return results;
      }
      /**
       * Provides a type guard to test in the given key is an {@link AnimationAPIImpl.AnimationKey}.
       *
       * @param key - A key value to test.
       *
       * @returns Whether the given key is an animation key.
       */
      static isAnimationKey(key) {
        return TJSPositionDataUtil.isAnimationKey(key);
      }
      /**
       * Returns the status _for the entire position group_ specified if all position instances of the group are scheduled.
       *
       * @param positionGroup - A position group.
       *
       * @param [options] - Options.
       *
       * @returns True if all are scheduled / false if just one position instance in the group is not scheduled.
       */
      static isScheduled(positionGroup, options) {
        if (isIterable(positionGroup)) {
          let index = -1;
          for (const entry of positionGroup) {
            index++;
            const actualPosition = this.#getPosition(entry);
            if (!actualPosition) {
              console.warn(`AnimationGroupAPI.isScheduled warning: No TJSPosition instance found at index: ${index}.`);
              continue;
            }
            if (!AnimationManager.isScheduled(actualPosition, options)) {
              return false;
            }
          }
        } else {
          const actualPosition = this.#getPosition(positionGroup);
          if (!actualPosition) {
            console.warn(`AnimationGroupAPI.isScheduled warning: No TJSPosition instance found.`);
            return false;
          }
          if (!AnimationManager.isScheduled(actualPosition, options)) {
            return false;
          }
        }
        return true;
      }
      /**
       * Provides the `from` animation tween for one or more positionable instances as a group.
       *
       * @param positionGroup - A position group.
       *
       * @param fromData - A position data object assigned to all positionable instances or a callback function invoked for
       *        unique data for each instance.
       *
       * @param [options] - Tween options assigned to all positionable instances or a callback function invoked for unique
       *        options for each instance.
       *
       * @returns Basic animation control.
       */
      static from(positionGroup, fromData, options) {
        if (!isObject(fromData) && typeof fromData !== "function") {
          throw new TypeError(`AnimationGroupAPI.from error: 'fromData' is not an object or function.`);
        }
        if (options !== void 0 && !isObject(options) && typeof options !== "function") {
          throw new TypeError(`AnimationGroupAPI.from error: 'options' is not an object or function.`);
        }
        const animationControls = /* @__PURE__ */ new Set();
        const cleanupFn = (data) => animationControls.delete(data.control);
        let index = -1;
        let callbackOptions;
        const hasDataCallback = typeof fromData === "function";
        const hasOptionCallback = typeof options === "function";
        const hasCallback = hasDataCallback || hasOptionCallback;
        if (hasCallback) {
          callbackOptions = { index, position: void 0, entry: void 0 };
        }
        let actualFromData = fromData;
        let actualOptions = isObject(options) ? options : void 0;
        if (isIterable(positionGroup)) {
          for (const entry of positionGroup) {
            index++;
            const actualPosition = this.#getPosition(entry);
            if (!actualPosition) {
              console.warn(`AnimationGroupAPI.from warning: No TJSPosition instance found at index: ${index}.`);
              continue;
            }
            if (hasCallback) {
              callbackOptions.index = index;
              callbackOptions.position = actualPosition;
              callbackOptions.entry = actualPosition !== entry ? entry : void 0;
            }
            if (hasDataCallback && typeof fromData === "function") {
              actualFromData = fromData(callbackOptions);
              if (actualFromData === null || actualFromData === void 0) {
                continue;
              }
              if (!isObject(actualFromData)) {
                throw new TypeError(`AnimationGroupAPI.from error: 'fromData' callback function iteration(${index}) failed to return an object.`);
              }
            }
            if (hasOptionCallback && typeof options === "function") {
              actualOptions = options(callbackOptions);
              if (actualOptions === null || actualOptions === void 0) {
                continue;
              }
              if (!isObject(actualOptions)) {
                throw new TypeError(`AnimationGroupAPI.from error: 'options' callback function iteration(${index}) failed to return an object.`);
              }
            }
            const animationControl = AnimationScheduler.from(actualPosition, actualFromData, actualOptions, cleanupFn);
            if (animationControl) {
              animationControls.add(animationControl);
            }
          }
        } else {
          const actualPosition = this.#getPosition(positionGroup);
          if (!actualPosition) {
            console.warn(`AnimationGroupAPI.from warning: No TJSPosition instance found.`);
            return AnimationGroupControl.voidControl;
          }
          if (hasCallback) {
            callbackOptions.index = 0;
            callbackOptions.position = actualPosition;
            callbackOptions.entry = actualPosition !== positionGroup ? positionGroup : void 0;
          }
          if (hasDataCallback && typeof fromData === "function") {
            actualFromData = fromData(callbackOptions);
            if (actualFromData === null || actualFromData === void 0) {
              return AnimationGroupControl.voidControl;
            }
            if (!isObject(actualFromData)) {
              throw new TypeError(`AnimationGroupAPI.from error: 'fromData' callback function failed to return an object.`);
            }
          }
          if (hasOptionCallback && typeof options === "function") {
            actualOptions = options(callbackOptions);
            if (actualOptions === null || actualOptions === void 0) {
              return AnimationGroupControl.voidControl;
            }
            if (!isObject(actualOptions)) {
              throw new TypeError(`AnimationGroupAPI.from error: 'options' callback function failed to return an object.`);
            }
          }
          const animationControl = AnimationScheduler.from(actualPosition, actualFromData, actualOptions, cleanupFn);
          if (animationControl) {
            animationControls.add(animationControl);
          }
        }
        return new AnimationGroupControl(animationControls);
      }
      /**
       * Provides the `fromTo` animation tween for one or more positionable instances as a group.
       *
       * @param positionGroup - A position group.
       *
       * @param fromData - A position data object assigned to all positionable instances or a callback function invoked for
       *        unique data for each instance.
       *
       * @param toData - A position data object assigned to all positionable instances or a callback function invoked for
       *        unique data for each instance.
       *
       * @param [options] - Tween options assigned to all positionable instances or a callback function invoked for unique
       *        options for each instance.
       *
       * @returns Basic animation control.
       */
      static fromTo(positionGroup, fromData, toData, options) {
        if (!isObject(fromData) && typeof fromData !== "function") {
          throw new TypeError(`AnimationGroupAPI.fromTo error: 'fromData' is not an object or function.`);
        }
        if (!isObject(toData) && typeof toData !== "function") {
          throw new TypeError(`AnimationGroupAPI.fromTo error: 'toData' is not an object or function.`);
        }
        if (options !== void 0 && !isObject(options) && typeof options !== "function") {
          throw new TypeError(`AnimationGroupAPI.fromTo error: 'options' is not an object or function.`);
        }
        const animationControls = /* @__PURE__ */ new Set();
        const cleanupFn = (data) => animationControls.delete(data.control);
        let index = -1;
        let callbackOptions;
        const hasFromCallback = typeof fromData === "function";
        const hasToCallback = typeof toData === "function";
        const hasOptionCallback = typeof options === "function";
        const hasCallback = hasFromCallback || hasToCallback || hasOptionCallback;
        if (hasCallback) {
          callbackOptions = { index, position: void 0, entry: void 0 };
        }
        let actualFromData = fromData;
        let actualToData = toData;
        let actualOptions = isObject(options) ? options : void 0;
        if (isIterable(positionGroup)) {
          for (const entry of positionGroup) {
            index++;
            const actualPosition = this.#getPosition(entry);
            if (!actualPosition) {
              console.warn(`AnimationGroupAPI.fromTo warning: No TJSPosition instance found at index: ${index}.`);
              continue;
            }
            if (hasCallback) {
              callbackOptions.index = index;
              callbackOptions.position = actualPosition;
              callbackOptions.entry = actualPosition !== entry ? entry : void 0;
            }
            if (hasFromCallback && typeof fromData === "function") {
              actualFromData = fromData(callbackOptions);
              if (actualFromData === null || actualFromData === void 0) {
                continue;
              }
              if (!isObject(actualFromData)) {
                throw new TypeError(`AnimationGroupAPI.fromTo error: 'fromData' callback function iteration(${index}) failed to return an object.`);
              }
            }
            if (hasToCallback && typeof toData === "function") {
              actualToData = toData(callbackOptions);
              if (actualToData === null || actualToData === void 0) {
                continue;
              }
              if (!isObject(actualToData)) {
                throw new TypeError(`AnimationGroupAPI.fromTo error: 'toData' callback function iteration(${index}) failed to return an object.`);
              }
            }
            if (hasOptionCallback && typeof options === "function") {
              actualOptions = options(callbackOptions);
              if (actualOptions === null || actualOptions === void 0) {
                continue;
              }
              if (!isObject(actualOptions)) {
                throw new TypeError(`AnimationGroupAPI.fromTo error: 'options' callback function iteration(${index}) failed to return an object.`);
              }
            }
            const animationControl = AnimationScheduler.fromTo(actualPosition, actualFromData, actualToData, actualOptions, cleanupFn);
            if (animationControl) {
              animationControls.add(animationControl);
            }
          }
        } else {
          const actualPosition = this.#getPosition(positionGroup);
          if (!actualPosition) {
            console.warn(`AnimationGroupAPI.fromTo warning: No TJSPosition instance found.`);
            return AnimationGroupControl.voidControl;
          }
          if (hasCallback) {
            callbackOptions.index = 0;
            callbackOptions.position = actualPosition;
            callbackOptions.entry = actualPosition !== positionGroup ? positionGroup : void 0;
          }
          if (hasFromCallback && typeof fromData === "function") {
            actualFromData = fromData(callbackOptions);
            if (actualFromData === null || actualFromData === void 0) {
              return AnimationGroupControl.voidControl;
            }
            if (!isObject(actualFromData)) {
              throw new TypeError(`AnimationGroupAPI.fromTo error: 'fromData' callback function failed to return an object.`);
            }
          }
          if (hasToCallback && typeof toData === "function") {
            actualToData = toData(callbackOptions);
            if (actualToData === null || actualToData === void 0) {
              return AnimationGroupControl.voidControl;
            }
            if (!isObject(actualToData)) {
              throw new TypeError(`AnimationGroupAPI.fromTo error: 'toData' callback function failed to return an object.`);
            }
          }
          if (hasOptionCallback && typeof options === "function") {
            actualOptions = options(callbackOptions);
            if (actualOptions === null || actualOptions === void 0) {
              return AnimationGroupControl.voidControl;
            }
            if (!isObject(actualOptions)) {
              throw new TypeError(`AnimationGroupAPI.fromTo error: 'options' callback function failed to return an object.`);
            }
          }
          const animationControl = AnimationScheduler.fromTo(actualPosition, actualFromData, actualToData, actualOptions, cleanupFn);
          if (animationControl) {
            animationControls.add(animationControl);
          }
        }
        return new AnimationGroupControl(animationControls);
      }
      /**
       * Provides the `to` animation tween for one or more positionable instances as a group.
       *
       * @param positionGroup - A position group.
       *
       * @param toData - A position data object assigned to all positionable instances or a callback function invoked for
       *        unique data for each instance.
       *
       * @param [options] - Tween options assigned to all positionable instances or a callback function invoked for unique
       *        options for each instance.
       *
       * @returns Basic animation control.
       */
      static to(positionGroup, toData, options) {
        if (!isObject(toData) && typeof toData !== "function") {
          throw new TypeError(`AnimationGroupAPI.to error: 'toData' is not an object or function.`);
        }
        if (options !== void 0 && !isObject(options) && typeof options !== "function") {
          throw new TypeError(`AnimationGroupAPI.to error: 'options' is not an object or function.`);
        }
        const animationControls = /* @__PURE__ */ new Set();
        const cleanupFn = (data) => animationControls.delete(data.control);
        let index = -1;
        let callbackOptions;
        const hasDataCallback = typeof toData === "function";
        const hasOptionCallback = typeof options === "function";
        const hasCallback = hasDataCallback || hasOptionCallback;
        if (hasCallback) {
          callbackOptions = { index, position: void 0, entry: void 0 };
        }
        let actualToData = toData;
        let actualOptions = isObject(options) ? options : void 0;
        if (isIterable(positionGroup)) {
          for (const entry of positionGroup) {
            index++;
            const actualPosition = this.#getPosition(entry);
            if (!actualPosition) {
              console.warn(`AnimationGroupAPI.to warning: No TJSPosition instance found at index: ${index}.`);
              continue;
            }
            if (hasCallback) {
              callbackOptions.index = index;
              callbackOptions.position = actualPosition;
              callbackOptions.entry = actualPosition !== entry ? entry : void 0;
            }
            if (hasDataCallback && typeof toData === "function") {
              actualToData = toData(callbackOptions);
              if (actualToData === null || actualToData === void 0) {
                continue;
              }
              if (!isObject(actualToData)) {
                throw new TypeError(`AnimationGroupAPI.to error: 'toData' callback function iteration(${index}) failed to return an object.`);
              }
            }
            if (hasOptionCallback && typeof options === "function") {
              actualOptions = options(callbackOptions);
              if (actualOptions === null || actualOptions === void 0) {
                continue;
              }
              if (!isObject(actualOptions)) {
                throw new TypeError(`AnimationGroupAPI.to error: 'options' callback function iteration(${index}) failed to return an object.`);
              }
            }
            const animationControl = AnimationScheduler.to(actualPosition, actualToData, actualOptions, cleanupFn);
            if (animationControl) {
              animationControls.add(animationControl);
            }
          }
        } else {
          const actualPosition = this.#getPosition(positionGroup);
          if (!actualPosition) {
            console.warn(`AnimationGroupAPI.to warning: No TJSPosition instance found.`);
            return AnimationGroupControl.voidControl;
          }
          if (hasCallback) {
            callbackOptions.index = 0;
            callbackOptions.position = actualPosition;
            callbackOptions.entry = actualPosition !== positionGroup ? positionGroup : void 0;
          }
          if (hasDataCallback && typeof toData === "function") {
            actualToData = toData(callbackOptions);
            if (actualToData === null || actualToData === void 0) {
              return AnimationGroupControl.voidControl;
            }
            if (!isObject(actualToData)) {
              throw new TypeError(`AnimationGroupAPI.to error: 'toData' callback function failed to return an object.`);
            }
          }
          if (hasOptionCallback && typeof options === "function") {
            actualOptions = options(callbackOptions);
            if (actualOptions === null || actualOptions === void 0) {
              return AnimationGroupControl.voidControl;
            }
            if (!isObject(actualOptions)) {
              throw new TypeError(`AnimationGroupAPI.to error: 'options' callback function failed to return an object.`);
            }
          }
          const animationControl = AnimationScheduler.to(actualPosition, actualToData, actualOptions, cleanupFn);
          if (animationControl) {
            animationControls.add(animationControl);
          }
        }
        return new AnimationGroupControl(animationControls);
      }
      /**
       * Provides the `quickTo` animation tweening function for one or more positionable instances as a group.
       *
       * @param positionGroup - A position group.
       *
       * @param keys - Animation keys to target.
       *
       * @param [options] - Quick tween options assigned to all positionable instances or a callback function invoked for
       *        unique options for each instance.
       *
       * @returns quick-to tween function.
       */
      static quickTo(positionGroup, keys, options) {
        if (!isIterable(keys)) {
          throw new TypeError(`AnimationGroupAPI.quickTo error: 'keys' is not an iterable list.`);
        }
        if (options !== void 0 && !isObject(options) && typeof options !== "function") {
          throw new TypeError(`AnimationGroupAPI.quickTo error: 'options' is not an object or function.`);
        }
        const quickToCallbacks = [];
        let index = -1;
        const hasOptionCallback = typeof options === "function";
        const callbackOptions = { index, position: void 0, entry: void 0 };
        let actualOptions = isObject(options) ? options : void 0;
        if (isIterable(positionGroup)) {
          for (const entry of positionGroup) {
            index++;
            const actualPosition = this.#getPosition(entry);
            if (!actualPosition) {
              console.warn(`AnimationGroupAPI.quickTo warning: No TJSPosition instance found at index: ${index}.`);
              continue;
            }
            callbackOptions.index = index;
            callbackOptions.position = actualPosition;
            callbackOptions.entry = actualPosition !== entry ? entry : void 0;
            if (hasOptionCallback && typeof options === "function") {
              actualOptions = options(callbackOptions);
              if (actualOptions === null || actualOptions === void 0) {
                continue;
              }
              if (!isObject(actualOptions)) {
                throw new TypeError(`AnimationGroupAPI.quickTo error: 'options' callback function iteration(${index}) failed to return an object.`);
              }
            }
            quickToCallbacks.push(actualPosition.animate.quickTo(keys, actualOptions));
          }
        } else {
          const actualPosition = this.#getPosition(positionGroup);
          if (!actualPosition) {
            console.warn(`AnimationGroupAPI.quickTo warning: No TJSPosition instance found.`);
            return;
          }
          callbackOptions.index = 0;
          callbackOptions.position = actualPosition;
          callbackOptions.entry = actualPosition !== positionGroup ? positionGroup : void 0;
          if (hasOptionCallback && typeof options === "function") {
            actualOptions = options(callbackOptions);
            if (actualOptions === null || actualOptions === void 0) {
              return;
            }
            if (!isObject(actualOptions)) {
              throw new TypeError(`AnimationGroupAPI.quickTo error: 'options' callback function failed to return an object.`);
            }
          }
          quickToCallbacks.push(actualPosition.animate.quickTo(keys, actualOptions));
        }
        const keysArray = [...keys];
        Object.freeze(keysArray);
        const quickToCB = (...args) => {
          const argsLength = args.length;
          if (argsLength === 0) {
            return;
          }
          if (typeof args[0] === "function") {
            const dataCallback = args[0];
            index = -1;
            let cntr = 0;
            if (isIterable(positionGroup)) {
              for (const entry of positionGroup) {
                index++;
                const actualPosition = this.#getPosition(entry);
                if (!actualPosition) {
                  continue;
                }
                callbackOptions.index = index;
                callbackOptions.position = actualPosition;
                callbackOptions.entry = actualPosition !== entry ? entry : void 0;
                const toData = dataCallback(callbackOptions);
                if (toData === null || toData === void 0) {
                  continue;
                }
                const toDataIterable = isIterable(toData);
                if (!Number.isFinite(toData) && !toDataIterable && !isObject(toData)) {
                  throw new TypeError(`AnimationGroupAPI.quickTo error: 'toData' callback function iteration(${index}) failed to return a finite number, iterable list, or object.`);
                }
                if (toDataIterable) {
                  quickToCallbacks[cntr++](...toData);
                } else {
                  quickToCallbacks[cntr++](toData);
                }
              }
            } else {
              const actualPosition = this.#getPosition(positionGroup);
              if (!actualPosition) {
                return;
              }
              callbackOptions.index = 0;
              callbackOptions.position = actualPosition;
              callbackOptions.entry = actualPosition !== positionGroup ? positionGroup : void 0;
              const toData = dataCallback(callbackOptions);
              if (toData === null || toData === void 0) {
                return;
              }
              const toDataIterable = isIterable(toData);
              if (!Number.isFinite(toData) && !toDataIterable && !isObject(toData)) {
                throw new TypeError(`AnimationGroupAPI.quickTo error: 'toData' callback function iteration(${index}) failed to return a finite number, iterable list, or object.`);
              }
              if (toDataIterable) {
                quickToCallbacks[cntr++](...toData);
              } else {
                quickToCallbacks[cntr++](toData);
              }
            }
          } else {
            for (let cntr = quickToCallbacks.length; --cntr >= 0; ) {
              quickToCallbacks[cntr](...args);
            }
          }
        };
        Object.defineProperty(quickToCB, "keys", {
          value: keysArray,
          writable: false,
          configurable: false
        });
        Object.defineProperty(quickToCB, "options", {
          /**
           * Sets options of quickTo tween.
           * @param options -
           */
          value: (options2) => {
            if (options2 !== void 0 && !isObject(options2)) {
              throw new TypeError(`AnimationGroupAPI.quickTo error: 'options' is not an object.`);
            }
            if (isObject(options2)) {
              for (let cntr = quickToCallbacks.length; --cntr >= 0; ) {
                quickToCallbacks[cntr].options(options2);
              }
            }
            return quickToCB;
          },
          writable: false,
          configurable: false
        });
        return quickToCB;
      }
    }
    Object.seal(AnimationGroupAPIImpl);
    class PositionStateAPI {
      /**
       */
      #data;
      /**
       */
      #dataSaved = /* @__PURE__ */ new Map();
      /**
       */
      #position;
      /**
       */
      #transforms;
      constructor(position, data, transforms) {
        this.#position = position;
        this.#data = data;
        this.#transforms = transforms;
        Object.seal(this);
      }
      /**
       * Clears all saved position data except any default state.
       */
      clear() {
        for (const key of this.#dataSaved.keys()) {
          if (key !== "#defaultData") {
            this.#dataSaved.delete(key);
          }
        }
      }
      /**
       * Returns any stored save state by name.
       *
       * @param options - Options.
       *
       * @param options.name - Saved data name.
       *
       * @returns Any saved position data.
       */
      get({ name: name2 }) {
        if (typeof name2 !== "string") {
          throw new TypeError(`TJSPosition - get error: 'name' is not a string.`);
        }
        return this.#dataSaved.get(name2);
      }
      /**
       * Returns any associated default position data.
       *
       * @returns Any saved default position data.
       */
      getDefault() {
        return this.#dataSaved.get("#defaultData");
      }
      /**
       * @returns The saved position data names / keys.
       */
      keys() {
        return this.#dataSaved.keys();
      }
      /**
       * Removes and returns any position data by name.
       *
       * @param options - Options.
       *
       * @param options.name - Name to remove and retrieve.
       *
       * @returns Any saved position data.
       */
      remove({ name: name2 }) {
        if (typeof name2 !== "string") {
          throw new TypeError(`TJSPosition - remove: 'name' is not a string.`);
        }
        const data = this.#dataSaved.get(name2);
        this.#dataSaved.delete(name2);
        return data;
      }
      /**
       * Resets position instance to default data and invokes set.
       *
       * @param [options] - Optional parameters.
       *
       * @param [options.keepZIndex=false] - When true keeps current z-index.
       *
       * @param [options.invokeSet=true] - When true invokes set method.
       *
       * @returns Operation successful.
       */
      reset({ keepZIndex = false, invokeSet = true } = {}) {
        const defaultData = this.#dataSaved.get("#defaultData");
        if (!isObject(defaultData)) {
          return false;
        }
        if (this.#position.animate.isScheduled) {
          this.#position.animate.cancel();
        }
        const zIndex = this.#position.zIndex;
        const data = Object.assign({}, defaultData);
        if (keepZIndex) {
          data.zIndex = zIndex;
        }
        this.#transforms.reset(data);
        const parent = this.#position.parent;
        if (parent?.reactive?.minimized) {
          parent?.maximize?.({ animate: false, duration: 0 });
        }
        if (invokeSet) {
          setTimeout(() => this.#position.set(data), 0);
        }
        return true;
      }
      /**
       * Restores a saved positional state returning the data. Several optional parameters are available to control
       * whether the restore action occurs silently (no store / inline styles updates), animates to the stored data, or
       * simply sets the stored data. Restoring via {@link AnimationAPI.to} allows specification of the duration and
       * easing along with configuring a Promise to be returned if awaiting the end of the animation.
       *
       * @param options - Parameters
       *
       * @param options.name - Saved data set name.
       *
       * @param [options.remove=false] - Deletes data set.
       *
       * @param [options.properties] - Specific properties to set / animate.
       *
       * @param [options.silent] - Set position data directly; no store or style updates.
       *
       * @param [options.async=false] - If animating return a Promise that resolves with any saved data.
       *
       * @param [options.animateTo=false] - Animate to restore data.
       *
       * @param [options.duration=0.1] - Duration in seconds.
       *
       * @param [options.ease='linear'] - Easing function name or function.
       *
       * @returns Any saved position data.
       */
      restore({ name: name2, remove = false, properties, silent = false, async = false, animateTo = false, duration = 0.1, ease = "linear" }) {
        if (typeof name2 !== "string") {
          throw new TypeError(`TJSPosition - restore error: 'name' is not a string.`);
        }
        const dataSaved = this.#dataSaved.get(name2);
        if (dataSaved) {
          if (remove) {
            this.#dataSaved.delete(name2);
          }
          let data = dataSaved;
          if (isIterable(properties)) {
            data = {};
            for (const property of properties) {
              data[property] = dataSaved[property];
            }
          }
          if (silent) {
            for (const property in data) {
              if (property in this.#data) {
                this.#data[property] = data[property];
              }
            }
            return dataSaved;
          } else if (animateTo) {
            if (data.transformOrigin !== this.#position.transformOrigin) {
              this.#position.transformOrigin = data.transformOrigin;
            }
            if (async) {
              return this.#position.animate.to(data, { duration, ease }).finished.then(() => dataSaved);
            } else {
              this.#position.animate.to(data, { duration, ease });
            }
          } else {
            this.#position.set(data);
          }
        }
        return async ? Promise.resolve(dataSaved) : dataSaved;
      }
      /**
       * Saves current position state with the opportunity to add extra data to the saved state. Simply include extra
       * properties in `options` to save extra data.
       *
       * @param options - Options.
       *
       * @param options.name - name to index this saved data.
       *
       * @param [optionsGet] - Additional options for {@link TJSPosition.get} when serializing position state. By default,
       *        `nullable` values are included.
       *
       * @returns Current position data plus any extra data stored.
       */
      save({ name: name2, ...extra }, optionsGet) {
        if (typeof name2 !== "string") {
          throw new TypeError(`TJSPosition - save error: 'name' is not a string.`);
        }
        const data = this.#position.get(extra, optionsGet);
        this.#dataSaved.set(name2, data);
        return data;
      }
      /**
       * Directly sets a saved position state. Simply include extra properties in `options` to set extra data.
       *
       * @param opts - Options.
       *
       * @param opts.name - name to index this saved data.
       */
      set({ name: name2, ...data }) {
        if (typeof name2 !== "string") {
          throw new TypeError(`TJSPosition - set error: 'name' is not a string.`);
        }
        this.#dataSaved.set(name2, data);
      }
    }
    class SystemBase {
      /**
       * When true constrains the min / max width or height to element.
       */
      #constrain;
      /**
       */
      #element;
      /**
       * When true the validator is active.
       */
      #enabled;
      /**
       * Provides a manual setting of the element height. As things go `offsetHeight` causes a browser layout and is not
       * performance oriented. If manually set this height is used instead of `offsetHeight`.
       */
      #height;
      /**
       * Set from an optional value in the constructor to lock accessors preventing modification.
       */
      #lock;
      /**
       * Stores the subscribers.
       */
      #subscribers = [];
      /**
       * Provides a manual setting of the element width. As things go `offsetWidth` causes a browser layout and is not
       * performance oriented. If manually set this width is used instead of `offsetWidth`.
       */
      #width;
      /**
       * @param [options] - Initial options.
       *
       * @param [options.constrain=true] - Initial constrained state.
       *
       * @param [options.element] - Target element.
       *
       * @param [options.enabled=true] - Enabled state.
       *
       * @param [options.lock=false] - Lock parameters from being set.
       *
       * @param [options.width] - Manual width.
       *
       * @param [options.height] - Manual height.
       */
      constructor({ constrain = true, element: element2, enabled = true, lock = false, width: width2, height } = {}) {
        this.#constrain = true;
        this.#enabled = true;
        this.constrain = constrain;
        this.element = element2;
        this.enabled = enabled;
        this.width = width2;
        this.height = height;
        this.#lock = typeof lock === "boolean" ? lock : false;
      }
      /**
       * @returns The current constrain state.
       */
      get constrain() {
        return this.#constrain;
      }
      /**
       * @returns Target element.
       */
      get element() {
        return this.#element;
      }
      /**
       * @returns The current enabled state.
       */
      get enabled() {
        return this.#enabled;
      }
      /**
       * @returns Get manual height.
       */
      get height() {
        return this.#height;
      }
      /**
       * @return Get locked state.
       */
      get locked() {
        return this.#lock;
      }
      /**
       * @returns Get manual width.
       */
      get width() {
        return this.#width;
      }
      /**
       * @param constrain - New constrain state.
       */
      set constrain(constrain) {
        if (this.#lock) {
          return;
        }
        if (typeof constrain !== "boolean") {
          throw new TypeError(`'constrain' is not a boolean.`);
        }
        this.#constrain = constrain;
        this.#updateSubscribers();
      }
      /**
       * @param element - Set target element.
       */
      set element(element2) {
        if (this.#lock) {
          return;
        }
        if (element2 === void 0 || element2 === null || A11yHelper.isFocusTarget(element2)) {
          this.#element = element2;
        } else {
          throw new TypeError(`'element' is not a HTMLElement, undefined, or null.`);
        }
        this.#updateSubscribers();
      }
      /**
       * @param enabled - New enabled state.
       */
      set enabled(enabled) {
        if (this.#lock) {
          return;
        }
        if (typeof enabled !== "boolean") {
          throw new TypeError(`'enabled' is not a boolean.`);
        }
        this.#enabled = enabled;
        this.#updateSubscribers();
      }
      /**
       * @param height - Set manual height.
       */
      set height(height) {
        if (this.#lock) {
          return;
        }
        if (height === void 0 || Number.isFinite(height)) {
          this.#height = height;
        } else {
          throw new TypeError(`'height' is not a finite number or undefined.`);
        }
        this.#updateSubscribers();
      }
      /**
       * @param width - Set manual width.
       */
      set width(width2) {
        if (this.#lock) {
          return;
        }
        if (width2 === void 0 || Number.isFinite(width2)) {
          this.#width = width2;
        } else {
          throw new TypeError(`'width' is not a finite number or undefined.`);
        }
        this.#updateSubscribers();
      }
      /**
       * Set manual width & height.
       *
       * @param width - New manual width.
       *
       * @param height - New manual height.
       */
      setDimension(width2, height) {
        if (this.#lock) {
          return;
        }
        if (width2 === void 0 || Number.isFinite(width2)) {
          this.#width = width2;
        } else {
          throw new TypeError(`'width' is not a finite number or undefined.`);
        }
        if (height === void 0 || Number.isFinite(height)) {
          this.#height = height;
        } else {
          throw new TypeError(`'height' is not a finite number or undefined.`);
        }
        this.#updateSubscribers();
      }
      /**
       * @param handler - Callback function that is invoked on update / changes. Receives a copy of the TJSPositionData.
       *
       * @returns Unsubscribe function.
       */
      subscribe(handler) {
        const currentIdx = this.#subscribers.findIndex((entry) => entry === handler);
        if (currentIdx === -1) {
          this.#subscribers.push(handler);
          handler(this);
        }
        return () => {
          const existingIdx = this.#subscribers.findIndex((entry) => entry === handler);
          if (existingIdx !== -1) {
            this.#subscribers.splice(existingIdx, 1);
          }
        };
      }
      /**
       * Updates subscribers on changes.
       */
      #updateSubscribers() {
        for (let cntr = 0; cntr < this.#subscribers.length; cntr++) {
          this.#subscribers[cntr](this);
        }
      }
    }
    class Centered extends SystemBase {
      /**
       * Get the left constraint based on any manual target values or the browser inner width.
       *
       * @param width - Target width.
       *
       * @returns Calculated left constraint.
       */
      getLeft(width2) {
        const boundsWidth = this.width ?? this.element?.offsetWidth ?? globalThis.innerWidth;
        return (boundsWidth - width2) / 2;
      }
      /**
       * Get the top constraint based on any manual target values or the browser inner height.
       *
       * @param height - Target height.
       *
       * @returns Calculated top constraint.
       */
      getTop(height) {
        const boundsHeight = this.height ?? this.element?.offsetHeight ?? globalThis.innerHeight;
        return (boundsHeight - height) / 2;
      }
    }
    class AdapterValidators {
      /**
       */
      #enabled = true;
      /**
       */
      #validatorData;
      /**
       */
      #mapUnsubscribe = /* @__PURE__ */ new Map();
      #updateFn;
      /**
       * @returns Returns this and internal storage for validator adapter.
       */
      static create(updateFn) {
        const validatorAPI = new AdapterValidators(updateFn);
        return [validatorAPI, validatorAPI.#validatorData];
      }
      /**
       */
      constructor(updateFn) {
        this.#validatorData = [];
        this.#updateFn = updateFn;
        Object.seal(this);
      }
      /**
       * @returns Returns the enabled state.
       */
      get enabled() {
        return this.#enabled;
      }
      /**
       * @returns Returns the length of the validators array.
       */
      get length() {
        return this.#validatorData.length;
      }
      /**
       * @param enabled - Sets enabled state.
       */
      set enabled(enabled) {
        if (typeof enabled !== "boolean") {
          throw new TypeError(`'enabled' is not a boolean.`);
        }
        this.#enabled = enabled;
      }
      /**
       * Provides an iterator for validators.
       *
       * @returns iterator.
       */
      *[Symbol.iterator]() {
        if (this.#validatorData.length === 0) {
          return;
        }
        for (const entry of this.#validatorData) {
          yield { ...entry };
        }
      }
      /**
       * Adds the given validators.
       *
       * @param validators - Validators to add.
       */
      add(...validators) {
        let subscribeCount = 0;
        for (const validator of validators) {
          const validatorType = typeof validator;
          if (validatorType !== "function" && validatorType !== "object" || validator === null) {
            throw new TypeError(`AdapterValidator error: 'validator' is not a function or object.`);
          }
          let data = void 0;
          let subscribeFn = void 0;
          switch (validatorType) {
            case "function":
              data = {
                id: void 0,
                validate: validator,
                weight: 1
              };
              subscribeFn = validator.subscribe;
              break;
            case "object":
              if ("validate" in validator) {
                if (typeof validator.validate !== "function") {
                  throw new TypeError(`AdapterValidator error: 'validate' attribute is not a function.`);
                }
                if (validator.weight !== void 0 && typeof validator.weight !== "number" || (validator?.weight < 0 || validator?.weight > 1)) {
                  throw new TypeError(`AdapterValidator error: 'weight' attribute is not a number between '0 - 1' inclusive.`);
                }
                data = {
                  id: validator.id !== void 0 ? validator.id : void 0,
                  validate: validator.validate.bind(validator),
                  weight: validator.weight || 1
                };
                subscribeFn = validator.validate.subscribe ?? validator.subscribe;
              } else {
                throw new TypeError(`AdapterValidator error: 'validate' attribute is not a function.`);
              }
              break;
          }
          const index = this.#validatorData.findIndex((value) => data.weight < value.weight);
          if (index >= 0) {
            this.#validatorData.splice(index, 0, data);
          } else {
            this.#validatorData.push(data);
          }
          if (typeof subscribeFn === "function") {
            const unsubscribe = subscribeFn.call(validator, this.#updateFn);
            if (typeof unsubscribe !== "function") {
              throw new TypeError("AdapterValidator error: Validator has subscribe function, but no unsubscribe function is returned.");
            }
            if (this.#mapUnsubscribe.has(data.validate)) {
              throw new Error("AdapterValidator error: Validator added already has an unsubscribe function registered.");
            }
            this.#mapUnsubscribe.set(data.validate, unsubscribe);
            subscribeCount++;
          }
        }
        if (subscribeCount < validators.length) {
          this.#updateFn();
        }
      }
      /**
       * Clears / removes all validators.
       */
      clear() {
        this.#validatorData.length = 0;
        for (const unsubscribe of this.#mapUnsubscribe.values()) {
          unsubscribe();
        }
        this.#mapUnsubscribe.clear();
        this.#updateFn();
      }
      /**
       * Removes one or more given validators.
       *
       * @param validators - Validators to remove.
       */
      remove(...validators) {
        const length = this.#validatorData.length;
        if (length === 0) {
          return;
        }
        for (const data of validators) {
          const actualValidator = typeof data === "function" ? data : isObject(data) ? data.validate : void 0;
          if (!actualValidator) {
            continue;
          }
          for (let cntr = this.#validatorData.length; --cntr >= 0; ) {
            if (this.#validatorData[cntr].validate === actualValidator) {
              this.#validatorData.splice(cntr, 1);
              let unsubscribe = void 0;
              if (typeof (unsubscribe = this.#mapUnsubscribe.get(actualValidator)) === "function") {
                unsubscribe();
                this.#mapUnsubscribe.delete(actualValidator);
              }
            }
          }
        }
        if (length !== this.#validatorData.length) {
          this.#updateFn();
        }
      }
      /**
       * Remove validators by the provided callback. The callback takes 3 parameters: `id`, `validator`, and `weight`.
       * Any truthy value returned will remove that validator.
       *
       * @param callback - Callback function to evaluate each validator entry.
       */
      removeBy(callback) {
        const length = this.#validatorData.length;
        if (length === 0) {
          return;
        }
        if (typeof callback !== "function") {
          throw new TypeError(`AdapterValidator error: 'callback' is not a function.`);
        }
        this.#validatorData = this.#validatorData.filter((data) => {
          const remove = callback.call(callback, { ...data });
          if (remove) {
            let unsubscribe;
            if (typeof (unsubscribe = this.#mapUnsubscribe.get(data.validate)) === "function") {
              unsubscribe();
              this.#mapUnsubscribe.delete(data.validate);
            }
          }
          return !remove;
        });
        if (length !== this.#validatorData.length) {
          this.#updateFn();
        }
      }
      /**
       * Removes any validators with matching IDs.
       *
       * @param ids - IDs to remove.
       */
      removeById(...ids) {
        const length = this.#validatorData.length;
        if (length === 0) {
          return;
        }
        this.#validatorData = this.#validatorData.filter((data) => {
          let remove = false;
          for (const id of ids) {
            remove ||= data.id === id;
          }
          if (remove) {
            let unsubscribe;
            if (typeof (unsubscribe = this.#mapUnsubscribe.get(data.validate)) === "function") {
              unsubscribe();
              this.#mapUnsubscribe.delete(data.validate);
            }
          }
          return !remove;
        });
        if (length !== this.#validatorData.length) {
          this.#updateFn();
        }
      }
    }
    class TransformBounds extends SystemBase {
      static #TRANSFORM_DATA = new TJSTransformData();
      /**
       * Provides a validator that respects transforms in positional data constraining the position to within the target
       * elements bounds.
       *
       * @param valData - The associated validation data for position updates.
       *
       * @returns Potentially adjusted position data.
       */
      validate(valData) {
        if (!this.enabled) {
          return valData.position;
        }
        const boundsWidth = this.width ?? this.element?.offsetWidth ?? globalThis.innerWidth;
        const boundsHeight = this.height ?? this.element?.offsetHeight ?? globalThis.innerHeight;
        if (typeof valData.position.width === "number") {
          const maxW = valData.maxWidth ?? (this.constrain ? boundsWidth : Number.MAX_SAFE_INTEGER);
          valData.position.width = clamp(valData.width, valData.minWidth, maxW);
        }
        if (typeof valData.position.height === "number") {
          const maxH = valData.maxHeight ?? (this.constrain ? boundsHeight : Number.MAX_SAFE_INTEGER);
          valData.position.height = clamp(valData.height, valData.minHeight, maxH);
        }
        const data = valData.transforms.getData(valData.position, TransformBounds.#TRANSFORM_DATA, valData);
        const initialX = data.boundingRect.x;
        const initialY = data.boundingRect.y;
        const marginTop = valData.marginTop ?? 0;
        const marginLeft = valData.marginLeft ?? 0;
        if (data.boundingRect.bottom + marginTop > boundsHeight) {
          data.boundingRect.y += boundsHeight - data.boundingRect.bottom - marginTop;
        }
        if (data.boundingRect.right + marginLeft > boundsWidth) {
          data.boundingRect.x += boundsWidth - data.boundingRect.right - marginLeft;
        }
        if (data.boundingRect.top - marginTop < 0) {
          data.boundingRect.y += Math.abs(data.boundingRect.top - marginTop);
        }
        if (data.boundingRect.left - marginLeft < 0) {
          data.boundingRect.x += Math.abs(data.boundingRect.left - marginLeft);
        }
        valData.position.left -= initialX - data.boundingRect.x;
        valData.position.top -= initialY - data.boundingRect.y;
        return valData.position;
      }
    }
    class PositionChangeSet {
      left;
      top;
      width;
      height;
      maxHeight;
      maxWidth;
      minHeight;
      minWidth;
      zIndex;
      transform;
      transformOrigin;
      constructor() {
        this.left = false;
        this.top = false;
        this.width = false;
        this.height = false;
        this.maxHeight = false;
        this.maxWidth = false;
        this.minHeight = false;
        this.minWidth = false;
        this.zIndex = false;
        this.transform = false;
        this.transformOrigin = false;
      }
      hasChange() {
        return this.left || this.top || this.width || this.height || this.maxHeight || this.maxWidth || this.minHeight || this.minWidth || this.zIndex || this.transform || this.transformOrigin;
      }
      set(value) {
        this.left = value;
        this.top = value;
        this.width = value;
        this.height = value;
        this.maxHeight = value;
        this.maxWidth = value;
        this.minHeight = value;
        this.minWidth = value;
        this.zIndex = value;
        this.transform = value;
        this.transformOrigin = value;
      }
    }
    class UpdateElementData {
      changeSet;
      data;
      dataSubscribers;
      dimensionData;
      options;
      queued;
      storeDimension;
      storeTransform;
      styleCache;
      subscribers;
      transforms;
      transformData;
      constructor(changeSet, data, options, styleCache, subscribers, transforms) {
        this.changeSet = changeSet;
        this.data = data;
        this.dataSubscribers = Object.seal(new TJSPositionData());
        this.dimensionData = Object.seal({ width: 0, height: 0 });
        this.options = options;
        this.queued = false;
        this.styleCache = styleCache;
        this.storeDimension = writable(this.dimensionData);
        this.subscribers = subscribers;
        this.transforms = transforms;
        this.transformData = new TJSTransformData();
        this.storeTransform = writable(this.transformData, () => {
          this.options.transformSubscribed = true;
          return () => this.options.transformSubscribed = false;
        });
      }
    }
    class UpdateElementManager {
      /**
       * Stores the active list of all TJSPosition instances currently updating. The list entries are recycled between
       * updates.
       */
      static list = [];
      /**
       * Tracks the current position in the list.
       */
      static listCntr = 0;
      static updatePromise;
      /**
       * Potentially adds the given element and internal updateData instance to the list.
       *
       * @param el - An HTMLElement instance.
       *
       * @param updateData - An UpdateElementData instance.
       *
       * @returns The unified next frame update promise. Returns `currentTime`.
       */
      static add(el, updateData) {
        if (this.listCntr < this.list.length) {
          const entry = this.list[this.listCntr];
          entry[0] = el;
          entry[1] = updateData;
        } else {
          this.list.push([el, updateData]);
        }
        this.listCntr++;
        updateData.queued = true;
        if (!this.updatePromise) {
          this.updatePromise = this.wait();
        }
        return this.updatePromise;
      }
      /**
       * Await on `nextAnimationFrame` and iterate over list map invoking callback functions.
       *
       * @returns The next frame Promise / currentTime from nextAnimationFrame.
       */
      static async wait() {
        const currentTime = await nextAnimationFrame();
        this.updatePromise = void 0;
        for (let cntr = this.listCntr; --cntr >= 0; ) {
          const entry = this.list[cntr];
          const el = entry[0];
          const updateData = entry[1];
          entry[0] = void 0;
          entry[1] = void 0;
          updateData.queued = false;
          if (!el.isConnected) {
            continue;
          }
          if (updateData.options.ortho) {
            UpdateElementManager.#updateElementOrtho(el, updateData);
          } else {
            UpdateElementManager.#updateElement(el, updateData);
          }
          if (updateData.options.calculateTransform || updateData.options.transformSubscribed) {
            UpdateElementManager.#updateTransform(updateData);
          }
          this.updateSubscribers(updateData);
        }
        this.listCntr = 0;
        return currentTime;
      }
      /**
       * Potentially immediately updates the given element.
       *
       * @param el - An HTMLElement instance.
       *
       * @param updateData - An UpdateElementData instance.
       */
      static immediate(el, updateData) {
        if (!el.isConnected) {
          return;
        }
        if (updateData.options.ortho) {
          UpdateElementManager.#updateElementOrtho(el, updateData);
        } else {
          UpdateElementManager.#updateElement(el, updateData);
        }
        if (updateData.options.calculateTransform || updateData.options.transformSubscribed) {
          UpdateElementManager.#updateTransform(updateData);
        }
        this.updateSubscribers(updateData);
      }
      /**
       * @param updateData - Data change set.
       */
      static updateSubscribers(updateData) {
        const data = updateData.data;
        const changeSet = updateData.changeSet;
        if (!changeSet.hasChange()) {
          return;
        }
        const output = TJSPositionDataUtil.copyData(data, updateData.dataSubscribers);
        const subscribers = updateData.subscribers;
        if (subscribers.length > 0) {
          for (let cntr = 0; cntr < subscribers.length; cntr++) {
            subscribers[cntr](output);
          }
        }
        if (changeSet.width || changeSet.height) {
          updateData.dimensionData.width = data.width;
          updateData.dimensionData.height = data.height;
          updateData.storeDimension.set(updateData.dimensionData);
        }
        changeSet.set(false);
      }
      // Internal Implementation ----------------------------------------------------------------------------------------
      /**
       * Temporary data for validation.
       */
      static #validationData = Object.seal({
        height: 0,
        width: 0,
        marginLeft: 0,
        marginTop: 0
      });
      /**
       * Decouples updates to any parent target HTMLElement inline styles. Invoke {@link TJSPosition.elementUpdated} to
       * await on the returned promise that is resolved with the current render time via `nextAnimationFrame` /
       * `requestAnimationFrame`. This allows the underlying data model to be updated immediately while updates to the
       * element are in sync with the browser and potentially in the future be further throttled.
       *
       * @param el - The target HTMLElement.
       *
       * @param updateData - Update data.
       */
      static #updateElement(el, updateData) {
        const changeSet = updateData.changeSet;
        const data = updateData.data;
        if (changeSet.left) {
          el.style.left = `${data.left}px`;
        }
        if (changeSet.top) {
          el.style.top = `${data.top}px`;
        }
        if (changeSet.zIndex) {
          el.style.zIndex = typeof data.zIndex === "number" ? `${data.zIndex}` : "";
        }
        if (changeSet.width) {
          el.style.width = typeof data.width === "number" ? `${data.width}px` : data.width;
        }
        if (changeSet.height) {
          el.style.height = typeof data.height === "number" ? `${data.height}px` : data.height;
        }
        if (changeSet.transformOrigin) {
          el.style.transformOrigin = data.transformOrigin;
        }
        if (changeSet.transform) {
          el.style.transform = updateData.transforms.isActive ? updateData.transforms.getCSS() : "";
        }
      }
      /**
       * Decouples updates to any parent target HTMLElement inline styles. Invoke {@link TJSPosition.elementUpdated} to
       * await on the returned promise that is resolved with the current render time via `nextAnimationFrame` /
       * `requestAnimationFrame`. This allows the underlying data model to be updated immediately while updates to the
       * element are in sync with the browser and potentially in the future be further throttled.
       *
       * @param el - The target HTMLElement.
       *
       * @param updateData - Update data.
       */
      static #updateElementOrtho(el, updateData) {
        const changeSet = updateData.changeSet;
        const data = updateData.data;
        if (changeSet.zIndex) {
          el.style.zIndex = typeof data.zIndex === "number" ? `${data.zIndex}` : "";
        }
        if (changeSet.width) {
          el.style.width = typeof data.width === "number" ? `${data.width}px` : data.width;
        }
        if (changeSet.height) {
          el.style.height = typeof data.height === "number" ? `${data.height}px` : data.height;
        }
        if (changeSet.transformOrigin) {
          el.style.transformOrigin = data.transformOrigin;
        }
        if (changeSet.left || changeSet.top || changeSet.transform) {
          el.style.transform = updateData.transforms.getCSSOrtho(data);
        }
      }
      /**
       * Updates the applied transform data and sets the readable `transform` store.
       *
       * @param updateData - Update element data.
       */
      static #updateTransform(updateData) {
        const validationData = this.#validationData;
        validationData.height = updateData.data.height !== "auto" && updateData.data.height !== "inherit" ? updateData.data.height : updateData.styleCache.offsetHeight;
        validationData.width = updateData.data.width !== "auto" && updateData.data.width !== "inherit" ? updateData.data.width : updateData.styleCache.offsetWidth;
        validationData.marginLeft = updateData.styleCache.marginLeft;
        validationData.marginTop = updateData.styleCache.marginTop;
        updateData.transforms.getData(updateData.data, updateData.transformData, validationData);
        updateData.storeTransform.set(updateData.transformData);
      }
    }
    var _a;
    class TJSPosition {
      /**
       * Public API for {@link TJSPosition.Initial}.
       */
      static #positionInitial = Object.freeze({
        browserCentered: new Centered({ lock: true }),
        Centered
      });
      /**
       * Public API for {@link TJSPosition.Validators}
       */
      static #positionValidators = Object.freeze({
        TransformBounds,
        transformWindow: new TransformBounds({ lock: true })
      });
      /**
       * Stores all position data / properties.
       */
      #data = Object.seal(new TJSPositionData());
      /**
       * Provides the animation API.
       */
      #animate = new AnimationAPIImpl(this, this.#data);
      /**
       * Provides a way to turn on / off the position handling.
       */
      #enabled = true;
      /**
       * Stores ongoing options that are set in the constructor or by transform store subscription.
       */
      #options = {
        calculateTransform: false,
        initial: void 0,
        ortho: true,
        transformSubscribed: false
      };
      /**
       * The associated parent for positional data tracking. Used in validators.
       */
      #parent;
      /**
       * Stores the style attributes that changed on update.
       */
      #positionChangeSet = new PositionChangeSet();
      /**
       * Tracks the current state if this position instance is a candidate for resize observation by the `resizeObserver`
       * action. This is `true` when `width` or `height` is `auto` or `inherit`.
       */
      #resizeObservable = false;
      /**
       */
      #stores;
      /**
       * Stores an instance of the computer styles for the target element.
       */
      #styleCache;
      /**
       * Stores the subscribers.
       */
      #subscribers = [];
      /**
       */
      #transforms = new TJSTransforms();
      /**
       */
      #updateElementData;
      /**
       * Stores the UpdateElementManager wait promise.
       */
      #updateElementPromise;
      /**
       */
      #validators;
      /**
       */
      #validatorData;
      /**
       */
      #state = new PositionStateAPI(this, this.#data, this.#transforms);
      /**
       * @returns Public Animation Group API.
       */
      static get Animate() {
        return AnimationGroupAPIImpl;
      }
      /**
       * @returns TJSPositionData constructor.
       */
      static get Data() {
        return TJSPositionData;
      }
      /**
       * @returns TJSPosition default initial systems.
       */
      static get Initial() {
        return this.#positionInitial;
      }
      /**
       * @returns `SystemBase` constructor.
       */
      static get SystemBase() {
        return SystemBase;
      }
      /**
       * Returns TJSTransformData class / constructor.
       *
       * @returns TransformData class / constructor.
       */
      static get TransformData() {
        return TJSTransformData;
      }
      /**
       * Returns default validator systems.
       *
       * @returns Available validators.
       */
      static get Validators() {
        return this.#positionValidators;
      }
      /**
       * Returns a list of supported transform origins.
       *
       * @returns The supported transform origin strings.
       */
      static get transformOrigins() {
        return TJSTransforms.transformOrigins;
      }
      /**
       * Convenience to copy from source to target of two TJSPositionData like objects. If a target is not supplied a new
       * {@link TJSPositionData} instance is created.
       *
       * @param source - The source instance to copy from.
       *
       * @param [target] - Target TJSPositionData like object; if one is not provided a new instance is created.
       *
       * @returns The target instance with all TJSPositionData fields.
       */
      static copyData(source, target) {
        return TJSPositionDataUtil.copyData(source, target);
      }
      /**
       * Returns a duplicate of a given position instance copying any options and validators. The position parent is not
       * copied and a new one must be set manually via the {@link TJSPosition.parent} setter.
       *
       * @param position - A position instance.
       *
       * @param [options] - Unique new options to set.
       *
       * @returns A duplicate position instance.
       */
      static duplicate(position, options = {}) {
        if (!(position instanceof _a)) {
          throw new TypeError(`'position' is not an instance of TJSPosition.`);
        }
        const newPosition = new _a(options);
        newPosition.#options = Object.assign({}, position.#options, options);
        newPosition.#validators.add(...position.#validators);
        newPosition.set(position.#data);
        return newPosition;
      }
      /**
       * @param [parentOrOptions] - A  potential parent element or object w/ `elementTarget` accessor. You may also forego
       *        setting the parent and pass in the options object.
       *
       * @param [options] - The options object.
       */
      constructor(parentOrOptions, options) {
        if (isPlainObject(parentOrOptions)) {
          options = parentOrOptions;
        } else {
          this.#parent = parentOrOptions;
        }
        this.#styleCache = new TJSPositionStyleCache();
        const updateData = new UpdateElementData(this.#positionChangeSet, this.#data, this.#options, this.#styleCache, this.#subscribers, this.#transforms);
        this.#updateElementData = updateData;
        if (typeof options?.calculateTransform === "boolean") {
          this.#options.calculateTransform = options.calculateTransform;
        }
        if (typeof options?.ortho === "boolean") {
          this.#options.ortho = options.ortho;
        }
        this.#stores = Object.freeze({
          // The main properties for manipulating TJSPosition.
          height: propertyStore(this, "height"),
          left: propertyStore(this, "left"),
          rotateX: propertyStore(this, "rotateX"),
          rotateY: propertyStore(this, "rotateY"),
          rotateZ: propertyStore(this, "rotateZ"),
          scale: propertyStore(this, "scale"),
          top: propertyStore(this, "top"),
          transformOrigin: propertyStore(this, "transformOrigin"),
          translateX: propertyStore(this, "translateX"),
          translateY: propertyStore(this, "translateY"),
          translateZ: propertyStore(this, "translateZ"),
          width: propertyStore(this, "width"),
          zIndex: propertyStore(this, "zIndex"),
          // Stores that control validation when width / height is not `auto`.
          maxHeight: propertyStore(this, "maxHeight"),
          maxWidth: propertyStore(this, "maxWidth"),
          minHeight: propertyStore(this, "minHeight"),
          minWidth: propertyStore(this, "minWidth"),
          // Readable stores based on updates or from resize observer changes.
          dimension: { subscribe: updateData.storeDimension.subscribe },
          element: { subscribe: this.#styleCache.stores.element.subscribe },
          resizeContentHeight: { subscribe: this.#styleCache.stores.resizeContentHeight.subscribe },
          resizeContentWidth: { subscribe: this.#styleCache.stores.resizeContentWidth.subscribe },
          resizeObservable: { subscribe: this.#styleCache.stores.resizeObservable.subscribe },
          resizeOffsetHeight: { subscribe: this.#styleCache.stores.resizeOffsetHeight.subscribe },
          resizeOffsetWidth: { subscribe: this.#styleCache.stores.resizeOffsetWidth.subscribe },
          transform: { subscribe: updateData.storeTransform.subscribe },
          // Protected store that should only be set by resizeObserver action.
          resizeObserved: this.#styleCache.stores.resizeObserved
        });
        Object.defineProperty(this.#stores.transformOrigin, "values", {
          get: () => _a.transformOrigins
        });
        subscribeIgnoreFirst(this.#stores.resizeObserved, (resizeData) => {
          const parent = this.#parent;
          const el = A11yHelper.isFocusTarget(parent) ? parent : parent?.elementTarget;
          if (A11yHelper.isFocusTarget(el) && Number.isFinite(resizeData?.offsetWidth) && Number.isFinite(resizeData?.offsetHeight)) {
            this.set();
          }
        });
        [this.#validators, this.#validatorData] = AdapterValidators.create(() => this.set());
        if (options?.initial) {
          const initial = options.initial;
          if (typeof initial?.getLeft !== "function" || typeof initial?.getTop !== "function") {
            throw new Error(`'options.initial' position helper does not contain 'getLeft' and / or 'getTop' functions.`);
          }
          this.#options.initial = initial;
        }
        if (options?.validator) {
          if (isIterable(options?.validator)) {
            this.validators.add(...options.validator);
          } else {
            const validatorFn = options.validator;
            this.validators.add(validatorFn);
          }
        }
        Object.seal(this);
        if (isObject(options)) {
          this.set(options);
        }
      }
      /**
       * Returns the animation API.
       *
       * @returns Animation instance API.
       */
      get animate() {
        return this.#animate;
      }
      /**
       * Returns the dimension data for the readable store.
       *
       * @returns Dimension data.
       */
      get dimension() {
        return this.#updateElementData.dimensionData;
      }
      /**
       * Returns the enabled state.
       *
       * @returns Enabled state.
       */
      get enabled() {
        return this.#enabled;
      }
      /**
       * Returns the current HTMLElement being positioned.
       *
       * @returns Current HTMLElement being positioned.
       */
      get element() {
        return this.#styleCache.el;
      }
      /**
       * Returns a promise that is resolved on the next element update with the time of the update.
       *
       * @returns Promise resolved on element update.
       */
      get elementUpdated() {
        return this.#updateElementPromise;
      }
      /**
       * Returns the associated {@link TJSPosition.PositionParent} instance.
       *
       * @returns The current position parent instance.
       */
      get parent() {
        return this.#parent;
      }
      /**
       * Returns the state API.
       *
       * @returns TJSPosition state API.
       */
      get state() {
        return this.#state;
      }
      /**
       * Returns the derived writable stores for individual data variables.
       *
       * @returns Derived / writable stores.
       */
      get stores() {
        return this.#stores;
      }
      /**
       * Returns the transform data for the readable store.
       *
       * @returns Transform Data.
       */
      get transform() {
        return this.#updateElementData.transformData;
      }
      /**
       * Returns the validators.
       *
       * @returns Validators API
       */
      get validators() {
        return this.#validators;
      }
      /**
       * Sets the enabled state.
       *
       * @param enabled - New enabled state.
       */
      set enabled(enabled) {
        if (typeof enabled !== "boolean") {
          throw new TypeError(`'enabled' is not a boolean.`);
        }
        this.#enabled = enabled;
      }
      /**
       * Sets the associated {@link TJSPosition.PositionParent} instance. Resets the style cache and default data.
       *
       * @param parent - A PositionParent instance or undefined to disassociate
       */
      set parent(parent) {
        if (parent !== void 0 && !A11yHelper.isFocusTarget(parent) && !isObject(parent)) {
          throw new TypeError(`'parent' is not an HTMLElement, object, or undefined.`);
        }
        this.#parent = parent;
        this.#state.remove({ name: "#defaultData" });
        this.#styleCache.reset();
        if (parent) {
          this.set(this.#data);
        }
      }
      // Data accessors ----------------------------------------------------------------------------------------------------
      /**
       * @returns height
       */
      get height() {
        return this.#data.height;
      }
      /**
       * @returns left
       */
      get left() {
        return this.#data.left;
      }
      /**
       * @returns maxHeight
       */
      get maxHeight() {
        return this.#data.maxHeight;
      }
      /**
       * @returns maxWidth
       */
      get maxWidth() {
        return this.#data.maxWidth;
      }
      /**
       * @returns minHeight
       */
      get minHeight() {
        return this.#data.minHeight;
      }
      /**
       * @returns minWidth
       */
      get minWidth() {
        return this.#data.minWidth;
      }
      /**
       * @returns rotateX
       */
      get rotateX() {
        return this.#data.rotateX;
      }
      /**
       * @returns rotateY
       */
      get rotateY() {
        return this.#data.rotateY;
      }
      /**
       * @returns rotateZ
       */
      get rotateZ() {
        return this.#data.rotateZ;
      }
      /**
       * @returns Alias for rotateZ
       */
      get rotation() {
        return this.#data.rotateZ;
      }
      /**
       * @returns scale
       */
      get scale() {
        return this.#data.scale;
      }
      /**
       * @returns top
       */
      get top() {
        return this.#data.top;
      }
      /**
       * @returns transformOrigin
       */
      get transformOrigin() {
        return this.#data.transformOrigin;
      }
      /**
       * @returns translateX
       */
      get translateX() {
        return this.#data.translateX;
      }
      /**
       * @returns translateY
       */
      get translateY() {
        return this.#data.translateY;
      }
      /**
       * @returns translateZ
       */
      get translateZ() {
        return this.#data.translateZ;
      }
      /**
       * @returns width
       */
      get width() {
        return this.#data.width;
      }
      /**
       * @returns z-index
       */
      get zIndex() {
        return this.#data.zIndex;
      }
      /**
       * @param height -
       */
      set height(height) {
        this.#stores.height.set(height);
      }
      /**
       * @param left -
       */
      set left(left) {
        this.#stores.left.set(left);
      }
      /**
       * @param maxHeight -
       */
      set maxHeight(maxHeight) {
        this.#stores.maxHeight.set(maxHeight);
      }
      /**
       * @param maxWidth -
       */
      set maxWidth(maxWidth) {
        this.#stores.maxWidth.set(maxWidth);
      }
      /**
       * @param minHeight -
       */
      set minHeight(minHeight) {
        this.#stores.minHeight.set(minHeight);
      }
      /**
       * @param minWidth -
       */
      set minWidth(minWidth) {
        this.#stores.minWidth.set(minWidth);
      }
      /**
       * @param rotateX -
       */
      set rotateX(rotateX) {
        this.#stores.rotateX.set(rotateX);
      }
      /**
       * @param rotateY -
       */
      set rotateY(rotateY) {
        this.#stores.rotateY.set(rotateY);
      }
      /**
       * @param rotateZ -
       */
      set rotateZ(rotateZ) {
        this.#stores.rotateZ.set(rotateZ);
      }
      /**
       * @param  rotateZ - alias for rotateZ
       */
      set rotation(rotateZ) {
        this.#stores.rotateZ.set(rotateZ);
      }
      /**
       * @param scale -
       */
      set scale(scale2) {
        this.#stores.scale.set(scale2);
      }
      /**
       * @param top -
       */
      set top(top) {
        this.#stores.top.set(top);
      }
      /**
       * @param transformOrigin -
       */
      set transformOrigin(transformOrigin) {
        if (TJSTransforms.transformOrigins.includes(transformOrigin)) {
          this.#stores.transformOrigin.set(transformOrigin);
        }
      }
      /**
       * @param translateX -
       */
      set translateX(translateX) {
        this.#stores.translateX.set(translateX);
      }
      /**
       * @param translateY -
       */
      set translateY(translateY) {
        this.#stores.translateY.set(translateY);
      }
      /**
       * @param translateZ -
       */
      set translateZ(translateZ) {
        this.#stores.translateZ.set(translateZ);
      }
      /**
       * @param width -
       */
      set width(width2) {
        this.#stores.width.set(width2);
      }
      /**
       * @param zIndex -
       */
      set zIndex(zIndex) {
        this.#stores.zIndex.set(zIndex);
      }
      /**
       * Assigns current position data to given object `data` object. By default, `null` position data is not assigned.
       * Other options allow configuration of the data assigned including setting default numeric values for any properties
       * that are null.
       *
       * @param [data] - Target to assign current position data.
       *
       * @param [options] - Defines options for specific keys and substituting null for numeric default values. By
       *        default, nullable keys are included.
       *
       * @returns Passed in object with current position data.
       */
      get(data = {}, options = {}) {
        const keys = options?.keys;
        const excludeKeys = options?.exclude;
        const nullable = options?.nullable ?? true;
        const numeric = options?.numeric ?? false;
        if (isIterable(keys)) {
          for (const key of keys) {
            data[key] = numeric ? TJSPositionDataUtil.getDataOrDefault(this, key) : this[key];
            if (!nullable && data[key] === null) {
              delete data[key];
            }
          }
          if (isIterable(excludeKeys)) {
            for (const key of excludeKeys) {
              delete data[key];
            }
          }
          return data;
        } else {
          data = Object.assign(data, this.#data);
          if (isIterable(excludeKeys)) {
            for (const key of excludeKeys) {
              delete data[key];
            }
          }
          if (numeric) {
            TJSPositionDataUtil.setNumericDefaults(data);
          }
          if (!nullable) {
            for (const key in data) {
              if (data[key] === null) {
                delete data[key];
              }
            }
          }
          return data;
        }
      }
      /**
       * @returns Current position data.
       */
      toJSON() {
        return Object.assign({}, this.#data);
      }
      /**
       * All calculation and updates of position are implemented in {@link TJSPosition}. This allows position to be fully
       * reactive and in control of updating inline styles for a connected {@link HTMLElement}.
       *
       * The initial set call with a target element will always set width / height as this is necessary for correct
       * calculations.
       *
       * When a target element is present updated styles are applied after validation. To modify the behavior of set
       * implement one or more validator functions and add them via the validator API available from
       * {@link TJSPosition.validators}.
       *
       * Updates to any target element are decoupled from the underlying TJSPosition data. This method returns this
       * instance that you can then await on the target element inline style update by using
       * {@link TJSPosition.elementUpdated}.
       *
       * Relative updates to any property of {@link TJSPositionData} are possible by specifying properties as strings.
       * This string should be in the form of '+=', '-=', or '*=' and float / numeric value. IE '+=0.2'.
       * {@link TJSPosition.set} will apply the `addition`, `subtraction`, or `multiplication` operation specified against
       * the current value of the given property. Please see {@link Data.TJSPositionDataRelative} for a detailed
       * description.
       *
       * @param [position] - TJSPosition data to set.
       *
       * @param [options] - Additional options.
       *
       * @returns This TJSPosition instance.
       */
      set(position = {}, options = {}) {
        if (!isObject(position)) {
          throw new TypeError(`TJSPosition - set error: 'position' is not an object.`);
        }
        const parent = this.#parent;
        if (!this.#enabled) {
          return this;
        }
        if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
          return this;
        }
        const immediateElementUpdate = options?.immediateElementUpdate ?? false;
        const data = this.#data;
        const transforms = this.#transforms;
        const targetEl = A11yHelper.isFocusTarget(parent) ? parent : parent?.elementTarget;
        const el = A11yHelper.isFocusTarget(targetEl) && targetEl.isConnected ? targetEl : void 0;
        const changeSet = this.#positionChangeSet;
        const styleCache = this.#styleCache;
        if (el) {
          if (!styleCache.hasData(el)) {
            styleCache.update(el);
            if (!styleCache.hasWillChange) ;
            changeSet.set(true);
            this.#updateElementData.queued = false;
          }
          ConvertStringData.process(position, this.#data, el);
          position = this.#updatePosition(position, parent, el, styleCache);
          if (position === null) {
            return this;
          }
        }
        if (NumberGuard.isFinite(position.left)) {
          position.left = Math.round(position.left);
          if (data.left !== position.left) {
            data.left = position.left;
            changeSet.left = true;
          }
        }
        if (NumberGuard.isFinite(position.top)) {
          position.top = Math.round(position.top);
          if (data.top !== position.top) {
            data.top = position.top;
            changeSet.top = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.maxHeight)) {
          position.maxHeight = typeof position.maxHeight === "number" ? Math.round(position.maxHeight) : null;
          if (data.maxHeight !== position.maxHeight) {
            data.maxHeight = position.maxHeight;
            changeSet.maxHeight = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.maxWidth)) {
          position.maxWidth = typeof position.maxWidth === "number" ? Math.round(position.maxWidth) : null;
          if (data.maxWidth !== position.maxWidth) {
            data.maxWidth = position.maxWidth;
            changeSet.maxWidth = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.minHeight)) {
          position.minHeight = typeof position.minHeight === "number" ? Math.round(position.minHeight) : null;
          if (data.minHeight !== position.minHeight) {
            data.minHeight = position.minHeight;
            changeSet.minHeight = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.minWidth)) {
          position.minWidth = typeof position.minWidth === "number" ? Math.round(position.minWidth) : null;
          if (data.minWidth !== position.minWidth) {
            data.minWidth = position.minWidth;
            changeSet.minWidth = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.rotateX)) {
          if (data.rotateX !== position.rotateX) {
            data.rotateX = transforms.rotateX = position.rotateX;
            changeSet.transform = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.rotateY)) {
          if (data.rotateY !== position.rotateY) {
            data.rotateY = transforms.rotateY = position.rotateY;
            changeSet.transform = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.rotateZ)) {
          if (data.rotateZ !== position.rotateZ) {
            data.rotateZ = transforms.rotateZ = position.rotateZ;
            changeSet.transform = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.scale)) {
          position.scale = typeof position.scale === "number" ? clamp(position.scale, 0, 1e3) : null;
          if (data.scale !== position.scale) {
            data.scale = transforms.scale = position.scale;
            changeSet.transform = true;
          }
        }
        if (typeof position.transformOrigin === "string" && TJSTransforms.transformOrigins.includes(position.transformOrigin) || position.transformOrigin === null) {
          if (data.transformOrigin !== position.transformOrigin) {
            data.transformOrigin = position.transformOrigin;
            changeSet.transformOrigin = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.translateX)) {
          if (data.translateX !== position.translateX) {
            data.translateX = transforms.translateX = position.translateX;
            changeSet.transform = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.translateY)) {
          if (data.translateY !== position.translateY) {
            data.translateY = transforms.translateY = position.translateY;
            changeSet.transform = true;
          }
        }
        if (NumberGuard.isFiniteOrNull(position.translateZ)) {
          if (data.translateZ !== position.translateZ) {
            data.translateZ = transforms.translateZ = position.translateZ;
            changeSet.transform = true;
          }
        }
        if (NumberGuard.isFinite(position.zIndex)) {
          position.zIndex = Math.round(position.zIndex);
          if (data.zIndex !== position.zIndex) {
            data.zIndex = position.zIndex;
            changeSet.zIndex = true;
          }
        }
        const widthIsObservable = position.width === "auto" || position.width === "inherit";
        if (NumberGuard.isFiniteOrNull(position.width) || widthIsObservable) {
          position.width = typeof position.width === "number" ? Math.round(position.width) : position.width;
          if (data.width !== position.width) {
            data.width = position.width;
            changeSet.width = true;
          }
        }
        const heightIsObservable = position.height === "auto" || position.height === "inherit";
        if (NumberGuard.isFiniteOrNull(position.height) || heightIsObservable) {
          position.height = typeof position.height === "number" ? Math.round(position.height) : position.height;
          if (data.height !== position.height) {
            data.height = position.height;
            changeSet.height = true;
          }
        }
        const resizeObservable = widthIsObservable || heightIsObservable;
        if (this.#resizeObservable !== resizeObservable) {
          this.#resizeObservable = resizeObservable;
          this.#styleCache.stores.resizeObservable.set(resizeObservable);
        }
        if (el) {
          const defaultData = this.#state.getDefault();
          if (!isObject(defaultData)) {
            this.#state.save({ name: "#defaultData", ...Object.assign({}, data) });
          }
          if (immediateElementUpdate) {
            UpdateElementManager.immediate(el, this.#updateElementData);
            this.#updateElementPromise = Promise.resolve(globalThis.performance.now());
          } else if (!this.#updateElementData.queued) {
            this.#updateElementPromise = UpdateElementManager.add(el, this.#updateElementData);
          }
        } else {
          UpdateElementManager.updateSubscribers(this.#updateElementData);
        }
        return this;
      }
      /**
       * @param handler - Callback function that is invoked on update / changes. Receives a readonly copy of the
       *        TJSPositionData.
       *
       * @returns Unsubscribe function.
       */
      subscribe(handler) {
        const currentIdx = this.#subscribers.findIndex((entry) => entry === handler);
        if (currentIdx === -1) {
          this.#subscribers.push(handler);
          handler(Object.assign({}, this.#data));
        }
        return () => {
          const existingIdx = this.#subscribers.findIndex((entry) => entry === handler);
          if (existingIdx !== -1) {
            this.#subscribers.splice(existingIdx, 1);
          }
        };
      }
      /**
       * Provides the {@link Writable} store `update` method. Receive and return a {@link TJSPositionData} instance to
       * update the position state. You may manipulate numeric properties by providing relative adjustments described in
       * {@link TJSPositionDataRelative}.
       *
       * @param updater -
       */
      update(updater) {
        const result = updater(this.get());
        if (!isObject(result)) {
          throw new TypeError(`'result' of 'updater' is not an object.`);
        }
        this.set(result);
      }
      // Internal Implementation ----------------------------------------------------------------------------------------
      /**
       * Temporary data storage for `TJSPosition.#updatePosition`.
       */
      static #updateDataCopy = Object.seal(new TJSPositionData());
      /**
       * Temporary data storage for `TJSPosition.#updatePosition`.
       */
      static #validationData = Object.seal({
        position: void 0,
        parent: void 0,
        el: void 0,
        computed: void 0,
        transforms: void 0,
        height: void 0,
        width: void 0,
        marginLeft: void 0,
        marginTop: void 0,
        maxHeight: void 0,
        maxWidth: void 0,
        minHeight: void 0,
        minWidth: void 0,
        rest: void 0
      });
      /**
       * @param data -
       *
       * @param parent -
       *
       * @param el -
       *
       * @param styleCache -
       *
       * @returns Updated position data or null if validation fails.
       */
      #updatePosition({
        // Directly supported parameters
        left,
        top,
        maxWidth,
        maxHeight,
        minWidth,
        minHeight,
        width: width2,
        height,
        rotateX,
        rotateY,
        rotateZ,
        scale: scale2,
        transformOrigin,
        translateX,
        translateY,
        translateZ,
        zIndex,
        // Aliased parameters
        rotation: rotation2,
        ...rest
      }, parent, el, styleCache) {
        let currentPosition = TJSPositionDataUtil.copyData(this.#data, _a.#updateDataCopy);
        if (width2 !== void 0 || el.style.width === "") {
          const widthValid = width2 === null || Number.isFinite(width2);
          if (width2 === "auto" || currentPosition.width === "auto" && !widthValid) {
            currentPosition.width = "auto";
            width2 = styleCache.offsetWidth;
          } else if (width2 === "inherit" || currentPosition.width === "inherit" && !widthValid) {
            currentPosition.width = "inherit";
            width2 = styleCache.offsetWidth;
          } else {
            const newWidth = NumberGuard.isFinite(width2) ? width2 : currentPosition.width;
            currentPosition.width = width2 = NumberGuard.isFinite(newWidth) ? Math.round(newWidth) : styleCache.offsetWidth;
          }
        } else {
          width2 = Number.isFinite(currentPosition.width) ? currentPosition.width : styleCache.offsetWidth;
        }
        if (height !== void 0 || el.style.height === "") {
          const heightValid = height === null || Number.isFinite(height);
          if (height === "auto" || currentPosition.height === "auto" && !heightValid) {
            currentPosition.height = "auto";
            height = styleCache.offsetHeight;
          } else if (height === "inherit" || currentPosition.height === "inherit" && !heightValid) {
            currentPosition.height = "inherit";
            height = styleCache.offsetHeight;
          } else {
            const newHeight = NumberGuard.isFinite(height) ? height : currentPosition.height;
            currentPosition.height = height = NumberGuard.isFinite(newHeight) ? Math.round(newHeight) : styleCache.offsetHeight;
          }
        } else {
          height = Number.isFinite(currentPosition.height) ? currentPosition.height : styleCache.offsetHeight;
        }
        if (NumberGuard.isFinite(left)) {
          currentPosition.left = left;
        } else if (!Number.isFinite(currentPosition.left)) {
          currentPosition.left = typeof this.#options?.initial?.getLeft === "function" ? this.#options.initial.getLeft(width2) : 0;
        }
        if (Number.isFinite(top)) {
          currentPosition.top = top;
        } else if (!Number.isFinite(currentPosition.top)) {
          currentPosition.top = typeof this.#options?.initial?.getTop === "function" ? this.#options.initial.getTop(height) : 0;
        }
        if (NumberGuard.isFiniteOrNull(maxHeight)) {
          currentPosition.maxHeight = NumberGuard.isFinite(maxHeight) ? Math.round(maxHeight) : null;
        }
        if (NumberGuard.isFiniteOrNull(maxWidth)) {
          currentPosition.maxWidth = NumberGuard.isFinite(maxWidth) ? Math.round(maxWidth) : null;
        }
        if (NumberGuard.isFiniteOrNull(minHeight)) {
          currentPosition.minHeight = NumberGuard.isFinite(minHeight) ? Math.round(minHeight) : null;
        }
        if (NumberGuard.isFiniteOrNull(minWidth)) {
          currentPosition.minWidth = NumberGuard.isFinite(minWidth) ? Math.round(minWidth) : null;
        }
        if (NumberGuard.isFiniteOrNull(rotateX)) {
          currentPosition.rotateX = rotateX;
        }
        if (NumberGuard.isFiniteOrNull(rotateY)) {
          currentPosition.rotateY = rotateY;
        }
        if (rotateZ !== currentPosition.rotateZ && NumberGuard.isFiniteOrNull(rotateZ)) {
          currentPosition.rotateZ = rotateZ;
        } else if (rotation2 !== currentPosition.rotateZ && NumberGuard.isFiniteOrNull(rotation2)) {
          currentPosition.rotateZ = rotation2;
        }
        if (NumberGuard.isFiniteOrNull(translateX)) {
          currentPosition.translateX = translateX;
        }
        if (NumberGuard.isFiniteOrNull(translateY)) {
          currentPosition.translateY = translateY;
        }
        if (NumberGuard.isFiniteOrNull(translateZ)) {
          currentPosition.translateZ = translateZ;
        }
        if (NumberGuard.isFiniteOrNull(scale2)) {
          currentPosition.scale = typeof scale2 === "number" ? clamp(scale2, 0, 1e3) : null;
        }
        if (typeof transformOrigin === "string" || transformOrigin === null) {
          currentPosition.transformOrigin = TJSTransforms.transformOrigins.includes(transformOrigin) ? transformOrigin : null;
        }
        if (NumberGuard.isFiniteOrNull(zIndex)) {
          currentPosition.zIndex = typeof zIndex === "number" ? Math.round(zIndex) : zIndex;
        }
        const validatorData = this.#validatorData;
        if (this.#validators.enabled && validatorData.length) {
          const validationData = _a.#validationData;
          validationData.parent = parent;
          validationData.el = el;
          validationData.computed = styleCache.computed;
          validationData.transforms = this.#transforms;
          validationData.height = height;
          validationData.width = width2;
          validationData.marginLeft = styleCache.marginLeft;
          validationData.marginTop = styleCache.marginTop;
          validationData.maxHeight = styleCache.maxHeight ?? currentPosition.maxHeight;
          validationData.maxWidth = styleCache.maxWidth ?? currentPosition.maxWidth;
          const isMinimized = parent?.reactive?.minimized ?? false;
          validationData.minHeight = isMinimized ? currentPosition.minHeight ?? 0 : styleCache.minHeight || (currentPosition.minHeight ?? 0);
          validationData.minWidth = isMinimized ? currentPosition.minWidth ?? 0 : styleCache.minWidth || (currentPosition.minWidth ?? 0);
          for (let cntr = 0; cntr < validatorData.length; cntr++) {
            validationData.position = currentPosition;
            validationData.rest = rest;
            currentPosition = validatorData[cntr].validate(validationData);
            if (currentPosition === null) {
              return null;
            }
          }
        }
        return currentPosition;
      }
    }
    _a = TJSPosition;
    if (typeof window !== "undefined")
      (window.__svelte || (window.__svelte = { v: /* @__PURE__ */ new Set() })).v.add(PUBLIC_VERSION);
    const cssVariables = new TJSStyleManager({ docKey: "#__trl-root-styles", version: 1 });
    class ResizeObserverManager {
      /** @type {Map<HTMLElement, import('./types-local').ResizeObserverSubscriber[]>} */
      #elMap = /* @__PURE__ */ new Map();
      /** @type {ResizeObserver} */
      #resizeObserver;
      /**
       * Defines the various shape / update type of the given target.
       *
       * @type {{ [key: string]: number }}
       */
      static #updateTypes = Object.freeze({
        none: 0,
        attribute: 1,
        function: 2,
        resizeObserved: 3,
        setContentBounds: 4,
        setDimension: 5,
        storeObject: 6,
        storesObject: 7
      });
      constructor() {
        this.#resizeObserver = new ResizeObserver((entries) => {
          for (const entry of entries) {
            const subscribers = this.#elMap.get(entry?.target);
            if (Array.isArray(subscribers)) {
              const contentWidth = entry.contentRect.width;
              const contentHeight = entry.contentRect.height;
              for (const subscriber of subscribers) {
                ResizeObserverManager.#updateSubscriber(subscriber, contentWidth, contentHeight);
              }
            }
          }
        });
      }
      /**
       * Add an {@link HTMLElement} and {@link ResizeObserverData.ResizeTarget} instance for monitoring. Create cached
       * style attributes for the given element include border & padding dimensions for offset width / height calculations.
       *
       * @param {HTMLElement}    el - The element to observe.
       *
       * @param {import('./types').ResizeObserverData.ResizeTarget} target - A target that contains one of several
       *        mechanisms for updating resize data.
       */
      add(el, target) {
        if (!CrossWindow.isHTMLElement(el)) {
          throw new TypeError(`ResizeObserverManager.add error: 'el' is not a HTMLElement.`);
        }
        if (this.#hasTarget(el, target)) {
          return;
        }
        const updateType = ResizeObserverManager.#getUpdateType(target);
        if (updateType === 0) {
          throw new Error(`ResizeObserverManager.add error: 'target' is not a valid ResizeObserverManager target.`);
        }
        const computed = globalThis.getComputedStyle(el);
        const borderBottom = StyleParse.pixels(el.style.borderBottom) ?? StyleParse.pixels(computed.borderBottom) ?? 0;
        const borderLeft = StyleParse.pixels(el.style.borderLeft) ?? StyleParse.pixels(computed.borderLeft) ?? 0;
        const borderRight = StyleParse.pixels(el.style.borderRight) ?? StyleParse.pixels(computed.borderRight) ?? 0;
        const borderTop = StyleParse.pixels(el.style.borderTop) ?? StyleParse.pixels(computed.borderTop) ?? 0;
        const paddingBottom = StyleParse.pixels(el.style.paddingBottom) ?? StyleParse.pixels(computed.paddingBottom) ?? 0;
        const paddingLeft = StyleParse.pixels(el.style.paddingLeft) ?? StyleParse.pixels(computed.paddingLeft) ?? 0;
        const paddingRight = StyleParse.pixels(el.style.paddingRight) ?? StyleParse.pixels(computed.paddingRight) ?? 0;
        const paddingTop = StyleParse.pixels(el.style.paddingTop) ?? StyleParse.pixels(computed.paddingTop) ?? 0;
        const data = {
          updateType,
          target,
          // Stores most recent contentRect.width and contentRect.height values from ResizeObserver.
          contentWidth: 0,
          contentHeight: 0,
          // Convenience data for total border & padding for offset width & height calculations.
          styles: {
            additionalWidth: borderLeft + borderRight + paddingLeft + paddingRight,
            additionalHeight: borderTop + borderBottom + paddingTop + paddingBottom
          }
        };
        if (this.#elMap.has(el)) {
          const subscribers = this.#elMap.get(el);
          subscribers.push(data);
        } else {
          this.#elMap.set(el, [data]);
        }
        this.#resizeObserver.observe(el);
      }
      /**
       * Clears and unobserves all currently tracked elements and managed targets.
       */
      clear() {
        for (const el of this.#elMap.keys()) {
          this.#resizeObserver.unobserve(el);
        }
        this.#elMap.clear();
      }
      /**
       * Removes all {@link ResizeObserverData.ResizeTarget} instances for the given element from monitoring when just an
       * element is provided otherwise removes a specific target from the monitoring map. If no more targets remain then
       * the element is removed from monitoring.
       *
       * @param {HTMLElement} el - Element to remove from monitoring.
       *
       * @param {import('./types').ResizeObserverData.ResizeTarget} [target] - A specific target to remove from monitoring.
       */
      remove(el, target = void 0) {
        const subscribers = this.#elMap.get(el);
        if (Array.isArray(subscribers)) {
          if (target !== void 0) {
            const index = subscribers.findIndex((entry) => entry.target === target);
            if (index >= 0) {
              subscribers.splice(index, 1);
            }
          } else {
            subscribers.length = 0;
          }
          if (subscribers.length === 0) {
            this.#elMap.delete(el);
            this.#resizeObserver.unobserve(el);
          }
        }
      }
      /**
       * Provides a function that when invoked with an element updates the cached styles for each subscriber of the
       * element.
       *
       * The style attributes cached to calculate offset height / width include border & padding dimensions. You only need
       * to update the cache if you change border or padding attributes of the element.
       *
       * @param {HTMLElement} el - A HTML element.
       */
      updateCache(el) {
        const subscribers = this.#elMap.get(el);
        if (Array.isArray(subscribers)) {
          const computed = globalThis.getComputedStyle(el);
          const borderBottom = StyleParse.pixels(el.style.borderBottom) ?? StyleParse.pixels(computed.borderBottom) ?? 0;
          const borderLeft = StyleParse.pixels(el.style.borderLeft) ?? StyleParse.pixels(computed.borderLeft) ?? 0;
          const borderRight = StyleParse.pixels(el.style.borderRight) ?? StyleParse.pixels(computed.borderRight) ?? 0;
          const borderTop = StyleParse.pixels(el.style.borderTop) ?? StyleParse.pixels(computed.borderTop) ?? 0;
          const paddingBottom = StyleParse.pixels(el.style.paddingBottom) ?? StyleParse.pixels(computed.paddingBottom) ?? 0;
          const paddingLeft = StyleParse.pixels(el.style.paddingLeft) ?? StyleParse.pixels(computed.paddingLeft) ?? 0;
          const paddingRight = StyleParse.pixels(el.style.paddingRight) ?? StyleParse.pixels(computed.paddingRight) ?? 0;
          const paddingTop = StyleParse.pixels(el.style.paddingTop) ?? StyleParse.pixels(computed.paddingTop) ?? 0;
          const additionalWidth = borderLeft + borderRight + paddingLeft + paddingRight;
          const additionalHeight = borderTop + borderBottom + paddingTop + paddingBottom;
          for (const subscriber of subscribers) {
            subscriber.styles.additionalWidth = additionalWidth;
            subscriber.styles.additionalHeight = additionalHeight;
            ResizeObserverManager.#updateSubscriber(subscriber, subscriber.contentWidth, subscriber.contentHeight);
          }
        }
      }
      // Internal implementation ----------------------------------------------------------------------------------------
      /**
       * Determines the shape of the target instance regarding valid update mechanisms to set width & height changes.
       *
       * @param {import('./types').ResizeObserverData.ResizeTarget}  target - The target instance.
       *
       * @returns {number} Update type value.
       */
      static #getUpdateType(target) {
        if (typeof target?.resizeObserved === "function") {
          return this.#updateTypes.resizeObserved;
        }
        if (typeof target?.setDimension === "function") {
          return this.#updateTypes.setDimension;
        }
        if (typeof target?.setContentBounds === "function") {
          return this.#updateTypes.setContentBounds;
        }
        const targetType = typeof target;
        if (targetType !== null && (targetType === "object" || targetType === "function")) {
          if (isWritableStore(target.resizeObserved)) {
            return this.#updateTypes.storeObject;
          }
          const stores = target?.stores;
          if (isObject(stores) || typeof stores === "function") {
            if (isWritableStore(stores.resizeObserved)) {
              return this.#updateTypes.storesObject;
            }
          }
        }
        if (targetType !== null && targetType === "object") {
          return this.#updateTypes.attribute;
        }
        if (targetType === "function") {
          return this.#updateTypes.function;
        }
        return this.#updateTypes.none;
      }
      /**
       * Determines if a given element and target is already being observed.
       *
       * @param {HTMLElement} el - A HTMLElement.
       *
       * @param {import('./types').ResizeObserverData.ResizeTarget} [target] - A specific target to find.
       *
       * @returns {boolean} Whether the target is already being tracked for the given element.
       */
      #hasTarget(el, target) {
        if (target === void 0 || target === null) {
          return false;
        }
        const subscribers = this.#elMap.get(el);
        if (Array.isArray(subscribers)) {
          return subscribers.findIndex((entry) => entry.target === target) >= 0;
        }
        return false;
      }
      /**
       * Updates a subscriber target with given content width & height values. Offset width & height is calculated from
       * the content values + cached styles.
       *
       * @param {import('./types-local').ResizeObserverSubscriber} subscriber - Internal data about subscriber.
       *
       * @param {number|undefined}  contentWidth - ResizeObserver `contentRect.width` value or undefined.
       *
       * @param {number|undefined}  contentHeight - ResizeObserver `contentRect.height` value or undefined.
       */
      static #updateSubscriber(subscriber, contentWidth, contentHeight) {
        const styles = subscriber.styles;
        subscriber.contentWidth = contentWidth;
        subscriber.contentHeight = contentHeight;
        const offsetWidth = Number.isFinite(contentWidth) ? contentWidth + styles.additionalWidth : void 0;
        const offsetHeight = Number.isFinite(contentHeight) ? contentHeight + styles.additionalHeight : void 0;
        const target = subscriber.target;
        switch (subscriber.updateType) {
          case this.#updateTypes.attribute:
            target.contentWidth = contentWidth;
            target.contentHeight = contentHeight;
            target.offsetWidth = offsetWidth;
            target.offsetHeight = offsetHeight;
            break;
          case this.#updateTypes.function:
            target?.(offsetWidth, offsetHeight, contentWidth, contentHeight);
            break;
          case this.#updateTypes.resizeObserved:
            target.resizeObserved?.(offsetWidth, offsetHeight, contentWidth, contentHeight);
            break;
          case this.#updateTypes.setContentBounds:
            target.setContentBounds?.(contentWidth, contentHeight);
            break;
          case this.#updateTypes.setDimension:
            target.setDimension?.(offsetWidth, offsetHeight);
            break;
          case this.#updateTypes.storeObject:
            target.resizeObserved.update((object) => {
              object.contentHeight = contentHeight;
              object.contentWidth = contentWidth;
              object.offsetHeight = offsetHeight;
              object.offsetWidth = offsetWidth;
              return object;
            });
            break;
          case this.#updateTypes.storesObject:
            target.stores.resizeObserved.update((object) => {
              object.contentHeight = contentHeight;
              object.contentWidth = contentWidth;
              object.offsetHeight = offsetHeight;
              object.offsetWidth = offsetWidth;
              return object;
            });
            break;
        }
      }
    }
    const resizeObserverActionManager = new ResizeObserverManager();
    function resizeObserver(node, target) {
      resizeObserverActionManager.add(node, target);
      return {
        /**
         * @param {import('#runtime/util/dom/observer').ResizeObserverData.ResizeTarget} newTarget - A
         *        {@link ResizeObserverManager} target to update with observed width & height changes.
         */
        update: (newTarget) => {
          resizeObserverActionManager.remove(node, target);
          target = newTarget;
          resizeObserverActionManager.add(node, target);
        },
        destroy: () => {
          resizeObserverActionManager.remove(node, target);
        }
      };
    }
    resizeObserver.updateCache = function(el) {
      resizeObserverActionManager.updateCache(el);
    };
    function applyStyles(node, properties) {
      function setProperties() {
        if (!isObject(properties)) {
          return;
        }
        for (const prop of Object.keys(properties)) {
          node.style.setProperty(`${prop}`, properties[prop]);
        }
      }
      setProperties();
      return {
        /**
         * @param {{ [key: string]: string | null }}  newProperties - Key / value object of properties to set.
         */
        update: (newProperties) => {
          properties = newProperties;
          setProperties();
        }
      };
    }
    function dynamicAction(node, { action, data } = {}) {
      let actionResult;
      if (typeof action === "function") {
        actionResult = action(node, data);
      }
      return {
        /**
         * @param {import('./types').DynamicActionOptions} newOptions - Defines the new action to dynamically mount.
         */
        update: (newOptions) => {
          if (!isObject(newOptions)) {
            actionResult?.destroy?.();
            action = void 0;
            data = void 0;
            return;
          }
          const { action: newAction, data: newData } = newOptions;
          if (typeof newAction !== "function") {
            console.warn(`dynamicAction.update warning: Aborting as 'action' is not a function.`);
            return;
          }
          const hasNewData = newData !== data;
          if (hasNewData) {
            data = newData;
          }
          if (newAction !== action) {
            actionResult?.destroy?.();
            action = newAction;
            actionResult = action(node, data);
          } else if (hasNewData) {
            actionResult?.update?.(data);
          }
        },
        destroy: () => {
          actionResult?.destroy?.();
          action = void 0;
          data = void 0;
          actionResult = void 0;
        }
      };
    }
    class TJSDefaultTransition {
      static #options = {};
      static #default = () => void 0;
      /**
       * @returns {() => undefined} Default empty transition.
       */
      static get default() {
        return this.#default;
      }
      /**
       * @returns {{}} Default empty options.
       */
      static get options() {
        return this.#options;
      }
    }
    class AppShellContextInternal {
      /** @type {import('./types').AppShell.Context.Internal.stores} */
      #stores;
      constructor() {
        this.#stores = {
          elementContent: writable(void 0),
          elementRoot: writable(void 0)
        };
        Object.freeze(this.#stores);
        Object.seal(this);
      }
      /**
       * @returns {import('./types').AppShell.Context.Internal.stores} The internal context stores for `elementContent` /
       *          `elementRoot`
       */
      get stores() {
        return this.#stores;
      }
    }
    function localize(stringId, data) {
      const result = !isObject(data) ? globalThis.game.i18n.localize(stringId) : globalThis.game.i18n.format(stringId, data);
      return result !== void 0 ? result : "";
    }
    function create_if_block$a(ctx) {
      let span;
      let t;
      return {
        c() {
          span = element("span");
          t = text$1(
            /*label*/
            ctx[3]
          );
          attr(span, "class", "svelte-seq-acci");
          toggle_class(
            span,
            "has-icon",
            /*icon*/
            ctx[4] !== void 0
          );
        },
        m(target, anchor) {
          insert(target, span, anchor);
          append(span, t);
        },
        p(ctx2, dirty) {
          if (dirty & /*label*/
          8) set_data(
            t,
            /*label*/
            ctx2[3]
          );
          if (dirty & /*icon*/
          16) {
            toggle_class(
              span,
              "has-icon",
              /*icon*/
              ctx2[4] !== void 0
            );
          }
        },
        d(detaching) {
          if (detaching) {
            detach(span);
          }
        }
      };
    }
    function create_fragment$p(ctx) {
      let a;
      let html_tag;
      let html_anchor;
      let a_class_value;
      let applyStyles_action;
      let mounted;
      let dispose;
      let if_block = (
        /*label*/
        ctx[3] && create_if_block$a(ctx)
      );
      return {
        c() {
          a = element("a");
          html_tag = new HtmlTag(false);
          html_anchor = empty();
          if (if_block) if_block.c();
          html_tag.a = html_anchor;
          attr(a, "class", a_class_value = "header-button " + /*button*/
          ctx[0].class + " svelte-seq-acci");
          attr(
            a,
            "aria-label",
            /*label*/
            ctx[3]
          );
          attr(a, "tabindex", "0");
          attr(a, "role", "button");
          toggle_class(
            a,
            "keep-minimized",
            /*keepMinimized*/
            ctx[2]
          );
        },
        m(target, anchor) {
          insert(target, a, anchor);
          html_tag.m(
            /*icon*/
            ctx[4],
            a
          );
          append(a, html_anchor);
          if (if_block) if_block.m(a, null);
          if (!mounted) {
            dispose = [
              listen(a, "click", stop_propagation(prevent_default(
                /*onClick*/
                ctx[5]
              ))),
              listen(a, "contextmenu", stop_propagation(prevent_default(
                /*onContextMenu*/
                ctx[6]
              ))),
              listen(
                a,
                "keydown",
                /*onKeydown*/
                ctx[7]
              ),
              listen(
                a,
                "keyup",
                /*onKeyup*/
                ctx[8]
              ),
              action_destroyer(applyStyles_action = applyStyles.call(
                null,
                a,
                /*styles*/
                ctx[1]
              ))
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*icon*/
          16) html_tag.p(
            /*icon*/
            ctx2[4]
          );
          if (
            /*label*/
            ctx2[3]
          ) {
            if (if_block) {
              if_block.p(ctx2, dirty);
            } else {
              if_block = create_if_block$a(ctx2);
              if_block.c();
              if_block.m(a, null);
            }
          } else if (if_block) {
            if_block.d(1);
            if_block = null;
          }
          if (dirty & /*button*/
          1 && a_class_value !== (a_class_value = "header-button " + /*button*/
          ctx2[0].class + " svelte-seq-acci")) {
            attr(a, "class", a_class_value);
          }
          if (dirty & /*label*/
          8) {
            attr(
              a,
              "aria-label",
              /*label*/
              ctx2[3]
            );
          }
          if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
          2) applyStyles_action.update.call(
            null,
            /*styles*/
            ctx2[1]
          );
          if (dirty & /*button, keepMinimized*/
          5) {
            toggle_class(
              a,
              "keep-minimized",
              /*keepMinimized*/
              ctx2[2]
            );
          }
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(a);
          }
          if (if_block) if_block.d();
          mounted = false;
          run_all(dispose);
        }
      };
    }
    const s_REGEX_HTML = /^\s*<.*>$/;
    function instance$p($$self, $$props, $$invalidate) {
      let title;
      let icon;
      let label;
      let keepMinimized;
      let keyCode;
      let styles;
      let { button = void 0 } = $$props;
      function onClick(event) {
        const invoke = button?.onPress ?? button?.onclick;
        if (typeof invoke === "function") {
          invoke({ button, event });
          $$invalidate(0, button);
        }
      }
      function onContextMenu(event) {
        if (button?.onContextMenu === "function") {
          button.onContextMenu({ button, event });
          $$invalidate(0, button);
        }
      }
      function onKeydown(event) {
        if (event.code === keyCode) {
          event.preventDefault();
          event.stopPropagation();
        }
      }
      function onKeyup(event) {
        if (event.code === keyCode) {
          const invoke = button.onPress ?? button.onclick;
          if (typeof invoke === "function") {
            invoke({ button, event });
            $$invalidate(0, button);
          }
          event.preventDefault();
          event.stopPropagation();
        }
      }
      $$self.$$set = ($$props2) => {
        if ("button" in $$props2) $$invalidate(0, button = $$props2.button);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*button*/
        1) {
          $$invalidate(9, title = isObject(button) && typeof button.title === "string" ? localize(button.title) : "");
        }
        if ($$self.$$.dirty & /*button, title*/
        513) {
          $$invalidate(4, icon = isObject(button) && typeof button.icon !== "string" ? void 0 : s_REGEX_HTML.test(button.icon) ? button.icon : `<i class="${button.icon}" title="${title}"></i>`);
        }
        if ($$self.$$.dirty & /*button*/
        1) {
          $$invalidate(3, label = isObject(button) && typeof button.label === "string" ? localize(button.label) : void 0);
        }
        if ($$self.$$.dirty & /*button*/
        1) {
          $$invalidate(2, keepMinimized = isObject(button) && typeof button.keepMinimized === "boolean" ? button.keepMinimized : false);
        }
        if ($$self.$$.dirty & /*button*/
        1) {
          keyCode = isObject(button) && typeof button.keyCode === "string" ? button.keyCode : "Enter";
        }
        if ($$self.$$.dirty & /*button*/
        1) {
          $$invalidate(1, styles = isObject(button) && isObject(button.styles) ? button.styles : void 0);
        }
      };
      return [
        button,
        styles,
        keepMinimized,
        label,
        icon,
        onClick,
        onContextMenu,
        onKeydown,
        onKeyup,
        title
      ];
    }
    class TJSHeaderButton extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$p, create_fragment$p, safe_not_equal, { button: 0 });
      }
      get button() {
        return this.$$.ctx[0];
      }
      set button(button) {
        this.$$set({ button });
        flush();
      }
    }
    function get_each_context$7(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[31] = list[i];
      return child_ctx;
    }
    function get_each_context_1$2(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[31] = list[i];
      return child_ctx;
    }
    function create_if_block$9(ctx) {
      let img;
      let img_src_value;
      return {
        c() {
          img = element("img");
          attr(img, "class", "tjs-app-icon keep-minimized svelte-seq-5lh4qo");
          if (!src_url_equal(img.src, img_src_value = /*$storeHeaderIcon*/
          ctx[6])) attr(img, "src", img_src_value);
          attr(img, "alt", "icon");
        },
        m(target, anchor) {
          insert(target, img, anchor);
        },
        p(ctx2, dirty) {
          if (dirty[0] & /*$storeHeaderIcon*/
          64 && !src_url_equal(img.src, img_src_value = /*$storeHeaderIcon*/
          ctx2[6])) {
            attr(img, "src", img_src_value);
          }
        },
        d(detaching) {
          if (detaching) {
            detach(img);
          }
        }
      };
    }
    function create_each_block_1$2(ctx) {
      let switch_instance;
      let switch_instance_anchor;
      let current;
      const switch_instance_spread_levels = [
        /*button*/
        ctx[31].props
      ];
      var switch_value = (
        /*button*/
        ctx[31].class
      );
      function switch_props(ctx2, dirty) {
        let switch_instance_props = {};
        for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
          switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
        }
        if (dirty !== void 0 && dirty[0] & /*buttonsLeft*/
        2) {
          switch_instance_props = assign(switch_instance_props, get_spread_update(switch_instance_spread_levels, [get_spread_object(
            /*button*/
            ctx2[31].props
          )]));
        }
        return { props: switch_instance_props };
      }
      if (switch_value) {
        switch_instance = construct_svelte_component(switch_value, switch_props(ctx));
      }
      return {
        c() {
          if (switch_instance) create_component(switch_instance.$$.fragment);
          switch_instance_anchor = empty();
        },
        m(target, anchor) {
          if (switch_instance) mount_component(switch_instance, target, anchor);
          insert(target, switch_instance_anchor, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          if (dirty[0] & /*buttonsLeft*/
          2 && switch_value !== (switch_value = /*button*/
          ctx2[31].class)) {
            if (switch_instance) {
              group_outros();
              const old_component = switch_instance;
              transition_out(old_component.$$.fragment, 1, 0, () => {
                destroy_component(old_component, 1);
              });
              check_outros();
            }
            if (switch_value) {
              switch_instance = construct_svelte_component(switch_value, switch_props(ctx2, dirty));
              create_component(switch_instance.$$.fragment);
              transition_in(switch_instance.$$.fragment, 1);
              mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
            } else {
              switch_instance = null;
            }
          } else if (switch_value) {
            const switch_instance_changes = dirty[0] & /*buttonsLeft*/
            2 ? get_spread_update(switch_instance_spread_levels, [get_spread_object(
              /*button*/
              ctx2[31].props
            )]) : {};
            switch_instance.$set(switch_instance_changes);
          }
        },
        i(local) {
          if (current) return;
          if (switch_instance) transition_in(switch_instance.$$.fragment, local);
          current = true;
        },
        o(local) {
          if (switch_instance) transition_out(switch_instance.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(switch_instance_anchor);
          }
          if (switch_instance) destroy_component(switch_instance, detaching);
        }
      };
    }
    function create_each_block$7(ctx) {
      let switch_instance;
      let switch_instance_anchor;
      let current;
      const switch_instance_spread_levels = [
        /*button*/
        ctx[31].props
      ];
      var switch_value = (
        /*button*/
        ctx[31].class
      );
      function switch_props(ctx2, dirty) {
        let switch_instance_props = {};
        for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
          switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
        }
        if (dirty !== void 0 && dirty[0] & /*buttonsRight*/
        4) {
          switch_instance_props = assign(switch_instance_props, get_spread_update(switch_instance_spread_levels, [get_spread_object(
            /*button*/
            ctx2[31].props
          )]));
        }
        return { props: switch_instance_props };
      }
      if (switch_value) {
        switch_instance = construct_svelte_component(switch_value, switch_props(ctx));
      }
      return {
        c() {
          if (switch_instance) create_component(switch_instance.$$.fragment);
          switch_instance_anchor = empty();
        },
        m(target, anchor) {
          if (switch_instance) mount_component(switch_instance, target, anchor);
          insert(target, switch_instance_anchor, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          if (dirty[0] & /*buttonsRight*/
          4 && switch_value !== (switch_value = /*button*/
          ctx2[31].class)) {
            if (switch_instance) {
              group_outros();
              const old_component = switch_instance;
              transition_out(old_component.$$.fragment, 1, 0, () => {
                destroy_component(old_component, 1);
              });
              check_outros();
            }
            if (switch_value) {
              switch_instance = construct_svelte_component(switch_value, switch_props(ctx2, dirty));
              create_component(switch_instance.$$.fragment);
              transition_in(switch_instance.$$.fragment, 1);
              mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
            } else {
              switch_instance = null;
            }
          } else if (switch_value) {
            const switch_instance_changes = dirty[0] & /*buttonsRight*/
            4 ? get_spread_update(switch_instance_spread_levels, [get_spread_object(
              /*button*/
              ctx2[31].props
            )]) : {};
            switch_instance.$set(switch_instance_changes);
          }
        },
        i(local) {
          if (current) return;
          if (switch_instance) transition_in(switch_instance.$$.fragment, local);
          current = true;
        },
        o(local) {
          if (switch_instance) transition_out(switch_instance.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(switch_instance_anchor);
          }
          if (switch_instance) destroy_component(switch_instance, detaching);
        }
      };
    }
    function create_key_block(ctx) {
      let header;
      let t0;
      let h4;
      let t1_value = localize(
        /*$storeTitle*/
        ctx[7]
      ) + "";
      let t1;
      let t2;
      let t3;
      let span;
      let t4;
      let draggable_action;
      let minimizable_action;
      let current;
      let mounted;
      let dispose;
      let if_block = typeof /*$storeHeaderIcon*/
      ctx[6] === "string" && create_if_block$9(ctx);
      let each_value_1 = ensure_array_like(
        /*buttonsLeft*/
        ctx[1]
      );
      let each_blocks_1 = [];
      for (let i = 0; i < each_value_1.length; i += 1) {
        each_blocks_1[i] = create_each_block_1$2(get_each_context_1$2(ctx, each_value_1, i));
      }
      const out = (i) => transition_out(each_blocks_1[i], 1, 1, () => {
        each_blocks_1[i] = null;
      });
      let each_value = ensure_array_like(
        /*buttonsRight*/
        ctx[2]
      );
      let each_blocks = [];
      for (let i = 0; i < each_value.length; i += 1) {
        each_blocks[i] = create_each_block$7(get_each_context$7(ctx, each_value, i));
      }
      const out_1 = (i) => transition_out(each_blocks[i], 1, 1, () => {
        each_blocks[i] = null;
      });
      return {
        c() {
          header = element("header");
          if (if_block) if_block.c();
          t0 = space();
          h4 = element("h4");
          t1 = text$1(t1_value);
          t2 = space();
          for (let i = 0; i < each_blocks_1.length; i += 1) {
            each_blocks_1[i].c();
          }
          t3 = space();
          span = element("span");
          t4 = space();
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
          attr(h4, "class", "window-title svelte-seq-5lh4qo");
          set_style(
            h4,
            "display",
            /*displayHeaderTitle*/
            ctx[4]
          );
          attr(span, "class", "tjs-window-header-spacer keep-minimized svelte-seq-5lh4qo");
          attr(header, "class", "window-header flexrow svelte-seq-5lh4qo");
        },
        m(target, anchor) {
          insert(target, header, anchor);
          if (if_block) if_block.m(header, null);
          append(header, t0);
          append(header, h4);
          append(h4, t1);
          append(header, t2);
          for (let i = 0; i < each_blocks_1.length; i += 1) {
            if (each_blocks_1[i]) {
              each_blocks_1[i].m(header, null);
            }
          }
          append(header, t3);
          append(header, span);
          append(header, t4);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(header, null);
            }
          }
          current = true;
          if (!mounted) {
            dispose = [
              listen(
                header,
                "pointerdown",
                /*onPointerdown*/
                ctx[19]
              ),
              action_destroyer(draggable_action = /*draggable*/
              ctx[0].call(
                null,
                header,
                /*dragOptions*/
                ctx[3]
              )),
              action_destroyer(minimizable_action = /*minimizable*/
              ctx[18].call(
                null,
                header,
                /*$storeMinimizable*/
                ctx[5]
              ))
            ];
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (typeof /*$storeHeaderIcon*/
          ctx2[6] === "string") {
            if (if_block) {
              if_block.p(ctx2, dirty);
            } else {
              if_block = create_if_block$9(ctx2);
              if_block.c();
              if_block.m(header, t0);
            }
          } else if (if_block) {
            if_block.d(1);
            if_block = null;
          }
          if ((!current || dirty[0] & /*$storeTitle*/
          128) && t1_value !== (t1_value = localize(
            /*$storeTitle*/
            ctx2[7]
          ) + "")) set_data(t1, t1_value);
          if (dirty[0] & /*displayHeaderTitle*/
          16) {
            set_style(
              h4,
              "display",
              /*displayHeaderTitle*/
              ctx2[4]
            );
          }
          if (dirty[0] & /*buttonsLeft*/
          2) {
            each_value_1 = ensure_array_like(
              /*buttonsLeft*/
              ctx2[1]
            );
            let i;
            for (i = 0; i < each_value_1.length; i += 1) {
              const child_ctx = get_each_context_1$2(ctx2, each_value_1, i);
              if (each_blocks_1[i]) {
                each_blocks_1[i].p(child_ctx, dirty);
                transition_in(each_blocks_1[i], 1);
              } else {
                each_blocks_1[i] = create_each_block_1$2(child_ctx);
                each_blocks_1[i].c();
                transition_in(each_blocks_1[i], 1);
                each_blocks_1[i].m(header, t3);
              }
            }
            group_outros();
            for (i = each_value_1.length; i < each_blocks_1.length; i += 1) {
              out(i);
            }
            check_outros();
          }
          if (dirty[0] & /*buttonsRight*/
          4) {
            each_value = ensure_array_like(
              /*buttonsRight*/
              ctx2[2]
            );
            let i;
            for (i = 0; i < each_value.length; i += 1) {
              const child_ctx = get_each_context$7(ctx2, each_value, i);
              if (each_blocks[i]) {
                each_blocks[i].p(child_ctx, dirty);
                transition_in(each_blocks[i], 1);
              } else {
                each_blocks[i] = create_each_block$7(child_ctx);
                each_blocks[i].c();
                transition_in(each_blocks[i], 1);
                each_blocks[i].m(header, null);
              }
            }
            group_outros();
            for (i = each_value.length; i < each_blocks.length; i += 1) {
              out_1(i);
            }
            check_outros();
          }
          if (draggable_action && is_function(draggable_action.update) && dirty[0] & /*dragOptions*/
          8) draggable_action.update.call(
            null,
            /*dragOptions*/
            ctx2[3]
          );
          if (minimizable_action && is_function(minimizable_action.update) && dirty[0] & /*$storeMinimizable*/
          32) minimizable_action.update.call(
            null,
            /*$storeMinimizable*/
            ctx2[5]
          );
        },
        i(local) {
          if (current) return;
          for (let i = 0; i < each_value_1.length; i += 1) {
            transition_in(each_blocks_1[i]);
          }
          for (let i = 0; i < each_value.length; i += 1) {
            transition_in(each_blocks[i]);
          }
          current = true;
        },
        o(local) {
          each_blocks_1 = each_blocks_1.filter(Boolean);
          for (let i = 0; i < each_blocks_1.length; i += 1) {
            transition_out(each_blocks_1[i]);
          }
          each_blocks = each_blocks.filter(Boolean);
          for (let i = 0; i < each_blocks.length; i += 1) {
            transition_out(each_blocks[i]);
          }
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(header);
          }
          if (if_block) if_block.d();
          destroy_each(each_blocks_1, detaching);
          destroy_each(each_blocks, detaching);
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function create_fragment$o(ctx) {
      let previous_key = (
        /*draggable*/
        ctx[0]
      );
      let key_block_anchor;
      let current;
      let key_block = create_key_block(ctx);
      return {
        c() {
          key_block.c();
          key_block_anchor = empty();
        },
        m(target, anchor) {
          key_block.m(target, anchor);
          insert(target, key_block_anchor, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          if (dirty[0] & /*draggable*/
          1 && safe_not_equal(previous_key, previous_key = /*draggable*/
          ctx2[0])) {
            group_outros();
            transition_out(key_block, 1, 1, noop);
            check_outros();
            key_block = create_key_block(ctx2);
            key_block.c();
            transition_in(key_block, 1);
            key_block.m(key_block_anchor.parentNode, key_block_anchor);
          } else {
            key_block.p(ctx2, dirty);
          }
        },
        i(local) {
          if (current) return;
          transition_in(key_block);
          current = true;
        },
        o(local) {
          transition_out(key_block);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(key_block_anchor);
          }
          key_block.d(detaching);
        }
      };
    }
    function instance$o($$self, $$props, $$invalidate) {
      let $focusKeep;
      let $focusAuto;
      let $elementRoot;
      let $storeHeaderButtons;
      let $storeMinimized;
      let $storeHeaderNoTitleMinimized;
      let $storeDraggable;
      let $storeMinimizable;
      let $storeHeaderIcon;
      let $storeTitle;
      let { draggable: draggable$1 = void 0 } = $$props;
      let { draggableOptions = void 0 } = $$props;
      const application = getContext("#external")?.application;
      const { focusAuto, focusKeep } = application.reactive.storeAppOptions;
      component_subscribe($$self, focusAuto, (value) => $$invalidate(26, $focusAuto = value));
      component_subscribe($$self, focusKeep, (value) => $$invalidate(25, $focusKeep = value));
      const { elementRoot } = getContext("#internal").stores;
      component_subscribe($$self, elementRoot, (value) => $$invalidate(27, $elementRoot = value));
      const storeTitle = application.reactive.storeAppOptions.title;
      component_subscribe($$self, storeTitle, (value) => $$invalidate(7, $storeTitle = value));
      const storeDraggable = application.reactive.storeAppOptions.draggable;
      component_subscribe($$self, storeDraggable, (value) => $$invalidate(24, $storeDraggable = value));
      const storeDragging = application.reactive.storeUIState.dragging;
      const storeHeaderButtons = application.reactive.storeUIState.headerButtons;
      component_subscribe($$self, storeHeaderButtons, (value) => $$invalidate(21, $storeHeaderButtons = value));
      const storeHeaderIcon = application.reactive.storeAppOptions.headerIcon;
      component_subscribe($$self, storeHeaderIcon, (value) => $$invalidate(6, $storeHeaderIcon = value));
      const storeHeaderNoTitleMinimized = application.reactive.storeAppOptions.headerNoTitleMinimized;
      component_subscribe($$self, storeHeaderNoTitleMinimized, (value) => $$invalidate(23, $storeHeaderNoTitleMinimized = value));
      const storeMinimizable = application.reactive.storeAppOptions.minimizable;
      component_subscribe($$self, storeMinimizable, (value) => $$invalidate(5, $storeMinimizable = value));
      const storeMinimized = application.reactive.storeUIState.minimized;
      component_subscribe($$self, storeMinimized, (value) => $$invalidate(22, $storeMinimized = value));
      const s_DRAG_TARGET_CLASSLIST = Object.freeze(["tjs-app-icon", "tjs-window-header-spacer", "window-header", "window-title"]);
      let dragOptions;
      let displayHeaderTitle;
      let buttonsLeft;
      let buttonsRight;
      function minimizable(node, booleanStore) {
        const callback = (event) => {
          if (event.target.classList.contains("window-title") || event.target.classList.contains("window-header") || event.target.classList.contains("keep-minimized")) {
            application._onToggleMinimize(event);
          }
        };
        function activateListeners() {
          node.addEventListener("dblclick", callback);
        }
        function removeListeners() {
          node.removeEventListener("dblclick", callback);
        }
        if (booleanStore) {
          activateListeners();
        }
        return {
          update: (booleanStore2) => {
            if (booleanStore2) {
              activateListeners();
            } else {
              removeListeners();
            }
          },
          destroy: () => removeListeners()
        };
      }
      function onPointerdown(event) {
        const rootEl = $elementRoot;
        application.position.animate.cancel();
        if ($focusAuto && A11yHelper.isFocusTarget(rootEl) && rootEl?.isConnected) {
          if ($focusKeep) {
            const activeWindow = application.reactive.activeWindow;
            const focusOutside = A11yHelper.isFocusTarget(activeWindow.document.activeElement) && !rootEl.contains(activeWindow.document.activeElement);
            if (focusOutside) {
              rootEl.focus();
            } else {
              event.preventDefault();
            }
          } else {
            rootEl.focus();
          }
        }
      }
      $$self.$$set = ($$props2) => {
        if ("draggable" in $$props2) $$invalidate(0, draggable$1 = $$props2.draggable);
        if ("draggableOptions" in $$props2) $$invalidate(20, draggableOptions = $$props2.draggableOptions);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty[0] & /*draggable*/
        1) {
          $$invalidate(0, draggable$1 = typeof draggable$1 === "function" ? draggable$1 : draggable);
        }
        if ($$self.$$.dirty[0] & /*draggableOptions, $storeDraggable*/
        17825792) {
          $$invalidate(3, dragOptions = Object.assign(
            {},
            {
              tween: true,
              tweenOptions: { duration: 0.06, ease: "cubicOut" }
            },
            isObject(draggableOptions) ? draggableOptions : {},
            {
              position: application.position,
              enabled: $storeDraggable,
              storeDragging,
              hasTargetClassList: s_DRAG_TARGET_CLASSLIST
            }
          ));
        }
        if ($$self.$$.dirty[0] & /*$storeHeaderNoTitleMinimized, $storeMinimized*/
        12582912) {
          $$invalidate(4, displayHeaderTitle = $storeHeaderNoTitleMinimized && $storeMinimized ? "none" : null);
        }
        if ($$self.$$.dirty[0] & /*$storeHeaderButtons, buttonsLeft, buttonsRight*/
        2097158) {
          {
            $$invalidate(1, buttonsLeft = []);
            $$invalidate(2, buttonsRight = []);
            for (const button of $storeHeaderButtons) {
              const buttonsList = typeof button?.alignLeft === "boolean" && button?.alignLeft ? buttonsLeft : buttonsRight;
              buttonsList.push(TJSSvelte.config.isConfigEmbed(button?.svelte) ? { ...button.svelte } : {
                class: TJSHeaderButton,
                props: { button }
              });
            }
          }
        }
      };
      return [
        draggable$1,
        buttonsLeft,
        buttonsRight,
        dragOptions,
        displayHeaderTitle,
        $storeMinimizable,
        $storeHeaderIcon,
        $storeTitle,
        focusAuto,
        focusKeep,
        elementRoot,
        storeTitle,
        storeDraggable,
        storeHeaderButtons,
        storeHeaderIcon,
        storeHeaderNoTitleMinimized,
        storeMinimizable,
        storeMinimized,
        minimizable,
        onPointerdown,
        draggableOptions,
        $storeHeaderButtons,
        $storeMinimized,
        $storeHeaderNoTitleMinimized,
        $storeDraggable
      ];
    }
    class TJSApplicationHeader extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$o, create_fragment$o, safe_not_equal, { draggable: 0, draggableOptions: 20 }, null, [-1, -1]);
      }
    }
    class ResizeHandleTransform {
      /**
       * Stores inverted app transform matrix.
       */
      static #invMat = new Mat4();
      /**
       * Stores converted world delta width & height change.
       */
      static #pDeltaLocal = new Vec3();
      /**
       * Stores point down in local space.
       */
      static #pLocalDown = new Vec3();
      /**
       * Stores point drag in local space.
       */
      static #pLocalDrag = new Vec3();
      /**
       * Stores point down in world space.
       */
      static #pScreenDown = new Vec3();
      /**
       * Stores point drag in world space.
       */
      static #pScreenDrag = new Vec3();
      /**
       * Compute the delta width and height in local space given the app transform matrix and initial pointer down and
       * drag screen coordinates.
       *
       * @param {Mat4} transformMat - App transform matrix.
       *
       * @param {number} pScreenDownX - Pointer down X position in screen coords.
       *
       * @param {number} pScreenDownY - Pointer down Y position in screen coords.
       *
       * @param {number} pScreenDragX - Current pointer drag X position in screen coords.
       *
       * @param {number} pScreenDragY - Current pointer drag Y position in screen coords.
       *
       * @returns {Vec3} Output vector for width & height changes (x = deltaWidth, y = deltaHeight).
       */
      static computeDelta(transformMat, pScreenDownX, pScreenDownY, pScreenDragX, pScreenDragY) {
        Mat4.invert(this.#invMat, transformMat);
        this.#pScreenDown[0] = pScreenDownX;
        this.#pScreenDown[1] = pScreenDownY;
        this.#pScreenDrag[0] = pScreenDragX;
        this.#pScreenDrag[1] = pScreenDragY;
        Vec3.transformMat4(this.#pLocalDown, this.#pScreenDown, this.#invMat);
        Vec3.transformMat4(this.#pLocalDrag, this.#pScreenDrag, this.#invMat);
        this.#pDeltaLocal[0] = this.#pLocalDrag[0] - this.#pLocalDown[0];
        this.#pDeltaLocal[1] = this.#pLocalDrag[1] - this.#pLocalDown[1];
        return this.#pDeltaLocal;
      }
    }
    function create_fragment$n(ctx) {
      let div;
      let resizable_action;
      let mounted;
      let dispose;
      return {
        c() {
          div = element("div");
          div.innerHTML = `<i class="fas fa-arrows-alt-h svelte-seq-gtiiil"></i>`;
          attr(div, "class", "window-resizable-handle svelte-seq-gtiiil");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          ctx[11](div);
          if (!mounted) {
            dispose = [
              listen(
                div,
                "pointerdown",
                /*onPointerdown*/
                ctx[6]
              ),
              action_destroyer(resizable_action = /*resizable*/
              ctx[7].call(null, div, {
                active: (
                  /*$storeResizable*/
                  ctx[1]
                ),
                storeResizing: (
                  /*storeResizing*/
                  ctx[5]
                )
              }))
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (resizable_action && is_function(resizable_action.update) && dirty & /*$storeResizable*/
          2) resizable_action.update.call(null, {
            active: (
              /*$storeResizable*/
              ctx2[1]
            ),
            storeResizing: (
              /*storeResizing*/
              ctx2[5]
            )
          });
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          ctx[11](null);
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function instance$n($$self, $$props, $$invalidate) {
      let $storeElementRoot;
      let $storeMinimized;
      let $storeResizable;
      let { isResizable = false } = $$props;
      const application = getContext("#external")?.application;
      const storeElementRoot = getContext("#internal").stores.elementRoot;
      component_subscribe($$self, storeElementRoot, (value) => $$invalidate(9, $storeElementRoot = value));
      const storeResizable = application.reactive.storeAppOptions.resizable;
      component_subscribe($$self, storeResizable, (value) => $$invalidate(1, $storeResizable = value));
      const storeMinimized = application.reactive.storeUIState.minimized;
      component_subscribe($$self, storeMinimized, (value) => $$invalidate(10, $storeMinimized = value));
      const storeResizing = application.reactive.storeUIState.resizing;
      let elementResize;
      function onPointerdown() {
        application.position.animate.cancel();
      }
      function resizable(node, { active: active2 = true, storeResizing: storeResizing2 = void 0 } = {}) {
        let position = null;
        let resizing = false;
        let pScreenDownX = 0;
        let pScreenDownY = 0;
        const handlers = {
          resizeDown: ["pointerdown", (e) => onResizePointerDown(e), false],
          resizeMove: ["pointermove", (e) => onResizePointerMove(e), false],
          resizeUp: ["pointerup", (e) => onResizePointerUp(e), false]
        };
        function activateListeners() {
          node.addEventListener(...handlers.resizeDown);
          $$invalidate(8, isResizable = true);
          node.style.display = "block";
        }
        function removeListeners() {
          if (typeof storeResizing2?.set === "function") {
            storeResizing2.set(false);
          }
          node.removeEventListener(...handlers.resizeDown);
          node.removeEventListener(...handlers.resizeMove);
          node.removeEventListener(...handlers.resizeUp);
          node.style.display = "none";
          $$invalidate(8, isResizable = false);
        }
        if (active2) {
          activateListeners();
        } else {
          node.style.display = "none";
        }
        function onResizePointerDown(event) {
          event.preventDefault();
          resizing = false;
          position = application.position.get();
          if (position.height === "auto") {
            position.height = $storeElementRoot.clientHeight;
          }
          if (position.width === "auto") {
            position.width = $storeElementRoot.clientWidth;
          }
          pScreenDownX = event.clientX;
          pScreenDownY = event.clientY;
          node.addEventListener(...handlers.resizeMove);
          node.addEventListener(...handlers.resizeUp);
          node.setPointerCapture(event.pointerId);
        }
        function onResizePointerMove(event) {
          event.preventDefault();
          if (!resizing && typeof storeResizing2?.set === "function") {
            resizing = true;
            storeResizing2.set(true);
          }
          const pDeltaLocal = ResizeHandleTransform.computeDelta(application.position.transform.mat4, pScreenDownX, pScreenDownY, event.clientX, event.clientY);
          application.position.set({
            width: position.width + pDeltaLocal[0],
            height: position.height + pDeltaLocal[1]
          });
        }
        function onResizePointerUp(event) {
          resizing = false;
          if (typeof storeResizing2?.set === "function") {
            storeResizing2.set(false);
          }
          event.preventDefault();
          node.removeEventListener(...handlers.resizeMove);
          node.removeEventListener(...handlers.resizeUp);
          application?._onResize?.(event);
        }
        return {
          update: ({ active: active3 }) => {
            if (active3) {
              activateListeners();
            } else {
              removeListeners();
            }
          },
          destroy: () => removeListeners()
        };
      }
      function div_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          elementResize = $$value;
          $$invalidate(0, elementResize), $$invalidate(8, isResizable), $$invalidate(10, $storeMinimized), $$invalidate(9, $storeElementRoot);
        });
      }
      $$self.$$set = ($$props2) => {
        if ("isResizable" in $$props2) $$invalidate(8, isResizable = $$props2.isResizable);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*elementResize, isResizable, $storeMinimized, $storeElementRoot*/
        1793) {
          if (elementResize) {
            $$invalidate(0, elementResize.style.display = isResizable && !$storeMinimized ? "block" : "none", elementResize);
            const elementRoot = $storeElementRoot;
            if (elementRoot) {
              elementRoot.classList[isResizable ? "add" : "remove"]("resizable");
            }
          }
        }
      };
      return [
        elementResize,
        $storeResizable,
        storeElementRoot,
        storeResizable,
        storeMinimized,
        storeResizing,
        onPointerdown,
        resizable,
        isResizable,
        $storeElementRoot,
        $storeMinimized,
        div_binding
      ];
    }
    class ResizableHandle extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$n, create_fragment$n, safe_not_equal, { isResizable: 8 });
      }
    }
    function create_fragment$m(ctx) {
      let div;
      let mounted;
      let dispose;
      return {
        c() {
          div = element("div");
          attr(div, "class", "tjs-focus-wrap svelte-seq-kjcljd");
          attr(div, "tabindex", "0");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          ctx[4](div);
          if (!mounted) {
            dispose = listen(
              div,
              "focus",
              /*onFocus*/
              ctx[1]
            );
            mounted = true;
          }
        },
        p: noop,
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          ctx[4](null);
          mounted = false;
          dispose();
        }
      };
    }
    function instance$m($$self, $$props, $$invalidate) {
      let { elementRoot = void 0 } = $$props;
      let { enabled = true } = $$props;
      let ignoreElements, wrapEl;
      function onFocus() {
        if (!enabled) {
          return;
        }
        if (A11yHelper.isFocusTarget(elementRoot)) {
          const firstFocusEl = A11yHelper.getFirstFocusableElement(elementRoot, ignoreElements);
          if (A11yHelper.isFocusTarget(firstFocusEl) && firstFocusEl !== wrapEl) {
            firstFocusEl.focus();
          } else {
            elementRoot.focus();
          }
        }
      }
      function div_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          wrapEl = $$value;
          $$invalidate(0, wrapEl);
        });
      }
      $$self.$$set = ($$props2) => {
        if ("elementRoot" in $$props2) $$invalidate(2, elementRoot = $$props2.elementRoot);
        if ("enabled" in $$props2) $$invalidate(3, enabled = $$props2.enabled);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*wrapEl*/
        1) {
          if (wrapEl) {
            ignoreElements = /* @__PURE__ */ new Set([wrapEl]);
          }
        }
      };
      return [wrapEl, onFocus, elementRoot, enabled, div_binding];
    }
    class TJSFocusWrap extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$m, create_fragment$m, safe_not_equal, { elementRoot: 2, enabled: 3 });
      }
    }
    function create_else_block$2(ctx) {
      let div;
      let tjsapplicationheader;
      let t0;
      let section;
      let applyStyles_action;
      let t1;
      let resizablehandle;
      let t2;
      let tjsfocuswrap;
      let div_id_value;
      let div_class_value;
      let div_data_appid_value;
      let applyStyles_action_1;
      let dynamicAction_action;
      let current;
      let mounted;
      let dispose;
      tjsapplicationheader = new TJSApplicationHeader({
        props: {
          draggable: (
            /*draggable*/
            ctx[6]
          ),
          draggableOptions: (
            /*draggableOptions*/
            ctx[7]
          )
        }
      });
      const default_slot_template = (
        /*#slots*/
        ctx[37].default
      );
      const default_slot = create_slot(
        default_slot_template,
        ctx,
        /*$$scope*/
        ctx[36],
        null
      );
      resizablehandle = new ResizableHandle({});
      tjsfocuswrap = new TJSFocusWrap({
        props: {
          elementRoot: (
            /*elementRoot*/
            ctx[1]
          ),
          enabled: (
            /*focusWrapEnabled*/
            ctx[11]
          )
        }
      });
      return {
        c() {
          div = element("div");
          create_component(tjsapplicationheader.$$.fragment);
          t0 = space();
          section = element("section");
          if (default_slot) default_slot.c();
          t1 = space();
          create_component(resizablehandle.$$.fragment);
          t2 = space();
          create_component(tjsfocuswrap.$$.fragment);
          attr(section, "class", "window-content svelte-seq-90z1oq");
          attr(section, "tabindex", "-1");
          attr(div, "id", div_id_value = /*application*/
          ctx[10].id);
          attr(div, "class", div_class_value = "app window-app " + /*application*/
          ctx[10].options.classes.join(" ") + " svelte-seq-90z1oq");
          attr(div, "data-appid", div_data_appid_value = /*application*/
          ctx[10].appId);
          attr(div, "role", "application");
          attr(div, "tabindex", "-1");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          mount_component(tjsapplicationheader, div, null);
          append(div, t0);
          append(div, section);
          if (default_slot) {
            default_slot.m(section, null);
          }
          ctx[40](section);
          append(div, t1);
          mount_component(resizablehandle, div, null);
          append(div, t2);
          mount_component(tjsfocuswrap, div, null);
          ctx[41](div);
          current = true;
          if (!mounted) {
            dispose = [
              listen(
                section,
                "pointerdown",
                /*onPointerdownContent*/
                ctx[22]
              ),
              action_destroyer(applyStyles_action = applyStyles.call(
                null,
                section,
                /*stylesContent*/
                ctx[9]
              )),
              action_destroyer(
                /*contentResizeObserver*/
                ctx[18].call(
                  null,
                  section,
                  /*resizeObservedContent*/
                  ctx[23]
                )
              ),
              listen(div, "close:popup", stop_propagation(prevent_default(
                /*onClosePopup*/
                ctx[19]
              ))),
              listen(
                div,
                "keydown",
                /*onKeydown*/
                ctx[20]
              ),
              listen(
                div,
                "pointerdown",
                /*onPointerdownApp*/
                ctx[21],
                true
              ),
              action_destroyer(applyStyles_action_1 = applyStyles.call(
                null,
                div,
                /*stylesApp*/
                ctx[8]
              )),
              action_destroyer(dynamicAction_action = dynamicAction.call(
                null,
                div,
                /*appResizeObserver*/
                ctx[12]
              ))
            ];
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          const tjsapplicationheader_changes = {};
          if (dirty[0] & /*draggable*/
          64) tjsapplicationheader_changes.draggable = /*draggable*/
          ctx2[6];
          if (dirty[0] & /*draggableOptions*/
          128) tjsapplicationheader_changes.draggableOptions = /*draggableOptions*/
          ctx2[7];
          tjsapplicationheader.$set(tjsapplicationheader_changes);
          if (default_slot) {
            if (default_slot.p && (!current || dirty[1] & /*$$scope*/
            32)) {
              update_slot_base(
                default_slot,
                default_slot_template,
                ctx2,
                /*$$scope*/
                ctx2[36],
                !current ? get_all_dirty_from_scope(
                  /*$$scope*/
                  ctx2[36]
                ) : get_slot_changes(
                  default_slot_template,
                  /*$$scope*/
                  ctx2[36],
                  dirty,
                  null
                ),
                null
              );
            }
          }
          if (applyStyles_action && is_function(applyStyles_action.update) && dirty[0] & /*stylesContent*/
          512) applyStyles_action.update.call(
            null,
            /*stylesContent*/
            ctx2[9]
          );
          const tjsfocuswrap_changes = {};
          if (dirty[0] & /*elementRoot*/
          2) tjsfocuswrap_changes.elementRoot = /*elementRoot*/
          ctx2[1];
          if (dirty[0] & /*focusWrapEnabled*/
          2048) tjsfocuswrap_changes.enabled = /*focusWrapEnabled*/
          ctx2[11];
          tjsfocuswrap.$set(tjsfocuswrap_changes);
          if (!current || dirty[0] & /*application*/
          1024 && div_id_value !== (div_id_value = /*application*/
          ctx2[10].id)) {
            attr(div, "id", div_id_value);
          }
          if (!current || dirty[0] & /*application*/
          1024 && div_class_value !== (div_class_value = "app window-app " + /*application*/
          ctx2[10].options.classes.join(" ") + " svelte-seq-90z1oq")) {
            attr(div, "class", div_class_value);
          }
          if (!current || dirty[0] & /*application*/
          1024 && div_data_appid_value !== (div_data_appid_value = /*application*/
          ctx2[10].appId)) {
            attr(div, "data-appid", div_data_appid_value);
          }
          if (applyStyles_action_1 && is_function(applyStyles_action_1.update) && dirty[0] & /*stylesApp*/
          256) applyStyles_action_1.update.call(
            null,
            /*stylesApp*/
            ctx2[8]
          );
          if (dynamicAction_action && is_function(dynamicAction_action.update) && dirty[0] & /*appResizeObserver*/
          4096) dynamicAction_action.update.call(
            null,
            /*appResizeObserver*/
            ctx2[12]
          );
        },
        i(local) {
          if (current) return;
          transition_in(tjsapplicationheader.$$.fragment, local);
          transition_in(default_slot, local);
          transition_in(resizablehandle.$$.fragment, local);
          transition_in(tjsfocuswrap.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(tjsapplicationheader.$$.fragment, local);
          transition_out(default_slot, local);
          transition_out(resizablehandle.$$.fragment, local);
          transition_out(tjsfocuswrap.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          destroy_component(tjsapplicationheader);
          if (default_slot) default_slot.d(detaching);
          ctx[40](null);
          destroy_component(resizablehandle);
          destroy_component(tjsfocuswrap);
          ctx[41](null);
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function create_if_block$8(ctx) {
      let div;
      let tjsapplicationheader;
      let t0;
      let section;
      let applyStyles_action;
      let t1;
      let resizablehandle;
      let t2;
      let tjsfocuswrap;
      let div_id_value;
      let div_class_value;
      let div_data_appid_value;
      let applyStyles_action_1;
      let dynamicAction_action;
      let div_intro;
      let div_outro;
      let current;
      let mounted;
      let dispose;
      tjsapplicationheader = new TJSApplicationHeader({
        props: {
          draggable: (
            /*draggable*/
            ctx[6]
          ),
          draggableOptions: (
            /*draggableOptions*/
            ctx[7]
          )
        }
      });
      const default_slot_template = (
        /*#slots*/
        ctx[37].default
      );
      const default_slot = create_slot(
        default_slot_template,
        ctx,
        /*$$scope*/
        ctx[36],
        null
      );
      resizablehandle = new ResizableHandle({});
      tjsfocuswrap = new TJSFocusWrap({
        props: { elementRoot: (
          /*elementRoot*/
          ctx[1]
        ) }
      });
      return {
        c() {
          div = element("div");
          create_component(tjsapplicationheader.$$.fragment);
          t0 = space();
          section = element("section");
          if (default_slot) default_slot.c();
          t1 = space();
          create_component(resizablehandle.$$.fragment);
          t2 = space();
          create_component(tjsfocuswrap.$$.fragment);
          attr(section, "class", "window-content svelte-seq-90z1oq");
          attr(section, "tabindex", "-1");
          attr(div, "id", div_id_value = /*application*/
          ctx[10].id);
          attr(div, "class", div_class_value = "app window-app " + /*application*/
          ctx[10].options.classes.join(" ") + " svelte-seq-90z1oq");
          attr(div, "data-appid", div_data_appid_value = /*application*/
          ctx[10].appId);
          attr(div, "role", "application");
          attr(div, "tabindex", "-1");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          mount_component(tjsapplicationheader, div, null);
          append(div, t0);
          append(div, section);
          if (default_slot) {
            default_slot.m(section, null);
          }
          ctx[38](section);
          append(div, t1);
          mount_component(resizablehandle, div, null);
          append(div, t2);
          mount_component(tjsfocuswrap, div, null);
          ctx[39](div);
          current = true;
          if (!mounted) {
            dispose = [
              listen(
                section,
                "pointerdown",
                /*onPointerdownContent*/
                ctx[22]
              ),
              action_destroyer(applyStyles_action = applyStyles.call(
                null,
                section,
                /*stylesContent*/
                ctx[9]
              )),
              action_destroyer(
                /*contentResizeObserver*/
                ctx[18].call(
                  null,
                  section,
                  /*resizeObservedContent*/
                  ctx[23]
                )
              ),
              listen(div, "close:popup", stop_propagation(prevent_default(
                /*onClosePopup*/
                ctx[19]
              ))),
              listen(
                div,
                "keydown",
                /*onKeydown*/
                ctx[20]
              ),
              listen(
                div,
                "pointerdown",
                /*onPointerdownApp*/
                ctx[21],
                true
              ),
              action_destroyer(applyStyles_action_1 = applyStyles.call(
                null,
                div,
                /*stylesApp*/
                ctx[8]
              )),
              action_destroyer(dynamicAction_action = dynamicAction.call(
                null,
                div,
                /*appResizeObserver*/
                ctx[12]
              ))
            ];
            mounted = true;
          }
        },
        p(new_ctx, dirty) {
          ctx = new_ctx;
          const tjsapplicationheader_changes = {};
          if (dirty[0] & /*draggable*/
          64) tjsapplicationheader_changes.draggable = /*draggable*/
          ctx[6];
          if (dirty[0] & /*draggableOptions*/
          128) tjsapplicationheader_changes.draggableOptions = /*draggableOptions*/
          ctx[7];
          tjsapplicationheader.$set(tjsapplicationheader_changes);
          if (default_slot) {
            if (default_slot.p && (!current || dirty[1] & /*$$scope*/
            32)) {
              update_slot_base(
                default_slot,
                default_slot_template,
                ctx,
                /*$$scope*/
                ctx[36],
                !current ? get_all_dirty_from_scope(
                  /*$$scope*/
                  ctx[36]
                ) : get_slot_changes(
                  default_slot_template,
                  /*$$scope*/
                  ctx[36],
                  dirty,
                  null
                ),
                null
              );
            }
          }
          if (applyStyles_action && is_function(applyStyles_action.update) && dirty[0] & /*stylesContent*/
          512) applyStyles_action.update.call(
            null,
            /*stylesContent*/
            ctx[9]
          );
          const tjsfocuswrap_changes = {};
          if (dirty[0] & /*elementRoot*/
          2) tjsfocuswrap_changes.elementRoot = /*elementRoot*/
          ctx[1];
          tjsfocuswrap.$set(tjsfocuswrap_changes);
          if (!current || dirty[0] & /*application*/
          1024 && div_id_value !== (div_id_value = /*application*/
          ctx[10].id)) {
            attr(div, "id", div_id_value);
          }
          if (!current || dirty[0] & /*application*/
          1024 && div_class_value !== (div_class_value = "app window-app " + /*application*/
          ctx[10].options.classes.join(" ") + " svelte-seq-90z1oq")) {
            attr(div, "class", div_class_value);
          }
          if (!current || dirty[0] & /*application*/
          1024 && div_data_appid_value !== (div_data_appid_value = /*application*/
          ctx[10].appId)) {
            attr(div, "data-appid", div_data_appid_value);
          }
          if (applyStyles_action_1 && is_function(applyStyles_action_1.update) && dirty[0] & /*stylesApp*/
          256) applyStyles_action_1.update.call(
            null,
            /*stylesApp*/
            ctx[8]
          );
          if (dynamicAction_action && is_function(dynamicAction_action.update) && dirty[0] & /*appResizeObserver*/
          4096) dynamicAction_action.update.call(
            null,
            /*appResizeObserver*/
            ctx[12]
          );
        },
        i(local) {
          if (current) return;
          transition_in(tjsapplicationheader.$$.fragment, local);
          transition_in(default_slot, local);
          transition_in(resizablehandle.$$.fragment, local);
          transition_in(tjsfocuswrap.$$.fragment, local);
          add_render_callback(() => {
            if (!current) return;
            if (div_outro) div_outro.end(1);
            div_intro = create_in_transition(
              div,
              /*inTransition*/
              ctx[2],
              /*inTransitionOptions*/
              ctx[4]
            );
            div_intro.start();
          });
          current = true;
        },
        o(local) {
          transition_out(tjsapplicationheader.$$.fragment, local);
          transition_out(default_slot, local);
          transition_out(resizablehandle.$$.fragment, local);
          transition_out(tjsfocuswrap.$$.fragment, local);
          if (div_intro) div_intro.invalidate();
          div_outro = create_out_transition(
            div,
            /*outTransition*/
            ctx[3],
            /*outTransitionOptions*/
            ctx[5]
          );
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          destroy_component(tjsapplicationheader);
          if (default_slot) default_slot.d(detaching);
          ctx[38](null);
          destroy_component(resizablehandle);
          destroy_component(tjsfocuswrap);
          ctx[39](null);
          if (detaching && div_outro) div_outro.end();
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function create_fragment$l(ctx) {
      let current_block_type_index;
      let if_block;
      let if_block_anchor;
      let current;
      const if_block_creators = [create_if_block$8, create_else_block$2];
      const if_blocks = [];
      function select_block_type(ctx2, dirty) {
        if (
          /*inTransition*/
          ctx2[2] !== TJSDefaultTransition.default || /*outTransition*/
          ctx2[3] !== TJSDefaultTransition.default
        ) return 0;
        return 1;
      }
      current_block_type_index = select_block_type(ctx);
      if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
      return {
        c() {
          if_block.c();
          if_block_anchor = empty();
        },
        m(target, anchor) {
          if_blocks[current_block_type_index].m(target, anchor);
          insert(target, if_block_anchor, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          let previous_block_index = current_block_type_index;
          current_block_type_index = select_block_type(ctx2);
          if (current_block_type_index === previous_block_index) {
            if_blocks[current_block_type_index].p(ctx2, dirty);
          } else {
            group_outros();
            transition_out(if_blocks[previous_block_index], 1, 1, () => {
              if_blocks[previous_block_index] = null;
            });
            check_outros();
            if_block = if_blocks[current_block_type_index];
            if (!if_block) {
              if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
              if_block.c();
            } else {
              if_block.p(ctx2, dirty);
            }
            transition_in(if_block, 1);
            if_block.m(if_block_anchor.parentNode, if_block_anchor);
          }
        },
        i(local) {
          if (current) return;
          transition_in(if_block);
          current = true;
        },
        o(local) {
          transition_out(if_block);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(if_block_anchor);
          }
          if_blocks[current_block_type_index].d(detaching);
        }
      };
    }
    function instance$l($$self, $$props, $$invalidate) {
      let appResizeObserver;
      let $focusKeep;
      let $focusAuto;
      let $minimized;
      let $focusTrap;
      let $resizeObservable;
      let { $$slots: slots = {}, $$scope } = $$props;
      let { elementContent = void 0 } = $$props;
      let { elementRoot = void 0 } = $$props;
      let { draggable: draggable2 = void 0 } = $$props;
      let { draggableOptions = void 0 } = $$props;
      let { stylesApp = void 0 } = $$props;
      let { stylesContent = void 0 } = $$props;
      const application = getContext("#external")?.application;
      const { focusAuto, focusKeep, focusTrap } = application.reactive.storeAppOptions;
      component_subscribe($$self, focusAuto, (value) => $$invalidate(32, $focusAuto = value));
      component_subscribe($$self, focusKeep, (value) => $$invalidate(42, $focusKeep = value));
      component_subscribe($$self, focusTrap, (value) => $$invalidate(34, $focusTrap = value));
      const { minimized } = application.reactive.storeUIState;
      component_subscribe($$self, minimized, (value) => $$invalidate(33, $minimized = value));
      const { resizeObservable } = application.position.stores;
      component_subscribe($$self, resizeObservable, (value) => $$invalidate(35, $resizeObservable = value));
      let { appOffsetHeight = false } = $$props;
      let { appOffsetWidth = false } = $$props;
      const initialAppResizeObserver = !!appOffsetHeight || !!appOffsetWidth;
      let { contentOffsetHeight = false } = $$props;
      let { contentOffsetWidth = false } = $$props;
      const contentResizeObserver = !!contentOffsetHeight || !!contentOffsetWidth ? resizeObserver : () => null;
      const internal = new AppShellContextInternal();
      const s_IGNORE_CLASSES = { ignoreClasses: ["tjs-focus-wrap"] };
      setContext("#internal", internal);
      let focusWrapEnabled;
      let { transition = TJSDefaultTransition.default } = $$props;
      let { inTransition = TJSDefaultTransition.default } = $$props;
      let { outTransition = TJSDefaultTransition.default } = $$props;
      let { transitionOptions = void 0 } = $$props;
      let { inTransitionOptions = TJSDefaultTransition.options } = $$props;
      let { outTransitionOptions = TJSDefaultTransition.options } = $$props;
      let oldTransition = TJSDefaultTransition.default;
      let oldTransitionOptions = void 0;
      onMount(() => elementRoot.focus());
      function onClosePopup(event) {
        if (!$focusAuto) {
          return;
        }
        const targetEl = event?.detail?.target;
        if (!A11yHelper.isFocusTarget(targetEl)) {
          return;
        }
        if (A11yHelper.isFocusable(targetEl)) {
          return;
        }
        const elementRootContains = elementRoot.contains(targetEl);
        if (targetEl === elementRoot) {
          elementRoot.focus();
        } else if (targetEl === elementContent) {
          elementContent.focus();
        } else if (elementRootContains) {
          if (elementContent.contains(targetEl)) {
            elementContent.focus();
          } else {
            elementRoot.focus();
          }
        }
      }
      function onKeydown(event) {
        if ((event.target === elementRoot || event.target === elementContent) && KeyboardManager && KeyboardManager?._getMatchingActions?.(KeyboardManager?.getKeyboardEventContext?.(event))?.length) {
          event.target?.blur();
          return;
        }
        if (focusWrapEnabled && event.shiftKey && event.code === "Tab") {
          const allFocusable = A11yHelper.getFocusableElements(elementRoot, s_IGNORE_CLASSES);
          const firstFocusEl = allFocusable.length > 0 ? allFocusable[0] : void 0;
          const lastFocusEl = allFocusable.length > 0 ? allFocusable[allFocusable.length - 1] : void 0;
          const activeWindow = application.reactive.activeWindow;
          if (elementRoot === activeWindow.document.activeElement || firstFocusEl === activeWindow.document.activeElement) {
            if (A11yHelper.isFocusTarget(lastFocusEl) && firstFocusEl !== lastFocusEl) {
              lastFocusEl.focus();
            }
            event.preventDefault();
            event.stopPropagation();
          }
        }
        if (typeof application?.options?.popOut === "boolean" && application.options.popOut && application !== globalThis.ui?.activeWindow) {
          application.bringToTop.call(application);
        }
      }
      function onPointerdownApp() {
        if (typeof application?.options?.popOut === "boolean" && application.options.popOut && application !== globalThis.ui?.activeWindow) {
          application.bringToTop.call(application);
        }
      }
      function onPointerdownContent(event) {
        const focusable = A11yHelper.isFocusable(event.target);
        if (!focusable && $focusAuto) {
          if ($focusKeep) {
            const activeWindow = application.reactive.activeWindow;
            const focusOutside = !elementRoot.contains(activeWindow.document.activeElement);
            if (focusOutside) {
              elementContent.focus();
            } else {
              event.preventDefault();
            }
          } else {
            elementContent.focus();
          }
        }
      }
      function resizeObservedContent(offsetWidth, offsetHeight) {
        $$invalidate(27, contentOffsetWidth = offsetWidth);
        $$invalidate(26, contentOffsetHeight = offsetHeight);
      }
      function resizeObservedApp(offsetWidth, offsetHeight, contentWidth, contentHeight) {
        application.position.stores.resizeObserved.update((object) => {
          object.contentWidth = contentWidth;
          object.contentHeight = contentHeight;
          object.offsetWidth = offsetWidth;
          object.offsetHeight = offsetHeight;
          return object;
        });
        $$invalidate(24, appOffsetHeight = offsetHeight);
        $$invalidate(25, appOffsetWidth = offsetWidth);
      }
      function section_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          elementContent = $$value;
          $$invalidate(0, elementContent);
        });
      }
      function div_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          elementRoot = $$value;
          $$invalidate(1, elementRoot);
        });
      }
      function section_binding_1($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          elementContent = $$value;
          $$invalidate(0, elementContent);
        });
      }
      function div_binding_1($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          elementRoot = $$value;
          $$invalidate(1, elementRoot);
        });
      }
      $$self.$$set = ($$props2) => {
        if ("elementContent" in $$props2) $$invalidate(0, elementContent = $$props2.elementContent);
        if ("elementRoot" in $$props2) $$invalidate(1, elementRoot = $$props2.elementRoot);
        if ("draggable" in $$props2) $$invalidate(6, draggable2 = $$props2.draggable);
        if ("draggableOptions" in $$props2) $$invalidate(7, draggableOptions = $$props2.draggableOptions);
        if ("stylesApp" in $$props2) $$invalidate(8, stylesApp = $$props2.stylesApp);
        if ("stylesContent" in $$props2) $$invalidate(9, stylesContent = $$props2.stylesContent);
        if ("appOffsetHeight" in $$props2) $$invalidate(24, appOffsetHeight = $$props2.appOffsetHeight);
        if ("appOffsetWidth" in $$props2) $$invalidate(25, appOffsetWidth = $$props2.appOffsetWidth);
        if ("contentOffsetHeight" in $$props2) $$invalidate(26, contentOffsetHeight = $$props2.contentOffsetHeight);
        if ("contentOffsetWidth" in $$props2) $$invalidate(27, contentOffsetWidth = $$props2.contentOffsetWidth);
        if ("transition" in $$props2) $$invalidate(28, transition = $$props2.transition);
        if ("inTransition" in $$props2) $$invalidate(2, inTransition = $$props2.inTransition);
        if ("outTransition" in $$props2) $$invalidate(3, outTransition = $$props2.outTransition);
        if ("transitionOptions" in $$props2) $$invalidate(29, transitionOptions = $$props2.transitionOptions);
        if ("inTransitionOptions" in $$props2) $$invalidate(4, inTransitionOptions = $$props2.inTransitionOptions);
        if ("outTransitionOptions" in $$props2) $$invalidate(5, outTransitionOptions = $$props2.outTransitionOptions);
        if ("$$scope" in $$props2) $$invalidate(36, $$scope = $$props2.$$scope);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty[1] & /*$resizeObservable*/
        16) {
          $$invalidate(12, appResizeObserver = initialAppResizeObserver || $resizeObservable ? {
            action: resizeObserver,
            data: resizeObservedApp
          } : void 0);
        }
        if ($$self.$$.dirty[0] & /*elementContent*/
        1) {
          if (elementContent !== void 0 && elementContent !== null) {
            getContext("#internal").stores.elementContent.set(elementContent);
          }
        }
        if ($$self.$$.dirty[0] & /*elementRoot*/
        2) {
          if (elementRoot !== void 0 && elementRoot !== null) {
            getContext("#internal").stores.elementRoot.set(elementRoot);
          }
        }
        if ($$self.$$.dirty[1] & /*$focusAuto, $focusTrap, $minimized*/
        14) {
          $$invalidate(11, focusWrapEnabled = $focusAuto && $focusTrap && !$minimized);
        }
        if ($$self.$$.dirty[0] & /*oldTransition, transition*/
        1342177280) {
          if (oldTransition !== transition) {
            const newTransition = typeof transition === "function" ? transition : TJSDefaultTransition.default;
            $$invalidate(2, inTransition = newTransition);
            $$invalidate(3, outTransition = newTransition);
            $$invalidate(30, oldTransition = newTransition);
          }
        }
        if ($$self.$$.dirty[0] & /*transitionOptions*/
        536870912 | $$self.$$.dirty[1] & /*oldTransitionOptions*/
        1) {
          if (oldTransitionOptions !== transitionOptions) {
            const newOptions = transitionOptions !== TJSDefaultTransition.options && isObject(transitionOptions) ? transitionOptions : TJSDefaultTransition.options;
            $$invalidate(4, inTransitionOptions = newOptions);
            $$invalidate(5, outTransitionOptions = newOptions);
            $$invalidate(31, oldTransitionOptions = newOptions);
          }
        }
        if ($$self.$$.dirty[0] & /*inTransition*/
        4) {
          if (typeof inTransition !== "function") {
            $$invalidate(2, inTransition = TJSDefaultTransition.default);
          }
        }
        if ($$self.$$.dirty[0] & /*outTransition, application*/
        1032) {
          {
            if (typeof outTransition !== "function") {
              $$invalidate(3, outTransition = TJSDefaultTransition.default);
            }
            const defaultCloseAnimation = application?.options?.defaultCloseAnimation;
            if (typeof defaultCloseAnimation === "boolean" && defaultCloseAnimation && outTransition !== TJSDefaultTransition.default) {
              $$invalidate(10, application.options.defaultCloseAnimation = false, application);
            }
          }
        }
        if ($$self.$$.dirty[0] & /*inTransitionOptions*/
        16) {
          if (!isObject(inTransitionOptions)) {
            $$invalidate(4, inTransitionOptions = TJSDefaultTransition.options);
          }
        }
        if ($$self.$$.dirty[0] & /*outTransitionOptions*/
        32) {
          if (!isObject(outTransitionOptions)) {
            $$invalidate(5, outTransitionOptions = TJSDefaultTransition.options);
          }
        }
      };
      return [
        elementContent,
        elementRoot,
        inTransition,
        outTransition,
        inTransitionOptions,
        outTransitionOptions,
        draggable2,
        draggableOptions,
        stylesApp,
        stylesContent,
        application,
        focusWrapEnabled,
        appResizeObserver,
        focusAuto,
        focusKeep,
        focusTrap,
        minimized,
        resizeObservable,
        contentResizeObserver,
        onClosePopup,
        onKeydown,
        onPointerdownApp,
        onPointerdownContent,
        resizeObservedContent,
        appOffsetHeight,
        appOffsetWidth,
        contentOffsetHeight,
        contentOffsetWidth,
        transition,
        transitionOptions,
        oldTransition,
        oldTransitionOptions,
        $focusAuto,
        $minimized,
        $focusTrap,
        $resizeObservable,
        $$scope,
        slots,
        section_binding,
        div_binding,
        section_binding_1,
        div_binding_1
      ];
    }
    class ApplicationShell extends SvelteComponent {
      constructor(options) {
        super();
        init(
          this,
          options,
          instance$l,
          create_fragment$l,
          safe_not_equal,
          {
            elementContent: 0,
            elementRoot: 1,
            draggable: 6,
            draggableOptions: 7,
            stylesApp: 8,
            stylesContent: 9,
            appOffsetHeight: 24,
            appOffsetWidth: 25,
            contentOffsetHeight: 26,
            contentOffsetWidth: 27,
            transition: 28,
            inTransition: 2,
            outTransition: 3,
            transitionOptions: 29,
            inTransitionOptions: 4,
            outTransitionOptions: 5
          },
          null,
          [-1, -1]
        );
      }
      get elementContent() {
        return this.$$.ctx[0];
      }
      set elementContent(elementContent) {
        this.$$set({ elementContent });
        flush();
      }
      get elementRoot() {
        return this.$$.ctx[1];
      }
      set elementRoot(elementRoot) {
        this.$$set({ elementRoot });
        flush();
      }
      get draggable() {
        return this.$$.ctx[6];
      }
      set draggable(draggable2) {
        this.$$set({ draggable: draggable2 });
        flush();
      }
      get draggableOptions() {
        return this.$$.ctx[7];
      }
      set draggableOptions(draggableOptions) {
        this.$$set({ draggableOptions });
        flush();
      }
      get stylesApp() {
        return this.$$.ctx[8];
      }
      set stylesApp(stylesApp) {
        this.$$set({ stylesApp });
        flush();
      }
      get stylesContent() {
        return this.$$.ctx[9];
      }
      set stylesContent(stylesContent) {
        this.$$set({ stylesContent });
        flush();
      }
      get appOffsetHeight() {
        return this.$$.ctx[24];
      }
      set appOffsetHeight(appOffsetHeight) {
        this.$$set({ appOffsetHeight });
        flush();
      }
      get appOffsetWidth() {
        return this.$$.ctx[25];
      }
      set appOffsetWidth(appOffsetWidth) {
        this.$$set({ appOffsetWidth });
        flush();
      }
      get contentOffsetHeight() {
        return this.$$.ctx[26];
      }
      set contentOffsetHeight(contentOffsetHeight) {
        this.$$set({ contentOffsetHeight });
        flush();
      }
      get contentOffsetWidth() {
        return this.$$.ctx[27];
      }
      set contentOffsetWidth(contentOffsetWidth) {
        this.$$set({ contentOffsetWidth });
        flush();
      }
      get transition() {
        return this.$$.ctx[28];
      }
      set transition(transition) {
        this.$$set({ transition });
        flush();
      }
      get inTransition() {
        return this.$$.ctx[2];
      }
      set inTransition(inTransition) {
        this.$$set({ inTransition });
        flush();
      }
      get outTransition() {
        return this.$$.ctx[3];
      }
      set outTransition(outTransition) {
        this.$$set({ outTransition });
        flush();
      }
      get transitionOptions() {
        return this.$$.ctx[29];
      }
      set transitionOptions(transitionOptions) {
        this.$$set({ transitionOptions });
        flush();
      }
      get inTransitionOptions() {
        return this.$$.ctx[4];
      }
      set inTransitionOptions(inTransitionOptions) {
        this.$$set({ inTransitionOptions });
        flush();
      }
      get outTransitionOptions() {
        return this.$$.ctx[5];
      }
      set outTransitionOptions(outTransitionOptions) {
        this.$$set({ outTransitionOptions });
        flush();
      }
    }
    cssVariables.setProperties({
      // Anchor text shadow / header buttons
      "--tjs-default-text-shadow-focus-hover": "0 0 8px var(--color-shadow-primary)",
      // TJSApplicationShell app background.
      "--tjs-app-background": `url("${globalThis.foundry.utils.getRoute("/ui/denim075.png")}")`
    }, false);
    class ApplicationState {
      /** @type {import('../../SvelteApp.js').SvelteApp} */
      #application;
      /**
       * Stores the current save state key being restored by animating. When a restore is already being animated with the
       * same name the subsequent restore animation is ignored.
       *
       * @type {string | undefined}
       */
      #currentRestoreKey;
      /** @type {Map<string, import('../../types').SvelteApp.API.State.Data>} */
      #dataSaved = /* @__PURE__ */ new Map();
      /**
       * @param {import('../../SvelteApp.js').SvelteApp}   application - The application.
       */
      constructor(application) {
        this.#application = application;
        Object.seal(this);
      }
      /**
       * Clears all saved application state.
       */
      clear() {
        this.#dataSaved.clear();
      }
      /**
       * Returns current application state along with any extra data passed into method.
       *
       * @param {object} [extra] - Extra data to add to application state.
       *
       * @returns {import('../../types').SvelteApp.API.State.Data} Passed in object with current application state.
       */
      current(extra = {}) {
        return Object.assign(extra, {
          position: this.#application?.position?.get(),
          beforeMinimized: this.#application?.position?.state.get({ name: "#beforeMinimized" }),
          options: this.#application?.reactive?.toJSON(),
          ui: { minimized: this.#application?.reactive?.minimized }
        });
      }
      /**
       * Gets any saved application state by name.
       *
       * @param {object}   options - Options.
       *
       * @param {string}   options.name - Saved data set name.
       *
       * @returns {import('../../types').SvelteApp.API.State.Data | undefined} Any saved application state.
       */
      get({ name: name2 }) {
        if (typeof name2 !== "string") {
          throw new TypeError(`[SvelteApp.state.get] error: 'name' is not a string.`);
        }
        return this.#dataSaved.get(name2);
      }
      /**
       * @returns {IterableIterator<string>} The saved application state names / keys.
       */
      keys() {
        return this.#dataSaved.keys();
      }
      /**
       * Removes and returns any saved application state by name.
       *
       * @param {object}   options - Options.
       *
       * @param {string}   options.name - Name to remove and retrieve.
       *
       * @returns {import('../../types').SvelteApp.API.State.Data | undefined} Any saved application state.
       */
      remove({ name: name2 }) {
        if (typeof name2 !== "string") {
          throw new TypeError(`[SvelteApp.state.remove] error: 'name' is not a string.`);
        }
        const data = this.#dataSaved.get(name2);
        this.#dataSaved.delete(name2);
        return data;
      }
      /**
       * Restores a previously saved application state by `name` returning the data. Several optional parameters are
       * available to animate / tween to the new state. When `animateTo` is true an animation is scheduled via
       * {@link #runtime/svelte/store/position!TJSPosition.API.Animation.to} and the duration and easing name or function may be
       * specified.
       *
       * @param {object}            options - Options.
       *
       * @param {string}            options.name - Saved data set name.
       *
       * @param {boolean}           [options.remove=false] - Remove data set.
       *
       * @param {boolean}           [options.animateTo=false] - Animate to restore data.
       *
       * @param {number}            [options.duration=0.1] - Duration in seconds.
       *
       * @param {import('@typhonjs-fvtt/runtime/svelte/easing').EasingReference} [options.ease='linear'] - Easing function or easing
       *        function name.
       *
       * @returns {import('../../types').SvelteApp.API.State.Data | undefined} Any saved application state.
       */
      restore({ name: name2, remove = false, animateTo = false, duration = 0.1, ease = "linear" }) {
        if (typeof name2 !== "string") {
          throw new TypeError(`[SvelteApp.state.restore] error: 'name' is not a string.`);
        }
        const dataSaved = this.#dataSaved.get(name2);
        if (dataSaved) {
          if (remove) {
            this.#dataSaved.delete(name2);
          }
          if (animateTo && name2 !== this.#currentRestoreKey) {
            this.#currentRestoreKey = name2;
            this.#setImpl(dataSaved, {
              animateTo,
              async: true,
              duration,
              ease
            }).then(() => {
              if (name2 === this.#currentRestoreKey) {
                this.#currentRestoreKey = void 0;
              }
            });
          }
        }
        return dataSaved;
      }
      /**
       * Saves current application state with the opportunity to add extra data to the saved state.
       *
       * @param {object}   options - Options.
       *
       * @param {string}   options.name - Name to index this saved state.
       *
       * @returns {import('../../types').SvelteApp.API.State.Data} Current saved application state.
       */
      save({ name: name2, ...extra }) {
        if (typeof name2 !== "string") {
          throw new TypeError(`[SvelteApp.state.save] error: 'name' is not a string.`);
        }
        const data = this.current(extra);
        this.#dataSaved.set(name2, data);
        return data;
      }
      /**
       * Sets application state from the given `SvelteApp.API.State.Data` instance. Several optional parameters are
       * available to animate / tween to the new state. When `animateTo` is true an animation is scheduled via
       * {@link #runtime/svelte/store/position!AnimationAPI.to} and the duration and easing name or function may be
       * specified.
       *
       * Note: If serializing application state any minimized apps will use the before minimized state on initial render
       * of the app as it is currently not possible to render apps with Foundry VTT core API in the minimized state.
       *
       * @param {import('../../types').SvelteApp.API.State.Data}   data - Saved data set name.
       *
       * @param {object}         [options] - Optional parameters
       *
       * @param {boolean}        [options.animateTo=false] - Animate to restore data.
       *
       * @param {number}         [options.duration=0.1] - Duration in seconds.
       *
       * @param {import('@typhonjs-fvtt/runtime/svelte/easing').EasingReference} [options.ease='linear'] - Easing function or easing
       *        function name.
       */
      set(data, options = {}) {
        this.#setImpl(data, { ...options, async: false });
      }
      // Internal implementation ----------------------------------------------------------------------------------------
      /**
       * Sets application state from the given `SvelteApp.API.State.Data` instance. Several optional parameters are
       * available to animate / tween to the new state. When `animateTo` is true an animation is scheduled via
       * {@link #runtime/svelte/store/position!AnimationAPI.to} and the duration and easing name or function may be
       * specified.
       *
       * Note: If serializing application state any minimized apps will use the before minimized state on initial render
       * of the app as it is currently not possible to render apps with Foundry VTT core API in the minimized state.
       *
       * @privateRemarks
       * TODO: THIS METHOD NEEDS TO BE REFACTORED WHEN TRL IS MADE INTO A STANDALONE FRAMEWORK.
       *
       * @param {import('../../types').SvelteApp.API.State.Data}   data - Saved data set name.
       *
       * @param {object}            [opts] - Optional parameters
       *
       * @param {boolean}           [opts.async=false] - If animating return a Promise that resolves with any saved data.
       *
       * @param {boolean}           [opts.animateTo=false] - Animate to restore data.
       *
       * @param {number}            [opts.duration=0.1] - Duration in seconds.
       *
       * @param {import('@typhonjs-fvtt/runtime/svelte/easing').EasingReference} [opts.ease='linear'] - Easing function or easing
       *        function name.
       *
       * @returns {undefined | Promise<void>} When asynchronous the animation Promise.
       */
      #setImpl(data, { async = false, animateTo = false, duration = 0.1, ease = "linear" } = {}) {
        if (!isObject(data)) {
          throw new TypeError(`[SvelteApp.state.set] error: 'data' is not an object.`);
        }
        const application = this.#application;
        if (!isObject(data?.position)) {
          console.warn(`[SvelteApp.state.set] warning: 'data.position' is not an object.`);
          return;
        }
        const rendered = application.rendered;
        if (animateTo) {
          if (!rendered) {
            console.warn(`[SvelteApp.state.set] warning: application is not rendered and 'animateTo' is true.`);
            return;
          }
          if (data.position.transformOrigin !== application.position.transformOrigin) {
            application.position.transformOrigin = data.position.transformOrigin;
          }
          if (isObject(data?.ui)) {
            const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
            if (application?.reactive?.minimized && !minimized) {
              application.maximize({ animate: false, duration: 0 });
            }
          }
          const promise2 = application.position.animate.to(data.position, {
            duration,
            ease,
            strategy: "cancelAll"
          }).finished.then(({ cancelled }) => {
            if (cancelled) {
              return;
            }
            if (isObject(data?.options)) {
              application?.reactive.mergeOptions(data.options);
            }
            if (isObject(data?.ui)) {
              const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
              if (!application?.reactive?.minimized && minimized) {
                application.minimize({ animate: false, duration: 0 });
              }
            }
            if (isObject(data?.beforeMinimized)) {
              application.position.state.set({ name: "#beforeMinimized", ...data.beforeMinimized });
            }
          });
          if (async) {
            return promise2;
          }
        } else {
          if (rendered) {
            if (isObject(data?.options)) {
              application?.reactive.mergeOptions(data.options);
            }
            if (isObject(data?.ui)) {
              const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
              if (application?.reactive?.minimized && !minimized) {
                application.maximize({ animate: false, duration: 0 });
              } else if (!application?.reactive?.minimized && minimized) {
                application.minimize({ animate: false, duration });
              }
            }
            if (isObject(data?.beforeMinimized)) {
              application.position.state.set({ name: "#beforeMinimized", ...data.beforeMinimized });
            }
            application.position.set(data.position);
          } else {
            let positionData = data.position;
            if (isObject(data.beforeMinimized)) {
              positionData = data.beforeMinimized;
              positionData.left = data.position.left;
              positionData.top = data.position.top;
            }
            application.position.set(positionData);
          }
        }
      }
    }
    class GetSvelteData {
      /** @type {import('svelte').SvelteComponent[] | null[]} */
      #applicationShellHolder;
      /** @type {import('./types').SvelteData[]} */
      #svelteData;
      /**
       * Keep a direct reference to the SvelteData array in an associated {@link SvelteApp}.
       *
       * @param {import('svelte').SvelteComponent[] | null[]}  applicationShellHolder - A reference to the mounted app shell.
       *
       * @param {import('./types').SvelteData[]}  svelteData - A reference to the SvelteData array of mounted components.
       */
      constructor(applicationShellHolder, svelteData) {
        this.#applicationShellHolder = applicationShellHolder;
        this.#svelteData = svelteData;
      }
      /**
       * Returns any mounted application shell.
       *
       * @returns {import('svelte').SvelteComponent} Any mounted application shell.
       */
      get appShell() {
        return this.#applicationShellHolder[0];
      }
      /**
       * Returns any mounted application shell.
       *
       * @deprecated Use {@link GetSvelteData.appShell}; since `0.2.0` removal in `0.5.0`.
       *
       * @returns {import('svelte').SvelteComponent} Any mounted application shell.
       */
      get applicationShell() {
        return this.#applicationShellHolder[0];
      }
      /**
       * Returns mounted application shell data / config.
       *
       * @internal
       *
       * @returns {import('./types').SvelteData} Any mounted application shell data.
       */
      get appShellData() {
        return this.#svelteData[0];
      }
    }
    class SvelteReactive {
      /**
       * @type {import('../SvelteApp').SvelteApp}
       */
      #application;
      /**
       * @type {boolean}
       */
      #initialized = false;
      /** @type {import('@typhonjs-fvtt/runtime/svelte/store/web-storage').WebStorage} */
      #sessionStorage;
      /**
       * The Application option store which is injected into mounted Svelte component context under the `external` key.
       *
       * @type {import('./types').StoreAppOptions}
       */
      #storeAppOptions;
      /**
       * Stores the update function for `#storeAppOptions`.
       *
       * @type {(this: void, updater: import('svelte/store').Updater<object>) => void}
       */
      #storeAppOptionsUpdate;
      /**
       * Stores the UI state data to make it accessible via getters.
       *
       * @type {object}
       */
      #dataUIState;
      /**
       * The UI option store which is injected into mounted Svelte component context under the `external` key.
       *
       * @type {import('./types').StoreUIOptions}
       */
      #storeUIState;
      /**
       * Stores the update function for `#storeUIState`.
       *
       * @type {(this: void, updater: import('svelte/store').Updater<object>) => void}
       */
      #storeUIStateUpdate;
      /**
       * Stores the unsubscribe functions from local store subscriptions.
       *
       * @type {import('svelte/store').Unsubscriber[]}
       */
      #storeUnsubscribe = [];
      /**
       * @param {import('../SvelteApp').SvelteApp} application - The host Foundry application.
       */
      constructor(application) {
        this.#application = application;
        const optionsSessionStorage = application?.options?.sessionStorage;
        if (optionsSessionStorage !== void 0 && !(optionsSessionStorage instanceof TJSWebStorage)) {
          throw new TypeError(`'options.sessionStorage' is not an instance of TJSWebStorage.`);
        }
        this.#sessionStorage = optionsSessionStorage !== void 0 ? optionsSessionStorage : new TJSSessionStorage();
      }
      /**
       * Initializes reactive support. Package private for internal use.
       *
       * @returns {import('./types-local').SvelteReactiveStores | undefined} Internal methods to interact with Svelte
       * stores.
       *
       * @package
       * @internal
       */
      initialize() {
        if (this.#initialized) {
          return;
        }
        this.#initialized = true;
        this.#storesInitialize();
        return {
          appOptionsUpdate: this.#storeAppOptionsUpdate,
          uiStateUpdate: this.#storeUIStateUpdate,
          subscribe: this.#storesSubscribe.bind(this),
          unsubscribe: this.#storesUnsubscribe.bind(this)
        };
      }
      // Store getters -----------------------------------------------------------------------------------------------------
      /**
       * @returns {import('@typhonjs-fvtt/runtime/svelte/store/web-storage').WebStorage} Returns WebStorage (session) instance.
       */
      get sessionStorage() {
        return this.#sessionStorage;
      }
      /**
       * Returns the store for app options.
       *
       * @returns {import('../../types').SvelteApp.API.Reactive.AppOptions} App options store.
       */
      get storeAppOptions() {
        return this.#storeAppOptions;
      }
      /**
       * Returns the store for UI options.
       *
       * @returns {import('../../types').SvelteApp.API.Reactive.UIState} UI options store.
       */
      get storeUIState() {
        return this.#storeUIState;
      }
      // Only reactive getters ---------------------------------------------------------------------------------------------
      /**
       * Returns the current active Window / WindowProxy UI state.
       *
       * @returns {Window} Active window UI state.
       */
      get activeWindow() {
        return this.#dataUIState.activeWindow ?? globalThis;
      }
      /**
       * Returns the current dragging UI state.
       *
       * @returns {boolean} Dragging UI state.
       */
      get dragging() {
        return this.#dataUIState.dragging;
      }
      /**
       * Returns the current minimized UI state.
       *
       * @returns {boolean} Minimized UI state.
       */
      get minimized() {
        return this.#dataUIState.minimized;
      }
      /**
       * Returns the current resizing UI state.
       *
       * @returns {boolean} Resizing UI state.
       */
      get resizing() {
        return this.#dataUIState.resizing;
      }
      /**
       * Sets the current active Window / WindowProxy UI state.
       *
       * Note: This is protected usage and used internally.
       *
       * @param {Window} activeWindow - Active Window / WindowProxy UI state.
       *
       * @hidden
       */
      set activeWindow(activeWindow) {
        if (activeWindow === void 0 || activeWindow === null || Object.prototype.toString.call(activeWindow) === "[object Window]") {
          this.#storeUIStateUpdate((options) => deepMerge(options, { activeWindow: activeWindow ?? globalThis }));
        }
      }
      // Reactive getter / setters -----------------------------------------------------------------------------------------
      /**
       * Returns the draggable app option.
       *
       * @returns {boolean} Draggable app option.
       */
      get draggable() {
        return this.#application?.options?.draggable;
      }
      /**
       * Returns the focusAuto app option.
       *
       * @returns {boolean} When true auto-management of app focus is enabled.
       */
      get focusAuto() {
        return this.#application?.options?.focusAuto;
      }
      /**
       * Returns the focusKeep app option.
       *
       * @returns {boolean} When `focusAuto` and `focusKeep` is true; keeps internal focus.
       */
      get focusKeep() {
        return this.#application?.options?.focusKeep;
      }
      /**
       * Returns the focusTrap app option.
       *
       * @returns {boolean} When true focus trapping / wrapping is enabled keeping focus inside app.
       */
      get focusTrap() {
        return this.#application?.options?.focusTrap;
      }
      /**
       * Returns the headerButtonNoClose app option.
       *
       * @returns {boolean} Remove the close the button in header app option.
       */
      get headerButtonNoClose() {
        return this.#application?.options?.headerButtonNoClose;
      }
      /**
       * Returns the headerButtonNoLabel app option.
       *
       * @returns {boolean} Remove the labels from buttons in header app option.
       */
      get headerButtonNoLabel() {
        return this.#application?.options?.headerButtonNoLabel;
      }
      /**
       * Returns the headerIcon app option.
       *
       * @returns {string|void} URL for header app icon.
       */
      get headerIcon() {
        return this.#application?.options?.headerIcon;
      }
      /**
       * Returns the headerNoTitleMinimized app option.
       *
       * @returns {boolean} When true removes the header title when minimized.
       */
      get headerNoTitleMinimized() {
        return this.#application?.options?.headerNoTitleMinimized;
      }
      /**
       * Returns the minimizable app option.
       *
       * @returns {boolean} Minimizable app option.
       */
      get minimizable() {
        return this.#application?.options?.minimizable;
      }
      /**
       * Returns the Foundry popOut state; {@link Application.popOut}
       *
       * @returns {boolean} Positionable app option.
       */
      get popOut() {
        return this.#application.popOut;
      }
      /**
       * Returns the positionable app option; {@link SvelteApp.Options.positionable}
       *
       * @returns {boolean} Positionable app option.
       */
      get positionable() {
        return this.#application?.options?.positionable;
      }
      /**
       * Returns the resizable option.
       *
       * @returns {boolean} Resizable app option.
       */
      get resizable() {
        return this.#application?.options?.resizable;
      }
      /**
       * Returns the title accessor from the parent Application class; {@link Application.title}
       *
       * @privateRemarks
       * TODO: Application v2; note that super.title localizes `this.options.title`; IMHO it shouldn't.    *
       *
       * @returns {string} Title.
       */
      get title() {
        return this.#application.title;
      }
      /**
       * Sets `this.options.draggable` which is reactive for application shells.
       *
       * @param {boolean}  draggable - Sets the draggable option.
       */
      set draggable(draggable2) {
        if (typeof draggable2 === "boolean") {
          this.setOptions("draggable", draggable2);
        }
      }
      /**
       * Sets `this.options.focusAuto` which is reactive for application shells.
       *
       * @param {boolean}  focusAuto - Sets the focusAuto option.
       */
      set focusAuto(focusAuto) {
        if (typeof focusAuto === "boolean") {
          this.setOptions("focusAuto", focusAuto);
        }
      }
      /**
       * Sets `this.options.focusKeep` which is reactive for application shells.
       *
       * @param {boolean}  focusKeep - Sets the focusKeep option.
       */
      set focusKeep(focusKeep) {
        if (typeof focusKeep === "boolean") {
          this.setOptions("focusKeep", focusKeep);
        }
      }
      /**
       * Sets `this.options.focusTrap` which is reactive for application shells.
       *
       * @param {boolean}  focusTrap - Sets the focusTrap option.
       */
      set focusTrap(focusTrap) {
        if (typeof focusTrap === "boolean") {
          this.setOptions("focusTrap", focusTrap);
        }
      }
      /**
       * Sets `this.options.headerButtonNoClose` which is reactive for application shells.
       *
       * @param {boolean}  headerButtonNoClose - Sets the headerButtonNoClose option.
       */
      set headerButtonNoClose(headerButtonNoClose) {
        if (typeof headerButtonNoClose === "boolean") {
          this.setOptions("headerButtonNoClose", headerButtonNoClose);
        }
      }
      /**
       * Sets `this.options.headerButtonNoLabel` which is reactive for application shells.
       *
       * @param {boolean}  headerButtonNoLabel - Sets the headerButtonNoLabel option.
       */
      set headerButtonNoLabel(headerButtonNoLabel) {
        if (typeof headerButtonNoLabel === "boolean") {
          this.setOptions("headerButtonNoLabel", headerButtonNoLabel);
        }
      }
      /**
       * Sets `this.options.headerIcon` which is reactive for application shells.
       *
       * @param {string | undefined}  headerIcon - Sets the headerButtonNoLabel option.
       */
      set headerIcon(headerIcon) {
        if (headerIcon === void 0 || typeof headerIcon === "string") {
          this.setOptions("headerIcon", headerIcon);
        }
      }
      /**
       * Sets `this.options.headerNoTitleMinimized` which is reactive for application shells.
       *
       * @param {boolean}  headerNoTitleMinimized - Sets the headerNoTitleMinimized option.
       */
      set headerNoTitleMinimized(headerNoTitleMinimized) {
        if (typeof headerNoTitleMinimized === "boolean") {
          this.setOptions("headerNoTitleMinimized", headerNoTitleMinimized);
        }
      }
      /**
       * Sets `this.options.minimizable` which is reactive for application shells that are also pop out.
       *
       * @param {boolean}  minimizable - Sets the minimizable option.
       */
      set minimizable(minimizable) {
        if (typeof minimizable === "boolean") {
          this.setOptions("minimizable", minimizable);
        }
      }
      /**
       * Sets `this.options.popOut` which is reactive for application shells. This will add / remove this application
       * from `ui.windows`.
       *
       * @param {boolean}  popOut - Sets the popOut option.
       */
      set popOut(popOut) {
        if (typeof popOut === "boolean") {
          this.setOptions("popOut", popOut);
        }
      }
      /**
       * Sets `this.options.positionable` enabling / disabling {@link SvelteApp.position}.
       *
       * @param {boolean}  positionable - Sets the positionable option.
       */
      set positionable(positionable) {
        if (typeof positionable === "boolean") {
          this.setOptions("positionable", positionable);
        }
      }
      /**
       * Sets `this.options.resizable` which is reactive for application shells.
       *
       * @param {boolean}  resizable - Sets the resizable option.
       */
      set resizable(resizable) {
        if (typeof resizable === "boolean") {
          this.setOptions("resizable", resizable);
        }
      }
      /**
       * Sets `this.options.title` which is reactive for application shells.
       *
       * Note: Will set empty string if title is undefined or null.
       *
       * @param {string | undefined | null}   title - Application title; will be localized, so a translation key is fine.
       */
      set title(title) {
        if (typeof title === "string") {
          this.setOptions("title", title);
        } else if (title === void 0 || title === null) {
          this.setOptions("title", "");
        }
      }
      // Reactive Options API -------------------------------------------------------------------------------------------
      /**
       * Provides a way to safely get this applications options given an accessor string which describes the
       * entries to walk. To access deeper entries into the object format the accessor string with `.` between entries
       * to walk.
       *
       * // TODO DOCUMENT the accessor in more detail.
       *
       * @param {string}   accessor - The path / key to set. You can set multiple levels.
       *
       * @param {*}        [defaultValue] - A default value returned if the accessor is not found.
       *
       * @returns {*} Value at the accessor.
       */
      getOptions(accessor, defaultValue) {
        return safeAccess(this.#application.options, accessor, defaultValue);
      }
      /**
       * Provides a way to merge `options` into this applications options and update the appOptions store.
       *
       * @param {object}   options - The options object to merge with `this.options`.
       */
      mergeOptions(options) {
        this.#storeAppOptionsUpdate((instanceOptions) => deepMerge(instanceOptions, options));
      }
      /**
       * Provides a way to safely set this applications options given an accessor string which describes the
       * entries to walk. To access deeper entries into the object format the accessor string with `.` between entries
       * to walk.
       *
       * Additionally, if an application shell Svelte component is mounted and exports the `appOptions` property then
       * the application options is set to `appOptions` potentially updating the application shell / Svelte component.
       *
       * @param {string}   accessor - The path / key to set. You can set multiple levels.
       *
       * @param {any}      value - Value to set.
       */
      setOptions(accessor, value) {
        const success = safeSet(this.#application.options, accessor, value, { createMissing: true });
        if (success) {
          this.#storeAppOptionsUpdate(() => this.#application.options);
        }
      }
      /**
       * Serializes the main {@link SvelteApp.Options} for common application state.
       *
       * @returns {import('../../types').SvelteApp.API.Reactive.Data} Common application state.
       */
      toJSON() {
        return {
          draggable: this.#application?.options?.draggable ?? true,
          focusAuto: this.#application?.options?.focusAuto ?? true,
          focusKeep: this.#application?.options?.focusKeep ?? false,
          focusTrap: this.#application?.options?.focusTrap ?? true,
          headerButtonNoClose: this.#application?.options?.headerButtonNoClose ?? false,
          headerButtonNoLabel: this.#application?.options?.headerButtonNoLabel ?? false,
          headerNoTitleMinimized: this.#application?.options?.headerNoTitleMinimized ?? false,
          minimizable: this.#application?.options?.minimizable ?? true,
          positionable: this.#application?.options?.positionable ?? true,
          resizable: this.#application?.options?.resizable ?? true
        };
      }
      /**
       * Updates the UI Options store with the current header buttons. You may dynamically add / remove header buttons
       * if using an application shell Svelte component. In either overriding `_getHeaderButtons` or responding to the
       * Hooks fired return a new button array and the uiOptions store is updated and the application shell will render
       * the new buttons.
       *
       * Optionally you can set in the SvelteApp app options {@link SvelteApp.Options.headerButtonNoClose}
       * to remove the close button and {@link SvelteApp.Options.headerButtonNoLabel} to true and labels will be
       * removed from the header buttons.
       *
       * @param {object} [opts] - Optional parameters (for internal use)
       *
       * @param {boolean} [opts.headerButtonNoClose] - The value for `headerButtonNoClose`.
       *
       * @param {boolean} [opts.headerButtonNoLabel] - The value for `headerButtonNoLabel`.
       */
      updateHeaderButtons({
        headerButtonNoClose = this.#application.options.headerButtonNoClose,
        headerButtonNoLabel = this.#application.options.headerButtonNoLabel
      } = {}) {
        let buttons = this.#application._getHeaderButtons();
        if (typeof headerButtonNoClose === "boolean" && headerButtonNoClose) {
          buttons = buttons.filter((button) => button.class !== "close");
        }
        if (typeof headerButtonNoLabel === "boolean" && headerButtonNoLabel) {
          for (const button of buttons) {
            button.label = void 0;
          }
        }
        this.#storeUIStateUpdate((options) => {
          options.headerButtons = buttons;
          return options;
        });
      }
      // Internal implementation ----------------------------------------------------------------------------------------
      /**
       * Initializes the Svelte stores and derived stores for the application options and UI state.
       *
       * While writable stores are created the update method is stored in private variables locally and derived Readable
       * stores are provided for essential options which are commonly used.
       *
       * These stores are injected into all Svelte components mounted under the `external` context: `storeAppOptions` and
       * `storeUIState`.
       */
      #storesInitialize() {
        const writableAppOptions = writable(this.#application.options);
        this.#storeAppOptionsUpdate = writableAppOptions.update;
        const storeAppOptions = {
          subscribe: writableAppOptions.subscribe,
          draggable: propertyStore(writableAppOptions, "draggable"),
          focusAuto: propertyStore(writableAppOptions, "focusAuto"),
          focusKeep: propertyStore(writableAppOptions, "focusKeep"),
          focusTrap: propertyStore(writableAppOptions, "focusTrap"),
          headerButtonNoClose: propertyStore(writableAppOptions, "headerButtonNoClose"),
          headerButtonNoLabel: propertyStore(writableAppOptions, "headerButtonNoLabel"),
          headerIcon: propertyStore(writableAppOptions, "headerIcon"),
          headerNoTitleMinimized: propertyStore(writableAppOptions, "headerNoTitleMinimized"),
          minimizable: propertyStore(writableAppOptions, "minimizable"),
          popOut: propertyStore(writableAppOptions, "popOut"),
          positionable: propertyStore(writableAppOptions, "positionable"),
          resizable: propertyStore(writableAppOptions, "resizable"),
          title: propertyStore(writableAppOptions, "title")
        };
        Object.freeze(storeAppOptions);
        this.#storeAppOptions = storeAppOptions;
        this.#dataUIState = {
          activeWindow: globalThis,
          dragging: false,
          headerButtons: [],
          minimized: this.#application._minimized,
          resizing: false
        };
        const writableUIOptions = writable(this.#dataUIState);
        this.#storeUIStateUpdate = writableUIOptions.update;
        const storeUIState = {
          subscribe: writableUIOptions.subscribe,
          // activeWindow: propertyStore(writableUIOptions, 'activeWindow'),
          activeWindow: derived(writableUIOptions, ($options, set) => set($options.activeWindow)),
          dragging: propertyStore(writableUIOptions, "dragging"),
          headerButtons: derived(writableUIOptions, ($options, set) => set($options.headerButtons)),
          minimized: derived(writableUIOptions, ($options, set) => set($options.minimized)),
          resizing: propertyStore(writableUIOptions, "resizing")
        };
        Object.freeze(storeUIState);
        this.#storeUIState = storeUIState;
      }
      /**
       * Registers local store subscriptions for app options. `popOut` controls registering this app with `ui.windows`.
       *
       * @see SvelteApp._injectHTML
       */
      #storesSubscribe() {
        this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.headerButtonNoClose, (value) => {
          this.updateHeaderButtons({ headerButtonNoClose: value });
        }));
        this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.headerButtonNoLabel, (value) => {
          this.updateHeaderButtons({ headerButtonNoLabel: value });
        }));
        this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.popOut, (value) => {
          if (value && this.#application.rendered) {
            globalThis.ui.windows[this.#application.appId] = this.#application;
          } else {
            delete globalThis.ui.windows[this.#application.appId];
          }
        }));
      }
      /**
       * Unsubscribes from any locally monitored stores.
       *
       * @see SvelteApp.close
       */
      #storesUnsubscribe() {
        this.#storeUnsubscribe.forEach((unsubscribe) => unsubscribe());
        this.#storeUnsubscribe = [];
      }
    }
    const applicationShellContract = ["elementRoot"];
    Object.freeze(applicationShellContract);
    function isApplicationShell(component) {
      if (component === null || component === void 0) {
        return false;
      }
      let compHasContract = true;
      let protoHasContract = true;
      for (const accessor of applicationShellContract) {
        const descriptor = Object.getOwnPropertyDescriptor(component, accessor);
        if (descriptor === void 0 || descriptor.get === void 0 || descriptor.set === void 0) {
          compHasContract = false;
        }
      }
      const prototype = Object.getPrototypeOf(component);
      for (const accessor of applicationShellContract) {
        const descriptor = Object.getOwnPropertyDescriptor(prototype, accessor);
        if (descriptor === void 0 || descriptor.get === void 0 || descriptor.set === void 0) {
          protoHasContract = false;
        }
      }
      return compHasContract || protoHasContract;
    }
    function loadSvelteConfig({ app, config, elementRootUpdate } = {}) {
      let target;
      if (CrossWindow.isHTMLElement(config.target)) {
        target = config.target;
      } else if (typeof config.target === "string") {
        const activeWindow = app?.reactive?.activeWindow;
        target = activeWindow?.document?.querySelector(config.target);
      }
      if (!CrossWindow.isHTMLElement(target)) {
        console.log(
          `%c[TRL] loadSvelteConfig error - Could not find target, '${config.target}', for config:
`,
          "background: rgb(57,34,34)",
          config
        );
        throw new Error();
      }
      const NewSvelteComponent = config.class;
      const svelteConfig = TJSSvelte.config.parseConfig({ ...config, target }, { contextExternal: true, thisArg: app });
      const externalContext = svelteConfig.context.get("#external");
      externalContext.application = app;
      externalContext.elementRootUpdate = elementRootUpdate;
      externalContext.sessionStorage = app.reactive.sessionStorage;
      let eventbus;
      if (isObject(app._eventbus) && typeof app._eventbus.createProxy === "function") {
        eventbus = app._eventbus.createProxy();
        externalContext.eventbus = eventbus;
      }
      Object.seal(externalContext);
      const component = new NewSvelteComponent(svelteConfig);
      svelteConfig.eventbus = eventbus;
      let element2;
      if (isApplicationShell(component)) {
        element2 = component.elementRoot;
      }
      if (!CrossWindow.isHTMLElement(element2)) {
        console.log(
          `%c[TRL] loadSvelteConfig error - No application shell contract found. Did you bind and export a HTMLElement as 'elementRoot' and include '<svelte:options accessors={true}/>'?

Offending config:
`,
          "background: rgb(57,34,34)",
          config
        );
        throw new Error();
      }
      return { config: svelteConfig, component, element: element2 };
    }
    class TJSAppIndex {
      /**
       * Stores all visible / rendered apps.
       *
       * @type {Map<string, import('@typhonjs-fvtt/runtime/svelte/application').SvelteApp>}
       */
      static #visibleApps = /* @__PURE__ */ new Map();
      /**
       * Adds a SvelteApp to all visible apps tracked.
       *
       * @param {import('@typhonjs-fvtt/runtime/svelte/application').SvelteApp} app - A SvelteApp
       *
       * @package
       */
      static add(app) {
        this.#visibleApps.set(app.id, app);
      }
      /**
       * Removes a SvelteApp from all visible apps tracked.
       *
       * @param {import('@typhonjs-fvtt/runtime/svelte/application').SvelteApp} app - A SvelteApp
       *
       * @package
       */
      static delete(app) {
        this.#visibleApps.delete(app.id);
      }
      /**
       * Gets a particular app by ID.
       *
       * @param {string}   key - App ID.
       *
       * @returns {import('@typhonjs-fvtt/runtime/svelte/application').SvelteApp} Associated app.
       */
      static get(key) {
        return this.#visibleApps.get(key);
      }
      /**
       * Returns whether an associated app by ID is being tracked.
       *
       * @param {string}   key - App ID.
       *
       * @returns {boolean} The given App ID is visible.
       */
      static has(key) {
        return this.#visibleApps.has(key);
      }
      /**
       * @returns {IterableIterator<string>} All visible app IDs.
       */
      static keys() {
        return this.#visibleApps.keys();
      }
      /**
       * @returns {IterableIterator<import('@typhonjs-fvtt/runtime/svelte/application').SvelteApp>} All visible apps.
       */
      static values() {
        return this.#visibleApps.values();
      }
    }
    class SvelteApp extends Application {
      /**
       * Stores the first mounted component which follows the application shell contract.
       *
       * @type {import('svelte').SvelteComponent[] | null[]} Application shell.
       */
      #applicationShellHolder = [null];
      /**
       * Stores and manages application state for saving / restoring / serializing.
       *
       * @type {import('./types').SvelteApp.API.State}
       */
      #applicationState;
      /**
       * Stores the target element which may not necessarily be the main element.
       *
       * @type {HTMLElement}
       */
      #elementTarget = null;
      /**
       * Stores the content element which is set for application shells.
       *
       * @type {HTMLElement}
       */
      #elementContent = null;
      /**
       * On initial render gating of `setPosition` invoked by `Application._render` occurs, so that percentage values
       * can correctly be positioned with initial helper constraints (centered).
       *
       * @type {boolean}
       */
      #gateSetPosition = false;
      /**
       * Stores initial z-index from `_renderOuter` to set to target element / Svelte component.
       *
       * @type {number}
       */
      #initialZIndex = 95;
      /**
       * Stores on mount state which is checked in _render to trigger onSvelteMount callback.
       *
       * @type {boolean}
       */
      #onMount = false;
      /**
       * The position store.
       *
       * @type {TJSPosition}
       */
      #position;
      /**
       * Contains the Svelte stores and reactive accessors.
       *
       * @type {SvelteReactive}
       */
      #reactive;
      /**
       * Stores SvelteData entries with instantiated Svelte components.
       *
       * @type {import('./internal/state-svelte/types').SvelteData[] | null[]}
       */
      #svelteData = [null];
      /**
       * Provides a helper class that combines multiple methods for interacting with the mounted components tracked in
       * #svelteData.
       *
       * @type {import('./types').SvelteApp.API.Svelte<Options>}
       */
      #getSvelteData = new GetSvelteData(this.#applicationShellHolder, this.#svelteData);
      /**
       * Contains methods to interact with the Svelte stores.
       *
       * @type {import('./internal/state-reactive/types-local').SvelteReactiveStores}
       */
      #stores;
      /**
       * @param {Partial<import('./types').SvelteApp.Options>} [options] - The options for the application.
       */
      constructor(options = {}) {
        super(options);
        if (!isObject(this.options.svelte)) {
          throw new Error(`SvelteApp - constructor - No Svelte configuration object found in 'options'.`);
        }
        this.#applicationState = new ApplicationState(this);
        this.#position = new TJSPosition(this, {
          ...this.position,
          ...this.options,
          initial: this.options.positionInitial,
          ortho: this.options.positionOrtho,
          validator: this.options.positionValidator
        });
        delete this.position;
        Object.defineProperty(this, "position", {
          get: () => this.#position,
          set: (position) => {
            if (isObject(position)) {
              this.#position.set(position);
            }
          }
        });
        this.#reactive = new SvelteReactive(this);
        this.#stores = this.#reactive.initialize();
      }
      /**
       * Specifies the default options that SvelteApp supports.
       *
       * @returns {import('./types').SvelteApp.Options} options - Application options.
       * @see https://foundryvtt.com/api/interfaces/client.ApplicationOptions.html
       */
      static get defaultOptions() {
        return (
          /** @type {import('./types').SvelteApp.Options} */
          deepMerge(super.defaultOptions, {
            defaultCloseAnimation: true,
            // If false the default slide close animation is not run.
            draggable: true,
            // If true then application shells are draggable.
            focusAuto: true,
            // When true auto-management of app focus is enabled.
            focusKeep: false,
            // When `focusAuto` and `focusKeep` is true; keeps internal focus.
            focusSource: void 0,
            // Stores any A11yFocusSource data that is applied when app is closed.
            focusTrap: true,
            // When true focus trapping / wrapping is enabled keeping focus inside app.
            headerButtonNoClose: false,
            // If true then the close header button is removed.
            headerButtonNoLabel: false,
            // If true then header button labels are removed for application shells.
            headerIcon: void 0,
            // Sets a header icon given an image URL.
            headerNoTitleMinimized: false,
            // If true then header title is hidden when application is minimized.
            minHeight: MIN_WINDOW_HEIGHT,
            // Assigned to position. Number specifying minimum window height.
            minWidth: MIN_WINDOW_WIDTH,
            // Assigned to position. Number specifying minimum window width.
            positionable: true,
            // If false then `position.set` does not take effect.
            positionInitial: TJSPosition.Initial.browserCentered,
            // A helper for initial position placement.
            positionOrtho: true,
            // When true TJSPosition is optimized for orthographic use.
            positionValidator: TJSPosition.Validators.transformWindow,
            // A function providing the default validator.
            sessionStorage: void 0,
            // An instance of WebStorage (session) to share across SvelteApps.
            svelte: void 0,
            // A Svelte configuration object.
            transformOrigin: "top left"
            // By default, 'top / left' respects rotation when minimizing.
          })
        );
      }
      /**
       * Returns the content element if an application shell is mounted.
       *
       * @returns {HTMLElement} Content element.
       */
      get elementContent() {
        return this.#elementContent;
      }
      /**
       * Returns the target element or main element if no target defined.
       *
       * @returns {HTMLElement} Target element.
       */
      get elementTarget() {
        return this.#elementTarget;
      }
      /**
       * Returns the reactive accessors & Svelte stores for SvelteApp.
       *
       * @returns {import('./types').SvelteApp.API.Reactive} The reactive accessors & Svelte stores.
       */
      get reactive() {
        return this.#reactive;
      }
      /**
       * Returns the application state manager.
       *
       * @returns {import('./types').SvelteApp.API.State} The application state manager.
       */
      get state() {
        return this.#applicationState;
      }
      /**
       * Returns the `Svelte` helper class w/ various methods to access the mounted application shell component.
       *
       * @returns {import('./types').SvelteApp.API.Svelte<Options>} `Svelte` / mounted application shell API.
       */
      get svelte() {
        return this.#getSvelteData;
      }
      /**
       * In this case of when a template is defined in app options `html` references the inner HTML / template. However,
       * to activate classic v1 tabs for a Svelte component the element target is passed as an array simulating JQuery as
       * the element is retrieved immediately and the core listeners use standard DOM queries.
       *
       * @protected
       * @ignore
       * @internal
       */
      _activateCoreListeners(html) {
        super._activateCoreListeners(typeof this.options.template === "string" ? html : [this.popOut ? this.#elementTarget?.firstChild : this.#elementTarget]);
      }
      /**
       * Provide an override to set this application as the active window regardless of z-index. Changes behaviour from
       * Foundry core.
       *
       * @param {object} [opts] - Optional parameters.
       *
       * @param {boolean} [opts.focus=true] - When true and the active element is not contained in the app `elementTarget`
       *        is focused..
       *
       * @param {boolean} [opts.force=false] - Force bring to top; will increment z-index by popOut order.
       *
       * @ignore
       * @internal
       */
      bringToTop({ focus = true, force = false } = {}) {
        if (this.reactive.activeWindow !== globalThis) {
          return;
        }
        if (force || this.popOut) {
          super.bringToTop();
        }
        const elementTarget = this.elementTarget;
        const activeElement = document.activeElement;
        if (focus && elementTarget && activeElement !== elementTarget && !elementTarget?.contains(activeElement)) {
          if (A11yHelper.isFocusTarget(activeElement)) {
            activeElement.blur();
          }
          elementTarget?.focus();
        }
        globalThis.ui.activeWindow = this;
      }
      /**
       * Note: This method is fully overridden and duplicated as Svelte components need to be destroyed manually and the
       * best visual result is to destroy them after the default slide up animation occurs, but before the element
       * is removed from the DOM.
       *
       * If you destroy the Svelte components before the slide up animation the Svelte elements are removed immediately
       * from the DOM. The purpose of overriding ensures the slide up animation is always completed before
       * the Svelte components are destroyed and then the element is removed from the DOM.
       *
       * Close the application and unregisters references to it within UI mappings.
       * This function returns a Promise which resolves once the window closing animation concludes.
       *
       * @param {object}   [options] - Optional parameters.
       *
       * @param {boolean}  [options.force] - Force close regardless of render state.
       *
       * @returns {Promise<void>}    A Promise which resolves once the application is closed.
       *
       * @ignore
       * @internal
       */
      async close(options = {}) {
        const states = Application.RENDER_STATES;
        if (!options.force && ![states.RENDERED, states.ERROR].includes(this._state)) {
          return;
        }
        const el = this.#elementTarget;
        if (!el) {
          this._state = states.CLOSED;
          return;
        }
        if (CrossWindow.getWindow(el, { throws: false }) !== globalThis) {
          return;
        }
        this._state = states.CLOSING;
        this.#stores.unsubscribe();
        const content = el.querySelector(".window-content");
        if (content) {
          content.style.overflow = "hidden";
          for (let cntr = content.children.length; --cntr >= 0; ) {
            content.children[cntr].style.overflow = "hidden";
          }
        }
        for (const cls of this.constructor._getInheritanceChain()) {
          Hooks.call(`close${cls.name}`, this, $(el));
        }
        const animate = typeof this.options.defaultCloseAnimation === "boolean" ? this.options.defaultCloseAnimation : true;
        if (animate) {
          el.style.minHeight = "0";
          const { paddingBottom, paddingTop } = globalThis.getComputedStyle(el);
          await el.animate([
            { maxHeight: `${el.clientHeight}px`, paddingTop, paddingBottom },
            { maxHeight: 0, paddingTop: 0, paddingBottom: 0 }
          ], { duration: 250, easing: "ease-in", fill: "forwards" }).finished;
        }
        const svelteDestroyPromises = [];
        for (const entry of this.#svelteData) {
          if (!isObject(entry)) {
            continue;
          }
          svelteDestroyPromises.push(TJSSvelte.util.outroAndDestroy(entry.component));
          const eventbus = entry.config.eventbus;
          if (isObject(eventbus) && typeof eventbus.off === "function") {
            eventbus.off();
            entry.config.eventbus = void 0;
          }
        }
        await Promise.allSettled(svelteDestroyPromises);
        TJSAppIndex.delete(this);
        this.#svelteData[0] = null;
        el.remove();
        this.position.state.restore({
          name: "#beforeMinimized",
          properties: ["width", "height"],
          silent: true,
          remove: true
        });
        this.#applicationShellHolder[0] = null;
        this._element = null;
        this.#elementContent = null;
        this.#elementTarget = null;
        delete globalThis.ui.windows[this.appId];
        this._minimized = false;
        this._scrollPositions = null;
        this._state = states.CLOSED;
        this.#onMount = false;
        this.#stores.uiStateUpdate((storeOptions) => deepMerge(storeOptions, { minimized: this._minimized }));
        A11yHelper.applyFocusSource(this.options.focusSource);
        delete this.options.focusSource;
      }
      /**
       * Specify the set of config buttons which should appear in the SvelteApp header. Buttons should be returned as
       * an Array of objects. The header buttons which are added to the application can be modified by the
       * `getApplicationHeaderButtons` hook.
       *
       * SvelteApp extends the button functionality with full reactivity for state changes during callbacks. Callbacks
       * receive the button data and can modify it to update the button state.
       *
       * @privateRemarks Provide a basic override implementation to extend types with additional SvelteApp functionality.
       *
       * @returns {import('./types').SvelteApp.HeaderButton[]} All header buttons.
       * @protected
       */
      _getHeaderButtons() {
        return super._getHeaderButtons();
      }
      /**
       * Inject the Svelte components defined in `this.options.svelte`. The Svelte component can attach to the existing
       * pop-out of Application or provide no template and render into a document fragment which is then attached to the
       * DOM.
       *
       * @protected
       * @ignore
       * @internal
       */
      _injectHTML() {
        this.reactive.updateHeaderButtons();
        const elementRootUpdate = () => {
          let cntr = 0;
          return (elementRoot) => {
            if (elementRoot !== null && elementRoot !== void 0 && cntr++ > 0) {
              this.#updateApplicationShell();
              return true;
            }
            return false;
          };
        };
        if (!isObject(this.options.svelte)) {
          throw new Error(`SvelteApp - _injectHTML - No Svelte configuration object found in 'options'.`);
        }
        const svelteData = loadSvelteConfig({
          app: this,
          config: this.options.svelte,
          elementRootUpdate
        });
        if (this.svelte.appShell !== null) {
          throw new Error(
            `SvelteApp - _injectHTML - An application shell is already mounted; offending config:
${JSON.stringify(this.options.svelte)}`
          );
        }
        this.#applicationShellHolder[0] = svelteData.component;
        if (TJSSvelte.util.isHMRProxy(svelteData.component) && Array.isArray(svelteData.component?.$$?.on_hmr)) {
          svelteData.component.$$.on_hmr.push(() => () => this.#updateApplicationShell());
        }
        this.#svelteData[0] = svelteData;
        this._element = $(this.svelte.appShell.elementRoot);
        this.#elementContent = hasGetter(this.svelte.appShell, "elementContent") ? this.svelte.appShell.elementContent : null;
        this.#elementTarget = hasGetter(this.svelte.appShell, "elementTarget") ? this.svelte.appShell.elementTarget : this.svelte.appShell.elementRoot;
        if (typeof this.options.positionable === "boolean" && this.options.positionable) {
          this.#elementTarget.style.zIndex = typeof this.options.zIndex === "number" ? this.options.zIndex : this.#initialZIndex ?? 95;
        }
        this.#stores.subscribe();
      }
      /**
       * Provides a mechanism to update the UI options store for maximized.
       *
       * Note: the sanity check is duplicated from {@link Application.maximize} the store is updated _before_
       * performing the rest of animations. This allows application shells to remove / show any resize handlers
       * correctly. Extra constraint data is stored in a saved position state in {@link SvelteApp.minimize}
       * to animate the content area.
       *
       * @param {object}   [opts] - Optional parameters.
       *
       * @param {boolean}  [opts.animate=true] - When true perform default maximizing animation.
       *
       * @param {number}   [opts.duration=0.1] - Controls content area animation duration in seconds.
       */
      async maximize({ animate = true, duration = 0.1 } = {}) {
        if (!this.popOut || [false, null].includes(this._minimized)) {
          return;
        }
        this._minimized = null;
        const durationMS = duration * 1e3;
        const element2 = this.elementTarget;
        const header = element2.querySelector(".window-header");
        const content = element2.querySelector(".window-content");
        const positionBefore = this.position.state.get({ name: "#beforeMinimized" });
        if (animate) {
          await this.position.state.restore({
            name: "#beforeMinimized",
            async: true,
            animateTo: true,
            properties: ["width"],
            duration: 0.1
          });
        }
        element2.classList.remove("minimized");
        for (let cntr = header.children.length; --cntr >= 0; ) {
          header.children[cntr].style.display = null;
        }
        content.style.display = null;
        let constraints;
        if (animate) {
          ({ constraints } = this.position.state.restore({
            name: "#beforeMinimized",
            animateTo: true,
            properties: ["height"],
            remove: true,
            duration
          }));
        } else {
          ({ constraints } = this.position.state.remove({ name: "#beforeMinimized" }));
        }
        await content.animate([
          { maxHeight: 0, paddingTop: 0, paddingBottom: 0, offset: 0 },
          { ...constraints, offset: 1 },
          { maxHeight: "100%", offset: 1 }
        ], { duration: durationMS, fill: "forwards" }).finished;
        this.position.set({
          minHeight: positionBefore.minHeight ?? this.options?.minHeight ?? MIN_WINDOW_HEIGHT,
          minWidth: positionBefore.minWidth ?? this.options?.minWidth ?? MIN_WINDOW_WIDTH
        });
        element2.style.minWidth = null;
        element2.style.minHeight = null;
        this._minimized = false;
        setTimeout(() => {
          content.style.overflow = null;
          for (let cntr = content.children.length; --cntr >= 0; ) {
            content.children[cntr].style.overflow = null;
          }
        }, 50);
        this.#stores.uiStateUpdate((options) => deepMerge(options, { minimized: false }));
      }
      /**
       * Provides a mechanism to update the UI options store for minimized.
       *
       * Note: the sanity check is duplicated from {@link Application.minimize} the store is updated _before_
       * performing the rest of animations. This allows application shells to remove / show any resize handlers
       * correctly. Extra constraint data is stored in a saved position state in {@link SvelteApp.minimize}
       * to animate the content area.
       *
       * @param {object}   [opts] - Optional parameters.
       *
       * @param {boolean}  [opts.animate=true] - When true perform default minimizing animation.
       *
       * @param {number}   [opts.duration=0.1] - Controls content area animation duration in seconds.
       */
      async minimize({ animate = true, duration = 0.1 } = {}) {
        if (!this.rendered || !this.popOut || [true, null].includes(this._minimized)) {
          return;
        }
        this.#stores.uiStateUpdate((options) => deepMerge(options, { minimized: true }));
        this._minimized = null;
        const durationMS = duration * 1e3;
        const element2 = this.elementTarget;
        const header = element2.querySelector(".window-header");
        const content = element2.querySelector(".window-content");
        const beforeMinWidth = this.position.minWidth;
        const beforeMinHeight = this.position.minHeight;
        this.position.set({ minWidth: 100, minHeight: 30 });
        element2.style.minWidth = "100px";
        element2.style.minHeight = "30px";
        if (content) {
          content.style.overflow = "hidden";
          for (let cntr = content.children.length; --cntr >= 0; ) {
            content.children[cntr].style.overflow = "hidden";
          }
        }
        const { paddingBottom, paddingTop } = globalThis.getComputedStyle(content);
        const constraints = {
          maxHeight: `${content.clientHeight}px`,
          paddingTop,
          paddingBottom
        };
        if (animate) {
          const animation2 = content.animate([
            constraints,
            { maxHeight: 0, paddingTop: 0, paddingBottom: 0 }
          ], { duration: durationMS, fill: "forwards" });
          animation2.finished.then(() => content.style.display = "none");
        } else {
          setTimeout(() => content.style.display = "none", durationMS);
        }
        const saved = this.position.state.save({ name: "#beforeMinimized", constraints });
        saved.minWidth = beforeMinWidth;
        saved.minHeight = beforeMinHeight;
        const headerOffsetHeight = header.offsetHeight;
        this.position.minHeight = headerOffsetHeight;
        if (animate) {
          await this.position.animate.to({ height: headerOffsetHeight }, { duration }).finished;
        }
        for (let cntr = header.children.length; --cntr >= 0; ) {
          const className = header.children[cntr].className;
          if (className.includes("window-title") || className.includes("close")) {
            continue;
          }
          if (className.includes("keep-minimized")) {
            header.children[cntr].style.display = "block";
            continue;
          }
          header.children[cntr].style.display = "none";
        }
        if (animate) {
          await this.position.animate.to({ width: MIN_WINDOW_WIDTH }, { duration: 0.1 }).finished;
        }
        element2.classList.add("minimized");
        this._minimized = true;
      }
      /**
       * Provides a callback after all Svelte components are initialized.
       */
      onSvelteMount() {
      }
      // eslint-disable-line no-unused-vars
      /**
       * Provides a callback after the main application shell is remounted. This may occur during HMR / hot module
       * replacement or directly invoked from the `elementRootUpdate` callback passed to the application shell component
       * context.
       */
      onSvelteRemount() {
      }
      // eslint-disable-line no-unused-vars
      /**
       * Override replacing HTML as Svelte components control the rendering process. Only potentially change the outer
       * application frame / title for pop-out applications.
       *
       * @protected
       * @ignore
       * @internal
       */
      _replaceHTML(element2, html) {
        if (!element2.length) {
          return;
        }
        this.reactive.updateHeaderButtons();
      }
      /**
       * Provides an override verifying that a new Application being rendered for the first time doesn't have a
       * corresponding DOM element already loaded. This is a check that only occurs when `this._state` is
       * `Application.RENDER_STATES.NONE`. It is useful in particular when SvelteApp has a static ID
       * explicitly set in `this.options.id` and long intro / outro transitions are assigned. If a new application
       * sharing this static ID attempts to open / render for the first time while an existing DOM element sharing
       * this static ID exists then the initial render is cancelled below rather than crashing later in the render
       * cycle {@link TJSPosition.set}.
       *
       * @protected
       * @ignore
       * @internal
       */
      async _render(force = false, options = {}) {
        if (isObject(options?.focusSource)) {
          this.options.focusSource = options.focusSource;
        }
        const activeWindow = this.reactive.activeWindow;
        try {
          if (this._state === Application.RENDER_STATES.NONE && A11yHelper.isFocusTarget(activeWindow.document.querySelector(`#${this.id}`))) {
            console.warn(`SvelteApp - _render: A DOM element already exists for CSS ID '${this.id}'. Cancelling initial render for new application with appId '${this.appId}'.`);
            return;
          }
        } catch (err) {
          console.warn(`SvelteApp - _render: Potentially malformed application ID '${this.id}'. Cancelling initial render for new application with appId '${this.appId}'.`);
          return;
        }
        this.#gateSetPosition = true;
        await super._render(force, options);
        this.#gateSetPosition = false;
        if ([Application.RENDER_STATES.CLOSING, Application.RENDER_STATES.RENDERING].includes(this._state)) {
          return;
        }
        if (!force && this._state <= Application.RENDER_STATES.NONE) {
          return;
        }
        if (!this._minimized) {
          this.#position.set({
            left: typeof this.options?.left === "string" ? this.options.left : void 0,
            height: typeof this.options?.height === "string" ? this.options.height : void 0,
            maxHeight: typeof this.options?.maxHeight === "string" ? this.options.maxHeight : void 0,
            maxWidth: typeof this.options?.maxWidth === "string" ? this.options.maxWidth : void 0,
            minHeight: typeof this.options?.minHeight === "string" ? this.options.minHeight : void 0,
            minWidth: typeof this.options?.minWidth === "string" ? this.options.minWidth : void 0,
            rotateX: typeof this.options?.rotateX === "string" ? this.options.rotateX : void 0,
            rotateY: typeof this.options?.rotateY === "string" ? this.options.rotateY : void 0,
            rotateZ: typeof this.options?.rotateZ === "string" ? this.options.rotateZ : void 0,
            rotation: typeof this.options?.rotation === "string" ? this.options.rotation : void 0,
            top: typeof this.options?.top === "string" ? this.options.top : void 0,
            width: typeof this.options?.width === "string" ? this.options.width : void 0,
            ...options
          });
        }
        if (!this.#onMount) {
          TJSAppIndex.add(this);
          this.onSvelteMount();
          this.#onMount = true;
        }
      }
      /**
       * Render the inner application content. Provide an empty JQuery element per the core Foundry API.
       *
       * @protected
       * @ignore
       * @internal
       */
      async _renderInner() {
        const activeWindow = this.reactive.activeWindow;
        const html = activeWindow.document.createDocumentFragment();
        return $(html);
      }
      /**
       * Stores the initial z-index set in `_renderOuter` which is used in `_injectHTML` to set the target element
       * z-index after the Svelte component is mounted.
       *
       * @protected
       * @ignore
       * @internal
       */
      async _renderOuter() {
        const html = await super._renderOuter();
        this.#initialZIndex = html[0].style.zIndex;
        return html;
      }
      /**
       * All calculation and updates of position are implemented in {@link TJSPosition.set}.
       * This allows position to be fully reactive and in control of updating inline styles for the application.
       *
       * This method remains for backward compatibility with Foundry. If you have a custom override quite likely you need
       * to update to using the {@link TJSPosition.validators} / ValidatorAPI functionality.
       *
       * @param {TJSPosition.API.Data.TJSPositionDataRelative}   [position] - TJSPosition data.
       *
       * @returns {TJSPosition} The updated position object for the application containing the new values.
       * @ignore
       */
      setPosition(position) {
        return !this.#gateSetPosition ? this.position.set(position) : this.position;
      }
      /**
       * This method is invoked by the `elementRootUpdate` callback that is added to the external context passed to
       * Svelte components. When invoked it updates the local element roots tracked by SvelteApp.
       *
       * This method may also be invoked by HMR / hot module replacement via `svelte-hmr`.
       */
      #updateApplicationShell() {
        const applicationShell = this.svelte.appShell;
        if (applicationShell !== null) {
          this._element = $(applicationShell.elementRoot);
          this.#elementContent = hasGetter(applicationShell, "elementContent") ? applicationShell.elementContent : null;
          this.#elementTarget = hasGetter(applicationShell, "elementTarget") ? applicationShell.elementTarget : null;
          if (this.#elementTarget === null) {
            this.#elementTarget = typeof this.options.selectorTarget === "string" ? this._element[0].querySelector(this.options.selectorTarget) : this._element[0];
          }
          if (typeof this.options.positionable === "boolean" && this.options.positionable) {
            this.#elementTarget.style.zIndex = typeof this.options.zIndex === "number" ? this.options.zIndex : this.#initialZIndex ?? 95;
            super.bringToTop();
            this.position.set(this.position.get());
          }
          super._activateCoreListeners([this.popOut ? this.#elementTarget?.firstChild : this.#elementTarget]);
          this.onSvelteRemount();
        }
      }
    }
    class PopoutSupport {
      static initialize() {
        Hooks.on("PopOut:loading", (app, popout) => {
          if (app instanceof SvelteApp) {
            app.position.enabled = false;
            app.state.save({
              name: "#beforePopout",
              headerButtonNoClose: app.reactive.headerButtonNoClose
            });
            app.reactive.activeWindow = popout;
            app.reactive.headerButtonNoClose = true;
          }
        });
        Hooks.on("PopOut:popin", (app) => this.#handleRejoin(app));
        Hooks.on("PopOut:close", (app) => this.#handleRejoin(app));
      }
      /**
       * Handles rejoining the app to main browser window.
       *
       * @param {Application} app - The target app.
       */
      static #handleRejoin(app) {
        if (app instanceof SvelteApp) {
          app.position.enabled = true;
          const beforeData = app.state.remove({ name: "#beforePopout" });
          if (beforeData) {
            app.reactive.headerButtonNoClose = beforeData?.headerButtonNoClose ?? false;
          }
          app.reactive.activeWindow = void 0;
        }
      }
    }
    PopoutSupport.initialize();
    class loading_bar {
      constructor() {
        this.total = 0;
        this.current = 0;
        this.lastPct = 0;
      }
      init(context, total) {
        this.context = context;
        this.total = total;
        this.current = 0;
        this.lastPct = 0;
        SceneNavigation.displayProgressBar({ label: this.context, pct: 1 });
      }
      incrementProgress() {
        this.current += 1;
        const pct = Math.round(this.current / this.total * 100);
        if (pct !== this.lastPct) {
          debug(`${pct}% loaded...`);
          SceneNavigation.displayProgressBar({ label: this.context, pct });
        }
        this.lastPct = pct;
      }
    }
    const LoadingBar = new loading_bar();
    const SequencerFileCache = {
      _videos: {},
      _preloadedFiles: /* @__PURE__ */ new Set(),
      _totalCacheSize: 0,
      _validTypes: ["video/webm", "video/x-webm", "application/octet-stream", "binary/octet-stream"],
      /** @type {Map<string, PIXI.Spritesheet>} */
      _spritesheets: /* @__PURE__ */ new Map(),
      /** @type {Map<string, Promise<{spritesheet: PIXI.Spritesheet, scale: number}> | null>} */
      _generateSpritesheetJobs: /* @__PURE__ */ new Map(),
      /** @type {Promise<import("../lib/spritesheets/SpritesheetGenerator.js").SpritesheetGenerator> | null} */
      _spritesheetGenerator: null,
      /**
       *
       * @param {string} inSrc
       * @returns {Promise<Blob>} the video blob
       */
      async loadVideo(inSrc) {
        if (!this._videos[inSrc]) {
          const blob = await fetch(inSrc, {
            mode: "cors",
            credentials: "same-origin"
          }).then((r) => r.blob()).catch((err) => {
            console.error(err);
          });
          if (this._validTypes.indexOf(blob?.type) === -1) return false;
          while (this._totalCacheSize + blob.size > 524288e3) {
            const entries = Object.entries(this._videos);
            entries.sort((a, b) => {
              return b[1].lastUsed - a[1].lastUsed;
            });
            const [oldSrc] = entries[0];
            this._preloadedFiles.delete(oldSrc);
            this._totalCacheSize -= this._videos[oldSrc].blob.size;
            delete this._videos[oldSrc];
          }
          this._totalCacheSize += blob.size;
          this._preloadedFiles.add(inSrc);
          this._videos[inSrc] = {
            blob,
            lastUsed: +/* @__PURE__ */ new Date()
          };
        }
        this._videos[inSrc].lastUsed = +/* @__PURE__ */ new Date();
        return this._videos[inSrc].blob;
      },
      srcExists(inSrc) {
        if (this._preloadedFiles.has(inSrc)) {
          return true;
        }
        return srcExists(inSrc);
      },
      async loadFile(inSrc, preload = false) {
        if (inSrc.toLowerCase().endsWith(".webm")) {
          let blob = await this.loadVideo(inSrc);
          if (!blob) return false;
          this._preloadedFiles.add(inSrc);
          if (preload) return true;
          return get_video_texture(blob);
        } else if (foundry.audio.AudioHelper.hasAudioExtension(inSrc)) {
          try {
            const audio2 = await foundry.audio.AudioHelper.preloadSound(inSrc);
            if (audio2) {
              this._preloadedFiles.add(inSrc);
            }
            return audio2;
          } catch (err) {
            console.error(`Failed to load audio: ${inSrc}`);
            return false;
          }
        }
        const texture = await loadTexture(inSrc);
        if (texture) {
          this._preloadedFiles.add(inSrc);
        }
        return texture;
      },
      /**
       * @param {string} inSrc
       * @param {Object} options
       * @param {number} options.minimumScale
       * @return {Promise<PIXI.Spritesheet | undefined>} the compiled spritesheet or
       * undefined if it cannot be generated
       */
      async requestCompiledSpritesheet(inSrc, { minimumScale }) {
        if (!inSrc.toLowerCase().endsWith(".webm")) {
          return;
        }
        if (this._spritesheetGenerator == null) {
          this._spritesheetGenerator = import("./SpritesheetGenerator-n0EQ8ruS.js").then((m) => m.SpritesheetGenerator.create());
        }
        const generator = await this._spritesheetGenerator;
        if (!generator) {
          return;
        }
        let job = this._generateSpritesheetJobs.get(inSrc);
        if (!job) {
          job = new Promise(async (resolve) => {
            const timeStart = Date.now();
            debug(
              `Spritesheets | generating ${inSrc}. Required scale ${minimumScale}`
            );
            const blob = await this.loadVideo(inSrc);
            const buffer = await blob.arrayBuffer();
            let spritesheet = null;
            let scale2 = 1;
            try {
              const result2 = await generator.spritesheetFromBuffer({
                buffer,
                id: inSrc,
                minimumScale
              });
              spritesheet = result2.spritesheet;
              scale2 = result2.scale;
            } catch (error) {
              console.warn(error);
              resolve(null);
              return;
            }
            const w = spritesheet.baseTexture.width;
            const h = spritesheet.baseTexture.height;
            const megaBytes = Math.round(
              spritesheet.baseTexture.resource._levelBuffers.reduce(
                (acc, cur) => acc + cur.levelBuffer.byteLength,
                0
              ) / 1e5
            ) / 10;
            const time2 = Math.round((Date.now() - timeStart) / 100) / 10;
            debug(
              `Spritesheets | ${inSrc} generated in ${time2}s. ${w}x${h} ${megaBytes}mb`
            );
            resolve({ spritesheet, scale: scale2 });
          });
          this._generateSpritesheetJobs.set(inSrc, job);
        }
        const result = await job;
        if (result && result.scale < minimumScale) {
          debug(
            `Spritesheets | ${inSrc} minimum scale of ${minimumScale} requested but only scale ${result.scale} is available`
          );
          return void 0;
        }
        return result?.spritesheet;
      },
      registerSpritesheet(inSrc, inSpriteSheet) {
        const existingSheetRef = this._spritesheets.get(inSrc);
        if (existingSheetRef) {
          existingSheetRef[1] += 1;
        } else {
          this._spritesheets.set(inSrc, [inSpriteSheet, 1]);
        }
      },
      /**
       *
       * @param {string} inSrc
       * @returns {void}
       */
      async unloadSpritesheet(inSrc) {
        const existingSheetRef = this._spritesheets.get(inSrc);
        if (!existingSheetRef) {
          console.error("trying to unlaod spritesheet that was not loaded:", inSrc);
          return;
        }
        existingSheetRef[1] -= 1;
        if (existingSheetRef[1] > 0) {
          return;
        }
        this._generateSpritesheetJobs.delete(inSrc);
        this._spritesheets.delete(inSrc);
        const sheet = existingSheetRef[0];
        sheet.data?.meta?.related_multi_packs ?? [];
        sheet.linkedSheets;
        const cacheKeys = [
          get_sheet_image_url(inSrc, sheet),
          foundry.utils.getRoute(inSrc)
        ];
        await PIXI.Assets.unload(cacheKeys.filter((src) => !!src));
        if (sheet.textures) {
          Object.values(sheet.textures).forEach((t) => t.destroy());
        }
        sheet.baseTexture?.destroy();
      }
    };
    function get_sheet_image_url(inSrc, sheet) {
      const imageName = sheet?.data?.meta?.image;
      if (!imageName) {
        return;
      }
      const srcPrefix = inSrc.split("/").slice(0, -1).join("/");
      const sheetSrc = `${srcPrefix}/${imageName}`;
      return foundry.utils.getRoute(sheetSrc);
    }
    async function get_video_texture(inBlob) {
      return new Promise(async (resolve, reject) => {
        const video = document.createElement("video");
        video.preload = "auto";
        video.crossOrigin = "anonymous";
        video.controls = true;
        video.autoplay = false;
        video.autoload = true;
        video.muted = true;
        video.src = URL.createObjectURL(inBlob);
        let canplay = true;
        video.oncanplay = async () => {
          if (!canplay) return;
          canplay = false;
          video.height = video.videoHeight;
          video.width = video.videoWidth;
          const baseTexture = PIXI.BaseTexture.from(video, {
            resourceOptions: { autoPlay: false }
          });
          if (game.settings.get(CONSTANTS.MODULE_NAME, "enable-fix-pixi")) {
            baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
          }
          const texture = new PIXI.Texture(baseTexture);
          resolve(texture);
          video.oncanplay = null;
        };
        video.onerror = () => {
          URL.revokeObjectURL(video.src);
          reject();
        };
      });
    }
    class SequencerFileBase {
      static make(inData, inDBPath, inMetadata) {
        if (typeof inData === "string" && !inDBPath && !inMetadata) {
          return new SequencerFilePlain(inData);
        }
        const originalFile = inData?.file ?? inData;
        const file = foundry.utils.duplicate(originalFile);
        const isRangeFind = typeof file !== "string" && !Array.isArray(originalFile) ? Object.keys(originalFile).filter((key) => key.endsWith("ft")).length > 0 : false;
        return isRangeFind ? new SequencerFileRangeFind(inData, inDBPath, inMetadata) : new SequencerFile(inData, inDBPath, inMetadata);
      }
    }
    class SequencerFilePlain extends SequencerFileBase {
      #file;
      rangeFind = false;
      constructor(inData) {
        super();
        this.#file = inData;
      }
      getFileForDistance() {
        return this.getFile();
      }
      getFile() {
        return this.#file;
      }
      getAllFiles() {
        return [this.#file];
      }
      async validate() {
        return Promise.resolve(!!this.#file);
      }
      clone() {
        return new SequencerFilePlain(this.#file);
      }
      getTimestamps() {
        return void 0;
      }
    }
    class SequencerFile extends SequencerFileBase {
      rangeFind = false;
      get isFlipbook() {
        return this.originalMetadata?.flipbook;
      }
      constructor(inData, inDBPath, inMetadata) {
        super();
        inData = foundry.utils.duplicate(inData);
        inMetadata = foundry.utils.duplicate(inMetadata);
        this.originalData = inData;
        this.originalMetadata = inMetadata;
        for (let [key, value] of Object.entries(inMetadata)) {
          this[key] = value;
        }
        this.dbPath = inDBPath;
        this.moduleName = inDBPath.split(".")[0];
        this.originalFile = inData?.file ?? inData;
        this.file = foundry.utils.duplicate(this.originalFile);
        this.fileIndex = null;
        this.twister = false;
      }
      clone() {
        return SequencerFile.make(
          this.originalData,
          this.dbPath,
          this.originalMetadata
        );
      }
      async validate() {
        let isValid = true;
        const directories = {};
        const allFiles = this.getAllFiles();
        for (const file of allFiles) {
          let directory = file.split("/");
          directory.pop();
          directory = directory.join("/");
          if (directories[directory] === void 0) {
            directories[directory] = await getFiles(directory);
          }
        }
        for (const file of allFiles) {
          let directory = file.split("/");
          directory.pop();
          directory = directory.join("/");
          if (directories[directory].indexOf(file) === -1) {
            console.warn(
              `"${this.dbPath}" has an incorrect file path, could not find file. Points to:
${file}`
            );
            isValid = false;
          }
        }
        return isValid;
      }
      getAllFiles() {
        return [this.file].deepFlatten();
      }
      getFile() {
        if (Array.isArray(this.file)) {
          this.fileIndex = is_real_number(this.fileIndex) ? this.fileIndex : random_array_element(this.file, {
            twister: this.twister,
            index: true
          });
          return this.file[this.fileIndex];
        }
        return this.file;
      }
      getTimestamps() {
        if (Array.isArray(this.originalMetadata?.timestamps)) {
          return this.originalMetadata?.timestamps?.[this.fileIndex] ?? this.originalMetadata?.timestamps[0];
        }
        return this.originalMetadata?.timestamps;
      }
      getPreviewFile(entry) {
        let parts = entry.split(".");
        let files2 = this.getAllFiles();
        if (Array.isArray(files2)) {
          if (is_real_number(parts[parts.length - 1])) {
            files2 = files2[parts[parts.length - 1]];
          } else {
            const index = Math.floor(interpolate(0, files2.length - 1, 0.5));
            files2 = files2?.[index - 1] ?? files2[index];
          }
        }
        return files2;
      }
      getFileForDistance() {
        return this.getFile();
      }
    }
    class SequencerFileRangeFind extends SequencerFile {
      rangeFind = true;
      constructor(...args) {
        super(...args);
        this._fileDistanceMap = false;
      }
      static get ftToDistanceMap() {
        return {
          "90ft": canvas.grid.size * 15,
          "60ft": canvas.grid.size * 9,
          "30ft": canvas.grid.size * 5,
          "15ft": canvas.grid.size * 2,
          "05ft": 0
        };
      }
      get _gridSizeDiff() {
        return canvas.grid.size / this.template[0];
      }
      getAllFiles() {
        return Object.values(this.file).deepFlatten();
      }
      getFile(inFt = "15ft") {
        if (inFt && this.file[inFt]) {
          if (Array.isArray(this.file[inFt])) {
            const fileIndex = is_real_number(this.fileIndex) ? Math.min(this.file[inFt].length - 1, this.fileIndex) : random_array_element(this.file[inFt], {
              twister: this.twister,
              index: true
            });
            return this.file[inFt][fileIndex];
          }
          return this.file[inFt];
        }
        return this.file;
      }
      getPreviewFile(entry) {
        let parts = entry.split(".");
        const ft = parts.find(
          (part) => Object.keys(SequencerFileRangeFind.ftToDistanceMap).indexOf(part) > -1
        );
        if (!ft) {
          return super.getPreviewFile(entry);
        }
        const fileIndex = parts.slice(parts.indexOf(ft) + 1)?.[0];
        if (is_real_number(Number(fileIndex))) {
          this.fileIndex = Number(fileIndex);
        }
        return this.getFile(ft);
      }
      _getMatchingDistance(inEntry) {
        return SequencerFileRangeFind.ftToDistanceMap[inEntry] / this._gridSizeDiff;
      }
      getFileForDistance(inDistance) {
        if (!this._fileDistanceMap) {
          let distances = Object.keys(this.file).filter(
            (entry) => Object.keys(SequencerFileRangeFind.ftToDistanceMap).indexOf(entry) > -1
          ).map((ft) => {
            return {
              file: this.getFile(ft),
              minDistance: this._getMatchingDistance(ft)
            };
          });
          let uniqueDistances = [
            ...new Set(distances.map((item) => item.minDistance))
          ];
          uniqueDistances.sort((a, b) => a - b);
          let max = Math.max(...uniqueDistances);
          let min = Math.min(...uniqueDistances);
          this._fileDistanceMap = distances.map((entry) => {
            entry.distances = {
              min: entry.minDistance === min ? 0 : entry.minDistance,
              max: entry.minDistance === max ? Infinity : uniqueDistances[uniqueDistances.indexOf(entry.minDistance) + 1]
            };
            return entry;
          });
        }
        const possibleFiles = this._fileDistanceMap.filter((entry) => {
          const relativeDistance = inDistance / this._gridSizeDiff;
          return relativeDistance >= entry.distances.min && relativeDistance < entry.distances.max;
        }).map((entry) => entry.file).flat();
        return possibleFiles.length > 1 ? random_array_element(possibleFiles, { twister: this.twister }) : possibleFiles[0];
      }
    }
    class Database {
      #entriesStore = writable({});
      privateModules = [];
      flattenedEntries = [];
      flattenedEntriesAdvanced = [];
      inverseFlattenedEntries = /* @__PURE__ */ new Map();
      get entries() {
        return get_store_value(this.#entriesStore);
      }
      set entries(entries) {
        this.#entriesStore.set(entries);
      }
      get entriesStore() {
        return this.#entriesStore;
      }
      get publicModules() {
        return Object.keys(this.entries).filter(
          (module2) => !this.privateModules.includes(module2)
        );
      }
      get publicFlattenedEntries() {
        return this.flattenedEntries.filter((entry) => {
          return this.privateModules.indexOf(entry.split(".")[0]) === -1;
        });
      }
      get publicFlattenedSimpleEntries() {
        return make_array_unique(
          this.publicFlattenedEntries.map((entry) => {
            return entry.split(CONSTANTS.FEET_REGEX)[0];
          })
        );
      }
      get publicFlattenedEntriesAdvanced() {
        return this.flattenedEntriesAdvanced.filter((entry) => {
          return this.privateModules.indexOf(entry.path.split(".")[0]) === -1;
        });
      }
      /**
       *  Retrieves an object of every public entry
       *
       * @return {object}
       */
      get filePathDatabasePaths() {
        const fileDatabaseObject = {};
        Object.entries(this.entries).map((entry) => entry[1]).deepFlatten().forEach((sequencerFile) => {
          if (sequencerFile?.rangeFind) {
            Object.entries(sequencerFile.file).forEach((entry) => {
              fileDatabaseObject[entry[1]] = sequencerFile.dbPath + "." + entry[0];
            });
          } else {
            fileDatabaseObject[sequencerFile.file] = sequencerFile.dbPath;
          }
        });
        return fileDatabaseObject;
      }
      /**
       *  Registers a set of entries to the database on the given module name
       *
       * @param  {string}      inModuleName  The namespace to assign to the inserted entries
       * @param  {object}      inEntries     The entries to merge into the database
       * @param  {boolean}     isPrivate     Whether to mark these entries as private and not show in Effect Player or Database Viewer
       * @param  {boolean}     override      Whether to ignore existing entries and completely override the module's previous entries
       * @return {boolean}
       */
      registerEntries(inModuleName, inEntries, isPrivate = false, override = false) {
        if (inModuleName.includes("."))
          return this._throwError(
            "registerEntries",
            "module name must not contain periods"
          );
        if (this.entries[inModuleName] && !override)
          custom_warning(
            "Sequencer",
            `registerEntries | module "${inModuleName}" has already been registered to the database! Do you have two similar modules active?`,
            true
          );
        this._flatten(inEntries, inModuleName);
        const processedEntries = this._processEntries(inModuleName, inEntries);
        if (override) {
          if (isPrivate) {
            this.privateModules.push(inModuleName);
          } else {
            this.privateModules = this.privateModules.filter((mod) => module !== inModuleName);
          }
          this.entries[inModuleName] = processedEntries;
        } else {
          if (isPrivate) this.privateModules.push(inModuleName);
          this.entries = foundry.utils.mergeObject(this.entries, {
            [inModuleName]: processedEntries
          });
        }
        debug(
          `Sequencer | Database | Entries for "${inModuleName}" registered`
        );
        Hooks.callAll("registerSequencerDatabaseEntries", inModuleName);
        return true;
      }
      /**
       *  Validates the entries under a certain module name, checking whether paths to assets are correct or not
       *
       * @param  {string}     inModuleName    The namespace to assign to the inserted entries
       * @return {boolean}
       */
      async validateEntries(inModuleName) {
        let entries = this.getEntry(inModuleName);
        if (!Array.isArray(entries)) {
          entries = [entries];
        }
        ui.notifications.info(
          `Validating paths registered to "${inModuleName}"...`
        );
        let isValid = true;
        LoadingBar.init(
          `Validating paths registered to "${inModuleName}"...`,
          entries.length
        );
        for (let entry of entries) {
          const result = await entry.validate();
          LoadingBar.incrementProgress();
          isValid = !(!result || !isValid);
        }
        if (!isValid) {
          ui.notifications.error(
            `Validation of paths registered to "${inModuleName}" failed! Errors logged in console.`
          );
        } else {
          ui.notifications.info(
            `Validation of paths registered to "${inModuleName}" complete! No errors found!`
          );
        }
      }
      /**
       *  Quickly checks if the entry exists in the database
       *
       * @param  {string}      inString    The entry to find in the database
       * @return {boolean}                 If the entry exists in the database
       */
      entryExists(inString) {
        if (typeof inString !== "string")
          return this._throwError("entryExists", "inString must be of type string");
        inString = inString.trim();
        if (inString === "")
          return this._throwError("entryExists", "inString cannot be empty");
        inString = inString.replace(/\[[0-9]+]$/, "");
        return this.flattenedEntries.find((entry) => entry.startsWith(inString));
      }
      /**
       *  Gets the entry in the database by a dot-notated string
       *
       * @param  {string}                     inString        The entry to find in the database
       * @param  {boolean}                    softFail        Whether it should soft fail (no error) when no entry was found
       * @return {array|SequencerFile|boolean}                The found entry in the database, or false if not found (with warning)
       */
      getEntry(inString, { softFail = false } = {}) {
        if (typeof inString !== "string") {
          if (softFail) return false;
          return this._throwError("getEntry", "inString must be of type string");
        }
        inString = inString.trim();
        if (inString === "") {
          if (softFail) return false;
          return this._throwError("getEntry", "inString cannot be empty");
        }
        inString = inString.replace(/\[[0-9]+]$/, "");
        if (!this.entryExists(inString)) {
          if (softFail) return false;
          return this._throwError(
            "getEntry",
            `Could not find ${inString} in database`
          );
        }
        let ft = false;
        let index = false;
        if (CONSTANTS.FEET_REGEX.test(inString)) {
          ft = inString.match(CONSTANTS.FEET_REGEX)[0];
          const split = inString.split(ft).filter((str) => str !== "");
          if (split.length > 1) {
            index = split[1].split(".")[0];
          }
          inString = split[0];
        }
        const module2 = inString.split(".")[0];
        const exactEntries = this.entries[module2].filter((entry) => {
          return entry.dbPath === inString;
        });
        let filteredEntries = (exactEntries.length ? exactEntries : this.entries[module2].filter((entry) => {
          return entry.dbPath.startsWith(inString);
        })).map((entry) => {
          let foundFile = entry;
          if (ft) foundFile = entry.file?.[ft] ?? foundFile;
          if (index) foundFile = foundFile?.[index] ?? foundFile;
          return foundFile;
        });
        if (!filteredEntries.length)
          return this._throwError(
            "getEntry",
            `Could not find ${inString} in database`
          );
        const foundEntry = filteredEntries.length === 1 ? filteredEntries[0] : filteredEntries;
        if (index && filteredEntries.length === 1) {
          foundEntry.fileIndex = Number(index);
        }
        return foundEntry;
      }
      /**
       *  Gets all files under a database path
       *
       * @param  {string}         inDBPath    The module to get all files from
       * @return {array|boolean}                  The found entries in the database under the module's name, or false if not found (with warning)
       */
      getAllFileEntries(inDBPath) {
        if (typeof inDBPath !== "string")
          return this._throwError(
            "getAllFileEntries",
            "inDBPath must be of type string"
          );
        inDBPath = inDBPath.trim();
        if (inDBPath === "")
          return this._throwError("getAllFileEntries", "inDBPath cannot be empty");
        if (!this.entryExists(inDBPath))
          return this._throwError(
            "getAllFileEntries",
            `Could not find ${inDBPath} in database`
          );
        const entries = this._recurseGetFilePaths(inDBPath);
        return make_array_unique(entries.flat());
      }
      /**
       *  Get all valid entries under a certain path
       *
       * @param  {string}             inPath      The database path to get entries under
       * @return {array|boolean}                  An array containing the next layer of valid paths
       */
      getPathsUnder(inPath, {
        ranges = false,
        arrays = false,
        match = false,
        fullyQualified = false
      } = {}) {
        if (typeof inPath !== "string")
          return this._throwError("getPathsUnder", "inPath must be of type string");
        inPath = inPath.trim();
        if (inPath === "")
          return this._throwError("getPathsUnder", "inPath cannot be empty");
        inPath = inPath.replace(/\[[0-9]+]$/, "");
        if (!this.entryExists(inPath))
          return this._throwError(
            "getPathsUnder",
            `Could not find ${inPath} in database`
          );
        const entries = this.flattenedEntries.filter((e) => {
          return (e.startsWith(inPath + ".") || e === inPath) && (!match || match && e.match(match));
        });
        if (entries.length === 0) return [];
        return make_array_unique(
          entries.map((e) => !arrays ? e.split(CONSTANTS.ARRAY_REGEX)[0] : e).map((e) => !ranges ? e.split(CONSTANTS.FEET_REGEX)[0] : e).map((e) => !fullyQualified ? e.split(inPath)[1] : e).map((e) => !fullyQualified ? e ? e.split(".")[1] : "" : e).filter(Boolean)
        );
      }
      /**
       *  Get all valid entries under a certain path
       *
       * @param  {string}             inPath          The database path to search for
       * @param  {boolean}            publicOnly      Whether to only search for public modules
       * @return {array|boolean}                      An array containing potential database paths
       */
      searchFor(inPath, publicOnly = true) {
        const modules = publicOnly ? this.publicModules : Object.keys(this.entries);
        const originalEntries = publicOnly ? this.publicFlattenedEntries : this.flattenedEntries;
        if ((!inPath || inPath === "") && !modules.includes(inPath)) return modules;
        if (typeof inPath !== "string") {
          return this._throwError("searchFor", "inString must be of type string");
        }
        inPath = inPath.trim();
        if (inPath === "")
          return this._throwError("searchFor", "inString cannot be empty");
        inPath = inPath.replace(/\[[0-9]+]$/, "");
        inPath = inPath.trim();
        let entries = originalEntries.filter(
          (e) => e.startsWith(inPath) && e !== inPath
        );
        if (inPath.endsWith(".")) inPath = inPath.substring(0, inPath.length - 1);
        let length = inPath.split(".").length + 1;
        let foundEntries = entries.map((e) => {
          let path = e.split(CONSTANTS.FEET_REGEX)[0];
          return path.split(".").slice(0, length).join(".");
        });
        if (foundEntries.length === 0) {
          const regexString = str_to_search_regex_str(inPath).replace(/\s+/g, "|");
          const searchParts = regexString.split("|").length;
          const regexSearch = new RegExp(regexString, "gu");
          foundEntries = originalEntries.filter((e) => {
            return e.match(regexSearch)?.length >= searchParts;
          }).map((e) => {
            return e.split(CONSTANTS.FEET_REGEX)[0];
          });
        }
        return make_array_unique(foundEntries);
      }
      /**
       * Throws an error without THROWING one. Duh.
       *
       * @param inFunctionName
       * @param inError
       * @returns {boolean}
       * @private
       */
      _throwError(inFunctionName, inError) {
        let error = `Sequencer | Database | ${inFunctionName} - ${inError}`;
        ui.notifications.error(error);
        return false;
      }
      /**
       * Gets all file paths from the entirety of
       *
       * @param inDBPath
       * @returns {Array}
       * @private
       */
      _recurseGetFilePaths(inDBPath) {
        const module2 = inDBPath.split(".")[0];
        return this.entries[module2].filter((entry) => entry.dbPath.startsWith(inDBPath)).map((entry) => {
          return entry.getAllFiles();
        }).flat();
      }
      /**
       * Flattens a given object to just their db path and file path
       *
       * @param entries
       * @param inModule
       * @private
       */
      _flatten(entries, inModule) {
        let flattened = flatten_object(
          foundry.utils.duplicate({ [inModule]: entries })
        );
        this.flattenedEntries = make_array_unique(
          this.flattenedEntries.concat(
            Object.keys(flattened).map((file) => file.split(".file")[0])
          )
        );
        this.flattenedEntriesAdvanced = make_array_unique(
          this.flattenedEntriesAdvanced.concat(
            Object.entries(flattened).map(([path, file]) => ({
              path: path.split(".file")[0],
              file,
              isAudio: file.endsWith("ogg") || file.endsWith("mp3") || file.endsWith("wav")
            }))
          )
        );
        this.inverseFlattenedEntries = Object.keys(flattened).reduce(
          (acc, entry) => {
            return acc.set(flattened[entry], entry.split(".file")[0]);
          },
          this.inverseFlattenedEntries
        );
      }
      /**
       * Processes and recurse into a large object containing file paths at any given depth
       *
       * @param moduleName
       * @param entries
       * @returns {object}
       * @private
       */
      _processEntries(moduleName, entries) {
        const allPaths = new Set(
          this.flattenedEntries.filter((e) => e.split(".")[0] === moduleName).map((e) => e.split(CONSTANTS.FEET_REGEX)[0])
        );
        const allTemplates = foundry.utils.mergeObject(entries?._templates ?? {}, {
          default: [100, 0, 0]
        });
        if (entries?._templates) {
          delete entries?._templates;
        }
        const moduleEntries = [];
        const mediaFileExtensions = Object.keys(CONST.FILE_CATEGORIES.IMAGE).concat(Object.keys(CONST.FILE_CATEGORIES.VIDEO)).concat(Object.keys(CONST.FILE_CATEGORIES.AUDIO)).concat(["json"]);
        for (let wholeDBPath of allPaths) {
          let metadata = this._getCleanData(entries);
          let dbPath = wholeDBPath.split(".");
          dbPath.shift();
          let combinedPath = "";
          for (let part of dbPath) {
            combinedPath = combinedPath ? combinedPath + "." + part : part;
            const entry = foundry.utils.getProperty(entries, combinedPath);
            if (Array.isArray(entry) || typeof entry === "string" || entry?.file) {
              metadata = this._getCleanData(entry, { existingData: metadata });
              break;
            }
            metadata = this._getCleanData(entry, { existingData: metadata });
          }
          if (!metadata.template) metadata.template = "default";
          if (typeof metadata.template === "string") {
            metadata.template = allTemplates?.[metadata.template] ?? allTemplates?.["default"];
          }
          let data = foundry.utils.getProperty(entries, dbPath.join("."));
          if (!Array.isArray(data) && !(typeof data === "string")) {
            data = this._getCleanData(data, { metadata: false });
          }
          if (typeof data === "string") {
            const existingEntry = this.entryExists(data);
            const extension = data.split(".")[data.split(".").length - 1].toLowerCase();
            if (!existingEntry && extension && !mediaFileExtensions.includes(extension)) {
              console.warn(
                `Sequencer | Database | registerEntries - failed to register ${wholeDBPath} to ${data}!`
              );
              this.flattenedEntries.splice(
                this.flattenedEntries.indexOf(wholeDBPath),
                1
              );
              continue;
            } else if (existingEntry) {
              const sequencerFile = this.getEntry(data);
              const clone = sequencerFile.clone();
              clone.dbPath = wholeDBPath;
              clone.metadata = foundry.utils.mergeObject(
                clone.metadata ?? {},
                metadata ?? {}
              );
              moduleEntries.push(clone);
              continue;
            }
          }
          moduleEntries.push(SequencerFileBase.make(data, wholeDBPath, metadata));
        }
        return moduleEntries;
      }
      _getCleanData(data, { existingData = {}, metadata = true } = {}) {
        data = Object.entries(data).filter((entry) => {
          return metadata === entry[0].startsWith("_");
        });
        if (metadata) {
          data = data.map((entry) => [entry[0].slice(1), entry[1]]);
        }
        return foundry.utils.mergeObject(existingData, Object.fromEntries(data));
      }
    }
    const SequencerDatabase = new Database();
    function create_if_block_2$1(ctx) {
      let a;
      let i;
      let mounted;
      let dispose;
      return {
        c() {
          a = element("a");
          i = element("i");
          attr(i, "class", "fas svelte-seq-uyryhb");
          toggle_class(
            i,
            "fa-angle-down",
            /*data*/
            ctx[0].open
          );
          toggle_class(i, "fa-angle-right", !/*data*/
          ctx[0].open);
          attr(a, "class", "svelte-seq-uyryhb");
        },
        m(target, anchor) {
          insert(target, a, anchor);
          append(a, i);
          if (!mounted) {
            dispose = listen(
              a,
              "click",
              /*click_handler*/
              ctx[6]
            );
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (dirty & /*data*/
          1) {
            toggle_class(
              i,
              "fa-angle-down",
              /*data*/
              ctx2[0].open
            );
          }
          if (dirty & /*data*/
          1) {
            toggle_class(i, "fa-angle-right", !/*data*/
            ctx2[0].open);
          }
        },
        d(detaching) {
          if (detaching) {
            detach(a);
          }
          mounted = false;
          dispose();
        }
      };
    }
    function create_if_block$7(ctx) {
      let t0;
      let a0;
      let i0;
      let t1;
      let a1;
      let mounted;
      let dispose;
      let if_block = !/*data*/
      ctx[0].hasChildren && create_if_block_1$4(ctx);
      return {
        c() {
          if (if_block) if_block.c();
          t0 = space();
          a0 = element("a");
          i0 = element("i");
          t1 = space();
          a1 = element("a");
          a1.innerHTML = `<i class="fas fa-play svelte-seq-uyryhb"></i>`;
          attr(i0, "class", "fas fa-database svelte-seq-uyryhb");
          toggle_class(
            i0,
            "flash-it",
            /*flashDBPath*/
            ctx[2]
          );
          attr(a0, "class", "database-entry-button svelte-seq-uyryhb");
          attr(a0, "title", localize("SEQUENCER.Database.CopyDatabasePath"));
          attr(a1, "class", "database-entry-button svelte-seq-uyryhb");
          attr(a1, "title", localize("SEQUENCER.Database.PlayPreview"));
        },
        m(target, anchor) {
          if (if_block) if_block.m(target, anchor);
          insert(target, t0, anchor);
          insert(target, a0, anchor);
          append(a0, i0);
          insert(target, t1, anchor);
          insert(target, a1, anchor);
          if (!mounted) {
            dispose = [
              listen(
                a0,
                "click",
                /*click_handler_2*/
                ctx[8]
              ),
              listen(
                a1,
                "click",
                /*click_handler_3*/
                ctx[9]
              )
            ];
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (!/*data*/
          ctx2[0].hasChildren) {
            if (if_block) {
              if_block.p(ctx2, dirty);
            } else {
              if_block = create_if_block_1$4(ctx2);
              if_block.c();
              if_block.m(t0.parentNode, t0);
            }
          } else if (if_block) {
            if_block.d(1);
            if_block = null;
          }
          if (dirty & /*flashDBPath*/
          4) {
            toggle_class(
              i0,
              "flash-it",
              /*flashDBPath*/
              ctx2[2]
            );
          }
        },
        d(detaching) {
          if (detaching) {
            detach(t0);
            detach(a0);
            detach(t1);
            detach(a1);
          }
          if (if_block) if_block.d(detaching);
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function create_if_block_1$4(ctx) {
      let a;
      let i;
      let mounted;
      let dispose;
      return {
        c() {
          a = element("a");
          i = element("i");
          attr(i, "class", "fas fa-file svelte-seq-uyryhb");
          toggle_class(
            i,
            "flash-it",
            /*flashFilePath*/
            ctx[1]
          );
          attr(a, "class", "database-entry-button svelte-seq-uyryhb");
          attr(a, "title", localize("SEQUENCER.Database.CopyFilePath"));
        },
        m(target, anchor) {
          insert(target, a, anchor);
          append(a, i);
          if (!mounted) {
            dispose = listen(
              a,
              "click",
              /*click_handler_1*/
              ctx[7]
            );
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (dirty & /*flashFilePath*/
          2) {
            toggle_class(
              i,
              "flash-it",
              /*flashFilePath*/
              ctx2[1]
            );
          }
        },
        d(detaching) {
          if (detaching) {
            detach(a);
          }
          mounted = false;
          dispose();
        }
      };
    }
    function create_fragment$k(ctx) {
      let div3;
      let div2;
      let t0;
      let show_if = (
        /*data*/
        ctx[0].fullPath.includes(".")
      );
      let t1;
      let div1;
      let div0;
      let t2;
      let t3_value = (
        /*data*/
        ctx[0].path + ""
      );
      let t3;
      let div1_title_value;
      let if_block0 = (
        /*data*/
        ctx[0].hasChildren && create_if_block_2$1(ctx)
      );
      let if_block1 = show_if && create_if_block$7(ctx);
      return {
        c() {
          div3 = element("div");
          div2 = element("div");
          if (if_block0) if_block0.c();
          t0 = space();
          if (if_block1) if_block1.c();
          t1 = space();
          div1 = element("div");
          div0 = element("div");
          t2 = space();
          t3 = text$1(t3_value);
          attr(div0, "class", "database-entry-text-highlight svelte-seq-uyryhb");
          attr(div1, "class", "database-entry-text svelte-seq-uyryhb");
          attr(div1, "title", div1_title_value = /*data*/
          ctx[0].path);
          attr(div2, "class", "database-entry-text-container svelte-seq-uyryhb");
          attr(div3, "class", "database-entry svelte-seq-uyryhb");
          set_style(
            div3,
            "margin-left",
            /*data*/
            ctx[0].depth * 15 + "px"
          );
        },
        m(target, anchor) {
          insert(target, div3, anchor);
          append(div3, div2);
          if (if_block0) if_block0.m(div2, null);
          append(div2, t0);
          if (if_block1) if_block1.m(div2, null);
          append(div2, t1);
          append(div2, div1);
          append(div1, div0);
          div0.innerHTML = /*highlight*/
          ctx[3];
          append(div1, t2);
          append(div1, t3);
        },
        p(ctx2, [dirty]) {
          if (
            /*data*/
            ctx2[0].hasChildren
          ) {
            if (if_block0) {
              if_block0.p(ctx2, dirty);
            } else {
              if_block0 = create_if_block_2$1(ctx2);
              if_block0.c();
              if_block0.m(div2, t0);
            }
          } else if (if_block0) {
            if_block0.d(1);
            if_block0 = null;
          }
          if (dirty & /*data*/
          1) show_if = /*data*/
          ctx2[0].fullPath.includes(".");
          if (show_if) {
            if (if_block1) {
              if_block1.p(ctx2, dirty);
            } else {
              if_block1 = create_if_block$7(ctx2);
              if_block1.c();
              if_block1.m(div2, t1);
            }
          } else if (if_block1) {
            if_block1.d(1);
            if_block1 = null;
          }
          if (dirty & /*highlight*/
          8) div0.innerHTML = /*highlight*/
          ctx2[3];
          if (dirty & /*data*/
          1 && t3_value !== (t3_value = /*data*/
          ctx2[0].path + "")) set_data(t3, t3_value);
          if (dirty & /*data*/
          1 && div1_title_value !== (div1_title_value = /*data*/
          ctx2[0].path)) {
            attr(div1, "title", div1_title_value);
          }
          if (dirty & /*data*/
          1) {
            set_style(
              div3,
              "margin-left",
              /*data*/
              ctx2[0].depth * 15 + "px"
            );
          }
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div3);
          }
          if (if_block0) if_block0.d();
          if (if_block1) if_block1.d();
        }
      };
    }
    function instance$k($$self, $$props, $$invalidate) {
      let highlight;
      let $searchRegex;
      let { data } = $$props;
      const searchRegex = databaseStore.searchRegex;
      component_subscribe($$self, searchRegex, (value) => $$invalidate(5, $searchRegex = value));
      let flashFilePath = false;
      let flashDBPath = false;
      const click_handler = (e) => {
        databaseStore.openTreePath(data.fullPath, !data.open, e.ctrlKey);
      };
      const click_handler_1 = (e) => {
        databaseStore.copyPath(data.fullPath, true, e.ctrlKey);
        $$invalidate(1, flashFilePath = true);
        setTimeout(
          () => {
            $$invalidate(1, flashFilePath = false);
          },
          400
        );
      };
      const click_handler_2 = (e) => {
        databaseStore.copyPath(data.fullPath, false, e.ctrlKey);
        $$invalidate(2, flashDBPath = true);
        setTimeout(
          () => {
            $$invalidate(2, flashDBPath = false);
          },
          400
        );
      };
      const click_handler_3 = () => {
        databaseStore.playFile(data.fullPath);
      };
      $$self.$$set = ($$props2) => {
        if ("data" in $$props2) $$invalidate(0, data = $$props2.data);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*data, $searchRegex*/
        33) {
          $$invalidate(3, highlight = data.path.replace($searchRegex, "<mark>$&</mark>"));
        }
      };
      return [
        data,
        flashFilePath,
        flashDBPath,
        highlight,
        searchRegex,
        $searchRegex,
        click_handler,
        click_handler_1,
        click_handler_2,
        click_handler_3
      ];
    }
    class TreeViewEntry extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$k, create_fragment$k, safe_not_equal, { data: 0 });
      }
    }
    function create_fragment$j(ctx) {
      let div;
      return {
        c() {
          div = element("div");
          set_style(div, "margin-bottom", "0.3rem");
        },
        m(target, anchor) {
          insert(target, div, anchor);
        },
        p: noop,
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div);
          }
        }
      };
    }
    function instance$j($$self, $$props, $$invalidate) {
      let { data } = $$props;
      $$self.$$set = ($$props2) => {
        if ("data" in $$props2) $$invalidate(0, data = $$props2.data);
      };
      return [data];
    }
    class TreeViewSeparator extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$j, create_fragment$j, safe_not_equal, { data: 0 });
      }
    }
    const viewSize = 320;
    let animatedSprite;
    let offscreenApp;
    let canvas$1;
    let offscreenCanvas;
    let oldImageSrcs = /* @__PURE__ */ new Map();
    function initOffscreenApp(databaseStore2) {
      if (offscreenApp) {
        return;
      }
      canvas$1 = document.createElement("canvas");
      canvas$1.style.display = "none";
      offscreenCanvas = canvas$1.transferControlToOffscreen();
      offscreenApp = new PIXI.Application({ autoStart: false, view: offscreenCanvas, backgroundColor: 10066329, sharedTicker: false });
      offscreenApp.ticker.stop();
      document.body.appendChild(canvas$1);
      offscreenCanvas.style = {};
      offscreenCanvas.width = viewSize;
      offscreenCanvas.height = viewSize;
    }
    async function playSpritesheet(file, databaseStore2) {
      initOffscreenApp();
      const sheet = await SequencerFileCache.loadFile(file);
      if (!sheet) {
        return;
      }
      SequencerFileCache.registerSpritesheet(file, sheet);
      const frames = Object.values(sheet.animations)?.[0];
      if (!frames) {
        return;
      }
      const framerate = sheet.data?.meta?.framerate ?? 30;
      const frametime = 1 / framerate * 1e3;
      databaseStore2.metadata.set({
        type: "Spritesheet",
        duration: Math.round(frametime * frames.length) + "ms"
      });
      if (!animatedSprite) {
        animatedSprite = new PIXI.AnimatedSprite(frames, true);
        animatedSprite.anchor.set(0.5);
        animatedSprite.x = viewSize / 2;
        animatedSprite.y = viewSize / 2;
        offscreenApp.stage.addChild(animatedSprite);
      }
      let queuedFrame;
      let renderedFrame;
      animatedSprite.onFrameChange = async (frame) => {
        if (queuedFrame !== renderedFrame) {
          return;
        }
        let curerntImageSrc = oldImageSrcs.get(frame);
        if (!curerntImageSrc) {
          queuedFrame = frame;
          offscreenApp.render();
          const imageBlob = await offscreenCanvas.convertToBlob();
          curerntImageSrc = URL.createObjectURL(imageBlob);
          oldImageSrcs.set(frame, curerntImageSrc);
          renderedFrame = frame;
        }
        databaseStore2.elements.image.src = curerntImageSrc;
      };
      animatedSprite.textures = frames;
      animatedSprite.scale.set(viewSize / Math.max(frames[0].width, frames[0].height));
      animatedSprite.file = file;
      animatedSprite.play();
    }
    async function cleanupSpritesheet(destroy = false) {
      if (!animatedSprite) {
        return;
      }
      animatedSprite.stop();
      animatedSprite.onFrameChange = null;
      if (animatedSprite.file) {
        await SequencerFileCache.unloadSpritesheet(animatedSprite.file);
        animatedSprite.file = null;
      }
      oldImageSrcs.values().forEach((objectUrl) => {
        URL.revokeObjectURL(objectUrl);
      });
      oldImageSrcs.clear();
      if (destroy) {
        animatedSprite.destroy();
        animatedSprite = null;
        offscreenApp.destroy();
        canvas$1.remove();
        offscreenCanvas = null;
        offscreenApp = void 0;
      }
    }
    let lastFile = false;
    function getFileData(entryText) {
      let entry = SequencerDatabase.getEntry(entryText);
      if (Array.isArray(entry)) {
        if (entry.includes(lastFile) && entry.length > 1) {
          entry.splice(entry.indexOf(lastFile), 1);
        }
        entry = random_array_element(entry);
        lastFile = entry;
      }
      let previewFile = entry?.file ?? entry;
      if (entry instanceof SequencerFileBase) {
        previewFile = entry.clone().getPreviewFile(entryText);
      }
      let lowerCaseEntry = previewFile ? previewFile.toLowerCase() : "unknown.jpg";
      const isAudio = lowerCaseEntry.endsWith("ogg") || lowerCaseEntry.endsWith("mp3") || lowerCaseEntry.endsWith("wav");
      const isSpritesheet = lowerCaseEntry.endsWith("json");
      const isImage = !lowerCaseEntry.endsWith("webm") && !isAudio && !isSpritesheet;
      const isVideo = !isAudio && !isImage && !isSpritesheet;
      const icon = previewFile ? isVideo || isSpritesheet ? "fa-film" : isAudio ? "fa-volume-high" : "fa-image" : "fa-question-mark";
      const title = previewFile ? isVideo ? "Animated WebM" : isSpritesheet ? "Spritesheet" : isAudio ? "Audio" : "Image" : "Unknown";
      return {
        file: previewFile ?? "unknown.jpg",
        dbEntry: entry,
        icon,
        title,
        isAudio,
        isImage,
        isSpritesheet,
        isVideo
      };
    }
    function copyPath(dbPath, getFilepath, quotes = false) {
      const tempInput = document.createElement("input");
      tempInput.value = `${dbPath}`;
      let entry;
      if (getFilepath) {
        entry = Sequencer.Database.getEntry(dbPath);
        if (Array.isArray(entry)) {
          entry = random_array_element(entry);
        }
        if (entry instanceof SequencerFileBase) {
          const specificFt = dbPath.match(CONSTANTS.FEET_REGEX);
          if (specificFt) {
            const ft = specificFt[0].replaceAll(".", "");
            entry = entry.getFile(ft);
          } else {
            const files2 = entry.getAllFiles();
            if (Array.isArray(files2)) {
              const index = Math.floor(interpolate(0, files2.length - 1, 0.5));
              entry = files2[index];
            }
          }
        }
        tempInput.value = `${entry?.file ?? entry}`;
      }
      if (quotes) {
        tempInput.value = `"${tempInput.value}"`;
      }
      document.body.appendChild(tempInput);
      tempInput.select();
      document.execCommand("copy");
      document.body.removeChild(tempInput);
      document.execCommand("copy");
    }
    async function playFile(entry) {
      await cleanupSpritesheet();
      const { file, isAudio, isImage, isVideo, isSpritesheet } = getFileData(entry);
      databaseStore.elements.audio.classList.toggle("hidden", !isAudio);
      databaseStore.elements.image.classList.toggle("hidden", !isImage && !isSpritesheet);
      databaseStore.elements.player.classList.toggle("hidden", !isVideo);
      if (isImage) {
        databaseStore.elements.image.src = file;
        databaseStore.metadata.set({
          type: "Image",
          duration: "n/a"
        });
        return;
      }
      if (isSpritesheet) {
        playSpritesheet(file, databaseStore);
        return;
      }
      const element2 = isAudio ? databaseStore.elements.audio : databaseStore.elements.player;
      element2.onerror = () => {
        const error = `Sequencer Database Viewer | Could not play file: ${file}`;
        ui.notifications.error(error);
        console.error(error);
      };
      element2.oncanplay = () => {
        element2.play();
      };
      element2.onloadedmetadata = () => {
        databaseStore.metadata.set({
          type: isVideo ? "Video" : isAudio ? "Audio" : "Image",
          duration: isImage ? "n/a" : element2.duration * 1e3 + "ms"
        });
      };
      element2.src = file;
    }
    const treeStore = writable({});
    const visibleTreeStore = writable([]);
    let flattenedEntries = [];
    const entriesStore = SequencerDatabase.entriesStore;
    const packStore = writable(SequencerDatabase.publicModules);
    const selectedPackStore = writable("all");
    const searchStore = writable("");
    const cleanSearchStore = writable("");
    const searchRegexStore = writable(new RegExp("", "gu"));
    SequencerDatabase.entriesStore.subscribe(() => {
      packStore.set(SequencerDatabase.publicModules);
    });
    const databaseStore = {
      metadata: writable(false),
      allRanges: writable(false),
      fileTypes: writable("all"),
      subLists: writable(false),
      listView: writable(false),
      packStore,
      selectedPackStore,
      visibleTreeStore,
      search: searchStore,
      cleanSearchStore,
      searchRegex: searchRegexStore,
      elements: {},
      cleanupSpritesheet() {
        cleanupSpritesheet(true);
      },
      copyPath,
      playFile,
      openTreePath
    };
    entriesStore.subscribe(() => {
      filterFlattenedEntries();
    });
    databaseStore.allRanges.subscribe(() => {
      filterFlattenedEntries();
    });
    databaseStore.subLists.subscribe(() => {
      filterFlattenedEntries();
    });
    databaseStore.selectedPackStore.subscribe(() => {
      filterFlattenedEntries();
    });
    searchStore.subscribe((val) => {
      const cleanSearch = str_to_search_regex_str(val).replace(/\s+/g, "|");
      cleanSearchStore.set(cleanSearch);
      searchRegexStore.set(new RegExp(cleanSearch, "gu"));
      updateVisualTree();
    });
    function filterFlattenedEntries() {
      const selectedPack = get_store_value(selectedPackStore);
      const search = get_store_value(searchStore);
      const searchRegex = get_store_value(searchRegexStore);
      const subLists = get_store_value(databaseStore.subLists);
      const allRanges = get_store_value(databaseStore.allRanges);
      flattenedEntries = make_array_unique(
        SequencerDatabase.publicFlattenedEntries.filter((e) => {
          return (selectedPack === "all" || e.startsWith(selectedPack + ".")) && (!search || e.match(searchRegex));
        }).map((e) => !subLists ? e.split(CONSTANTS.ARRAY_REGEX)[0] : e).map((e) => !allRanges ? e.split(CONSTANTS.FEET_REGEX)[0] : e)
      );
      treeStore.set(
        flattenedEntries.reduce((acc, entry) => {
          let path = "";
          for (const part of entry.split(".")) {
            const fullPath = path ? path + "." + part : part;
            path = path ? path + ".children." + part : part;
            if (!foundry.utils.getProperty(acc, path)) {
              foundry.utils.setProperty(
                acc,
                path,
                foundry.utils.mergeObject(
                  {
                    path: part,
                    fullPath,
                    open: false,
                    children: {}
                  },
                  foundry.utils.getProperty(acc, path)
                )
              );
            }
          }
          return acc;
        }, {})
      );
    }
    function openTreePath(fullPath, open, openAll = false) {
      treeStore.update((tree) => {
        const fullTreePath = fullPath.split(".").join(".children.");
        const node = foundry.utils.getProperty(tree, fullTreePath);
        foundry.utils.setProperty(tree, fullTreePath + ".open", open);
        if ((!open || openAll) && !foundry.utils.isEmpty(node.children)) {
          recurseOpenTree(node.children, open);
        }
        return tree;
      });
    }
    function recurseOpenTree(children2, open) {
      for (const node of Object.values(children2)) {
        node.open = open;
        if (!foundry.utils.isEmpty(node.children)) {
          recurseOpenTree(node.children, open);
        }
      }
    }
    treeStore.subscribe(() => {
      updateVisualTree();
    });
    function updateVisualTree() {
      const tree = get_store_value(treeStore);
      const visibleTree = recurseTree(tree).deepFlatten().filter((e) => e.visible);
      visibleTreeStore.set(visibleTree);
    }
    function recurseTree(tree, path = "", depth = 0) {
      const search = get_store_value(searchStore);
      const searchRegex = get_store_value(searchRegexStore);
      const searchParts = get_store_value(cleanSearchStore).split("|");
      return Object.entries(tree).map(([key, data]) => {
        const fullPath = path ? path + "." + key : key;
        const children2 = recurseTree(
          data.children,
          fullPath,
          depth + 1
        ).deepFlatten();
        const matchParts = make_array_unique(fullPath.match(searchRegex) || []);
        const open = data.open || search && (matchParts.length >= searchParts.length || children2.filter((e) => e.visible).length);
        let visible = !search || matchParts.length >= searchParts.length;
        if (visible) {
          children2.forEach((e) => e.visible = true);
        } else {
          visible = children2.filter((e) => e.visible).length;
        }
        const entry = {
          class: TreeViewEntry,
          path: key,
          fullPath,
          open,
          visible,
          hasChildren: !foundry.utils.isEmpty(data.children),
          depth
        };
        const leaf = [entry];
        if ((data.open || entry.open) && entry.hasChildren) {
          leaf.push(...children2, {
            fullPath: foundry.utils.randomID(),
            class: TreeViewSeparator
          });
        }
        return leaf;
      });
    }
    function create_fragment$i(ctx) {
      let div2;
      let button0;
      let t0;
      let button1;
      let i1;
      let t1;
      let button2;
      let i2;
      let t2;
      let div1;
      let div0;
      let t3;
      let t4;
      let mounted;
      let dispose;
      return {
        c() {
          div2 = element("div");
          button0 = element("button");
          button0.innerHTML = `<i class="fas fa-play svelte-seq-flzvpb"></i>`;
          t0 = space();
          button1 = element("button");
          i1 = element("i");
          t1 = space();
          button2 = element("button");
          i2 = element("i");
          t2 = space();
          div1 = element("div");
          div0 = element("div");
          t3 = space();
          t4 = text$1(
            /*entry*/
            ctx[0]
          );
          attr(button0, "type", "button");
          attr(button0, "class", "btn_play svelte-seq-flzvpb");
          attr(button0, "title", localize("SEQUENCER.Database.PlayPreview"));
          attr(i1, "class", "fas fa-file svelte-seq-flzvpb");
          toggle_class(
            i1,
            "flash-it",
            /*flashFilePath*/
            ctx[1]
          );
          attr(button1, "type", "button");
          attr(button1, "class", "btn_copy_filepath svelte-seq-flzvpb");
          attr(button1, "title", localize("SEQUENCER.Database.CopyFilePath"));
          attr(i2, "class", "fas fa-database svelte-seq-flzvpb");
          toggle_class(
            i2,
            "flash-it",
            /*flashDBPath*/
            ctx[2]
          );
          attr(button2, "type", "button");
          attr(button2, "class", "btn_copy_databasepath svelte-seq-flzvpb");
          attr(button2, "title", localize("SEQUENCER.Database.CopyDatabasePath"));
          attr(div0, "class", "database-entry-text-highlight svelte-seq-flzvpb");
          attr(div1, "class", "database-entry-text svelte-seq-flzvpb");
          attr(
            div1,
            "title",
            /*entry*/
            ctx[0]
          );
          attr(div2, "class", "database-entry svelte-seq-flzvpb");
          attr(
            div2,
            "data-id",
            /*entry*/
            ctx[0]
          );
        },
        m(target, anchor) {
          insert(target, div2, anchor);
          append(div2, button0);
          append(div2, t0);
          append(div2, button1);
          append(button1, i1);
          append(div2, t1);
          append(div2, button2);
          append(button2, i2);
          append(div2, t2);
          append(div2, div1);
          append(div1, div0);
          div0.innerHTML = /*highlight*/
          ctx[3];
          append(div1, t3);
          append(div1, t4);
          if (!mounted) {
            dispose = [
              listen(
                button0,
                "click",
                /*click_handler*/
                ctx[6]
              ),
              listen(
                button1,
                "click",
                /*click_handler_1*/
                ctx[7]
              ),
              listen(
                button2,
                "click",
                /*click_handler_2*/
                ctx[8]
              )
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*flashFilePath*/
          2) {
            toggle_class(
              i1,
              "flash-it",
              /*flashFilePath*/
              ctx2[1]
            );
          }
          if (dirty & /*flashDBPath*/
          4) {
            toggle_class(
              i2,
              "flash-it",
              /*flashDBPath*/
              ctx2[2]
            );
          }
          if (dirty & /*highlight*/
          8) div0.innerHTML = /*highlight*/
          ctx2[3];
          if (dirty & /*entry*/
          1) set_data(
            t4,
            /*entry*/
            ctx2[0]
          );
          if (dirty & /*entry*/
          1) {
            attr(
              div1,
              "title",
              /*entry*/
              ctx2[0]
            );
          }
          if (dirty & /*entry*/
          1) {
            attr(
              div2,
              "data-id",
              /*entry*/
              ctx2[0]
            );
          }
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div2);
          }
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function instance$i($$self, $$props, $$invalidate) {
      let highlight;
      let $searchRegex;
      createEventDispatcher();
      let { entry } = $$props;
      const searchRegex = databaseStore.searchRegex;
      component_subscribe($$self, searchRegex, (value) => $$invalidate(5, $searchRegex = value));
      let flashFilePath = false;
      let flashDBPath = false;
      const click_handler = () => {
        databaseStore.playFile(entry);
      };
      const click_handler_1 = (e) => {
        databaseStore.copyPath(entry, true, e.ctrlKey);
        $$invalidate(1, flashFilePath = true);
        setTimeout(
          () => {
            $$invalidate(1, flashFilePath = false);
          },
          400
        );
      };
      const click_handler_2 = (e) => {
        databaseStore.copyPath(entry, false, e.ctrlKey);
        $$invalidate(2, flashDBPath = true);
        setTimeout(
          () => {
            $$invalidate(2, flashDBPath = false);
          },
          400
        );
      };
      $$self.$$set = ($$props2) => {
        if ("entry" in $$props2) $$invalidate(0, entry = $$props2.entry);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*entry, $searchRegex*/
        33) {
          $$invalidate(3, highlight = entry.replace($searchRegex, "<mark>$&</mark>"));
        }
      };
      return [
        entry,
        flashFilePath,
        flashDBPath,
        highlight,
        searchRegex,
        $searchRegex,
        click_handler,
        click_handler_1,
        click_handler_2
      ];
    }
    class DatabaseEntry extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$i, create_fragment$i, safe_not_equal, { entry: 0 });
      }
    }
    const DIRECTION_TYPE = {
      FRONT: "FRONT",
      // scroll up or left
      BEHIND: "BEHIND"
      // scroll down or right
    };
    const CALC_TYPE = {
      INIT: "INIT",
      FIXED: "FIXED",
      DYNAMIC: "DYNAMIC"
    };
    const LEADING_BUFFER = 2;
    class Virtual {
      param;
      callUpdate;
      firstRangeTotalSize = 0;
      firstRangeAverageSize = 0;
      lastCalcIndex = 0;
      fixedSizeValue = 0;
      calcType = CALC_TYPE.INIT;
      offset = 0;
      direction = "";
      range;
      constructor(param, callUpdate) {
        this.init(param, callUpdate);
      }
      init(param, callUpdate) {
        this.param = param;
        this.callUpdate = callUpdate;
        this.sizes = /* @__PURE__ */ new Map();
        this.firstRangeTotalSize = 0;
        this.firstRangeAverageSize = 0;
        this.fixedSizeValue = 0;
        this.calcType = CALC_TYPE.INIT;
        this.offset = 0;
        this.direction = "";
        this.range = /* @__PURE__ */ Object.create(null);
        if (param) {
          this.checkRange(0, param.keeps - 1);
        }
      }
      destroy() {
        this.init(null, null);
      }
      // return current render range
      getRange() {
        const range = /* @__PURE__ */ Object.create(null);
        range.start = this.range.start;
        range.end = this.range.end;
        range.padFront = this.range.padFront;
        range.padBehind = this.range.padBehind;
        return range;
      }
      isBehind() {
        return this.direction === DIRECTION_TYPE.BEHIND;
      }
      isFront() {
        return this.direction === DIRECTION_TYPE.FRONT;
      }
      // return start index offset
      getOffset(start) {
        return (start < 1 ? 0 : this.getIndexOffset(start)) + this.param.slotHeaderSize;
      }
      updateParam(key, value) {
        if (this.param && key in this.param) {
          if (key === "uniqueIds") {
            this.sizes.forEach((v, key2) => {
              if (!value.includes(key2)) {
                this.sizes.delete(key2);
              }
            });
          }
          this.param[key] = value;
        }
      }
      // save each size map by id
      saveSize(id, size) {
        this.sizes.set(id, size);
        if (this.calcType === CALC_TYPE.INIT) {
          this.fixedSizeValue = size;
          this.calcType = CALC_TYPE.FIXED;
        } else if (this.calcType === CALC_TYPE.FIXED && this.fixedSizeValue !== size) {
          this.calcType = CALC_TYPE.DYNAMIC;
          delete this.fixedSizeValue;
        }
        if (this.calcType !== CALC_TYPE.FIXED && typeof this.firstRangeTotalSize !== "undefined") {
          if (this.sizes.size < Math.min(this.param.keeps, this.param.uniqueIds.length)) {
            this.firstRangeTotalSize = [...this.sizes.values()].reduce((acc, val) => acc + val, 0);
            this.firstRangeAverageSize = Math.round(this.firstRangeTotalSize / this.sizes.size);
          } else {
            delete this.firstRangeTotalSize;
          }
        }
      }
      // in some special situation (e.g. length change) we need to update in a row
      // try going to render next range by a leading buffer according to current direction
      handleDataSourcesChange() {
        let start = this.range.start;
        if (this.isFront()) {
          start = start - LEADING_BUFFER;
        } else if (this.isBehind()) {
          start = start + LEADING_BUFFER;
        }
        start = Math.max(start, 0);
        this.updateRange(this.range.start, this.getEndByStart(start));
      }
      // when slot size change, we also need force update
      handleSlotSizeChange() {
        this.handleDataSourcesChange();
      }
      // calculating range on scroll
      handleScroll(offset2) {
        this.direction = offset2 < this.offset ? DIRECTION_TYPE.FRONT : DIRECTION_TYPE.BEHIND;
        this.offset = offset2;
        if (!this.param) {
          return;
        }
        if (this.direction === DIRECTION_TYPE.FRONT) {
          this.handleFront();
        } else if (this.direction === DIRECTION_TYPE.BEHIND) {
          this.handleBehind();
        }
      }
      // ----------- public method end -----------
      handleFront() {
        const overs = this.getScrollOvers();
        if (overs > this.range.start) {
          return;
        }
        const start = Math.max(overs - this.param.buffer, 0);
        this.checkRange(start, this.getEndByStart(start));
      }
      handleBehind() {
        const overs = this.getScrollOvers();
        if (overs < this.range.start + this.param.buffer) {
          return;
        }
        this.checkRange(overs, this.getEndByStart(overs));
      }
      // return the pass overs according to current scroll offset
      getScrollOvers() {
        const offset2 = this.offset - this.param.slotHeaderSize;
        if (offset2 <= 0) {
          return 0;
        }
        if (this.isFixedType()) {
          return Math.floor(offset2 / this.fixedSizeValue);
        }
        let low = 0;
        let middle = 0;
        let middleOffset = 0;
        let high = this.param.uniqueIds.length;
        while (low <= high) {
          middle = low + Math.floor((high - low) / 2);
          middleOffset = this.getIndexOffset(middle);
          if (middleOffset === offset2) {
            return middle;
          } else if (middleOffset < offset2) {
            low = middle + 1;
          } else if (middleOffset > offset2) {
            high = middle - 1;
          }
        }
        return low > 0 ? --low : 0;
      }
      // return a scroll offset from given index, can efficiency be improved more here?
      // although the call frequency is very high, its only a superposition of numbers
      getIndexOffset(givenIndex) {
        if (!givenIndex) {
          return 0;
        }
        let offset2 = 0;
        let indexSize = 0;
        for (let index = 0; index < givenIndex; index++) {
          indexSize = this.sizes.get(this.param.uniqueIds[index]);
          offset2 = offset2 + (typeof indexSize === "number" ? indexSize : this.getEstimateSize());
        }
        this.lastCalcIndex = Math.max(this.lastCalcIndex, givenIndex - 1);
        this.lastCalcIndex = Math.min(this.lastCalcIndex, this.getLastIndex());
        return offset2;
      }
      // is fixed size type
      isFixedType() {
        return this.calcType === CALC_TYPE.FIXED;
      }
      // return the real last index
      getLastIndex() {
        return this.param.uniqueIds.length - 1;
      }
      // in some conditions range is broke, we need correct it
      // and then decide whether need update to next range
      checkRange(start, end) {
        const keeps = this.param.keeps;
        const total = this.param.uniqueIds.length;
        if (total <= keeps) {
          start = 0;
          end = this.getLastIndex();
        } else if (end - start < keeps - 1) {
          start = end - keeps + 1;
        }
        if (this.range.start !== start) {
          this.updateRange(start, end);
        }
      }
      // setting to a new range and rerender
      updateRange(start, end) {
        this.range.start = start;
        this.range.end = end;
        this.range.padFront = this.getPadFront();
        this.range.padBehind = this.getPadBehind();
        this.callUpdate(this.getRange());
      }
      // return end base on start
      getEndByStart(start) {
        const theoryEnd = start + this.param.keeps - 1;
        const truelyEnd = Math.min(theoryEnd, this.getLastIndex());
        return truelyEnd;
      }
      // return total front offset
      getPadFront() {
        if (this.isFixedType()) {
          return this.fixedSizeValue * this.range.start;
        } else {
          return this.getIndexOffset(this.range.start);
        }
      }
      // return total behind offset
      getPadBehind() {
        const end = this.range.end;
        const lastIndex = this.getLastIndex();
        if (this.isFixedType()) {
          return (lastIndex - end) * this.fixedSizeValue;
        }
        if (this.lastCalcIndex === lastIndex) {
          return this.getIndexOffset(lastIndex) - this.getIndexOffset(end);
        } else {
          return (lastIndex - end) * this.getEstimateSize();
        }
      }
      // get the item estimate size
      getEstimateSize() {
        return this.isFixedType() ? this.fixedSizeValue : this.firstRangeAverageSize || this.param.estimateSize;
      }
    }
    function isBrowser() {
      return typeof document !== "undefined";
    }
    function create_fragment$h(ctx) {
      let div;
      let current;
      const default_slot_template = (
        /*#slots*/
        ctx[5].default
      );
      const default_slot = create_slot(
        default_slot_template,
        ctx,
        /*$$scope*/
        ctx[4],
        null
      );
      return {
        c() {
          div = element("div");
          if (default_slot) default_slot.c();
          attr(div, "class", "virtual-scroll-item");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          if (default_slot) {
            default_slot.m(div, null);
          }
          ctx[6](div);
          current = true;
        },
        p(ctx2, [dirty]) {
          if (default_slot) {
            if (default_slot.p && (!current || dirty & /*$$scope*/
            16)) {
              update_slot_base(
                default_slot,
                default_slot_template,
                ctx2,
                /*$$scope*/
                ctx2[4],
                !current ? get_all_dirty_from_scope(
                  /*$$scope*/
                  ctx2[4]
                ) : get_slot_changes(
                  default_slot_template,
                  /*$$scope*/
                  ctx2[4],
                  dirty,
                  null
                ),
                null
              );
            }
          }
        },
        i(local) {
          if (current) return;
          transition_in(default_slot, local);
          current = true;
        },
        o(local) {
          transition_out(default_slot, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          if (default_slot) default_slot.d(detaching);
          ctx[6](null);
        }
      };
    }
    function instance$h($$self, $$props, $$invalidate) {
      let { $$slots: slots = {}, $$scope } = $$props;
      let { horizontal = false } = $$props;
      let { uniqueKey } = $$props;
      let { type = "item" } = $$props;
      let resizeObserver2;
      let itemDiv;
      let previousSize;
      const dispatch2 = createEventDispatcher();
      const shapeKey = horizontal ? "offsetWidth" : "offsetHeight";
      onMount(() => {
        if (typeof ResizeObserver !== "undefined") {
          resizeObserver2 = new ResizeObserver(dispatchSizeChange);
          resizeObserver2.observe(itemDiv);
        }
      });
      afterUpdate(dispatchSizeChange);
      onDestroy(() => {
        if (resizeObserver2) {
          resizeObserver2.disconnect();
          resizeObserver2 = null;
        }
      });
      function dispatchSizeChange() {
        const size = itemDiv ? itemDiv[shapeKey] : 0;
        if (size === previousSize) return;
        previousSize = size;
        dispatch2("resize", { id: uniqueKey, size, type });
      }
      function div_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          itemDiv = $$value;
          $$invalidate(0, itemDiv);
        });
      }
      $$self.$$set = ($$props2) => {
        if ("horizontal" in $$props2) $$invalidate(1, horizontal = $$props2.horizontal);
        if ("uniqueKey" in $$props2) $$invalidate(2, uniqueKey = $$props2.uniqueKey);
        if ("type" in $$props2) $$invalidate(3, type = $$props2.type);
        if ("$$scope" in $$props2) $$invalidate(4, $$scope = $$props2.$$scope);
      };
      return [itemDiv, horizontal, uniqueKey, type, $$scope, slots, div_binding];
    }
    class Item extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$h, create_fragment$h, safe_not_equal, { horizontal: 1, uniqueKey: 2, type: 3 });
      }
    }
    const get_footer_slot_changes = (dirty) => ({});
    const get_footer_slot_context = (ctx) => ({});
    function get_each_context$6(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[39] = list[i];
      child_ctx[41] = i;
      return child_ctx;
    }
    const get_default_slot_changes = (dirty) => ({
      data: dirty[0] & /*displayItems*/
      4,
      index: dirty[0] & /*displayItems*/
      4
    });
    const get_default_slot_context = (ctx) => ({
      data: (
        /*dataItem*/
        ctx[39]
      ),
      index: (
        /*dataIndex*/
        ctx[41]
      )
    });
    const get_header_slot_changes = (dirty) => ({});
    const get_header_slot_context = (ctx) => ({});
    function create_if_block_1$3(ctx) {
      let item;
      let current;
      item = new Item({
        props: {
          type: "slot",
          uniqueKey: "header",
          $$slots: { default: [create_default_slot_2$1] },
          $$scope: { ctx }
        }
      });
      item.$on(
        "resize",
        /*onItemResized*/
        ctx[6]
      );
      return {
        c() {
          create_component(item.$$.fragment);
        },
        m(target, anchor) {
          mount_component(item, target, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          const item_changes = {};
          if (dirty[0] & /*$$scope*/
          536870912) {
            item_changes.$$scope = { dirty, ctx: ctx2 };
          }
          item.$set(item_changes);
        },
        i(local) {
          if (current) return;
          transition_in(item.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(item.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          destroy_component(item, detaching);
        }
      };
    }
    function create_default_slot_2$1(ctx) {
      let current;
      const header_slot_template = (
        /*#slots*/
        ctx[26].header
      );
      const header_slot = create_slot(
        header_slot_template,
        ctx,
        /*$$scope*/
        ctx[29],
        get_header_slot_context
      );
      return {
        c() {
          if (header_slot) header_slot.c();
        },
        m(target, anchor) {
          if (header_slot) {
            header_slot.m(target, anchor);
          }
          current = true;
        },
        p(ctx2, dirty) {
          if (header_slot) {
            if (header_slot.p && (!current || dirty[0] & /*$$scope*/
            536870912)) {
              update_slot_base(
                header_slot,
                header_slot_template,
                ctx2,
                /*$$scope*/
                ctx2[29],
                !current ? get_all_dirty_from_scope(
                  /*$$scope*/
                  ctx2[29]
                ) : get_slot_changes(
                  header_slot_template,
                  /*$$scope*/
                  ctx2[29],
                  dirty,
                  get_header_slot_changes
                ),
                get_header_slot_context
              );
            }
          }
        },
        i(local) {
          if (current) return;
          transition_in(header_slot, local);
          current = true;
        },
        o(local) {
          transition_out(header_slot, local);
          current = false;
        },
        d(detaching) {
          if (header_slot) header_slot.d(detaching);
        }
      };
    }
    function create_default_slot_1$1(ctx) {
      let t;
      let current;
      const default_slot_template = (
        /*#slots*/
        ctx[26].default
      );
      const default_slot = create_slot(
        default_slot_template,
        ctx,
        /*$$scope*/
        ctx[29],
        get_default_slot_context
      );
      return {
        c() {
          if (default_slot) default_slot.c();
          t = space();
        },
        m(target, anchor) {
          if (default_slot) {
            default_slot.m(target, anchor);
          }
          insert(target, t, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          if (default_slot) {
            if (default_slot.p && (!current || dirty[0] & /*$$scope, displayItems*/
            536870916)) {
              update_slot_base(
                default_slot,
                default_slot_template,
                ctx2,
                /*$$scope*/
                ctx2[29],
                !current ? get_all_dirty_from_scope(
                  /*$$scope*/
                  ctx2[29]
                ) : get_slot_changes(
                  default_slot_template,
                  /*$$scope*/
                  ctx2[29],
                  dirty,
                  get_default_slot_changes
                ),
                get_default_slot_context
              );
            }
          }
        },
        i(local) {
          if (current) return;
          transition_in(default_slot, local);
          current = true;
        },
        o(local) {
          transition_out(default_slot, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(t);
          }
          if (default_slot) default_slot.d(detaching);
        }
      };
    }
    function create_each_block$6(key_2, ctx) {
      let first;
      let item;
      let current;
      item = new Item({
        props: {
          uniqueKey: (
            /*dataItem*/
            ctx[39][
              /*key*/
              ctx[0]
            ]
          ),
          horizontal: (
            /*isHorizontal*/
            ctx[1]
          ),
          type: "item",
          $$slots: { default: [create_default_slot_1$1] },
          $$scope: { ctx }
        }
      });
      item.$on(
        "resize",
        /*onItemResized*/
        ctx[6]
      );
      return {
        key: key_2,
        first: null,
        c() {
          first = empty();
          create_component(item.$$.fragment);
          this.first = first;
        },
        m(target, anchor) {
          insert(target, first, anchor);
          mount_component(item, target, anchor);
          current = true;
        },
        p(new_ctx, dirty) {
          ctx = new_ctx;
          const item_changes = {};
          if (dirty[0] & /*displayItems, key*/
          5) item_changes.uniqueKey = /*dataItem*/
          ctx[39][
            /*key*/
            ctx[0]
          ];
          if (dirty[0] & /*isHorizontal*/
          2) item_changes.horizontal = /*isHorizontal*/
          ctx[1];
          if (dirty[0] & /*$$scope, displayItems*/
          536870916) {
            item_changes.$$scope = { dirty, ctx };
          }
          item.$set(item_changes);
        },
        i(local) {
          if (current) return;
          transition_in(item.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(item.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(first);
          }
          destroy_component(item, detaching);
        }
      };
    }
    function create_if_block$6(ctx) {
      let item;
      let current;
      item = new Item({
        props: {
          type: "slot",
          uniqueKey: "footer",
          $$slots: { default: [create_default_slot$2] },
          $$scope: { ctx }
        }
      });
      item.$on(
        "resize",
        /*onItemResized*/
        ctx[6]
      );
      return {
        c() {
          create_component(item.$$.fragment);
        },
        m(target, anchor) {
          mount_component(item, target, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          const item_changes = {};
          if (dirty[0] & /*$$scope*/
          536870912) {
            item_changes.$$scope = { dirty, ctx: ctx2 };
          }
          item.$set(item_changes);
        },
        i(local) {
          if (current) return;
          transition_in(item.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(item.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          destroy_component(item, detaching);
        }
      };
    }
    function create_default_slot$2(ctx) {
      let current;
      const footer_slot_template = (
        /*#slots*/
        ctx[26].footer
      );
      const footer_slot = create_slot(
        footer_slot_template,
        ctx,
        /*$$scope*/
        ctx[29],
        get_footer_slot_context
      );
      return {
        c() {
          if (footer_slot) footer_slot.c();
        },
        m(target, anchor) {
          if (footer_slot) {
            footer_slot.m(target, anchor);
          }
          current = true;
        },
        p(ctx2, dirty) {
          if (footer_slot) {
            if (footer_slot.p && (!current || dirty[0] & /*$$scope*/
            536870912)) {
              update_slot_base(
                footer_slot,
                footer_slot_template,
                ctx2,
                /*$$scope*/
                ctx2[29],
                !current ? get_all_dirty_from_scope(
                  /*$$scope*/
                  ctx2[29]
                ) : get_slot_changes(
                  footer_slot_template,
                  /*$$scope*/
                  ctx2[29],
                  dirty,
                  get_footer_slot_changes
                ),
                get_footer_slot_context
              );
            }
          }
        },
        i(local) {
          if (current) return;
          transition_in(footer_slot, local);
          current = true;
        },
        o(local) {
          transition_out(footer_slot, local);
          current = false;
        },
        d(detaching) {
          if (footer_slot) footer_slot.d(detaching);
        }
      };
    }
    function create_fragment$g(ctx) {
      let div2;
      let t0;
      let div0;
      let each_blocks = [];
      let each_1_lookup = /* @__PURE__ */ new Map();
      let t1;
      let t2;
      let div1;
      let current;
      let mounted;
      let dispose;
      let if_block0 = (
        /*$$slots*/
        ctx[8].header && create_if_block_1$3(ctx)
      );
      let each_value = ensure_array_like(
        /*displayItems*/
        ctx[2]
      );
      const get_key = (ctx2) => (
        /*dataItem*/
        ctx2[39][
          /*key*/
          ctx2[0]
        ]
      );
      for (let i = 0; i < each_value.length; i += 1) {
        let child_ctx = get_each_context$6(ctx, each_value, i);
        let key = get_key(child_ctx);
        each_1_lookup.set(key, each_blocks[i] = create_each_block$6(key, child_ctx));
      }
      let if_block1 = (
        /*$$slots*/
        ctx[8].footer && create_if_block$6(ctx)
      );
      return {
        c() {
          div2 = element("div");
          if (if_block0) if_block0.c();
          t0 = space();
          div0 = element("div");
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
          t1 = space();
          if (if_block1) if_block1.c();
          t2 = space();
          div1 = element("div");
          set_style(
            div0,
            "padding",
            /*paddingStyle*/
            ctx[3]
          );
          attr(div0, "class", "virtual-scroll-wrapper");
          attr(div1, "class", "shepherd");
          set_style(
            div1,
            "width",
            /*isHorizontal*/
            ctx[1] ? "0px" : "100%"
          );
          set_style(
            div1,
            "height",
            /*isHorizontal*/
            ctx[1] ? "100%" : "0px"
          );
          set_style(div2, "overflow-y", "auto");
          set_style(div2, "height", "inherit");
          attr(div2, "class", "virtual-scroll-root");
        },
        m(target, anchor) {
          insert(target, div2, anchor);
          if (if_block0) if_block0.m(div2, null);
          append(div2, t0);
          append(div2, div0);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(div0, null);
            }
          }
          append(div2, t1);
          if (if_block1) if_block1.m(div2, null);
          append(div2, t2);
          append(div2, div1);
          ctx[27](div1);
          ctx[28](div2);
          current = true;
          if (!mounted) {
            dispose = listen(
              div2,
              "scroll",
              /*onScroll*/
              ctx[7]
            );
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (
            /*$$slots*/
            ctx2[8].header
          ) {
            if (if_block0) {
              if_block0.p(ctx2, dirty);
              if (dirty[0] & /*$$slots*/
              256) {
                transition_in(if_block0, 1);
              }
            } else {
              if_block0 = create_if_block_1$3(ctx2);
              if_block0.c();
              transition_in(if_block0, 1);
              if_block0.m(div2, t0);
            }
          } else if (if_block0) {
            group_outros();
            transition_out(if_block0, 1, 1, () => {
              if_block0 = null;
            });
            check_outros();
          }
          if (dirty[0] & /*displayItems, key, isHorizontal, onItemResized, $$scope*/
          536870983) {
            each_value = ensure_array_like(
              /*displayItems*/
              ctx2[2]
            );
            group_outros();
            each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value, each_1_lookup, div0, outro_and_destroy_block, create_each_block$6, null, get_each_context$6);
            check_outros();
          }
          if (!current || dirty[0] & /*paddingStyle*/
          8) {
            set_style(
              div0,
              "padding",
              /*paddingStyle*/
              ctx2[3]
            );
          }
          if (
            /*$$slots*/
            ctx2[8].footer
          ) {
            if (if_block1) {
              if_block1.p(ctx2, dirty);
              if (dirty[0] & /*$$slots*/
              256) {
                transition_in(if_block1, 1);
              }
            } else {
              if_block1 = create_if_block$6(ctx2);
              if_block1.c();
              transition_in(if_block1, 1);
              if_block1.m(div2, t2);
            }
          } else if (if_block1) {
            group_outros();
            transition_out(if_block1, 1, 1, () => {
              if_block1 = null;
            });
            check_outros();
          }
          if (!current || dirty[0] & /*isHorizontal*/
          2) {
            set_style(
              div1,
              "width",
              /*isHorizontal*/
              ctx2[1] ? "0px" : "100%"
            );
          }
          if (!current || dirty[0] & /*isHorizontal*/
          2) {
            set_style(
              div1,
              "height",
              /*isHorizontal*/
              ctx2[1] ? "100%" : "0px"
            );
          }
        },
        i(local) {
          if (current) return;
          transition_in(if_block0);
          for (let i = 0; i < each_value.length; i += 1) {
            transition_in(each_blocks[i]);
          }
          transition_in(if_block1);
          current = true;
        },
        o(local) {
          transition_out(if_block0);
          for (let i = 0; i < each_blocks.length; i += 1) {
            transition_out(each_blocks[i]);
          }
          transition_out(if_block1);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div2);
          }
          if (if_block0) if_block0.d();
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].d();
          }
          if (if_block1) if_block1.d();
          ctx[27](null);
          ctx[28](null);
          mounted = false;
          dispose();
        }
      };
    }
    function instance$g($$self, $$props, $$invalidate) {
      let { $$slots: slots = {}, $$scope } = $$props;
      const $$slots = compute_slots(slots);
      let { key = "id" } = $$props;
      let { data } = $$props;
      let { keeps = 30 } = $$props;
      let { estimateSize = 50 } = $$props;
      let { isHorizontal = false } = $$props;
      let { start = 0 } = $$props;
      let { offset: offset2 = 0 } = $$props;
      let { pageMode = false } = $$props;
      let { topThreshold = 0 } = $$props;
      let { bottomThreshold = 0 } = $$props;
      let displayItems = [];
      let paddingStyle;
      let directionKey = isHorizontal ? "scrollLeft" : "scrollTop";
      let range = null;
      let virtual = new Virtual(
        {
          slotHeaderSize: 0,
          slotFooterSize: 0,
          keeps,
          estimateSize,
          buffer: Math.round(keeps / 3),
          // recommend for a third of keeps
          uniqueIds: getUniqueIdFromDataSources()
        },
        onRangeChanged
      );
      let root;
      let shepherd;
      const dispatch2 = createEventDispatcher();
      function getSize(id) {
        return virtual.sizes.get(id);
      }
      function getSizes() {
        return virtual.sizes.size;
      }
      function getOffset() {
        if (pageMode && isBrowser()) {
          return document.documentElement[directionKey] || document.body[directionKey];
        } else {
          return root ? Math.ceil(root[directionKey]) : 0;
        }
      }
      function getClientSize() {
        const key2 = isHorizontal ? "clientWidth" : "clientHeight";
        if (pageMode && isBrowser()) {
          return document.documentElement[key2] || document.body[key2];
        } else {
          return root ? Math.ceil(root[key2]) : 0;
        }
      }
      function getScrollSize() {
        const key2 = isHorizontal ? "scrollWidth" : "scrollHeight";
        if (pageMode && isBrowser()) {
          return document.documentElement[key2] || document.body[key2];
        } else {
          return root ? Math.ceil(root[key2]) : 0;
        }
      }
      function updatePageModeFront() {
        if (root && isBrowser()) {
          const rect = root.getBoundingClientRect();
          const { defaultView } = root.ownerDocument;
          const offsetFront = isHorizontal ? rect.left + defaultView.pageXOffset : rect.top + defaultView.pageYOffset;
          virtual.updateParam("slotHeaderSize", offsetFront);
        }
      }
      function scrollToOffset(offset3) {
        if (!isBrowser()) return;
        if (pageMode) {
          document.body[directionKey] = offset3;
          document.documentElement[directionKey] = offset3;
        } else if (root) {
          $$invalidate(4, root[directionKey] = offset3, root);
        }
      }
      function scrollToIndex(index) {
        if (index >= data.length - 1) {
          scrollToBottom();
        } else {
          const offset3 = virtual.getOffset(index);
          scrollToOffset(offset3);
        }
      }
      function scrollToBottom() {
        if (shepherd) {
          const offset3 = shepherd[isHorizontal ? "offsetLeft" : "offsetTop"];
          scrollToOffset(offset3);
          setTimeout(
            () => {
              if (getOffset() + getClientSize() + 1 < getScrollSize()) {
                scrollToBottom();
              }
            },
            3
          );
        }
      }
      onMount(() => {
        if (start) {
          scrollToIndex(start);
        } else if (offset2) {
          scrollToOffset(offset2);
        }
        if (pageMode) {
          updatePageModeFront();
          document.addEventListener("scroll", onScroll, { passive: false });
        }
      });
      onDestroy(() => {
        virtual.destroy();
        if (pageMode && isBrowser()) {
          document.removeEventListener("scroll", onScroll);
        }
      });
      function getUniqueIdFromDataSources() {
        return data.map((dataSource) => dataSource[key]);
      }
      function onItemResized(event) {
        const { id, size, type } = event.detail;
        if (type === "item") virtual.saveSize(id, size);
        else if (type === "slot") {
          if (id === "header") virtual.updateParam("slotHeaderSize", size);
          else if (id === "footer") virtual.updateParam("slotFooterSize", size);
        }
      }
      function onRangeChanged(range_) {
        range = range_;
        $$invalidate(3, paddingStyle = $$invalidate(3, paddingStyle = isHorizontal ? `0px ${range.padBehind}px 0px ${range.padFront}px` : `${range.padFront}px 0px ${range.padBehind}px`));
        $$invalidate(2, displayItems = data.slice(range.start, range.end + 1));
      }
      function onScroll(event) {
        const offset3 = getOffset();
        const clientSize = getClientSize();
        const scrollSize = getScrollSize();
        if (offset3 < 0 || offset3 + clientSize > scrollSize || !scrollSize) {
          return;
        }
        virtual.handleScroll(offset3);
        emitEvent(offset3, clientSize, scrollSize, event);
      }
      function emitEvent(offset3, clientSize, scrollSize, event) {
        dispatch2("scroll", { event, range: virtual.getRange() });
        if (virtual.isFront() && !!data.length && offset3 - topThreshold <= 0) {
          dispatch2("top");
        } else if (virtual.isBehind() && offset3 + clientSize + bottomThreshold >= scrollSize) {
          dispatch2("bottom");
        }
      }
      function handleKeepsChange(keeps2) {
        virtual.updateParam("keeps", keeps2);
        virtual.handleSlotSizeChange();
      }
      async function handleDataSourcesChange(data2) {
        virtual.updateParam("uniqueIds", getUniqueIdFromDataSources());
        virtual.handleDataSourcesChange();
      }
      function div1_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          shepherd = $$value;
          $$invalidate(5, shepherd);
        });
      }
      function div2_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          root = $$value;
          $$invalidate(4, root);
        });
      }
      $$self.$$set = ($$props2) => {
        if ("key" in $$props2) $$invalidate(0, key = $$props2.key);
        if ("data" in $$props2) $$invalidate(9, data = $$props2.data);
        if ("keeps" in $$props2) $$invalidate(10, keeps = $$props2.keeps);
        if ("estimateSize" in $$props2) $$invalidate(11, estimateSize = $$props2.estimateSize);
        if ("isHorizontal" in $$props2) $$invalidate(1, isHorizontal = $$props2.isHorizontal);
        if ("start" in $$props2) $$invalidate(12, start = $$props2.start);
        if ("offset" in $$props2) $$invalidate(13, offset2 = $$props2.offset);
        if ("pageMode" in $$props2) $$invalidate(14, pageMode = $$props2.pageMode);
        if ("topThreshold" in $$props2) $$invalidate(15, topThreshold = $$props2.topThreshold);
        if ("bottomThreshold" in $$props2) $$invalidate(16, bottomThreshold = $$props2.bottomThreshold);
        if ("$$scope" in $$props2) $$invalidate(29, $$scope = $$props2.$$scope);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty[0] & /*offset*/
        8192) {
          scrollToOffset(offset2);
        }
        if ($$self.$$.dirty[0] & /*start*/
        4096) {
          scrollToIndex(start);
        }
        if ($$self.$$.dirty[0] & /*keeps*/
        1024) {
          handleKeepsChange(keeps);
        }
        if ($$self.$$.dirty[0] & /*data*/
        512) {
          handleDataSourcesChange();
        }
      };
      return [
        key,
        isHorizontal,
        displayItems,
        paddingStyle,
        root,
        shepherd,
        onItemResized,
        onScroll,
        $$slots,
        data,
        keeps,
        estimateSize,
        start,
        offset2,
        pageMode,
        topThreshold,
        bottomThreshold,
        getSize,
        getSizes,
        getOffset,
        getClientSize,
        getScrollSize,
        updatePageModeFront,
        scrollToOffset,
        scrollToIndex,
        scrollToBottom,
        slots,
        div1_binding,
        div2_binding,
        $$scope
      ];
    }
    class VirtualScroll extends SvelteComponent {
      constructor(options) {
        super();
        init(
          this,
          options,
          instance$g,
          create_fragment$g,
          safe_not_equal,
          {
            key: 0,
            data: 9,
            keeps: 10,
            estimateSize: 11,
            isHorizontal: 1,
            start: 12,
            offset: 13,
            pageMode: 14,
            topThreshold: 15,
            bottomThreshold: 16,
            getSize: 17,
            getSizes: 18,
            getOffset: 19,
            getClientSize: 20,
            getScrollSize: 21,
            updatePageModeFront: 22,
            scrollToOffset: 23,
            scrollToIndex: 24,
            scrollToBottom: 25
          },
          null,
          [-1, -1]
        );
      }
      get getSize() {
        return this.$$.ctx[17];
      }
      get getSizes() {
        return this.$$.ctx[18];
      }
      get getOffset() {
        return this.$$.ctx[19];
      }
      get getClientSize() {
        return this.$$.ctx[20];
      }
      get getScrollSize() {
        return this.$$.ctx[21];
      }
      get updatePageModeFront() {
        return this.$$.ctx[22];
      }
      get scrollToOffset() {
        return this.$$.ctx[23];
      }
      get scrollToIndex() {
        return this.$$.ctx[24];
      }
      get scrollToBottom() {
        return this.$$.ctx[25];
      }
    }
    function get_each_context$5(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[42] = list[i];
      child_ctx[44] = i;
      return child_ctx;
    }
    function create_each_block$5(ctx) {
      let option;
      let t_value = (
        /*pack*/
        ctx[42] + ""
      );
      let t;
      let option_value_value;
      return {
        c() {
          option = element("option");
          t = text$1(t_value);
          option.__value = option_value_value = /*pack*/
          ctx[42];
          set_input_value(option, option.__value);
        },
        m(target, anchor) {
          insert(target, option, anchor);
          append(option, t);
        },
        p(ctx2, dirty) {
          if (dirty[0] & /*$packStore*/
          256 && t_value !== (t_value = /*pack*/
          ctx2[42] + "")) set_data(t, t_value);
          if (dirty[0] & /*$packStore*/
          256 && option_value_value !== (option_value_value = /*pack*/
          ctx2[42])) {
            option.__value = option_value_value;
            set_input_value(option, option.__value);
          }
        },
        d(detaching) {
          if (detaching) {
            detach(option);
          }
        }
      };
    }
    function create_else_block_1(ctx) {
      let div;
      let virtualscroll;
      let current;
      virtualscroll = new VirtualScroll({
        props: {
          data: (
            /*$visibleTreeStore*/
            ctx[10]
          ),
          key: "fullPath",
          $$slots: {
            default: [
              create_default_slot_2,
              ({ data }) => ({ 41: data }),
              ({ data }) => [0, data ? 1024 : 0]
            ]
          },
          $$scope: { ctx }
        }
      });
      return {
        c() {
          div = element("div");
          create_component(virtualscroll.$$.fragment);
          attr(div, "class", "sequencer-database-entries-tree svelte-seq-r6s8zx");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          mount_component(virtualscroll, div, null);
          current = true;
        },
        p(ctx2, dirty) {
          const virtualscroll_changes = {};
          if (dirty[0] & /*$visibleTreeStore*/
          1024) virtualscroll_changes.data = /*$visibleTreeStore*/
          ctx2[10];
          if (dirty[1] & /*$$scope, data*/
          17408) {
            virtualscroll_changes.$$scope = { dirty, ctx: ctx2 };
          }
          virtualscroll.$set(virtualscroll_changes);
        },
        i(local) {
          if (current) return;
          transition_in(virtualscroll.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(virtualscroll.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          destroy_component(virtualscroll);
        }
      };
    }
    function create_if_block_1$2(ctx) {
      let div;
      let virtualscroll;
      let current;
      virtualscroll = new VirtualScroll({
        props: {
          data: (
            /*filteredEntries*/
            ctx[7]
          ),
          key: "entry",
          $$slots: {
            default: [
              create_default_slot_1,
              ({ data }) => ({ 41: data }),
              ({ data }) => [0, data ? 1024 : 0]
            ]
          },
          $$scope: { ctx }
        }
      });
      return {
        c() {
          div = element("div");
          create_component(virtualscroll.$$.fragment);
          attr(div, "class", "sequencer-database-entries svelte-seq-r6s8zx");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          mount_component(virtualscroll, div, null);
          current = true;
        },
        p(ctx2, dirty) {
          const virtualscroll_changes = {};
          if (dirty[0] & /*filteredEntries*/
          128) virtualscroll_changes.data = /*filteredEntries*/
          ctx2[7];
          if (dirty[1] & /*$$scope, data*/
          17408) {
            virtualscroll_changes.$$scope = { dirty, ctx: ctx2 };
          }
          virtualscroll.$set(virtualscroll_changes);
        },
        i(local) {
          if (current) return;
          transition_in(virtualscroll.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(virtualscroll.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          destroy_component(virtualscroll);
        }
      };
    }
    function create_default_slot_2(ctx) {
      let switch_instance;
      let switch_instance_anchor;
      let current;
      var switch_value = (
        /*data*/
        ctx[41].class
      );
      function switch_props(ctx2, dirty) {
        return { props: { data: (
          /*data*/
          ctx2[41]
        ) } };
      }
      if (switch_value) {
        switch_instance = construct_svelte_component(switch_value, switch_props(ctx));
      }
      return {
        c() {
          if (switch_instance) create_component(switch_instance.$$.fragment);
          switch_instance_anchor = empty();
        },
        m(target, anchor) {
          if (switch_instance) mount_component(switch_instance, target, anchor);
          insert(target, switch_instance_anchor, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          if (dirty[1] & /*data*/
          1024 && switch_value !== (switch_value = /*data*/
          ctx2[41].class)) {
            if (switch_instance) {
              group_outros();
              const old_component = switch_instance;
              transition_out(old_component.$$.fragment, 1, 0, () => {
                destroy_component(old_component, 1);
              });
              check_outros();
            }
            if (switch_value) {
              switch_instance = construct_svelte_component(switch_value, switch_props(ctx2));
              create_component(switch_instance.$$.fragment);
              transition_in(switch_instance.$$.fragment, 1);
              mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
            } else {
              switch_instance = null;
            }
          } else if (switch_value) {
            const switch_instance_changes = {};
            if (dirty[1] & /*data*/
            1024) switch_instance_changes.data = /*data*/
            ctx2[41];
            switch_instance.$set(switch_instance_changes);
          }
        },
        i(local) {
          if (current) return;
          if (switch_instance) transition_in(switch_instance.$$.fragment, local);
          current = true;
        },
        o(local) {
          if (switch_instance) transition_out(switch_instance.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(switch_instance_anchor);
          }
          if (switch_instance) destroy_component(switch_instance, detaching);
        }
      };
    }
    function create_default_slot_1(ctx) {
      let databaseentry;
      let current;
      databaseentry = new DatabaseEntry({ props: { entry: (
        /*data*/
        ctx[41].entry
      ) } });
      return {
        c() {
          create_component(databaseentry.$$.fragment);
        },
        m(target, anchor) {
          mount_component(databaseentry, target, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          const databaseentry_changes = {};
          if (dirty[1] & /*data*/
          1024) databaseentry_changes.entry = /*data*/
          ctx2[41].entry;
          databaseentry.$set(databaseentry_changes);
        },
        i(local) {
          if (current) return;
          transition_in(databaseentry.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(databaseentry.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          destroy_component(databaseentry, detaching);
        }
      };
    }
    function create_else_block$1(ctx) {
      let t;
      return {
        c() {
          t = text$1("No file loaded...");
        },
        m(target, anchor) {
          insert(target, t, anchor);
        },
        p: noop,
        d(detaching) {
          if (detaching) {
            detach(t);
          }
        }
      };
    }
    function create_if_block$5(ctx) {
      let t0;
      let t1_value = (
        /*$metadata*/
        ctx[11].type + ""
      );
      let t1;
      let t2;
      let t3_value = (
        /*$metadata*/
        ctx[11].duration + ""
      );
      let t3;
      return {
        c() {
          t0 = text$1("Type: ");
          t1 = text$1(t1_value);
          t2 = text$1(" | Duration: ");
          t3 = text$1(t3_value);
        },
        m(target, anchor) {
          insert(target, t0, anchor);
          insert(target, t1, anchor);
          insert(target, t2, anchor);
          insert(target, t3, anchor);
        },
        p(ctx2, dirty) {
          if (dirty[0] & /*$metadata*/
          2048 && t1_value !== (t1_value = /*$metadata*/
          ctx2[11].type + "")) set_data(t1, t1_value);
          if (dirty[0] & /*$metadata*/
          2048 && t3_value !== (t3_value = /*$metadata*/
          ctx2[11].duration + "")) set_data(t3, t3_value);
        },
        d(detaching) {
          if (detaching) {
            detach(t0);
            detach(t1);
            detach(t2);
            detach(t3);
          }
        }
      };
    }
    function create_default_slot$1(ctx) {
      let div5;
      let div0;
      let select0;
      let option0;
      let t1;
      let select1;
      let option1;
      let option2;
      let option3;
      let select1_disabled_value;
      let select1_title_value;
      let t5;
      let input0;
      let t6;
      let input1;
      let t7;
      let label0;
      let t9;
      let input2;
      let t10;
      let label1;
      let t12;
      let input3;
      let t13;
      let label2;
      let t15;
      let div4;
      let current_block_type_index;
      let if_block0;
      let t16;
      let div3;
      let div1;
      let video;
      let t17;
      let img;
      let t18;
      let audio2;
      let t19;
      let div2;
      let current;
      let mounted;
      let dispose;
      let each_value = ensure_array_like(
        /*$packStore*/
        ctx[8]
      );
      let each_blocks = [];
      for (let i = 0; i < each_value.length; i += 1) {
        each_blocks[i] = create_each_block$5(get_each_context$5(ctx, each_value, i));
      }
      const if_block_creators = [create_if_block_1$2, create_else_block_1];
      const if_blocks = [];
      function select_block_type(ctx2, dirty) {
        if (
          /*$listView*/
          ctx2[5]
        ) return 0;
        return 1;
      }
      current_block_type_index = select_block_type(ctx);
      if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
      function select_block_type_1(ctx2, dirty) {
        if (
          /*$metadata*/
          ctx2[11]
        ) return create_if_block$5;
        return create_else_block$1;
      }
      let current_block_type = select_block_type_1(ctx);
      let if_block1 = current_block_type(ctx);
      return {
        c() {
          div5 = element("div");
          div0 = element("div");
          select0 = element("select");
          option0 = element("option");
          option0.textContent = `${localize("SEQUENCER.Database.AllPacks")}`;
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
          t1 = space();
          select1 = element("select");
          option1 = element("option");
          option1.textContent = `${localize("SEQUENCER.Database.All")}`;
          option2 = element("option");
          option2.textContent = `${localize("SEQUENCER.Database.Video")}`;
          option3 = element("option");
          option3.textContent = `${localize("SEQUENCER.Database.Sound")}`;
          t5 = space();
          input0 = element("input");
          t6 = space();
          input1 = element("input");
          t7 = space();
          label0 = element("label");
          label0.textContent = `${localize("SEQUENCER.Database.ShowAllRanges")}`;
          t9 = space();
          input2 = element("input");
          t10 = space();
          label1 = element("label");
          label1.textContent = `${localize("SEQUENCER.Database.ShowSubLists")}`;
          t12 = space();
          input3 = element("input");
          t13 = space();
          label2 = element("label");
          label2.textContent = `${localize("SEQUENCER.Database.ListView")}`;
          t15 = space();
          div4 = element("div");
          if_block0.c();
          t16 = space();
          div3 = element("div");
          div1 = element("div");
          video = element("video");
          video.innerHTML = ``;
          t17 = space();
          img = element("img");
          t18 = space();
          audio2 = element("audio");
          audio2.innerHTML = `<source type="audio"/>`;
          t19 = space();
          div2 = element("div");
          if_block1.c();
          option0.__value = "all";
          set_input_value(option0, option0.__value);
          attr(select0, "name", "pack-select");
          attr(select0, "class", "svelte-seq-r6s8zx");
          if (
            /*$selectedPackStore*/
            ctx[3] === void 0
          ) add_render_callback(() => (
            /*select0_change_handler*/
            ctx[28].call(select0)
          ));
          option1.__value = "all";
          set_input_value(option1, option1.__value);
          option2.__value = "video";
          set_input_value(option2, option2.__value);
          option3.__value = "sound";
          set_input_value(option3, option3.__value);
          attr(select1, "name", "file-select");
          select1.disabled = select1_disabled_value = !/*$allRanges*/
          ctx[4];
          attr(select1, "title", select1_title_value = /*$allRanges*/
          ctx[4] ? "" : localize("SEQUENCER.Database.FileTypePredicate"));
          attr(select1, "class", "svelte-seq-r6s8zx");
          if (
            /*$fileTypes*/
            ctx[1] === void 0
          ) add_render_callback(() => (
            /*select1_change_handler*/
            ctx[29].call(select1)
          ));
          attr(input0, "class", "ml-2 svelte-seq-r6s8zx");
          attr(input0, "placeholder", localize("SEQUENCER.Database.Search"));
          attr(input0, "type", "text");
          attr(input1, "class", "ml-2");
          attr(input1, "id", "database-all-ranges");
          attr(input1, "type", "checkbox");
          attr(label0, "class", "all-ranges-label svelte-seq-r6s8zx");
          attr(label0, "for", "database-all-ranges");
          attr(input2, "class", "ml-2");
          attr(input2, "id", "include-sub-lists");
          attr(input2, "type", "checkbox");
          attr(label1, "class", "all-ranges-label svelte-seq-r6s8zx");
          attr(label1, "for", "include-sub-lists");
          attr(input3, "class", "ml-2");
          attr(input3, "id", "treeview");
          attr(input3, "type", "checkbox");
          attr(label2, "class", "all-ranges-label svelte-seq-r6s8zx");
          attr(label2, "for", "treeview");
          attr(div0, "class", "sequencer-database-header svelte-seq-r6s8zx");
          video.autoplay = true;
          attr(video, "class", "database-player svelte-seq-r6s8zx");
          attr(video, "height", "335");
          video.loop = true;
          attr(video, "preload", "");
          attr(video, "width", "335");
          attr(img, "class", "database-image hidden svelte-seq-r6s8zx");
          audio2.controls = true;
          audio2.autoplay = true;
          attr(audio2, "class", "database-audio hidden svelte-seq-r6s8zx");
          attr(div1, "class", "sequencer-database-player-content svelte-seq-r6s8zx");
          attr(div2, "class", "sequencer-database-metadata-footer svelte-seq-r6s8zx");
          attr(div3, "class", "sequencer-database-player-container svelte-seq-r6s8zx");
          attr(div4, "class", "sequencer-database-entries-container svelte-seq-r6s8zx");
          attr(div5, "class", "sequencer-database-content svelte-seq-r6s8zx");
        },
        m(target, anchor) {
          insert(target, div5, anchor);
          append(div5, div0);
          append(div0, select0);
          append(select0, option0);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(select0, null);
            }
          }
          select_option(
            select0,
            /*$selectedPackStore*/
            ctx[3],
            true
          );
          append(div0, t1);
          append(div0, select1);
          append(select1, option1);
          append(select1, option2);
          append(select1, option3);
          select_option(
            select1,
            /*$fileTypes*/
            ctx[1],
            true
          );
          append(div0, t5);
          append(div0, input0);
          set_input_value(
            input0,
            /*$search*/
            ctx[2]
          );
          append(div0, t6);
          append(div0, input1);
          input1.checked = /*$allRanges*/
          ctx[4];
          append(div0, t7);
          append(div0, label0);
          append(div0, t9);
          append(div0, input2);
          input2.checked = /*$subLists*/
          ctx[9];
          append(div0, t10);
          append(div0, label1);
          append(div0, t12);
          append(div0, input3);
          input3.checked = /*$listView*/
          ctx[5];
          append(div0, t13);
          append(div0, label2);
          append(div5, t15);
          append(div5, div4);
          if_blocks[current_block_type_index].m(div4, null);
          append(div4, t16);
          append(div4, div3);
          append(div3, div1);
          append(div1, video);
          ctx[34](video);
          append(div1, t17);
          append(div1, img);
          ctx[37](img);
          append(div1, t18);
          append(div1, audio2);
          ctx[38](audio2);
          append(div3, t19);
          append(div3, div2);
          if_block1.m(div2, null);
          current = true;
          if (!mounted) {
            dispose = [
              listen(
                select0,
                "change",
                /*select0_change_handler*/
                ctx[28]
              ),
              listen(
                select1,
                "change",
                /*select1_change_handler*/
                ctx[29]
              ),
              listen(
                input0,
                "input",
                /*input0_input_handler*/
                ctx[30]
              ),
              listen(
                input1,
                "change",
                /*input1_change_handler*/
                ctx[31]
              ),
              listen(
                input2,
                "change",
                /*input2_change_handler*/
                ctx[32]
              ),
              listen(
                input3,
                "change",
                /*input3_change_handler*/
                ctx[33]
              ),
              listen(
                video,
                "mouseenter",
                /*mouseenter_handler*/
                ctx[35]
              ),
              listen(
                video,
                "mouseleave",
                /*mouseleave_handler*/
                ctx[36]
              )
            ];
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (dirty[0] & /*$packStore*/
          256) {
            each_value = ensure_array_like(
              /*$packStore*/
              ctx2[8]
            );
            let i;
            for (i = 0; i < each_value.length; i += 1) {
              const child_ctx = get_each_context$5(ctx2, each_value, i);
              if (each_blocks[i]) {
                each_blocks[i].p(child_ctx, dirty);
              } else {
                each_blocks[i] = create_each_block$5(child_ctx);
                each_blocks[i].c();
                each_blocks[i].m(select0, null);
              }
            }
            for (; i < each_blocks.length; i += 1) {
              each_blocks[i].d(1);
            }
            each_blocks.length = each_value.length;
          }
          if (dirty[0] & /*$selectedPackStore, $packStore*/
          264) {
            select_option(
              select0,
              /*$selectedPackStore*/
              ctx2[3]
            );
          }
          if (!current || dirty[0] & /*$allRanges*/
          16 && select1_disabled_value !== (select1_disabled_value = !/*$allRanges*/
          ctx2[4])) {
            select1.disabled = select1_disabled_value;
          }
          if (!current || dirty[0] & /*$allRanges*/
          16 && select1_title_value !== (select1_title_value = /*$allRanges*/
          ctx2[4] ? "" : localize("SEQUENCER.Database.FileTypePredicate"))) {
            attr(select1, "title", select1_title_value);
          }
          if (dirty[0] & /*$fileTypes*/
          2) {
            select_option(
              select1,
              /*$fileTypes*/
              ctx2[1]
            );
          }
          if (dirty[0] & /*$search*/
          4 && input0.value !== /*$search*/
          ctx2[2]) {
            set_input_value(
              input0,
              /*$search*/
              ctx2[2]
            );
          }
          if (dirty[0] & /*$allRanges*/
          16) {
            input1.checked = /*$allRanges*/
            ctx2[4];
          }
          if (dirty[0] & /*$subLists*/
          512) {
            input2.checked = /*$subLists*/
            ctx2[9];
          }
          if (dirty[0] & /*$listView*/
          32) {
            input3.checked = /*$listView*/
            ctx2[5];
          }
          let previous_block_index = current_block_type_index;
          current_block_type_index = select_block_type(ctx2);
          if (current_block_type_index === previous_block_index) {
            if_blocks[current_block_type_index].p(ctx2, dirty);
          } else {
            group_outros();
            transition_out(if_blocks[previous_block_index], 1, 1, () => {
              if_blocks[previous_block_index] = null;
            });
            check_outros();
            if_block0 = if_blocks[current_block_type_index];
            if (!if_block0) {
              if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
              if_block0.c();
            } else {
              if_block0.p(ctx2, dirty);
            }
            transition_in(if_block0, 1);
            if_block0.m(div4, t16);
          }
          if (current_block_type === (current_block_type = select_block_type_1(ctx2)) && if_block1) {
            if_block1.p(ctx2, dirty);
          } else {
            if_block1.d(1);
            if_block1 = current_block_type(ctx2);
            if (if_block1) {
              if_block1.c();
              if_block1.m(div2, null);
            }
          }
        },
        i(local) {
          if (current) return;
          transition_in(if_block0);
          current = true;
        },
        o(local) {
          transition_out(if_block0);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div5);
          }
          destroy_each(each_blocks, detaching);
          if_blocks[current_block_type_index].d();
          ctx[34](null);
          ctx[37](null);
          ctx[38](null);
          if_block1.d();
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function create_fragment$f(ctx) {
      let applicationshell;
      let updating_elementRoot;
      let current;
      function applicationshell_elementRoot_binding(value) {
        ctx[39](value);
      }
      let applicationshell_props = {
        $$slots: { default: [create_default_slot$1] },
        $$scope: { ctx }
      };
      if (
        /*elementRoot*/
        ctx[0] !== void 0
      ) {
        applicationshell_props.elementRoot = /*elementRoot*/
        ctx[0];
      }
      applicationshell = new ApplicationShell({ props: applicationshell_props });
      binding_callbacks.push(() => bind(applicationshell, "elementRoot", applicationshell_elementRoot_binding));
      return {
        c() {
          create_component(applicationshell.$$.fragment);
        },
        m(target, anchor) {
          mount_component(applicationshell, target, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          const applicationshell_changes = {};
          if (dirty[0] & /*$metadata, databaseStore, filteredEntries, $listView, $visibleTreeStore, $subLists, $allRanges, $search, $fileTypes, $selectedPackStore, $packStore*/
          4094 | dirty[1] & /*$$scope*/
          16384) {
            applicationshell_changes.$$scope = { dirty, ctx: ctx2 };
          }
          if (!updating_elementRoot && dirty[0] & /*elementRoot*/
          1) {
            updating_elementRoot = true;
            applicationshell_changes.elementRoot = /*elementRoot*/
            ctx2[0];
            add_flush_callback(() => updating_elementRoot = false);
          }
          applicationshell.$set(applicationshell_changes);
        },
        i(local) {
          if (current) return;
          transition_in(applicationshell.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(applicationshell.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          destroy_component(applicationshell, detaching);
        }
      };
    }
    function instance$f($$self, $$props, $$invalidate) {
      let $fileTypes;
      let $search;
      let $selectedPackStore;
      let $searchRegex;
      let $cleanSearchStore;
      let $allRanges;
      let $entriesStore;
      let $listView;
      let $packStore;
      let $subLists;
      let $visibleTreeStore;
      let $metadata;
      const { application } = getContext("#external");
      let { elementRoot } = $$props;
      let entries = [];
      let filteredEntries = [];
      const selectedPackStore2 = databaseStore.selectedPackStore;
      component_subscribe($$self, selectedPackStore2, (value) => $$invalidate(3, $selectedPackStore = value));
      const packStore2 = databaseStore.packStore;
      component_subscribe($$self, packStore2, (value) => $$invalidate(8, $packStore = value));
      const fileTypes = databaseStore.fileTypes;
      component_subscribe($$self, fileTypes, (value) => $$invalidate(1, $fileTypes = value));
      const metadata = databaseStore.metadata;
      component_subscribe($$self, metadata, (value) => $$invalidate(11, $metadata = value));
      const allRanges = databaseStore.allRanges;
      component_subscribe($$self, allRanges, (value) => $$invalidate(4, $allRanges = value));
      const subLists = databaseStore.subLists;
      component_subscribe($$self, subLists, (value) => $$invalidate(9, $subLists = value));
      const listView = databaseStore.listView;
      component_subscribe($$self, listView, (value) => $$invalidate(5, $listView = value));
      const visibleTreeStore2 = databaseStore.visibleTreeStore;
      component_subscribe($$self, visibleTreeStore2, (value) => $$invalidate(10, $visibleTreeStore = value));
      const search = databaseStore.search;
      component_subscribe($$self, search, (value) => $$invalidate(2, $search = value));
      const cleanSearchStore2 = databaseStore.cleanSearchStore;
      component_subscribe($$self, cleanSearchStore2, (value) => $$invalidate(26, $cleanSearchStore = value));
      const searchRegex = databaseStore.searchRegex;
      component_subscribe($$self, searchRegex, (value) => $$invalidate(25, $searchRegex = value));
      const entriesStore2 = SequencerDatabase.entriesStore;
      component_subscribe($$self, entriesStore2, (value) => $$invalidate(27, $entriesStore = value));
      listView.set(game.settings.get(CONSTANTS.MODULE_NAME, "db-list-view"));
      onDestroy(() => databaseStore.cleanupSpritesheet());
      function select0_change_handler() {
        $selectedPackStore = select_value(this);
        selectedPackStore2.set($selectedPackStore);
      }
      function select1_change_handler() {
        $fileTypes = select_value(this);
        fileTypes.set($fileTypes);
      }
      function input0_input_handler() {
        $search = this.value;
        search.set($search);
      }
      function input1_change_handler() {
        $allRanges = this.checked;
        allRanges.set($allRanges);
      }
      function input2_change_handler() {
        $subLists = this.checked;
        subLists.set($subLists);
      }
      function input3_change_handler() {
        $listView = this.checked;
        listView.set($listView);
      }
      function video_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          databaseStore.elements.player = $$value;
          $$invalidate(6, databaseStore);
        });
      }
      const mouseenter_handler = () => {
        $$invalidate(6, databaseStore.elements.player.controls = true, databaseStore);
      };
      const mouseleave_handler = () => {
        $$invalidate(6, databaseStore.elements.player.controls = false, databaseStore);
      };
      function img_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          databaseStore.elements.image = $$value;
          $$invalidate(6, databaseStore);
        });
      }
      function audio_binding($$value) {
        binding_callbacks[$$value ? "unshift" : "push"](() => {
          databaseStore.elements.audio = $$value;
          $$invalidate(6, databaseStore);
        });
      }
      function applicationshell_elementRoot_binding(value) {
        elementRoot = value;
        $$invalidate(0, elementRoot);
      }
      $$self.$$set = ($$props2) => {
        if ("elementRoot" in $$props2) $$invalidate(0, elementRoot = $$props2.elementRoot);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty[0] & /*$listView*/
        32) {
          game.settings.set(CONSTANTS.MODULE_NAME, "db-list-view", $listView);
        }
        if ($$self.$$.dirty[0] & /*$entriesStore, $allRanges*/
        134217744) {
          {
            const specificRanges = $allRanges ? Sequencer.Database.publicFlattenedEntriesAdvanced : Sequencer.Database.publicFlattenedSimpleEntries;
            $$invalidate(24, entries = specificRanges.map((entry) => {
              if (typeof entry === "string") {
                return { pack: entry.split(".")[0], entry };
              } else {
                return {
                  pack: entry.path.split(".")[0],
                  entry: entry.path,
                  isAudio: entry.isAudio
                };
              }
            }));
          }
        }
        if ($$self.$$.dirty[0] & /*$allRanges*/
        16) {
          {
            if (!$allRanges) set_store_value(fileTypes, $fileTypes = "all", $fileTypes);
          }
        }
        if ($$self.$$.dirty[0] & /*$cleanSearchStore, entries, $searchRegex, $selectedPackStore, $search, $fileTypes*/
        117440526) {
          {
            const searchParts = $cleanSearchStore.split("|");
            $$invalidate(7, filteredEntries = entries.filter((part) => {
              const matchParts = make_array_unique(part.entry.match($searchRegex) || []);
              return ($selectedPackStore === "all" || $selectedPackStore === part.pack) && ($search === "" || matchParts.length >= searchParts.length) && ($fileTypes === "all" || $fileTypes === "video" && !part.isAudio || $fileTypes === "sound" && part.isAudio);
            }));
          }
        }
      };
      return [
        elementRoot,
        $fileTypes,
        $search,
        $selectedPackStore,
        $allRanges,
        $listView,
        databaseStore,
        filteredEntries,
        $packStore,
        $subLists,
        $visibleTreeStore,
        $metadata,
        selectedPackStore2,
        packStore2,
        fileTypes,
        metadata,
        allRanges,
        subLists,
        listView,
        visibleTreeStore2,
        search,
        cleanSearchStore2,
        searchRegex,
        entriesStore2,
        entries,
        $searchRegex,
        $cleanSearchStore,
        $entriesStore,
        select0_change_handler,
        select1_change_handler,
        input0_input_handler,
        input1_change_handler,
        input2_change_handler,
        input3_change_handler,
        video_binding,
        mouseenter_handler,
        mouseleave_handler,
        img_binding,
        audio_binding,
        applicationshell_elementRoot_binding
      ];
    }
    class Database_shell extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$f, create_fragment$f, safe_not_equal, { elementRoot: 0 }, null, [-1, -1]);
      }
      get elementRoot() {
        return this.$$.ctx[0];
      }
      set elementRoot(elementRoot) {
        this.$$set({ elementRoot });
        flush();
      }
    }
    class DatabaseViewerApp extends SvelteApp {
      static get defaultOptions() {
        return foundry.utils.mergeObject(super.defaultOptions, {
          title: game.i18n.localize("SEQUENCER.Database.Title"),
          classes: ["dialog"],
          width: 900,
          height: 425,
          svelte: {
            class: Database_shell,
            target: document.body
          }
        });
      }
      static getActiveApp() {
        return Object.values(ui.windows).find((app) => {
          return app instanceof this && app._state > Application.RENDER_STATES.CLOSED;
        });
      }
      static async show(options = {}) {
        const existingApp = this.getActiveApp();
        if (existingApp) return existingApp.render(false, { focus: true });
        return new Promise((resolve) => {
          options.resolve = resolve;
          new this(options).render(true, { focus: true });
        });
      }
    }
    function create_if_block$4(ctx) {
      let p;
      let raw0_value = localize("SEQUENCER.HowTo.PermissionsExplanation") + "";
      let t0;
      let button;
      let i;
      let t1;
      let html_tag;
      let raw1_value = localize("SEQUENCER.HowTo.OpenModuleSettings") + "";
      let mounted;
      let dispose;
      return {
        c() {
          p = element("p");
          t0 = space();
          button = element("button");
          i = element("i");
          t1 = space();
          html_tag = new HtmlTag(false);
          attr(i, "class", "fas fa-plug");
          html_tag.a = null;
          attr(button, "type", "button");
          attr(button, "class", "w-100 open-module-settings");
        },
        m(target, anchor) {
          insert(target, p, anchor);
          p.innerHTML = raw0_value;
          insert(target, t0, anchor);
          insert(target, button, anchor);
          append(button, i);
          append(button, t1);
          html_tag.m(raw1_value, button);
          if (!mounted) {
            dispose = listen(
              button,
              "click",
              /*openSettings*/
              ctx[0]
            );
            mounted = true;
          }
        },
        p: noop,
        d(detaching) {
          if (detaching) {
            detach(p);
            detach(t0);
            detach(button);
          }
          mounted = false;
          dispose();
        }
      };
    }
    function create_fragment$e(ctx) {
      let div;
      let p0;
      let raw0_value = localize("SEQUENCER.HowTo.Welcome") + "";
      let t0;
      let p1;
      let raw1_value = localize("SEQUENCER.HowTo.Explanation") + "";
      let t1;
      let t2;
      let p2;
      let raw2_value = localize("SEQUENCER.HowTo.PlayerExplanation") + "";
      let t3;
      let p3;
      let raw3_value = localize("SEQUENCER.HowTo.LayerExplanation") + "";
      let t4;
      let ol;
      let li0;
      let strong0;
      let raw4_value = localize("SEQUENCER.HowTo.Click") + "";
      let br0;
      let t5;
      let html_tag;
      let raw5_value = localize("SEQUENCER.HowTo.ClickLabel") + "";
      let t6;
      let li1;
      let strong1;
      let raw6_value = localize("SEQUENCER.HowTo.ClickDrag") + "";
      let br1;
      let t7;
      let html_tag_1;
      let raw7_value = localize("SEQUENCER.HowTo.ClickDragLabel") + "";
      let t8;
      let li2;
      let strong2;
      let raw8_value = localize("SEQUENCER.HowTo.Shift") + "";
      let br2;
      let t9;
      let html_tag_2;
      let raw9_value = localize("SEQUENCER.HowTo.ShiftLabel") + "";
      let t10;
      let li3;
      let strong3;
      let raw10_value = localize("SEQUENCER.HowTo.ShiftControl") + "";
      let br3;
      let t11;
      let html_tag_3;
      let raw11_value = localize("SEQUENCER.HowTo.ShiftControlLabel") + "";
      let t12;
      let li4;
      let strong4;
      let raw12_value = localize("SEQUENCER.HowTo.MoreToCome") + "";
      let if_block = game.user.isGM && create_if_block$4(ctx);
      return {
        c() {
          div = element("div");
          p0 = element("p");
          t0 = space();
          p1 = element("p");
          t1 = space();
          if (if_block) if_block.c();
          t2 = space();
          p2 = element("p");
          t3 = space();
          p3 = element("p");
          t4 = space();
          ol = element("ol");
          li0 = element("li");
          strong0 = element("strong");
          br0 = element("br");
          t5 = space();
          html_tag = new HtmlTag(false);
          t6 = space();
          li1 = element("li");
          strong1 = element("strong");
          br1 = element("br");
          t7 = space();
          html_tag_1 = new HtmlTag(false);
          t8 = space();
          li2 = element("li");
          strong2 = element("strong");
          br2 = element("br");
          t9 = space();
          html_tag_2 = new HtmlTag(false);
          t10 = space();
          li3 = element("li");
          strong3 = element("strong");
          br3 = element("br");
          t11 = space();
          html_tag_3 = new HtmlTag(false);
          t12 = space();
          li4 = element("li");
          strong4 = element("strong");
          html_tag.a = null;
          attr(li0, "class", "svelte-seq-1gfsmnk");
          html_tag_1.a = null;
          attr(li1, "class", "svelte-seq-1gfsmnk");
          html_tag_2.a = null;
          attr(li2, "class", "svelte-seq-1gfsmnk");
          html_tag_3.a = null;
          attr(li3, "class", "svelte-seq-1gfsmnk");
          attr(li4, "class", "svelte-seq-1gfsmnk");
          attr(div, "class", "howto-container svelte-seq-1gfsmnk");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          append(div, p0);
          p0.innerHTML = raw0_value;
          append(div, t0);
          append(div, p1);
          p1.innerHTML = raw1_value;
          append(div, t1);
          if (if_block) if_block.m(div, null);
          append(div, t2);
          append(div, p2);
          p2.innerHTML = raw2_value;
          append(div, t3);
          append(div, p3);
          p3.innerHTML = raw3_value;
          append(div, t4);
          append(div, ol);
          append(ol, li0);
          append(li0, strong0);
          strong0.innerHTML = raw4_value;
          append(li0, br0);
          append(li0, t5);
          html_tag.m(raw5_value, li0);
          append(ol, t6);
          append(ol, li1);
          append(li1, strong1);
          strong1.innerHTML = raw6_value;
          append(li1, br1);
          append(li1, t7);
          html_tag_1.m(raw7_value, li1);
          append(ol, t8);
          append(ol, li2);
          append(li2, strong2);
          strong2.innerHTML = raw8_value;
          append(li2, br2);
          append(li2, t9);
          html_tag_2.m(raw9_value, li2);
          append(ol, t10);
          append(ol, li3);
          append(li3, strong3);
          strong3.innerHTML = raw10_value;
          append(li3, br3);
          append(li3, t11);
          html_tag_3.m(raw11_value, li3);
          append(ol, t12);
          append(ol, li4);
          append(li4, strong4);
          strong4.innerHTML = raw12_value;
        },
        p(ctx2, [dirty]) {
          if (game.user.isGM) if_block.p(ctx2, dirty);
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          if (if_block) if_block.d();
        }
      };
    }
    function instance$e($$self) {
      async function openSettings() {
        const settings = new SettingsConfig();
        await settings.render(true);
        await wait$1(75);
        const focusElement = settings.element.find('select[name="sequencer.permissions-effect-create"]');
        settings.element.find("div.scrollable").scrollTop(focusElement.offset().top);
        await wait$1(250);
        focusElement.css("box-shadow", "rgba(200, 64, 67, 0.75) inset 0 0 10px");
        await wait$1(5e3);
        focusElement.css("box-shadow", "none");
      }
      return [openSettings];
    }
    class HowTo extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$e, create_fragment$e, safe_not_equal, {});
      }
    }
    function get_each_context$4(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[6] = list[i];
      child_ctx[8] = i;
      return child_ctx;
    }
    function create_if_block_1$1(ctx) {
      let div;
      return {
        c() {
          div = element("div");
          set_style(div, "border-right", "1px solid rgba(0,0,0,0.5)");
          set_style(div, "margin", "0 10px");
        },
        m(target, anchor) {
          insert(target, div, anchor);
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
        }
      };
    }
    function create_if_block$3(ctx) {
      let i;
      let i_class_value;
      return {
        c() {
          i = element("i");
          attr(i, "class", i_class_value = "icon " + /*tab*/
          ctx[6].icon + " svelte-seq-123fyp");
        },
        m(target, anchor) {
          insert(target, i, anchor);
        },
        p(ctx2, dirty) {
          if (dirty & /*tabs*/
          2 && i_class_value !== (i_class_value = "icon " + /*tab*/
          ctx2[6].icon + " svelte-seq-123fyp")) {
            attr(i, "class", i_class_value);
          }
        },
        d(detaching) {
          if (detaching) {
            detach(i);
          }
        }
      };
    }
    function create_each_block$4(key_1, ctx) {
      let first;
      let t0;
      let div;
      let t1;
      let t2_value = localize(
        /*tab*/
        ctx[6].label
      ) + "";
      let t2;
      let t3;
      let mounted;
      let dispose;
      let if_block0 = (
        /*separateElements*/
        ctx[3] && /*index*/
        ctx[8] > 0 && create_if_block_1$1()
      );
      let if_block1 = (
        /*tab*/
        ctx[6].icon && create_if_block$3(ctx)
      );
      function click_handler() {
        return (
          /*click_handler*/
          ctx[5](
            /*tab*/
            ctx[6]
          )
        );
      }
      return {
        key: key_1,
        first: null,
        c() {
          first = empty();
          if (if_block0) if_block0.c();
          t0 = space();
          div = element("div");
          if (if_block1) if_block1.c();
          t1 = space();
          t2 = text$1(t2_value);
          t3 = space();
          attr(div, "class", "item item-piles-flexrow item-piles-clickable-link svelte-seq-123fyp");
          attr(div, "data-tab", "rest");
          toggle_class(
            div,
            "underscore",
            /*underscore*/
            ctx[2]
          );
          toggle_class(
            div,
            "active",
            /*activeTab*/
            ctx[0] === /*tab*/
            ctx[6].value
          );
          this.first = first;
        },
        m(target, anchor) {
          insert(target, first, anchor);
          if (if_block0) if_block0.m(target, anchor);
          insert(target, t0, anchor);
          insert(target, div, anchor);
          if (if_block1) if_block1.m(div, null);
          append(div, t1);
          append(div, t2);
          append(div, t3);
          if (!mounted) {
            dispose = listen(div, "click", click_handler);
            mounted = true;
          }
        },
        p(new_ctx, dirty) {
          ctx = new_ctx;
          if (
            /*separateElements*/
            ctx[3] && /*index*/
            ctx[8] > 0
          ) {
            if (if_block0) ;
            else {
              if_block0 = create_if_block_1$1();
              if_block0.c();
              if_block0.m(t0.parentNode, t0);
            }
          } else if (if_block0) {
            if_block0.d(1);
            if_block0 = null;
          }
          if (
            /*tab*/
            ctx[6].icon
          ) {
            if (if_block1) {
              if_block1.p(ctx, dirty);
            } else {
              if_block1 = create_if_block$3(ctx);
              if_block1.c();
              if_block1.m(div, t1);
            }
          } else if (if_block1) {
            if_block1.d(1);
            if_block1 = null;
          }
          if (dirty & /*tabs*/
          2 && t2_value !== (t2_value = localize(
            /*tab*/
            ctx[6].label
          ) + "")) set_data(t2, t2_value);
          if (dirty & /*underscore*/
          4) {
            toggle_class(
              div,
              "underscore",
              /*underscore*/
              ctx[2]
            );
          }
          if (dirty & /*activeTab, tabs*/
          3) {
            toggle_class(
              div,
              "active",
              /*activeTab*/
              ctx[0] === /*tab*/
              ctx[6].value
            );
          }
        },
        d(detaching) {
          if (detaching) {
            detach(first);
            detach(t0);
            detach(div);
          }
          if (if_block0) if_block0.d(detaching);
          if (if_block1) if_block1.d();
          mounted = false;
          dispose();
        }
      };
    }
    function create_fragment$d(ctx) {
      let nav;
      let each_blocks = [];
      let each_1_lookup = /* @__PURE__ */ new Map();
      let nav_style_value;
      let each_value = ensure_array_like(
        /*tabs*/
        ctx[1].filter(func)
      );
      const get_key = (ctx2) => (
        /*tab*/
        ctx2[6].value
      );
      for (let i = 0; i < each_value.length; i += 1) {
        let child_ctx = get_each_context$4(ctx, each_value, i);
        let key = get_key(child_ctx);
        each_1_lookup.set(key, each_blocks[i] = create_each_block$4(key, child_ctx));
      }
      return {
        c() {
          nav = element("nav");
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
          attr(nav, "class", "tabs svelte-seq-123fyp");
          attr(nav, "data-group", "primary");
          attr(nav, "style", nav_style_value = /*$$props*/
          ctx[4].style);
        },
        m(target, anchor) {
          insert(target, nav, anchor);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(nav, null);
            }
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*underscore, activeTab, tabs, separateElements*/
          15) {
            each_value = ensure_array_like(
              /*tabs*/
              ctx2[1].filter(func)
            );
            each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value, each_1_lookup, nav, destroy_block, create_each_block$4, null, get_each_context$4);
          }
          if (dirty & /*$$props*/
          16 && nav_style_value !== (nav_style_value = /*$$props*/
          ctx2[4].style)) {
            attr(nav, "style", nav_style_value);
          }
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(nav);
          }
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].d();
          }
        }
      };
    }
    const func = (tab) => !tab.hidden;
    function instance$d($$self, $$props, $$invalidate) {
      let { activeTab } = $$props;
      let { tabs } = $$props;
      let { underscore = false } = $$props;
      let { separateElements = false } = $$props;
      const click_handler = (tab) => {
        $$invalidate(0, activeTab = tab.value);
      };
      $$self.$$set = ($$new_props) => {
        $$invalidate(4, $$props = assign(assign({}, $$props), exclude_internal_props($$new_props)));
        if ("activeTab" in $$new_props) $$invalidate(0, activeTab = $$new_props.activeTab);
        if ("tabs" in $$new_props) $$invalidate(1, tabs = $$new_props.tabs);
        if ("underscore" in $$new_props) $$invalidate(2, underscore = $$new_props.underscore);
        if ("separateElements" in $$new_props) $$invalidate(3, separateElements = $$new_props.separateElements);
      };
      $$props = exclude_internal_props($$props);
      return [activeTab, tabs, underscore, separateElements, $$props, click_handler];
    }
    class Tabs extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$d, create_fragment$d, safe_not_equal, {
          activeTab: 0,
          tabs: 1,
          underscore: 2,
          separateElements: 3
        });
      }
    }
    const SequenceManager = {
      VisibleEffects: writable({}),
      RunningSounds: writable({}),
      RunningSequences: writable({})
    };
    SequenceManager.VisibleEffects.get = (id) => {
      return get_store_value(SequenceManager.VisibleEffects)[id];
    };
    SequenceManager.VisibleEffects.add = (id, data) => {
      SequenceManager.VisibleEffects.update((effects) => {
        effects[id] = data;
        return effects;
      });
    };
    SequenceManager.VisibleEffects.delete = (id) => {
      SequenceManager.VisibleEffects.update((effects) => {
        delete effects[id];
        return effects;
      });
    };
    SequenceManager.VisibleEffects.values = () => {
      return Object.values(get_store_value(SequenceManager.VisibleEffects));
    };
    SequenceManager.RunningSounds.get = (id) => {
      return get_store_value(SequenceManager.RunningSounds)[id];
    };
    SequenceManager.RunningSounds.add = (id, data) => {
      SequenceManager.RunningSounds.update((sounds) => {
        sounds[id] = data;
        return sounds;
      });
    };
    SequenceManager.RunningSounds.delete = (id) => {
      SequenceManager.RunningSounds.update((sounds) => {
        delete sounds[id];
        return sounds;
      });
    };
    SequenceManager.RunningSounds.values = () => {
      return Object.values(get_store_value(SequenceManager.RunningSounds));
    };
    SequenceManager.RunningSounds.keys = () => {
      return Object.keys(get_store_value(SequenceManager.RunningSounds));
    };
    SequenceManager.RunningSequences.get = (id) => {
      return get_store_value(SequenceManager.RunningSequences)[id];
    };
    SequenceManager.RunningSequences.add = (id, sequence) => {
      SequenceManager.RunningSequences.update((sequences) => {
        sequences[id] = sequence;
        return sequences;
      });
    };
    SequenceManager.RunningSequences.delete = (id) => {
      SequenceManager.RunningSequences.update((sequences) => {
        delete sequences[id];
        return sequences;
      });
    };
    SequenceManager.RunningSequences.clearFinishedSequences = () => {
      SequenceManager.RunningSequences.update((sequences) => {
        for (const sequence of Object.values(sequences)) {
          if (sequence.status === CONSTANTS.STATUS.COMPLETE || sequence.status === CONSTANTS.STATUS.ABORTED) {
            delete sequences[sequence.id];
          }
        }
        return sequences;
      });
    };
    SequenceManager.RunningSequences.stopAll = () => {
      SequenceManager.RunningSequences.update((sequences) => {
        for (const sequence of Object.values(sequences)) {
          sequence._abort();
        }
        return sequences;
      });
    };
    SequenceManager.RunningSequences.values = () => {
      return Object.values(get_store_value(SequenceManager.RunningSequences));
    };
    class ColorMatrixFilter extends globalThis.PIXI.ColorMatrixFilter {
      /**
       * Properties & default values:
       *     - hue [false]
       *     - brightness [1]
       *     - contrast [1]
       *     - saturate [1]
       */
      constructor(inData) {
        super();
        this.isValid = true;
        this.values = /* @__PURE__ */ new Map();
        for (let [key, value] of Object.entries(inData)) {
          this.setValue(key, value);
          if (!this.isValid) break;
        }
      }
      setValue(key, value) {
        try {
          this.values[key] = value;
          super[key](value, true);
        } catch (err) {
          ui.notifications.warn(
            `Sequencer | ${this.constructor.name} | Could not set property ${key}`
          );
        }
      }
      get hue() {
        return this.values?.hue ?? 0;
      }
      set hue(value) {
        this.values.hue = value;
        super.hue(value);
        this.setOthers("hue");
      }
      get contrast() {
        return this.values?.contrast ?? 0;
      }
      set contrast(value) {
        this.values.contrast = value;
        super.contrast(value);
      }
      get brightness() {
        return this.values?.brightness ?? 0;
      }
      set brightness(value) {
        this.values.brightness = value;
        super.brightness(value);
      }
      get saturate() {
        return this.values?.saturate ?? 0;
      }
      set saturate(value) {
        this.values.saturate = value;
        super.saturate(value);
      }
      setOthers(ignoreKey) {
        for (let [key, value] of this.values.entries) {
          if (key === ignoreKey) continue;
          this.setValue(key, value);
        }
      }
    }
    class BlurFilter extends globalThis.PIXI.BlurFilter {
      /**
       * Properties & default values:
       *     - strength [8]
       *     - blur [2]
       *     - blurX [2]
       *     - blurY [2]
       *     - quality [4]
       *     - resolution [PIXI.settings.FILTER_RESOLUTION]
       *     - kernelSize [5]
       */
      constructor(inData = {}) {
        inData = foundry.utils.mergeObject(
          {
            strength: 1,
            quality: 4,
            resolution: PIXI.settings.FILTER_RESOLUTION,
            kernelSize: 5
          },
          inData
        );
        super(...Object.values(inData));
        this.isValid = true;
        for (let [key, value] of Object.entries(inData)) {
          try {
            this[key] = value;
          } catch (err) {
            ui.notifications.warn(
              `Sequencer | ${this.constructor.name} | Could not set property ${key}`
            );
            this.isValid = false;
          }
        }
      }
    }
    class NoiseFilter extends globalThis.PIXI.NoiseFilter {
      /**
       * Properties & default values:
       *     - noise [0.5]
       *     - seed [Math.random()]
       */
      constructor(inData = {}) {
        super();
        inData = foundry.utils.mergeObject(
          {
            noise: 0.5,
            seed: Math.random()
          },
          inData
        );
        this.isValid = true;
        for (let [key, value] of Object.entries(inData)) {
          try {
            this[key] = value;
          } catch (err) {
            ui.notifications.warn(
              `Sequencer | ${this.constructor.name} | Could not set property ${key}`
            );
            this.isValid = false;
          }
        }
      }
    }
    class GlowFilter extends globalThis.PIXI.Filter {
      static defaults = {
        distance: 10,
        outerStrength: 4,
        innerStrength: 0,
        color: 16777215,
        quality: 0.1,
        knockout: false,
        alpha: 1
      };
      constructor(options) {
        const opts = Object.assign({}, GlowFilter.defaults, options);
        const {
          outerStrength,
          innerStrength,
          color,
          knockout,
          quality,
          alpha
        } = opts;
        const distance = Math.round(opts.distance);
        super(vertex, frag.replace(/__ANGLE_STEP_SIZE__/gi, `${(1 / quality / distance).toFixed(7)}`).replace(/__DIST__/gi, `${distance.toFixed(0)}.0`));
        this.uniforms.glowColor = new Float32Array([0, 0, 0, 1]);
        this.uniforms.alpha = 1;
        Object.assign(this, {
          color,
          outerStrength,
          innerStrength,
          padding: distance,
          knockout,
          alpha
        });
        this.isValid = true;
      }
      /**
       * The color of the glow.
       * @default 0xFFFFFF
       */
      get color() {
        return PIXI.utils.rgb2hex(this.uniforms.glowColor);
      }
      set color(value) {
        PIXI.utils.hex2rgb(value, this.uniforms.glowColor);
      }
      /**
       * The strength of the glow outward from the edge of the sprite.
       * @default 4
       */
      get outerStrength() {
        return this.uniforms.outerStrength;
      }
      set outerStrength(value) {
        this.uniforms.outerStrength = value;
      }
      /**
       * The strength of the glow inward from the edge of the sprite.
       * @default 0
       */
      get innerStrength() {
        return this.uniforms.innerStrength;
      }
      set innerStrength(value) {
        this.uniforms.innerStrength = value;
      }
      /**
       * Only draw the glow, not the texture itself
       * @default false
       */
      get knockout() {
        return this.uniforms.knockout;
      }
      set knockout(value) {
        this.uniforms.knockout = value;
      }
      /**
       * The alpha value of the glow
       * @default 1
       */
      get alpha() {
        return this.uniforms.alpha;
      }
      set alpha(value) {
        this.uniforms.alpha = value;
      }
    }
    const frag = `varying vec2 vTextureCoord;
varying vec4 vColor;

uniform sampler2D uSampler;

uniform float outerStrength;
uniform float innerStrength;

uniform vec4 glowColor;

uniform vec4 filterArea;
uniform vec4 filterClamp;
uniform bool knockout;
uniform float alpha;

const float PI = 3.14159265358979323846264;

const float DIST = __DIST__;
const float ANGLE_STEP_SIZE = min(__ANGLE_STEP_SIZE__, PI * 2.0);
const float ANGLE_STEP_NUM = ceil(PI * 2.0 / ANGLE_STEP_SIZE);

const float MAX_TOTAL_ALPHA = ANGLE_STEP_NUM * DIST * (DIST + 1.0) / 2.0;

void main(void) {
    vec2 px = vec2(1.0 / filterArea.x, 1.0 / filterArea.y);

    float totalAlpha = 0.0;

    vec2 direction;
    vec2 displaced;
    vec4 curColor;

    for (float angle = 0.0; angle < PI * 2.0; angle += ANGLE_STEP_SIZE) {
       direction = vec2(cos(angle), sin(angle)) * px;

       for (float curDistance = 0.0; curDistance < DIST; curDistance++) {
           displaced = clamp(vTextureCoord + direction * 
                   (curDistance + 1.0), filterClamp.xy, filterClamp.zw);

           curColor = texture2D(uSampler, displaced);

           totalAlpha += (DIST - curDistance) * curColor.a;
       }
    }
    
    curColor = texture2D(uSampler, vTextureCoord);

    float alphaRatio = (totalAlpha / MAX_TOTAL_ALPHA);

    float innerGlowAlpha = (1.0 - alphaRatio) * innerStrength * curColor.a;
    float innerGlowStrength = min(1.0, innerGlowAlpha);
    
    vec4 innerColor = mix(curColor, glowColor, innerGlowStrength);

    float outerGlowAlpha = alphaRatio * outerStrength * (1. - curColor.a);
    float outerGlowStrength = min(1.0 - innerColor.a, outerGlowAlpha);

    if (knockout) {
      float resultAlpha = (outerGlowAlpha + innerGlowAlpha) * alpha;
      gl_FragColor = vec4(glowColor.rgb * resultAlpha, resultAlpha);
    }
    else {
      vec4 outerGlowColor = outerGlowStrength * glowColor.rgba * alpha;
      gl_FragColor = innerColor + outerGlowColor;
    }
}`;
    const vertex = `attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;

uniform mat3 projectionMatrix;

varying vec2 vTextureCoord;

void main(void)
{
    gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
    vTextureCoord = aTextureCoord;
}`;
    let shader = `
uniform sampler2D uSampler;
varying vec2 vTextureCoord;
float alpha;

void main() {
    vec4 pixel = texture2D(uSampler, vTextureCoord);
    alpha = smoothstep(0.6,1.0,pixel.a);
    gl_FragColor = vec4(alpha, alpha, alpha, pixel.a);
}
`;
    class ClipFilter extends PIXI.Filter {
      constructor() {
        super(null, shader);
      }
    }
    const filters = {
      ColorMatrix: ColorMatrixFilter,
      Blur: BlurFilter,
      Noise: NoiseFilter,
      Glow: GlowFilter,
      Clip: ClipFilter
    };
    class MaskFilter extends AbstractBaseFilter {
      /** @override */
      static fragmentShader = `        varying vec2 vTextureCoord;

        uniform sampler2D uSampler;
        uniform sampler2D uMaskSampler;

        void main(void) {
            gl_FragColor = texture2D(uSampler, vTextureCoord)
                * texture2D(uMaskSampler, vTextureCoord).a;
        }`;
      /** @override */
      static defaultUniforms = { uMaskSampler: null };
      /** @type {DisplayObject[]|null} */
      masks = [];
      /** @override */
      apply(filterManager, input, output, clearMode, currentState) {
        const maskFilterTexture = filterManager.getFilterTexture();
        const originalFilterTexture = this.#push(
          filterManager,
          currentState,
          maskFilterTexture
        );
        const renderer = filterManager.renderer;
        for (const mask of this.masks) {
          if (mask?.obj?.destroyed) continue;
          const renderable = mask.renderable;
          mask.renderable = true;
          mask.render(renderer);
          mask.renderable = renderable;
        }
        renderer.batch.flush();
        this.#pop(filterManager, currentState, originalFilterTexture);
        this.uniforms.uMaskSampler = maskFilterTexture;
        filterManager.applyFilter(this, input, output, clearMode);
        filterManager.returnFilterTexture(maskFilterTexture);
      }
      #push(filterManager, currentState, maskFilterTexture) {
        const originalFilterTexture = currentState.renderTexture;
        currentState.renderTexture = maskFilterTexture;
        filterManager.defaultFilterStack.push(currentState);
        filterManager.bindAndClear(maskFilterTexture);
        return originalFilterTexture;
      }
      #pop(filterManager, currentState, originalFilterTexture) {
        currentState.renderTexture = originalFilterTexture;
        filterManager.defaultFilterStack.pop();
        if (filterManager.activeState === currentState) {
          return;
        }
        filterManager.activeState = currentState;
        const globalUniforms = filterManager.globalUniforms.uniforms;
        globalUniforms.outputFrame = currentState.sourceFrame;
        globalUniforms.resolution = currentState.resolution;
        const inputSize = globalUniforms.inputSize;
        const inputPixel = globalUniforms.inputPixel;
        const inputClamp = globalUniforms.inputClamp;
        inputSize[0] = currentState.destinationFrame.width;
        inputSize[1] = currentState.destinationFrame.height;
        inputSize[2] = 1 / inputSize[0];
        inputSize[3] = 1 / inputSize[1];
        inputPixel[0] = Math.round(inputSize[0] * currentState.resolution);
        inputPixel[1] = Math.round(inputSize[1] * currentState.resolution);
        inputPixel[2] = 1 / inputPixel[0];
        inputPixel[3] = 1 / inputPixel[1];
        inputClamp[0] = 0.5 * inputPixel[2];
        inputClamp[1] = 0.5 * inputPixel[3];
        inputClamp[2] = currentState.sourceFrame.width * inputSize[2] - 0.5 * inputPixel[2];
        inputClamp[3] = currentState.sourceFrame.height * inputSize[3] - 0.5 * inputPixel[3];
        if (currentState.legacy) {
          const filterArea = globalUniforms.filterArea;
          filterArea[0] = currentState.destinationFrame.width;
          filterArea[1] = currentState.destinationFrame.height;
          filterArea[2] = currentState.sourceFrame.x;
          filterArea[3] = currentState.sourceFrame.y;
          globalUniforms.filterClamp = globalUniforms.inputClamp;
        }
        filterManager.globalUniforms.update();
      }
    }
    const SequencerAnimationEngine = {
      _animations: [],
      _debug: void 0,
      _deltas: [],
      ticker: false,
      dt: 0,
      isRunning: false,
      addAnimation(origin, attributes = [], timeDifference = 0) {
        if (!Array.isArray(attributes)) attributes = [attributes];
        return new Promise((resolve) => {
          this._animations.push({
            origin,
            attributes: attributes.map((attribute) => {
              attribute.targetId = get_object_identifier(attribute.target) + "-" + attribute.propertyName;
              attribute.id = foundry.utils.randomID();
              attribute.started = false;
              attribute.initialized = false;
              attribute.finishing = false;
              attribute.complete = false;
              attribute.previousValue = null;
              attribute.progress = 0;
              attribute.value = 0;
              attribute.coreValue = 0;
              attribute.isFunkyProperty = attribute.propertyName.startsWith("scale.");
              attribute.duration = attribute.duration ?? 0;
              attribute.durationDone = timeDifference ?? 0;
              if (attribute?.looping) {
                attribute.loopDuration = attribute.loopDuration ?? attribute.duration ?? 0;
                attribute.loopDurationDone = timeDifference % attribute.loopDuration;
                attribute.loops = attribute.loops ?? 0;
                attribute.loopsDone = Math.floor(
                  attribute.durationDone / attribute.duration
                );
                attribute.index = attribute.loopsDone % attribute.values.length;
                attribute.nextIndex = (attribute.loopsDone + 1) % attribute.values.length;
                if (!attribute.pingPong && attribute.nextIndex === 0) {
                  attribute.index = 0;
                  attribute.nextIndex = 1;
                }
              }
              return attribute;
            }),
            complete: false,
            totalDt: timeDifference,
            resolve
          });
          this._animations.sort((a, b) => {
            const aNumAbsolute = a.attributes.filter((attr2) => !attr2.complete && attr2.absolute).length;
            const bNumAbsolute = b.attributes.filter((attr2) => !attr2.complete && attr2.absolute).length;
            return bNumAbsolute - aNumAbsolute;
          });
          if (!this.ticker) {
            this.start();
          }
          debug(`Added animations to Animation Engine`);
        });
      },
      endAnimations(target) {
        this._animations = this._animations.filter(
          (animation2) => animation2.origin !== target
        );
      },
      updateStartValues(target, propertyName) {
        const targetId = get_object_identifier(target) + "-" + propertyName;
        if (targetId in this._coreValues) {
          this._coreValues[targetId].value = deep_get(target, propertyName);
        }
      },
      start() {
        if (!this.ticker) {
          debug(`Animation Engine Started`);
          this.ticker = CanvasAnimation.ticker;
          this.ticker.add(this.nextFrame.bind(this));
        }
      },
      nextFrame() {
        if (this._animations.length === 0) {
          this._coreValues = {};
          return;
        }
        this._storedValues = {};
        this._animations.forEach((animation2) => this._animate(animation2));
        for (let delta of Object.values(this._storedValues)) {
          try {
            deep_set(delta.target, delta.propertyName, delta.value);
          } catch (err) {
          }
        }
        this._animations = this._animations.filter((animation2) => !animation2.complete);
      },
      _coreValues: {},
      _storedValues: {},
      _animate(animation2) {
        animation2.totalDt += this.ticker.elapsedMS;
        animation2.attributes.filter((attribute) => {
          return !attribute.complete && animation2.totalDt >= attribute.delay;
        }).forEach((attr2) => {
          this._animateAttribute(attr2);
          if (attr2.finishing) {
            attr2.complete = true;
            if (this._coreValues[attr2.targetId]) {
              delete this._coreValues[attr2.targetId];
            }
          }
        });
        animation2.complete = animation2.attributes.filter((attribute) => !attribute.complete).length === 0;
        if (animation2.complete) {
          animation2.resolve();
        }
      },
      _animateAttribute(attribute) {
        if (this._coreValues[attribute.targetId] === void 0) {
          this._coreValues[attribute.targetId] = {
            id: attribute.id,
            value: deep_get(attribute.target, attribute.propertyName)
          };
        }
        if (this._storedValues[attribute.targetId] === void 0) {
          this._storedValues[attribute.targetId] = {
            value: this._coreValues[attribute.targetId].value,
            target: attribute.target,
            propertyName: attribute.propertyName,
            targetId: attribute.targetId
          };
        }
        if (attribute?.looping && attribute?.indefinite) {
          this._handleIndefiniteLoop(attribute);
        } else if (attribute?.looping) {
          this._handleLoops(attribute);
        } else {
          this._handleDefault(attribute);
        }
        if (attribute.absolute) {
          this._coreValues[attribute.targetId].value = attribute.value;
          const delta = this._coreValues[attribute.targetId].value - this._storedValues[attribute.targetId].value;
          this._storedValues[attribute.targetId].value += delta;
        } else {
          if (attribute.isFunkyProperty) {
            const coreValue = this._coreValues[attribute.targetId].value;
            attribute.delta = coreValue * attribute.value - coreValue;
            this._storedValues[attribute.targetId].value += attribute.delta;
          } else {
            this._storedValues[attribute.targetId].value += attribute.value;
          }
        }
      },
      _handleBaseLoop(attribute) {
        if (!attribute.initialized) {
          if (attribute.values.length === 1) {
            attribute.values.unshift(this._coreValues[attribute.targetId].value);
          }
          attribute.initialized = true;
        }
        attribute.loopDurationDone += this.ticker.deltaMS;
        attribute.progress = attribute.loopDurationDone / attribute.loopDuration;
        attribute.value = interpolate(
          attribute.values[attribute.index],
          attribute.values[attribute.nextIndex],
          attribute.progress,
          attribute.ease
        );
        if (attribute.progress >= 1) {
          attribute.loopDurationDone -= attribute.loopDuration;
          attribute.index = (attribute.index + 1) % attribute.values.length;
          attribute.nextIndex = (attribute.nextIndex + 1) % attribute.values.length;
          if (!attribute.pingPong && attribute.nextIndex === 0) {
            attribute.index = 0;
            attribute.nextIndex = 1;
          }
          attribute.loopsDone++;
          attribute.value = interpolate(
            attribute.values[attribute.index],
            attribute.values[attribute.nextIndex],
            attribute.progress % 1,
            attribute.ease
          );
        }
      },
      _handleIndefiniteLoop(attribute) {
        return this._handleBaseLoop(attribute);
      },
      _handleLoops(attribute) {
        this._handleBaseLoop(attribute);
        attribute.durationDone += this.ticker.deltaMS;
        attribute.overallProgress = attribute.durationDone / attribute.duration;
        if (attribute.progress >= 1 && attribute.loopsDone === attribute.loops * 2) {
          attribute.finishing = true;
          attribute.value = attribute.values[attribute.index];
        }
        if (attribute.overallProgress >= 1) {
          attribute.finishing = true;
        }
      },
      _handleDefault(attribute) {
        if (!attribute.initialized) {
          if (attribute.from === void 0) {
            attribute.from = this._coreValues[attribute.targetId].value;
          }
          attribute.initialized = true;
        }
        attribute.durationDone += this.ticker.deltaMS;
        attribute.progress = attribute.durationDone / attribute.duration;
        attribute.value = interpolate(
          attribute.from,
          attribute.to,
          clamp$1(attribute.progress, 0, 1),
          attribute.ease
        );
        if (attribute.progress >= 1) {
          attribute.value = attribute.to;
          attribute.finishing = true;
        }
      }
    };
    const flagManager = {
      flagEffectAddBuffer: /* @__PURE__ */ new Map(),
      flagEffectRemoveBuffer: /* @__PURE__ */ new Map(),
      flagSoundAddBuffer: /* @__PURE__ */ new Map(),
      flagSoundRemoveBuffer: /* @__PURE__ */ new Map(),
      _latestFlagVersion: false,
      get latestFlagVersion() {
        if (!this._latestFlagVersion) {
          const versions = Object.keys(this.effectMigrations);
          versions.sort((a, b) => {
            return foundry.utils.isNewerVersion(a, b) ? -1 : 1;
          });
          this._latestFlagVersion = versions[0];
        }
        return this._latestFlagVersion;
      },
      /**
       * Sanitizes the effect data, accounting for changes to the structure in previous versions
       *
       * @param inDocument
       * @returns {array}
       */
      getEffectFlags(inDocument) {
        let effects = foundry.utils.getProperty(inDocument, CONSTANTS.EFFECTS_FLAG);
        if (!effects?.length) return [];
        effects = foundry.utils.deepClone(effects);
        const changes = [];
        for (let [effectId, effectData] of effects) {
          let effectVersion = effectData?.flagVersion ?? "1.0.0";
          let changesAdded = false;
          if (effectData._id !== effectId) {
            changesAdded = true;
            effectData._id = effectId;
            changes.push(effectData);
            debug(
              `Fixed effect with broken ID ${effectId}`
            );
          }
          if (effectData.flagVersion === this.latestFlagVersion) continue;
          for (let [version, migration] of Object.entries(this.effectMigrations)) {
            if (!foundry.utils.isNewerVersion(version, effectVersion)) continue;
            effectData = migration(inDocument, effectData);
          }
          debug(
            `Migrated effect with ID ${effectId} from version ${effectVersion} to version ${this.latestFlagVersion}`
          );
          effectData.flagVersion = this.latestFlagVersion;
          if (!changesAdded) changes.push(effectData);
        }
        if (changes.length) {
          flagManager.addEffectFlags(inDocument.uuid, changes);
        }
        return effects;
      },
      effectMigrations: {
        "2.0.0": (inDocument, effectData) => {
          effectData._id = effectData.id;
          effectData.creationTimestamp = effectData.timestamp;
          if (effectData.template) {
            effectData.template = {
              gridSize: effectData.template[0],
              startPoint: effectData.template[1],
              endPoint: effectData.template[2]
            };
          }
          if (effectData.attachTo) {
            effectData.attachTo = {
              active: true,
              align: "center",
              rotation: true,
              bindVisibility: true,
              bindAlpha: true
            };
            effectData.source = inDocument.uuid;
            const objectSize = get_object_dimensions(inDocument, true);
            effectData.offset = {
              x: effectData.position.x - objectSize.width,
              y: effectData.position.y - objectSize.height
            };
          } else if (effectData.position) {
            effectData.source = effectData.position;
          }
          if (effectData.reachTowards) {
            effectData.stretchTo = {
              attachTo: false,
              onlyX: false
            };
          }
          if (effectData.filters) {
            effectData.filters = Object.entries(effectData.filters).map((entry) => {
              return {
                className: entry[0],
                ...entry[1]
              };
            });
          }
          effectData.moveSpeed = effectData.speed;
          effectData.target = null;
          effectData.forcedIndex = null;
          effectData.flipX = false;
          effectData.flipY = false;
          effectData.nameOffsetMap = {};
          effectData.sequenceId = 0;
          delete effectData.id;
          delete effectData.timestamp;
          delete effectData.position;
          delete effectData.reachTowards;
          delete effectData.speed;
          delete effectData.audioVolume;
          delete effectData.gridSizeDifference;
          delete effectData.template;
          if (effectData.animatedProperties) {
            delete effectData.animatedProperties.fadeInAudio;
            delete effectData.animatedProperties.fadeOutAudio;
          }
          effectData = foundry.utils.mergeObject(
            effectData,
            effectData.animatedProperties
          );
          delete effectData.animatedProperties;
          return effectData;
        },
        "2.0.6": (inDocument, effectData) => {
          effectData.private = null;
          return effectData;
        },
        "2.0.8": (inDocument, effectData) => {
          if (effectData.stretchTo) {
            effectData.stretchTo.tiling = false;
          }
          return effectData;
        },
        "2.0.9": (inDocument, effectData) => {
          effectData.tilingTexture = false;
          if (effectData.stretchTo?.tiling !== void 0) {
            if (effectData.stretchTo.tiling) {
              effectData.tilingTexture = {
                scale: { x: 1, y: 1 },
                position: { x: 0, y: 0 }
              };
            }
            delete effectData.stretchTo.tiling;
          }
          return effectData;
        },
        "2.1.0": (inDocument, effectData) => {
          if (effectData.randomOffset) {
            effectData.randomOffset = {
              source: !effectData.target ? effectData.randomOffset : false,
              target: !!effectData.target ? effectData.randomOffset : false
            };
          }
          if (effectData.nameOffsetMap) {
            Object.values(effectData.nameOffsetMap).forEach((offsetMap) => {
              if (offsetMap.randomOffset) {
                offsetMap.randomOffset = {
                  source: !offsetMap.target ? offsetMap.randomOffset : false,
                  target: !!offsetMap.target ? offsetMap.randomOffset : false
                };
              }
            });
          }
          return effectData;
        },
        "3.2.0": (inDocument, effectData) => {
          if (effectData.attachTo?.followRotation !== void 0) {
            effectData.attachTo.bindRotation = effectData.attachTo.followRotation;
            delete effectData.attachTo.followRotation;
          }
          if (effectData.scaleToObject) {
            if (effectData.attachTo?.active) {
              effectData.attachTo.bindScale = true;
            } else if (!effectData.attachTo?.active && !effectData.attachTo?.bindScale) {
              effectData.scaleToObject = false;
              effectData.size = get_object_dimensions(inDocument);
            }
          }
          return effectData;
        },
        "3.2.1": (inDocument, effectData) => {
          if (effectData.noLoop !== void 0) {
            effectData.loopOptions = {
              loops: 1,
              loopDelay: 0,
              endOnLastLoop: false
            };
            delete effectData.noLoop;
          }
          return effectData;
        }
      },
      /**
       * Adds effects to a given document
       *
       * @param inObjectUUID
       * @param inEffects
       */
      addEffectFlags: (inObjectUUID, inEffects) => {
        if (!Array.isArray(inEffects)) inEffects = [inEffects];
        sequencerSocket.executeAsGM(
          SOCKET_HANDLERS.ADD_EFFECT_FLAGS,
          inObjectUUID,
          inEffects
        );
      },
      /**
       * Removes effects from a given document
       *
       * @param inObjectUUID
       * @param inEffects
       * @param removeAll
       */
      removeEffectFlags: (inObjectUUID, inEffects, removeAll = false) => {
        sequencerSocket.executeAsGM(
          SOCKET_HANDLERS.REMOVE_EFFECT_FLAGS,
          inObjectUUID,
          inEffects,
          removeAll
        );
      },
      _addEffectFlags: (inObjectUUID, inEffects) => {
        if (!Array.isArray(inEffects)) inEffects = [inEffects];
        let flagsToSet = flagManager.flagEffectAddBuffer.get(inObjectUUID) ?? {
          effects: []
        };
        flagsToSet.effects.push(...inEffects);
        flagManager.flagEffectAddBuffer.set(inObjectUUID, flagsToSet);
        flagManager.updateEffectFlags();
      },
      _removeEffectFlags: (inObjectUUID, inEffects, removeAll = false) => {
        if (inEffects && !Array.isArray(inEffects)) inEffects = [inEffects];
        let flagsToSet = flagManager.flagEffectRemoveBuffer.get(inObjectUUID) ?? {
          effects: [],
          removeAll
        };
        if (inEffects) flagsToSet.effects.push(...inEffects);
        flagManager.flagEffectRemoveBuffer.set(inObjectUUID, flagsToSet);
        flagManager.updateEffectFlags();
      },
      updateEffectFlags: foundry.utils.debounce(async () => {
        let flagsToAdd = Array.from(flagManager.flagEffectAddBuffer);
        let flagsToRemove = Array.from(flagManager.flagEffectRemoveBuffer);
        flagManager.flagEffectAddBuffer.clear();
        flagManager.flagEffectRemoveBuffer.clear();
        flagsToAdd.forEach((entry) => entry[1].original = true);
        flagsToRemove.forEach((entry) => entry[1].original = true);
        const objects = /* @__PURE__ */ new Set([
          ...flagsToAdd.map((effect) => effect[0]).filter(Boolean),
          ...flagsToRemove.map((effect) => effect[0]).filter(Boolean)
        ]);
        flagsToAdd = new Map(flagsToAdd);
        flagsToRemove = new Map(flagsToRemove);
        const actorUpdates = {};
        const sceneObjectsToUpdate = {};
        for (let objectUUID of objects) {
          let object = fromUuidSync(objectUUID);
          if (!object) {
            debug(
              `Failed to set flags on non-existent object with UUID: ${objectUUID}`
            );
            continue;
          }
          const isLinkedToken = object instanceof TokenDocument && object.actorLink;
          const isLinkedActor = object instanceof Actor && object.prototypeToken.actorLink;
          let toAdd = flagsToAdd.get(objectUUID) ?? { effects: [] };
          let toRemove = flagsToRemove.get(objectUUID) ?? {
            effects: [],
            removeAll: false
          };
          let origExistingFlags = foundry.utils.getProperty(object, CONSTANTS.EFFECTS_FLAG) ?? [];
          if (isLinkedToken) {
            origExistingFlags = origExistingFlags.concat(foundry.utils.getProperty(object.actor, CONSTANTS.EFFECTS_FLAG) ?? []);
          }
          const existingFlags = new Map(origExistingFlags);
          if (toRemove?.removeAll) {
            toRemove.effects = Array.from(existingFlags).map((entry) => entry[0]);
          }
          for (let effect of toAdd.effects) {
            if (typeof effect === "string") {
              effect = existingFlags.get(effect);
              if (!effect) continue;
            }
            existingFlags.set(effect._id, effect);
          }
          for (let effect of toRemove.effects) {
            if (typeof effect === "string") {
              effect = existingFlags.get(effect);
              if (!effect) continue;
            }
            existingFlags.delete(effect._id);
          }
          let flagsToSet = Array.from(existingFlags);
          const options = {};
          if ((isLinkedToken || isLinkedActor) && (toAdd.original || toRemove.original)) {
            const actor = object?.actor ?? object;
            actorUpdates[actor.id] = flagsToSet.filter(
              (effect) => is_UUID(effect[1]?.source) && effect[1]?.attachTo?.active && effect[1]?.persistOptions?.persistTokenPrototype
            );
            flagsToSet = flagsToSet.filter(
              (effect) => !effect[1]?.persistOptions?.persistTokenPrototype || !is_UUID(effect[1]?.source) || !effect[1]?.attachTo?.active
            );
            if (isLinkedToken && game.modules.get("multilevel-tokens")?.active && foundry.utils.getProperty(object, "flags.multilevel-tokens.stoken")) {
              options["mlt_bypass"] = true;
            }
          }
          if (object?.documentName === "Scene") {
            const sceneId = object.id;
            sceneObjectsToUpdate[sceneId] = sceneObjectsToUpdate[sceneId] ?? {
              updates: {},
              documents: {}
            };
            sceneObjectsToUpdate[sceneId].updates[CONSTANTS.EFFECTS_FLAG] = flagsToSet;
          } else if (!(object instanceof Actor) && origExistingFlags.length !== flagsToSet.length) {
            const sceneId = object.parent.id;
            const docName = object.documentName;
            sceneObjectsToUpdate[sceneId] = sceneObjectsToUpdate[sceneId] ?? {
              updates: {},
              documents: {}
            };
            sceneObjectsToUpdate[sceneId].documents[docName] = sceneObjectsToUpdate[sceneId].documents[docName] ?? {
              options: {},
              updates: []
            };
            sceneObjectsToUpdate[sceneId].documents[docName].options = options;
            sceneObjectsToUpdate[sceneId].documents[docName].updates.push({
              _id: object.id,
              [CONSTANTS.EFFECTS_FLAG]: flagsToSet
            });
          }
        }
        for (const [sceneId, sceneData] of Object.entries(sceneObjectsToUpdate)) {
          const scene = game.scenes.get(sceneId);
          if (!foundry.utils.isEmpty(sceneData.updates)) {
            await scene.update(sceneData.updates);
          }
          for (const [documentType, documentData] of Object.entries(
            sceneData.documents
          )) {
            await scene.updateEmbeddedDocuments(
              documentType,
              documentData.updates,
              documentData.options
            );
            debug(
              `Flags set for documents of type "${documentType}" in scene with ID "${sceneId}"`
            );
          }
        }
        await Actor.updateDocuments(
          Object.entries(actorUpdates).map(([actorId, effects]) => ({
            _id: actorId,
            [CONSTANTS.EFFECTS_FLAG]: effects
          }))
        );
      }, 250),
      /**
       * Sanitizes the effect data, accounting for changes to the structure in previous versions
       *
       * @param inDocument
       * @returns {array}
       */
      getSoundFlags(inDocument) {
        let sounds = foundry.utils.getProperty(inDocument, CONSTANTS.SOUNDS_FLAG);
        if (!sounds?.length) return [];
        sounds = foundry.utils.deepClone(sounds);
        const changes = [];
        for (let [soundId, soundData] of sounds) {
          let soundVersion = soundData?.flagVersion ?? "1.0.0";
          if (soundData.flagVersion === this.latestFlagVersion) continue;
          for (let [version, migration] of Object.entries(this.soundMigrations)) {
            if (!foundry.utils.isNewerVersion(version, soundVersion)) continue;
            soundData = migration(inDocument, soundData);
          }
          debug(
            `Migrated sound with ID ${soundId} from version ${soundVersion} to version ${this.latestFlagVersion}`
          );
          soundData.flagVersion = this.latestFlagVersion;
          changes.push(soundData);
        }
        if (changes.length) {
          flagManager.addSoundFlags(inDocument.uuid, changes);
        }
        return sounds;
      },
      soundMigrations: {},
      /**
       * Adds sounds to a given document
       *
       * @param inObjectUUID
       * @param inSounds
       */
      addSoundFlags: (inObjectUUID, inSounds) => {
        if (!Array.isArray(inSounds)) inSounds = [inSounds];
        sequencerSocket.executeAsGM(
          SOCKET_HANDLERS.ADD_EFFECT_FLAGS,
          inObjectUUID,
          inSounds
        );
      },
      /**
       * Removes sounds from a given document
       *
       * @param inObjectUUID
       * @param inSounds
       * @param removeAll
       */
      removeSoundFlags: (inObjectUUID, inSounds, removeAll = false) => {
        sequencerSocket.executeAsGM(
          SOCKET_HANDLERS.REMOVE_EFFECT_FLAGS,
          inObjectUUID,
          inSounds,
          removeAll
        );
      },
      _addSoundFlags: (sceneID, inSounds) => {
        if (!Array.isArray(inSounds)) inSounds = [inSounds];
        let flagsToSet = flagManager.flagAddBuffer.get(sceneID) ?? {
          effects: []
        };
        flagsToSet.effects.push(...inSounds);
        flagManager.flagSoundAddBuffer.set(sceneID, flagsToSet);
        flagManager.updateSoundFlags();
      },
      _removeSoundFlags: (sceneID, inSounds, removeAll = false) => {
        if (inSounds && !Array.isArray(inSounds)) inSounds = [inSounds];
        let flagsToSet = flagManager.flagRemoveBuffer.get(sceneID) ?? {
          effects: [],
          removeAll
        };
        if (inSounds) flagsToSet.effects.push(...inSounds);
        flagManager.flagSoundRemoveBuffer.set(sceneID, flagsToSet);
        flagManager.updateSoundFlags();
      },
      updateFlags: foundry.utils.debounce(async () => {
        let soundFlagsToAdd = Array.from(flagManager.flagSoundAddBuffer);
        let soundFlagsToRemove = Array.from(flagManager.flagSoundRemoveBuffer);
        flagManager.flagSoundAddBuffer.clear();
        flagManager.flagSoundRemoveBuffer.clear();
        soundFlagsToAdd.forEach((entry) => entry[1].original = true);
        soundFlagsToRemove.forEach((entry) => entry[1].original = true);
        const scenes = /* @__PURE__ */ new Set([
          ...soundFlagsToAdd.map((sound) => sound[0]).filter(Boolean),
          ...soundFlagsToRemove.map((sound) => sound[0]).filter(Boolean)
        ]);
        soundFlagsToAdd = new Map(soundFlagsToAdd);
        soundFlagsToRemove = new Map(soundFlagsToRemove);
        const scenesToUpdate = {};
        for (let sceneId of scenes) {
          let scene = game.scenes.get(sceneId);
          if (!scene) {
            debug(
              `Failed to set flags on non-existent scene with ID: ${sceneId}`
            );
            continue;
          }
          let soundsToAdd = soundFlagsToAdd.get(sceneId) ?? { sounds: [] };
          let soundsToRemove = soundFlagsToRemove.get(sceneId) ?? {
            sounds: [],
            removeAll: false
          };
          const existingFlags = new Map(
            foundry.utils.getProperty(scene, CONSTANTS.SOUNDS_FLAG) ?? []
          );
          if (soundsToRemove?.removeAll) {
            soundsToRemove.sounds = Array.from(existingFlags).map((entry) => entry[0]);
          }
          for (let sound of soundsToAdd.sounds) {
            if (typeof sound === "string") {
              sound = existingFlags.get(sound);
              if (!sound) continue;
            }
            existingFlags.set(sound.sound_id, sound);
          }
          for (let sound of soundsToRemove.sounds) {
            if (typeof sound === "string") {
              sound = existingFlags.get(sound);
              if (!sound) continue;
            }
            existingFlags.delete(sound.sound_id);
          }
          let flagsToSet = Array.from(existingFlags);
          scenesToUpdate[sceneId] = scenesToUpdate[sceneId] ?? {
            updates: {},
            documents: {}
          };
          scenesToUpdate[sceneId].updates[CONSTANTS.EFFECTS_FLAG] = flagsToSet;
        }
        for (const [sceneId, sceneData] of Object.entries(scenesToUpdate)) {
          const scene = game.scenes.get(sceneId);
          await scene.update(sceneData.updates);
        }
      }, 250)
    };
    function createSoundListener(sound, name2, func2) {
      if (CONSTANTS.IS_V12) {
        return sound.addEventListener(name2, func2);
      }
      return sound.on(name2, func2);
    }
    class SequencerSoundManager {
      /**
       * Returns all the currently running sounds
       *
       * @returns {Array}
       */
      static get sounds() {
        return Array.from(SequenceManager.RunningSounds.values());
      }
      /**
       * Opens the Sequencer Manager with the sounds tab open
       */
      static show() {
        return EffectsUIApp.show({ tab: "manager" });
      }
      /**
       * Play an audio file.
       *
       * @param {Object} data The data that describes the audio to play.
       * @param {boolean} [push=false] A flag indicating whether to make other clients play the audio, too.
       * @returns {Number} A promise that resolves when the audio file has finished playing.
       */
      static async play(data, push = true) {
        if (push)
          sequencerSocket.executeForOthers(SOCKET_HANDLERS.PLAY_SOUND, data);
        return this._play(data);
      }
      /**
       * @param {Object} data
       * @returns {Number}
       * @private
       */
      static async _play(data) {
        if (data.delete) return false;
        Hooks.callAll("createSequencerSound", data);
        debug(`Playing sound:`, data);
        const playSound = game.settings.get("sequencer", "soundsEnabled") && game.user.viewedScene === data.sceneId && (!data?.users?.length || data?.users?.includes(game.userId));
        data.volume = playSound ? (data.volume ?? 0.8) * (!CONSTANTS.IS_V12 ? game.settings.get("core", "globalInterfaceVolume") : 1) : 0;
        let sound;
        if (data.location) {
          let location2 = (is_UUID(data.location) ? fromUuidSync(data.location) : null) ?? { x: data.location?.x ?? 0, y: data.location?.x ?? 0 };
          if (data.offset) {
            location2.x += (data.offset.source.x || 0) * (data.offset.gridUnits ? canvas.grid.size : 1);
            location2.y += (data.offset.source.y || 0) * (data.offset.gridUnits ? canvas.grid.size : 1);
          }
          if (data.randomOffset?.source) {
            const twister = createMersenneTwister(data.seed);
            location2 = get_random_offset(location2, data.randomOffset.source, twister);
          }
          sound = await canvas.sounds.playAtPosition(data.src, location2, data.locationOptions?.radius || 5, {
            gmAlways: false,
            walls: false,
            easing: true,
            muffledEffect: { type: "lowpass" },
            ...data.locationOptions,
            volume: data.volume,
            channel: data.channel || "interface"
          });
        } else {
          sound = await game.audio.play(data.src, {
            ...data.locationOptions,
            volume: data.fadeIn ? 0 : data.volume,
            loop: data.loop,
            offset: data.startTime,
            context: game.audio[data.channel || "interface"]
          });
        }
        if (!sound) return false;
        sound.sequencer_data = data;
        sound.sound_id = data.id;
        sound.sound_playing = playSound || game.user.isGM;
        SequenceManager.RunningSounds.add(data.id, sound);
        if (data.fadeIn && playSound) {
          SequencerAnimationEngine.addAnimation(data.id, {
            target: sound,
            propertyName: "volume",
            from: 0,
            to: data.volume,
            duration: Math.min(data.fadeIn.duration, data.duration),
            ease: data.fadeIn.ease,
            delay: Math.min(data.fadeIn.delay, data.duration),
            absolute: true
          });
        }
        if (data.fadeOut && playSound) {
          SequencerAnimationEngine.addAnimation(data.id, {
            target: sound,
            propertyName: "volume",
            from: data.volume,
            to: 0,
            duration: Math.min(data.fadeOut.duration, data.duration),
            ease: data.fadeOut.ease,
            delay: Math.max(
              data.duration - data.fadeOut.duration + data.fadeOut.delay,
              0
            ),
            absolute: true
          });
        }
        if (data.duration) {
          setTimeout(() => {
            sound.stop();
          }, data.duration);
        }
        new Promise((resolve) => {
          createSoundListener(sound, "stop", resolve);
          createSoundListener(sound, "end", resolve);
        }).then(() => {
          SequenceManager.RunningSounds.delete(data.id);
          Hooks.callAll("endedSequencerSound", data);
        });
        return data.duration;
      }
      static _validateFilters(inFilter) {
        if (inFilter?.sounds) {
          if (!Array.isArray(inFilter.sounds)) {
            inFilter.sounds = [inFilter.sounds];
          }
          inFilter.sounds = inFilter.sounds.map((sound) => {
            if (!(typeof sound === "string" || sound instanceof foundry?.audio?.Sound))
              throw custom_error(
                "Sequencer",
                "SoundManager | collections in inFilter.sounds must be of type string or Sound"
              );
            if (sound instanceof foundry?.audio?.Sound) return sound.sequencer_data.id;
            return sound;
          });
        }
        if (inFilter?.name && typeof inFilter?.name !== "string")
          throw custom_error(
            "Sequencer",
            "SoundManager | inFilter.name must be of type string"
          );
        if (inFilter?.sceneId) {
          if (typeof inFilter.sceneId !== "string")
            throw custom_error(
              "Sequencer",
              "SoundManager | inFilter.sceneId must be of type string"
            );
          if (!game.scenes.get(inFilter.sceneId))
            throw custom_error(
              "Sequencer",
              "SoundManager | inFilter.sceneId must be a valid scene id (could not find scene)"
            );
        } else {
          inFilter.sceneId = game.user.viewedScene;
        }
        if (inFilter?.origin && typeof inFilter?.origin !== "string")
          throw custom_error(
            "Sequencer",
            "SoundManager | inFilter.origin must be of type string"
          );
        if (!inFilter.sounds && !inFilter.name && !inFilter.sceneId && !inFilter.origin) {
          return false;
        }
        return foundry.utils.mergeObject(
          {
            sounds: null,
            name: null,
            sceneId: null,
            origin: null
          },
          inFilter
        );
      }
      static _filterSounds(inFilter) {
        if (inFilter.name) {
          inFilter.name = new RegExp(
            "^" + str_to_search_regex_str(safe_str(inFilter.name)) + "$",
            "gu"
          );
        }
        return this.sounds.filter((sound) => {
          return (inFilter.sounds === null || inFilter.sounds.includes(sound.sequencer_data.id)) && (inFilter.name === null || sound.sequencer_data.name && inFilter.name && sound.sequencer_data.name.match(inFilter.name)?.length) && (inFilter.sceneId === null || sound.sequencer_data.sceneId === inFilter.sceneId) && (inFilter.origin === null || inFilter.origin === sound.sequencer_data.origin);
        });
      }
      static getSounds(inFilter = {}) {
        const filters2 = this._validateFilters(inFilter);
        if (!inFilter)
          throw custom_error(
            "Sequencer",
            "SoundManager | getSounds | Incorrect or incomplete parameters provided"
          );
        return this._filterSounds(filters2);
      }
      static stop(ids) {
        custom_warning(
          "Sequencer",
          "SoundManager | stop | This method is becoming deprecated, please use Sequencer.SoundManager.endSounds instead",
          false
        );
        return this.endSounds({ sounds: ids });
      }
      static endSounds(inFilter, push = true) {
        const filters2 = this._validateFilters(inFilter);
        const sounds = this._filterSounds(filters2);
        if (!sounds?.length) return;
        const ids = sounds.map((sound) => sound.sequencer_data.id);
        if (push && game.user.isGM) {
          sequencerSocket.executeForOthers(SOCKET_HANDLERS.END_SOUNDS, ids);
        }
        return this._endSounds(ids);
      }
      /**
       * @param ids
       * @private
       */
      static _endSounds(ids) {
        for (const id of ids) {
          const sound = SequenceManager.RunningSounds.get(id);
          if (sound) {
            sound.stop();
          }
        }
      }
      static endAllSounds(push = true) {
        const ids = SequenceManager.RunningSounds.keys();
        if (push && game.user.isGM) {
          sequencerSocket.executeForOthers(SOCKET_HANDLERS.END_SOUNDS, ids);
        }
        return this._endSounds(ids);
      }
    }
    let lockedView = false;
    class SequencerFoundryReplicator {
      static registerHooks() {
        Hooks.on("canvasPan", () => {
          if (!lockedView) return;
          canvas.stage.pivot.set(lockedView.x, lockedView.y);
          canvas.stage.scale.set(lockedView.scale, lockedView.scale);
          canvas.updateBlur(lockedView.scale);
          canvas.controls._onCanvasPan();
          canvas.hud.align();
        });
      }
      static _validateObject(inObject, sceneId) {
        if (is_UUID(inObject) || !is_object_canvas_data(inObject)) {
          inObject = get_object_from_scene(inObject, sceneId);
        }
        return inObject?._object ?? inObject;
      }
      static _getPositionFromData(data) {
        const source = data.nameOffsetMap[data.source] ? data.nameOffsetMap[data.source].source : this._validateObject(data.source, data.sceneId);
        const position = source instanceof PlaceableObject ? get_object_position(source) : source?.worldPosition || source?.center || source;
        const multiplier = data.randomOffset;
        const twister = createMersenneTwister(data.seed);
        if (source && multiplier) {
          let randomOffset = get_random_offset(
            source,
            multiplier,
            twister
          );
          position.x -= randomOffset.x;
          position.y -= randomOffset.y;
        }
        let extraOffset = data.offset;
        if (extraOffset) {
          let newOffset = {
            x: extraOffset.x,
            y: extraOffset.y
          };
          if (extraOffset.gridUnits) {
            newOffset.x *= canvas.grid.size;
            newOffset.y *= canvas.grid.size;
          }
          if (extraOffset.local) {
            newOffset = rotateAroundPoint(
              0,
              0,
              newOffset.x,
              newOffset.y,
              source?.rotation ?? 0
            );
          }
          position.x -= newOffset.x;
          position.y -= newOffset.y;
        }
        return position;
      }
      static playScrollingText(data, push = true) {
        if (push) {
          sequencerSocket.executeForOthers(
            SOCKET_HANDLERS.CREATE_SCROLLING_TEXT,
            data
          );
        }
        return this._playScrollingText(data);
      }
      static _playScrollingText(data) {
        if (game.user.viewedScene !== data.sceneId) return;
        if (data.users.length && !data.users.includes(game.userId)) return;
        canvas.interface.createScrollingText(
          this._getPositionFromData(data),
          data.content,
          data.options
        );
        return data.options?.duration ?? 2e3;
      }
      static panCanvas(data, push = true) {
        if (push) {
          sequencerSocket.executeForOthers(SOCKET_HANDLERS.PAN_CANVAS, data);
        }
        return this._panCanvas(data);
      }
      static _panCanvas(data) {
        if (game.user.viewedScene !== data.sceneId) return;
        if (data.users.length && !data.users.includes(game.userId)) return;
        if (data.source) {
          const position = this._getPositionFromData(data);
          canvas.animatePan({
            x: position.x,
            y: position.y,
            scale: data.scale,
            duration: data.duration,
            speed: data.speed
          });
          if (data.speed) {
            let ray = new Ray(canvas.stage.pivot, {
              x: position.x,
              y: position.y
            });
            data.duration = Math.round(ray.distance * 1e3 / data.speed);
          }
          if (data.lockView > 0) {
            setTimeout(() => {
              lockedView = {
                x: position.x,
                y: position.y,
                scale: data.scale
              };
            }, data.duration);
            setTimeout(() => {
              lockedView = false;
            }, data.lockView + data.duration);
          }
        } else {
          data.duration = 0;
        }
        if (data.shake) {
          setTimeout(() => {
            this._shake(data.shake);
          }, data.duration);
        }
        return data.duration + Math.max(data.lockView ?? 0, data.shake?.duration ?? 0);
      }
      static _shake(shakeData) {
        if (!shakeData.duration || !shakeData.frequency) return;
        let x = random_float_between(-1, 1);
        let y = random_float_between(-1, 1);
        let rot = shakeData.rotation ? random_float_between(-1, 1) : 0;
        let positions = [{ x, y, rot }];
        for (let index = 0; index < Math.floor(shakeData.duration / shakeData.frequency); index++) {
          x = flip_negate(x, Math.random());
          y = flip_negate(y, Math.random());
          rot = shakeData.rotation ? flip_negate(rot, Math.random()) : 0;
          positions.push({ x, y, rot });
        }
        let currentDuration = 0;
        positions = positions.map((pos) => {
          let fadeStrength = 1;
          if (shakeData.fadeInDuration && currentDuration <= shakeData.fadeInDuration) {
            fadeStrength = Math.max(
              0,
              Math.min(1, currentDuration / shakeData.fadeInDuration)
            );
          }
          if (shakeData.fadeOutDuration && currentDuration >= shakeData.duration - shakeData.fadeOutDuration) {
            fadeStrength = Math.max(
              0,
              Math.min(
                1,
                (shakeData.duration - currentDuration) / shakeData.fadeOutDuration
              )
            );
          }
          pos.x *= shakeData.strength * fadeStrength;
          pos.y *= shakeData.strength * fadeStrength;
          if (shakeData.rotation) {
            pos.rot *= shakeData.strength / 7.5 * fadeStrength;
          } else {
            pos.rot = 0;
          }
          currentDuration += shakeData.frequency;
          return {
            transform: `translate(${pos.x}px, ${pos.y}px) rotate(${pos.rot}deg)`
          };
        });
        document.getElementById("board").animate(positions, {
          duration: shakeData.duration
        });
      }
    }
    const SequencerPreloader = {
      _usersToRespond: /* @__PURE__ */ new Set(),
      _clientsDone: [],
      _resolve: () => {
      },
      /**
       *  Caches provided file(s) locally, vastly improving loading speed of those files.
       *
       * @param {Array|String}        inSrcs
       * @param {Boolean}             showProgressBar
       * @returns {Promise<void>}
       */
      preload(inSrcs, showProgressBar = false) {
        if (Array.isArray(inSrcs)) {
          inSrcs.forEach((str) => {
            if (typeof str !== "string") {
              throw custom_error(
                "Sequencer",
                "preload | each entry in inSrcs must be of type string"
              );
            }
          });
        } else if (typeof inSrcs !== "string") {
          throw custom_error(
            "Sequencer",
            "preload | inSrcs must be of type string or array of strings"
          );
        }
        if (typeof showProgressBar !== "boolean") {
          throw custom_error(
            "Sequencer",
            "preload | showProgressBar must be of type of boolean"
          );
        }
        const srcs = this._cleanSrcs(inSrcs);
        if (srcs.length === 0) return;
        return this._preloadLocal(srcs, showProgressBar);
      },
      /**
       *  Causes each connected client (including the caller) to fetch and cache the provided file(s) locally, vastly
       *  improving loading speed of those files.
       *
       * @param {Array|String}        inSrcs
       * @param {Boolean}             showProgressBar
       * @returns {Promise<void>}
       */
      preloadForClients(inSrcs, showProgressBar = false) {
        if (Array.isArray(inSrcs)) {
          inSrcs.forEach((str) => {
            if (typeof str !== "string") {
              throw custom_error(
                "Sequencer",
                "preloadForClients | each entry in inSrcs must be of type string"
              );
            }
          });
        } else if (typeof inSrcs !== "string") {
          throw custom_error(
            "Sequencer",
            "preloadForClients | inSrcs must be of type string or array of strings"
          );
        }
        if (typeof showProgressBar !== "boolean") {
          throw custom_error(
            "Sequencer",
            "preloadForClients | showProgressBar must be of type of boolean"
          );
        }
        const srcs = this._cleanSrcs(inSrcs);
        if (srcs.length === 0) return;
        if (!user_can_do("permissions-preload")) {
          custom_warning(
            "Sequencer",
            "preloadForClients - You do not have permission to force other clients to preload. Preloading locally instead."
          );
          return this._preloadLocal(srcs, showProgressBar);
        }
        const promise2 = new Promise((resolve) => {
          this._resolve = resolve;
        });
        this._usersToRespond = new Set(
          game.users.filter((user) => user.active).map((user) => user.id)
        );
        sequencerSocket.executeForEveryone(
          SOCKET_HANDLERS.PRELOAD,
          game.user.id,
          srcs,
          showProgressBar
        );
        return promise2;
      },
      async respond(inSenderId, inSrcs, showProgressBar) {
        const numFilesFailedToLoad = await this._preloadLocal(
          inSrcs,
          showProgressBar
        );
        return sequencerSocket.executeAsUser(
          SOCKET_HANDLERS.PRELOAD_RESPONSE,
          inSenderId,
          game.user.id,
          numFilesFailedToLoad
        );
      },
      handleResponse(inUserId, numFilesFailedToLoad) {
        this._usersToRespond.delete(inUserId);
        this._clientsDone.push({
          userId: inUserId,
          numFilesFailedToLoad
        });
        if (this._usersToRespond.size > 0) return;
        this._clientsDone.forEach((user) => {
          if (user.numFilesFailedToLoad > 0) {
            debug(
              `${game.users.get(user.userId).name} preloaded files, failed to preload ${user.numFilesFailedToLoad} files`
            );
          } else {
            debug(
              `${game.users.get(user.userId).name} preloaded files successfully`
            );
          }
        });
        debug(`All clients responded to file preloads`);
        this._resolve();
        this._usersToRespond = /* @__PURE__ */ new Set();
        this._clientsDone = [];
        this._resolve = () => {
        };
      },
      /**
       * Filters and cleans up file paths given to the preload methods
       *
       * @private
       */
      _cleanSrcs(inSrcs) {
        if (!Array.isArray(inSrcs)) {
          inSrcs = [inSrcs];
        }
        if (inSrcs.length === 0) {
          custom_warning("Sequencer", "You need to provide files to preload");
          return [];
        }
        inSrcs = make_array_unique(
          inSrcs.filter(Boolean).map((src) => {
            if (Sequencer.Database.entryExists(src)) {
              return Sequencer.Database.getAllFileEntries(src);
            }
            return src;
          }).deepFlatten()
        );
        if (inSrcs.length >= 750) {
          custom_warning(
            "Sequencer",
            "You are preloading over 750 files, you are most likely preloading more files than the system can cache.",
            true
          );
        }
        return inSrcs;
      },
      /**
       * The method that actually preloads files locally, with an optional progress bar
       *
       * @private
       */
      _preloadLocal(inSrcs, showProgressBar) {
        let startTime = performance.now();
        let numFilesToLoad = inSrcs.length;
        debug(`Preloading ${numFilesToLoad} files...`);
        if (showProgressBar)
          LoadingBar.init(
            `Sequencer - Preloading ${numFilesToLoad} files`,
            numFilesToLoad
          );
        return new Promise(async (resolve) => {
          let numFilesFailedToLoad = 0;
          const loadingPromises = inSrcs.map(async (src) => {
            const blob = await SequencerFileCache.loadFile(src, true);
            if (showProgressBar) LoadingBar.incrementProgress();
            if (!blob) {
              numFilesFailedToLoad++;
            }
          });
          await Promise.allSettled(loadingPromises);
          const timeTaken = (performance.now() - startTime) / 1e3;
          let failedToLoad = ` (${numFilesFailedToLoad} failed to load)`;
          debug(
            `Preloading ${numFilesToLoad} files took ${timeTaken}s` + failedToLoad
          );
          resolve(numFilesFailedToLoad);
        });
      }
    };
    const SOCKET_HANDLERS = {
      PLAY_EFFECT: "playEffect",
      END_EFFECTS: "endEffects",
      UPDATE_EFFECT: "updateEffects",
      ADD_EFFECT_ANIMATIONS: "addEffectAnimations",
      PLAY_SOUND: "playSound",
      END_SOUNDS: "endSounds",
      PRELOAD: "preload",
      PRELOAD_RESPONSE: "preloadResponse",
      UPDATE_DOCUMENT: "updateDocument",
      ADD_EFFECT_FLAGS: "addEffectFlags",
      REMOVE_EFFECT_FLAGS: "removeEffectFlags",
      UPDATE_EFFECT_POSITION: "updateEffectPosition",
      ADD_SOUND_FLAGS: "addSoundFlags",
      REMOVE_SOUND_FLAGS: "removeSoundFlags",
      CREATE_SCROLLING_TEXT: "createScrollingText",
      PAN_CANVAS: "panCanvas",
      RUN_SEQUENCE_LOCALLY: "runSequenceLocally"
    };
    let sequencerSocket;
    function registerSocket() {
      if (sequencerSocket) return;
      sequencerSocket = socketlib.registerModule(CONSTANTS.MODULE_NAME);
      sequencerSocket.register(
        SOCKET_HANDLERS.PLAY_EFFECT,
        (...args) => SequencerEffectManager._playEffect(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.END_EFFECTS,
        (...args) => SequencerEffectManager._endManyEffects(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.UPDATE_EFFECT,
        (...args) => SequencerEffectManager._updateEffect(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.ADD_EFFECT_ANIMATIONS,
        (...args) => SequencerEffectManager._addEffectAnimations(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.PLAY_SOUND,
        (...args) => SequencerSoundManager._play(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.END_SOUNDS,
        (...args) => SequencerSoundManager._endSounds(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.PRELOAD,
        (...args) => SequencerPreloader.respond(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.PRELOAD_RESPONSE,
        (...args) => SequencerPreloader.handleResponse(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.UPDATE_DOCUMENT,
        (...args) => updateDocument(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.ADD_EFFECT_FLAGS,
        (...args) => flagManager._addEffectFlags(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.REMOVE_EFFECT_FLAGS,
        (...args) => flagManager._removeEffectFlags(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.UPDATE_EFFECT_POSITION,
        (...args) => SequencerEffectManager._updatePosition(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.ADD_SOUND_FLAGS,
        (...args) => flagManager._addSoundFlags(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.REMOVE_SOUND_FLAGS,
        (...args) => flagManager._removeSoundFlags(...args)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.CREATE_SCROLLING_TEXT,
        (data) => SequencerFoundryReplicator._playScrollingText(data)
      );
      sequencerSocket.register(
        SOCKET_HANDLERS.PAN_CANVAS,
        (data) => SequencerFoundryReplicator._panCanvas(data)
      );
      sequencerSocket.register(SOCKET_HANDLERS.RUN_SEQUENCE_LOCALLY, (data) => {
        debug("Playing remote Sequence");
        new Sequence().fromJSON(data).play();
      });
    }
    async function updateDocument(documentUuid, updates, animate) {
      const document2 = await fromUuid(documentUuid);
      return document2.update(updates, animate);
    }
    const PositionContainer = /* @__PURE__ */ new Map();
    const TemporaryPositionsContainer = /* @__PURE__ */ new Map();
    class SequencerEffectManager {
      /**
       * Returns all the currently running effects on the canvas
       *
       * @returns {Array}
       */
      static get effects() {
        return Array.from(SequenceManager.VisibleEffects.values());
      }
      static _updatePosition(uuid, position) {
        TemporaryPositionsContainer.set(uuid, position);
      }
      static getPositionForUUID(uuid) {
        return TemporaryPositionsContainer.get(uuid);
      }
      /**
       * Opens the Sequencer Manager UI with the effects tab open
       */
      static show() {
        return EffectsUIApp.show({ tab: "manager" });
      }
      /**
       * Play an effect on the canvas.
       *
       * @param {object} data The data that describes the audio to play
       * @param {boolean} [push=true] A flag indicating whether or not to make other clients play the effect
       * @returns {CanvasEffect} A CanvasEffect object
       */
      static async play(data, push = true) {
        if (!user_can_do("permissions-effect-create")) {
          custom_warning(
            "Sequencer",
            "EffectManager | play | Players do not have permissions to play effects. This can be configured in Sequencer's module settings."
          );
          return;
        }
        if (push) sequencerSocket.executeForOthers(SOCKET_HANDLERS.PLAY_EFFECT, data);
        return this._playEffect(data);
      }
      /**
       * Get effects that are playing on the canvas based on a set of filters
       *
       * @param {object} inFilter An object containing filters that determine which effects to return
       *                             - object: An ID or a PlaceableObject
       *                             - name: The name of the effect
       *                             - sceneId: the ID of the scene to search within
       * @returns {Array} An array containing effects that match the given filter
       */
      static getEffects(inFilter = {}) {
        const filters2 = this._validateFilters(inFilter);
        if (!inFilter)
          throw custom_error(
            "Sequencer",
            "EffectManager | getEffects | Incorrect or incomplete parameters provided"
          );
        return this._filterEffects(filters2);
      }
      /**
       * Updates effects based on a set of filters
       *
       * @param {object} inFilter An object containing filters that determine which effects to return
       *                             - object: An ID or a PlaceableObject
       *                             - name: The name of the effect
       *                             - sceneId: the ID of the scene to search within
       *                             - effects: a single CanvasEffect or its ID, or an array of such
       * @param {object} inUpdates
       * @returns {promise}
       */
      static updateEffects(inFilter, inUpdates) {
        inFilter = this._validateFilters(inFilter);
        if (!inFilter)
          throw custom_error(
            "Sequencer",
            "EffectManager | updateEffects | Incorrect or incomplete parameters provided"
          );
        CanvasEffect.validateUpdate(inUpdates);
        const effectsToUpdate = this._filterEffects(inFilter).filter(
          (effect) => effect.userCanUpdate
        );
        return Promise.allSettled(
          effectsToUpdate.map((effect) => effect.update(inUpdates))
        );
      }
      /**
       * End effects that are playing on the canvas based on a set of filters
       *
       * @param {object} inFilter An object containing filters that determine which effects to end
       *                             - object: An ID or a PlaceableObject
       *                             - name: The name of the effect
       *                             - sceneId: the ID of the scene to search within
       *                             - effects: a single CanvasEffect or its ID, or an array of such
       * @param {boolean} [push=true] A flag indicating whether or not to make other clients end the effects
       * @returns {promise} A promise that resolves when the effects have ended
       */
      static async endEffects(inFilter = {}, push = true) {
        inFilter = this._validateFilters(inFilter);
        if (!inFilter)
          throw custom_error(
            "Sequencer",
            "EffectManager | endEffects | Incorrect or incomplete parameters provided"
          );
        const effectsToEnd = this._getEffectsByFilter(inFilter);
        if (!effectsToEnd.length) return;
        if (push)
          sequencerSocket.executeForOthers(
            SOCKET_HANDLERS.END_EFFECTS,
            effectsToEnd
          );
        return this._endManyEffects(effectsToEnd);
      }
      /**
       * End all effects that are playing on the canvas
       *
       * @param {string} [inSceneId] A parameter which determines which scene to end all effects on, defaults to current viewed scene
       * @param {boolean} [push=true] A flag indicating whether or not to make other clients end all effects
       * @returns {promise} A promise that resolves when all of the effects have _ended
       */
      static async endAllEffects(inSceneId = game.user.viewedScene, push = true) {
        const inFilter = this._validateFilters({ sceneId: inSceneId });
        if (!inFilter)
          throw custom_error(
            "Sequencer",
            "EffectManager | endAllEffects | Incorrect or incomplete parameters provided"
          );
        const effectsToEnd = this._getEffectsByFilter(inFilter);
        if (!effectsToEnd.length) return;
        if (push)
          sequencerSocket.executeForOthers(
            SOCKET_HANDLERS.END_EFFECTS,
            effectsToEnd
          );
        return this._endManyEffects(effectsToEnd);
      }
      static _getEffectsByFilter(inFilter) {
        return make_array_unique(
          this._filterEffects(inFilter).filter((effect) => effect.userCanDelete).map((effect) => {
            return effect.data?.persistOptions?.persistTokenPrototype && is_UUID(effect?.data.source) && effect.data?.attachTo?.active ? effect.data?.persistOptions?.id ?? effect.id : effect.id;
          })
        );
      }
      /**
       * If an effect has been named its position will be cached, which can be retrieved with this method
       *
       * @param {string} inName
       * @returns {object|boolean}
       * @private
       */
      static getEffectPositionByName(inName) {
        if (!(typeof inName === "string"))
          throw custom_error(
            "Sequencer",
            "EffectManager | getEffectPositionByName | inName must be of type string"
          );
        return PositionContainer.get(inName) ?? false;
      }
      /**
       * Filters the existing effects based on the given filter
       *
       * @param inFilter
       * @returns {array}
       * @private
       */
      static _filterEffects(inFilter) {
        if (inFilter.name) {
          inFilter.name = new RegExp(
            "^" + str_to_search_regex_str(safe_str(inFilter.name)) + "$",
            "gu"
          );
        }
        let effects = this.effects;
        if (inFilter.sceneId && inFilter.sceneId !== canvas.scene.id) {
          effects = get_all_documents_from_scene(inFilter.sceneId).map((doc) => {
            return foundry.utils.getProperty(doc, CONSTANTS.EFFECTS_FLAG);
          }).filter((flags) => !!flags).map((flags) => {
            return flags.map((flag) => CanvasEffect.make(flag[1]));
          }).deepFlatten();
        }
        return effects.filter((effect) => {
          return (!inFilter.effects || inFilter.effects.includes(effect.id)) && (!inFilter.name || effect.data.name && effect.data.name.match(inFilter.name)?.length) && (!inFilter.source || inFilter.source === effect.data.source) && (!inFilter.target || inFilter.target === effect.data.target) && (!inFilter.origin || inFilter.origin === effect.data.origin);
        });
      }
      /**
       * Validates an object actually exists, and gets its UUID
       *
       * @param object
       * @param sceneId
       * @returns {string}
       * @private
       */
      static _validateObject(object, sceneId) {
        if (!(object instanceof foundry.abstract.Document || object instanceof PlaceableObject || typeof object === "string")) {
          throw custom_error(
            "Sequencer",
            "EffectManager | object must be instance of PlaceableObject or of type string"
          );
        } else if (object instanceof PlaceableObject || object instanceof foundry.abstract.Document) {
          object = get_object_identifier(object?.document ?? object);
        } else if (typeof object === "string") {
          const actualObject = get_object_from_scene(object, sceneId);
          if (!actualObject) {
            throw custom_error(
              "Sequencer",
              `EffectManager | could not find object with ID: ${object}`
            );
          }
          const uuid = get_object_identifier(actualObject);
          if (!uuid) {
            throw custom_error(
              "Sequencer",
              `EffectManager | could could not establish identifier of object with ID: ${object}`
            );
          }
          object = uuid;
        }
        return object;
      }
      /**
       * Validates the filter given to any of the above public methods
       *
       * @param inFilter
       * @returns {boolean}
       * @private
       */
      static _validateFilters(inFilter) {
        if (inFilter?.sceneId) {
          if (typeof inFilter.sceneId !== "string")
            throw custom_error(
              "Sequencer",
              "EffectManager | inFilter.sceneId must be of type string"
            );
          if (!game.scenes.get(inFilter.sceneId))
            throw custom_error(
              "Sequencer",
              "EffectManager | inFilter.sceneId must be a valid scene id (could not find scene)"
            );
        } else {
          inFilter.sceneId = game.user.viewedScene;
        }
        if (inFilter?.object) {
          inFilter.source = this._validateObject(inFilter.object, inFilter.sceneId);
          delete inFilter.object;
        }
        if (inFilter?.source) {
          inFilter.source = this._validateObject(inFilter.source, inFilter.sceneId);
        }
        if (inFilter?.target) {
          inFilter.target = this._validateObject(inFilter.target, inFilter.sceneId);
        }
        if (inFilter?.name && typeof inFilter?.name !== "string")
          throw custom_error(
            "Sequencer",
            "EffectManager | inFilter.name must be of type string"
          );
        if (inFilter?.origin && typeof inFilter?.origin !== "string")
          throw custom_error(
            "Sequencer",
            "EffectManager | inFilter.origin must be of type string"
          );
        if (inFilter?.effects) {
          if (!Array.isArray(inFilter.effects))
            inFilter.effects = [inFilter.effects];
          inFilter.effects = inFilter.effects.map((effect) => {
            if (!(typeof effect === "string" || effect instanceof CanvasEffect))
              throw custom_error(
                "Sequencer",
                "EffectManager | collections in inFilter.effects must be of type string or CanvasEffect"
              );
            if (effect instanceof CanvasEffect) return effect.id;
            return effect;
          });
        }
        if (!inFilter.name && !inFilter.origin && !inFilter.target && !inFilter.sceneId && !inFilter.effects && !inFilter.origin)
          return false;
        return foundry.utils.mergeObject(
          {
            effects: false,
            name: false,
            source: false,
            target: false,
            sceneId: false,
            origin: false
          },
          inFilter
        );
      }
      /**
       * Actually plays the effect on the canvas
       *
       * @param data
       * @param setFlags
       * @returns {Promise<{duration: Promise<number>, promise: Promise<void>}>}
       * @private
       */
      static async _playEffect(data, setFlags = true) {
        const effect = CanvasEffect.make(data);
        if (data.persist && setFlags && effect.context && effect.owner && !effect.isSourceTemporary && !data.temporary && !data.remote) {
          flagManager.addEffectFlags(effect.context.uuid, effect.data);
        }
        if (!effect.shouldPlay) return;
        const playData = effect.play();
        SequenceManager.VisibleEffects.add(effect.id, effect);
        if (effect.data.name) {
          effect._ticker.add(() => {
            if (effect.isDestroyed) return;
            PositionContainer.set(effect.data.name, {
              start: effect.sourcePosition,
              end: effect.targetPosition
            });
          });
        }
        if (data.temporary && effect.owner) {
          let lastSourcePosition = {};
          let lastTargetPosition = {};
          effect._addToTicker(() => {
            if (effect.source && !effect.isSourceDestroyed) {
              const sourceData = effect.getSourceData();
              if (JSON.stringify(sourceData) !== lastSourcePosition) {
                sequencerSocket.executeForOthers(
                  SOCKET_HANDLERS.UPDATE_EFFECT_POSITION,
                  data.source,
                  sourceData
                );
                lastSourcePosition = JSON.stringify(sourceData);
              }
            }
            if (effect.target && !effect.isTargetDestroyed) {
              const targetData = effect.getTargetData();
              if (JSON.stringify(targetData) !== lastTargetPosition) {
                sequencerSocket.executeForOthers(
                  SOCKET_HANDLERS.UPDATE_EFFECT_POSITION,
                  data.target,
                  targetData
                );
                lastTargetPosition = JSON.stringify(targetData);
              }
            }
          });
        }
        if (!data.persist) {
          playData.promise.then(() => this._removeEffect(effect));
        }
        return playData;
      }
      /**
       * Updates a single effect with the given data
       *
       * @param inEffectId
       * @param inUpdates
       * @returns {promise|boolean}
       * @private
       */
      static _updateEffect(inEffectId, inUpdates) {
        const effect = SequenceManager.VisibleEffects.get(inEffectId);
        if (!effect) return false;
        return effect._update(inUpdates);
      }
      /**
       * Updates a single effect with new animations
       *
       * @param inEffectId
       * @param inAnimations
       * @param inLoopingAnimations
       * @returns {promise|boolean}
       * @private
       */
      static _addEffectAnimations(inEffectId, inAnimations, inLoopingAnimations) {
        const effect = SequenceManager.VisibleEffects.get(inEffectId);
        if (!effect) return false;
        return effect._addAnimations(inAnimations, inLoopingAnimations);
      }
      /**
       * Sets up persisting effects when the scene is first loaded
       *
       * @returns {promise}
       */
      static async initializePersistentEffects() {
        await this.tearDownPersists();
        const allObjects = get_all_documents_from_scene();
        allObjects.push(canvas.scene);
        const docEffectsMap = allObjects.reduce((acc, doc) => {
          let effects = flagManager.getEffectFlags(doc);
          effects.forEach((e) => {
            if (is_UUID(e[1].source) && e[1].source !== doc.uuid) {
              e[1].delete = true;
            }
          });
          if (doc instanceof TokenDocument && doc?.actorLink) {
            const actorEffects = flagManager.getEffectFlags(doc?.actor);
            actorEffects.forEach((e) => {
              e[1]._id = foundry.utils.randomID();
              e[1].source = doc.uuid;
              e[1].sceneId = doc.parent.id;
            });
            effects = effects.concat(actorEffects);
          }
          if (effects.length) {
            acc[doc.uuid] = effects;
          }
          return acc;
        }, {});
        const promises = Object.entries(docEffectsMap).map(([uuid, effects]) => {
          return this._playEffectMap(effects, fromUuidSync(uuid));
        }).flat();
        return Promise.all(promises).then(() => {
          Hooks.callAll("sequencerEffectManagerReady");
        });
      }
      /**
       * Tears down persisting effects when the scene is unloaded
       */
      static tearDownPersists() {
        return Promise.allSettled(
          this.effects.map((effect) => {
            SequenceManager.VisibleEffects.delete(effect.id);
            return effect.destroy();
          })
        );
      }
      static setup() {
        Hooks.on("preCreateToken", this._patchCreationData.bind(this));
        Hooks.on("preCreateDrawing", this._patchCreationData.bind(this));
        Hooks.on("preCreateTile", this._patchCreationData.bind(this));
        Hooks.on("preCreateMeasuredTemplate", this._patchCreationData.bind(this));
        Hooks.on("createToken", this._documentCreated.bind(this));
        Hooks.on("createDrawing", this._documentCreated.bind(this));
        Hooks.on("createTile", this._documentCreated.bind(this));
        Hooks.on("createMeasuredTemplate", this._documentCreated.bind(this));
      }
      /**
       * Patches an object's creation data before it's created so that the effect plays on it correctly
       *
       * @param inDocument
       * @param data
       * @param options
       * @returns {*}
       */
      static async _patchCreationData(inDocument, data, options) {
        const effects = flagManager.getEffectFlags(inDocument);
        if (!effects?.length) return;
        const updates = {};
        let documentUuid;
        if (!inDocument._id) {
          const documentId = foundry.utils.randomID();
          documentUuid = inDocument.uuid + documentId;
          updates["_id"] = documentId;
          options.keepId = true;
        } else {
          documentUuid = inDocument.uuid;
        }
        updates[CONSTANTS.EFFECTS_FLAG] = this._patchEffectDataForDocument(
          documentUuid,
          effects
        );
        return inDocument.updateSource(updates);
      }
      static _patchEffectDataForDocument(inDocumentUuid, effects) {
        return effects.map((effect) => {
          effect[0] = foundry.utils.randomID();
          const effectData = effect[1];
          effectData._id = effect[0];
          if (is_UUID(effectData.source)) {
            if (effectData.masks.includes(effectData.source)) {
              const index = effectData.masks.indexOf(effectData.source);
              effectData.masks[index] = inDocumentUuid;
            }
            effectData.source = inDocumentUuid;
          }
          effectData.sceneId = inDocumentUuid.split(".")[1];
          return effect;
        });
      }
      /**
       * Plays the effects of a given document on creation
       *
       * @param inDocument
       * @returns {*}
       */
      static async _documentCreated(inDocument) {
        let effects = flagManager.getEffectFlags(inDocument);
        if (inDocument instanceof TokenDocument && inDocument?.actorLink) {
          let actorEffects = flagManager.getEffectFlags(inDocument?.actor);
          if (actorEffects.length) {
            actorEffects = this._patchEffectDataForDocument(
              inDocument.uuid,
              actorEffects
            );
          }
          effects = effects.concat(actorEffects);
        }
        if (!effects?.length) return;
        return this._playEffectMap(effects, inDocument);
      }
      /**
       * Plays multiple effects at the same time
       *
       * @param inEffects
       * @param inDocument
       * @returns {Promise<{duration: Promise<number>, promise: Promise<void>}[]>}
       * @private
       */
      static _playEffectMap(inEffects, inDocument) {
        if (inEffects instanceof Map) inEffects = Array.from(inEffects);
        return Promise.all(
          inEffects.map((effect) => {
            if (!CanvasEffect.checkValid(effect[1])) {
              if (!game.user.isGM) return;
              custom_warning(
                `Sequencer`,
                `Removed effect from ${inDocument.uuid} as it no longer had a valid source or target`
              );
              return flagManager.removeEffectFlags(inDocument.uuid, effect);
            }
            return this._playEffect(effect[1], false).then((result) => {
              if (!result) {
                debug("Error playing effect");
              }
            }).catch((err) => {
              debug("Error playing effect:", err);
            });
          })
        );
      }
      /**
       * Ends one or many effects at the same time, returning a promise that resolves once every effect has fully ended
       *
       * @param inEffectIds
       * @returns {Promise}
       * @private
       */
      static async _endManyEffects(inEffectIds = false) {
        const actorEffectsToEnd = this.effects.filter((effect) => {
          return effect.context?.actorLink && inEffectIds.includes(effect.data?.persistOptions?.id);
        });
        const effectsByActorUuid = Object.values(
          group_by(actorEffectsToEnd, "context.actor.uuid")
        );
        const regularEffectsToEnd = this.effects.filter((effect) => {
          return inEffectIds.includes(effect.id) || !effect.context?.actorLink && inEffectIds.includes(effect.data?.persistOptions?.id);
        });
        const effectsByContextUuid = Object.values(
          group_by(regularEffectsToEnd, "context.uuid")
        );
        effectsByContextUuid.forEach((effects) => {
          effects = effects.filter(
            (effect) => effect.data.persist && !effect.data.temporary
          );
          if (!effects.length) return;
          const effectData = effects.map((effect) => effect.data);
          flagManager.removeEffectFlags(
            effects[0].context.uuid,
            effectData,
            !inEffectIds
          );
        });
        effectsByActorUuid.forEach((effects) => {
          effects = effects.filter(
            (effect) => effect.data.persist && !effect.data.temporary
          );
          if (!effects.length) return;
          const effectContext = effects[0].context;
          const effectData = effects.map((effect) => effect.data);
          if (!(effectContext instanceof TokenDocument && effectContext.actorLink && effectContext.actor.prototypeToken.actorLink)) {
            return;
          }
          const persistentEffectData = effectData.filter(
            (data) => is_UUID(data?.source) && data?.persistOptions?.persistTokenPrototype
          );
          if (!persistentEffectData.length) return;
          const actorEffects = flagManager.getEffectFlags(effectContext.actor);
          const applicableActorEffects = actorEffects.filter((effect) => {
            return is_UUID(effect[1]?.source) && effect[1]?.persistOptions?.persistTokenPrototype && persistentEffectData.some(
              (persistentEffect) => persistentEffect.persistOptions.id === effect[1]?.persistOptions?.id
            );
          }).map((e) => e[0]);
          flagManager.removeEffectFlags(
            effectContext.actor.uuid,
            applicableActorEffects,
            !inEffectIds
          );
        });
        const effectsToEnd = effectsByContextUuid.concat(effectsByActorUuid).deepFlatten();
        return Promise.allSettled(
          effectsToEnd.map((effect) => this._removeEffect(effect))
        );
      }
      static _effectContextFilter(inUUID, effectData) {
        return effectData?.source === inUUID || effectData?.target === inUUID || (effectData?.tiedDocuments ?? []).indexOf(inUUID) > -1;
      }
      /**
       * Handles the deletion of objects that effects are attached to
       *
       * @param inUUID
       * @returns {Promise}
       */
      static objectDeleted(inUUID) {
        const documentsToCheck = game.scenes.filter((scene) => scene.id !== game.user.viewedScene).map((scene) => [scene, ...get_all_documents_from_scene(scene.id)]).deepFlatten();
        const documentEffectsToEnd = documentsToCheck.map((obj) => {
          const objEffects = flagManager.getEffectFlags(obj);
          const effectsToEnd = objEffects.filter(
            ([effectId, effectData]) => this._effectContextFilter(inUUID, effectData)
          );
          return {
            document: obj,
            effects: effectsToEnd.map((effect) => effect[0])
          };
        }).filter((obj) => obj.effects.length);
        const visibleEffectsToEnd = this.effects.filter((effect) => this._effectContextFilter(inUUID, effect.data)).map((e) => e.id);
        return Promise.allSettled([
          this._endManyEffects(visibleEffectsToEnd),
          ...documentEffectsToEnd.map((obj) => {
            return flagManager.removeEffectFlags(obj.document.uuid, obj.effects);
          })
        ]);
      }
      /**
       * Removes the effect from the manager and ends it, returning a promise that resolves once the effect has fully _ended
       *
       * @param effect
       * @returns {Promise}
       * @private
       */
      static _removeEffect(effect) {
        SequenceManager.VisibleEffects.delete(effect.id);
        TemporaryPositionsContainer.delete(effect.data.source);
        TemporaryPositionsContainer.delete(effect.data.target);
        return effect.endEffect();
      }
    }
    class BaseEffectsLayer extends InteractionLayer {
      static get layerOptions() {
        return foundry.utils.mergeObject(super.layerOptions, {
          elevation: 1e8,
          name: "sequencerEffects"
        });
      }
    }
    class SequencerInterfaceLayer extends InteractionLayer {
      constructor(...args) {
        super(...args);
      }
      static get layerOptions() {
        return foundry.utils.mergeObject(super.layerOptions, {
          elevation: 1e8,
          name: "sequencerInterfaceEffects"
        });
      }
      deactivate() {
        super.deactivate();
        if (!this.active) return;
        this._clearChildren();
        this.active = false;
        InteractionManager.tearDown();
      }
      _setup() {
        if (!this.UIContainer || this.UIContainer._destroyed) {
          this.UIContainer = new PIXI.Container();
          this.UIContainer.sortableChildren = true;
          this.UIContainer.parentName = "sequencerUIContainer";
          this.UIContainer.zIndex = 1e13;
          this.addChild(this.UIContainer);
          this.linePoint = this.UIContainer.addChild(new PIXI.Graphics());
          this.line = this.UIContainer.addChild(new PIXI.Graphics());
          this.lineHead = this.UIContainer.addChild(new PIXI.Graphics());
          this.suggestionPoint = this.UIContainer.addChild(new PIXI.Graphics());
          this.effectHoverBoxes = this.UIContainer.addChild(new PIXI.Graphics());
          this.effectSelectionBorder = this.UIContainer.addChild(
            new PIXI.Graphics()
          );
          this.effectSourcePosition = this.UIContainer.addChild(
            new PIXI.Graphics()
          );
          this.effectTargetPosition = this.UIContainer.addChild(
            new PIXI.Graphics()
          );
          this.suggestionPoint.filters = [new PIXI.AlphaFilter(0.75)];
          this.effectSourcePosition.filters = [new PIXI.AlphaFilter(0.75)];
          this.effectTargetPosition.filters = [new PIXI.AlphaFilter(0.75)];
          this.effectSelectionBorder.zIndex = 1;
          this.effectSourcePosition.interactive = true;
          this.effectSourcePosition.on("mousedown", () => {
            SelectionManager.sourcePointSelected();
          });
          this.effectTargetPosition.interactive = true;
          this.effectTargetPosition.on("mousedown", () => {
            SelectionManager.targetPointSelected();
          });
        }
      }
      async _draw(...args) {
      }
      render(...args) {
        super.render(...args);
        this._setup();
        this._clearChildren();
        this._drawHoveredEffectElements();
        if (!this.active) return;
        this._drawLine();
        this._drawPoints();
        this._drawSelectedEffectElements();
        this._drawSuggestionPoint();
      }
      _clearChildren() {
        if (!this.UIContainer) return;
        this.UIContainer.children.forEach((child) => {
          child.clear();
        });
      }
      _drawLine() {
        if (!EffectPlayer.startPos || !EffectPlayer.endPos || game?.activeTool !== "play-effect")
          return;
        this.line.lineStyle(3, CONSTANTS.COLOR.PRIMARY, 1);
        this.line.moveTo(EffectPlayer.startPos.x, EffectPlayer.startPos.y);
        this.line.lineTo(EffectPlayer.endPos.x, EffectPlayer.endPos.y);
      }
      _drawPoints() {
        if (game?.activeTool !== "play-effect") return;
        const startPos = EffectPlayer.startPos || EffectPlayer.cursorPos;
        this.linePoint.beginFill(CONSTANTS.COLOR.PRIMARY);
        this.linePoint.drawCircle(startPos.x, startPos.y, 5);
        if (EffectPlayer.sourceAttachFound) {
          this._drawCrossAtLocation(this.linePoint, startPos);
        }
        if (!EffectPlayer.endPos) return;
        const angle = new Ray(startPos, EffectPlayer.endPos).angle;
        this.lineHead.beginFill(CONSTANTS.COLOR.PRIMARY);
        this.lineHead.moveTo(0, -5);
        this.lineHead.lineTo(-15, 30);
        this.lineHead.lineTo(15, 30);
        this.lineHead.endFill();
        this.lineHead.rotation = angle + Math.PI / 2;
        this.lineHead.position.set(EffectPlayer.endPos.x, EffectPlayer.endPos.y);
        if (EffectPlayer.targetAttachFound) {
          this.linePoint.beginFill(CONSTANTS.COLOR.SECONDARY);
          this._drawCrossAtLocation(this.linePoint, EffectPlayer.endPos);
        }
      }
      _drawHoveredEffectElements() {
        const effects = new Set(SelectionManager.hoveredEffects);
        if (SelectionManager.hoveredEffectUI)
          effects.add(SelectionManager.hoveredEffectUI);
        for (const effect of effects) {
          if (!effect || effect === SelectionManager.selectedEffect || effect.data.screenSpace || effect._isEnding)
            continue;
          this._drawBoxAroundEffect(this.effectHoverBoxes, effect);
        }
      }
      _drawSelectedEffectElements() {
        if (!SelectionManager.selectedEffect) return;
        this._drawBoxAroundEffect(
          this.effectSelectionBorder,
          SelectionManager.selectedEffect,
          true
        );
        this._drawEffectStartEndPoints(SelectionManager.selectedEffect);
      }
      _drawBoxAroundEffect(graphic, effect, selected = false) {
        if (!effect || effect._destroyed || !effect.spriteContainer || !effect.ready)
          return;
        graphic.lineStyle(3, selected ? CONSTANTS.COLOR.PRIMARY : 16777215, 0.9);
        let boundingBox = effect.sprite.getLocalBounds();
        let dimensions = {
          x: effect.position.x + boundingBox.x * effect.sprite.scale.x,
          y: effect.position.y + boundingBox.y * effect.sprite.scale.y,
          width: boundingBox.width * effect.sprite.scale.x,
          height: boundingBox.height * effect.sprite.scale.y
        };
        if (effect.data.shapes.length) {
          for (const shape of Object.values(effect.shapes)) {
            boundingBox = shape.getLocalBounds();
            dimensions = {
              x: Math.min(
                dimensions.x,
                effect.position.x + boundingBox.x * shape.scale.x
              ),
              y: Math.min(
                dimensions.y,
                effect.position.y + boundingBox.y * shape.scale.y
              ),
              width: Math.max(dimensions.width, boundingBox.width * shape.scale.x),
              height: Math.max(
                dimensions.height,
                boundingBox.height * shape.scale.y
              )
            };
          }
        }
        const rotation2 = Math.normalizeRadians(
          effect.rotationContainer.rotation + effect.spriteContainer.rotation + effect.sprite.rotation
        );
        this._drawRectangle(graphic, effect.position, rotation2, dimensions);
      }
      _drawRectangle(graphic, position, rotation2, dimensions) {
        graphic.moveTo(
          ...rotate_coordinate(
            position,
            {
              x: dimensions.x,
              y: dimensions.y
            },
            -rotation2
          )
        );
        graphic.lineTo(
          ...rotate_coordinate(
            position,
            {
              x: dimensions.x + dimensions.width,
              y: dimensions.y
            },
            -rotation2
          )
        );
        graphic.lineTo(
          ...rotate_coordinate(
            position,
            {
              x: dimensions.x + dimensions.width,
              y: dimensions.y + dimensions.height
            },
            -rotation2
          )
        );
        graphic.lineTo(
          ...rotate_coordinate(
            position,
            {
              x: dimensions.x,
              y: dimensions.y + dimensions.height
            },
            -rotation2
          )
        );
        graphic.lineTo(
          ...rotate_coordinate(
            position,
            {
              x: dimensions.x,
              y: dimensions.y
            },
            -rotation2
          )
        );
        graphic.lineTo(
          ...rotate_coordinate(
            position,
            {
              x: dimensions.x + dimensions.width,
              y: dimensions.y
            },
            -rotation2
          )
        );
      }
      /**
       * Draws the start/end point circles
       * @private
       */
      _drawEffectStartEndPoints(effect) {
        if (!effect || effect._destroyed || !effect.spriteContainer) return;
        if (!effect.data.stretchTo || !effect.sourcePosition || !effect.targetPosition)
          return;
        this.effectSourcePosition.beginFill(CONSTANTS.COLOR.PRIMARY);
        this.effectSourcePosition.drawCircle(
          effect.sourcePosition.x,
          effect.sourcePosition.y,
          canvas.grid.size * 0.25
        );
        if (typeof effect.data.source === "string") {
          this._drawCrossAtLocation(
            this.effectSourcePosition,
            effect.sourcePosition
          );
        }
        this.effectTargetPosition.beginFill(CONSTANTS.COLOR.SECONDARY);
        this.effectTargetPosition.drawCircle(
          effect.targetPosition.x,
          effect.targetPosition.y,
          canvas.grid.size * 0.25
        );
        this.effectTargetPosition.alpha = 0.75;
        if (typeof effect.data.target === "string") {
          this._drawCrossAtLocation(
            this.effectTargetPosition,
            effect.targetPosition
          );
        }
      }
      _drawSuggestionPoint() {
        if (!SelectionManager.suggestedProperties || !SelectionManager.selectedEffect)
          return;
        const effect = SelectionManager.selectedEffect;
        const suggestion = SelectionManager.suggestedProperties;
        this.suggestionPoint.position.set(0, 0);
        this.suggestionPoint.rotation = 0;
        if (effect.data.stretchTo) {
          this.suggestionPoint.beginFill(suggestion.color);
          this.suggestionPoint.drawCircle(
            suggestion.position.x,
            suggestion.position.y,
            canvas.grid.size * 0.25
          );
          if (suggestion.showCursor) {
            this._drawCrossAtLocation(this.suggestionPoint, suggestion.position);
          }
          return;
        }
        const boundingBox = effect.spriteContainer.getLocalBounds();
        const dimensions = {
          x: boundingBox.x * effect.scale.x,
          y: boundingBox.y * effect.scale.y,
          width: boundingBox.width * effect.scale.x,
          height: boundingBox.height * effect.scale.y
        };
        this.suggestionPoint.lineStyle(3, CONSTANTS.COLOR.PRIMARY, 0.9);
        this.suggestionPoint.position.set(
          suggestion.position.x,
          suggestion.position.y
        );
        this._drawRectangle(
          this.suggestionPoint,
          suggestion.position,
          effect.rotation,
          dimensions,
          true
        );
        if (suggestion.showCursor) {
          this.suggestionPoint.beginFill(CONSTANTS.COLOR.SECONDARY);
          this._drawCrossAtLocation(this.suggestionPoint);
        }
        if (suggestion.showPoint) {
          this.suggestionPoint.drawCircle(0, 0, canvas.grid.size * 0.2);
        }
      }
      _drawCrossAtLocation(inElement, inPosition = { x: 0, y: 0 }) {
        inElement.drawRect(
          inPosition.x + canvas.grid.size * -0.05,
          inPosition.y + canvas.grid.size * -0.5,
          canvas.grid.size * 0.1,
          canvas.grid.size
        );
        inElement.drawRect(
          inPosition.x + canvas.grid.size * -0.5,
          inPosition.y + canvas.grid.size * -0.05,
          canvas.grid.size,
          canvas.grid.size * 0.1
        );
      }
    }
    class UIEffectsLayer extends InteractionLayer {
      static get layerOptions() {
        return foundry.utils.mergeObject(super.layerOptions, {
          zIndex: 999999999999999,
          name: "sequencerEffectsAboveEverything"
        });
      }
      updateTransform() {
        if (this.sortableChildren && this.sortDirty) {
          this.sortChildren();
        }
        this._boundsID++;
        this.transform.updateTransform(PIXI.Transform.IDENTITY);
        this.worldAlpha = this.alpha;
        for (let child of this.children) {
          if (child.visible) {
            child.updateTransform();
          }
        }
      }
    }
    let layer = false;
    class SequencerAboveUILayer {
      constructor(name2, zIndex = 0.1) {
        this.canvas = document.createElement("canvas");
        this.canvas.id = name2;
        this.canvas.style.cssText = `
            position:absolute;
            touch-action: none;
            pointer-events: none;
            width:100%;
            height:100%;
            z-index:${zIndex};
            padding: 0;
            margin: 0;
        `;
        document.body.appendChild(this.canvas);
        this.app = new PIXI.Application({
          width: window.innerWidth,
          height: window.innerHeight,
          view: this.canvas,
          antialias: true,
          backgroundAlpha: 0,
          sharedTicker: true
        });
        this.app.resizeTo = window;
        this.app.stage.renderable = false;
      }
      static setup() {
        if (!game.settings.get("sequencer", "enable-above-ui-screenspace")) return;
        layer = new this("sequencerUILayerAbove", 1e4);
      }
      static getLayer() {
        return layer ? layer.app.stage : canvas.sequencerEffectsUILayer;
      }
      static addChild(...args) {
        const layer2 = this.getLayer();
        const result = layer2.addChild(...args);
        layer2.renderable = layer2.children.length > 0;
        return result;
      }
      static sortChildren() {
        return this.getLayer().sortChildren();
      }
      static removeContainerByEffect(inEffect) {
        const layer2 = this.getLayer();
        if (!(layer2 instanceof SequencerAboveUILayer)) return;
        const child = layer2.children.find((child2) => child2 === inEffect);
        if (!child) return;
        layer2.removeChild(child);
        layer2.renderable = layer2.children.length > 0;
      }
      updateTransform() {
        if (this.app.stage.sortableChildren && this.app.stage.sortDirty) {
          this.app.stage.sortChildren();
        }
        this.app.stage._boundsID++;
        this.app.stage.transform.updateTransform(PIXI.Transform.IDENTITY);
        this.app.stage.worldAlpha = this.app.stage.alpha;
        for (let child of this.app.stage.children) {
          if (child.visible) {
            child.updateTransform();
          }
        }
      }
    }
    const tempMat = new PIXI.Matrix();
    const uvPoint = new PIXI.Point();
    class VisionSamplerShaderGenerator extends BatchShaderGenerator {
      generateSampleSrc(maxTextures) {
        let src = "\n\n";
        for (let i = 0; i < maxTextures; i++) {
          if (i > 0) {
            src += "\nelse ";
          }
          if (i < maxTextures - 1) {
            src += `if(vTextureId < ${i}.5)`;
          }
          src += "\n{";
          src += `
	color = texture(uSamplers[${i}], coord, unclamped == coord ? 0.0f : -32.0f);`;
          src += "\n}";
        }
        src += "\n";
        src += "\n";
        return src;
      }
    }
    const emptyColorMatrix = new Uint8Array(20);
    class VisionSamplerShader extends BaseSamplerShader {
      static classPluginName = "sequencerVisionBatch";
      static batchShaderGeneratorClass = VisionSamplerShaderGenerator;
      static batchGeometry = [
        { id: "aVertexPosition", size: 2, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aTextureCoord", size: 2, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aClampOffset", size: 2, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aClampFrame", size: 4, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aMapCoord1", size: 3, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aMapCoord2", size: 3, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aMapCoord3", size: 3, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aColor", size: 4, normalized: true, type: PIXI.TYPES.UNSIGNED_BYTE },
        { id: "aColorMatrixR", size: 4, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aColorMatrixG", size: 4, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aColorMatrixB", size: 4, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aColorMatrixA", size: 4, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aColorMatrixOffset", size: 4, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aColorMatrixAlpha", size: 1, normalized: false, type: PIXI.TYPES.FLOAT },
        { id: "aShaderFlags", size: 1, normalized: false, type: PIXI.TYPES.UNSIGNED_SHORT },
        { id: "aTextureId", size: 1, normalized: false, type: PIXI.TYPES.UNSIGNED_SHORT }
      ];
      static #SHADER_FLAGS = foundry.utils.BitMask.generateShaderBitMaskConstants([
        "IS_TILING",
        "IS_VISION_MASKING_ENABLED",
        "IS_COLOR_MATRIX_ENABLED"
      ]);
      /** @override */
      static batchVertexSize = 42;
      /** @override */
      static reservedTextureUnits = 1;
      // We need a texture unit for the occlusion texture
      /**
       * The batch vertex shader source.
       * @type {string}
       */
      static batchVertexShader = (
        /* glsl */
        `#version 300 es
	precision highp float;
	
	uniform vec2 screenDimensions;
	uniform mat3 projectionMatrix;
	uniform mat3 translationMatrix;
	uniform vec4 tint;
	
	in vec2 aVertexPosition;
	in vec2 aTextureCoord;
	in vec2 aClampOffset;
	in vec4 aClampFrame;
	in vec3 aMapCoord1;
	in vec3 aMapCoord2;
	in vec3 aMapCoord3;
	in vec4 aColor;
	in vec4 aColorMatrixR;
	in vec4 aColorMatrixG;
	in vec4 aColorMatrixB;
	in vec4 aColorMatrixA;
	in vec4 aColorMatrixOffset;
	in float aColorMatrixAlpha;
	in float aShaderFlags;
	in float aTextureId;
	
	out vec2 vTextureCoord;
	out vec2 vVisionCoord;
	flat out vec2 vClampOffset;
	flat out vec4 vClampFrame;
	flat out mat3 vMapCoord;
	flat out vec4 vColor;
	flat out mat4 vColorMatrix;
	flat out vec4 vColorMatrixOffset;
	flat out float vColorMatrixAlpha;
	flat out uint vShaderFlags;
	flat out float vTextureId;
	
	void main(void) {
		gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0f)).xy, 0.0f, 1.0f);
		vTextureCoord = aTextureCoord;
		vVisionCoord = aVertexPosition / screenDimensions;
		vColor = aColor * tint;
		vClampOffset = aClampOffset;
		vClampFrame = aClampFrame;
		vMapCoord = mat3(aMapCoord1, aMapCoord2, aMapCoord3);
		vColorMatrix = mat4(aColorMatrixR, aColorMatrixG, aColorMatrixB, aColorMatrixA);
		vColorMatrixOffset = aColorMatrixOffset;
		vColorMatrixAlpha = aColorMatrixAlpha;
		vShaderFlags = uint(aShaderFlags);
		vTextureId = aTextureId;
	}
	`
      );
      /**
       * The batch fragment shader source.
       * @type {string}
       */
      static batchFragmentShader = (
        /* glsl */
        `#version 300 es
	precision mediump float;

	in vec2 vTextureCoord;
	in vec2 vVisionCoord;
	flat in float vTextureId;
	flat in vec4 vColor;
	flat in mat3 vMapCoord;
	flat in uint vShaderFlags;
	flat in vec2 vClampOffset;
	flat in vec4 vClampFrame;
	flat in mat4 vColorMatrix;
	flat in vec4 vColorMatrixOffset;
	flat in float vColorMatrixAlpha;

	uniform bool enableVisionMasking;
	uniform sampler2D visionMaskTexture;
	uniform sampler2D uSamplers[%count%];
	out vec4 fragColor;

	${VisionSamplerShader.#SHADER_FLAGS}

	void main(void){
		bool isTilingEnabled = ((vShaderFlags & IS_TILING) == IS_TILING);
		bool isVisionMaskingEnabled = ((vShaderFlags & IS_VISION_MASKING_ENABLED) == IS_VISION_MASKING_ENABLED);
		bool isColorMatrixEnabled = ((vShaderFlags & IS_COLOR_MATRIX_ENABLED) == IS_COLOR_MATRIX_ENABLED);

		vec2 coord = vTextureCoord;
		vec2 unclamped;
		if (isTilingEnabled) {
			coord = coord + ceil(vClampOffset - coord);
			coord = (vMapCoord * vec3(coord, 1.0)).xy;
			unclamped = coord;
			coord = clamp(coord, vClampFrame.xy, vClampFrame.zw);
		} else {
			unclamped = coord;
	  }
	  float mask = isVisionMaskingEnabled && enableVisionMasking ? texture(visionMaskTexture, vVisionCoord).r : 1.0;

	  vec4 color;
	  %forloop%

		if (isColorMatrixEnabled && vColorMatrixAlpha > 0.0) {
			if (color.a > 0.0) {
	      color.rgb /= color.a;
	    }
			vec4 result = color * vColorMatrix + vColorMatrixOffset;

			vec3 rgb = mix(color.rgb, result.rgb, vColorMatrixAlpha);
			rgb *= result.a;
			color = vec4(rgb, result.a);
	  }
	  fragColor = color * mask * vColor;
	}	
	`
      );
      /** @override */
      static batchDefaultUniforms(maxTex) {
        return {
          screenDimensions: [1, 1],
          visionMaskTexture: maxTex
        };
      }
      /** @override */
      static _preRenderBatch(batchRenderer) {
        const uniforms = batchRenderer._shader.uniforms;
        uniforms.screenDimensions = canvas.screenDimensions;
        uniforms.enableVisionMasking = canvas?.visibility?.visible ?? canvas?.effects?.visibility?.visible ?? true;
        batchRenderer.renderer.texture.bind(canvas.masks.vision.renderTexture, uniforms.visionMaskTexture);
      }
      /** @override */
      static _packInterleavedGeometry(element2, attributeBuffer, indexBuffer, aIndex, iIndex) {
        const { float32View, uint32View, uint16View } = attributeBuffer;
        const mesh = element2.object;
        const packedVertices = aIndex / this.vertexSize;
        const indices = element2.indices;
        for (let i = 0; i < indices.length; i++) {
          indexBuffer[iIndex++] = packedVertices + indices[i];
        }
        const vertexData = element2.vertexData;
        let uvs = element2.uvs;
        const alpha = Math.min(element2.worldAlpha, 1);
        const baseTexture = element2._texture.baseTexture;
        const argb = PIXI.Color.shared.setValue(element2._tintRGB).toPremultiplied(alpha, baseTexture.alphaMode > 0);
        const textureId = element2._texture.baseTexture._batchLocation;
        const mapCoord = mesh.uvMatrix.mapCoord.toArray(true);
        let clampFrame = mesh.uvMatrix.uClampFrame;
        let clampOffset = mesh.uvMatrix.uClampOffset;
        if (mesh.tiling) {
          const tex = element2._texture;
          const w = tex.width;
          const h = tex.height;
          const W = mesh.width;
          const H = mesh.height;
          const lt = mesh.tileTransform.localTransform;
          tempMat.set(lt.a * w / W, lt.b * w / H, lt.c * h / W, lt.d * h / H, lt.tx / W, lt.ty / H);
          const anchorX = mesh.uvRespectAnchor ? mesh.anchor.x : 0;
          const anchorY = mesh.uvRespectAnchor ? mesh.anchor.y : 0;
          uvs[0] = uvs[6] = -anchorX;
          uvs[1] = uvs[3] = -anchorY;
          uvs[2] = uvs[4] = 1 - anchorX;
          uvs[5] = uvs[7] = 1 - anchorY;
        }
        const vertexSize = this.vertexSize;
        const colorMatrixArray = mesh.colorMatrixFilter?.matrix ?? emptyColorMatrix;
        const colorMatrixAlpha = mesh.colorMatrixFilter?.alpha ?? 1;
        for (let i = 0, j = 0; i < vertexData.length; i += 2, j += vertexSize) {
          uvPoint.set(uvs[i], uvs[i + 1]);
          if (mesh.tiling) {
            tempMat.applyInverse(uvPoint, uvPoint);
          }
          let k = aIndex + j;
          float32View[k++] = vertexData[i];
          float32View[k++] = vertexData[i + 1];
          float32View[k++] = uvPoint.x;
          float32View[k++] = uvPoint.y;
          float32View[k++] = clampOffset[0];
          float32View[k++] = clampOffset[1];
          float32View[k++] = clampFrame[0];
          float32View[k++] = clampFrame[1];
          float32View[k++] = clampFrame[2];
          float32View[k++] = clampFrame[3];
          float32View[k++] = mapCoord[0];
          float32View[k++] = mapCoord[1];
          float32View[k++] = mapCoord[2];
          float32View[k++] = mapCoord[3];
          float32View[k++] = mapCoord[4];
          float32View[k++] = mapCoord[5];
          float32View[k++] = mapCoord[6];
          float32View[k++] = mapCoord[7];
          float32View[k++] = mapCoord[8];
          uint32View[k++] = argb;
          float32View[k++] = colorMatrixArray[0];
          float32View[k++] = colorMatrixArray[1];
          float32View[k++] = colorMatrixArray[2];
          float32View[k++] = colorMatrixArray[3];
          float32View[k++] = colorMatrixArray[5];
          float32View[k++] = colorMatrixArray[6];
          float32View[k++] = colorMatrixArray[7];
          float32View[k++] = colorMatrixArray[8];
          float32View[k++] = colorMatrixArray[10];
          float32View[k++] = colorMatrixArray[11];
          float32View[k++] = colorMatrixArray[12];
          float32View[k++] = colorMatrixArray[13];
          float32View[k++] = colorMatrixArray[15];
          float32View[k++] = colorMatrixArray[16];
          float32View[k++] = colorMatrixArray[17];
          float32View[k++] = colorMatrixArray[18];
          float32View[k++] = colorMatrixArray[4];
          float32View[k++] = colorMatrixArray[9];
          float32View[k++] = colorMatrixArray[14];
          float32View[k++] = colorMatrixArray[19];
          float32View[k++] = colorMatrixAlpha;
          k <<= 1;
          uint16View[k++] = mesh.shaderFlags.valueOf();
          uint16View[k++] = textureId;
        }
      }
      // need to override createPlugin as canvas.performance is not defined in foundry when
      // we need it to be
      static createPlugin() {
        const shaderClass = this;
        const geometryClass = Array.isArray(shaderClass.batchGeometry) ? class BatchGeometry extends PIXI.Geometry {
          constructor(_static = false) {
            super();
            this._buffer = new PIXI.Buffer(null, _static, false);
            this._indexBuffer = new PIXI.Buffer(null, _static, true);
            for (const { id, size, normalized, type } of shaderClass.batchGeometry) {
              this.addAttribute(id, this._buffer, size, normalized, type);
            }
            this.addIndex(this._indexBuffer);
          }
        } : shaderClass.batchGeometry;
        return class BatchPlugin extends shaderClass.batchRendererClass {
          /** @override */
          static get shaderGeneratorClass() {
            return shaderClass.batchShaderGeneratorClass;
          }
          /* ---------------------------------------- */
          /** @override */
          static get defaultVertexSrc() {
            return shaderClass.batchVertexShader;
          }
          /* ---------------------------------------- */
          /** @override */
          static get defaultFragmentTemplate() {
            return shaderClass.batchFragmentShader;
          }
          /* ---------------------------------------- */
          /** @override */
          static get defaultUniforms() {
            return shaderClass.batchDefaultUniforms;
          }
          /* ---------------------------------------- */
          /**
           * The batch plugin constructor.
           * @param {PIXI.Renderer} renderer    The renderer
           */
          constructor(renderer) {
            super(renderer);
            this.geometryClass = geometryClass;
            this.vertexSize = shaderClass.batchVertexSize;
            this.reservedTextureUnits = shaderClass.reservedTextureUnits;
            this._packInterleavedGeometry = shaderClass._packInterleavedGeometry;
            this._preRenderBatch = shaderClass._preRenderBatch;
          }
          /* ---------------------------------------- */
          /** @inheritdoc */
          setShaderGenerator(options) {
            super.setShaderGenerator(options);
          }
          /* ---------------------------------------- */
          /** @inheritdoc */
          contextChange() {
            this.shaderGenerator = null;
            super.contextChange();
          }
        };
      }
    }
    class TilingSpriteMesh extends SpriteMesh {
      /** @type {PIXI.Transform} */
      tileTransform;
      /** @type {PIXI.TextureMatrix} */
      uvMatrix;
      /** @type {PIXI.ColorMatrixFilter} */
      #colorMatrixFilter;
      shaderFlags = new foundry.utils.BitMask({
        isTiling: false,
        isVisionMaskingEnabled: true,
        isColorMatrixEnabled: false
      });
      /**
       * @param {PIXI.Texture} texture - The {@link PIXI.Texture} bound to this mesh
       * @param {object} [options = {}]
       * @property {typeof BaseSamplerShader} options.shaderClassader Shader class used by this sprite mesh.
       * @property {boolean} options.tiling Shader class used by this sprite mesh.
       * @property {boolean} options.isVisionMaskingEnabled Shader class used by this sprite mesh.
       */
      constructor(texture, { shaderClass = VisionSamplerShader, tiling = false, isVisionMaskingEnabled = true } = {}) {
        super(texture, shaderClass);
        this.tileTransform = new PIXI.Transform();
        this.tiling = tiling;
        this.isVisionMaskingEnabled = isVisionMaskingEnabled;
        this.uvMatrix = this.texture.uvMatrix || new PIXI.TextureMatrix(texture);
      }
      get tiling() {
        return this.shaderFlags.hasState("isTiling");
      }
      /**
       * @param {boolean} value
       */
      set tiling(value) {
        this.shaderFlags.toggleState("isTiling", value);
      }
      get isVisionMaskingEnabled() {
        return this.shaderFlags.hasState("isVisionMaskingEnabled");
      }
      /**
       * @param {boolean} value
       */
      set isVisionMaskingEnabled(value) {
        this.shaderFlags.toggleState("isVisionMaskingEnabled", value);
      }
      /**
       * @param {PIXI.ColorMatrixFilter | null} value
       */
      set colorMatrixFilter(value) {
        this.shaderFlags.toggleState("isColorMatrixEnabled", value != null);
        this.#colorMatrixFilter = value;
      }
      get colorMatrixFilter() {
        return this.#colorMatrixFilter;
      }
      /** The scaling of the image that is being tiled. */
      get tileScale() {
        return this.tileTransform.scale;
      }
      set tileScale(value) {
        this.tileTransform.scale.copyFrom(value);
      }
      /** The offset of the image that is being tiled. */
      get tilePosition() {
        return this.tileTransform.position;
      }
      set tilePosition(value) {
        this.tileTransform.position.copyFrom(value);
      }
      _onTextureUpdate() {
        super._onTextureUpdate();
        if (this.uvMatrix) {
          this.uvMatrix.texture = this._texture;
        }
      }
      /**
       * Renders the object using the WebGL renderer
       * @param renderer - The renderer
       */
      _render(renderer) {
        this.uvMatrix.update();
        this.tileTransform.updateLocalTransform();
        super._render(renderer);
      }
      destroy() {
        super.destroy();
        this.tileTransform = null;
        this.uvMatrix = null;
      }
    }
    class AnimatedSpriteMesh extends TilingSpriteMesh {
      /**
       * @param textures - An array of {@link PIXI.Texture} or frame
       *  objects that make up the animation.
       * @param {object} [options = {}]
       * @property {typeof BaseSamplerShader} options.shaderClassader Shader class used by this sprite mesh.
       * @property {boolean} options.tiling Shader class used by this sprite mesh.
       * @property {boolean} options.isVisionMaskingEnabled Shader class used by this sprite mesh.
       */
      constructor(textures, { autoUpdate = true, shaderClass = VisionSamplerShader, isVisionMaskingEnabled = true, tiling } = {}) {
        const texture = textures[0] instanceof PIXI.Texture ? textures[0] : textures[0].texture;
        super(texture, { shaderClass, tiling, isVisionMaskingEnabled });
        this._textures = [];
        this._durations = null;
        this._autoUpdate = autoUpdate;
        this._isConnectedToTicker = false;
        this.animationSpeed = 1;
        this.loop = true;
        this.updateAnchor = false;
        this.onComplete = null;
        this.onFrameChange = null;
        this.onLoop = null;
        this._currentTime = 0;
        this._playing = false;
        this._previousFrame = null;
        this.textures = textures;
      }
      /** Stops the AnimatedSprite. */
      stop() {
        if (!this._playing) {
          return;
        }
        this._playing = false;
        if (this._autoUpdate && this._isConnectedToTicker) {
          PIXI.Ticker.shared.remove(this.update, this);
          this._isConnectedToTicker = false;
        }
      }
      /** Plays the AnimatedSprite. */
      play() {
        if (this._playing) {
          return;
        }
        this._playing = true;
        if (this._autoUpdate && !this._isConnectedToTicker) {
          PIXI.Ticker.shared.add(this.update, this, PIXI.UPDATE_PRIORITY.HIGH);
          this._isConnectedToTicker = true;
        }
      }
      /**
       * Stops the AnimatedSprite and goes to a specific frame.
       * @param frameNumber - Frame index to stop at.
       */
      gotoAndStop(frameNumber) {
        this.stop();
        this.currentFrame = frameNumber;
      }
      /**
       * Goes to a specific frame and begins playing the AnimatedSprite.
       * @param frameNumber - Frame index to start at.
       */
      gotoAndPlay(frameNumber) {
        this.currentFrame = frameNumber;
        this.play();
      }
      /**
       * Updates the object transform for rendering.
       * @param deltaTime - Time since last tick.
       */
      update(deltaTime) {
        if (!this._playing) {
          return;
        }
        const elapsed = this.animationSpeed * deltaTime;
        const previousFrame = this.currentFrame;
        if (this._durations !== null) {
          let lag = this._currentTime % 1 * this._durations[this.currentFrame];
          lag += elapsed / 60 * 1e3;
          while (lag < 0) {
            this._currentTime--;
            lag += this._durations[this.currentFrame];
          }
          const sign = Math.sign(this.animationSpeed * deltaTime);
          this._currentTime = Math.floor(this._currentTime);
          while (lag >= this._durations[this.currentFrame]) {
            lag -= this._durations[this.currentFrame] * sign;
            this._currentTime += sign;
          }
          this._currentTime += lag / this._durations[this.currentFrame];
        } else {
          this._currentTime += elapsed;
        }
        if (this._currentTime < 0 && !this.loop) {
          this.gotoAndStop(0);
          if (this.onComplete) {
            this.onComplete();
          }
        } else if (this._currentTime >= this._textures.length && !this.loop) {
          this.gotoAndStop(this._textures.length - 1);
          if (this.onComplete) {
            this.onComplete();
          }
        } else if (previousFrame !== this.currentFrame) {
          if (this.loop && this.onLoop) {
            if (this.animationSpeed > 0 && this.currentFrame < previousFrame || this.animationSpeed < 0 && this.currentFrame > previousFrame) {
              this.onLoop();
            }
          }
          this.updateTexture();
        }
      }
      /** Updates the displayed texture to match the current frame index. */
      updateTexture() {
        const currentFrame = this.currentFrame;
        if (this._previousFrame === currentFrame) {
          return;
        }
        this._previousFrame = currentFrame;
        this._texture = this._textures[currentFrame];
        this._textureID = -1;
        this._textureTrimmedID = -1;
        this._cachedTint = [1, 1, 1, 1];
        this.uvs.set(this._texture._uvs.uvsFloat32);
        if (this.updateAnchor) {
          this._anchor.copyFrom(this._texture.defaultAnchor);
        }
        if (this.onFrameChange) {
          this.onFrameChange(this.currentFrame);
        }
        if (this.uvMatrix) {
          this.uvMatrix.texture = this._texture;
        }
      }
      destroy(options) {
        this.stop();
        super.destroy(options);
        this.onComplete = null;
        this.onFrameChange = null;
        this.onLoop = null;
      }
      /**
       * A short hand way of creating an AnimatedSprite from an array of frame ids.
       * @param frames - The array of frames ids the AnimatedSprite will use as its texture frames.
       * @returns - The new animated sprite with the specified frames.
       */
      static fromFrames(frames) {
        const textures = [];
        for (let i = 0; i < frames.length; ++i) {
          textures.push(PIXI.Texture.from(frames[i]));
        }
        return new AnimatedSprite(textures);
      }
      /**
       * A short hand way of creating an AnimatedSprite from an array of image ids.
       * @param images - The array of image urls the AnimatedSprite will use as its texture frames.
       * @returns The new animate sprite with the specified images as frames.
       */
      static fromImages(images) {
        const textures = [];
        for (let i = 0; i < images.length; ++i) {
          textures.push(PIXI.Texture.from(images[i]));
        }
        return new AnimatedSprite(textures);
      }
      /**
       * The total number of frames in the AnimatedSprite. This is the same as number of textures
       * assigned to the AnimatedSprite.
       * @readonly
       * @default
       */
      get totalFrames() {
        return this._textures.length;
      }
      /** The array of textures used for this AnimatedSprite. */
      get textures() {
        return this._textures;
      }
      set textures(value) {
        if (value[0] instanceof PIXI.Texture) {
          this._textures = value;
          this._durations = null;
        } else {
          this._textures = [];
          this._durations = [];
          for (let i = 0; i < value.length; i++) {
            this._textures.push(value[i].texture);
            this._durations.push(value[i].time);
          }
        }
        this._previousFrame = null;
        this.gotoAndStop(0);
        this.updateTexture();
      }
      /** The AnimatedSprite's current frame index. */
      get currentFrame() {
        let currentFrame = Math.floor(this._currentTime) % this._textures.length;
        if (currentFrame < 0) {
          currentFrame += this._textures.length;
        }
        return currentFrame;
      }
      set currentFrame(value) {
        if (value < 0 || value > this.totalFrames - 1) {
          throw new Error(
            `[AnimatedSprite]: Invalid frame index value ${value}, expected to be between 0 and totalFrames ${this.totalFrames}.`
          );
        }
        const previousFrame = this.currentFrame;
        this._currentTime = value;
        if (previousFrame !== this.currentFrame) {
          this.updateTexture();
        }
      }
      /**
       * Indicates if the AnimatedSprite is currently playing.
       * @readonly
       */
      get playing() {
        return this._playing;
      }
      /** Whether to use Ticker.shared to auto update animation time. */
      get autoUpdate() {
        return this._autoUpdate;
      }
      set autoUpdate(value) {
        if (value !== this._autoUpdate) {
          this._autoUpdate = value;
          if (!this._autoUpdate && this._isConnectedToTicker) {
            PIXI.Ticker.shared.remove(this.update, this);
            this._isConnectedToTicker = false;
          } else if (this._autoUpdate && !this._isConnectedToTicker && this._playing) {
            PIXI.Ticker.shared.add(this.update, this);
            this._isConnectedToTicker = true;
          }
        }
      }
    }
    class Asset {
      destroy() {
      }
    }
    class TextureAsset extends Asset {
      filepath;
      texture;
      constructor({ filepath, texture }) {
        super();
        this.filepath = filepath;
        this.texture = texture;
      }
    }
    class VideoAsset extends Asset {
      /** @type {string} */
      filepath;
      /** @type {PIXI.Texture} */
      texture;
      /** @type {HTMLVideoElement  } */
      video;
      /**
       *
       * @param {object} params
       * @param {string} params.filepath
       * @param {PIXI.Texture} params.texture
       * @param {HTMLVideoElement} params.video
       */
      constructor({ filepath, texture, video }) {
        super();
        this.filepath = filepath;
        this.texture = texture;
        this.video = video;
      }
      destroy() {
        try {
          this.video.pause();
          URL.revokeObjectURL(this.video.src);
          this.video.removeAttribute("src");
          this.video.onerror = null;
          this.video.oncanplay = null;
          this.video.load();
          this.video = null;
        } catch (err) {
        }
        this.texture.destroy(true);
      }
    }
    class VideoSpritesheetAsset extends Asset {
      /** @type {string} */
      filepath;
      /** @type {PIXI.Spritesheet} */
      spritesheet;
      /** @type {PIXI.FrameObject[]} */
      frameObjects;
      /** @type {number} */
      framerate;
      /**
       * @param {object} params
       * @param {string} params.filepath
       * @param {PIXI.Spritesheet} params.spritesheet
       */
      constructor({ filepath, spritesheet }) {
        super();
        this.filepath = filepath;
        this.spritesheet = spritesheet;
        this.framerate = this.spritesheet.data?.meta?.frameRate ?? 30;
        const frametime = 1 / this.framerate * 1e3;
        this.frameObjects = (Object.values(spritesheet.animations)[0] ?? []).map((texture) => ({
          texture,
          time: frametime
        }));
        this.#register();
      }
      destroy() {
        return SequencerFileCache.unloadSpritesheet(this.filepath);
      }
      #register() {
        SequencerFileCache.registerSpritesheet(this.filepath, this.spritesheet);
      }
    }
    class FlipbookAsset extends Asset {
      /** @type {string[]} */
      filepaths;
      /** @type {PIXI.FrameObject[]} */
      frameObjects;
      /** @type {number} */
      framerate;
      /**
       * @param {object} params
       * @param {string[]} params.filepaths
       * @param {PIXI.Texture[]} params.textures
       * @param {number} params.framerate
       */
      constructor({ filepaths, textures, framerate = 24 }) {
        super();
        this.filepaths = filepaths;
        this.framerate = framerate;
        const frametime = 1 / framerate * 1e3;
        this.frameObjects = textures.map((texture) => ({ texture, time: frametime }));
      }
      destroy() {
      }
    }
    class PlaybackControls {
      destroy() {
      }
    }
    class VideoPlaybackControls extends PlaybackControls {
      /** @type {HTMLVideoElement} */
      #video;
      /** @type {PIXI.Texture} */
      #texture;
      /**
       * @param {HTMLVideoElement} video
       * @param {PIXI.Texture} texture
       */
      constructor(video, texture) {
        super();
        this.#video = video;
        this.#texture = texture;
      }
      async play() {
        try {
          await this.#video.play();
          this.#texture.update();
        } catch (error) {
        }
      }
      stop() {
        this.#video.pause();
      }
      get duration() {
        return this.#video.duration;
      }
      get isPlaying() {
        return !this.#video.paused;
      }
      get loop() {
        return this.#video.loop;
      }
      set loop(value) {
        this.#video.loop = value;
      }
      get volume() {
        return this.#video.volume;
      }
      set volume(value) {
        this.#video.muted = !value;
        this.#video.volume = value;
      }
      get currentTime() {
        return this.#video.currentTime;
      }
      set currentTime(value) {
        this.#video.currentTime = value;
      }
      get playbackRate() {
        return this.#video.playbackRate;
      }
      set playbackRate(value) {
        this.#video.playbackRate = value;
      }
      destroy() {
        this.stop();
        this.#video = null;
        this.#texture = null;
      }
    }
    class SpritePlaybackControls extends PlaybackControls {
      /** @type {AnimatedSpriteMesh} */
      #sprite;
      /** @type {number} */
      #framerate;
      /** @type {number} */
      #framecount;
      /**
       * @param {AnimatedSpriteMesh} sprite
       * @param {number} framerate
       * @param {number} framecount
       */
      constructor(sprite, framerate, framecount) {
        super();
        this.#sprite = sprite;
        this.#framerate = framerate;
        this.#framecount = framecount;
      }
      async play() {
        this.#sprite.play();
      }
      stop() {
        this.#sprite.stop();
      }
      get duration() {
        return this.#framecount / this.#framerate;
      }
      get isPlaying() {
        return this.#sprite.playing;
      }
      get loop() {
        return this.#sprite.loop;
      }
      set loop(value) {
        this.#sprite.loop = value;
      }
      get volume() {
        return 0;
      }
      set volume(_value) {
      }
      get currentTime() {
        const accurateTime = (this.#sprite.currentFrame + 1) / this.#framerate;
        return Math.round(accurateTime * 1e3) / 1e3;
      }
      set currentTime(value) {
        const newFrame = Math.floor(value * this.#framerate);
        const newFrameIndex = Math.clamp(newFrame, 0, this.#framecount - 1);
        if (this.#sprite.playing) {
          this.#sprite.gotoAndPlay(newFrameIndex);
        } else {
          this.#sprite.gotoAndStop(newFrameIndex);
        }
      }
      get playbackRate() {
        return this.#sprite.animationSpeed;
      }
      set playbackRate(value) {
        this.#sprite.animationSpeed = value;
      }
      destroy() {
        this.stop();
        this.#sprite = null;
      }
    }
    class SequencerSpriteManager extends PIXI.Container {
      // @ts-ignore
      #id = foundry.utils.randomID();
      /** @type {import("../modules/sequencer-file.js").SequencerFile} */
      #file;
      /** @type {{ antialiasing: PIXI.SCALE_MODES; tiling?: boolean; xray?: boolean; isPersisted: boolean }} */
      #sharedSpriteConfig;
      /** @type {string | undefined} */
      #activeAssetPath;
      /** @type {Map<string | undefined, VideoAsset | VideoSpritesheetAsset | TextureAsset | FlipbookAsset>} */
      #relatedAssets = /* @__PURE__ */ new Map();
      /** @type {VideoPlaybackControls | SpritePlaybackControls | null} */
      #playbackControls;
      /** @type {Promise<void> | undefined} */
      #preloadingPromise;
      /** @type {TilingSpriteMesh | null} */
      #managedSprite;
      /** @type {PIXI.Text | null} */
      #textSprite;
      /** @type {{width: number, height: number, scaleX: number, scaleY: number}} */
      #defaultScaling = {
        width: 0,
        height: 0,
        scaleX: 1,
        scaleY: 1
      };
      get preloadingPromise() {
        if (this.#preloadingPromise) {
          return this.#preloadingPromise;
        }
        return Promise.resolve();
      }
      /**
       * @param {import("../modules/sequencer-file.js").SequencerFile} file
       * @param {{ antialiasing: PIXI.SCALE_MODES; tiling?: boolean; xray?: boolean; isPersisted: boolean }} options
       */
      constructor(file, options) {
        super();
        this.#sharedSpriteConfig = options ?? {
          antialiasing: PIXI.SCALE_MODES.LINEAR,
          tiling: false,
          xray: false,
          isPersisted: false
        };
        this.#file = file;
      }
      //#region public api
      get activePath() {
        return this.#activeAssetPath;
      }
      get activeAsset() {
        return this.#relatedAssets.get(this.activePath);
      }
      get texture() {
        return this.managedSprite?.texture;
      }
      get managedSprite() {
        return this.#managedSprite;
      }
      /** @return {PreciseText | null} */
      get textSprite() {
        return this.#textSprite;
      }
      /**
       * caches current scaling values to be used for spritesheet scaling if needed
       */
      updateDefaultScaling() {
        this.#defaultScaling = {
          scaleX: this.scale.x,
          scaleY: this.scale.y,
          width: this.width,
          height: this.height
        };
      }
      updateVideoTextures() {
        this.#managedSprite?.texture?.update();
      }
      /**
       * @param {string | undefined} filePath
       */
      async activate(filePath) {
        if (!filePath || this.#activeAssetPath === filePath) {
          return;
        }
        let nextAsset = this.#relatedAssets.get(filePath);
        if (this.#relatedAssets.has(filePath) && nextAsset == null) {
          return;
        }
        if (!nextAsset) {
          this.#relatedAssets[filePath] = void 0;
          nextAsset = await this.#loadAsset(filePath);
          if (this.destroyed) {
            return;
          }
          this.#relatedAssets.set(filePath, nextAsset);
        }
        if (this.#activateAsset(nextAsset)) {
          this.#activeAssetPath = filePath;
        }
        if (this.#sharedSpriteConfig.isPersisted && !this.#sharedSpriteConfig.tiling) {
          requestAnimationFrame(async () => {
            const minimumScale = this.#calculateMinimumSpritesheetScale();
            const spritesheet = await SequencerFileCache.requestCompiledSpritesheet(filePath, {
              minimumScale
            });
            if (!spritesheet) {
              return;
            }
            const previousAsset = this.activeAsset;
            const asset = new VideoSpritesheetAsset({ filepath: filePath, spritesheet });
            this.#relatedAssets.set(filePath, asset);
            if (this.destroyed || this.#activeAssetPath !== filePath) {
              return;
            }
            this.#activateAsset(asset);
            previousAsset?.destroy();
          });
        }
      }
      /**
       * @param {{ text: string | null; textStyle: Partial<PIXI.ITextStyle> | PIXI.TextStyle }} textData
       */
      addText(textData) {
        if (this.#textSprite) {
          this.#textSprite.text = textData.text ?? "";
          return this.#textSprite;
        }
        const textSprite = new PreciseText(textData.text ?? "", textData.textStyle);
        textSprite.resolution = 5;
        textSprite.zIndex = 1;
        textSprite.anchor.set(0.5, 0.5);
        this.#textSprite = textSprite;
        return this.addChild(textSprite);
      }
      removeText() {
        this.#textSprite?.destroy();
        this.#textSprite = null;
      }
      /**
       * @param {string | number} filepath
       */
      removeSprite(filepath) {
        this.#relatedAssets[filepath]?.destroy();
        delete this.#relatedAssets[filepath];
      }
      async preloadVariants() {
        if (!this.#preloadingPromise) {
          return this.#preloadingPromise = this.#preloadVariants();
        }
        return this.#preloadingPromise;
      }
      destroy() {
        this.#playbackControls?.destroy();
        for (const asset of this.#relatedAssets.values()) {
          asset?.destroy();
        }
        this.#relatedAssets.clear();
        this.managedSprite?.removeFromParent();
        this.managedSprite?.destroy();
        super.destroy({ children: true });
      }
      //#endregion
      //#region Managed Sprite proxies
      async play() {
        this.#playbackControls?.play();
      }
      stop() {
        this.#playbackControls?.stop();
      }
      get tileScale() {
        return this.managedSprite?.tileScale;
      }
      set tileScale(point) {
        if (point) {
          this.managedSprite?.tileScale.copyFrom(point);
        }
      }
      get tilePosition() {
        return this.managedSprite?.tilePosition;
      }
      set tilePosition(point) {
        if (point) {
          this.managedSprite?.tilePosition.copyFrom(point);
        }
      }
      get anchor() {
        return this.managedSprite?.anchor;
      }
      set anchor(point) {
        if (point) {
          this.managedSprite?.anchor.copyFrom(point);
        }
      }
      get tint() {
        return this.managedSprite?.tint ?? 16777215;
      }
      set tint(value) {
        if (this.managedSprite) {
          this.managedSprite.tint = typeof value === "number" ? Math.floor(value) : value;
        }
      }
      get scale() {
        return this.managedSprite?.scale || super.scale;
      }
      set scale(point) {
        this.managedSprite?.scale.copyFrom(point);
      }
      get width() {
        return this.managedSprite?.width ?? 0;
      }
      set width(value) {
        if (!this.managedSprite) {
          return;
        }
        this.managedSprite.width = value;
      }
      get height() {
        return this.managedSprite?.height ?? 0;
      }
      set height(value) {
        if (!this.managedSprite) {
          return;
        }
        this.managedSprite.height = value;
      }
      get resolution() {
        return this.#textSprite?.resolution;
      }
      set resolution(value) {
        if (!this.#textSprite) return;
        this.#textSprite.resolution = value ?? 5;
      }
      /** @type {} */
      get hasAnimatedMedia() {
        return this.activeAsset instanceof VideoAsset || this.activeAsset instanceof VideoSpritesheetAsset || this.activeAsset instanceof FlipbookAsset;
      }
      get playing() {
        return this.#playbackControls?.isPlaying ?? false;
      }
      get duration() {
        return this.#playbackControls?.duration ?? 0;
      }
      get volume() {
        return this.#playbackControls?.volume ?? 0;
      }
      set volume(value) {
        if (!this.#playbackControls) return;
        this.#playbackControls.volume = value;
      }
      get loop() {
        return this.#playbackControls?.loop ?? false;
      }
      set loop(value) {
        if (!this.#playbackControls) return;
        this.#playbackControls.loop = value;
      }
      get currentTime() {
        return this.#playbackControls?.currentTime ?? 0;
      }
      set currentTime(value) {
        if (!this.#playbackControls) return;
        this.#playbackControls.currentTime = value;
      }
      get playbackRate() {
        return this.#playbackControls?.playbackRate ?? 1;
      }
      set playbackRate(value) {
        if (!this.#playbackControls) return;
        this.#playbackControls.playbackRate = value;
      }
      /** @return {PIXI.ColorMatrixFilter | null} */
      get colorMatrixFilter() {
        return this.managedSprite?.colorMatrixFilter ?? null;
      }
      set colorMatrixFilter(value) {
        if (!this.managedSprite) return;
        this.managedSprite.colorMatrixFilter = value;
        if (this.#textSprite) {
          if (value) {
            this.#textSprite.filters?.push(value);
          } else if (this.#textSprite.filters) {
            this.#textSprite.filters = this.#textSprite.filters.filter(
              (filter2) => !(filter2 instanceof PIXI.ColorMatrixFilter)
            );
          }
        }
      }
      async #preloadVariants() {
        if (this.#activeAssetPath === "TEXT") {
          return;
        }
        if (!this.#file || this.#file.isFlipbook) {
          return;
        }
        const allFiles = this.#file.getAllFiles();
        for (const filePath of allFiles) {
          if (this.#relatedAssets[filePath]) {
            continue;
          }
          const asset = await this.#loadAsset(filePath);
          this.#relatedAssets.set(filePath, asset);
        }
      }
      /**
       * @param {string} filepath
       */
      async #loadAsset(filepath) {
        if (this.#file && this.#file.isFlipbook) {
          return this.#loadFlipbook(this.#file.getAllFiles(), this.#file.originalMetadata);
        }
        const texture = await SequencerFileCache.loadFile(filepath);
        if (texture?.baseTexture?.resource instanceof PIXI.CompressedTextureResource && texture?.baseTexture?.resource.levels === 1) {
          texture?.baseTexture?.setStyle(0, 0);
        } else if (this.#sharedSpriteConfig.antialiasing && this.#sharedSpriteConfig.antialiasing !== PIXI.SCALE_MODES.LINEAR) {
          texture?.baseTexture.setStyle(0, this.#sharedSpriteConfig.antialiasing);
        }
        if (texture instanceof PIXI.Spritesheet) {
          return new VideoSpritesheetAsset({ filepath, spritesheet: texture });
        }
        if (texture.baseTexture?.resource?.source instanceof HTMLVideoElement) {
          return new VideoAsset({ filepath, texture, video: texture.baseTexture.resource.source });
        }
        return new TextureAsset({ filepath, texture });
      }
      /**
       * @param {string[]} filepaths
       * @param {{ fps: number; }} metadata
       */
      async #loadFlipbook(filepaths, metadata) {
        const textures = (await Promise.all(filepaths.map(async (filepath) => loadTexture(filepath)))).filter(
          (t) => t instanceof PIXI.Texture
        );
        return new FlipbookAsset({ filepaths, textures, framerate: metadata?.fps ?? 24 });
      }
      /**
       * @param {VideoAsset | VideoSpritesheetAsset | TextureAsset | FlipbookAsset} nextAsset
       * @returns
       */
      #activateAsset(nextAsset) {
        let view;
        let controls;
        const tiling = this.#sharedSpriteConfig.tiling;
        const isVisionMaskingEnabled = !this.#sharedSpriteConfig.xray;
        if (nextAsset instanceof VideoAsset) {
          view = new TilingSpriteMesh(nextAsset.texture, { isVisionMaskingEnabled, tiling });
          controls = new VideoPlaybackControls(nextAsset.video, nextAsset.texture);
        } else if (nextAsset instanceof VideoSpritesheetAsset || nextAsset instanceof FlipbookAsset) {
          view = new AnimatedSpriteMesh(nextAsset.frameObjects, {
            autoUpdate: true,
            isVisionMaskingEnabled,
            tiling: !!tiling
          });
          controls = new SpritePlaybackControls(view, nextAsset.framerate, nextAsset.frameObjects.length);
        } else if (nextAsset instanceof TextureAsset) {
          view = new TilingSpriteMesh(nextAsset.texture, { isVisionMaskingEnabled, tiling });
          controls = null;
        } else {
          return false;
        }
        this.#applyPreviousValues(view, controls);
        if (this.#playbackControls) {
          this.#playbackControls.destroy();
        } else {
          controls?.play();
        }
        this.#playbackControls = controls;
        if (this.managedSprite) {
          this.managedSprite.removeFromParent();
          this.managedSprite.destroy();
        }
        this.#managedSprite = view;
        this.addChildAt(view, 0);
        return true;
      }
      /**
       *
       * @param {TilingSpriteMesh} view
       * @param {VideoPlaybackControls | SpritePlaybackControls | null} controls
       */
      #applyPreviousValues(view, controls) {
        if (this.managedSprite) {
          const prev = this.managedSprite;
          view.tint = prev.tint;
          view.anchor.copyFrom(prev.anchor);
          view.scale.copyFrom(prev.scale);
          view.width = prev.width;
          view.height = prev.height;
          view.tileScale.copyFrom(prev.tileScale);
          view.tilePosition.copyFrom(prev.tilePosition);
          view.pivot.copyFrom(prev.pivot);
          view.skew.copyFrom(prev.skew);
          view.alpha = prev.alpha;
          view.colorMatrixFilter = prev.colorMatrixFilter;
          view.position.copyFrom(prev.position);
          view.alphaMode = prev.alphaMode;
          view.blendMode = prev.blendMode;
          view.cullable = prev.cullable;
          view.renderable = prev.renderable;
          view.visible = prev.visible;
          view.tiling = prev.tiling;
        }
        if (controls && this.#playbackControls) {
          const prev = this.#playbackControls;
          controls.volume = prev.volume;
          controls.loop = prev.loop;
          controls.playbackRate = prev.playbackRate;
          controls.currentTime = prev.currentTime;
          if (prev.isPlaying) {
            controls.play();
          } else {
            controls.stop();
          }
        }
      }
      /**
       * calculates the minimum scale required for generated spritesheets
       * @returns {number}
       */
      #calculateMinimumSpritesheetScale() {
        const defaultScale = this.#defaultScaling;
        const maxScale = Math.max(defaultScale.scaleX || 1, defaultScale.scaleY || 1);
        return Math.min(maxScale, 1);
      }
    }
    class CrosshairsPlaceable extends MeasuredTemplate {
      constructor(...args) {
        super(...args);
        this.tag = "sequencer-crosshair-" + foundry.utils.randomID();
        this.cachedLocation = false;
      }
      #handlers = {
        mouseup: null,
        move: null,
        wheel: null
      };
      #promise = {
        resolve: null
      };
      #isDrag = false;
      #isPanning = false;
      #customText = false;
      #distanceText = false;
      #rangeHighlight = false;
      isValid = true;
      #lastPositions = false;
      get crosshair() {
        return this.document.crosshair;
      }
      get callbacks() {
        return this.document.callbacks;
      }
      get range() {
        const objLocation = this.crosshair.location.obj?.center ?? this.crosshair.location.obj;
        return canvas.grid.measurePath([objLocation, this.position]).distance;
      }
      updateCrosshair(data) {
        this.document.crosshair = foundry.utils.mergeObject(this.document.crosshair, data);
        if (this.#customText) {
          this.#customText.destroy();
          this.#customText = null;
        }
        if (this.#distanceText) {
          this.#distanceText.destroy();
          this.#distanceText = null;
        }
        if (this.#rangeHighlight) {
          this.#rangeHighlight.destroy();
          this.#rangeHighlight = null;
        }
        return this.draw();
      }
      async #refreshIcon() {
        this.controlIcon.renderable = !!this.crosshair.icon.texture;
        if (this.crosshair.icon.texture) {
          this.controlIcon.iconSrc = this.crosshair.icon.texture;
          this.controlIcon.texture = await loadTexture(this.controlIcon.iconSrc);
          this.controlIcon.icon.texture = this.controlIcon.texture;
        }
        if (!this.crosshair.icon.borderVisible) {
          this.controlIcon.bg.clear();
        }
      }
      _refreshRulerText() {
        const shapeWidth = this.shape.width ?? 0;
        const shapeHeight = this.shape.height ?? 0;
        if (this.crosshair.location.showRange && this.crosshair.location.obj) {
          if (!this.#distanceText && this.crosshair.location.obj) {
            const style = CONFIG.canvasTextStyle.clone();
            style.align = "center";
            this.#distanceText = this.template.addChild(new PreciseText("", style));
          }
          const actualHeight = (this.shapeHeight || this.shape.radius) + canvas.grid.size / 2;
          this.#distanceText.anchor.set(0.5, 0.5);
          this.#distanceText.position.set(shapeWidth / 2, actualHeight);
          const { units } = this.document.parent.grid;
          this.#distanceText.text = this.range.toString() + " " + units;
        }
        this.ruler.renderable = !!this.crosshair.label?.text;
        if (!this.ruler.renderable) return;
        if (this.crosshair.label?.text) {
          if (!this.#customText) {
            const style = CONFIG.canvasTextStyle.clone();
            style.align = "center";
            this.#customText = this.template.addChild(new PreciseText("", style));
          }
          if (this.#customText.text !== this.crosshair.label.text) this.#customText.text = this.crosshair.label.text;
          this.#customText.anchor.set(0.5);
          this.#customText.position.set(
            shapeWidth / 2 + this.crosshair.label.dx,
            shapeHeight / 2 + this.crosshair.label.dy
          );
        } else {
          if (this.#customText) {
            this.#customText.text = "";
            this.#customText.destroy();
            this.#customText = null;
          }
          return super._refreshRulerText();
        }
      }
      _refreshTemplate() {
        const t = this.template.clear();
        t.lineStyle(this._borderThickness, this.document.borderColor, 0.75).beginFill(0, 0);
        if (this.texture) t.beginTextureFill({ texture: this.texture });
        else t.beginFill(0, 0);
        t.drawShape(this.shape);
        if (!this.crosshair.lockDrag) {
          t.lineStyle(this._borderThickness, 0).beginFill(0, 0.5).drawCircle(0, 0, 6).drawCircle(this.ray.dx, this.ray.dy, 6).endFill();
        }
      }
      #refreshRangeHighlight() {
        const loc = this.crosshair.location;
        if (!loc.displayRangePoly || !loc.obj || loc.limitMaxRange === null) return;
        if (this.#rangeHighlight) {
          this.#rangeHighlight.clear();
        } else {
          this.#rangeHighlight = new PIXI.Graphics();
          canvas.tokens.addChild(this.#rangeHighlight);
          this.#rangeHighlight.zIndex = -1;
          this.#rangeHighlight.interactive = false;
          canvas.tokens.sortChildren();
        }
        const object = loc.obj;
        const position = {
          x: object?.center?.x ?? object?.position?.x ?? object?.x,
          y: object?.center?.y ?? object?.position?.y ?? object?.y
        };
        const { w, h } = object;
        const distance = loc.limitMaxRange + Math.max(w, h) / 2 / canvas.dimensions.distancePixels;
        this.#rangeHighlight.beginFill(loc.rangePolyFillColor ?? 16777215, clamp$1(loc.rangePolyFillAlpha ?? 0.25, 0, 1)).lineStyle(2, loc.rangePolyLineColor ?? 16711680, clamp$1(loc.rangePolyLineAlpha ?? 0.5, 0, 1)).drawPolygon(canvas.grid.getCircle(position, distance));
      }
      async show() {
        await this.draw();
        this.layer.addChild(this);
        this.oldInteractiveChildren = this.layer.interactiveChildren;
        this.layer.interactiveChildren = false;
        this.#updateLocation();
        this.#refreshRangeHighlight();
        this.#runCallback(CONSTANTS.CALLBACKS.SHOW, this);
        return this.#activateShowListeners();
      }
      async #activateShowListeners() {
        return new Promise((resolve) => {
          this.#promise.resolve = resolve;
          this.#handlers.move = this.#onMove.bind(this);
          this.#handlers.mouseup = this.#onMouseUp.bind(this);
          this.#handlers.wheel = this.#onWheel.bind(this);
          canvas.stage.on("mousemove", this.#handlers.move);
          canvas.stage.on("pointerup", this.#handlers.mouseup);
          canvas.app.view.onwheel = this.#handlers.wheel;
        });
      }
      #getSnappedPoint(point, mode = this.crosshair.snap.position, resolution = this.crosshair.snap.resolution) {
        resolution = !resolution || resolution <= 0 ? canvas.grid.size : resolution;
        return canvas.grid.getSnappedPoint(point, { mode, resolution });
      }
      #onMove(evt) {
        const now2 = Date.now();
        const leftDown = (evt.buttons & 1) > 0;
        const rightDown = (evt.buttons & 2) > 0;
        this.#isDrag = !!(leftDown && canvas.mouseInteractionManager.isDragging);
        this.#isPanning = !!(rightDown && canvas.mouseInteractionManager.isDragging);
        if (this.#isPanning) return;
        if (this.#isDrag) {
          canvas.mouseInteractionManager.cancel(evt);
        }
        evt.preventDefault();
        if (now2 - this.moveTime <= 20) return;
        const moved = this.#updateLocation();
        this.#runCallback(CONSTANTS.CALLBACKS.MOUSE_MOVE, this);
        if (moved) this.#runCallback(CONSTANTS.CALLBACKS.MOVE, this);
        this.#updateLineOfSight();
        this.refresh();
        this.moveTime = now2;
      }
      #updateLineOfSight() {
        if (!this.crosshair.location?.obj || this.crosshair.location?.wallBehavior === CONSTANTS.PLACEMENT_RESTRICTIONS.ANYWHERE) return;
        const object = this.crosshair.location.obj;
        const objLocation = {
          x: object?.center?.x ?? object?.position?.x ?? object?.x,
          y: object?.center?.y ?? object?.position?.y ?? object?.y
        };
        const location2 = { x: this.document.x, y: this.document.y };
        const exitEarly = this.#lastPositions && (this.#lastPositions.obj.x === objLocation.x && this.#lastPositions.obj.y === objLocation.y && this.#lastPositions.tgt.x === location2.x && this.#lastPositions.tgt.y === location2.y);
        this.#lastPositions = {
          obj: objLocation,
          tgt: location2
        };
        if (exitEarly) return;
        const type = this.crosshair.location?.wallBehavior === CONSTANTS.PLACEMENT_RESTRICTIONS.LINE_OF_SIGHT ? "sight" : "move";
        const collisions = CONFIG.Canvas.polygonBackends.sight.testCollision(objLocation, location2, {
          type,
          useThreshold: true
        });
        const wasValid = this.isValid;
        this.isValid = !collisions.length;
        if (!this.isValid && wasValid) {
          this.#runCallback(CONSTANTS.CALLBACKS.COLLIDE, this, collisions);
        } else if (this.isValid && !wasValid) {
          this.#runCallback(CONSTANTS.CALLBACKS.STOP_COLLIDING, this);
        }
      }
      #handleLockedEdge(mouseLocation) {
        let snappedMouseLocation = this.#getSnappedPoint(mouseLocation, CONST.GRID_SNAPPING_MODES.CENTER);
        const { lockToEdgeDirection } = this.crosshair.location;
        const placeable = this.crosshair.location.obj;
        const shape = placeable.getShape();
        const { w, h } = this.crosshair.location.obj;
        const shapePoints = shape?.points ?? [0, 0, w, 0, w, h, 0, h];
        const points = shapePoints.map((point, i) => {
          const worldCoord = i % 2 === 0 ? placeable.document.x : placeable.document.y;
          return point + worldCoord;
        });
        const centerPoint = placeable.getCenterPoint();
        const ray = new Ray(centerPoint, mouseLocation);
        let intersection;
        for (let i = 0; i < points.length; i += 2) {
          intersection = ray.intersectSegment([points[i], points[i + 1], points[i + 2 >= points.length ? 0 : i + 2], points[i + 3 >= points.length ? 1 : i + 3]]);
          if (intersection) break;
        }
        if (!intersection) return {};
        let snappedIntersection = this.#getSnappedPoint(intersection, CONST.GRID_SNAPPING_MODES.EDGE_MIDPOINT);
        if (canvas.scene.grid.type === CONST.GRID_TYPES.SQUARE) {
          const size = this.document.parent.grid.size;
          const left = snappedMouseLocation.x < points[0];
          const above = snappedMouseLocation.y < points[1];
          const right = snappedMouseLocation.x > points[2];
          const below = snappedMouseLocation.y > points[5];
          if ((left || right) && (below || above)) {
            snappedIntersection.x = left ? points[0] - size : right ? points[2] + size : snappedIntersection.x;
            snappedIntersection.y = above ? points[1] - size : right ? points[5] + size : snappedIntersection.y;
            if (above && left) {
              snappedIntersection.x = points[0];
              snappedIntersection.y = points[1];
            } else if (above && right) {
              snappedIntersection.x = points[2];
              snappedIntersection.y = points[3];
            } else if (below && right) {
              snappedIntersection.x = points[4];
              snappedIntersection.y = points[5];
            } else if (below && left) {
              snappedIntersection.x = points[6];
              snappedIntersection.y = points[7];
            }
          }
        }
        if (lockToEdgeDirection) {
          const lockedRay = Ray.towardsPoint(snappedIntersection, snappedMouseLocation, 2);
          const snappedDirection = this.#getSnappedPoint(lockedRay.B, CONST.GRID_SNAPPING_MODES.CENTER);
          snappedMouseLocation.x = snappedDirection.x;
          snappedMouseLocation.y = snappedDirection.y;
        }
        const { direction, distance } = this.#getDraggedMatrix(snappedIntersection, snappedMouseLocation);
        return {
          ...snappedIntersection,
          direction,
          distance
        };
      }
      #handleLimit(mouseLocation, targetLocation) {
        const ray = new Ray(targetLocation, mouseLocation);
        const gridPath = canvas.grid.measurePath([targetLocation, mouseLocation]);
        const limitMinRange = is_real_number(this.crosshair.location.limitMinRange) ? this.crosshair.location.limitMinRange : 0;
        const limitMaxRange = is_real_number(this.crosshair.location.limitMaxRange) ? this.crosshair.location.limitMaxRange : Infinity;
        let finalLocation = mouseLocation;
        if (gridPath.cost < limitMinRange) {
          finalLocation = canvas.grid.getTranslatedPoint(targetLocation, Math.toDegrees(ray.angle), limitMinRange);
        } else if (gridPath.cost > limitMaxRange) {
          finalLocation = canvas.grid.getTranslatedPoint(targetLocation, Math.toDegrees(ray.angle), limitMaxRange);
        }
        const snappedPosition = this.#getSnappedPoint(finalLocation);
        const { direction, distance } = this.#getDraggedMatrix(targetLocation, snappedPosition);
        return {
          ...snappedPosition,
          direction,
          distance
        };
      }
      #updateLocation() {
        let mouseLocation = get_mouse_position();
        mouseLocation.x += this.crosshair.location.offset.x ?? 0;
        mouseLocation.y += this.crosshair.location.offset.y ?? 0;
        let update2;
        if (this.crosshair.location.obj && (this.crosshair.location.lockToEdge || this.crosshair.location.limitMinRange || this.crosshair.location.limitMaxRange)) {
          const location2 = this.crosshair.location.obj;
          const targetLocation = {
            x: location2?.center?.x ?? location2?.position?.x ?? location2?.x,
            y: location2?.center?.y ?? location2?.position?.y ?? location2?.y
          };
          if (this.crosshair.location.lockToEdge) {
            update2 = this.#handleLockedEdge(mouseLocation);
          } else if (this.crosshair.location.limitMinRange || this.crosshair.location.limitMaxRange) {
            update2 = this.#handleLimit(mouseLocation, targetLocation);
          }
        } else if (this.#isDrag) {
          const { direction, distance } = this.#getDraggedMatrix(this.document, mouseLocation);
          update2 = { distance, direction };
        } else {
          const snappedPosition = this.#getSnappedPoint(mouseLocation);
          update2 = {
            x: snappedPosition.x,
            y: snappedPosition.y
          };
        }
        const isChanged = update2?.x !== void 0 && this.document.x !== update2.x || update2?.y !== void 0 && this.document.y !== update2.y || update2?.direction !== void 0 && this.document.direction !== update2.direction || update2?.distance !== void 0 && this.document.distance !== update2.distance;
        this.document.updateSource(update2);
        return isChanged;
      }
      #getDistance(dragDistance) {
        if (this.crosshair.distanceMin === null && this.crosshair.distanceMax === null) {
          return this.document.distance;
        }
        const min = this.crosshair.distanceMin ?? this.document.originalConfig.distance;
        const max = this.crosshair.distanceMax ?? this.document.originalConfig.distance;
        return Math.min(Math.max(0.5, dragDistance, min), max);
      }
      #getDraggedMatrix(source, target) {
        const dragAngle = new Ray(source, target).angle;
        const dragDistance = canvas.grid.measurePath([source, target]);
        const direction = this.crosshair.snap.direction ? Math.round(Math.toDegrees(dragAngle) / this.crosshair.snap.direction) * this.crosshair.snap.direction : Math.toDegrees(dragAngle);
        return {
          direction: direction || 0,
          distance: this.#getDistance(dragDistance.distance)
        };
      }
      #onMouseUp(evt) {
        const event = evt?.nativeEvent ?? evt;
        if (!(event.which === 1 || event.which === 3)) return;
        if (this.#isDrag) {
          this.#isDrag = false;
          return;
        }
        if (this.#isPanning) {
          this.#isPanning = false;
          return;
        }
        return event.which === 1 ? this.#onConfirm() : this.#onCancel();
      }
      #onConfirm() {
        const position = this.document.getOrientation();
        if (!this.isValid) {
          this.#runCallback(CONSTANTS.CALLBACKS.INVALID_PLACEMENT, position);
          return;
        }
        const placedCallback = this.#runCallback(CONSTANTS.CALLBACKS.PLACED, position);
        if (placedCallback === false) {
          return;
        }
        this.cachedLocation = position;
        this.#promise.resolve(this.document);
        this.destroy();
      }
      #onCancel() {
        this.#runCallback(CONSTANTS.CALLBACKS.CANCEL);
        this.#promise.resolve(false);
        this.destroy();
      }
      #runCallback(name2, ...params) {
        if (!this.callbacks[name2]) return;
        return this.callbacks[name2](...params);
      }
      #clearHandlers() {
        this.layer.interactiveChildren = this.oldInteractiveChildren;
        canvas.stage.off("mousemove", this.#handlers.move);
        canvas.stage.off("pointerup", this.#handlers.mouseup);
        canvas.app.view.onwheel = null;
      }
      #onWheel(evt) {
        if (!evt.altKey && !evt.ctrlKey && !evt.shiftKey) return;
        evt.stopPropagation();
        if (evt.shiftKey) this.#updateDirection(evt);
        if (evt.altKey) this.#updateDistance(evt);
        if (evt.ctrlKey) ;
        this.refresh();
      }
      #updateDistance(evt) {
        const step = this.document.parent.grid.distance / 2;
        const delta = step * Math.sign(-evt.deltaY);
        let distance = this.document.distance + delta;
        distance = Math.max(0.5, distance.toNearest(step));
        distance = this.#getDistance(distance);
        this.document.updateSource({ distance });
      }
      #updateDirection(evt) {
        if (this.crosshair.lockManualRotation) return;
        const scrollDelta = Math.sign(evt.deltaY);
        let delta = this.crosshair.snap.direction ? this.crosshair.snap.direction * scrollDelta : scrollDelta * 5;
        if (delta < 0) delta += 360;
        if (delta > 360) delta -= 360;
        const direction = Math.max(1, this.document.direction + delta);
        this.document.updateSource({ direction });
      }
      /** @override */
      _getGridHighlightPositions() {
        if (!this.crosshair.gridHighlight) return [];
        return super._getGridHighlightPositions();
      }
      /** @override */
      _destroy(options = {}) {
        super._destroy(options);
        if (this.#rangeHighlight) this.#rangeHighlight.destroy();
        this.#clearHandlers();
      }
      /** @override */
      async draw() {
        await super.draw();
        this.#refreshIcon();
        return this;
      }
    }
    const hooksManager = {
      _hooks: /* @__PURE__ */ new Map(),
      _hooksRegistered: /* @__PURE__ */ new Set(),
      addHook(effectUuid, hookName, callable, callNow = false) {
        if (!this._hooksRegistered.has(hookName)) {
          debug("registering hook for: " + hookName);
          this._hooksRegistered.add(hookName);
          Hooks.on(hookName, (...args) => {
            this._hookCalled(hookName, ...args);
          });
        }
        const key = hookName + "-" + effectUuid;
        if (!this._hooks.has(key)) {
          this._hooks.set(key, []);
        }
        this._hooks.get(key).push(callable);
        if (callNow) {
          setTimeout(() => {
            callable();
          }, 20);
        }
      },
      _hookCalled(hookName, ...args) {
        Array.from(this._hooks).filter((entry) => entry[0].startsWith(hookName + "-")).map((hooks) => hooks[1]).deepFlatten().forEach((callback) => callback(...args));
      },
      removeHooks(effectUuid) {
        Array.from(this._hooks).filter((entry) => entry[0].endsWith("-" + effectUuid)).forEach((entry) => this._hooks.delete(entry[0]));
      }
    };
    const SyncGroups = {
      times: /* @__PURE__ */ new Map(),
      effectIds: /* @__PURE__ */ new Map(),
      get(effect) {
        const fullName = effect.data.sceneId + "-" + effect.data.syncGroup;
        const effectIds = new Set(this.effectIds.get(fullName));
        if (effectIds && !effectIds.has(effect.id)) {
          effectIds.add(effect.id);
          this.effectIds.set(fullName, Array.from(effectIds));
        }
        return this.times.get(fullName);
      },
      set(effect) {
        const fullName = effect.data.sceneId + "-" + effect.data.syncGroup;
        this.times.set(fullName, effect.data.creationTimestamp);
        this.effectIds.set(fullName, [effect.id]);
      },
      remove(effect) {
        const fullName = effect.data.sceneId + "-" + effect.data.syncGroup;
        const effectIds = new Set(this.effectIds.get(fullName));
        effectIds.delete(effect.id);
        if (effectIds.size) {
          this.effectIds.set(fullName, Array.from(effectIds));
        } else {
          this.effectIds.delete(fullName);
          this.times.delete(fullName);
        }
      }
    };
    class CanvasEffect extends PIXI.Container {
      #elevation = 0;
      #sort = 0;
      #sortLayer = 800;
      constructor(inData) {
        super();
        this.sortableChildren = true;
        this.interactiveChildren = false;
        this.actualCreationTime = +/* @__PURE__ */ new Date();
        this.data = inData;
        this._resolve = null;
        this._durationResolve = null;
        this.ready = false;
        this._ended = false;
        this._isEnding = false;
        this._cachedSourceData = {};
        this._cachedTargetData = {};
        this.sourceOffset = { x: 0, y: 0 };
        this.targetOffset = { x: 0, y: 0 };
        this.uuid = false;
      }
      static get protectedValues() {
        return [
          "_id",
          "sequenceId",
          "creationTimestamp",
          "creatorUserId",
          "moduleName",
          "index",
          "repetition",
          "moves",
          "fadeIn",
          "fadeOut",
          "scaleIn",
          "scaleOut",
          "rotateIn",
          "rotateOut",
          "fadeInAudio",
          "fadeOutAudio",
          "animations",
          "nameOffsetMap",
          "persist"
        ];
      }
      /** @type {number} */
      get elevation() {
        return this.#elevation;
      }
      set elevation(value) {
        this.#elevation = value;
      }
      /** @type {number} */
      get sort() {
        return this.#sort;
      }
      set sort(value) {
        this.#sort = value;
      }
      /** @type {number} */
      get sortLayer() {
        return this.#sortLayer;
      }
      set sortLayer(value) {
        this.#sortLayer = value;
      }
      get context() {
        return this.data.attachTo?.active && this.sourceDocument ? this.sourceDocument : game.scenes.get(this.data.sceneId);
      }
      get isIsometricActive() {
        const sceneIsIsometric = foundry.utils.getProperty(
          game.scenes.get(this.data.sceneId),
          CONSTANTS.INTEGRATIONS.ISOMETRIC.SCENE_ENABLED
        );
        return CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE && sceneIsIsometric;
      }
      get creationTimestamp() {
        if (this.data.syncGroup) {
          const time2 = SyncGroups.get(this);
          if (time2) return time2;
          SyncGroups.set(this);
        }
        return this.data.creationTimestamp;
      }
      /**
       * The ID of the effect
       *
       * @returns {string}
       */
      get id() {
        return this.data._id;
      }
      /**
       * Whether this effect is destroyed or is in the process of being destroyed
       */
      get isDestroyed() {
        return this.destroyed || this.source && this.isSourceDestroyed || this.target && this.isTargetDestroyed;
      }
      /**
       * Whether the source of this effect is temporary
       *
       * @returns {boolean}
       */
      get isSourceTemporary() {
        return this.data.attachTo?.active && this.sourceDocument && !is_UUID(this.sourceDocument?.uuid);
      }
      /**
       * Whether the source of this effect has been destroyed
       *
       * @returns {boolean}
       */
      get isSourceDestroyed() {
        return this.source && this.source?.destroyed && (!this.sourceDocument?.object || this.sourceDocument?.object?.destroyed || this.source.constructor.name === "Crosshairs");
      }
      /**
       * Whether the target of this effect is temporary
       *
       * @returns {boolean}
       */
      get isTargetTemporary() {
        return (this.data.stretchTo?.attachTo || this.data.rotateTowards?.attachTo) && this.targetDocument && !is_UUID(this.targetDocument.uuid);
      }
      /**
       * Whether the target of this effect has been destroyed
       *
       * @returns {boolean}
       */
      get isTargetDestroyed() {
        return this.target && this.target?.destroyed && (!this.targetDocument?.object || this.targetDocument?.object?.destroyed || this.target.constructor.name === "Crosshairs");
      }
      /**
       * The source object (or source location) of the effect
       *
       * @returns {boolean|object}
       */
      get source() {
        if (!this._source && this.data.source) {
          const getDifferentTarget = this.data.source === this.data.target;
          this._source = this._getObjectByID(this.data.source?.uuid ?? this.data.source, getDifferentTarget, true) ?? this.data.source;
          this._source = this._source?._object ?? this._source;
        }
        return this._source;
      }
      /**
       * Retrieves the source document
       *
       * @returns {Document|PlaceableObject}
       */
      get sourceDocument() {
        return this.source?.document ?? this.source;
      }
      /**
       * Retrieves the PIXI object for the source object
       *
       * @returns {*|PIXI.Sprite|TileHUD<Application.Options>}
       */
      get sourceMesh() {
        return this.source?.mesh ?? this.source?.template;
      }
      /**
       * The source position with the relevant offsets calculated
       *
       * @returns {{x: number, y: number}}
       */
      get sourcePosition() {
        let position = this.getSourceData().position;
        let offset2 = this._getOffset(this.data.source, true);
        if (this.data.attachTo?.active && this.data.attachTo?.align && this.data.attachTo?.align !== "center") {
          const additionalOffset = align({
            context: this.source,
            spriteWidth: this.sprite.width,
            spriteHeight: this.sprite.height,
            align: this.data.attachTo?.align,
            edge: this.data.attachTo?.edge
          });
          offset2.x += additionalOffset.x;
          offset2.y += additionalOffset.y;
        }
        return {
          x: position.x - offset2.x + this.sourceOffset.x,
          y: position.y - offset2.y + this.sourceOffset.y
        };
      }
      /**
       * The target object (or target location) of the effect
       *
       * @returns {boolean|object}
       */
      get target() {
        if (!this._target && this.data.target) {
          const getDifferentTarget = this.data.source === this.data.target;
          this._target = this._getObjectByID(this.data.target?.uuid ?? this.data.target, getDifferentTarget, false) ?? this.data.target;
          this._target = this._target?._object ?? this._target;
        }
        return this._target;
      }
      /**
       * Retrieves the document of the target
       *
       * @returns {Document|PlaceableObject}
       */
      get targetDocument() {
        return this.target?.document ?? this.target;
      }
      /**
       * Retrieves the PIXI object for the target object
       *
       * @returns {*|PIXI.Sprite|TileHUD<Application.Options>}
       */
      get targetMesh() {
        return this.target?.mesh ?? this.target?.template;
      }
      /**
       * The target position with the relevant offsets calculated
       *
       * @returns {{x: number, y: number}}
       */
      get targetPosition() {
        const position = this.getTargetData().position;
        const offset2 = this._getOffset(this.data.target);
        return {
          x: position.x - offset2.x + this.targetOffset.x,
          y: position.y - offset2.y + this.targetOffset.y
        };
      }
      /**
       * Returns this effect's world position
       *
       * @returns {{x: number, y: number}}
       */
      get worldPosition() {
        const t = canvas.stage.worldTransform;
        return {
          x: (this.sprite.worldTransform.tx - t.tx) / canvas.stage.scale.x,
          y: (this.sprite.worldTransform.ty - t.ty) / canvas.stage.scale.y
        };
      }
      /**
       * Whether the current user is the owner of this effect
       *
       * @returns {boolean}
       */
      get owner() {
        return this.data.creatorUserId === game.user.id;
      }
      get loopDelay() {
        return this.data.loopOptions?.loopDelay ?? 0;
      }
      get loops() {
        return this.data.loopOptions?.loops ?? 0;
      }
      /**
       * Whether the current user can update this effect
       *
       * @returns {boolean}
       */
      get userCanUpdate() {
        return game.user.isGM || this.owner || this.data.attachTo?.active && this.sourceDocument.canUserModify(game.user, "update");
      }
      /**
       * Whether the current user can delete this effect
       *
       * @returns {boolean}
       */
      get userCanDelete() {
        return this.userCanUpdate || user_can_do("permissions-effect-delete");
      }
      /**
       * Whether this effect is on the current scene
       *
       * @returns {boolean}
       */
      get onCurrentScene() {
        return this.data.sceneId === game.user.viewedScene;
      }
      /**
       * Whether this effect should be shown as faded or not - effects created by users for other users should be shown
       * for all
       *
       * @returns {boolean}
       */
      get shouldShowFadedVersion() {
        return this.data.users && this.data.users.length && !(this.data.users.length === 1 && this.data.users.includes(this.data.creatorUserId)) && !this.data.users.includes(game.userId);
      }
      set effectAlpha(value) {
        if (this.sprite) {
          this.sprite.alpha = value;
        }
        if (this.shapes) {
          Object.values(this.shapes).forEach((shape) => {
            shape.alpha = value;
          });
        }
      }
      get effectAlpha() {
        return this.sprite?.alpha;
      }
      async playMedia() {
        if (this.destroyed || this._ended || this._isEnding) {
          return;
        }
        await this.sprite.play();
        this._setupTimestampHook(this.mediaCurrentTime * 1e3);
      }
      updateTexture() {
        this.sprite.updateVideoTextures();
      }
      async pauseMedia() {
        this.sprite.stop();
      }
      get mediaLooping() {
        return this.sprite.loop;
      }
      set mediaLooping(looping) {
        return this.sprite.loop = looping;
      }
      get mediaIsPlaying() {
        return this.sprite.playing;
      }
      get mediaCurrentTime() {
        return this.sprite.currentTime;
      }
      get mediaPlaybackRate() {
        return this.sprite.playbackRate;
      }
      set mediaPlaybackRate(inPlaybackRate) {
        this.sprite.playbackRate = inPlaybackRate;
      }
      set mediaCurrentTime(newTime) {
        this.sprite.currentTime = newTime;
      }
      get mediaDuration() {
        return this.sprite.duration;
      }
      get mediaDurationMs() {
        return this.mediaDuration * 1e3;
      }
      get hasAnimatedMedia() {
        return this.sprite.hasAnimatedMedia;
      }
      /**
       * The template of the effect, determining the effect's internal grid size, and start/end padding
       *
       * @returns {object}
       */
      get template() {
        return this._template;
      }
      /**
       * The grid size difference between the internal effect's grid vs the grid on the canvas. If the effect is in screen space, we ignore this.
       *
       * @returns {number}
       */
      get gridSizeDifference() {
        return canvas.grid.size / (this.template?.gridSize ?? this.defaultGridSize);
      }
      get defaultGridSize() {
        return 100;
      }
      /**
       * Whether the effect should be flipped on any given axis
       *
       * @returns {number}
       */
      get flipX() {
        const offsetMap = this._nameOffsetMap?.[this.data.source];
        let flip = this.data.flipX ? -1 : 1;
        if (offsetMap && offsetMap.mirrorX !== void 0) {
          flip *= offsetMap.mirrorX ? -1 : 1;
        }
        return flip;
      }
      get flipY() {
        const offsetMap = this._nameOffsetMap?.[this.data.source];
        let flip = this.data.flipY ? -1 : 1;
        if (offsetMap && offsetMap.mirrorY !== void 0) {
          flip *= offsetMap.mirrorY ? -1 : 1;
        }
        return flip;
      }
      /**
       * Whether this effect should play at all, depending on a multitude of factors
       *
       * @returns {boolean}
       */
      get shouldPlay() {
        return (game.user.viewedScene === this.data.sceneId || this.data.creatorUserId === game.userId) && (game.user.isGM || !this.data.users || this.data.users.length === 0 || this.data.users.includes(game.userId));
      }
      get shouldPlayVisible() {
        let playVisible = this.shouldPlay && game.settings.get("sequencer", "effectsEnabled") && game.user.viewedScene === this.data.sceneId;
        if (game.settings.get("core", "photosensitiveMode")) {
          playVisible = false;
          throttled_custom_warning(
            this.data.moduleName,
            "Photosensitive Mode is turned on, so Sequencer's visual effects aren't being rendered"
          );
        }
        return playVisible;
      }
      static make(inData) {
        return !inData.persist ? new CanvasEffect(inData) : new PersistentCanvasEffect(inData);
      }
      static checkValid(effectData) {
        if (effectData.delete) {
          return false;
        }
        let sourceExists = true;
        let targetExists = true;
        if (effectData.source && is_UUID(effectData.source)) {
          sourceExists = fromUuidSync(effectData.source);
        }
        if (effectData.target && is_UUID(effectData.target)) {
          targetExists = fromUuidSync(effectData.target);
        }
        for (let tiedDocumentUuid of effectData?.tiedDocuments ?? []) {
          if (tiedDocumentUuid && is_UUID(tiedDocumentUuid)) {
            let tiedDocumentExists = fromUuidSync(tiedDocumentUuid);
            if (!tiedDocumentExists) return false;
          }
        }
        if (effectData.source && is_UUID(effectData.source) && effectData.target && is_UUID(effectData.target)) {
          const sourceScene = effectData.source.split(".")[1];
          const targetScene = effectData.target.split(".")[1];
          if (sourceScene !== targetScene || sourceScene !== effectData.sceneId)
            return false;
        }
        return sourceExists && targetExists;
      }
      /**
       * Validates that the update contains the appropriate data
       *
       * @param inUpdates
       */
      static validateUpdate(inUpdates) {
        const updateKeys = Object.keys(inUpdates);
        const protectedValues = updateKeys.filter(
          (key) => CanvasEffect.protectedValues.includes(key)
        );
        if (protectedValues.length) {
          throw custom_error(
            "Sequencer",
            `CanvasEffect | update | You cannot update the following keys of an effect's data: ${protectedValues.join(
              "\n - "
            )}`
          );
        }
        if (updateKeys.includes("source")) {
          if (!(is_UUID(inUpdates.source) || is_object_canvas_data(inUpdates.source))) {
            throw custom_error(
              "Sequencer",
              `CanvasEffect | update | source must be of type document UUID or object with X and Y coordinates`
            );
          }
        }
        if (updateKeys.includes("target")) {
          if (!(is_UUID(inUpdates.target) || is_object_canvas_data(inUpdates.target))) {
            throw custom_error(
              "Sequencer",
              `CanvasEffect | update | target must be of type document UUID or object with X and Y coordinates`
            );
          }
        }
      }
      getHook(type, uuid) {
        if (!is_UUID(uuid)) return false;
        const parts = uuid.split(".");
        return type + parts[parts.length - 2];
      }
      /**
       * Gets the source hook name
       *
       * @param {string} type
       * @returns {string|boolean}
       */
      getSourceHook(type = "") {
        return this.getHook(type, this.data.source);
      }
      /**
       * The source object's current position, or its current position
       *
       * @returns {boolean|object}
       */
      getSourceData() {
        if (this.data.temporary && !this.owner) {
          return SequencerEffectManager.getPositionForUUID(this.data.source);
        }
        if (this.source instanceof PlaceableObject && this.isSourceDestroyed) {
          return {
            ...this._cachedSourceData
          };
        }
        let crosshairPos = this.source instanceof CrosshairsPlaceable ? this.sourceDocument.getOrientation() : false;
        crosshairPos = crosshairPos?.source;
        const position = this.source instanceof PlaceableObject && !this.isSourceTemporary ? get_object_position(this.source) : crosshairPos || this.source?.worldPosition || this.source?.center || this.source;
        const { width: width2, height } = crosshairPos || get_object_dimensions(this.source);
        if (this.isIsometricActive && this.source instanceof PlaceableObject) {
          position.x += (this.sourceDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size;
          position.y -= (this.sourceDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size;
          if (this.data.isometric?.overlay || this.target instanceof PlaceableObject) {
            position.x += (this.source?.height ?? height) / 2;
            position.y -= (this.source?.height ?? height) / 2;
          }
        }
        if (position !== void 0) {
          this._cachedSourceData.position = position;
        }
        if (width2 !== void 0 && height !== void 0) {
          this._cachedSourceData.width = width2;
          this._cachedSourceData.height = height;
        }
        let rotation2 = 0;
        if (this.source instanceof MeasuredTemplate && this.sourceDocument?.t !== "rect") {
          rotation2 = Math.normalizeRadians(
            Math.toRadians(this.sourceDocument?.direction)
          );
        } else if (!(this.source instanceof MeasuredTemplate)) {
          rotation2 = this.sourceDocument?.rotation ? Math.normalizeRadians(Math.toRadians(this.sourceDocument?.rotation)) : 0;
        }
        if (rotation2 !== void 0) {
          this._cachedSourceData.rotation = rotation2;
        }
        const alpha = this.sourceDocument instanceof TokenDocument || this.sourceDocument instanceof TileDocument ? this.sourceDocument?._source?.alpha ?? 1 : 1;
        if (alpha !== void 0) {
          this._cachedSourceData.alpha = alpha;
        }
        return {
          ...this._cachedSourceData
        };
      }
      /**
       * Gets the target hook name
       *
       * @param {string} type
       * @returns {string|boolean}
       */
      getTargetHook(type = "") {
        return this.getHook(type, this.data.target);
      }
      /**
       * The target object's current position, or its current position
       *
       * @returns {boolean|object}
       */
      getTargetData() {
        if (this.data.temporary && !this.owner) {
          return SequencerEffectManager.getPositionForUUID(this.data.target) ?? this.getSourceData();
        }
        if (this.target instanceof PlaceableObject && this.isTargetDestroyed) {
          return {
            ...this._cachedTargetData
          };
        }
        let crosshairPos = this.target instanceof CrosshairsPlaceable ? this.targetDocument.getOrientation() : false;
        crosshairPos = crosshairPos?.target ?? crosshairPos?.source;
        const position = this.target instanceof PlaceableObject && !this.isTargetTemporary && !this.isTargetDestroyed ? get_object_position(this.target, { measure: true }) : crosshairPos || this.target?.worldPosition || this.target?.center || this.target;
        const { width: width2, height } = crosshairPos || get_object_dimensions(this.target);
        if (this.isIsometricActive && this.target instanceof PlaceableObject) {
          const targetHeight = (this.target?.height ?? height) / 2;
          position.x += (this.targetDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size + targetHeight;
          position.y -= (this.targetDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size + targetHeight;
        }
        if (width2 !== void 0 && height !== void 0) {
          this._cachedTargetData.width = width2;
          this._cachedTargetData.height = height;
        }
        if (position !== void 0) {
          this._cachedTargetData.position = position;
        }
        let rotation2 = 0;
        if (this.target instanceof MeasuredTemplate && this.targetDocument?.t !== "rect") {
          rotation2 = Math.normalizeRadians(
            Math.toRadians(this.targetDocument?.direction)
          );
        } else if (!(this.target instanceof MeasuredTemplate)) {
          rotation2 = this.targetDocument?.rotation ? Math.normalizeRadians(Math.toRadians(this.targetDocument?.rotation)) : 0;
        }
        if (rotation2 !== void 0) {
          this._cachedTargetData.rotation = rotation2;
        }
        const alpha = this.targetDocument instanceof TokenDocument || this.targetDocument instanceof TileDocument ? this.targetDocument?.alpha ?? 1 : 1;
        if (alpha !== void 0) {
          this._cachedTargetData.alpha = alpha;
        }
        return {
          ...this._cachedTargetData
        };
      }
      /**
       * Calculates the offset for a given offset property and name mapping
       *
       * @param {string} offsetMapName
       * @param {boolean} source
       * @returns {{x: number, y: number}|*}
       * @private
       */
      _getOffset(offsetMapName, source = false) {
        const key = source ? "source" : "target";
        const offset2 = {
          x: 0,
          y: 0
        };
        let twister = this._twister;
        let nameOffsetMap = this._nameOffsetMap?.[this.data.name];
        if (nameOffsetMap) {
          twister = nameOffsetMap.twister;
        }
        if (this.data.missed && (!source || !this.data.target)) {
          let missedOffset = this._offsetCache[key]?.missedOffset || calculate_missed_position(this.source, this.target, twister);
          this._offsetCache[key].missedOffset = missedOffset;
          offset2.x -= missedOffset.x;
          offset2.y -= missedOffset.y;
        }
        const obj = source ? this.source : this.target;
        const multiplier = source ? this.data.randomOffset?.source : this.data.randomOffset?.target;
        if (obj && multiplier) {
          let randomOffset = this._offsetCache[key]?.randomOffset || get_random_offset(obj, multiplier, twister);
          this._offsetCache[key].randomOffset = randomOffset;
          offset2.x -= randomOffset.x;
          offset2.y -= randomOffset.y;
        }
        let extraOffset = this.data?.offset?.[key];
        if (extraOffset) {
          let newOffset = {
            x: extraOffset.x,
            y: extraOffset.y
          };
          if (extraOffset.gridUnits) {
            newOffset.x *= canvas.grid.size;
            newOffset.y *= canvas.grid.size;
          }
          if (extraOffset.local) {
            if (!this._cachedSourceData?.position || !this._cachedTargetData?.position) {
              this.getSourceData();
              this.getTargetData();
            }
            const startPos = this._cachedSourceData.position;
            const endPos = this._cachedTargetData.position;
            const angle = this.target ? new Ray(startPos, endPos).angle : Ray.fromAngle(
              startPos.x,
              startPos.y,
              this._cachedSourceData.rotation,
              1
            ).angle;
            newOffset = rotateAroundPoint(
              0,
              0,
              newOffset.x,
              newOffset.y,
              -angle
            );
          }
          offset2.x -= newOffset.x;
          offset2.y -= newOffset.y;
        }
        let offsetMap = this._nameOffsetMap?.[offsetMapName];
        if (!this._offsetCache[key]["nameCache"][offsetMapName]) {
          this._offsetCache[key]["nameCache"][offsetMapName] = {};
        }
        if (offsetMap) {
          if (offsetMap.missed) {
            const missedOffset = this._offsetCache[key]["nameCache"][offsetMapName]?.missedOffset || calculate_missed_position(
              offsetMap.sourceObj,
              offsetMap.targetObj,
              offsetMap.twister
            );
            this._offsetCache[key]["nameCache"][offsetMapName].missedOffset = missedOffset;
            offset2.x -= missedOffset.x;
            offset2.y -= missedOffset.y;
          }
          const obj2 = offsetMap.targetObj || offsetMap.sourceObj;
          const multiplier2 = offsetMap.randomOffset?.source || offsetMap.randomOffset?.target;
          if (obj2 && multiplier2) {
            let randomOffset = this._offsetCache[key]["nameCache"][offsetMapName]?.randomOffset || get_random_offset(obj2, multiplier2, offsetMap.twister);
            this._offsetCache[key]["nameCache"][offsetMapName].randomOffset = randomOffset;
            offset2.x -= randomOffset.x;
            offset2.y -= randomOffset.y;
          }
          if (offsetMap.offset) {
            offset2.x += offsetMap.offset.x;
            offset2.y += offsetMap.offset.y;
          }
        }
        return offset2;
      }
      /**
       * Initializes the name offset map by establishing targets
       *
       * @param inOffsetMap
       * @returns {{setup}|*}
       * @private
       */
      _setupOffsetMap(inOffsetMap) {
        if (!inOffsetMap.setup) {
          inOffsetMap.setup = true;
          inOffsetMap.sourceObj = inOffsetMap.source ? this._validateObject(inOffsetMap.source) : false;
          inOffsetMap.targetObj = inOffsetMap.target ? this._validateObject(inOffsetMap.target) : false;
          const repetition = this.data.repetition % inOffsetMap.repetitions;
          const seed = get_hash(`${inOffsetMap.seed}-${repetition}`);
          inOffsetMap.twister = createMersenneTwister(seed);
        }
        return inOffsetMap;
      }
      /**
       * Plays the effect, returning two promises; one that resolves once the duration has been established, and another
       * when the effect has finished playing
       *
       * @returns {Object}
       */
      play() {
        const durationPromise = new Promise((resolve, reject) => {
          this._durationResolve = resolve;
        });
        const finishPromise = new Promise(async (resolve, reject) => {
          this._resolve = resolve;
          Hooks.callAll("createSequencerEffect", this);
          debug(`Playing effect:`, this.data);
          this._initialize();
        });
        return {
          duration: durationPromise,
          promise: finishPromise
        };
      }
      /**
       *  Ends the effect
       */
      endEffect() {
        if (this._ended) return;
        Hooks.callAll("endedSequencerEffect", this);
        this.destroy();
      }
      destroy(...args) {
        this._destroyDependencies();
        return super.destroy(...args);
      }
      /**
       * Updates this effect with the given parameters
       * @param inUpdates
       * @returns {Promise}
       */
      async update(inUpdates) {
        if (!this.userCanUpdate)
          throw custom_error(
            "Sequencer",
            "CanvasEffect | Update | You do not have permission to update this effect"
          );
        CanvasEffect.validateUpdate(inUpdates);
        const newData = foundry.utils.deepClone(this.data);
        const updateKeys = Object.keys(inUpdates);
        updateKeys.forEach((key) => {
          foundry.utils.setProperty(newData, key, inUpdates[key]);
        });
        if (Object.keys(foundry.utils.diffObject(newData, this.data)).length === 0) {
          debug(
            `Skipped updating effect with ID ${this.id} - no changes needed`
          );
          return;
        }
        if (this.data.persist) {
          const originalSourceUUID = is_UUID(this.data.source) && this.data.attachTo ? this.data.source : "Scene." + this.data.sceneId;
          const newSourceUUID = is_UUID(newData.source) && newData.attachTo ? newData.source : "Scene." + newData.sceneId;
          if (originalSourceUUID !== newSourceUUID) {
            flagManager.removeEffectFlags(originalSourceUUID, newData);
          }
          flagManager.addEffectFlags(newSourceUUID, newData);
        }
        debug(`Updated effect with ID ${this.id}`);
        return sequencerSocket.executeForEveryone(
          SOCKET_HANDLERS.UPDATE_EFFECT,
          this.id,
          newData
        );
      }
      async addAnimatedProperties({ animations = [], loopingAnimation = [] } = {}) {
        const animationsToAdd = [];
        if (!Array.isArray(animations)) {
          throw custom_error(
            this.data.moduleName,
            `animations must be an array of arrays`
          );
        }
        for (const animationData of animations) {
          if (!Array.isArray(animationData)) {
            throw custom_error(
              this.data.moduleName,
              `each entry in animations must be an array, each with target, property name, and animation options`
            );
          }
          const result = validateAnimation(...animationData);
          if (typeof result === "string") {
            throw custom_error(this.data.moduleName, result);
          }
          result.creationTimestamp = +/* @__PURE__ */ new Date();
          animationsToAdd.push(result);
        }
        if (!Array.isArray(loopingAnimation)) {
          throw custom_error(
            this.data.moduleName,
            `loopingAnimation must be an array of arrays`
          );
        }
        for (const animationData of loopingAnimation) {
          if (!Array.isArray(animationData)) {
            throw custom_error(
              this.data.moduleName,
              `each entry in loopingAnimation must be an array, each with target, property name, and animation options`
            );
          }
          const result = validateLoopingAnimation(...animationData);
          if (typeof result === "string") {
            throw custom_error(this.data.moduleName, result);
          }
          result.creationTimestamp = +/* @__PURE__ */ new Date();
          animationsToAdd.push(result);
        }
        if (this.data.persist) {
          const originalSourceUUID = is_UUID(this.data.source) && this.data.attachTo ? this.data.source : "Scene." + this.data.sceneId;
          const newData = foundry.utils.deepClone(this.data);
          newData.animations = (newData.animations ?? []).concat(
            foundry.utils.deepClone(animationsToAdd)
          );
          flagManager.addEffectFlags(originalSourceUUID, newData);
        }
        return sequencerSocket.executeForEveryone(
          SOCKET_HANDLERS.ADD_EFFECT_ANIMATIONS,
          this.id,
          animationsToAdd
        );
      }
      async _addAnimations(inAnimations) {
        this._playAnimations(foundry.utils.deepClone(inAnimations));
        this.data.animations = (this.data.animations ?? []).concat(inAnimations);
      }
      /**
       * Updates the effect
       *
       * @param inUpdates
       * @returns {Promise}
       * @private
       */
      _update(inUpdates) {
        this.data = inUpdates;
        Hooks.callAll("updateSequencerEffect", this);
        this._destroyDependencies();
        return this._reinitialize();
      }
      /**
       * Determines whether a position is within the bounds of this effect
       *
       * @param inPosition
       * @returns {boolean}
       */
      isPositionWithinBounds(inPosition) {
        if (!this.spriteContainer) return false;
        return is_position_within_bounds(
          inPosition,
          this.spriteContainer,
          this.parent
        );
      }
      /**
       * Initializes the effect and places it on the canvas
       *
       * @returns {Promise}
       * @private
       */
      async _initialize() {
        this.ready = false;
        this._initializeVariables();
        this._addToContainer();
        this._createFile();
        this._updateCurrentFilePath(false, true);
        await this._createSprite();
        this._calculateDuration();
        this._createShapes();
        await this._setupMasks();
        await this._transformSprite();
        this._playPresetAnimations();
        this._playCustomAnimations();
        this._setEndTimeout();
        this._registerTickers();
        this._timeoutVisibility();
        await this._startEffect();
        this.ready = true;
      }
      /**
       * Reinitializes the effect after it has been updated
       *
       * @param play
       * @returns {Promise}
       * @private
       */
      async _reinitialize() {
        this.renderable = false;
        if (!this.shouldPlay) {
          return Sequencer.EffectManager._removeEffect(this);
        }
        this.actualCreationTime = +/* @__PURE__ */ new Date();
        return this._initialize();
      }
      /**
       * Initializes variables core to the function of the effect
       * This is run as a part of the construction of the effect
       *
       * @private
       */
      _initializeVariables() {
        this.rotationContainer = this.addChild(new PIXI.Container());
        this.rotationContainer.id = this.id + "-rotationContainer";
        this.isometricContainer = this.rotationContainer.addChild(
          new PIXI.Container()
        );
        this.isometricContainer.id = this.id + "-isometricContainer";
        this.spriteContainer = this.isometricContainer.addChild(
          new PIXI.Container()
        );
        this.spriteContainer.id = this.id + "-spriteContainer";
        this._template = this.data.template;
        this._ended = null;
        this._maskContainer = null;
        this._maskSprite = null;
        this._file = null;
        this._loopOffset = 0;
        this.effectFilters = {};
        this._animationDuration = 0;
        this._animationTimes = {};
        this._twister = createMersenneTwister(this.creationTimestamp);
        this._video = null;
        this._distanceCache = null;
        this._isRangeFind = false;
        this._customAngle = 0;
        this._currentFilePath = this.data.file;
        this._relatedSprites = {};
        this._hooks = [];
        this._lastDimensions = {};
        this._lastScreenDimensions = {};
        if (this._resetTimeout) {
          clearTimeout(this._resetTimeout);
        }
        this._resetTimeout = null;
        this._source = false;
        this._target = false;
        this._offsetCache = {
          source: { nameCache: {} },
          target: { nameCache: {} }
        };
        this._nameOffsetMap = Object.fromEntries(
          Object.entries(
            foundry.utils.deepClone(this.data.nameOffsetMap ?? {})
          ).map((entry) => {
            return [entry[0], this._setupOffsetMap(entry[1])];
          })
        );
        this.uuid = !is_UUID(this.context.uuid) ? this.id : this.context.uuid + ".data.flags.sequencer.effects." + this.id;
        this._ticker = CanvasAnimation.ticker;
        this._tickerMethods = [];
      }
      _addToTicker(func2) {
        this._tickerMethods.push(func2);
        this._ticker.add(func2, this);
      }
      /**
       * Destroys all dependencies to this element, such as tickers, animations, textures, and child elements
       *
       * @private
       */
      _destroyDependencies() {
        if (this._ended) return;
        this._ended = true;
        this.mask = null;
        hooksManager.removeHooks(this.uuid);
        this._tickerMethods.forEach((func2) => this._ticker.remove(func2, this));
        this._ticker = null;
        SequencerAnimationEngine.endAnimations(this.id);
        if (this._maskContainer) this._maskContainer.destroy({ children: true });
        if (this._maskSprite) {
          try {
            this._maskSprite.texture.destroy(true);
            this._maskSprite.destroy();
          } catch (err) {
          }
        }
        this.sprite.destroy();
        this.sprite = null;
        try {
          if (this.data.screenSpace) {
            SequencerAboveUILayer.removeContainerByEffect(this);
          }
        } catch (err) {
        }
        if (this.data.syncGroup) {
          SyncGroups.remove(this);
        }
        this.removeChildren().forEach((child) => child.destroy({ children: true }));
      }
      /**
       * Plays preset animations
       *
       * @private
       */
      _playPresetAnimations() {
        this._moveTowards();
        this._fadeOut();
        this._fadeIn();
        this._rotateOut();
        this._rotateIn();
        this._scaleOut();
        this._scaleIn();
        this._fadeOutAudio();
        this._fadeInAudio();
      }
      /**
       * Gets an object based on an identifier, checking if it exists within the named offset map, whether it's a
       * coordinate object, or if it's an UUID that needs to be fetched from the scene
       *
       * @param inIdentifier
       * @param specific
       * @param returnSource
       * @returns {*}
       * @private
       */
      _getObjectByID(inIdentifier, specific = false, returnSource = false) {
        let source = inIdentifier;
        let offsetMap = this._nameOffsetMap?.[inIdentifier];
        if (offsetMap) {
          if (specific) {
            source = (returnSource ? offsetMap?.sourceObj || offsetMap?.targetObj : offsetMap?.targetObj || offsetMap?.sourceObj) || source;
          } else {
            source = offsetMap?.targetObj || offsetMap?.sourceObj || source;
          }
        } else {
          source = this._validateObject(source);
        }
        return source;
      }
      /**
       * Validates the given parameter, whether it's a UUID or a coordinate object, and returns the proper one
       *
       * @param inObject
       * @returns {*}
       * @private
       */
      _validateObject(inObject) {
        if (is_UUID(inObject) || !is_object_canvas_data(inObject)) {
          inObject = get_object_from_scene(inObject, this.data.sceneId);
          inObject = inObject?._object ?? inObject;
        }
        return inObject;
      }
      /**
       * Adds this effect to the appropriate container on the right layer
       *
       * @private
       */
      _addToContainer() {
        let layer2;
        if (this.data.screenSpaceAboveUI) {
          layer2 = SequencerAboveUILayer;
        } else if (this.data.screenSpace) {
          layer2 = canvas.sequencerEffectsUILayer;
        } else if (this.data.aboveInterface) {
          layer2 = canvas.controls;
        } else if (this.data.aboveLighting) {
          layer2 = canvas.interface;
        } else {
          layer2 = canvas.primary;
        }
        layer2.addChild(this);
        layer2.sortChildren();
      }
      get startTimeMs() {
        return this._startTime * 1e3;
      }
      get endTimeMs() {
        return this._endTime * 1e3;
      }
      /**
       * Calculates the duration of this effect, based on animation durations, the video source duration, end/start times, etc
       *
       * @private
       */
      _calculateDuration() {
        let playbackRate = this.data.playbackRate || 1;
        this.mediaPlaybackRate = playbackRate;
        this._animationDuration = this.data.duration || this.mediaDurationMs;
        if (this.data.moveSpeed && this.data.moves) {
          let distance = distance_between(
            this.sourcePosition,
            this.targetPosition
          );
          let durationFromSpeed = distance / this.data.moveSpeed * 1e3;
          this._animationDuration = Math.max(durationFromSpeed, this.data.duration);
        } else if (!this.data.duration && !this.hasAnimatedMedia) {
          let fadeDuration = (this.data.fadeIn?.duration ?? 0) + (this.data.fadeOut?.duration ?? 0);
          let scaleDuration = (this.data.scaleIn?.duration ?? 0) + (this.data.scaleOut?.duration ?? 0);
          let rotateDuration = (this.data.rotateIn?.duration ?? 0) + (this.data.rotateOut?.duration ?? 0);
          let moveDuration = 0;
          if (this.data.moves) {
            let distance = distance_between(
              this.sourcePosition,
              this.targetPosition
            );
            moveDuration = (this.data.moveSpeed ? distance / this.data.moveSpeed * 1e3 : 1e3) + this.data.moves.delay;
          }
          let animationDurations = this.data.animations?.length ? Math.max(
            ...this.data.animations.map((animation2) => {
              if (animation2.looping) {
                if (animation2.loops === 0) return 0;
                return (animation2?.duration ?? 0) * (animation2?.loops ?? 0) + (animation2?.delay ?? 0);
              } else {
                return (animation2?.duration ?? 0) + (animation2?.delay ?? 0);
              }
            })
          ) : 0;
          this._animationDuration = Math.max(
            fadeDuration,
            scaleDuration,
            rotateDuration,
            moveDuration,
            animationDurations
          );
          this._animationDuration = this._animationDuration || 1e3;
        }
        this._startTime = 0;
        if (this.data.time?.start && this.mediaCurrentTime !== null) {
          let currentTime = !this.data.time.start.isPerc ? this.data.time.start.value ?? 0 : this._animationDuration * this.data.time.start.value;
          this.mediaCurrentTime = currentTime / 1e3;
          this._startTime = currentTime / 1e3;
        }
        this._endTime = this._animationDuration;
        if (this.data.time?.end) {
          if (this.data.time.end.isPerc) {
            this._endTime = this._animationDuration - this._animationDuration * this.data.time.end.value;
          } else {
            this._endTime = this.data.time.isRange ? this.data.time.end.value : this._animationDuration - this.data.time.end.value;
          }
        }
        this._endTime /= 1e3;
        this._animationDuration = clamp$1(this.endTimeMs - this.startTimeMs, 0, this._animationDuration);
        if (this._file?.markers && this._startTime === 0 && this._endTime === this.mediaDuration) {
          this._animationTimes.loopStart = this._file.markers.loop.start / playbackRate / 1e3;
          this._animationTimes.loopEnd = this._file.markers.loop.end / playbackRate / 1e3;
          this._animationTimes.forcedEnd = this._file.markers.forcedEnd / playbackRate / 1e3;
        }
        this._totalDuration = this.loops ? this._animationDuration * this.loops + this.loopDelay * (this.loops - 1) : this._animationDuration;
        this._totalDuration /= playbackRate;
        if (this.data.persist) {
          this.mediaLooping = (!this.data.time || this._startTime === 0 && this._endTime === this.mediaDuration) && this._animationTimes.loopStart === void 0 && this._animationTimes.loopEnd === void 0 && !this.loops && !this.loopDelay;
        } else {
          this.mediaLooping = this._startTime === 0 && this._endTime > this.mediaDuration && !(this.loops && this.loopDelay);
        }
        this._durationResolve(this._totalDuration);
      }
      /**
       * If this effect is animatable, hold off on rendering it for a bit so that the animations have time to initialize to
       * prevent it from spawning and then jumping to the right place
       *
       * @private
       */
      _timeoutVisibility() {
        if (!this.data.animations) {
          return this._setupHooks();
        }
        setTimeout(() => {
          this._setupHooks();
        }, 50);
      }
      /**
       * Add Ticker handler to check for updates to attached objects
       *
       * @private
       */
      _registerTickers() {
        if (this.data.stretchTo && this.data.stretchTo?.attachTo) {
          this._addToTicker(this._transformStretchToAttachedSprite);
        }
        if (this.data.attachTo?.active && !this.data.stretchTo?.attachTo) {
          this._addToTicker(this._transformAttachedNoStretchSprite);
        }
        if (this.rotateTowards && this.data.rotateTowards?.attachTo) {
          this._addToTicker(this._transformRotateTowardsAttachedSprite);
        }
        if (this.data.scaleToObject && this.data?.attachTo?.active && this.data?.attachTo?.bindScale) {
          const { heightWidthRatio, widthHeightRatio, baseScaleX, baseScaleY } = this._getBaseScale();
          this._addToTicker(() => {
            this._applyScaleToObject(heightWidthRatio, widthHeightRatio, baseScaleX, baseScaleY);
            this._setAnchors();
          });
        }
        if (this.isSourceTemporary) {
          this._addToTicker(this._checkSourceDestroyed);
        }
        if (this.isTargetTemporary) {
          this._addToTicker(this._checkTargetDestroyed);
        }
      }
      _checkSourceDestroyed() {
        if (this.isSourceDestroyed) {
          this._source = this.sourcePosition;
          SequencerEffectManager.endEffects({ effects: this });
        }
      }
      _checkTargetDestroyed() {
        if (this.isTargetDestroyed) {
          this._source = this.targetPosition;
          SequencerEffectManager.endEffects({ effects: this });
        }
      }
      _createFile() {
        if (this.data.file === "") {
          return;
        }
        let file;
        if (this.data.customRange) {
          const template = this.template ? [this.template.gridSize, this.template.startPoint, this.template.endPoint] : [100, 0, 0];
          file = SequencerFileBase.make(
            this.data.file,
            "temporary.range.file",
            { template }
          );
        } else if (Sequencer.Database.entryExists(this.data.file)) {
          file = Sequencer.Database.getEntry(this.data.file).clone();
        } else {
          file = SequencerFileBase.make(this.data.file);
          this._currentFilePath = this.data.file;
        }
        if (file.template) {
          this._template = foundry.utils.mergeObject(
            { gridSize: file.template[0], startPoint: file.template[1], endPoint: file.template[2] },
            this.data.template
          );
        }
        file.fileIndex = this.data.forcedIndex;
        file.twister = this._twister;
        this._file = file;
        this._isRangeFind = file?.rangeFind;
      }
      _updateCurrentFilePath(distance, showDistanceWarning = false) {
        if (!this._file) {
          return;
        }
        if (!this.data.stretchTo) {
          this._currentFilePath = this._file.getFile();
          return;
        }
        distance = distance || new Ray(this.sourcePosition, this.targetPosition).distance;
        if (distance === 0 && showDistanceWarning) {
          custom_error(
            "effect",
            `stretchTo - You are stretching over a distance of "0", you may be attempting to stretch between two of the same coordinates!`
          );
        }
        this._currentFilePath = this._file.getFileForDistance(distance);
      }
      /**
       * Creates the sprite, and the relevant containers that manage the position and offsets of the overall visual look of the sprite
       *
       * @private
       */
      async _createSprite() {
        this.renderable = false;
        const spriteData = {
          antialiasing: this.data?.fileOptions?.antialiasing,
          tiling: this.data.tilingTexture,
          xray: this.data.xray || this.data.screenSpace || this.data.screenSpaceAboveUI,
          isPersisted: this.data.persist && !this.data.loopOptions?.endOnLastLoop
        };
        this.sprite = new SequencerSpriteManager(this._file, spriteData);
        this.spriteContainer.addChild(this.sprite);
        this.sprite.id = this.id + "-sprite";
        this.sprite.loopDelay = this.loopDelay;
        this.sprite.currentTime = this._startTime;
        this.sprite.loop = this.loops;
        await this.sprite.activate(this._currentFilePath);
        this.sprite.volume = (this.data.volume ?? 0) * game.settings.get("core", "globalInterfaceVolume");
        if (this._isRangeFind && this.data.stretchTo && (this.data.attachTo?.active || this.data.stretchTo?.attachTo?.active)) {
          this.sprite.preloadVariants();
        }
        if (this.data.text) {
          const text2 = this.data.text.text;
          const fontSettings = foundry.utils.deepClone(this.data.text);
          fontSettings.fontSize = (fontSettings?.fontSize ?? 26) * (150 / canvas.grid.size);
          const textSprite = this.sprite.addText({ text: text2, textStyle: fontSettings });
          textSprite.zIndex = 1;
          const textAnchor = this.data.text.anchor;
          textSprite.anchor.set(textAnchor?.x ?? 0.5, textAnchor?.y ?? 0.5);
        }
        this.sprite.filters = [];
        if (this.data.filters) {
          for (let index = 0; index < this.data.filters.length; index++) {
            const filterData = this.data.filters[index];
            const filter2 = new filters[filterData.className](filterData.data);
            filter2.id = this.id + "-" + filterData.className + "-" + index.toString();
            if (filter2 instanceof PIXI.ColorMatrixFilter) {
              this.sprite.colorMatrixFilter = filter2;
            } else {
              this.sprite.filters.push(filter2);
            }
            const filterKeyName = filterData.name || filterData.className;
            this.effectFilters[filterKeyName] = filter2;
          }
        }
        this.effectAlpha = this.data.opacity;
        let spriteOffsetX = this.data.spriteOffset?.x ?? 0;
        let spriteOffsetY = this.data.spriteOffset?.y ?? 0;
        if (this.data.spriteOffset?.gridUnits) {
          spriteOffsetX *= canvas.grid.size;
          spriteOffsetY *= canvas.grid.size;
        }
        this.sprite.position.set(spriteOffsetX, spriteOffsetY);
        this.sprite.anchor?.set(
          this.data.spriteAnchor?.x ?? 0.5,
          this.data.spriteAnchor?.y ?? 0.5
        );
        let spriteRotation = this.data.spriteRotation ?? 0;
        if (this.data.randomSpriteRotation) {
          spriteRotation += random_float_between(-360, 360, this._twister);
        }
        this.sprite.rotation = Math.normalizeRadians(
          Math.toRadians(spriteRotation)
        );
        this._customAngle = this.data.angle ?? 0;
        if (this.data.randomRotation) {
          this._customAngle += random_float_between(-360, 360, this._twister);
        }
        const offsetMap = this._nameOffsetMap?.[this.data.source];
        if (offsetMap?.angle !== void 0) {
          this._customAngle += offsetMap?.angle;
        }
        if (offsetMap?.randomRotation) {
          this._customAngle += random_float_between(-360, 360, offsetMap.twister);
        }
        this.spriteContainer.rotation = -Math.normalizeRadians(
          Math.toRadians(this._customAngle)
        );
        if (CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE) {
          this.isometricContainer.rotation = Math.PI / 4;
        }
        if (this.data.tint) {
          this.sprite.tint = this.data.tint;
        }
        if (this.shouldShowFadedVersion) {
          this.alpha = game.settings.get(CONSTANTS.MODULE_NAME, "user-effect-opacity") / 100;
          this.filters = [
            new PIXI.ColorMatrixFilter({
              saturation: -1
            })
          ];
        }
        this.updateElevation();
      }
      _createShapes() {
        const nonMaskShapes = (this.data?.shapes ?? []).filter(
          (shape) => !shape.isMask
        );
        this.shapes = {};
        for (const shape of nonMaskShapes) {
          const graphic = createShape(shape);
          graphic.filters = this.sprite.filters;
          this.spriteContainer.addChild(graphic);
          this.shapes[shape?.name ?? "shape-" + foundry.utils.randomID()] = graphic;
        }
      }
      updateElevation() {
        let targetElevation = Math.max(
          get_object_elevation(this.source ?? {}),
          get_object_elevation(this.target ?? {})
        );
        if (!CONSTANTS.IS_V12) targetElevation += 1;
        let effectElevation = this.data.elevation?.elevation ?? 0;
        if (!this.data.elevation?.absolute) {
          effectElevation += targetElevation;
        }
        this.elevation = effectElevation;
        let sort;
        const isIsometric = foundry.utils.getProperty(
          game.scenes.get(this.data.sceneId),
          CONSTANTS.INTEGRATIONS.ISOMETRIC.SCENE_ENABLED
        );
        if (CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE && isIsometric) {
          const sourceSort = this.source ? (this.sourceMesh?.sort ?? 0) + (this.data.isometric?.overlay ? 1 : -1) : 0;
          const targetSort = this.target ? (this.targetMesh?.sort ?? 0) + (this.data.isometric?.overlay ? 1 : -1) : 0;
          sort = Math.max(sourceSort, targetSort);
        } else {
          sort = !is_real_number(this.data.zIndex) ? this?.parent?.children?.length ?? 0 : 1e5;
        }
        sort += 100 + (this.data.aboveLighting ? 300 : 0);
        this.zIndex = sort + (is_real_number(this.data.zIndex) ? this.data.zIndex : 0);
        this.sort = sort;
        this.sortLayer = this.data.sortLayer;
        if (this.parent) {
          this.parent.sortChildren();
        }
      }
      updateTransform() {
        super.updateTransform();
        if (this.data.screenSpace || this.data.screenSpaceAboveUI) {
          const [screenWidth, screenHeight] = canvas.screenDimensions;
          if (this._lastScreenDimensions?.screenWidth !== screenWidth && this._lastScreenDimensions?.screenHeight !== screenHeight) {
            this._lastScreenDimensions.screenWidth = screenWidth;
            this._lastScreenDimensions.screenHeight = screenHeight;
          }
          this.position.set(
            (this.data.screenSpacePosition?.x ?? 0) + screenWidth * (this.data.screenSpaceAnchor?.x ?? this.data.anchor?.x ?? 0.5),
            (this.data.screenSpacePosition?.y ?? 0) + screenHeight * (this.data.screenSpaceAnchor?.y ?? this.data.anchor?.y ?? 0.5)
          );
          if (this.data.screenSpaceScale) {
            const scaleData = this.data.screenSpaceScale ?? { x: 1, y: 1 };
            let scaleX = scaleData.x;
            let scaleY = scaleData.y;
            this._lastScreenDimensions.width = this.sprite.texture?.width || this._lastScreenDimensions.width || this.sprite.width || this.spriteContainer.children[this.spriteContainer.children.length - 1].width;
            this._lastScreenDimensions.height = this.sprite.texture?.height || this._lastScreenDimensions.height || this.sprite.height || this.spriteContainer.children[this.spriteContainer.children.length - 1].height;
            if (scaleData.fitX) {
              scaleX = scaleX * (screenWidth / this._lastScreenDimensions.width);
            }
            if (scaleData.fitY) {
              scaleY = scaleY * (screenHeight / this._lastScreenDimensions.height);
            }
            scaleX = scaleData.ratioX ? scaleY : scaleX;
            scaleY = scaleData.ratioY ? scaleX : scaleY;
            this.scale.set(scaleX, scaleY);
          }
        }
      }
      async _setupMasks() {
        const maskShapes = this.data.shapes.filter((shape) => shape.isMask);
        if (!this.data?.masks?.length && !maskShapes.length) return;
        const maskFilter = MaskFilter.create();
        for (const uuid of this.data.masks) {
          const documentObj = fromUuidSync(uuid);
          if (!documentObj || documentObj.parent.id !== this.data.sceneId) continue;
          const obj = documentObj.object;
          let shape = obj?.mesh;
          let shapeToAdd = shape;
          if (obj instanceof MeasuredTemplate || obj instanceof Drawing) {
            shape = obj?.shape?.geometry?.graphicsData?.[0]?.shape ?? obj?.shape;
            if (game.modules.get("walledtemplates")?.active && obj.walledtemplates?.walledTemplate) {
              let wt = obj.walledtemplates.walledTemplate;
              wt.options.padding = 3 * canvas.dimensions.distancePixels;
              shape = wt.computeShape();
              wt.options.padding = 0;
            }
            shapeToAdd = new PIXI.LegacyGraphics().beginFill().drawShape(shape).endFill();
            if (obj instanceof MeasuredTemplate) {
              shapeToAdd.position.set(documentObj.x, documentObj.y);
            } else {
              const {
                x,
                y,
                shape: { width: width2, height },
                rotation: rotation2
              } = documentObj;
              shapeToAdd.pivot.set(width2 / 2, height / 2);
              shapeToAdd.position.set(x + width2 / 2, y + height / 2);
              shapeToAdd.angle = rotation2;
            }
            shapeToAdd.cullable = true;
            shapeToAdd.custom = true;
            shapeToAdd.renderable = false;
            shapeToAdd.uuid = uuid;
            canvas.stage.addChild(shapeToAdd);
          }
          shapeToAdd.obj = obj;
          const updateMethod = (doc) => {
            if (doc !== documentObj) return;
            const mask = maskFilter.masks.find((shape2) => shape2.uuid === uuid);
            if (!mask) return;
            if (!mask.custom) return;
            mask.clear();
            if (obj instanceof MeasuredTemplate) {
              mask.position.set(documentObj.x, documentObj.y);
              let maskObj = documentObj.object;
              shape = obj?.shape?.geometry?.graphicsData?.[0]?.shape ?? obj?.shape;
              if (game.modules.get("walledtemplates")?.active && maskObj.walledtemplates?.walledTemplate) {
                let wt = maskObj.walledtemplates.walledTemplate;
                wt.options.padding = 3 * canvas.dimensions.distancePixels;
                shape = wt.computeShape();
                wt.options.padding = 0;
              }
            } else {
              const {
                x,
                y,
                shape: { width: width2, height },
                rotation: rotation2
              } = documentObj;
              mask.pivot.set(width2 / 2, height / 2);
              mask.position.set(x + width2 / 2, y + height / 2);
              mask.angle = rotation2;
            }
            mask.beginFill().drawShape(shape).endFill();
          };
          if (game.modules.get("walledtemplates")?.active) {
            hooksManager.addHook(this.uuid, "createWall", () => {
              setTimeout(() => {
                updateMethod(documentObj);
              }, 100);
            });
            hooksManager.addHook(this.uuid, "updateWall", () => {
              setTimeout(() => {
                updateMethod(documentObj);
              }, 100);
            });
            hooksManager.addHook(this.uuid, "deleteWall", () => {
              setTimeout(() => {
                updateMethod(documentObj);
              }, 100);
            });
          }
          hooksManager.addHook(this.uuid, this.getHook("update", uuid), (doc) => {
            setTimeout(() => {
              updateMethod(doc);
            }, 100);
          });
          maskFilter.masks.push(shapeToAdd);
        }
        for (const shapeData of maskShapes) {
          const shape = createShape(shapeData);
          shape.cullable = true;
          shape.custom = true;
          shape.renderable = false;
          this.spriteContainer.addChild(shape);
          this.shapes[shapeData?.name ?? "shape-" + foundry.utils.randomID()] = shape;
          maskFilter.masks.push(shape);
        }
        this.sprite.filters.push(maskFilter);
      }
      /**
       * Sets up the hooks relating to this effect's source and target
       *
       * @private
       */
      _setupHooks() {
        const attachedToSource = this.data.attachTo?.active && is_UUID(this.data.source);
        const attachedToTarget = (this.data.stretchTo?.attachTo || this.data.rotateTowards?.attachTo) && is_UUID(this.data.target);
        const baseRenderable = this.shouldPlayVisible;
        let renderable = baseRenderable;
        let alpha = null;
        if (attachedToSource) {
          hooksManager.addHook(this.uuid, this.getSourceHook("delete"), (doc) => {
            const uuid = doc.uuid;
            if (doc !== this.sourceDocument) return;
            this._source = this._cachedSourceData.position;
            SequencerEffectManager.objectDeleted(uuid);
          });
          if (this.isSourceDestroyed) {
            SequencerEffectManager.objectDeleted(this.sourceDocument.uuid);
          }
          if (this.data.attachTo?.bindVisibility) {
            hooksManager.addHook(
              this.uuid,
              "sightRefresh",
              () => {
                const sourceVisible = this.source && !this.sourceMesh?.occluded;
                const sourceHidden = this.sourceDocument && (this.sourceDocument?.hidden ?? false);
                const targetVisible = this.target && (!attachedToTarget || (this.targetMesh?.occluded ?? true));
                this.renderable = baseRenderable && (!sourceHidden || game.user.isGM) && (sourceVisible || targetVisible) && this._checkWallCollisions();
                this.alpha = sourceVisible && sourceHidden ? 0.5 : 1;
                renderable = baseRenderable && this.renderable;
              },
              true
            );
          }
          if (this.data.attachTo?.bindAlpha || this.data.attachTo?.bindElevation) {
            hooksManager.addHook(this.uuid, this.getSourceHook("update"), (doc) => {
              if (doc !== this.sourceDocument) return;
              if (this.data.attachTo?.bindAlpha) {
                this.spriteContainer.alpha = this.getSourceData().alpha;
              }
              if (this.data.attachTo?.bindElevation) {
                this.updateElevation();
              }
            });
          }
          if (this.data.attachTo?.bindAlpha) {
            alpha = this.getSourceData().alpha;
          }
        }
        if (attachedToTarget) {
          hooksManager.addHook(this.uuid, this.getTargetHook("delete"), (doc) => {
            if (doc !== this.target) return;
            this._target = this._cachedTargetData.position;
            const uuid = doc.uuid;
            SequencerEffectManager.objectDeleted(uuid);
          });
          if (this.isTargetDestroyed) {
            SequencerEffectManager.objectDeleted(this.targetDocument.uuid);
          }
          hooksManager.addHook(this.uuid, this.getTargetHook("update"), (doc) => {
            if (doc !== this.target) return;
            this.updateElevation();
          });
        }
        for (let uuid of this.data?.tiedDocuments ?? []) {
          const tiedDocument = fromUuidSync(uuid);
          if (tiedDocument) {
            hooksManager.addHook(
              this.uuid,
              this.getHook("delete", tiedDocument.uuid),
              (doc) => {
                if (tiedDocument !== doc) return;
                SequencerEffectManager.objectDeleted(doc.uuid);
              }
            );
          }
        }
        setTimeout(() => {
          this.renderable = renderable;
          this.spriteContainer.alpha = alpha ?? 1;
        }, 25);
      }
      /**
       * Calculates the padding and scale to stretch an effect across the given distance
       *
       * If the file is a SequencerFileBase instance, it will also pick the appropriate file for the right distance
       *
       * @param {number} distance
       * @param {number} textureWidth
       * @returns {Object}
       * @private
       */
      async _getDistanceScaling(distance, textureWidth) {
        if (!this._distanceCache || this._distanceCache?.distance !== distance) {
          let scaleX = 1;
          let scaleY = 1;
          if (this._file instanceof SequencerFileBase) {
            const startPoint = this.template?.startPoint ?? 0;
            const endPoint = this.template?.endPoint ?? 0;
            const widthWithPadding = textureWidth - (startPoint + endPoint);
            const spriteScale = distance / widthWithPadding;
            scaleX = spriteScale;
            scaleY = this.data.stretchTo?.onlyX ? widthWithPadding / textureWidth : spriteScale;
          }
          this._distanceCache = {
            scaleX,
            scaleY,
            distance
          };
        }
        return this._distanceCache;
      }
      /**
       * Applies the distance scaling to the sprite based on the previous method
       *
       * @returns {Promise<void>}
       * @private
       */
      async _applyDistanceScaling() {
        const ray = new Ray(this.sourcePosition, this.targetPosition);
        this._rotateTowards(ray);
        const distance = ray.distance / (this.data.scale.x ?? 1);
        this._updateCurrentFilePath(distance);
        await this.sprite.activate(this._currentFilePath);
        const texture = this.sprite.texture;
        let { scaleX, scaleY } = await this._getDistanceScaling(distance, texture.width);
        if (this.data.attachTo?.active) {
          this.position.set(
            this.sourcePosition.x,
            this.sourcePosition.y
          );
        }
        if (this.data.tilingTexture) {
          const scaleX2 = this.data.scale.x ?? 1;
          const scaleY2 = this.data.scale.y ?? 1;
          this.sprite.scale.set(scaleX2 * this.flipX, scaleY2 * this.flipY);
          this.sprite.width = distance * scaleX2;
          this.sprite.height = texture.height * scaleY2;
          this.sprite.tileScale.x = this.data.tilingTexture.scale.x * scaleX2;
          this.sprite.tileScale.y = this.data.tilingTexture.scale.y * scaleY2;
          this.sprite.tilePosition = this.data.tilingTexture.position;
        } else {
          this.sprite.scale.set(
            scaleX * (this.data.scale.x ?? 1) * this.flipX,
            scaleY * (this.data.scale.y ?? 1) * this.flipY
          );
        }
      }
      _setAnchors() {
        let anchor = { x: 0.5, y: 0.5, ...this.data.spriteAnchor ?? null };
        if (this.data.rotateTowards && this.data.rotateTowards.template || this.data.stretchTo) {
          const textureWidth = this.sprite.texture?.width ?? this.sprite.width;
          const templateAnchorX = this.template ? this.template.startPoint / textureWidth : void 0;
          anchor = { x: templateAnchorX, y: 0.5 };
        }
        if (this.data.rotateTowards && !this.data.rotateTowards.template && !this.data.anchor) {
          this.spriteContainer.pivot.set(this.sprite.width * -0.5, 0);
        } else {
          this.spriteContainer.pivot.set(
            interpolate(
              this.sprite.width * -0.5,
              this.sprite.width * 0.5,
              this.data.anchor?.x ?? 0.5
            ),
            interpolate(
              this.sprite.height * -0.5,
              this.sprite.height * 0.5,
              this.data.anchor?.y ?? 0.5
            )
          );
        }
        this.sprite.anchor?.set(
          this.flipX === 1 ? anchor.x : 1 - anchor.x,
          anchor.y
        );
      }
      _checkWallCollisions() {
        if (!this.data.stretchTo?.attachTo || !this.data.stretchTo?.requiresLineOfSight)
          return true;
        const ray = new Ray(this.sourcePosition, this.targetPosition);
        const blockingObjects = canvas.walls.checkCollision(ray, { type: "sight" });
        if (!blockingObjects.length && !this.data.stretchTo?.hideLineOfSight) {
          SequencerEffectManager.endEffects({ effects: this });
        }
        return !blockingObjects.length;
      }
      /**
       * Rotates the effect towards the target
       *
       * @param ray
       * @private
       */
      _rotateTowards(ray) {
        if (!ray) {
          const sourcePosition = this.flipX === 1 ? this.sourcePosition : this.targetPosition;
          const targetPosition = this.flipX === 1 ? this.targetPosition : this.sourcePosition;
          ray = new Ray(sourcePosition, targetPosition);
        }
        this.rotationContainer.rotation = Math.normalizeRadians(
          ray.angle + Math.toRadians(this.data.rotateTowards?.rotationOffset ?? 0)
        );
        this._tweakRotationForIsometric();
      }
      _tweakRotationForIsometric() {
        if (!CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE) return;
        if (this.data.stretchTo) {
          let skew = Math.normalizeRadians(
            this.rotationContainer.rotation - Math.PI / 4
          );
          if (Math.abs(skew) >= Math.PI / 2 - 0.5 && Math.abs(skew) <= Math.PI / 2 + 0.5) {
            skew -= Math.PI / 2;
          }
          this.isometricContainer.skew.set(Math.normalizeRadians(skew), 0);
          this.isometricContainer.rotation = 0;
        } else if (this.data?.isometric?.overlay) {
          this.rotationContainer.rotation = 0;
          let skew = Math.PI / 4 + this.rotationContainer.rotation;
          this.isometricContainer.skew.set(
            Math.normalizeRadians(skew - Math.PI / 4),
            0
          );
          this.isometricContainer.scale.set(
            1,
            window.scale ?? CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION
          );
        } else {
          this.isometricContainer.rotation = 0;
        }
      }
      /**
       * Transforms the sprite, rotating it, stretching it, scaling it, sizing it according its data
       *
       * @private
       */
      async _transformSprite() {
        if (this.data.stretchTo) {
          await this._applyDistanceScaling();
        }
        if (!this.data.stretchTo) {
          this._transformNoStretchSprite();
        }
        if (!this.data.screenSpace && (!this.data.attachTo?.active || this.data.stretchTo?.attachTo)) {
          this.position.set(this.sourcePosition.x, this.sourcePosition.y);
        }
        if (this.data.rotateTowards) {
          this._rotateTowards();
        }
        this._setAnchors();
        this._tweakRotationForIsometric();
        this.sprite.updateDefaultScaling();
      }
      async _transformStretchToAttachedSprite() {
        try {
          await this._applyDistanceScaling();
          this._setAnchors();
        } catch (err) {
        }
      }
      _transformNoStretchSprite() {
        if (this.data.tilingTexture) {
          this.sprite.tileScale = {
            x: this.data.tilingTexture.scale.x * this.gridSizeDifference,
            y: this.data.tilingTexture.scale.y * this.gridSizeDifference
          };
          this.sprite.tilePosition = this.data.tilingTexture.position;
        }
        const { heightWidthRatio, widthHeightRatio, baseScaleX, baseScaleY } = this._getBaseScale();
        if (this.data.scaleToObject) {
          this._applyScaleToObject(heightWidthRatio, widthHeightRatio, baseScaleX, baseScaleY);
        } else if (this.data.size) {
          let { height, width: width2 } = this.data.size;
          if (this.data.size.width === "auto" || this.data.size.height === "auto") {
            height = this.sprite.height;
            width2 = this.sprite.width;
            if (this.data.size.width === "auto") {
              height = this.data.size.height;
              if (this.data.size.gridUnits) {
                height *= canvas.grid.size;
              }
              width2 = height * widthHeightRatio;
            } else if (this.data.size.height === "auto") {
              width2 = this.data.size.width;
              if (this.data.size.gridUnits) {
                width2 *= canvas.grid.size;
              }
              height = width2 * heightWidthRatio;
            }
          } else if (this.data.size.gridUnits) {
            height *= canvas.grid.size;
            width2 *= canvas.grid.size;
          }
          this.sprite.width = width2 * baseScaleX;
          this.sprite.height = height * baseScaleY;
        } else if (this.data.screenSpace) {
          this.sprite.scale.set(
            baseScaleX,
            baseScaleY
          );
        } else {
          this.sprite.scale.set(
            baseScaleX * this.gridSizeDifference,
            baseScaleY * this.gridSizeDifference
          );
        }
      }
      /**
       * Calculate the base scale and aspect ratios of the sprite
       *
       * @returns {{heightWidthRatio: number, widthHeightRatio: number, baseScaleX: number, baseScaleY: number}}
       *
       * @private
       */
      _getBaseScale() {
        const heightWidthRatio = this.sprite.height / this.sprite.width;
        const widthHeightRatio = this.sprite.width / this.sprite.height;
        const baseScaleX = (this.data.scale?.x ?? 1) * (this.data.spriteScale?.x ?? 1) * this.flipX;
        const baseScaleY = (this.data.scale?.y ?? 1) * (this.data.spriteScale?.y ?? 1) * this.flipY;
        return {
          heightWidthRatio,
          widthHeightRatio,
          baseScaleX,
          baseScaleY
        };
      }
      _applyScaleToObject(heightWidthRatio, widthHeightRatio, baseScaleX, baseScaleY) {
        try {
          let { width: width2, height } = this.getSourceData();
          if (this.sourceDocument instanceof TokenDocument) {
            width2 *= this.data.scaleToObject?.considerTokenScale ? this.sourceDocument.texture.scaleX : 1;
            height *= this.data.scaleToObject?.considerTokenScale ? this.sourceDocument.texture.scaleY : 1;
          }
          if (width2 === this._lastDimensions.width && height === this._lastDimensions.height) return;
          this._lastDimensions = { width: width2, height };
          const ratioToUse = heightWidthRatio > widthHeightRatio;
          if (this.data.scaleToObject?.uniform) {
            let newWidth = Math.max(width2, height);
            height = Math.max(width2, height);
            width2 = newWidth;
          } else {
            width2 = width2 * (ratioToUse ? widthHeightRatio : 1);
            height = height * (!ratioToUse ? heightWidthRatio : 1);
          }
          this.sprite.width = width2 * (this.data.scaleToObject?.scale ?? 1) * baseScaleX;
          this.sprite.height = height * (this.data.scaleToObject?.scale ?? 1) * baseScaleY;
          SequencerAnimationEngine.updateStartValues(this.sprite, "width");
          SequencerAnimationEngine.updateStartValues(this.sprite, "height");
          SequencerAnimationEngine.updateStartValues(this.sprite, "scale.x");
          SequencerAnimationEngine.updateStartValues(this.sprite, "scale.y");
        } catch (err) {
        }
      }
      async _transformAttachedNoStretchSprite() {
        if (this.isDestroyed) return;
        const applyRotation = this.data.attachTo?.bindRotation && !(this.sourceDocument instanceof TokenDocument && this.sourceDocument.lockRotation) && (this.sourceDocument?.rotation !== void 0 || this.sourceDocument?.direction !== void 0) && !this.data.rotateTowards && !this.data.stretchTo;
        if (applyRotation) {
          this.rotationContainer.rotation = this.getSourceData().rotation;
        }
        this._tweakRotationForIsometric();
        this.position.set(
          this.sourcePosition.x,
          this.sourcePosition.y
        );
      }
      async _transformRotateTowardsAttachedSprite() {
        if (this.isDestroyed) return;
        try {
          this._rotateTowards();
          this._setAnchors();
        } catch (err) {
          debug_error(err);
        }
      }
      /**
       * Provided an animation targeting the rotation of the sprite's primary container, this method will counter-rotate
       * the sprite in an equal fashion so that the sprite's rotation remains static relative to this animation
       *
       * @param animation
       * @returns {*[]}
       * @private
       */
      _counterAnimateRotation(animation2) {
        if (animation2.target === this.spriteContainer && this.data.zeroSpriteRotation) {
          delete animation2.target;
          let counterAnimation = foundry.utils.deepClone(animation2);
          animation2.target = this.spriteContainer;
          counterAnimation.target = this.sprite;
          if (counterAnimation.values) {
            counterAnimation.values = counterAnimation.values.map(
              (value) => value * -1
            );
          } else {
            counterAnimation.from *= -1;
            counterAnimation.to *= -1;
          }
          if (!Array.isArray(animation2)) {
            animation2 = [animation2, counterAnimation];
          } else {
            animation2.push(counterAnimation);
          }
        }
        return animation2;
      }
      /**
       * Plays the custom animations of this effect
       *
       * @returns {number}
       * @private
       */
      _playCustomAnimations() {
        if (!this.data.animations) return 0;
        this._playAnimations(
          foundry.utils.deepClone(this.data.animations) ?? [],
          this.actualCreationTime - this.creationTimestamp
        );
      }
      _playAnimations(animations, timeDifference = 0) {
        let animationsToSend = [];
        const oneShotAnimations = animations.filter(
          (animation2) => !animation2.looping && !animation2.fromEnd
        );
        for (let animation2 of oneShotAnimations) {
          if (animation2.target === "alphaFilter") {
            animation2.target = this;
            animation2.propertyName = "effectAlpha";
          } else if (animation2.target === "effect") {
            animation2.target = this;
          } else {
            animation2.target = foundry.utils.getProperty(this, animation2.target);
          }
          if (!animation2.target) continue;
          if (animation2.propertyName.indexOf("rotation") > -1) {
            animation2.from = animation2.from * (Math.PI / 180);
            animation2.to = animation2.to * (Math.PI / 180);
          }
          if (["position.x", "position.y", "height", "width"].includes(
            animation2.propertyName
          ) && animation2.gridUnits) {
            animation2.from *= canvas.grid.size;
            animation2.to *= canvas.grid.size;
          }
          if (["position.x", "position.y", "height", "width"].includes(
            animation2.propertyName
          ) && animation2.screenSpace) {
            const [screenWidth, screenHeight] = canvas.screenDimensions;
            const dimension = animation2.propertyName === "position.x" || animation2.propertyName === "width" ? screenWidth : screenHeight;
            animation2.from *= dimension;
            animation2.to *= dimension;
          }
          if (["hue"].includes(animation2.propertyName)) {
            animation2.getPropertyName = "values." + animation2.propertyName;
          }
          animationsToSend = animationsToSend.concat(
            this._counterAnimateRotation(animation2)
          );
        }
        const loopingAnimations = animations.filter(
          (animation2) => animation2.looping
        );
        for (let animation2 of loopingAnimations) {
          if (animation2.target === "alphaFilter") {
            animation2.target = this;
            animation2.propertyName = "effectAlpha";
          } else if (animation2.target === "effect") {
            animation2.target = this;
          } else {
            animation2.target = foundry.utils.getProperty(this, animation2.target);
          }
          if (!animation2.target) continue;
          if (animation2.propertyName.indexOf("rotation") > -1) {
            animation2.values = animation2.values.map((angle) => {
              return angle * (Math.PI / 180);
            });
          }
          if (["position.x", "position.y", "height", "width"].includes(
            animation2.propertyName
          ) && animation2.gridUnits) {
            animation2.values = animation2.values.map((value) => {
              return value * canvas.grid.size;
            });
          }
          if (["hue"].includes(animation2.propertyName)) {
            animation2.getPropertyName = "values." + animation2.propertyName;
          }
          animationsToSend = animationsToSend.concat(
            this._counterAnimateRotation(animation2)
          );
        }
        if (!(this instanceof PersistentCanvasEffect)) {
          animationsToSend = animationsToSend.concat(
            this._getFromEndCustomAnimations()
          );
        }
        setTimeout(() => {
          SequencerAnimationEngine.addAnimation(
            this.id,
            animationsToSend,
            timeDifference
          );
        }, 20);
      }
      _getFromEndCustomAnimations(immediate = false) {
        let fromEndAnimations = [];
        const animations = foundry.utils.deepClone(this.data.animations) ?? [];
        const oneShotEndingAnimations = animations.filter(
          (animation2) => !animation2.looping && animation2.fromEnd
        );
        for (let animation2 of oneShotEndingAnimations) {
          animation2.target = foundry.utils.getProperty(this, animation2.target);
          if (!animation2.target) continue;
          animation2.delay = is_real_number(immediate) ? Math.max(immediate - animation2.duration + animation2.delay, 0) : Math.max(
            this._totalDuration - animation2.duration + animation2.delay,
            0
          );
          if (animation2.propertyName.indexOf("rotation") > -1) {
            animation2.from = animation2.from * (Math.PI / 180);
            animation2.to = animation2.to * (Math.PI / 180);
          }
          if (["position.x", "position.y", "height", "width"].includes(
            animation2.propertyName
          ) && animation2.gridUnits) {
            animation2.from *= canvas.grid.size;
            animation2.to *= canvas.grid.size;
          }
          fromEndAnimations = fromEndAnimations.concat(
            this._counterAnimateRotation(animation2)
          );
        }
        return fromEndAnimations;
      }
      /**
       * Fades in the effect at the start of the effect
       *
       * @returns {number|*}
       * @private
       */
      _fadeIn() {
        if (!this.data.fadeIn || !this.sprite) return 0;
        let fadeIn = this.data.fadeIn;
        if (this.actualCreationTime - (this.creationTimestamp + fadeIn.duration + fadeIn.delay) > 0) {
          return;
        }
        this.effectAlpha = 0;
        SequencerAnimationEngine.addAnimation(this.id, {
          target: this,
          propertyName: "effectAlpha",
          to: this.data.opacity,
          duration: fadeIn.duration,
          ease: fadeIn.ease,
          delay: fadeIn.delay,
          absolute: true
        });
        return fadeIn.duration + fadeIn.delay;
      }
      /**
       * Fades in the effect's audio at the start of the effect
       *
       * @returns {number|*}
       * @private
       */
      _fadeInAudio() {
        if (!this.data.fadeInAudio || !this.sprite || !this.sprite.hasAnimatedMedia) return 0;
        let fadeInAudio = this.data.fadeInAudio;
        if (this.actualCreationTime - (this.creationTimestamp + fadeInAudio.duration + fadeInAudio.delay) > 0)
          return;
        this.sprite.volume = 0;
        SequencerAnimationEngine.addAnimation(this.id, {
          target: this.sprite,
          propertyName: "volume",
          to: (this.data.volume ?? 0) * game.settings.get("core", "globalInterfaceVolume"),
          duration: fadeInAudio.duration,
          ease: fadeInAudio.ease,
          delay: fadeInAudio.delay,
          absolute: true
        });
        return fadeInAudio.duration + fadeInAudio.delay;
      }
      /**
       * Fades out the effect at the end of the effect's duration
       *
       * @returns {number|*}
       * @private
       */
      _fadeOut(immediate = false) {
        if (!this.data.fadeOut || !this.sprite) return 0;
        let fadeOut = this.data.fadeOut;
        fadeOut.delay = is_real_number(immediate) ? Math.max(immediate - fadeOut.duration + fadeOut.delay, 0) : Math.max(this._totalDuration - fadeOut.duration + fadeOut.delay, 0);
        SequencerAnimationEngine.addAnimation(this.id, {
          target: this,
          propertyName: "effectAlpha",
          to: 0,
          duration: fadeOut.duration,
          ease: fadeOut.ease,
          delay: fadeOut.delay,
          absolute: true
        });
        return fadeOut.duration + fadeOut.delay;
      }
      /**
       * Fades out the effect at the end of the effect's duration
       *
       * @returns {number|*}
       * @private
       */
      _fadeOutAudio(immediate = false) {
        if (!this.data.fadeOutAudio || !this.sprite || !this.sprite.hasAnimatedMedia) return 0;
        let fadeOutAudio = this.data.fadeOutAudio;
        fadeOutAudio.delay = is_real_number(immediate) ? Math.max(immediate - fadeOutAudio.duration + fadeOutAudio.delay, 0) : Math.max(
          this._totalDuration - fadeOutAudio.duration + fadeOutAudio.delay,
          0
        );
        setTimeout(() => {
          SequencerAnimationEngine.addAnimation(this.id, {
            target: this.sprite,
            propertyName: "volume",
            to: 0,
            duration: fadeOutAudio.duration,
            ease: fadeOutAudio.ease,
            delay: fadeOutAudio.delay,
            absolute: true
          });
        });
        return fadeOutAudio.duration + fadeOutAudio.delay;
      }
      /**
       * Determines the scale to animate from or to
       * @param property
       * @returns {{x: number, y: number}}
       * @private
       */
      _determineScale(property) {
        let scale2 = {
          x: this.sprite.scale.x,
          y: this.sprite.scale.y
        };
        if (is_real_number(property.value)) {
          scale2.x *= property.value * this.gridSizeDifference * this.flipX;
          scale2.y *= property.value * this.gridSizeDifference * this.flipY;
        } else {
          scale2.x *= property.value.x * this.gridSizeDifference * this.flipX;
          scale2.y *= property.value.y * this.gridSizeDifference * this.flipY;
        }
        return scale2;
      }
      /**
       * Scales the effect in at the start of the effect
       *
       * @returns {number|*}
       * @private
       */
      _scaleIn() {
        if (!this.data.scaleIn || !this.sprite) return 0;
        let scaleIn = this.data.scaleIn;
        let fromScale = this._determineScale(scaleIn);
        if (this.actualCreationTime - (this.creationTimestamp + scaleIn.duration + scaleIn.delay) > 0)
          return;
        let toScale = {
          x: this.sprite.scale.x,
          y: this.sprite.scale.y
        };
        this.sprite.scale.set(fromScale.x, fromScale.y);
        SequencerAnimationEngine.addAnimation(this.id, [
          {
            target: this.sprite,
            propertyName: "scale.x",
            from: fromScale.x,
            to: toScale.x,
            duration: scaleIn.duration,
            ease: scaleIn.ease,
            delay: scaleIn.delay,
            absolute: true
          },
          {
            target: this.sprite,
            propertyName: "scale.y",
            from: fromScale.y,
            to: toScale.y,
            duration: scaleIn.duration,
            ease: scaleIn.ease,
            delay: scaleIn.delay,
            absolute: true
          }
        ]);
        return scaleIn.duration + scaleIn.delay;
      }
      /**
       * Scales the effect out at the end of the effect's duration
       *
       * @returns {number|*}
       * @private
       */
      _scaleOut(immediate = false) {
        if (!this.data.scaleOut || !this.sprite) return 0;
        let scaleOut = this.data.scaleOut;
        let scale2 = this._determineScale(scaleOut);
        scaleOut.delay = is_real_number(immediate) ? Math.max(immediate - scaleOut.duration + scaleOut.delay, 0) : Math.max(
          this._totalDuration - scaleOut.duration + scaleOut.delay,
          0
        );
        SequencerAnimationEngine.addAnimation(this.id, [
          {
            target: this.sprite,
            propertyName: "scale.x",
            to: scale2.x,
            duration: scaleOut.duration,
            ease: scaleOut.ease,
            delay: scaleOut.delay,
            absolute: true
          },
          {
            target: this.sprite,
            propertyName: "scale.y",
            to: scale2.y,
            duration: scaleOut.duration,
            ease: scaleOut.ease,
            delay: scaleOut.delay,
            absolute: true
          }
        ]);
        return scaleOut.duration + scaleOut.delay;
      }
      /**
       * Rotates the effect in at the start of the effect
       *
       * @returns {number|*}
       * @private
       */
      _rotateIn() {
        if (!this.data.rotateIn || !this.sprite) return 0;
        let rotateIn = this.data.rotateIn;
        if (this.actualCreationTime - (this.creationTimestamp + rotateIn.duration + rotateIn.delay) > 0)
          return;
        let original_radians = this.spriteContainer.rotation;
        this.spriteContainer.rotation = rotateIn.value * (Math.PI / 180);
        SequencerAnimationEngine.addAnimation(
          this.id,
          this._counterAnimateRotation({
            target: this.spriteContainer,
            propertyName: "rotation",
            to: original_radians,
            duration: rotateIn.duration,
            ease: rotateIn.ease,
            delay: rotateIn.delay,
            absolute: true
          })
        );
        return rotateIn.duration + rotateIn.delay;
      }
      /**
       * Rotates the effect out at the end of the effect's duration
       *
       * @returns {number|*}
       * @private
       */
      _rotateOut(immediate = false) {
        if (!this.data.rotateOut || !this.sprite) return 0;
        let rotateOut = this.data.rotateOut;
        rotateOut.delay = is_real_number(immediate) ? Math.max(immediate - rotateOut.duration + rotateOut.delay, 0) : Math.max(
          this._totalDuration - rotateOut.duration + rotateOut.delay,
          0
        );
        SequencerAnimationEngine.addAnimation(
          this.id,
          this._counterAnimateRotation({
            target: this.spriteContainer,
            propertyName: "rotation",
            to: rotateOut.value * (Math.PI / 180),
            duration: rotateOut.duration,
            ease: rotateOut.ease,
            delay: rotateOut.delay,
            absolute: true
          })
        );
        return rotateOut.duration + rotateOut.delay;
      }
      /**
       * Causes the effect to move towards the given location
       *
       * @returns {number|*}
       * @private
       */
      _moveTowards() {
        if (!this.data.moves || !this.sprite) return 0;
        let moves2 = this.data.moves;
        let movementDuration = this._totalDuration;
        if (this.data.moveSpeed) {
          const distance = distance_between(
            this.sourcePosition,
            this.targetPosition
          );
          movementDuration = distance / this.data.moveSpeed * 1e3;
        }
        if (this.data.moves.rotate) this._rotateTowards();
        const duration = movementDuration - moves2.delay;
        if (this.actualCreationTime - (this.creationTimestamp + duration + moves2.delay) > 0)
          return;
        SequencerAnimationEngine.addAnimation(this.id, [
          {
            target: this,
            propertyName: "position.x",
            to: this.targetPosition.x,
            duration,
            ease: moves2.ease,
            delay: moves2.delay,
            absolute: true
          },
          {
            target: this,
            propertyName: "position.y",
            to: this.targetPosition.y,
            duration,
            ease: moves2.ease,
            delay: moves2.delay,
            absolute: true
          }
        ]);
        return duration + moves2.delay;
      }
      /**
       * If this effect is temporary, this sets the timeout for when the effect should resolve and get removed;
       *
       * @private
       */
      _setEndTimeout() {
        setTimeout(() => {
          this._resolve(this.data);
          this.endEffect();
        }, this._totalDuration);
      }
      _setupTimestampHook(offset2) {
        if (!this._file?.originalMetadata?.timestamps || this._ended) return;
        const timestamps = this._file.getTimestamps();
        const timestampArray = Array.isArray(timestamps) ? timestamps : [timestamps];
        for (const timestamp of timestampArray) {
          if (!is_real_number(timestamp)) continue;
          let realTimestamp = timestamp - offset2 / this.mediaPlaybackRate;
          if (realTimestamp < 0) {
            realTimestamp += this._endTime;
          }
          setTimeout(() => {
            if (this._ended) return;
            Hooks.callAll("sequencerEffectTimestamp", this, this._file);
            if (this.mediaLooping) {
              const offsets = (this._endTime - this.mediaCurrentTime) * -1e3;
              this._setupTimestampHook(offsets);
            }
          }, realTimestamp);
        }
      }
      /**
       * Starts the loop of this effect, calculating the difference between the effect's creation time, and the actual
       * creation time on the client
       *
       * @returns {Promise<void>}
       * @private
       */
      async _startEffect() {
        if (!this.hasAnimatedMedia) return;
        let creationTimeDifference = this.data.persist ? this.actualCreationTime - this.creationTimestamp : 0;
        creationTimeDifference *= this.mediaPlaybackRate;
        this._currentLoops = Math.floor(creationTimeDifference / this._totalDuration) + 1;
        if (this.loops && this._currentLoops > this.loops) {
          if (this.data.loopOptions?.endOnLastLoop || !this.data.persist) {
            return this.endEffect();
          }
          await this.pauseMedia();
          this.mediaCurrentTime = this._endTime;
          if (this.sprite.texture) {
            const oldRenderable = this.renderable;
            this.renderable = false;
            setTimeout(() => {
              this.updateTexture();
              setTimeout(() => {
                this.renderable ||= oldRenderable;
              }, 150);
            }, 150);
          }
          return;
        }
        return this._startLoop(creationTimeDifference);
      }
      /**
       * Kicks off the loop, or just sets the video to loop
       *
       * @param creationTimeDifference
       * @returns {Promise<void>}
       * @private
       */
      async _startLoop(creationTimeDifference) {
        if (!this._animationTimes.loopStart) {
          this._loopOffset = creationTimeDifference % this._animationDuration / 1e3;
        } else if (creationTimeDifference / 1e3 > this._animationTimes.loopStart) {
          const loopDuration = this._animationTimes.loopEnd - this._animationTimes.loopStart;
          this._loopOffset = creationTimeDifference % (loopDuration * 1e3) / 1e3;
        }
        if (this._loopOffset) {
          this.mediaCurrentTime = this._loopOffset;
        }
        await this.playMedia();
        this._addToTicker(this.loopHandler);
      }
      async loopHandler() {
        if (this._ended || this._isEnding) {
          return;
        }
        if (this.mediaCurrentTime < (this._animationTimes.loopEnd ?? this._endTime)) {
          return;
        }
        if (this.restartLoopHandler != null) {
          return;
        }
        this.pauseMedia();
        this.mediaCurrentTime = this._endTime;
        if ((this.loops || !this.data.persist) && this._currentLoops >= this.loops) {
          if (!this.data.persist || this.data.persist && this.data.loopOptions?.endOnLastLoop) {
            this.endEffect();
          }
          this._ticker?.remove(this.loopHandler, this);
          return;
        }
        const restartTime = this._startTime === 0 && this._animationTimes.loopStart ? this._animationTimes.loopStart : this._startTime;
        if (!this.loopDelay) {
          this._currentLoops++;
          this.mediaCurrentTime = restartTime;
          this.playMedia();
          return;
        }
        this._currentLoops++;
        this.restartLoopHandler = setTimeout(() => {
          this.restartLoopHandler = null;
          this.mediaCurrentTime = restartTime;
          this.playMedia();
        }, this.loopDelay);
      }
    }
    class PersistentCanvasEffect extends CanvasEffect {
      /** @OVERRIDE */
      _playPresetAnimations() {
        this._moveTowards();
        this._fadeIn();
        this._scaleIn();
        this._rotateIn();
      }
      /** @OVERRIDE */
      _timeoutVisibility() {
        let creationTimeDifference = this.actualCreationTime - this.creationTimestamp;
        let timeout = creationTimeDifference === 0 && !this.data.animations ? 0 : 50;
        setTimeout(() => {
          this._setupHooks();
        }, timeout);
      }
      /** @OVERRIDE */
      _setEndTimeout() {
        let creationTimeDifference = this.actualCreationTime - this.creationTimestamp;
        if (this.loops && creationTimeDifference >= this._totalDuration && this.hasAnimatedMedia) {
          setTimeout(() => {
            this.pauseMedia();
          }, this._totalDuration);
        }
      }
      /** @OVERRIDE */
      async endEffect() {
        if (this._isEnding) return;
        this._isEnding = true;
        let extraEndDuration = this.data.extraEndDuration ?? 0;
        this.mediaLooping = false;
        if (this._animationTimes?.forcedEnd) {
          this.mediaCurrentTime = this._animationTimes.forcedEnd;
          extraEndDuration += (this.mediaDuration - (this._animationTimes?.forcedEnd ?? 0)) * 1e3;
        } else if (this._animationTimes?.loopEnd) {
          extraEndDuration += (this.mediaDuration - this.mediaCurrentTime) * 1e3;
        }
        const fromEndCustomAnimations = this._getFromEndCustomAnimations(extraEndDuration);
        const durations = [
          this._fadeOut(extraEndDuration),
          this._fadeOutAudio(extraEndDuration),
          this._scaleOut(extraEndDuration),
          this._rotateOut(extraEndDuration),
          extraEndDuration,
          ...fromEndCustomAnimations.map(
            (animation2) => animation2.duration + animation2.delay
          )
        ].filter(Boolean);
        SequencerAnimationEngine.addAnimation(this.id, fromEndCustomAnimations);
        const waitDuration = Math.max(...durations, 0);
        this._resolve(waitDuration);
        return new Promise(
          (resolve) => setTimeout(() => {
            super.endEffect();
            resolve(this.data);
          }, waitDuration)
        );
      }
    }
    function createShape(shape) {
      const graphic = new PIXI.LegacyGraphics();
      graphic.id = "sequencer-graphics-" + foundry.utils.randomID();
      graphic.beginFill(
        shape?.isMask || !shape?.fillColor ? 16777215 : shape?.fillColor,
        shape?.isMask ? 1 : shape?.fillAlpha ?? 0
      );
      graphic.lineStyle(
        shape.lineSize ?? (shape?.isMask ? 1 : 0),
        shape?.lineColor ?? 16777215
      );
      const offsetX = (shape.offset?.x ?? 0) * (shape.offset?.gridUnits ? canvas.grid.size : 1);
      const offsetY = (shape.offset?.y ?? 0) * (shape.offset?.gridUnits ? canvas.grid.size : 1);
      const sizeMultiplier = shape.gridUnits ? canvas.grid.size : 1;
      graphic.offset = {
        x: offsetX,
        y: offsetY
      };
      switch (shape.type) {
        case CONSTANTS.SHAPES.CIRC:
          graphic.drawCircle(
            graphic.offset.x,
            graphic.offset.y,
            shape.radius * sizeMultiplier
          );
          break;
        case CONSTANTS.SHAPES.RECT:
          graphic.drawRect(
            graphic.offset.x,
            graphic.offset.y,
            shape.width * sizeMultiplier,
            shape.height * sizeMultiplier
          );
          break;
        case CONSTANTS.SHAPES.ELIP:
          graphic.drawEllipse(
            graphic.offset.x,
            graphic.offset.y,
            shape.width * sizeMultiplier,
            shape.height * sizeMultiplier
          );
          break;
        case CONSTANTS.SHAPES.RREC:
          graphic.drawRoundedRect(
            graphic.offset.x,
            graphic.offset.y,
            shape.width * sizeMultiplier,
            shape.height * sizeMultiplier,
            shape.radius * sizeMultiplier
          );
          break;
        case CONSTANTS.SHAPES.POLY:
          graphic.drawPolygon(
            shape.points.map((point) => {
              return new PIXI.Point(
                point[0] * sizeMultiplier,
                point[1] * sizeMultiplier
              );
            })
          );
          break;
      }
      graphic.alpha = shape.alpha ?? 1;
      graphic.endFill();
      if (shape.anchor) {
        graphic.pivot.set(
          shape.width * (shape.anchor.x ?? 0),
          shape.height * (shape.anchor.y ?? 0)
        );
      }
      return graphic;
    }
    function calculate_missed_position(source, target, twister) {
      const sourcePosition = get_object_position(source);
      const sourceDimensions = get_object_dimensions(source, true);
      if (!target) {
        const angle2 = twister.random() * Math.PI * 2;
        let x2 = Math.cos(angle2) * sourceDimensions.width;
        let y2 = Math.sin(angle2) * sourceDimensions.height;
        return {
          x: random_float_between(x2 * 1.5, x2 * 2.5, twister),
          y: random_float_between(y2 * 1.5, y2 * 2.5, twister)
        };
      }
      const targetDimensions = get_object_dimensions(target, true);
      const targetPosition = get_object_position(target);
      const ray = new Ray(targetPosition, sourcePosition);
      let startRadians = ray.angle + Math.PI / 2;
      let endRadians = ray.angle - Math.PI / 2;
      const sizeCompensation = Math.max(
        1,
        Math.abs(sourceDimensions.width - targetDimensions.height)
      );
      let distance = ray.distance / canvas.grid.size - sizeCompensation;
      if (distance <= 1) {
        const angle2 = twister.random() > 0.5 ? ray.angle + Math.PI / 4 : ray.angle - Math.PI / 4;
        const x2 = Math.cos(angle2) * targetDimensions.width;
        const y2 = Math.sin(angle2) * targetDimensions.height;
        return { x: x2, y: y2 };
      }
      distance = Math.max(Math.abs(distance - 15), 6);
      endRadians -= Math.PI / distance;
      startRadians += Math.PI / distance;
      const angle = interpolate(startRadians, endRadians, twister.random());
      const x = Math.cos(angle) * targetDimensions.width;
      const y = Math.sin(angle) * targetDimensions.height;
      return {
        x: random_float_between(x * 1.5, x * 2.5, twister),
        y: random_float_between(y * 1.5, y * 2.5, twister)
      };
    }
    function get_object_position(obj, { measure = false, exact = false } = {}) {
      if (obj instanceof CanvasEffect) {
        return obj.worldPosition;
      }
      obj = obj?._object ?? obj.object ?? obj;
      let pos = {};
      if (obj instanceof MeasuredTemplate) {
        if (measure) {
          if (obj.document.t === "cone" || obj.document.t === "ray") {
            pos.x = obj.ray.B.x;
            pos.y = obj.ray.B.y;
          }
        }
        if (obj.document.t === "rect") {
          pos.x = obj.x;
          pos.y = obj.y;
          if (!exact) {
            pos.x += Math.abs(obj.shape.width / 2) + obj.shape.x;
            pos.y += Math.abs(obj.shape.height / 2) + obj.shape.y;
          }
        }
      } else if (obj instanceof Tile) {
        pos = {
          x: obj.document.x,
          y: obj.document.y
        };
        if (!exact) {
          pos.x += Math.abs(obj.document.width / 2);
          pos.y += Math.abs(obj.document.height / 2);
        }
      } else if (obj instanceof Token) {
        const halfSize = get_object_dimensions(obj, true);
        pos = {
          x: obj.x + halfSize.width,
          y: obj.y + halfSize.height
        };
        if (exact) {
          pos.x -= halfSize.width;
          pos.y -= halfSize.height;
        }
      } else if (obj instanceof Drawing) {
        pos = {
          x: obj.document.x,
          y: obj.document.y
        };
        if (!exact) {
          const halfSize = get_object_dimensions(obj, true);
          pos.x += halfSize.width;
          pos.y += halfSize.height;
        }
      }
      pos = {
        x: pos.x ?? obj?.x ?? obj?.position?.x ?? obj?.position?._x ?? obj?.document?.x ?? obj?.document?.position?.x ?? null,
        y: pos.y ?? obj?.y ?? obj?.position?.y ?? obj?.position?._y ?? obj?.document?.y ?? obj?.document?.position?.y ?? null,
        elevation: obj?.elevation ?? obj?.document?.elevation ?? null
      };
      if (pos.x === null) delete pos["x"];
      if (pos.y === null) delete pos["y"];
      if (pos.elevation === null) delete pos["elevation"];
      return pos;
    }
    function get_random_offset(target, randomOffset, twister = false) {
      let { width: width2, height } = get_object_dimensions(target, true);
      width2 *= randomOffset;
      height *= randomOffset;
      return {
        x: random_float_between(width2 * -1, width2, twister),
        y: random_float_between(height * -1, height, twister)
      };
    }
    function get_object_dimensions(inObj, half = false) {
      inObj = inObj?.object ?? inObj?._object ?? inObj;
      let width2 = inObj?.hitArea?.width ?? inObj?.w ?? inObj?.shape?.width ?? (inObj?.shape?.radius ? inObj?.shape?.radius * 2 : void 0) ?? inObj?.bounds?.width ?? inObj?.width ?? canvas.grid.size;
      let height = inObj?.hitArea?.height ?? inObj?.h ?? inObj?.shape?.height ?? (inObj?.shape?.radius ? inObj?.shape?.radius * 2 : void 0) ?? inObj?.bounds?.height ?? inObj?.height ?? canvas.grid.size;
      return {
        width: width2 / (half ? 2 : 1),
        height: height / (half ? 2 : 1)
      };
    }
    const alignments = {
      "top-left": { x: 0.5, y: 0.5 },
      top: { x: 0, y: 0.5 },
      "top-right": { x: -0.5, y: 0.5 },
      left: { x: 0.5, y: 0 },
      center: { x: 0, y: 0 },
      right: { x: -0.5, y: 0 },
      "bottom-left": { x: 0.5, y: -0.5 },
      bottom: { x: 0, y: -0.5 },
      "bottom-right": { x: -0.5, y: -0.5 }
    };
    function align({
      context,
      spriteWidth,
      spriteHeight,
      align: align2,
      edge
    } = {}) {
      let { width: width2, height } = get_object_dimensions(context);
      const alignRatio = alignments[align2];
      const offset2 = {
        x: interpolate(width2 * -0.5, width2 * 0.5, alignRatio.x + 0.5),
        y: interpolate(height * -0.5, height * 0.5, alignRatio.y + 0.5)
      };
      return {
        x: offset2.x + (edge && edge !== "on" ? spriteWidth * alignRatio.x * (edge === "outer" ? 1 : -1) : 0),
        y: offset2.y + (edge && edge !== "on" ? spriteHeight * alignRatio.y * (edge === "outer" ? 1 : -1) : 0)
      };
    }
    function is_object_canvas_data(inObj) {
      if (typeof inObj !== "object") return false;
      const keys = Object.keys(inObj);
      keys.sort();
      return keys.includes("x") && keys.includes("y") || keys.includes("height") && keys.includes("width") && keys.includes("x") && keys.includes("y");
    }
    function get_object_canvas_data(inObject, { measure = false, uuid = true } = {}) {
      inObject = inObject?.object ?? inObject;
      const data = {
        ...get_object_position(inObject, { measure }),
        ...get_object_dimensions(inObject?.mesh ?? inObject?.tile ?? inObject),
        elevation: get_object_elevation(inObject),
        cachedLocation: true
      };
      if (uuid) {
        data["uuid"] = inObject?.document?.uuid ?? inObject?.uuid;
      }
      return data;
    }
    function get_object_elevation(inObject) {
      return inObject?.document?.elevation ?? inObject?.elevation ?? 0;
    }
    function get_mouse_position(snapToGrid = false) {
      const pos = getCanvasMouse().getLocalPosition(canvas.app.stage);
      const snappingOptions = CONSTANTS.IS_V12 ? {
        mode: CONST.GRID_SNAPPING_MODES.CENTER | CONST.GRID_SNAPPING_MODES.VERTEX | CONST.GRID_SNAPPING_MODES.EDGE_MIDPOINT
      } : 2;
      return !snapToGrid ? new PIXI.Point(pos.x, pos.y) : canvas.grid.getSnappedPoint({ x: pos.x, y: pos.y }, snappingOptions);
    }
    function distance_between(p1, p2) {
      return new Ray(p1, p2).distance;
    }
    function is_position_within_bounds(inPosition, inElement, relativeTo) {
      const localPosition = inElement.toLocal(inPosition, relativeTo);
      return inElement.getLocalBounds().contains(localPosition.x, localPosition.y);
    }
    function rotate_coordinate(p1, p2, radians) {
      let cos = Math.cos(radians);
      let sin = Math.sin(radians);
      let nx = cos * (p2.x - p1.x) + sin * (p2.y - p1.y) + p1.x;
      let ny = cos * (p2.y - p1.y) - sin * (p2.x - p1.x) + p1.y;
      return [nx, ny];
    }
    function get_closest_token(inPosition, { minimumDistance = false } = {}) {
      let tokens = Array.from(canvas.scene.tokens);
      if (minimumDistance) {
        tokens = tokens.filter(
          (token) => distance_between(get_object_position(token), inPosition) <= minimumDistance
        );
      }
      tokens.sort((a, b) => {
        return distance_between(get_object_position(a), inPosition) - distance_between(get_object_position(b), inPosition);
      });
      return tokens?.[0] ?? false;
    }
    function rotateAroundPoint(cx, cy, x, y, radians) {
      const cos = Math.cos(radians);
      const sin = Math.sin(radians);
      const nx = cos * (x - cx) + sin * (y - cy) + cx;
      const ny = cos * (y - cy) - sin * (x - cx) + cy;
      return { x: nx, y: ny };
    }
    function validateAnimation(inTarget, inPropertyName, inOptions) {
      if (typeof inPropertyName !== "string") {
        return `inPropertyName must be of type string`;
      }
      if (typeof inTarget !== "string") {
        return `inTarget must be of type string`;
      }
      if (!is_real_number(inOptions.from)) {
        return `inOptions.from must be of type number`;
      }
      if (!is_real_number(inOptions.to)) {
        return `inOptions.to must be of type number`;
      }
      if (!is_real_number(inOptions.duration)) {
        return `inOptions.duration must be of type number`;
      }
      if (inOptions?.delay !== void 0 && !is_real_number(inOptions.delay)) {
        return `inOptions.delay must be of type number`;
      }
      if (inOptions?.ease !== void 0 && typeof inOptions.ease !== "string") {
        return `inOptions.ease must be of type string`;
      }
      if (inOptions?.fromEnd !== void 0 && typeof inOptions.fromEnd !== "boolean") {
        return `inOptions.fromEnd must be of type boolean`;
      }
      if (inOptions?.screenSpace !== void 0 && typeof inOptions.screenSpace !== "boolean") {
        return `inOptions.screenSpace must be of type boolean`;
      }
      if (inOptions?.gridUnits !== void 0) {
        if (typeof inOptions.gridUnits !== "boolean") {
          return `inOptions.gridUnits must be of type boolean`;
        }
        if (inOptions.gridUnits && ![
          "position.x",
          "position.y",
          "scale.x",
          "scale.y",
          "height",
          "width"
        ].includes(inPropertyName)) {
          return `if inOptions.gridUnits is true, inPropertyName must be position.x, position.y, scale.x, scale.y, width, or height`;
        }
      }
      if (inOptions?.absolute !== void 0 && typeof inOptions.absolute !== "boolean") {
        return `inOptions.absolute must be of type boolean`;
      }
      return {
        target: inTarget,
        propertyName: inPropertyName,
        from: inOptions?.from,
        to: inOptions?.to,
        duration: inOptions?.duration ?? 0,
        delay: inOptions?.delay ?? 0,
        ease: inOptions?.ease ?? "linear",
        looping: false,
        fromEnd: inOptions?.fromEnd ?? false,
        gridUnits: inOptions?.gridUnits ?? false,
        absolute: inOptions?.absolute ?? false,
        screenSpace: inOptions?.screenSpace ?? false
      };
    }
    function validateLoopingAnimation(inTarget, inPropertyName, inOptions) {
      if (typeof inPropertyName !== "string") {
        return `inPropertyName must be of type string`;
      }
      if (typeof inTarget !== "string") {
        return `inTarget must be of type string`;
      }
      if (!inOptions?.values) {
        if (!inOptions?.from === void 0 || !inOptions?.to === void 0) {
          return `if inOptions.values is not set, you must provide inOptions.from and inOptions.to`;
        }
        if (!is_real_number(inOptions.from)) {
          return `inOptions.from must be of type number`;
        }
        if (!is_real_number(inOptions.to)) {
          return `inOptions.to must be of type number`;
        }
        inOptions.values = [inOptions?.from, inOptions?.to];
        delete inOptions.from;
        delete inOptions.to;
      } else {
        if (!Array.isArray(inOptions.values)) {
          return `inOptions.values must be of type array`;
        }
        inOptions.values.forEach((value) => {
          if (!is_real_number(value)) {
            return `values in inOptions.keys must be of type number`;
          }
        });
      }
      if (!is_real_number(inOptions.duration)) {
        return `inOptions.duration must be of type number`;
      }
      if (inOptions?.delay !== void 0 && !is_real_number(inOptions.delay)) {
        return `inOptions.delay must be of type number`;
      }
      if (inOptions?.ease !== void 0 && typeof inOptions.ease !== "string") {
        return `inOptions.ease must be of type string`;
      }
      if (inOptions?.loops !== void 0 && !is_real_number(inOptions.loops)) {
        return `inOptions.loops must be of type number`;
      }
      if (inOptions?.pingPong !== void 0 && typeof inOptions.pingPong !== "boolean") {
        return `inOptions.pingPong must be of type boolean`;
      }
      if (inOptions?.screenSpace !== void 0 && typeof inOptions.screenSpace !== "boolean") {
        return `inOptions.screenSpace must be of type boolean`;
      }
      if (inOptions?.gridUnits !== void 0) {
        if (typeof inOptions.gridUnits !== "boolean") {
          return `inOptions.gridUnits must be of type boolean`;
        }
        if (inOptions.gridUnits && ![
          "position.x",
          "position.y",
          "scale.x",
          "scale.y",
          "height",
          "width"
        ].includes(inPropertyName)) {
          return `if inOptions.gridUnits is true, inPropertyName must be position.x, position.y, scale.x, scale.y, width, or height`;
        }
      }
      return {
        target: inTarget,
        propertyName: inPropertyName,
        values: inOptions?.values,
        duration: inOptions?.duration ?? 0,
        delay: inOptions?.delay ?? 0,
        ease: inOptions?.ease ?? "linear",
        looping: true,
        loops: inOptions?.loops,
        indefinite: inOptions?.loops === void 0 || !is_real_number(inOptions?.loops),
        pingPong: inOptions?.pingPong ?? false,
        gridUnits: inOptions?.gridUnits ?? false,
        screenSpace: inOptions?.screenSpace ?? false
      };
    }
    const PlayerSettings = {
      file: {
        label: "SEQUENCER.Player.Option.File",
        store: writable(""),
        default: ""
      },
      scale: {
        label: "SEQUENCER.Player.Option.Scale",
        store: writable(1),
        default: 1
      },
      users: {
        label: "SEQUENCER.Player.Option.ForUsers",
        store: writable([]),
        default: []
      },
      belowTokens: {
        label: "SEQUENCER.Player.Option.BelowTokens",
        store: writable(false),
        default: false
      },
      snapToGrid: {
        label: "SEQUENCER.Player.Option.SnapToGrid",
        store: writable(false),
        default: false
      },
      rotation: {
        label: "SEQUENCER.Player.Option.Rotation",
        store: writable(0),
        default: 0
      },
      randomRotation: {
        label: "SEQUENCER.Player.Option.Randomize",
        store: writable(false),
        default: false
      },
      fadeIn: {
        label: "SEQUENCER.Player.Option.FadeIn",
        store: writable(0),
        default: 0
      },
      fadeOut: {
        label: "SEQUENCER.Player.Option.FadeOut",
        store: writable(0),
        default: 0
      },
      scaleIn: {
        label: "SEQUENCER.Player.Option.ScaleIn",
        store: writable(0),
        default: 0
      },
      scaleOut: {
        label: "SEQUENCER.Player.Option.ScaleOut",
        store: writable(0),
        default: 0
      },
      mirrorX: {
        label: "SEQUENCER.Player.Option.MirrorX",
        store: writable(false),
        default: false
      },
      mirrorY: {
        label: "SEQUENCER.Player.Option.MirrorY",
        store: writable(false),
        default: false
      },
      randomMirrorX: {
        label: "SEQUENCER.Player.Option.Randomize",
        store: writable(false),
        default: false
      },
      randomMirrorY: {
        label: "SEQUENCER.Player.Option.Randomize",
        store: writable(false),
        default: false
      },
      offsetX: {
        label: "SEQUENCER.Player.Option.OffsetX",
        store: writable(0),
        default: 0
      },
      offsetY: {
        label: "SEQUENCER.Player.Option.OffsetY",
        store: writable(0),
        default: 0
      },
      offsetGridUnits: {
        label: "SEQUENCER.Player.Option.GridUnits",
        store: writable(false),
        default: false
      },
      randomOffsetAmount: {
        label: "SEQUENCER.Player.Option.RandomOffset",
        store: writable(0),
        default: 0
      },
      randomOffset: {
        label: "SEQUENCER.Player.Option.Randomize",
        store: writable(false),
        default: false
      },
      preload: {
        label: "SEQUENCER.Player.Option.Preload",
        store: writable(false),
        default: false
      },
      moveTowards: {
        label: "SEQUENCER.Player.Option.DragBehavior",
        label_off: "SEQUENCER.Player.Option.DragStretch",
        label_on: "SEQUENCER.Player.Option.DragMove",
        store: writable(false),
        default: false
      },
      moveSpeed: {
        label: "SEQUENCER.Player.Option.MoveSpeed",
        store: writable(0),
        default: 0
      },
      attachTo: {
        label: "SEQUENCER.Player.Option.AttachTo",
        store: writable(false),
        default: false,
        callback: (e) => {
          EffectPlayer.sourceAttach = e.target.checked;
        }
      },
      stretchToAttach: {
        label: "SEQUENCER.Player.Option.StretchToAttach",
        store: writable(false),
        default: false,
        callback: (e) => {
          EffectPlayer.targetAttach = e.target.checked;
        }
      },
      persist: {
        label: "SEQUENCER.Player.Option.Persist",
        store: writable(false),
        default: false
      },
      repeat: {
        label: "SEQUENCER.Player.Option.Repeat",
        store: writable(false),
        default: false
      },
      repetitions: {
        label: "SEQUENCER.Player.Option.Repetitions",
        store: writable(1),
        default: 1
      },
      repeatDelayMin: {
        label: "SEQUENCER.Player.Option.DelayMin",
        store: writable(200),
        default: 200
      },
      repeatDelayMax: {
        label: "SEQUENCER.Player.Option.DelayMax",
        store: writable(400),
        default: 400
      }
    };
    PlayerSettings.export = () => {
      return Object.fromEntries(
        Object.entries(PlayerSettings).map((entry) => {
          return [entry[0], get_store_value(entry[1].store)];
        })
      );
    };
    PlayerSettings.import = (settings) => {
      Object.entries(PlayerSettings).forEach((entry) => {
        if (settings?.[entry[0]] !== void 0) {
          entry[1].store.set(settings?.[entry[0]]);
        } else if (entry[1]?.default !== void 0) {
          entry[1].store.set(entry[1].default);
        }
      });
    };
    PlayerSettings.getPresets = () => {
      return Object.keys(game.settings.get(CONSTANTS.MODULE_NAME, "effectPresets"));
    };
    PlayerSettings.loadPreset = (name2) => {
      const effectPresets = game.settings.get(
        CONSTANTS.MODULE_NAME,
        "effectPresets"
      );
      return PlayerSettings.import(effectPresets[name2]);
    };
    PlayerSettings.savePreset = async (name2) => {
      const newName = await promptNewPresetName(name2);
      if (!newName) return;
      const effectPresets = game.settings.get(
        CONSTANTS.MODULE_NAME,
        "effectPresets"
      );
      effectPresets[newName] = PlayerSettings.export();
      return game.settings.set(
        CONSTANTS.MODULE_NAME,
        "effectPresets",
        effectPresets
      );
    };
    PlayerSettings.copyPreset = async (name2) => {
      const newName = await promptNewPresetName(name2, true);
      if (!newName) return;
      const effectPresets = game.settings.get(
        CONSTANTS.MODULE_NAME,
        "effectPresets"
      );
      effectPresets[newName] = PlayerSettings.export();
      return game.settings.set(
        CONSTANTS.MODULE_NAME,
        "effectPresets",
        effectPresets
      );
    };
    PlayerSettings.deletePreset = (name2) => {
      const effectPresets = game.settings.get(
        CONSTANTS.MODULE_NAME,
        "effectPresets"
      );
      delete effectPresets[name2];
      return game.settings.set(
        CONSTANTS.MODULE_NAME,
        "effectPresets",
        effectPresets
      );
    };
    async function promptNewPresetName(inName, copy = false) {
      const effectPresets = game.settings.get(
        CONSTANTS.MODULE_NAME,
        "effectPresets"
      );
      let title = copy ? game.i18n.localize("SEQUENCER.Player.CopyPresetTitle") : game.i18n.localize("SEQUENCER.Player.CreateNewPresetTitle");
      let presetName = await new Promise((resolve) => {
        new Dialog({
          title,
          content: `<p><input type="text" placeholder="${game.i18n.localize(
            "SEQUENCER.Player.CreateNewPresetInputLabel"
          )}" id="newPresetName" style="width:100%;"></p>`,
          buttons: {
            okay: {
              icon: '<i class="fas fa-check"></i>',
              label: game.i18n.localize("SEQUENCER.OK"),
              callback: async (html) => {
                let name2 = html.find("#newPresetName").val();
                if (name2 === "" || !name2) {
                  name2 = false;
                }
                resolve(name2);
              }
            },
            cancel: {
              icon: '<i class="fas fa-times"></i>',
              label: game.i18n.localize("SEQUENCER.Cancel"),
              callback: () => {
                resolve(false);
              }
            }
          },
          close: () => {
          },
          render: (html) => {
            html.find("#newPresetName").val(inName).focus();
          }
        }).render(true);
      });
      if (presetName) {
        if (presetName.toLowerCase() === "default") {
          Dialog.prompt({
            title: game.i18n.localize("SEQUENCER.Player.DefaultErrorTitle"),
            content: `<p>${game.i18n.localize(
              "SEQUENCER.Player.DefaultErrorContent"
            )}</p>`,
            label: game.i18n.localize("SEQUENCER.OK"),
            callback: () => {
            }
          });
          return false;
        }
        if (effectPresets[presetName]) {
          const overwrite = await Dialog.confirm({
            title: game.i18n.localize("SEQUENCER.Player.OverwritePresetTitle"),
            content: `<p>${game.i18n.format(
              "SEQUENCER.Player.OverwritePresetContent",
              { preset_name: presetName }
            )}</p>`
          });
          if (!overwrite) {
            presetName = await promptPresetName(presetName);
          }
        }
      }
      return presetName;
    }
    PlayerSettings.migrateOldPresets = () => {
      if (!game.user.isGM) return;
      const effectPresets = game.settings.get(
        CONSTANTS.MODULE_NAME,
        "effectPresets"
      );
      const presetsToUpdate = Object.values(effectPresets).filter((preset) => {
        return !preset?.version;
      });
      if (!presetsToUpdate.length) return;
      const newEffectPresets = Object.fromEntries(
        Object.entries(effectPresets).map(([name2, preset]) => {
          if (preset?.version) return [name2, preset];
          preset.version = game.modules.get(CONSTANTS.MODULE_NAME).version;
          if (preset.repetitions > 1) {
            preset.repeat = true;
          }
          if (preset.randomMirrorY) {
            preset.mirrorY = true;
          }
          if (preset.randomOffset) {
            preset.randomOffsetAmount = 1;
            preset.offsetGridUnits = true;
          }
          return [name2, preset];
        })
      );
      return game.settings.set(
        CONSTANTS.MODULE_NAME,
        "effectPresets",
        newEffectPresets
      );
    };
    const InteractionManager = {
      startDragPosition: false,
      state: {
        LeftMouseDown: false,
        RightMouseDown: false,
        Dragging: false
      },
      get isLayerActive() {
        return canvas.sequencerInterfaceLayer.active;
      },
      initialize() {
        window.addEventListener("mousedown", (event) => {
          if (!canvas.ready) return;
          if (!this.isLayerActive) return;
          const hover = document.elementFromPoint(event.clientX, event.clientY);
          if (!hover || hover.id !== "board") return;
          const button = event.button;
          if (!(button === 0 || button === 2)) return;
          if (button === 0) {
            this.state.LeftMouseDown = true;
            this._propagateEvent("mouseLeftDown");
          }
          if (button === 2) {
            this.state.RightMouseDown = true;
            this._propagateEvent("mouseRightDown");
          }
        });
        window.addEventListener("mouseup", (event) => {
          if (!canvas.ready) return;
          if (!this.isLayerActive) return;
          const hover = document.elementFromPoint(event.clientX, event.clientY);
          if (!hover || hover.id !== "board") return;
          if (document.activeElement.tagName !== "BODY") return;
          const button = event.button;
          if (!(button === 0 || button === 2)) return;
          if (button === 0) {
            this.state.LeftMouseDown = false;
            this._propagateEvent("mouseLeftUp");
            this.state.Dragging = false;
            this.startDragPosition = false;
          }
          if (button === 2) {
            this.state.RightMouseDown = false;
            this._propagateEvent("mouseRightUp");
            this.state.Dragging = false;
            this.startDragPosition = false;
          }
        });
        window.addEventListener("mousemove", (event) => {
          if (!canvas.ready) return;
          if (!this.isLayerActive) return;
          const hover = document.elementFromPoint(event.clientX, event.clientY);
          if (!hover || hover.id !== "board") return;
          this._propagateEvent("mouseMove");
          if (this.state.LeftMouseDown && !this.startDragPosition) {
            this.startDragPosition = get_mouse_position();
          }
          if (this.state.LeftMouseDown && !this.state.Dragging) {
            const distance = distance_between(
              this.startDragPosition,
              get_mouse_position()
            );
            this.state.Dragging = distance > 20;
          }
        });
        EffectPlayer.initialize();
        SelectionManager.initialize();
      },
      tearDown() {
        EffectPlayer.tearDown();
        SelectionManager.tearDown();
      },
      _propagateEvent(eventName) {
        if (EffectPlayer.isActive && EffectPlayer[eventName]) {
          EffectPlayer[eventName]();
        }
        if (SelectionManager.isActive && SelectionManager[eventName]) {
          SelectionManager[eventName]();
        }
      }
    };
    const EffectPlayer = {
      sequenceBuffer: [],
      playMany: false,
      playManySequenced: false,
      cursorPos: false,
      startPos: false,
      endPos: false,
      get snapLocationToGrid() {
        return get_store_value(PlayerSettings.snapToGrid.store);
      },
      get sourceAttach() {
        return get_store_value(PlayerSettings.attachTo.store);
      },
      get targetAttach() {
        return get_store_value(PlayerSettings.stretchToAttach.store);
      },
      sourceAttachFound: false,
      targetAttachFound: false,
      get isActive() {
        return InteractionManager.isLayerActive && game?.activeTool === "play-effect";
      },
      /**
       * Opens the Sequencer Effects UI with the player tab open
       */
      show() {
        return EffectsUIApp.show({ tab: "player" });
      },
      initialize() {
        this.layer = canvas.sequencerInterfaceLayer;
      },
      tearDown() {
        this._reset();
      },
      /**
       * Mouse events
       */
      mouseLeftDown() {
        this._evaluateStartPosition();
      },
      mouseLeftUp() {
        if (!this.startPos) return;
        this._playEffect();
        this.startPos = false;
        this.endPos = false;
        this.sourceAttachFound = false;
        this.targetAttachFound = false;
      },
      mouseRightUp() {
        this._reset();
      },
      mouseMove() {
        this._evaluateCursorPosition();
        if (InteractionManager.state.Dragging) {
          this._evaluateEndPosition();
        }
      },
      /**
       * Hotkeys
       */
      playManyUp() {
        this._playEffects();
        this._reset();
      },
      /**
       * Private methods
       */
      _evaluatePosition(attach = false) {
        let position = get_mouse_position(this.snapLocationToGrid);
        const attachToObject = attach ? get_closest_token(position, {
          minimumDistance: canvas.grid.size
        }) : false;
        let attachFound = false;
        if (attachToObject) {
          attachFound = true;
          position = get_object_position(attachToObject);
        }
        return [position, attachFound];
      },
      _evaluateCursorPosition() {
        const attach = InteractionManager.state.Dragging ? this.targetAttach : this.sourceAttach;
        [this.cursorPos] = this._evaluatePosition(attach);
      },
      _evaluateStartPosition() {
        if (this.startPos) return;
        [this.startPos, this.sourceAttachFound] = this._evaluatePosition(
          this.sourceAttach
        );
      },
      _evaluateEndPosition() {
        [this.endPos, this.targetAttachFound] = this._evaluatePosition(
          this.targetAttach
        );
      },
      _reset() {
        if (!this.layer) return;
        this.startPos = false;
        this.endPos = false;
        this.sourceAttachFound = false;
        this.targetAttachFound = false;
        this.sequenceBuffer = [];
        this._evaluateCursorPosition();
      },
      async _playEffect() {
        const settings = foundry.utils.mergeObject(PlayerSettings.export(), {
          ...InteractionManager.state,
          startPos: this.startPos,
          endPos: this.endPos
        });
        if (!settings.users.length || settings.users?.[0] === "all")
          settings.users = [];
        if (settings.file === "") return;
        if (!(Sequencer.Database.entryExists(settings.file) || await SequencerFileCache.srcExists(settings.file))) {
          throw custom_error(
            "Sequencer",
            `Sequencer Player | Could not find file or database entry: ${settings.file}`
          );
        }
        if (settings.preload) {
          await Sequencer.Preloader.preloadForClients(settings.file);
        }
        const sequence = this.sequenceBuffer.length > 0 && this.playManySequenced ? this.sequenceBuffer[this.sequenceBuffer.length - 1] : new Sequence();
        const effect = sequence.effect().file(settings.file).forUsers(settings.users).mirrorX(settings.mirrorX && !settings.randomMirrorX).mirrorY(settings.mirrorY && !settings.randomMirrorY).randomizeMirrorX(settings.randomMirrorX).randomizeMirrorY(settings.randomMirrorY).persist(settings.persist).belowTokens(settings.belowTokens);
        if (settings.repeat) {
          effect.repeats(
            settings.repetitions,
            settings.repeatDelayMin,
            settings.repeatDelayMax
          );
        }
        if (settings.fadeIn > 0) effect.fadeIn(settings.fadeIn);
        if (settings.fadeOut > 0) effect.fadeOut(settings.fadeOut);
        const offsetData = settings.randomOffset ? {
          offset: { x: settings.offsetX, y: settings.offsetY },
          gridUnits: settings.offsetGridUnits
        } : {};
        const offsetSource = !settings.Dragging ? offsetData : {};
        const attachToObject = settings.attachTo ? get_closest_token(settings.startPos, {
          minimumDistance: canvas.grid.size
        }) : false;
        if (attachToObject) {
          effect.attachTo(attachToObject, {
            randomOffset: settings.randomOffset && !settings.Dragging ? settings.randomOffsetAmount : false,
            ...offsetSource
          });
        } else {
          effect.atLocation(settings.startPos, {
            randomOffset: settings.randomOffset && !settings.Dragging ? settings.randomOffsetAmount : false,
            ...offsetSource
          });
        }
        if (settings.persist && settings.name && settings.name !== "" && settings.name !== "default" && settings.name !== "new") {
          effect.name("Preset: " + settings.name);
        }
        if (settings.Dragging) {
          if (settings.moveTowards) {
            effect.moveTowards(settings.endPos, {
              randomOffset: settings.randomOffset ? settings.randomOffsetAmount : false,
              ...offsetData
            });
            if (settings.moveSpeed) {
              effect.moveSpeed(settings.moveSpeed);
            }
          } else {
            let target = settings.stretchToAttach ? get_closest_token(settings.endPos, {
              minimumDistance: canvas.grid.size
            }) : settings.endPos;
            effect.stretchTo(target, {
              attachTo: settings.stretchToAttach,
              randomOffset: settings.randomOffset ? settings.randomOffsetAmount : false,
              ...offsetData
            });
          }
        }
        if (!settings.Dragging || settings.Dragging && settings.moveTowards) {
          effect.scale(settings.scale);
          if (settings.scaleIn > 0)
            effect.scaleIn(0, settings.scaleIn, { ease: "easeInOutSine" });
          if (settings.scaleOut > 0)
            effect.scaleOut(0, settings.scaleOut, { ease: "easeInOutSine" });
          effect.randomRotation(settings.randomRotation);
        }
        if (this.playManySequenced) {
          effect.waitUntilFinished();
        }
        if (!this.playManySequenced || this.sequenceBuffer.length === 0) {
          this.sequenceBuffer.push(sequence);
        }
        if (!this.playMany && !this.playManySequenced) this._playEffects();
      },
      _playEffects() {
        this.sequenceBuffer.forEach((sequence) => sequence.play());
        this.sequenceBuffer = [];
      }
    };
    const SelectionManager = {
      selectedEffect: false,
      hoveredEffects: /* @__PURE__ */ new Set(),
      suggestedProperties: false,
      sourceOrTarget: false,
      dragOffset: false,
      hoveredEffectUI: false,
      get snapToGrid() {
        return get_store_value(PlayerSettings.snapToGrid.store);
      },
      set snapToGrid(bool) {
        PlayerSettings.snapToGrid.store.set(bool);
      },
      set attachToTarget(bool) {
        PlayerSettings.stretchToAttach.store.set(bool);
      },
      get attachToTarget() {
        return get_store_value(PlayerSettings.stretchToAttach.store);
      },
      get isActive() {
        return InteractionManager.isLayerActive && game?.activeTool === "select-effect";
      },
      get effects() {
        return SequencerEffectManager.effects.filter(
          (effect) => effect.userCanDelete
        );
      },
      initialize() {
        this.layer = canvas.sequencerInterfaceLayer;
      },
      tearDown() {
        this._reset();
        this.hoveredEffects = /* @__PURE__ */ new Set();
      },
      sourcePointSelected() {
        this.sourceOrTarget = "source";
      },
      targetPointSelected() {
        this.sourceOrTarget = "target";
      },
      /**
       * Mouse Events
       */
      mouseLeftDown() {
        if (!this.selectedEffect) {
          return this._selectEffects();
        }
        if (!this.hoveredEffects.size) {
          this._reset();
        }
      },
      mouseRightDown() {
      },
      mouseLeftUp() {
        if (!InteractionManager.state.Dragging) {
          return this._selectEffects();
        }
        if (!InteractionManager.state.Dragging || !this.selectedEffect || !this.suggestedProperties)
          return;
        this._updateEffect();
      },
      mouseRightUp() {
        InteractionManager.state.LeftMouseDown = false;
        this.suggestedProperties = false;
        this.sourceOrTarget = false;
        this.dragOffset = false;
      },
      mouseMove() {
        this._evaluateHoveredEffects();
        if (InteractionManager.state.LeftMouseDown && !InteractionManager.state.RightMouseDown) {
          this._evaluateEffectPositionUpdate();
        }
      },
      /**
       * Hotkeys
       */
      async delete() {
        if (!this.selectedEffect) return;
        await SequencerEffectManager.endEffects({ effects: this.selectedEffect });
        this.selectedEffect = false;
      },
      attachToTargetDown() {
        if (InteractionManager.state.LeftMouseDown && !InteractionManager.state.RightMouseDown) {
          this._evaluateEffectPositionUpdate();
        }
      },
      /**
       * Private methods
       */
      _selectEffects() {
        this._reset();
        if (!this.hoveredEffects.size) return;
        const firstElement = Array.from(this.hoveredEffects)[0];
        this.selectedEffect = !firstElement.selected ? firstElement : false;
      },
      _evaluateHoveredEffects() {
        const position = get_mouse_position();
        this.hoveredEffects = this.effects.filter(
          (effect) => effect.isPositionWithinBounds(position)
        );
        this.hoveredEffects.sort((a, b) => {
          return a.data.layer !== b.data.zIndex ? a.data.zIndex - b.data.zIndex : a.data.layer - b.data.zIndex;
        });
        this.hoveredEffects = new Set(this.hoveredEffects);
      },
      _evaluateEffectPositionUpdate() {
        if (!this.selectedEffect) return;
        if (this.selectedEffect.data.stretchTo && !this.sourceOrTarget) {
          return;
        }
        let showCursor = false;
        let showPoint = this.snapToGrid;
        let position = get_mouse_position(this.snapToGrid);
        if (!this.selectedEffect.data.stretchTo && !this.dragOffset) {
          this.dragOffset = {
            x: position.x - this.selectedEffect.position.x,
            y: position.y - this.selectedEffect.position.y
          };
        }
        if (this.attachToTarget) {
          const obj = get_closest_token(position, {
            minimumDistance: canvas.grid.size
          });
          if (obj) {
            position = get_object_position(obj);
            showCursor = true;
            showPoint = false;
          }
        }
        if (this.dragOffset && !showCursor && !this.snapToGrid) {
          position.x -= this.dragOffset.x;
          position.y -= this.dragOffset.y;
        }
        const color = (this.sourceOrTarget || "source") === "source" ? CONSTANTS.COLOR.PRIMARY : CONSTANTS.COLOR.SECONDARY;
        this.suggestedProperties = {
          position,
          showCursor,
          showPoint,
          color
        };
      },
      _updateEffect() {
        if (!this.selectedEffect) return;
        const updates = {
          attachTo: this.selectedEffect.data.attachTo,
          stretchTo: this.selectedEffect.data.stretchTo
        };
        const obj = this.attachToTarget ? get_closest_token(this.suggestedProperties.position, {
          minimumDistance: canvas.grid.size,
          type: TokenDocument
        }) : false;
        let objUuid = obj ? get_object_identifier(obj) : false;
        if (this.sourceOrTarget === "source") {
          if (!updates.attachTo) {
            updates.attachTo = {
              active: false,
              align: "center",
              rotation: true,
              bindVisibility: true,
              bindAlpha: true
            };
          }
          updates.attachTo = foundry.utils.mergeObject(updates.attachTo || {}, {
            active: this.attachToTarget && !!objUuid
          });
          if (this.attachToTarget && objUuid) {
            updates.source = objUuid;
          } else {
            updates["source"] = this.suggestedProperties.position;
          }
        } else if (this.sourceOrTarget === "target") {
          updates.stretchTo.attachTo = this.attachToTarget && !!objUuid;
          if (this.attachToTarget && objUuid) {
            updates.target = objUuid;
          } else {
            updates["target"] = this.suggestedProperties.position;
          }
        } else {
          updates["source"] = this.suggestedProperties.position;
        }
        this.selectedEffect.update(updates);
        this.suggestedProperties = false;
        this.sourceOrTarget = false;
        this.dragOffset = false;
      },
      _reset() {
        this.selectedEffect = false;
        this.suggestedProperties = false;
        this.sourceOrTarget = false;
        this.dragOffset = false;
      }
    };
    function create_fragment$c(ctx) {
      let div1;
      let button;
      let t0;
      let div0;
      let t1_value = (
        /*getEffectName*/
        ctx[1](
          /*effect*/
          ctx[0]
        ) + ""
      );
      let t1;
      let mounted;
      let dispose;
      return {
        c() {
          div1 = element("div");
          button = element("button");
          button.innerHTML = `<i class="fas fa-times"></i>`;
          t0 = space();
          div0 = element("div");
          t1 = text$1(t1_value);
          attr(button, "class", "btn_end svelte-seq-1fyjvdk");
          attr(button, "type", "button");
          attr(div0, "class", "effect-text hover-text svelte-seq-1fyjvdk");
          attr(div1, "class", "effect hover-highlight svelte-seq-1fyjvdk");
        },
        m(target, anchor) {
          insert(target, div1, anchor);
          append(div1, button);
          append(div1, t0);
          append(div1, div0);
          append(div0, t1);
          if (!mounted) {
            dispose = [
              listen(
                button,
                "click",
                /*endEffect*/
                ctx[4]
              ),
              listen(
                div1,
                "mouseleave",
                /*mouseLeave*/
                ctx[3]
              ),
              listen(
                div1,
                "mouseover",
                /*mouseOver*/
                ctx[2]
              )
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*effect*/
          1 && t1_value !== (t1_value = /*getEffectName*/
          ctx2[1](
            /*effect*/
            ctx2[0]
          ) + "")) set_data(t1, t1_value);
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div1);
          }
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function instance$c($$self, $$props, $$invalidate) {
      let { effect } = $$props;
      function getEffectName(effect2) {
        let effectName = "";
        if (effect2.data.file && typeof effect2.data.file === "string") {
          effectName = effect2.data.file.split("\\").pop().split("/").pop();
        } else if (effect2.data.file && typeof effect2.data.file === "object") {
          effectName = Object.values(effect2.data.file)[0].split("\\").pop().split("/").pop();
        } else if (effect2.data.text) {
          effectName = "Text: " + effect2.data.text.text;
        } else {
          effectName = effect2.data?.shapes?.[0]?.type ? "Shape: " + effect2.data.shapes[0]?.type : "Unknown effect";
        }
        effectName = effect2.data.name ? `${effect2.data.name} (${effectName})` : effectName;
        if (effect2.data.creatorUserId !== game.userId) {
          let user_name = game.users.get(effect2.data.creatorUserId)?.name;
          let formattedUsername = user_name ? localize("SEQUENCER.ManagerPlayersEffect", { user_name }) : localize("SEQUENCER.ManagerUnknownEffect");
          effectName += ` (${formattedUsername})`;
        }
        return effectName;
      }
      function mouseOver() {
        SelectionManager.hoveredEffectUI = effect;
      }
      function mouseLeave() {
        SelectionManager.hoveredEffectUI = false;
      }
      function endEffect() {
        SequencerEffectManager.endEffects({ effects: [effect] });
      }
      $$self.$$set = ($$props2) => {
        if ("effect" in $$props2) $$invalidate(0, effect = $$props2.effect);
      };
      return [effect, getEffectName, mouseOver, mouseLeave, endEffect];
    }
    class EffectEntry extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$c, create_fragment$c, safe_not_equal, { effect: 0 });
      }
    }
    function create_fragment$b(ctx) {
      let div1;
      let button;
      let t0;
      let div0;
      let t1_value = (
        /*sound*/
        ctx[0].src.split("/").slice(-1) + ""
      );
      let t1;
      let mounted;
      let dispose;
      return {
        c() {
          div1 = element("div");
          button = element("button");
          button.innerHTML = `<i class="fas fa-times"></i>`;
          t0 = space();
          div0 = element("div");
          t1 = text$1(t1_value);
          attr(button, "class", "btn_end svelte-seq-1fyjvdk");
          attr(button, "type", "button");
          attr(div0, "class", "effect-text hover-text svelte-seq-1fyjvdk");
          attr(div1, "class", "effect hover-highlight svelte-seq-1fyjvdk");
        },
        m(target, anchor) {
          insert(target, div1, anchor);
          append(div1, button);
          append(div1, t0);
          append(div1, div0);
          append(div0, t1);
          if (!mounted) {
            dispose = listen(
              button,
              "click",
              /*endSound*/
              ctx[1]
            );
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*sound*/
          1 && t1_value !== (t1_value = /*sound*/
          ctx2[0].src.split("/").slice(-1) + "")) set_data(t1, t1_value);
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div1);
          }
          mounted = false;
          dispose();
        }
      };
    }
    function instance$b($$self, $$props, $$invalidate) {
      let { id } = $$props;
      let { sound } = $$props;
      function endSound() {
        SequencerSoundManager.endSounds({ sounds: [id] }, true);
      }
      $$self.$$set = ($$props2) => {
        if ("id" in $$props2) $$invalidate(2, id = $$props2.id);
        if ("sound" in $$props2) $$invalidate(0, sound = $$props2.sound);
      };
      return [sound, endSound, id];
    }
    class SoundEntry extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$b, create_fragment$b, safe_not_equal, { id: 2, sound: 0 });
      }
    }
    function get_each_context$3(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[10] = list[i][0];
      child_ctx[11] = list[i][1];
      return child_ctx;
    }
    function get_each_context_1$1(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[14] = list[i];
      return child_ctx;
    }
    function get_each_context_2$1(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[14] = list[i];
      return child_ctx;
    }
    function create_if_block_5(ctx) {
      let div;
      let h2;
      return {
        c() {
          div = element("div");
          h2 = element("h2");
          h2.textContent = `${localize("SEQUENCER.Manager.NothingPlaying")}`;
          attr(div, "class", "no-effects");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          append(div, h2);
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
        }
      };
    }
    function create_if_block_1(ctx) {
      let button;
      let t1;
      let div;
      let t2;
      let t3;
      let current;
      let mounted;
      let dispose;
      let if_block0 = (
        /*persistentEffects*/
        ctx[2].length && create_if_block_4(ctx)
      );
      let if_block1 = (
        /*temporaryEffects*/
        ctx[1].length && /*persistentEffects*/
        ctx[2].length && create_if_block_3()
      );
      let if_block2 = (
        /*temporaryEffects*/
        ctx[1].length && create_if_block_2(ctx)
      );
      return {
        c() {
          button = element("button");
          button.textContent = `${localize("SEQUENCER.Manager.EndAllEffects")}`;
          t1 = space();
          div = element("div");
          if (if_block0) if_block0.c();
          t2 = space();
          if (if_block1) if_block1.c();
          t3 = space();
          if (if_block2) if_block2.c();
          attr(button, "class", "w-100 end-all-effects mb-2 svelte-seq-nc5j73");
          attr(button, "type", "button");
          attr(div, "class", "effects svelte-seq-nc5j73");
        },
        m(target, anchor) {
          insert(target, button, anchor);
          insert(target, t1, anchor);
          insert(target, div, anchor);
          if (if_block0) if_block0.m(div, null);
          append(div, t2);
          if (if_block1) if_block1.m(div, null);
          append(div, t3);
          if (if_block2) if_block2.m(div, null);
          current = true;
          if (!mounted) {
            dispose = listen(
              button,
              "click",
              /*endAllEffects*/
              ctx[6]
            );
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (
            /*persistentEffects*/
            ctx2[2].length
          ) {
            if (if_block0) {
              if_block0.p(ctx2, dirty);
              if (dirty & /*persistentEffects*/
              4) {
                transition_in(if_block0, 1);
              }
            } else {
              if_block0 = create_if_block_4(ctx2);
              if_block0.c();
              transition_in(if_block0, 1);
              if_block0.m(div, t2);
            }
          } else if (if_block0) {
            group_outros();
            transition_out(if_block0, 1, 1, () => {
              if_block0 = null;
            });
            check_outros();
          }
          if (
            /*temporaryEffects*/
            ctx2[1].length && /*persistentEffects*/
            ctx2[2].length
          ) {
            if (if_block1) ;
            else {
              if_block1 = create_if_block_3();
              if_block1.c();
              if_block1.m(div, t3);
            }
          } else if (if_block1) {
            if_block1.d(1);
            if_block1 = null;
          }
          if (
            /*temporaryEffects*/
            ctx2[1].length
          ) {
            if (if_block2) {
              if_block2.p(ctx2, dirty);
              if (dirty & /*temporaryEffects*/
              2) {
                transition_in(if_block2, 1);
              }
            } else {
              if_block2 = create_if_block_2(ctx2);
              if_block2.c();
              transition_in(if_block2, 1);
              if_block2.m(div, null);
            }
          } else if (if_block2) {
            group_outros();
            transition_out(if_block2, 1, 1, () => {
              if_block2 = null;
            });
            check_outros();
          }
        },
        i(local) {
          if (current) return;
          transition_in(if_block0);
          transition_in(if_block2);
          current = true;
        },
        o(local) {
          transition_out(if_block0);
          transition_out(if_block2);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(button);
            detach(t1);
            detach(div);
          }
          if (if_block0) if_block0.d();
          if (if_block1) if_block1.d();
          if (if_block2) if_block2.d();
          mounted = false;
          dispose();
        }
      };
    }
    function create_if_block_4(ctx) {
      let h2;
      let t1;
      let div;
      let each_blocks = [];
      let each_1_lookup = /* @__PURE__ */ new Map();
      let current;
      let each_value_2 = ensure_array_like(
        /*persistentEffects*/
        ctx[2]
      );
      const get_key = (ctx2) => (
        /*effect*/
        ctx2[14].id
      );
      for (let i = 0; i < each_value_2.length; i += 1) {
        let child_ctx = get_each_context_2$1(ctx, each_value_2, i);
        let key = get_key(child_ctx);
        each_1_lookup.set(key, each_blocks[i] = create_each_block_2$1(key, child_ctx));
      }
      return {
        c() {
          h2 = element("h2");
          h2.textContent = `${localize("SEQUENCER.Manager.PersistentEffects")}`;
          t1 = space();
          div = element("div");
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
        },
        m(target, anchor) {
          insert(target, h2, anchor);
          insert(target, t1, anchor);
          insert(target, div, anchor);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(div, null);
            }
          }
          current = true;
        },
        p(ctx2, dirty) {
          if (dirty & /*persistentEffects*/
          4) {
            each_value_2 = ensure_array_like(
              /*persistentEffects*/
              ctx2[2]
            );
            group_outros();
            each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value_2, each_1_lookup, div, outro_and_destroy_block, create_each_block_2$1, null, get_each_context_2$1);
            check_outros();
          }
        },
        i(local) {
          if (current) return;
          for (let i = 0; i < each_value_2.length; i += 1) {
            transition_in(each_blocks[i]);
          }
          current = true;
        },
        o(local) {
          for (let i = 0; i < each_blocks.length; i += 1) {
            transition_out(each_blocks[i]);
          }
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(h2);
            detach(t1);
            detach(div);
          }
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].d();
          }
        }
      };
    }
    function create_each_block_2$1(key_1, ctx) {
      let first;
      let effectentry;
      let current;
      effectentry = new EffectEntry({ props: { effect: (
        /*effect*/
        ctx[14]
      ) } });
      return {
        key: key_1,
        first: null,
        c() {
          first = empty();
          create_component(effectentry.$$.fragment);
          this.first = first;
        },
        m(target, anchor) {
          insert(target, first, anchor);
          mount_component(effectentry, target, anchor);
          current = true;
        },
        p(new_ctx, dirty) {
          ctx = new_ctx;
          const effectentry_changes = {};
          if (dirty & /*persistentEffects*/
          4) effectentry_changes.effect = /*effect*/
          ctx[14];
          effectentry.$set(effectentry_changes);
        },
        i(local) {
          if (current) return;
          transition_in(effectentry.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(effectentry.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(first);
          }
          destroy_component(effectentry, detaching);
        }
      };
    }
    function create_if_block_3(ctx) {
      let hr;
      return {
        c() {
          hr = element("hr");
        },
        m(target, anchor) {
          insert(target, hr, anchor);
        },
        d(detaching) {
          if (detaching) {
            detach(hr);
          }
        }
      };
    }
    function create_if_block_2(ctx) {
      let h2;
      let t1;
      let div;
      let each_blocks = [];
      let each_1_lookup = /* @__PURE__ */ new Map();
      let current;
      let each_value_1 = ensure_array_like(
        /*temporaryEffects*/
        ctx[1]
      );
      const get_key = (ctx2) => (
        /*effect*/
        ctx2[14].id
      );
      for (let i = 0; i < each_value_1.length; i += 1) {
        let child_ctx = get_each_context_1$1(ctx, each_value_1, i);
        let key = get_key(child_ctx);
        each_1_lookup.set(key, each_blocks[i] = create_each_block_1$1(key, child_ctx));
      }
      return {
        c() {
          h2 = element("h2");
          h2.textContent = `${localize("SEQUENCER.Manager.TemporaryEffects")}`;
          t1 = space();
          div = element("div");
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
        },
        m(target, anchor) {
          insert(target, h2, anchor);
          insert(target, t1, anchor);
          insert(target, div, anchor);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(div, null);
            }
          }
          current = true;
        },
        p(ctx2, dirty) {
          if (dirty & /*temporaryEffects*/
          2) {
            each_value_1 = ensure_array_like(
              /*temporaryEffects*/
              ctx2[1]
            );
            group_outros();
            each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value_1, each_1_lookup, div, outro_and_destroy_block, create_each_block_1$1, null, get_each_context_1$1);
            check_outros();
          }
        },
        i(local) {
          if (current) return;
          for (let i = 0; i < each_value_1.length; i += 1) {
            transition_in(each_blocks[i]);
          }
          current = true;
        },
        o(local) {
          for (let i = 0; i < each_blocks.length; i += 1) {
            transition_out(each_blocks[i]);
          }
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(h2);
            detach(t1);
            detach(div);
          }
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].d();
          }
        }
      };
    }
    function create_each_block_1$1(key_1, ctx) {
      let first;
      let effectentry;
      let current;
      effectentry = new EffectEntry({ props: { effect: (
        /*effect*/
        ctx[14]
      ) } });
      return {
        key: key_1,
        first: null,
        c() {
          first = empty();
          create_component(effectentry.$$.fragment);
          this.first = first;
        },
        m(target, anchor) {
          insert(target, first, anchor);
          mount_component(effectentry, target, anchor);
          current = true;
        },
        p(new_ctx, dirty) {
          ctx = new_ctx;
          const effectentry_changes = {};
          if (dirty & /*temporaryEffects*/
          2) effectentry_changes.effect = /*effect*/
          ctx[14];
          effectentry.$set(effectentry_changes);
        },
        i(local) {
          if (current) return;
          transition_in(effectentry.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(effectentry.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(first);
          }
          destroy_component(effectentry, detaching);
        }
      };
    }
    function create_if_block$2(ctx) {
      let button;
      let t1;
      let div1;
      let h2;
      let t3;
      let div0;
      let each_blocks = [];
      let each_1_lookup = /* @__PURE__ */ new Map();
      let current;
      let mounted;
      let dispose;
      let each_value = ensure_array_like(
        /*sounds*/
        ctx[3]
      );
      const get_key = (ctx2) => (
        /*id*/
        ctx2[10]
      );
      for (let i = 0; i < each_value.length; i += 1) {
        let child_ctx = get_each_context$3(ctx, each_value, i);
        let key = get_key(child_ctx);
        each_1_lookup.set(key, each_blocks[i] = create_each_block$3(key, child_ctx));
      }
      return {
        c() {
          button = element("button");
          button.textContent = `${localize("SEQUENCER.Manager.EndAllSounds")}`;
          t1 = space();
          div1 = element("div");
          h2 = element("h2");
          h2.textContent = `${localize("SEQUENCER.Manager.Sounds")}`;
          t3 = space();
          div0 = element("div");
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
          attr(button, "class", "w-100 end-all-effects mb-2 svelte-seq-nc5j73");
          attr(button, "type", "button");
        },
        m(target, anchor) {
          insert(target, button, anchor);
          insert(target, t1, anchor);
          insert(target, div1, anchor);
          append(div1, h2);
          append(div1, t3);
          append(div1, div0);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(div0, null);
            }
          }
          current = true;
          if (!mounted) {
            dispose = listen(
              button,
              "click",
              /*endAllSounds*/
              ctx[7]
            );
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (dirty & /*sounds*/
          8) {
            each_value = ensure_array_like(
              /*sounds*/
              ctx2[3]
            );
            group_outros();
            each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value, each_1_lookup, div0, outro_and_destroy_block, create_each_block$3, null, get_each_context$3);
            check_outros();
          }
        },
        i(local) {
          if (current) return;
          for (let i = 0; i < each_value.length; i += 1) {
            transition_in(each_blocks[i]);
          }
          current = true;
        },
        o(local) {
          for (let i = 0; i < each_blocks.length; i += 1) {
            transition_out(each_blocks[i]);
          }
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(button);
            detach(t1);
            detach(div1);
          }
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].d();
          }
          mounted = false;
          dispose();
        }
      };
    }
    function create_each_block$3(key_1, ctx) {
      let first;
      let soundentry;
      let current;
      soundentry = new SoundEntry({
        props: {
          id: (
            /*id*/
            ctx[10]
          ),
          sound: (
            /*sound*/
            ctx[11]
          )
        }
      });
      return {
        key: key_1,
        first: null,
        c() {
          first = empty();
          create_component(soundentry.$$.fragment);
          this.first = first;
        },
        m(target, anchor) {
          insert(target, first, anchor);
          mount_component(soundentry, target, anchor);
          current = true;
        },
        p(new_ctx, dirty) {
          ctx = new_ctx;
          const soundentry_changes = {};
          if (dirty & /*sounds*/
          8) soundentry_changes.id = /*id*/
          ctx[10];
          if (dirty & /*sounds*/
          8) soundentry_changes.sound = /*sound*/
          ctx[11];
          soundentry.$set(soundentry_changes);
        },
        i(local) {
          if (current) return;
          transition_in(soundentry.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(soundentry.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(first);
          }
          destroy_component(soundentry, detaching);
        }
      };
    }
    function create_fragment$a(ctx) {
      let div;
      let t0;
      let t1;
      let current;
      let if_block0 = !/*effects*/
      ctx[0].length && !/*sounds*/
      ctx[3].length && create_if_block_5();
      let if_block1 = (
        /*effects*/
        ctx[0].length && create_if_block_1(ctx)
      );
      let if_block2 = (
        /*sounds*/
        ctx[3].length && create_if_block$2(ctx)
      );
      return {
        c() {
          div = element("div");
          if (if_block0) if_block0.c();
          t0 = space();
          if (if_block1) if_block1.c();
          t1 = space();
          if (if_block2) if_block2.c();
          attr(div, "class", "effects-container svelte-seq-nc5j73");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          if (if_block0) if_block0.m(div, null);
          append(div, t0);
          if (if_block1) if_block1.m(div, null);
          append(div, t1);
          if (if_block2) if_block2.m(div, null);
          current = true;
        },
        p(ctx2, [dirty]) {
          if (!/*effects*/
          ctx2[0].length && !/*sounds*/
          ctx2[3].length) {
            if (if_block0) ;
            else {
              if_block0 = create_if_block_5();
              if_block0.c();
              if_block0.m(div, t0);
            }
          } else if (if_block0) {
            if_block0.d(1);
            if_block0 = null;
          }
          if (
            /*effects*/
            ctx2[0].length
          ) {
            if (if_block1) {
              if_block1.p(ctx2, dirty);
              if (dirty & /*effects*/
              1) {
                transition_in(if_block1, 1);
              }
            } else {
              if_block1 = create_if_block_1(ctx2);
              if_block1.c();
              transition_in(if_block1, 1);
              if_block1.m(div, t1);
            }
          } else if (if_block1) {
            group_outros();
            transition_out(if_block1, 1, 1, () => {
              if_block1 = null;
            });
            check_outros();
          }
          if (
            /*sounds*/
            ctx2[3].length
          ) {
            if (if_block2) {
              if_block2.p(ctx2, dirty);
              if (dirty & /*sounds*/
              8) {
                transition_in(if_block2, 1);
              }
            } else {
              if_block2 = create_if_block$2(ctx2);
              if_block2.c();
              transition_in(if_block2, 1);
              if_block2.m(div, null);
            }
          } else if (if_block2) {
            group_outros();
            transition_out(if_block2, 1, 1, () => {
              if_block2 = null;
            });
            check_outros();
          }
        },
        i(local) {
          if (current) return;
          transition_in(if_block1);
          transition_in(if_block2);
          current = true;
        },
        o(local) {
          transition_out(if_block1);
          transition_out(if_block2);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          if (if_block0) if_block0.d();
          if (if_block1) if_block1.d();
          if (if_block2) if_block2.d();
        }
      };
    }
    function instance$a($$self, $$props, $$invalidate) {
      let effects;
      let sounds;
      let persistentEffects;
      let temporaryEffects;
      let $RunningSounds;
      let $VisibleEffects;
      const VisibleEffects = SequenceManager.VisibleEffects;
      component_subscribe($$self, VisibleEffects, (value) => $$invalidate(9, $VisibleEffects = value));
      const RunningSounds = SequenceManager.RunningSounds;
      component_subscribe($$self, RunningSounds, (value) => $$invalidate(8, $RunningSounds = value));
      function endAllEffects() {
        Sequencer.EffectManager.endEffects({
          effects: effects.filter((effect) => effect.userCanDelete && !effect.data.private)
        });
      }
      function endAllSounds() {
        SequencerSoundManager.endSounds({
          sounds: Object.entries($RunningSounds).filter((e) => e[1].sound_playing).map((e) => e[0])
        });
      }
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*$VisibleEffects*/
        512) {
          $$invalidate(0, effects = Object.values($VisibleEffects));
        }
        if ($$self.$$.dirty & /*$RunningSounds*/
        256) {
          $$invalidate(3, sounds = Object.entries($RunningSounds).filter((e) => e[1].sound_playing));
        }
        if ($$self.$$.dirty & /*effects*/
        1) {
          $$invalidate(2, persistentEffects = effects.filter((effect) => effect.data.persist));
        }
        if ($$self.$$.dirty & /*effects*/
        1) {
          $$invalidate(1, temporaryEffects = effects.filter((effect) => !effect.data.persist));
        }
      };
      return [
        effects,
        temporaryEffects,
        persistentEffects,
        sounds,
        VisibleEffects,
        RunningSounds,
        endAllEffects,
        endAllSounds,
        $RunningSounds,
        $VisibleEffects
      ];
    }
    class Manager extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$a, create_fragment$a, safe_not_equal, {});
      }
    }
    function create_fragment$9(ctx) {
      let div;
      let label;
      let t0_value = localize(
        /*setting*/
        ctx[0].label
      ) + "";
      let t0;
      let t1;
      let input0;
      let t2;
      let input1;
      let applyStyles_action;
      let mounted;
      let dispose;
      return {
        c() {
          div = element("div");
          label = element("label");
          t0 = text$1(t0_value);
          t1 = space();
          input0 = element("input");
          t2 = space();
          input1 = element("input");
          attr(label, "class", "svelte-seq-x3192w");
          input0.disabled = /*$isLocked*/
          ctx[6];
          attr(
            input0,
            "max",
            /*max*/
            ctx[2]
          );
          attr(
            input0,
            "min",
            /*min*/
            ctx[1]
          );
          attr(
            input0,
            "step",
            /*step*/
            ctx[4]
          );
          attr(input0, "type", "range");
          attr(input0, "class", "svelte-seq-x3192w");
          input1.disabled = /*$isLocked*/
          ctx[6];
          attr(
            input1,
            "max",
            /*maxInput*/
            ctx[3]
          );
          attr(
            input1,
            "min",
            /*min*/
            ctx[1]
          );
          input1.required = true;
          attr(
            input1,
            "step",
            /*step*/
            ctx[4]
          );
          attr(input1, "type", "number");
          attr(input1, "class", "svelte-seq-x3192w");
          set_style(div, "display", "flex");
          set_style(div, "align-items", "center");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          append(div, label);
          append(label, t0);
          append(div, t1);
          append(div, input0);
          set_input_value(
            input0,
            /*$store*/
            ctx[7]
          );
          append(div, t2);
          append(div, input1);
          set_input_value(
            input1,
            /*$store*/
            ctx[7]
          );
          if (!mounted) {
            dispose = [
              listen(
                input0,
                "change",
                /*input0_change_input_handler*/
                ctx[11]
              ),
              listen(
                input0,
                "input",
                /*input0_change_input_handler*/
                ctx[11]
              ),
              listen(
                input1,
                "input",
                /*input1_input_handler*/
                ctx[12]
              ),
              action_destroyer(applyStyles_action = applyStyles.call(
                null,
                div,
                /*styles*/
                ctx[5]
              ))
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*setting*/
          1 && t0_value !== (t0_value = localize(
            /*setting*/
            ctx2[0].label
          ) + "")) set_data(t0, t0_value);
          if (dirty & /*$isLocked*/
          64) {
            input0.disabled = /*$isLocked*/
            ctx2[6];
          }
          if (dirty & /*max*/
          4) {
            attr(
              input0,
              "max",
              /*max*/
              ctx2[2]
            );
          }
          if (dirty & /*min*/
          2) {
            attr(
              input0,
              "min",
              /*min*/
              ctx2[1]
            );
          }
          if (dirty & /*step*/
          16) {
            attr(
              input0,
              "step",
              /*step*/
              ctx2[4]
            );
          }
          if (dirty & /*$store*/
          128) {
            set_input_value(
              input0,
              /*$store*/
              ctx2[7]
            );
          }
          if (dirty & /*$isLocked*/
          64) {
            input1.disabled = /*$isLocked*/
            ctx2[6];
          }
          if (dirty & /*maxInput*/
          8) {
            attr(
              input1,
              "max",
              /*maxInput*/
              ctx2[3]
            );
          }
          if (dirty & /*min*/
          2) {
            attr(
              input1,
              "min",
              /*min*/
              ctx2[1]
            );
          }
          if (dirty & /*step*/
          16) {
            attr(
              input1,
              "step",
              /*step*/
              ctx2[4]
            );
          }
          if (dirty & /*$store*/
          128 && to_number(input1.value) !== /*$store*/
          ctx2[7]) {
            set_input_value(
              input1,
              /*$store*/
              ctx2[7]
            );
          }
          if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
          32) applyStyles_action.update.call(
            null,
            /*styles*/
            ctx2[5]
          );
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function instance$9($$self, $$props, $$invalidate) {
      let $isLocked;
      let $store;
      let { setting } = $$props;
      let { lock = false } = $$props;
      let { min = 0.1 } = $$props;
      let { max = 2 } = $$props;
      let { maxInput = Infinity } = $$props;
      let { step = 0.01 } = $$props;
      let { styles = {} } = $$props;
      const store = setting.store;
      component_subscribe($$self, store, (value) => $$invalidate(7, $store = value));
      const isLocked = lock ? lock.store : writable(false);
      component_subscribe($$self, isLocked, (value) => $$invalidate(6, $isLocked = value));
      function input0_change_input_handler() {
        $store = to_number(this.value);
        store.set($store);
      }
      function input1_input_handler() {
        $store = to_number(this.value);
        store.set($store);
      }
      $$self.$$set = ($$props2) => {
        if ("setting" in $$props2) $$invalidate(0, setting = $$props2.setting);
        if ("lock" in $$props2) $$invalidate(10, lock = $$props2.lock);
        if ("min" in $$props2) $$invalidate(1, min = $$props2.min);
        if ("max" in $$props2) $$invalidate(2, max = $$props2.max);
        if ("maxInput" in $$props2) $$invalidate(3, maxInput = $$props2.maxInput);
        if ("step" in $$props2) $$invalidate(4, step = $$props2.step);
        if ("styles" in $$props2) $$invalidate(5, styles = $$props2.styles);
      };
      return [
        setting,
        min,
        max,
        maxInput,
        step,
        styles,
        $isLocked,
        $store,
        store,
        isLocked,
        lock,
        input0_change_input_handler,
        input1_input_handler
      ];
    }
    class SliderInput extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$9, create_fragment$9, safe_not_equal, {
          setting: 0,
          lock: 10,
          min: 1,
          max: 2,
          maxInput: 3,
          step: 4,
          styles: 5
        });
      }
    }
    function create_fragment$8(ctx) {
      let div;
      let input;
      let input_disabled_value;
      let t0;
      let label_1;
      let t1_value = localize(
        /*setting*/
        ctx[0].label
      ) + "";
      let t1;
      let applyStyles_action;
      let mounted;
      let dispose;
      return {
        c() {
          div = element("div");
          input = element("input");
          t0 = space();
          label_1 = element("label");
          t1 = text$1(t1_value);
          attr(
            input,
            "id",
            /*id*/
            ctx[5]
          );
          input.disabled = input_disabled_value = /*$isLocked*/
          ctx[3] !== /*inverse*/
          ctx[1];
          set_style(input, "height", "15px");
          attr(input, "type", "checkbox");
          attr(
            label_1,
            "for",
            /*id*/
            ctx[5]
          );
          set_style(div, "display", "flex");
          set_style(div, "align-items", "center");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          append(div, input);
          input.checked = /*$store*/
          ctx[4];
          append(div, t0);
          append(div, label_1);
          append(label_1, t1);
          if (!mounted) {
            dispose = [
              listen(
                input,
                "change",
                /*input_change_handler*/
                ctx[10]
              ),
              action_destroyer(applyStyles_action = applyStyles.call(
                null,
                div,
                /*styles*/
                ctx[2]
              ))
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*$isLocked, inverse*/
          10 && input_disabled_value !== (input_disabled_value = /*$isLocked*/
          ctx2[3] !== /*inverse*/
          ctx2[1])) {
            input.disabled = input_disabled_value;
          }
          if (dirty & /*$store*/
          16) {
            input.checked = /*$store*/
            ctx2[4];
          }
          if (dirty & /*setting*/
          1 && t1_value !== (t1_value = localize(
            /*setting*/
            ctx2[0].label
          ) + "")) set_data(t1, t1_value);
          if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
          4) applyStyles_action.update.call(
            null,
            /*styles*/
            ctx2[2]
          );
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function instance$8($$self, $$props, $$invalidate) {
      let $store;
      let $isLocked;
      let { setting } = $$props;
      let { label = false } = $$props;
      let { lock = false } = $$props;
      let { inverse = false } = $$props;
      let { styles = {} } = $$props;
      const id = "sequencer-input-" + foundry.utils.randomID();
      const store = setting.store;
      component_subscribe($$self, store, (value) => $$invalidate(4, $store = value));
      const isLocked = lock ? lock.store : writable(inverse);
      component_subscribe($$self, isLocked, (value) => $$invalidate(3, $isLocked = value));
      function input_change_handler() {
        $store = this.checked;
        store.set($store);
      }
      $$self.$$set = ($$props2) => {
        if ("setting" in $$props2) $$invalidate(0, setting = $$props2.setting);
        if ("label" in $$props2) $$invalidate(8, label = $$props2.label);
        if ("lock" in $$props2) $$invalidate(9, lock = $$props2.lock);
        if ("inverse" in $$props2) $$invalidate(1, inverse = $$props2.inverse);
        if ("styles" in $$props2) $$invalidate(2, styles = $$props2.styles);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*$isLocked, inverse*/
        10) {
          {
            if ($isLocked !== inverse) {
              set_store_value(store, $store = false, $store);
            }
          }
        }
      };
      return [
        setting,
        inverse,
        styles,
        $isLocked,
        $store,
        id,
        store,
        isLocked,
        label,
        lock,
        input_change_handler
      ];
    }
    class Checkbox extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$8, create_fragment$8, safe_not_equal, {
          setting: 0,
          label: 8,
          lock: 9,
          inverse: 1,
          styles: 2
        });
      }
    }
    function create_if_block$1(ctx) {
      let label;
      let t_value = localize(
        /*setting*/
        ctx[0].label
      ) + "";
      let t;
      return {
        c() {
          label = element("label");
          t = text$1(t_value);
          attr(
            label,
            "for",
            /*id*/
            ctx[5]
          );
          attr(label, "class", "svelte-seq-ywsxq0");
        },
        m(target, anchor) {
          insert(target, label, anchor);
          append(label, t);
        },
        p(ctx2, dirty) {
          if (dirty & /*setting*/
          1 && t_value !== (t_value = localize(
            /*setting*/
            ctx2[0].label
          ) + "")) set_data(t, t_value);
        },
        d(detaching) {
          if (detaching) {
            detach(label);
          }
        }
      };
    }
    function create_fragment$7(ctx) {
      let div;
      let show_if = localize(
        /*setting*/
        ctx[0].label
      );
      let t;
      let input;
      let input_disabled_value;
      let applyStyles_action;
      let mounted;
      let dispose;
      let if_block = show_if && create_if_block$1(ctx);
      return {
        c() {
          div = element("div");
          if (if_block) if_block.c();
          t = space();
          input = element("input");
          attr(
            input,
            "id",
            /*id*/
            ctx[5]
          );
          attr(input, "type", "number");
          input.disabled = input_disabled_value = /*$isLocked*/
          ctx[3] !== /*inverse*/
          ctx[1];
          attr(input, "class", "svelte-seq-ywsxq0");
          set_style(div, "display", "flex");
          set_style(div, "align-items", "center");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          if (if_block) if_block.m(div, null);
          append(div, t);
          append(div, input);
          set_input_value(
            input,
            /*$store*/
            ctx[4]
          );
          if (!mounted) {
            dispose = [
              listen(
                input,
                "input",
                /*input_input_handler*/
                ctx[9]
              ),
              listen(
                input,
                "change",
                /*change_handler*/
                ctx[10]
              ),
              action_destroyer(applyStyles_action = applyStyles.call(
                null,
                div,
                /*styles*/
                ctx[2]
              ))
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*setting*/
          1) show_if = localize(
            /*setting*/
            ctx2[0].label
          );
          if (show_if) {
            if (if_block) {
              if_block.p(ctx2, dirty);
            } else {
              if_block = create_if_block$1(ctx2);
              if_block.c();
              if_block.m(div, t);
            }
          } else if (if_block) {
            if_block.d(1);
            if_block = null;
          }
          if (dirty & /*$isLocked, inverse*/
          10 && input_disabled_value !== (input_disabled_value = /*$isLocked*/
          ctx2[3] !== /*inverse*/
          ctx2[1])) {
            input.disabled = input_disabled_value;
          }
          if (dirty & /*$store*/
          16 && to_number(input.value) !== /*$store*/
          ctx2[4]) {
            set_input_value(
              input,
              /*$store*/
              ctx2[4]
            );
          }
          if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
          4) applyStyles_action.update.call(
            null,
            /*styles*/
            ctx2[2]
          );
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          if (if_block) if_block.d();
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function instance$7($$self, $$props, $$invalidate) {
      let $isLocked;
      let $store;
      let { setting } = $$props;
      let { lock = false } = $$props;
      let { inverse = false } = $$props;
      let { styles = {} } = $$props;
      const id = "sequencer-input-" + foundry.utils.randomID();
      const store = setting.store;
      component_subscribe($$self, store, (value) => $$invalidate(4, $store = value));
      const isLocked = lock ? lock.store : writable(inverse);
      component_subscribe($$self, isLocked, (value) => $$invalidate(3, $isLocked = value));
      function input_input_handler() {
        $store = to_number(this.value);
        store.set($store);
      }
      const change_handler = () => {
        if ($store === null) set_store_value(store, $store = 0, $store);
      };
      $$self.$$set = ($$props2) => {
        if ("setting" in $$props2) $$invalidate(0, setting = $$props2.setting);
        if ("lock" in $$props2) $$invalidate(8, lock = $$props2.lock);
        if ("inverse" in $$props2) $$invalidate(1, inverse = $$props2.inverse);
        if ("styles" in $$props2) $$invalidate(2, styles = $$props2.styles);
      };
      return [
        setting,
        inverse,
        styles,
        $isLocked,
        $store,
        id,
        store,
        isLocked,
        lock,
        input_input_handler,
        change_handler
      ];
    }
    class NumberInput extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$7, create_fragment$7, safe_not_equal, {
          setting: 0,
          lock: 8,
          inverse: 1,
          styles: 2
        });
      }
    }
    function create_fragment$6(ctx) {
      let div2;
      let div0;
      let span0;
      let t0_value = localize(
        /*setting*/
        ctx[0].label_off
      ) + "";
      let t0;
      let t1;
      let div1;
      let span1;
      let t2_value = localize(
        /*setting*/
        ctx[0].label_on
      ) + "";
      let t2;
      let applyStyles_action;
      let mounted;
      let dispose;
      return {
        c() {
          div2 = element("div");
          div0 = element("div");
          span0 = element("span");
          t0 = text$1(t0_value);
          t1 = space();
          div1 = element("div");
          span1 = element("span");
          t2 = text$1(t2_value);
          attr(div0, "class", "first svelte-seq-o0yoxs");
          toggle_class(div0, "active", !/*$store*/
          ctx[2]);
          attr(div1, "class", "second svelte-seq-o0yoxs");
          toggle_class(
            div1,
            "active",
            /*$store*/
            ctx[2]
          );
          set_style(div2, "display", "flex");
          set_style(div2, "align-items", "center");
          attr(div2, "class", "svelte-seq-o0yoxs");
        },
        m(target, anchor) {
          insert(target, div2, anchor);
          append(div2, div0);
          append(div0, span0);
          append(span0, t0);
          append(div2, t1);
          append(div2, div1);
          append(div1, span1);
          append(span1, t2);
          if (!mounted) {
            dispose = [
              listen(
                div0,
                "click",
                /*click_handler*/
                ctx[5]
              ),
              listen(
                div1,
                "click",
                /*click_handler_1*/
                ctx[6]
              ),
              action_destroyer(applyStyles_action = applyStyles.call(
                null,
                div2,
                /*finalStyles*/
                ctx[1]
              ))
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*setting*/
          1 && t0_value !== (t0_value = localize(
            /*setting*/
            ctx2[0].label_off
          ) + "")) set_data(t0, t0_value);
          if (dirty & /*$store*/
          4) {
            toggle_class(div0, "active", !/*$store*/
            ctx2[2]);
          }
          if (dirty & /*setting*/
          1 && t2_value !== (t2_value = localize(
            /*setting*/
            ctx2[0].label_on
          ) + "")) set_data(t2, t2_value);
          if (dirty & /*$store*/
          4) {
            toggle_class(
              div1,
              "active",
              /*$store*/
              ctx2[2]
            );
          }
          if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*finalStyles*/
          2) applyStyles_action.update.call(
            null,
            /*finalStyles*/
            ctx2[1]
          );
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div2);
          }
          mounted = false;
          run_all(dispose);
        }
      };
    }
    const width = 5.75;
    function instance$6($$self, $$props, $$invalidate) {
      let finalStyles;
      let $store;
      let { setting } = $$props;
      let { styles = {} } = $$props;
      const store = setting.store;
      component_subscribe($$self, store, (value) => $$invalidate(2, $store = value));
      const click_handler = () => {
        set_store_value(store, $store = false, $store);
      };
      const click_handler_1 = () => {
        set_store_value(store, $store = true, $store);
      };
      $$self.$$set = ($$props2) => {
        if ("setting" in $$props2) $$invalidate(0, setting = $$props2.setting);
        if ("styles" in $$props2) $$invalidate(4, styles = $$props2.styles);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*styles*/
        16) {
          $$invalidate(1, finalStyles = {
            ...styles,
            "--switch-width": `${width}em`,
            "--half-switch-width": `${width - 1.5}em`,
            "--half-switch-width-on": `${width - 1.7}em`
          });
        }
      };
      return [setting, finalStyles, $store, store, styles, click_handler, click_handler_1];
    }
    class SwitchToggle extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$6, create_fragment$6, safe_not_equal, { setting: 0, styles: 4 });
      }
    }
    function get_each_context$2(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[20] = list[i];
      return child_ctx;
    }
    function get_each_context_1(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[23] = list[i];
      return child_ctx;
    }
    function get_each_context_2(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[26] = list[i];
      return child_ctx;
    }
    function create_each_block_2(ctx) {
      let option;
      let t_value = (
        /*suggestion*/
        ctx[26] + ""
      );
      let t;
      let option_value_value;
      return {
        c() {
          option = element("option");
          t = text$1(t_value);
          option.__value = option_value_value = /*suggestion*/
          ctx[26];
          set_input_value(option, option.__value);
        },
        m(target, anchor) {
          insert(target, option, anchor);
          append(option, t);
        },
        p(ctx2, dirty) {
          if (dirty & /*suggestions*/
          2 && t_value !== (t_value = /*suggestion*/
          ctx2[26] + "")) set_data(t, t_value);
          if (dirty & /*suggestions*/
          2 && option_value_value !== (option_value_value = /*suggestion*/
          ctx2[26])) {
            option.__value = option_value_value;
            set_input_value(option, option.__value);
          }
        },
        d(detaching) {
          if (detaching) {
            detach(option);
          }
        }
      };
    }
    function create_each_block_1(key_1, ctx) {
      let option;
      let t_value = (
        /*user*/
        ctx[23].name + ""
      );
      let t;
      return {
        key: key_1,
        first: null,
        c() {
          option = element("option");
          t = text$1(t_value);
          option.__value = /*user*/
          ctx[23].id;
          set_input_value(option, option.__value);
          this.first = option;
        },
        m(target, anchor) {
          insert(target, option, anchor);
          append(option, t);
        },
        p(new_ctx, dirty) {
          ctx = new_ctx;
        },
        d(detaching) {
          if (detaching) {
            detach(option);
          }
        }
      };
    }
    function create_each_block$2(ctx) {
      let option;
      let t_value = (
        /*preset*/
        ctx[20] + ""
      );
      let t;
      return {
        c() {
          option = element("option");
          t = text$1(t_value);
          option.__value = /*preset*/
          ctx[20];
          set_input_value(option, option.__value);
        },
        m(target, anchor) {
          insert(target, option, anchor);
          append(option, t);
        },
        p: noop,
        d(detaching) {
          if (detaching) {
            detach(option);
          }
        }
      };
    }
    function create_fragment$5(ctx) {
      let div16;
      let fieldset0;
      let legend0;
      let t1;
      let button0;
      let t3;
      let div0;
      let input;
      let t4;
      let button1;
      let t5;
      let datalist;
      let t6;
      let div1;
      let label;
      let t8;
      let select0;
      let option0;
      let each_blocks_1 = [];
      let each1_lookup = /* @__PURE__ */ new Map();
      let t10;
      let div4;
      let div2;
      let t12;
      let div3;
      let select1;
      let option1;
      let t14;
      let button2;
      let t15;
      let button3;
      let i2;
      let button3_disabled_value;
      let t16;
      let button4;
      let i3;
      let button4_disabled_value;
      let t17;
      let fieldset1;
      let legend1;
      let t19;
      let div11;
      let sliderinput0;
      let t20;
      let numberinput0;
      let t21;
      let numberinput1;
      let t22;
      let div5;
      let t23;
      let sliderinput1;
      let t24;
      let div6;
      let t25;
      let checkbox0;
      let t26;
      let div7;
      let t27;
      let div8;
      let t28;
      let checkbox1;
      let t29;
      let checkbox2;
      let t30;
      let checkbox3;
      let t31;
      let checkbox4;
      let t32;
      let div9;
      let t33;
      let numberinput2;
      let t34;
      let numberinput3;
      let t35;
      let checkbox5;
      let t36;
      let numberinput4;
      let t37;
      let checkbox6;
      let t38;
      let div10;
      let t39;
      let fieldset2;
      let legend2;
      let t41;
      let div15;
      let numberinput5;
      let t42;
      let numberinput6;
      let t43;
      let div12;
      let t44;
      let checkbox7;
      let t45;
      let numberinput7;
      let t46;
      let numberinput8;
      let t47;
      let numberinput9;
      let t48;
      let div13;
      let t49;
      let switchtoggle;
      let t50;
      let numberinput10;
      let t51;
      let div14;
      let t52;
      let checkbox8;
      let t53;
      let checkbox9;
      let t54;
      let checkbox10;
      let t55;
      let checkbox11;
      let t56;
      let checkbox12;
      let current;
      let mounted;
      let dispose;
      let each_value_2 = ensure_array_like(
        /*suggestions*/
        ctx[1]
      );
      let each_blocks_2 = [];
      for (let i = 0; i < each_value_2.length; i += 1) {
        each_blocks_2[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i));
      }
      let each_value_1 = ensure_array_like(
        /*users*/
        ctx[5]
      );
      const get_key = (ctx2) => (
        /*user*/
        ctx2[23].id
      );
      for (let i = 0; i < each_value_1.length; i += 1) {
        let child_ctx = get_each_context_1(ctx, each_value_1, i);
        let key = get_key(child_ctx);
        each1_lookup.set(key, each_blocks_1[i] = create_each_block_1(key, child_ctx));
      }
      let each_value = ensure_array_like(
        /*presets*/
        ctx[8]
      );
      let each_blocks = [];
      for (let i = 0; i < each_value.length; i += 1) {
        each_blocks[i] = create_each_block$2(get_each_context$2(ctx, each_value, i));
      }
      sliderinput0 = new SliderInput({
        props: {
          setting: PlayerSettings.scale,
          styles: { "grid-column": `1 / 5` }
        }
      });
      numberinput0 = new NumberInput({
        props: {
          setting: PlayerSettings.scaleIn,
          styles: { "grid-column": `1 / 3` }
        }
      });
      numberinput1 = new NumberInput({
        props: {
          setting: PlayerSettings.scaleOut,
          styles: { "grid-column": `3 / 5` }
        }
      });
      sliderinput1 = new SliderInput({
        props: {
          setting: PlayerSettings.rotation,
          lock: PlayerSettings.randomRotation,
          min: "-180",
          max: "180",
          styles: { "grid-column": `1 / 5` }
        }
      });
      checkbox0 = new Checkbox({
        props: {
          setting: PlayerSettings.randomRotation,
          styles: { "grid-column": `2 / 4` }
        }
      });
      checkbox1 = new Checkbox({
        props: {
          setting: PlayerSettings.mirrorX,
          styles: { "grid-column": `1 / 3` }
        }
      });
      checkbox2 = new Checkbox({
        props: {
          setting: PlayerSettings.mirrorY,
          styles: { "grid-column": `3 / 5` }
        }
      });
      checkbox3 = new Checkbox({
        props: {
          setting: PlayerSettings.randomMirrorX,
          styles: { "grid-column": `1 / 3` },
          lock: PlayerSettings.mirrorX,
          inverse: true
        }
      });
      checkbox4 = new Checkbox({
        props: {
          setting: PlayerSettings.randomMirrorY,
          styles: { "grid-column": `3 / 5` },
          lock: PlayerSettings.mirrorY,
          inverse: true
        }
      });
      numberinput2 = new NumberInput({
        props: {
          setting: PlayerSettings.offsetX,
          lock: PlayerSettings.randomOffset,
          styles: { "grid-column": `1 / 3` }
        }
      });
      numberinput3 = new NumberInput({
        props: {
          setting: PlayerSettings.offsetY,
          lock: PlayerSettings.randomOffset,
          styles: { "grid-column": `3 / 5` }
        }
      });
      checkbox5 = new Checkbox({
        props: {
          setting: PlayerSettings.randomOffset,
          styles: { "grid-column": `1 / 3` }
        }
      });
      numberinput4 = new NumberInput({
        props: {
          setting: PlayerSettings.randomOffsetAmount,
          lock: PlayerSettings.randomOffset,
          inverse: true,
          styles: { "grid-column": `3 / 5` }
        }
      });
      checkbox6 = new Checkbox({
        props: {
          setting: PlayerSettings.offsetGridUnits,
          styles: { "grid-column": `2 / 5` }
        }
      });
      numberinput5 = new NumberInput({
        props: {
          setting: PlayerSettings.fadeIn,
          styles: { "grid-column": `1 / 3` }
        }
      });
      numberinput6 = new NumberInput({
        props: {
          setting: PlayerSettings.fadeOut,
          styles: { "grid-column": `3 / 5` }
        }
      });
      checkbox7 = new Checkbox({
        props: {
          setting: PlayerSettings.repeat,
          styles: { "grid-column": `1 / 3` }
        }
      });
      numberinput7 = new NumberInput({
        props: {
          setting: PlayerSettings.repetitions,
          lock: PlayerSettings.repeat,
          inverse: true,
          styles: { "grid-column": `3 / 5` }
        }
      });
      numberinput8 = new NumberInput({
        props: {
          setting: PlayerSettings.repeatDelayMin,
          lock: PlayerSettings.repeat,
          inverse: true,
          styles: { "grid-column": `3 / 4` }
        }
      });
      numberinput9 = new NumberInput({
        props: {
          setting: PlayerSettings.repeatDelayMax,
          lock: PlayerSettings.repeat,
          inverse: true,
          styles: { "grid-column": `4 / 5` }
        }
      });
      switchtoggle = new SwitchToggle({
        props: {
          setting: PlayerSettings.moveTowards,
          styles: { "grid-column": `1 / 3` }
        }
      });
      numberinput10 = new NumberInput({
        props: {
          setting: PlayerSettings.moveSpeed,
          lock: PlayerSettings.moveTowards,
          inverse: true,
          styles: { "grid-column": `3 / 5` }
        }
      });
      checkbox8 = new Checkbox({
        props: {
          setting: PlayerSettings.attachTo,
          styles: { "grid-column": `1 / 5` }
        }
      });
      checkbox9 = new Checkbox({
        props: {
          setting: PlayerSettings.stretchToAttach,
          styles: { "grid-column": `1 / 5` }
        }
      });
      checkbox10 = new Checkbox({
        props: {
          setting: PlayerSettings.snapToGrid,
          styles: { "grid-column": `1 / 5` }
        }
      });
      checkbox11 = new Checkbox({
        props: {
          setting: PlayerSettings.persist,
          styles: { "grid-column": `1 / 5` }
        }
      });
      checkbox12 = new Checkbox({
        props: {
          setting: PlayerSettings.belowTokens,
          styles: { "grid-column": `1 / 5` }
        }
      });
      return {
        c() {
          div16 = element("div");
          fieldset0 = element("fieldset");
          legend0 = element("legend");
          legend0.textContent = "Effect";
          t1 = space();
          button0 = element("button");
          button0.textContent = `${localize("SEQUENCER.Player.SwitchToLayer")}`;
          t3 = space();
          div0 = element("div");
          input = element("input");
          t4 = space();
          button1 = element("button");
          button1.innerHTML = `<i class="fas fa-file-import svelte-seq-1ipnpu1"></i>`;
          t5 = space();
          datalist = element("datalist");
          for (let i = 0; i < each_blocks_2.length; i += 1) {
            each_blocks_2[i].c();
          }
          t6 = space();
          div1 = element("div");
          label = element("label");
          label.textContent = "Play for users:";
          t8 = space();
          select0 = element("select");
          option0 = element("option");
          option0.textContent = `${localize("SEQUENCER.Player.AllUsers")}`;
          for (let i = 0; i < each_blocks_1.length; i += 1) {
            each_blocks_1[i].c();
          }
          t10 = space();
          div4 = element("div");
          div2 = element("div");
          div2.textContent = `${localize("SEQUENCER.Player.Presets")}`;
          t12 = space();
          div3 = element("div");
          select1 = element("select");
          option1 = element("option");
          option1.textContent = `${localize("SEQUENCER.Player.PresetsDefault")}`;
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
          t14 = space();
          button2 = element("button");
          button2.innerHTML = `<i class="fas fa-download svelte-seq-1ipnpu1"></i>`;
          t15 = space();
          button3 = element("button");
          i2 = element("i");
          t16 = space();
          button4 = element("button");
          i3 = element("i");
          t17 = space();
          fieldset1 = element("fieldset");
          legend1 = element("legend");
          legend1.textContent = "Transform";
          t19 = space();
          div11 = element("div");
          create_component(sliderinput0.$$.fragment);
          t20 = space();
          create_component(numberinput0.$$.fragment);
          t21 = space();
          create_component(numberinput1.$$.fragment);
          t22 = space();
          div5 = element("div");
          t23 = space();
          create_component(sliderinput1.$$.fragment);
          t24 = space();
          div6 = element("div");
          t25 = space();
          create_component(checkbox0.$$.fragment);
          t26 = space();
          div7 = element("div");
          t27 = space();
          div8 = element("div");
          t28 = space();
          create_component(checkbox1.$$.fragment);
          t29 = space();
          create_component(checkbox2.$$.fragment);
          t30 = space();
          create_component(checkbox3.$$.fragment);
          t31 = space();
          create_component(checkbox4.$$.fragment);
          t32 = space();
          div9 = element("div");
          t33 = space();
          create_component(numberinput2.$$.fragment);
          t34 = space();
          create_component(numberinput3.$$.fragment);
          t35 = space();
          create_component(checkbox5.$$.fragment);
          t36 = space();
          create_component(numberinput4.$$.fragment);
          t37 = space();
          create_component(checkbox6.$$.fragment);
          t38 = space();
          div10 = element("div");
          t39 = space();
          fieldset2 = element("fieldset");
          legend2 = element("legend");
          legend2.textContent = "Behavior";
          t41 = space();
          div15 = element("div");
          create_component(numberinput5.$$.fragment);
          t42 = space();
          create_component(numberinput6.$$.fragment);
          t43 = space();
          div12 = element("div");
          t44 = space();
          create_component(checkbox7.$$.fragment);
          t45 = space();
          create_component(numberinput7.$$.fragment);
          t46 = space();
          create_component(numberinput8.$$.fragment);
          t47 = space();
          create_component(numberinput9.$$.fragment);
          t48 = space();
          div13 = element("div");
          t49 = space();
          create_component(switchtoggle.$$.fragment);
          t50 = space();
          create_component(numberinput10.$$.fragment);
          t51 = space();
          div14 = element("div");
          t52 = space();
          create_component(checkbox8.$$.fragment);
          t53 = space();
          create_component(checkbox9.$$.fragment);
          t54 = space();
          create_component(checkbox10.$$.fragment);
          t55 = space();
          create_component(checkbox11.$$.fragment);
          t56 = space();
          create_component(checkbox12.$$.fragment);
          attr(button0, "class", "activate-layer svelte-seq-1ipnpu1");
          attr(button0, "type", "button");
          attr(input, "class", "file-input flex3");
          attr(input, "list", "dblist");
          attr(input, "placeholder", localize("SEQUENCER.Player.PathInput"));
          attr(input, "type", "text");
          attr(button1, "class", "custom-file-picker small-button svelte-seq-1ipnpu1");
          attr(button1, "type", "button");
          attr(datalist, "id", "dblist");
          attr(div0, "class", "file-settings svelte-seq-1ipnpu1");
          attr(label, "for", "user-select");
          option0.selected = true;
          option0.__value = "all";
          set_input_value(option0, option0.__value);
          attr(select0, "class", "user-select");
          attr(select0, "id", "user-select");
          select0.multiple = true;
          if (
            /*$userStore*/
            ctx[3] === void 0
          ) add_render_callback(() => (
            /*select0_change_handler*/
            ctx[11].call(select0)
          ));
          attr(div1, "class", "user-settings flexcol svelte-seq-1ipnpu1");
          attr(div2, "class", "row w-100");
          option1.__value = "default";
          set_input_value(option1, option1.__value);
          attr(select1, "class", "preset-select svelte-seq-1ipnpu1");
          if (
            /*selectedPreset*/
            ctx[2] === void 0
          ) add_render_callback(() => (
            /*select1_change_handler*/
            ctx[12].call(select1)
          ));
          attr(button2, "class", "save-preset small-button svelte-seq-1ipnpu1");
          attr(button2, "data-tooltip", "Save Preset");
          attr(button2, "type", "button");
          attr(i2, "class", "fas fa-copy svelte-seq-1ipnpu1");
          attr(button3, "class", "copy-preset small-button svelte-seq-1ipnpu1");
          attr(button3, "data-tooltip", "Copy Preset");
          button3.disabled = button3_disabled_value = /*selectedPreset*/
          ctx[2] === "default";
          attr(button3, "type", "button");
          attr(i3, "class", "fas fa-times svelte-seq-1ipnpu1");
          attr(button4, "class", "delete-preset small-button svelte-seq-1ipnpu1");
          attr(button4, "data-tooltip", "Delete Preset");
          button4.disabled = button4_disabled_value = /*selectedPreset*/
          ctx[2] === "default";
          attr(button4, "type", "button");
          attr(div3, "class", "preset-container svelte-seq-1ipnpu1");
          attr(div4, "class", "flexcol");
          attr(div5, "class", "divider");
          attr(div8, "class", "divider");
          attr(div9, "class", "divider");
          attr(div11, "class", "effect-transform-container");
          attr(div12, "class", "divider");
          attr(div13, "class", "divider");
          attr(div14, "class", "divider");
          attr(div15, "class", "effect-transform-container");
          attr(div16, "class", "effect-player-container");
        },
        m(target, anchor) {
          insert(target, div16, anchor);
          append(div16, fieldset0);
          append(fieldset0, legend0);
          append(fieldset0, t1);
          append(fieldset0, button0);
          append(fieldset0, t3);
          append(fieldset0, div0);
          append(div0, input);
          set_input_value(
            input,
            /*$fileStore*/
            ctx[0]
          );
          append(div0, t4);
          append(div0, button1);
          append(div0, t5);
          append(div0, datalist);
          for (let i = 0; i < each_blocks_2.length; i += 1) {
            if (each_blocks_2[i]) {
              each_blocks_2[i].m(datalist, null);
            }
          }
          append(fieldset0, t6);
          append(fieldset0, div1);
          append(div1, label);
          append(div1, t8);
          append(div1, select0);
          append(select0, option0);
          for (let i = 0; i < each_blocks_1.length; i += 1) {
            if (each_blocks_1[i]) {
              each_blocks_1[i].m(select0, null);
            }
          }
          select_options(
            select0,
            /*$userStore*/
            ctx[3]
          );
          append(fieldset0, t10);
          append(fieldset0, div4);
          append(div4, div2);
          append(div4, t12);
          append(div4, div3);
          append(div3, select1);
          append(select1, option1);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(select1, null);
            }
          }
          select_option(
            select1,
            /*selectedPreset*/
            ctx[2],
            true
          );
          append(div3, t14);
          append(div3, button2);
          append(div3, t15);
          append(div3, button3);
          append(button3, i2);
          append(div3, t16);
          append(div3, button4);
          append(button4, i3);
          append(div16, t17);
          append(div16, fieldset1);
          append(fieldset1, legend1);
          append(fieldset1, t19);
          append(fieldset1, div11);
          mount_component(sliderinput0, div11, null);
          append(div11, t20);
          mount_component(numberinput0, div11, null);
          append(div11, t21);
          mount_component(numberinput1, div11, null);
          append(div11, t22);
          append(div11, div5);
          append(div11, t23);
          mount_component(sliderinput1, div11, null);
          append(div11, t24);
          append(div11, div6);
          append(div11, t25);
          mount_component(checkbox0, div11, null);
          append(div11, t26);
          append(div11, div7);
          append(div11, t27);
          append(div11, div8);
          append(div11, t28);
          mount_component(checkbox1, div11, null);
          append(div11, t29);
          mount_component(checkbox2, div11, null);
          append(div11, t30);
          mount_component(checkbox3, div11, null);
          append(div11, t31);
          mount_component(checkbox4, div11, null);
          append(div11, t32);
          append(div11, div9);
          append(div11, t33);
          mount_component(numberinput2, div11, null);
          append(div11, t34);
          mount_component(numberinput3, div11, null);
          append(div11, t35);
          mount_component(checkbox5, div11, null);
          append(div11, t36);
          mount_component(numberinput4, div11, null);
          append(div11, t37);
          mount_component(checkbox6, div11, null);
          append(div11, t38);
          append(div11, div10);
          append(div16, t39);
          append(div16, fieldset2);
          append(fieldset2, legend2);
          append(fieldset2, t41);
          append(fieldset2, div15);
          mount_component(numberinput5, div15, null);
          append(div15, t42);
          mount_component(numberinput6, div15, null);
          append(div15, t43);
          append(div15, div12);
          append(div15, t44);
          mount_component(checkbox7, div15, null);
          append(div15, t45);
          mount_component(numberinput7, div15, null);
          append(div15, t46);
          mount_component(numberinput8, div15, null);
          append(div15, t47);
          mount_component(numberinput9, div15, null);
          append(div15, t48);
          append(div15, div13);
          append(div15, t49);
          mount_component(switchtoggle, div15, null);
          append(div15, t50);
          mount_component(numberinput10, div15, null);
          append(div15, t51);
          append(div15, div14);
          append(div15, t52);
          mount_component(checkbox8, div15, null);
          append(div15, t53);
          mount_component(checkbox9, div15, null);
          append(div15, t54);
          mount_component(checkbox10, div15, null);
          append(div15, t55);
          mount_component(checkbox11, div15, null);
          append(div15, t56);
          mount_component(checkbox12, div15, null);
          current = true;
          if (!mounted) {
            dispose = [
              listen(
                button0,
                "click",
                /*click_handler*/
                ctx[9]
              ),
              listen(
                input,
                "input",
                /*input_input_handler*/
                ctx[10]
              ),
              listen(
                button1,
                "click",
                /*handleClick*/
                ctx[7]
              ),
              listen(
                select0,
                "change",
                /*select0_change_handler*/
                ctx[11]
              ),
              listen(
                select1,
                "change",
                /*select1_change_handler*/
                ctx[12]
              ),
              listen(
                select1,
                "change",
                /*change_handler*/
                ctx[13]
              ),
              listen(
                button2,
                "click",
                /*click_handler_1*/
                ctx[14]
              ),
              listen(
                button3,
                "click",
                /*click_handler_2*/
                ctx[15]
              ),
              listen(
                button4,
                "click",
                /*click_handler_3*/
                ctx[16]
              )
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (dirty & /*$fileStore*/
          1 && input.value !== /*$fileStore*/
          ctx2[0]) {
            set_input_value(
              input,
              /*$fileStore*/
              ctx2[0]
            );
          }
          if (dirty & /*suggestions*/
          2) {
            each_value_2 = ensure_array_like(
              /*suggestions*/
              ctx2[1]
            );
            let i;
            for (i = 0; i < each_value_2.length; i += 1) {
              const child_ctx = get_each_context_2(ctx2, each_value_2, i);
              if (each_blocks_2[i]) {
                each_blocks_2[i].p(child_ctx, dirty);
              } else {
                each_blocks_2[i] = create_each_block_2(child_ctx);
                each_blocks_2[i].c();
                each_blocks_2[i].m(datalist, null);
              }
            }
            for (; i < each_blocks_2.length; i += 1) {
              each_blocks_2[i].d(1);
            }
            each_blocks_2.length = each_value_2.length;
          }
          if (dirty & /*users*/
          32) {
            each_value_1 = ensure_array_like(
              /*users*/
              ctx2[5]
            );
            each_blocks_1 = update_keyed_each(each_blocks_1, dirty, get_key, 1, ctx2, each_value_1, each1_lookup, select0, destroy_block, create_each_block_1, null, get_each_context_1);
          }
          if (dirty & /*$userStore, users*/
          40) {
            select_options(
              select0,
              /*$userStore*/
              ctx2[3]
            );
          }
          if (dirty & /*presets*/
          256) {
            each_value = ensure_array_like(
              /*presets*/
              ctx2[8]
            );
            let i;
            for (i = 0; i < each_value.length; i += 1) {
              const child_ctx = get_each_context$2(ctx2, each_value, i);
              if (each_blocks[i]) {
                each_blocks[i].p(child_ctx, dirty);
              } else {
                each_blocks[i] = create_each_block$2(child_ctx);
                each_blocks[i].c();
                each_blocks[i].m(select1, null);
              }
            }
            for (; i < each_blocks.length; i += 1) {
              each_blocks[i].d(1);
            }
            each_blocks.length = each_value.length;
          }
          if (dirty & /*selectedPreset, presets*/
          260) {
            select_option(
              select1,
              /*selectedPreset*/
              ctx2[2]
            );
          }
          if (!current || dirty & /*selectedPreset, presets*/
          260 && button3_disabled_value !== (button3_disabled_value = /*selectedPreset*/
          ctx2[2] === "default")) {
            button3.disabled = button3_disabled_value;
          }
          if (!current || dirty & /*selectedPreset, presets*/
          260 && button4_disabled_value !== (button4_disabled_value = /*selectedPreset*/
          ctx2[2] === "default")) {
            button4.disabled = button4_disabled_value;
          }
        },
        i(local) {
          if (current) return;
          transition_in(sliderinput0.$$.fragment, local);
          transition_in(numberinput0.$$.fragment, local);
          transition_in(numberinput1.$$.fragment, local);
          transition_in(sliderinput1.$$.fragment, local);
          transition_in(checkbox0.$$.fragment, local);
          transition_in(checkbox1.$$.fragment, local);
          transition_in(checkbox2.$$.fragment, local);
          transition_in(checkbox3.$$.fragment, local);
          transition_in(checkbox4.$$.fragment, local);
          transition_in(numberinput2.$$.fragment, local);
          transition_in(numberinput3.$$.fragment, local);
          transition_in(checkbox5.$$.fragment, local);
          transition_in(numberinput4.$$.fragment, local);
          transition_in(checkbox6.$$.fragment, local);
          transition_in(numberinput5.$$.fragment, local);
          transition_in(numberinput6.$$.fragment, local);
          transition_in(checkbox7.$$.fragment, local);
          transition_in(numberinput7.$$.fragment, local);
          transition_in(numberinput8.$$.fragment, local);
          transition_in(numberinput9.$$.fragment, local);
          transition_in(switchtoggle.$$.fragment, local);
          transition_in(numberinput10.$$.fragment, local);
          transition_in(checkbox8.$$.fragment, local);
          transition_in(checkbox9.$$.fragment, local);
          transition_in(checkbox10.$$.fragment, local);
          transition_in(checkbox11.$$.fragment, local);
          transition_in(checkbox12.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(sliderinput0.$$.fragment, local);
          transition_out(numberinput0.$$.fragment, local);
          transition_out(numberinput1.$$.fragment, local);
          transition_out(sliderinput1.$$.fragment, local);
          transition_out(checkbox0.$$.fragment, local);
          transition_out(checkbox1.$$.fragment, local);
          transition_out(checkbox2.$$.fragment, local);
          transition_out(checkbox3.$$.fragment, local);
          transition_out(checkbox4.$$.fragment, local);
          transition_out(numberinput2.$$.fragment, local);
          transition_out(numberinput3.$$.fragment, local);
          transition_out(checkbox5.$$.fragment, local);
          transition_out(numberinput4.$$.fragment, local);
          transition_out(checkbox6.$$.fragment, local);
          transition_out(numberinput5.$$.fragment, local);
          transition_out(numberinput6.$$.fragment, local);
          transition_out(checkbox7.$$.fragment, local);
          transition_out(numberinput7.$$.fragment, local);
          transition_out(numberinput8.$$.fragment, local);
          transition_out(numberinput9.$$.fragment, local);
          transition_out(switchtoggle.$$.fragment, local);
          transition_out(numberinput10.$$.fragment, local);
          transition_out(checkbox8.$$.fragment, local);
          transition_out(checkbox9.$$.fragment, local);
          transition_out(checkbox10.$$.fragment, local);
          transition_out(checkbox11.$$.fragment, local);
          transition_out(checkbox12.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div16);
          }
          destroy_each(each_blocks_2, detaching);
          for (let i = 0; i < each_blocks_1.length; i += 1) {
            each_blocks_1[i].d();
          }
          destroy_each(each_blocks, detaching);
          destroy_component(sliderinput0);
          destroy_component(numberinput0);
          destroy_component(numberinput1);
          destroy_component(sliderinput1);
          destroy_component(checkbox0);
          destroy_component(checkbox1);
          destroy_component(checkbox2);
          destroy_component(checkbox3);
          destroy_component(checkbox4);
          destroy_component(numberinput2);
          destroy_component(numberinput3);
          destroy_component(checkbox5);
          destroy_component(numberinput4);
          destroy_component(checkbox6);
          destroy_component(numberinput5);
          destroy_component(numberinput6);
          destroy_component(checkbox7);
          destroy_component(numberinput7);
          destroy_component(numberinput8);
          destroy_component(numberinput9);
          destroy_component(switchtoggle);
          destroy_component(numberinput10);
          destroy_component(checkbox8);
          destroy_component(checkbox9);
          destroy_component(checkbox10);
          destroy_component(checkbox11);
          destroy_component(checkbox12);
          mounted = false;
          run_all(dispose);
        }
      };
    }
    let lastInput = "";
    async function activateLayer() {
      ui.controls.initialize({
        control: "sequencer",
        tool: "play-effect"
      });
      canvas.sequencerInterfaceLayer.activate({ tool: "play-effect" });
    }
    function instance$5($$self, $$props, $$invalidate) {
      let $fileStore;
      let $userStore;
      const fileStore = PlayerSettings.file.store;
      component_subscribe($$self, fileStore, (value) => $$invalidate(0, $fileStore = value));
      const users2 = game.users.filter((user) => user.active);
      const userStore = PlayerSettings.users.store;
      component_subscribe($$self, userStore, (value) => $$invalidate(3, $userStore = value));
      let lastResults = [];
      let suggestions = [];
      const searchDebounce = foundry.utils.debounce(
        () => {
          const fileInput = get_store_value(fileStore).toLowerCase();
          if (!fileInput) {
            $$invalidate(1, suggestions = SequencerDatabase.publicModules);
            return;
          }
          if (lastInput === fileInput) return;
          let results = SequencerDatabase.searchFor(fileInput);
          if (lastResults.equals(results)) return;
          lastResults = foundry.utils.duplicate(results);
          if (results.length === 1 && results[0].startsWith(fileInput)) return;
          if (results.length > 100) {
            results = results.slice(0, 100);
          }
          $$invalidate(1, suggestions = foundry.utils.duplicate(results));
        },
        200
      );
      let filePicker = false;
      function handleClick() {
        if (!filePicker) {
          const file = get_store_value(fileStore);
          const current = SequencerDatabase.getEntry(file, { softFail: true }) ? file : "";
          filePicker = new FilePicker({
            type: "imageVideo",
            current,
            callback: (path) => {
              fileStore.set(path);
              filePicker = false;
            }
          });
        }
        filePicker.render(true, { focus: true });
      }
      const presets = PlayerSettings.getPresets();
      let selectedPreset = "default";
      const click_handler = () => activateLayer();
      function input_input_handler() {
        $fileStore = this.value;
        fileStore.set($fileStore);
      }
      function select0_change_handler() {
        $userStore = select_multiple_value(this);
        userStore.set($userStore);
        $$invalidate(5, users2);
      }
      function select1_change_handler() {
        selectedPreset = select_value(this);
        $$invalidate(2, selectedPreset);
        $$invalidate(8, presets);
      }
      const change_handler = () => PlayerSettings.loadPreset(selectedPreset);
      const click_handler_1 = () => PlayerSettings.savePreset(selectedPreset);
      const click_handler_2 = () => PlayerSettings.copyPreset(selectedPreset);
      const click_handler_3 = () => PlayerSettings.deletePreset(selectedPreset);
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*$fileStore*/
        1) {
          searchDebounce($fileStore);
        }
      };
      return [
        $fileStore,
        suggestions,
        selectedPreset,
        $userStore,
        fileStore,
        users2,
        userStore,
        handleClick,
        presets,
        click_handler,
        input_input_handler,
        select0_change_handler,
        select1_change_handler,
        change_handler,
        click_handler_1,
        click_handler_2,
        click_handler_3
      ];
    }
    class Player extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$5, create_fragment$5, safe_not_equal, {});
      }
    }
    function create_fragment$4(ctx) {
      let i0;
      let t;
      let i1;
      return {
        c() {
          i0 = element("i");
          i0.innerHTML = ``;
          t = space();
          i1 = element("i");
          attr(i0, "class", "fa-solid background-circle svelte-seq-1wkm0y3");
          toggle_class(
            i0,
            "invisible",
            /*$status*/
            ctx[1] === CONSTANTS.STATUS.READY
          );
          toggle_class(
            i0,
            "fa-arrow-right",
            /*$status*/
            ctx[1] === CONSTANTS.STATUS.RUNNING || /*$status*/
            ctx[1] === CONSTANTS.STATUS.READY
          );
          toggle_class(
            i0,
            "fa-check",
            /*$status*/
            ctx[1] === CONSTANTS.STATUS.COMPLETE
          );
          toggle_class(
            i0,
            "fa-arrow-down",
            /*$status*/
            ctx[1] === CONSTANTS.STATUS.SKIPPED
          );
          toggle_class(
            i0,
            "fa-times",
            /*$status*/
            ctx[1] === CONSTANTS.STATUS.ABORTED
          );
          attr(i1, "class", "fa-solid ");
        },
        m(target, anchor) {
          insert(target, i0, anchor);
          insert(target, t, anchor);
          insert(target, i1, anchor);
        },
        p(ctx2, [dirty]) {
          if (dirty & /*$status*/
          2) {
            toggle_class(
              i0,
              "invisible",
              /*$status*/
              ctx2[1] === CONSTANTS.STATUS.READY
            );
          }
          if (dirty & /*$status*/
          2) {
            toggle_class(
              i0,
              "fa-arrow-right",
              /*$status*/
              ctx2[1] === CONSTANTS.STATUS.RUNNING || /*$status*/
              ctx2[1] === CONSTANTS.STATUS.READY
            );
          }
          if (dirty & /*$status*/
          2) {
            toggle_class(
              i0,
              "fa-check",
              /*$status*/
              ctx2[1] === CONSTANTS.STATUS.COMPLETE
            );
          }
          if (dirty & /*$status*/
          2) {
            toggle_class(
              i0,
              "fa-arrow-down",
              /*$status*/
              ctx2[1] === CONSTANTS.STATUS.SKIPPED
            );
          }
          if (dirty & /*$status*/
          2) {
            toggle_class(
              i0,
              "fa-times",
              /*$status*/
              ctx2[1] === CONSTANTS.STATUS.ABORTED
            );
          }
        },
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(i0);
            detach(t);
            detach(i1);
          }
        }
      };
    }
    function instance$4($$self, $$props, $$invalidate) {
      let $status, $$unsubscribe_status = noop, $$subscribe_status = () => ($$unsubscribe_status(), $$unsubscribe_status = subscribe(status, ($$value) => $$invalidate(1, $status = $$value)), status);
      $$self.$$.on_destroy.push(() => $$unsubscribe_status());
      let { status } = $$props;
      $$subscribe_status();
      $$self.$$set = ($$props2) => {
        if ("status" in $$props2) $$subscribe_status($$invalidate(0, status = $$props2.status));
      };
      return [status, $status];
    }
    class SequenceStatus extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$4, create_fragment$4, safe_not_equal, { status: 0 });
      }
    }
    function create_fragment$3(ctx) {
      let div;
      let span0;
      let sequencestatus;
      let t0;
      let span1;
      let t2;
      let span2;
      let a;
      let current;
      let mounted;
      let dispose;
      sequencestatus = new SequenceStatus({ props: { status: (
        /*status*/
        ctx[2]
      ) } });
      return {
        c() {
          div = element("div");
          span0 = element("span");
          create_component(sequencestatus.$$.fragment);
          t0 = space();
          span1 = element("span");
          span1.textContent = `${/*sectionName*/
          ctx[3]}`;
          t2 = space();
          span2 = element("span");
          a = element("a");
          a.innerHTML = `<i class="fas fa-stop"></i>`;
          attr(span1, "class", "section-name svelte-seq-1ee9h3");
          attr(a, "class", "svelte-seq-1ee9h3");
          toggle_class(
            a,
            "section-done",
            /*$status*/
            ctx[1] > CONSTANTS.STATUS.READY
          );
          attr(span2, "class", "sequence-actions svelte-seq-1ee9h3");
          attr(span2, "data-tooltip", localize("SEQUENCER.Sequences.AbortSection"));
          attr(div, "class", "svelte-seq-1ee9h3");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          append(div, span0);
          mount_component(sequencestatus, span0, null);
          append(div, t0);
          append(div, span1);
          append(div, t2);
          append(div, span2);
          append(span2, a);
          current = true;
          if (!mounted) {
            dispose = listen(
              a,
              "click",
              /*click_handler*/
              ctx[4]
            );
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (!current || dirty & /*$status*/
          2) {
            toggle_class(
              a,
              "section-done",
              /*$status*/
              ctx2[1] > CONSTANTS.STATUS.READY
            );
          }
        },
        i(local) {
          if (current) return;
          transition_in(sequencestatus.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(sequencestatus.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          destroy_component(sequencestatus);
          mounted = false;
          dispose();
        }
      };
    }
    function instance$3($$self, $$props, $$invalidate) {
      let $status;
      let { section } = $$props;
      const status = section.sectionStatus;
      component_subscribe($$self, status, (value) => $$invalidate(1, $status = value));
      const sectionName = (section?.constructor?.niceName ? section?.constructor?.niceName : section.constructor.name) + (section?._name ? " - " + section._name : "");
      const click_handler = () => {
        section._abortSection();
      };
      $$self.$$set = ($$props2) => {
        if ("section" in $$props2) $$invalidate(0, section = $$props2.section);
      };
      return [section, $status, status, sectionName, click_handler];
    }
    class SequenceSection extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$3, create_fragment$3, safe_not_equal, { section: 0 });
      }
    }
    function get_each_context$1(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[6] = list[i];
      return child_ctx;
    }
    function create_each_block$1(ctx) {
      let sequencesection;
      let current;
      sequencesection = new SequenceSection({ props: { section: (
        /*section*/
        ctx[6]
      ) } });
      return {
        c() {
          create_component(sequencesection.$$.fragment);
        },
        m(target, anchor) {
          mount_component(sequencesection, target, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          const sequencesection_changes = {};
          if (dirty & /*sequence*/
          1) sequencesection_changes.section = /*section*/
          ctx2[6];
          sequencesection.$set(sequencesection_changes);
        },
        i(local) {
          if (current) return;
          transition_in(sequencesection.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(sequencesection.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          destroy_component(sequencesection, detaching);
        }
      };
    }
    function create_fragment$2(ctx) {
      let div;
      let span0;
      let sequencestatus;
      let t0;
      let span1;
      let t1;
      let t2;
      let t3_value = (
        /*sequence*/
        ctx[0].moduleName ? ` (${/*sequence*/
        ctx[0].moduleName})` : ""
      );
      let t3;
      let t4;
      let span2;
      let a0;
      let t5;
      let a1;
      let t6;
      let each_1_anchor;
      let current;
      let mounted;
      let dispose;
      sequencestatus = new SequenceStatus({ props: { status: (
        /*status*/
        ctx[3]
      ) } });
      let each_value = ensure_array_like(
        /*sequence*/
        ctx[0].sections
      );
      let each_blocks = [];
      for (let i = 0; i < each_value.length; i += 1) {
        each_blocks[i] = create_each_block$1(get_each_context$1(ctx, each_value, i));
      }
      const out = (i) => transition_out(each_blocks[i], 1, 1, () => {
        each_blocks[i] = null;
      });
      return {
        c() {
          div = element("div");
          span0 = element("span");
          create_component(sequencestatus.$$.fragment);
          t0 = space();
          span1 = element("span");
          t1 = text$1("Sequence ");
          t2 = text$1(
            /*index*/
            ctx[1]
          );
          t3 = text$1(t3_value);
          t4 = space();
          span2 = element("span");
          a0 = element("a");
          a0.innerHTML = `<i class="fas fa-trash-can"></i>`;
          t5 = space();
          a1 = element("a");
          a1.innerHTML = `<i class="fas fa-stop"></i>`;
          t6 = space();
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
          each_1_anchor = empty();
          attr(span1, "class", "sequence-name svelte-seq-1dcwqos");
          attr(a0, "class", "clear-sequence svelte-seq-1dcwqos");
          attr(a0, "data-tooltip", localize("SEQUENCER.Sequences.Clear"));
          toggle_class(
            a0,
            "sequence-done-show",
            /*$status*/
            ctx[2] > CONSTANTS.STATUS.RUNNING
          );
          attr(a1, "data-tooltip", localize("SEQUENCER.Sequences.AbortSequence"));
          attr(a1, "class", "svelte-seq-1dcwqos");
          toggle_class(
            a1,
            "sequence-done-hide",
            /*$status*/
            ctx[2] > CONSTANTS.STATUS.RUNNING
          );
          attr(span2, "class", "sequence-actions svelte-seq-1dcwqos");
          attr(div, "class", "sequence-name-container svelte-seq-1dcwqos");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          append(div, span0);
          mount_component(sequencestatus, span0, null);
          append(div, t0);
          append(div, span1);
          append(span1, t1);
          append(span1, t2);
          append(span1, t3);
          append(div, t4);
          append(div, span2);
          append(span2, a0);
          append(span2, t5);
          append(span2, a1);
          insert(target, t6, anchor);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(target, anchor);
            }
          }
          insert(target, each_1_anchor, anchor);
          current = true;
          if (!mounted) {
            dispose = [
              listen(
                a0,
                "click",
                /*click_handler*/
                ctx[4]
              ),
              listen(
                a1,
                "click",
                /*click_handler_1*/
                ctx[5]
              )
            ];
            mounted = true;
          }
        },
        p(ctx2, [dirty]) {
          if (!current || dirty & /*index*/
          2) set_data(
            t2,
            /*index*/
            ctx2[1]
          );
          if ((!current || dirty & /*sequence*/
          1) && t3_value !== (t3_value = /*sequence*/
          ctx2[0].moduleName ? ` (${/*sequence*/
          ctx2[0].moduleName})` : "")) set_data(t3, t3_value);
          if (!current || dirty & /*$status*/
          4) {
            toggle_class(
              a0,
              "sequence-done-show",
              /*$status*/
              ctx2[2] > CONSTANTS.STATUS.RUNNING
            );
          }
          if (!current || dirty & /*$status*/
          4) {
            toggle_class(
              a1,
              "sequence-done-hide",
              /*$status*/
              ctx2[2] > CONSTANTS.STATUS.RUNNING
            );
          }
          if (dirty & /*sequence*/
          1) {
            each_value = ensure_array_like(
              /*sequence*/
              ctx2[0].sections
            );
            let i;
            for (i = 0; i < each_value.length; i += 1) {
              const child_ctx = get_each_context$1(ctx2, each_value, i);
              if (each_blocks[i]) {
                each_blocks[i].p(child_ctx, dirty);
                transition_in(each_blocks[i], 1);
              } else {
                each_blocks[i] = create_each_block$1(child_ctx);
                each_blocks[i].c();
                transition_in(each_blocks[i], 1);
                each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor);
              }
            }
            group_outros();
            for (i = each_value.length; i < each_blocks.length; i += 1) {
              out(i);
            }
            check_outros();
          }
        },
        i(local) {
          if (current) return;
          transition_in(sequencestatus.$$.fragment, local);
          for (let i = 0; i < each_value.length; i += 1) {
            transition_in(each_blocks[i]);
          }
          current = true;
        },
        o(local) {
          transition_out(sequencestatus.$$.fragment, local);
          each_blocks = each_blocks.filter(Boolean);
          for (let i = 0; i < each_blocks.length; i += 1) {
            transition_out(each_blocks[i]);
          }
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
            detach(t6);
            detach(each_1_anchor);
          }
          destroy_component(sequencestatus);
          destroy_each(each_blocks, detaching);
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function instance$2($$self, $$props, $$invalidate) {
      let $status;
      let { sequence } = $$props;
      let { index } = $$props;
      const status = sequence.statusStore;
      component_subscribe($$self, status, (value) => $$invalidate(2, $status = value));
      const click_handler = () => {
        SequenceManager.RunningSequences.delete(sequence.id);
      };
      const click_handler_1 = () => {
        sequence._abort();
      };
      $$self.$$set = ($$props2) => {
        if ("sequence" in $$props2) $$invalidate(0, sequence = $$props2.sequence);
        if ("index" in $$props2) $$invalidate(1, index = $$props2.index);
      };
      return [sequence, index, $status, status, click_handler, click_handler_1];
    }
    let Sequence$2 = class Sequence extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$2, create_fragment$2, safe_not_equal, { sequence: 0, index: 1 });
      }
    };
    function get_each_context(ctx, list, i) {
      const child_ctx = ctx.slice();
      child_ctx[6] = list[i][0];
      child_ctx[7] = list[i][1];
      child_ctx[9] = i;
      return child_ctx;
    }
    function create_else_block(ctx) {
      let div0;
      let button0;
      let t1;
      let button1;
      let t3;
      let div1;
      let each_blocks = [];
      let each_1_lookup = /* @__PURE__ */ new Map();
      let current;
      let mounted;
      let dispose;
      let each_value = ensure_array_like(
        /*runningSequences*/
        ctx[0]
      );
      const get_key = (ctx2) => (
        /*id*/
        ctx2[6]
      );
      for (let i = 0; i < each_value.length; i += 1) {
        let child_ctx = get_each_context(ctx, each_value, i);
        let key = get_key(child_ctx);
        each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx));
      }
      return {
        c() {
          div0 = element("div");
          button0 = element("button");
          button0.textContent = `${localize("SEQUENCER.Sequences.ClearFinished")}`;
          t1 = space();
          button1 = element("button");
          button1.textContent = `${localize("SEQUENCER.Sequences.StopAll")}`;
          t3 = space();
          div1 = element("div");
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].c();
          }
          attr(button0, "type", "button");
          attr(button0, "class", "svelte-seq-1ismzan");
          attr(button1, "type", "button");
          attr(button1, "class", "svelte-seq-1ismzan");
          attr(div0, "class", "sequence-button-header svelte-seq-1ismzan");
          attr(div1, "class", "running-sequences svelte-seq-1ismzan");
        },
        m(target, anchor) {
          insert(target, div0, anchor);
          append(div0, button0);
          append(div0, t1);
          append(div0, button1);
          insert(target, t3, anchor);
          insert(target, div1, anchor);
          for (let i = 0; i < each_blocks.length; i += 1) {
            if (each_blocks[i]) {
              each_blocks[i].m(div1, null);
            }
          }
          current = true;
          if (!mounted) {
            dispose = [
              listen(
                button0,
                "click",
                /*click_handler*/
                ctx[3]
              ),
              listen(
                button1,
                "click",
                /*click_handler_1*/
                ctx[4]
              )
            ];
            mounted = true;
          }
        },
        p(ctx2, dirty) {
          if (dirty & /*runningSequences*/
          1) {
            each_value = ensure_array_like(
              /*runningSequences*/
              ctx2[0]
            );
            group_outros();
            each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx2, each_value, each_1_lookup, div1, outro_and_destroy_block, create_each_block, null, get_each_context);
            check_outros();
          }
        },
        i(local) {
          if (current) return;
          for (let i = 0; i < each_value.length; i += 1) {
            transition_in(each_blocks[i]);
          }
          current = true;
        },
        o(local) {
          for (let i = 0; i < each_blocks.length; i += 1) {
            transition_out(each_blocks[i]);
          }
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div0);
            detach(t3);
            detach(div1);
          }
          for (let i = 0; i < each_blocks.length; i += 1) {
            each_blocks[i].d();
          }
          mounted = false;
          run_all(dispose);
        }
      };
    }
    function create_if_block(ctx) {
      let div;
      let h2;
      return {
        c() {
          div = element("div");
          h2 = element("h2");
          h2.textContent = `${localize("SEQUENCER.Sequences.NoSequences")}`;
          attr(div, "class", "no-sequences");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          append(div, h2);
        },
        p: noop,
        i: noop,
        o: noop,
        d(detaching) {
          if (detaching) {
            detach(div);
          }
        }
      };
    }
    function create_each_block(key_1, ctx) {
      let first;
      let sequence_1;
      let current;
      sequence_1 = new Sequence$2({
        props: {
          sequence: (
            /*sequence*/
            ctx[7]
          ),
          index: (
            /*index*/
            ctx[9] + 1
          )
        }
      });
      return {
        key: key_1,
        first: null,
        c() {
          first = empty();
          create_component(sequence_1.$$.fragment);
          this.first = first;
        },
        m(target, anchor) {
          insert(target, first, anchor);
          mount_component(sequence_1, target, anchor);
          current = true;
        },
        p(new_ctx, dirty) {
          ctx = new_ctx;
          const sequence_1_changes = {};
          if (dirty & /*runningSequences*/
          1) sequence_1_changes.sequence = /*sequence*/
          ctx[7];
          if (dirty & /*runningSequences*/
          1) sequence_1_changes.index = /*index*/
          ctx[9] + 1;
          sequence_1.$set(sequence_1_changes);
        },
        i(local) {
          if (current) return;
          transition_in(sequence_1.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(sequence_1.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(first);
          }
          destroy_component(sequence_1, detaching);
        }
      };
    }
    function create_fragment$1(ctx) {
      let div;
      let current_block_type_index;
      let if_block;
      let current;
      const if_block_creators = [create_if_block, create_else_block];
      const if_blocks = [];
      function select_block_type(ctx2, dirty) {
        if (!/*runningSequences*/
        ctx2[0].length) return 0;
        return 1;
      }
      current_block_type_index = select_block_type(ctx);
      if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
      return {
        c() {
          div = element("div");
          if_block.c();
          attr(div, "class", "sequence-container svelte-seq-1ismzan");
        },
        m(target, anchor) {
          insert(target, div, anchor);
          if_blocks[current_block_type_index].m(div, null);
          current = true;
        },
        p(ctx2, [dirty]) {
          let previous_block_index = current_block_type_index;
          current_block_type_index = select_block_type(ctx2);
          if (current_block_type_index === previous_block_index) {
            if_blocks[current_block_type_index].p(ctx2, dirty);
          } else {
            group_outros();
            transition_out(if_blocks[previous_block_index], 1, 1, () => {
              if_blocks[previous_block_index] = null;
            });
            check_outros();
            if_block = if_blocks[current_block_type_index];
            if (!if_block) {
              if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
              if_block.c();
            } else {
              if_block.p(ctx2, dirty);
            }
            transition_in(if_block, 1);
            if_block.m(div, null);
          }
        },
        i(local) {
          if (current) return;
          transition_in(if_block);
          current = true;
        },
        o(local) {
          transition_out(if_block);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(div);
          }
          if_blocks[current_block_type_index].d();
        }
      };
    }
    function instance$1($$self, $$props, $$invalidate) {
      let runningSequences;
      let $RunningSequences;
      const RunningSequences = SequenceManager.RunningSequences;
      component_subscribe($$self, RunningSequences, (value) => $$invalidate(2, $RunningSequences = value));
      onMount(() => {
        SequenceManager.RunningSequences.clearFinishedSequences();
      });
      onDestroy(() => {
        SequenceManager.RunningSequences.clearFinishedSequences();
      });
      const click_handler = () => {
        SequenceManager.RunningSequences.clearFinishedSequences();
      };
      const click_handler_1 = () => {
        SequenceManager.RunningSequences.stopAll();
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*$RunningSequences*/
        4) {
          $$invalidate(0, runningSequences = Object.entries($RunningSequences));
        }
      };
      return [
        runningSequences,
        RunningSequences,
        $RunningSequences,
        click_handler,
        click_handler_1
      ];
    }
    class Sequences extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance$1, create_fragment$1, safe_not_equal, {});
      }
    }
    function create_default_slot(ctx) {
      let tabs_1;
      let updating_activeTab;
      let t;
      let switch_instance;
      let switch_instance_anchor;
      let current;
      function tabs_1_activeTab_binding(value) {
        ctx[4](value);
      }
      let tabs_1_props = { tabs: (
        /*tabs*/
        ctx[3]
      ) };
      if (
        /*activeTab*/
        ctx[1] !== void 0
      ) {
        tabs_1_props.activeTab = /*activeTab*/
        ctx[1];
      }
      tabs_1 = new Tabs({ props: tabs_1_props });
      binding_callbacks.push(() => bind(tabs_1, "activeTab", tabs_1_activeTab_binding));
      var switch_value = (
        /*component*/
        ctx[2]
      );
      function switch_props(ctx2, dirty) {
        return {};
      }
      if (switch_value) {
        switch_instance = construct_svelte_component(switch_value, switch_props());
      }
      return {
        c() {
          create_component(tabs_1.$$.fragment);
          t = space();
          if (switch_instance) create_component(switch_instance.$$.fragment);
          switch_instance_anchor = empty();
        },
        m(target, anchor) {
          mount_component(tabs_1, target, anchor);
          insert(target, t, anchor);
          if (switch_instance) mount_component(switch_instance, target, anchor);
          insert(target, switch_instance_anchor, anchor);
          current = true;
        },
        p(ctx2, dirty) {
          const tabs_1_changes = {};
          if (!updating_activeTab && dirty & /*activeTab*/
          2) {
            updating_activeTab = true;
            tabs_1_changes.activeTab = /*activeTab*/
            ctx2[1];
            add_flush_callback(() => updating_activeTab = false);
          }
          tabs_1.$set(tabs_1_changes);
          if (dirty & /*component*/
          4 && switch_value !== (switch_value = /*component*/
          ctx2[2])) {
            if (switch_instance) {
              group_outros();
              const old_component = switch_instance;
              transition_out(old_component.$$.fragment, 1, 0, () => {
                destroy_component(old_component, 1);
              });
              check_outros();
            }
            if (switch_value) {
              switch_instance = construct_svelte_component(switch_value, switch_props());
              create_component(switch_instance.$$.fragment);
              transition_in(switch_instance.$$.fragment, 1);
              mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
            } else {
              switch_instance = null;
            }
          }
        },
        i(local) {
          if (current) return;
          transition_in(tabs_1.$$.fragment, local);
          if (switch_instance) transition_in(switch_instance.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(tabs_1.$$.fragment, local);
          if (switch_instance) transition_out(switch_instance.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          if (detaching) {
            detach(t);
            detach(switch_instance_anchor);
          }
          destroy_component(tabs_1, detaching);
          if (switch_instance) destroy_component(switch_instance, detaching);
        }
      };
    }
    function create_fragment(ctx) {
      let applicationshell;
      let updating_elementRoot;
      let current;
      function applicationshell_elementRoot_binding(value) {
        ctx[5](value);
      }
      let applicationshell_props = {
        $$slots: { default: [create_default_slot] },
        $$scope: { ctx }
      };
      if (
        /*elementRoot*/
        ctx[0] !== void 0
      ) {
        applicationshell_props.elementRoot = /*elementRoot*/
        ctx[0];
      }
      applicationshell = new ApplicationShell({ props: applicationshell_props });
      binding_callbacks.push(() => bind(applicationshell, "elementRoot", applicationshell_elementRoot_binding));
      return {
        c() {
          create_component(applicationshell.$$.fragment);
        },
        m(target, anchor) {
          mount_component(applicationshell, target, anchor);
          current = true;
        },
        p(ctx2, [dirty]) {
          const applicationshell_changes = {};
          if (dirty & /*$$scope, component, activeTab*/
          134) {
            applicationshell_changes.$$scope = { dirty, ctx: ctx2 };
          }
          if (!updating_elementRoot && dirty & /*elementRoot*/
          1) {
            updating_elementRoot = true;
            applicationshell_changes.elementRoot = /*elementRoot*/
            ctx2[0];
            add_flush_callback(() => updating_elementRoot = false);
          }
          applicationshell.$set(applicationshell_changes);
        },
        i(local) {
          if (current) return;
          transition_in(applicationshell.$$.fragment, local);
          current = true;
        },
        o(local) {
          transition_out(applicationshell.$$.fragment, local);
          current = false;
        },
        d(detaching) {
          destroy_component(applicationshell, detaching);
        }
      };
    }
    function instance($$self, $$props, $$invalidate) {
      let component;
      const { application } = getContext("#external");
      let { elementRoot } = $$props;
      let tabs = [
        {
          value: "player",
          label: localize("SEQUENCER.Player.Title"),
          icon: "fas fa-play-circle",
          component: Player
        },
        {
          value: "manager",
          label: localize("SEQUENCER.Manager.Title"),
          icon: "fas fa-film",
          component: Manager
        },
        {
          value: "sequences",
          label: localize("SEQUENCER.Sequences.Title"),
          icon: "fas fa-play",
          component: Sequences
        },
        {
          value: "howto",
          label: localize("SEQUENCER.HowTo.Title"),
          icon: "fas fa-chalkboard-teacher",
          component: HowTo
        }
      ];
      let activeTab = application.options.tab ?? "manager";
      function tabs_1_activeTab_binding(value) {
        activeTab = value;
        $$invalidate(1, activeTab);
      }
      function applicationshell_elementRoot_binding(value) {
        elementRoot = value;
        $$invalidate(0, elementRoot);
      }
      $$self.$$set = ($$props2) => {
        if ("elementRoot" in $$props2) $$invalidate(0, elementRoot = $$props2.elementRoot);
      };
      $$self.$$.update = () => {
        if ($$self.$$.dirty & /*activeTab*/
        2) {
          $$invalidate(2, component = tabs.find((tab) => tab.value === activeTab).component);
        }
      };
      return [
        elementRoot,
        activeTab,
        component,
        tabs,
        tabs_1_activeTab_binding,
        applicationshell_elementRoot_binding
      ];
    }
    class Effects_ui_shell extends SvelteComponent {
      constructor(options) {
        super();
        init(this, options, instance, create_fragment, safe_not_equal, { elementRoot: 0 });
      }
      get elementRoot() {
        return this.$$.ctx[0];
      }
      set elementRoot(elementRoot) {
        this.$$set({ elementRoot });
        flush();
      }
    }
    class EffectsUIApp extends SvelteApp {
      static get defaultOptions() {
        return foundry.utils.mergeObject(super.defaultOptions, {
          title: game.i18n.localize("SEQUENCER.ManagerUI"),
          classes: ["dialog"],
          width: "auto",
          height: "auto",
          top: 65,
          left: 120,
          resizable: false,
          svelte: {
            class: Effects_ui_shell,
            target: document.body
          }
        });
      }
      static getActiveApp() {
        return Object.values(ui.windows).find((app) => {
          return app instanceof this && app._state > Application.RENDER_STATES.CLOSED;
        });
      }
      static async show(options = {}) {
        const existingApp = this.getActiveApp();
        if (existingApp) return existingApp.render(false, { focus: true });
        return new Promise((resolve) => {
          options.resolve = resolve;
          new this(options).render(true, { focus: true });
        });
      }
    }
    function registerSettings() {
      game.settings.register(CONSTANTS.MODULE_NAME, "enable-fix-pixi", {
        name: "SEQUENCER.Setting.EnablePixiFix.Title",
        hint: "SEQUENCER.Setting.EnablePixiFix.Label",
        scope: "client",
        config: true,
        default: false,
        requiresReload: true,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "enable-global-fix-pixi", {
        name: "SEQUENCER.Setting.EnableGlobalPixiFix.Title",
        hint: "SEQUENCER.Setting.EnableGlobalPixiFix.Label",
        scope: "client",
        config: true,
        default: false,
        requiresReload: true,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "enable-above-ui-screenspace", {
        name: "SEQUENCER.Setting.EnableAboveUIScreenspace.Title",
        hint: "SEQUENCER.Setting.EnableAboveUIScreenspace.Label",
        scope: "client",
        config: true,
        default: true,
        requiresReload: true,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "debug", {
        name: "SEQUENCER.Setting.Debug.Title",
        hint: "SEQUENCER.Setting.Debug.Label",
        scope: "client",
        config: true,
        default: false,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "showSidebarTools", {
        name: "SEQUENCER.Setting.ShowTools.Title",
        hint: "SEQUENCER.Setting.ShowTools.Label",
        scope: "client",
        config: true,
        default: true,
        requiresReload: true,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "showTokenSidebarTools", {
        name: "SEQUENCER.Setting.ShowTokenTools.Title",
        hint: "SEQUENCER.Setting.ShowTokenTools.Label",
        scope: "client",
        config: true,
        default: true,
        requiresReload: true,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "effectsEnabled", {
        name: "SEQUENCER.Setting.EnableEffects.Title",
        hint: "SEQUENCER.Setting.EnableEffects.Label",
        scope: "client",
        config: true,
        default: true,
        requiresReload: true,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "soundsEnabled", {
        name: "SEQUENCER.Setting.EnableSounds.Title",
        hint: "SEQUENCER.Setting.EnableSounds.Label",
        scope: "client",
        config: true,
        default: true,
        requiresReload: true,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "user-effect-opacity", {
        name: "SEQUENCER.Setting.ExternalEffectOpacity.Title",
        hint: "SEQUENCER.Setting.ExternalEffectOpacity.Label",
        scope: "client",
        config: true,
        default: 50,
        type: Number,
        range: {
          min: 0,
          max: 100,
          step: 1
        }
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "db-list-view", {
        scope: "client",
        config: false,
        default: false,
        type: Boolean
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "welcome-shown", {
        scope: "world",
        config: false,
        default: false,
        type: Boolean
      });
      const permissionLevels = [
        game.i18n.localize("SEQUENCER.Permission.Player"),
        game.i18n.localize("SEQUENCER.Permission.Trusted"),
        game.i18n.localize("SEQUENCER.Permission.Assistant"),
        game.i18n.localize("SEQUENCER.Permission.GM")
      ];
      game.settings.register(CONSTANTS.MODULE_NAME, "permissions-effect-create", {
        name: "SEQUENCER.Setting.Permission.EffectCreate.Title",
        hint: "SEQUENCER.Setting.Permission.EffectCreate.Label",
        scope: "world",
        config: true,
        default: 0,
        type: Number,
        choices: permissionLevels,
        requiresReload: true
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "permissions-effect-delete", {
        name: "SEQUENCER.Setting.Permission.EffectDelete.Title",
        hint: "SEQUENCER.Setting.Permission.EffectDelete.Label",
        scope: "world",
        config: true,
        default: 2,
        type: Number,
        choices: permissionLevels,
        requiresReload: true
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "permissions-sound-create", {
        name: "SEQUENCER.Setting.Permission.SoundCreate.Title",
        hint: "SEQUENCER.Setting.Permission.SoundCreate.Label",
        scope: "world",
        config: true,
        default: 0,
        type: Number,
        choices: permissionLevels,
        requiresReload: true
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "permissions-preload", {
        name: "SEQUENCER.Setting.Permission.PreloadClients.Title",
        hint: "SEQUENCER.Setting.Permission.PreloadClients.Label",
        scope: "world",
        config: true,
        default: 1,
        type: Number,
        choices: permissionLevels,
        requiresReload: true
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "permissions-sidebar-tools", {
        name: "SEQUENCER.Setting.Permission.UseSidebarTools.Title",
        hint: "SEQUENCER.Setting.Permission.UseSidebarTools.Label",
        scope: "world",
        config: true,
        default: 0,
        type: Number,
        choices: permissionLevels,
        requiresReload: true
      });
      game.settings.register(CONSTANTS.MODULE_NAME, "effectPresets", {
        scope: "client",
        default: {},
        type: Object
      });
      Hooks.on("getSceneControlButtons", (controls) => {
        if (!game.settings.get(CONSTANTS.MODULE_NAME, "showSidebarTools")) return;
        const selectTool = {
          icon: "fas fa-expand",
          name: "select-effect",
          title: "SEQUENCER.SidebarButtons.Select",
          visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools")
        };
        const playTool = {
          icon: "fas fa-play",
          name: "play-effect",
          title: "SEQUENCER.SidebarButtons.Play",
          visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
          onClick: () => {
            EffectsUIApp.show({ inFocus: true, tab: "player" });
          }
        };
        const viewer = {
          icon: "fas fa-film",
          name: "effectviewer",
          title: "SEQUENCER.SidebarButtons.Manager",
          button: true,
          visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
          onClick: () => {
            EffectsUIApp.show({ inFocus: true, tab: "manager" });
          }
        };
        const database = {
          icon: "fas fa-database",
          name: "effectdatabase",
          title: "SEQUENCER.SidebarButtons.Database",
          button: true,
          visible: user_can_do("permissions-sidebar-tools"),
          onClick: () => {
            DatabaseViewerApp.show();
          }
        };
        controls.push({
          name: CONSTANTS.MODULE_NAME,
          title: "Sequencer Layer",
          icon: "fas fa-list-ol",
          layer: "sequencerInterfaceLayer",
          visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
          activeTool: "select-effect",
          tools: [selectTool, playTool, database, viewer]
        });
        if (!game.settings.get(CONSTANTS.MODULE_NAME, "showTokenSidebarTools"))
          return;
        const bar = controls.find((c) => c.name === "token");
        bar.tools.push(database);
        bar.tools.push(viewer);
      });
      debug("Sequencer | Registered settings");
    }
    async function migrateSettings() {
      const oldScreenspaceSetting = game.settings.storage.get("client").getItem("sequencer.disable-above-ui-screenspace");
      if (oldScreenspaceSetting) {
        const value = oldScreenspaceSetting === "true";
        game.settings.storage.get("client").removeItem("sequencer.disable-above-ui-screenspace");
        await game.settings.set(
          CONSTANTS.MODULE_NAME,
          "enable-above-ui-screenspace",
          !value
        );
      }
    }
    function registerLayers() {
      CONFIG.Canvas.layers = foundry.utils.mergeObject(Canvas.layers, {
        sequencerEffects: {
          layerClass: BaseEffectsLayer,
          group: "primary"
        },
        sequencerInterfaceLayer: {
          layerClass: SequencerInterfaceLayer,
          group: "interface"
        },
        sequencerEffectsUILayer: {
          layerClass: UIEffectsLayer,
          group: "interface"
        }
      });
      if (!Object.is(Canvas.layers, CONFIG.Canvas.layers)) {
        const layers = Canvas.layers;
        Object.defineProperty(Canvas, "layers", {
          get: function() {
            return foundry.utils.mergeObject(layers, CONFIG.Canvas.layers);
          }
        });
      }
      debug("Registered Layers");
    }
    function registerBatchShader() {
      VisionSamplerShader.registerPlugin();
      debug("Registered VisionBatchShader");
    }
    const hotkeys = {
      get _ready() {
        return canvas.ready && canvas.sequencerInterfaceLayer.active;
      },
      playTool: {
        playManySequencedDown: () => {
          if (!hotkeys._ready) return;
          EffectPlayer.playManySequenced = true;
        },
        playManySequencedUp: () => {
          if (!hotkeys._ready) return;
          EffectPlayer.playManySequenced = false;
          if (!EffectPlayer.isActive) return;
          EffectPlayer.playManyUp();
        },
        playManyDown: () => {
          if (!hotkeys._ready) return;
          EffectPlayer.playMany = true;
        },
        playManyUp: () => {
          if (!hotkeys._ready) return;
          EffectPlayer.playMany = false;
          if (!EffectPlayer.isActive) return;
          EffectPlayer.playManyUp();
        },
        attachToDown: () => {
          if (!hotkeys._ready) return;
          PlayerSettings.attachTo.store.set(true);
          PlayerSettings.stretchToAttach.store.set(true);
        }
      },
      selectTool: {
        snapToGridDown: () => {
          if (!hotkeys._ready) return;
          SelectionManager.snapToGrid = true;
        },
        snapToGridUp: () => {
          if (!hotkeys._ready) return;
          SelectionManager.snapToGrid = false;
        },
        attachToTargetDown: () => {
          if (!hotkeys._ready) return;
          if (!SelectionManager.isActive) return;
          PlayerSettings.attachTo.store.set(true);
          PlayerSettings.stretchToAttach.store.set(true);
        },
        attachToTargetUp: () => {
          if (!hotkeys._ready) return;
          PlayerSettings.attachTo.store.set(false);
          PlayerSettings.stretchToAttach.store.set(false);
        },
        deleteDown: () => {
          if (!hotkeys._ready) return;
          SelectionManager.delete();
        }
      }
    };
    function registerHotkeys() {
      game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-control", {
        name: "SEQUENCER.Hotkeys.PlayTool.Control",
        editable: [{ key: "ControlLeft" }],
        onDown: hotkeys.playTool.playManySequencedDown,
        onUp: hotkeys.playTool.playManySequencedUp
      });
      game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-shift", {
        name: "SEQUENCER.Hotkeys.PlayTool.Shift",
        editable: [{ key: "ShiftLeft" }],
        onDown: hotkeys.playTool.playManyDown,
        onUp: hotkeys.playTool.playManyUp
      });
      game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-alt", {
        name: "SEQUENCER.Hotkeys.PlayTool.Alt",
        editable: [{ key: "AltLeft" }],
        onDown: hotkeys.playTool.attachToDown,
        onUp: hotkeys.playTool.attachToDown
      });
      game.keybindings.register(
        CONSTANTS.MODULE_NAME,
        "select-tool-hotkey-control",
        {
          name: "SEQUENCER.Hotkeys.SelectTool.Control",
          editable: [{ key: "ControlLeft" }],
          onDown: hotkeys.selectTool.snapToGridDown,
          onUp: hotkeys.selectTool.snapToGridUp
        }
      );
      game.keybindings.register(CONSTANTS.MODULE_NAME, "select-tool-hotkey-alt", {
        name: "SEQUENCER.Hotkeys.SelectTool.Alt",
        editable: [{ key: "AltLeft" }],
        onDown: hotkeys.selectTool.attachToTargetDown,
        onUp: hotkeys.selectTool.attachToTargetUp
      });
      game.keybindings.register(
        CONSTANTS.MODULE_NAME,
        "select-tool-hotkey-delete",
        {
          name: "SEQUENCER.Hotkeys.SelectTool.Delete",
          editable: [{ key: "Delete" }],
          onDown: hotkeys.selectTool.deleteDown
        }
      );
    }
    async function registerTypes(register) {
      fetch("modules/sequencer/typings/types.d.ts").then((response) => response.text()).then((content) => register("sequencer/types.d.ts", content));
    }
    const presetMap = /* @__PURE__ */ new Map();
    class SequencerPresets {
      /**
       * Adds a preset that can then be used in sequences
       *
       * @param {string} inName
       * @param {Function} inFunction
       * @param {boolean} [overwrite=false] overwrite
       * @returns {Map<string, Function>}
       */
      static add(inName, inFunction, overwrite = false) {
        if (typeof inName !== "string") {
          throw custom_error(
            "Sequencer",
            `SequencerPresets | inName must be of type string`
          );
        }
        if (!is_function$1(inFunction)) {
          throw custom_error(
            "Sequencer",
            `SequencerPresets | inFunction must be of type function`
          );
        }
        if (presetMap.get(inName) && !overwrite) {
          throw custom_error(
            "Sequencer",
            `SequencerPresets | Preset "${inName}" already exists`
          );
        }
        presetMap.set(inName, inFunction);
        debug(`Sequencer | Presets | Added "${inName}" preset`);
        return presetMap;
      }
      /**
       * Retrieves all presets
       *
       * @returns {Map<string, Function>}
       */
      static getAll() {
        return presetMap;
      }
      /**
       * Retrieves preset based on its name
       *
       * @param {string} name
       * @param {boolean} exact
       * @returns {Function}
       */
      static get(name2, exact = false) {
        if (presetMap.has(name2) || exact) {
          return presetMap.get(name2);
        }
        const parts = name2.split(".");
        parts.pop();
        while (parts.length) {
          const newName = parts.join(".");
          if (presetMap.has(newName)) {
            return presetMap.get(newName);
          }
        }
        return false;
      }
    }
    class Section {
      constructor(inSequence) {
        this.sequence = inSequence;
        this._applyTraits();
        this._sectionStatus = writable(CONSTANTS.STATUS.READY);
        this._playIf = true;
        this._waitUntilFinished = false;
        this._async = false;
        this._waitUntilFinishedDelay = [0, 0];
        this._repetitions = 1;
        this._currentRepetition = 0;
        this._repeatDelayMin = 0;
        this._repeatDelayMax = 0;
        this._repeatDelay = 0;
        this._delayMin = 0;
        this._delayMax = 0;
        this._basicDelay = 0;
        this._duration = false;
      }
      static niceName = "Section";
      /**
       * @protected
       */
      get _shouldAsync() {
        return this._async || this._waitAnyway;
      }
      /**
       * @protected
       */
      get shouldWaitUntilFinished() {
        return this._waitUntilFinished || this._waitAnyway;
      }
      /**
       * @protected
       */
      get _waitAnyway() {
        return (this._async || this._waitUntilFinished) && this._isLastRepetition || this._isLastRepetition && this._isLastSection;
      }
      /**
       * @protected
       */
      get _isLastSection() {
        return this.sequence.sections.length - 1 === this.sequence.sections.indexOf(this);
      }
      /** ------------------------------------------------------------------------------------------------------------------------------ *
       * Methods below this point should NOT be overridden by child instances of the class, they are integral to the sequence functioning
       * ------------------------------------------------------------------------------------------------------------------------------- */
      /**
       * @protected
       */
      get _isLastRepetition() {
        return this._repetitions === 1 || this._repetitions === this._currentRepetition + 1;
      }
      /**
       * @protected
       */
      get _currentWaitTime() {
        let waitUntilFinishedDelay = this._waitAnyway ? random_int_between(...this._waitUntilFinishedDelay) : 0;
        return waitUntilFinishedDelay + this._repeatDelay;
      }
      /**
       * Method overwritten by inheriting classes, which is called just before the "run" method is called (see below)
       *
       * @returns {Promise<void>}
       * @protected
       */
      async preRun() {
      }
      /**
       * Method overwritten by inheriting classes, which is called when this section is executed by the Sequence
       *
       * @returns {Promise<void>}
       * @protected
       */
      async run() {
      }
      /**
       * Method overwritten by inheriting classes, which is called when this section is serialized by the Sequence
       *
       * @returns {object}
       * @private
       */
      async _serialize() {
        return {
          async: this._async,
          delay: [this._delayMin, this._delayMax],
          waitUntilFinished: this._waitUntilFinished,
          waitUntilFinishedDelay: this._waitUntilFinishedDelay,
          repetitions: this._repetitions,
          repetitionsDelay: [this._repeatDelayMin, this._repeatDelayMax]
        };
      }
      _deserialize(data) {
        this._async = data.async;
        this._waitUntilFinished = data.waitUntilFinished;
        this._waitUntilFinishedDelay = data.waitUntilFinishedDelay;
        this._repetitions = data.repetitions;
        this._repeatDelayMin = data.repetitionsDelay[0];
        this._repeatDelayMax = data.repetitionsDelay[1];
        return this;
      }
      /**
       * Method overwritten by inheriting classes, which stores data or prepares data before the Sequence executes it (see EffectsSection)
       *
       * @protected
       */
      async _initialize() {
      }
      /**
       * Method overwritten by inheriting classes. Inheriting classes uses the following to apply traits to themselves:
       * - Object.assign(this.constructor.prototype, trait)
       *
       * @protected
       */
      _applyTraits() {
      }
      /**
       * Causes the section to be repeated n amount of times, with an optional delay. If given inRepeatDelayMin
       * and inRepeatDelayMax, a random repetition delay will be picked for every repetition
       *
       * @param {number} inRepetitions
       * @param {number} inRepeatDelayMin
       * @param {number} inRepeatDelayMax
       * @returns {Section} this
       */
      repeats(inRepetitions, inRepeatDelayMin = 0, inRepeatDelayMax) {
        if (!is_real_number(inRepetitions))
          throw this.sequence._customError(
            this,
            "repeats",
            "inRepetitions must be of type number"
          );
        if (!is_real_number(inRepeatDelayMin))
          throw this.sequence._customError(
            this,
            "repeats",
            "repeatDelayMin must be of type number"
          );
        if (inRepeatDelayMax && !is_real_number(inRepeatDelayMax)) {
          throw this.sequence._customError(
            this,
            "repeats",
            "repeatDelayMax must be of type number"
          );
        }
        this._repetitions = inRepetitions;
        this._repeatDelayMin = Math.min(
          inRepeatDelayMin,
          inRepeatDelayMax ?? inRepeatDelayMin
        );
        this._repeatDelayMax = Math.max(
          inRepeatDelayMin,
          inRepeatDelayMax ?? inRepeatDelayMin
        );
        return this;
      }
      /**
       * Causes the effect or sound to not play, and skip all delays, repetitions, waits, etc. If you pass a function,
       * the function should return something false-y if you do not want the effect or sound to play.
       *
       * @param {boolean|function} inCondition
       * @returns {Section} this
       */
      playIf(inCondition) {
        this._playIf = inCondition;
        return this;
      }
      /**
       * Causes the section to finish running before starting the next section.
       *
       * @param {number|boolean} [minDelay=0] minDelay
       * @param {number/null} [maxDelay=null] maxDelay
       * @returns {Section} this
       */
      waitUntilFinished(minDelay = 0, maxDelay = null) {
        if (minDelay === false) return this;
        if (!is_real_number(minDelay))
          throw this.sequence._customError(
            this,
            "waitUntilFinished",
            "minDelay must be of type number"
          );
        if (maxDelay !== null && !is_real_number(maxDelay))
          throw this.sequence._customError(
            this,
            "waitUntilFinished",
            "maxDelay must be of type number"
          );
        this._waitUntilFinished = true;
        this._waitUntilFinishedDelay = [
          Math.min(minDelay, maxDelay ?? minDelay),
          Math.max(minDelay, maxDelay ?? minDelay)
        ];
        return this;
      }
      /**
       * Causes each effect or sound to finish playing before the next one starts playing. This differs from
       * .waitUntilFinished() in the sense that this is for each repetition, whilst .waitUntilFinished() is
       * for the entire section.
       *
       * @returns {Section} this
       */
      async() {
        this._async = true;
        return this;
      }
      /**
       * Delays the effect or sound from being played for a set amount of milliseconds. If given a second number, a
       * random delay between the two numbers will be generated.
       *
       * @param {number} [msMin=1] minMs
       * @param {number} [msMax=1] maxMs
       * @returns {Section} this
       */
      delay(msMin, msMax) {
        if (!is_real_number(msMin))
          throw this.sequence._customError(
            this,
            "delay",
            "msMin must be of type number"
          );
        if (msMax && !is_real_number(msMax))
          throw this.sequence._customError(
            this,
            "delay",
            "msMax must be of type number"
          );
        this._delayMin = Math.min(msMin, msMax ?? msMin);
        this._delayMax = Math.max(msMin, msMax ?? msMin);
        return this;
      }
      /**
       * Overrides the duration of this section
       *
       * @param {number} inDuration
       * @returns {Section} this
       */
      duration(inDuration) {
        if (!is_real_number(inDuration))
          throw this.sequence._customError(
            this,
            "duration",
            "inDuration must be of type number"
          );
        this._duration = inDuration;
        return this;
      }
      /**
       * Applies a preset to the current section
       *
       * @param {string} presetName
       * @param {*} args
       * @returns {Section|FunctionSection|EffectSection|AnimationSection|SoundSection}
       */
      preset(presetName, ...args) {
        if (typeof presetName !== "string") {
          throw this.sequence._customError(
            this,
            "name",
            `inName must be of type string`
          );
        }
        const preset = SequencerPresets.get(presetName);
        if (!preset) {
          custom_warning(
            "Sequencer",
            `preset | Could not find preset with name "${presetName}"`
          );
          return this;
        }
        return preset(this, ...args);
      }
      /**
       * @protected
       */
      async _shouldPlay() {
        return is_function$1(this._playIf) ? await this._playIf() : this._playIf;
      }
      /**
       * @protected
       */
      _validateLocation(inLocation) {
        inLocation = validate_document(inLocation);
        if (typeof inLocation === "string") {
          inLocation = get_object_from_scene(inLocation) ?? inLocation;
        }
        if (typeof inLocation === "string") {
          inLocation = safe_str(inLocation);
        }
        return inLocation;
      }
      /**
       * @protected
       */
      async _execute() {
        if (!await this._shouldPlay()) {
          this.sectionStatus = CONSTANTS.STATUS.SKIPPED;
          return;
        }
        this._basicDelay = random_float_between(this._delayMin, this._delayMax);
        return new Promise(async (resolve) => {
          setTimeout(async () => {
            this.sectionStatus = CONSTANTS.STATUS.RUNNING;
            for (let i = 0; i < this._repetitions; i++) {
              if (get_store_value(this.sectionStatus) === CONSTANTS.STATUS.ABORTED) {
                resolve();
                return;
              }
              this._currentRepetition = i;
              this._repeatDelay = i !== this._repetitions - 1 ? random_float_between(
                this._repeatDelayMin,
                this._repeatDelayMax
              ) : 0;
              await this.preRun();
              if (this._shouldAsync) {
                await this.run();
              } else {
                this.run();
              }
              if (this._repetitions > 1 && i !== this._repetitions - 1) {
                await this._delayBetweenRepetitions();
              }
            }
            resolve();
          }, this._basicDelay);
        }).then(() => {
          this.sectionStatus = CONSTANTS.STATUS.COMPLETE;
        });
      }
      set sectionStatus(inStatus) {
        this._sectionStatus.update((currentStatus) => {
          if (currentStatus === CONSTANTS.STATUS.READY || currentStatus === CONSTANTS.STATUS.RUNNING && inStatus !== CONSTANTS.STATUS.ABORTED) {
            return inStatus;
          }
          return currentStatus;
        });
      }
      get sectionStatus() {
        return this._sectionStatus;
      }
      _abortSection() {
        this.sectionStatus = CONSTANTS.STATUS.ABORTED;
      }
      /**
       * @protected
       */
      async _delayBetweenRepetitions() {
        let self = this;
        return new Promise((resolve) => {
          setTimeout(resolve, self._repeatDelay);
        });
      }
    }
    class FunctionSection extends Section {
      constructor(inSequence, inFunc) {
        super(inSequence);
        if (!is_function$1(inFunc))
          this._customError(
            "create",
            "The given function needs to be an actual function"
          );
        this._func = inFunc;
        this._waitUntilFinished = inFunc.constructor.name === "AsyncFunction";
      }
      static niceName = "Function";
      /**
       * @returns {Promise<void>}
       */
      async run() {
        debug("Running function");
        await this._func();
      }
      /**
       * @returns {Promise}
       * @private
       */
      async _execute() {
        await this.run();
      }
    }
    const animation = {
      /**
       * Base properties
       */
      _animations: null,
      /**
       * Animates a property on the target of the animation.
       *
       * @param {string} inTarget
       * @param {string} inPropertyName
       * @param {object} inOptions
       *      @param {Number} inOptions.from       - a single number from which to animate
       *      @param {Number} inOptions.to         - a single number to which to animate
       *      @param {Number} inOptions.duration   - how long in ms the animation should take
       *      @param {Number} inOptions.delay      - inserts a delay in ms before the animation starts
       *      @param {String} inOptions.ease       - what type of easing the animation should use
       *      @param {Boolean} inOptions.gridUnits - if animating width or height, this will set it to work in the scene's grid units
       *      @param {Boolean} inOptions.screenSpace - if animating position, width, or height, this will set it to work in the screen's dimensions
       *      @param {Boolean} inOptions.fromEnd   - makes this animation play from the end, like fadeOut, scaleOut, etc
       *      @param {Boolean} inOptions.absolute  - makes this animation's values override the current value
       *
       * @returns this
       */
      animateProperty(inTarget, inPropertyName, inOptions = {}) {
        if (!this._animations) this._animations = [];
        const result = validateAnimation(
          inTarget,
          inPropertyName,
          inOptions
        );
        if (typeof result === "string") {
          throw this.sequence._customError(this, "animateProperty", result);
        }
        this._animations.push(result);
        return this;
      },
      /**
       * Loops a property between a set of values on the target
       *
       * @param {string} inTarget
       * @param {string} inPropertyName
       * @param {object} inOptions
       *      @param {Number} inOptions.from         - a single number from which to loop
       *      @param {Number} inOptions.to           - a single number to which to loop
       *      @param {Number} inOptions.values       - an array of values to loop between
       *      @param {Number} inOptions.duration     - how long in ms the loop should take
       *      @param {Number} inOptions.loops        - how many loops in total this animation should go through - if none are specified, the loop is indefinite
       *      @param {Number} inOptions.delay        - inserts a delay in ms before the animation starts
       *      @param {String} inOptions.ease         - what type of easing the animation should use
       *      @param {Boolean} inOptions.pingPong    - sets whether loop should interpolate to the first value after it reaches the first value, or simply set it to the first value
       *      @param {Boolean} inOptions.gridUnits   - if animating width or height, this will set it to work in the scene's grid units
       *      @param {Boolean} inOptions.screenSpace - if animating position, width, or height, this will set it to work in the screen's dimensions
       *      @param {Boolean} inOptions.absolute    - makes this animation's values override the current value
       *
       * @returns this
       */
      loopProperty(inTarget, inPropertyName, inOptions = {}) {
        if (!this._animations) this._animations = [];
        const result = validateLoopingAnimation(
          inTarget,
          inPropertyName,
          inOptions
        );
        if (typeof result === "string") {
          throw this.sequence._customError(this, "loopProperty", result);
        }
        this._animations.push(result);
        return this;
      }
    };
    const audio = {
      /**
       * Base properties
       */
      _volume: null,
      _channel: "interface",
      _fadeInAudio: null,
      _fadeOutAudio: null,
      /**
       * Sets the volume of the sound.
       *
       * @param {number} inVolume
       * @returns this
       */
      volume(inVolume) {
        if (!is_real_number(inVolume))
          throw this.sequence._customError(
            this,
            "volume",
            "inVolume must be of type number"
          );
        this._volume = Math.max(0, Math.min(1, inVolume));
        return this;
      },
      /**
       * Causes the animated section to fade in its audio (if any) when played
       *
       * @param {number} duration     How long the fade should be
       * @param {object} [options]    Additional options, such as easing and delay
       * @returns this
       */
      fadeInAudio(duration, options = {}) {
        if (typeof options !== "object")
          throw this.sequence._customError(
            this,
            "fadeInAudio",
            "options must be of type object"
          );
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0
          },
          options
        );
        if (!is_real_number(duration))
          throw this.sequence._customError(
            this,
            "fadeInAudio",
            "duration must be of type number"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "fadeInAudio",
            "options.ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "fadeInAudio",
            "options.delay must be of type number"
          );
        this._fadeInAudio = {
          duration,
          ease: options.ease,
          delay: options.delay
        };
        return this;
      },
      /**
       * Causes the audio to fade out at the end of the animated section's duration
       *
       * @param {number} duration     How long the fade should be
       * @param {object} [options]    Additional options, such as easing and delay
       * @returns this
       */
      fadeOutAudio(duration, options = {}) {
        if (typeof options !== "object")
          throw this.sequence._customError(
            this,
            "fadeOutAudio",
            "options must be of type object"
          );
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0
          },
          options
        );
        if (!is_real_number(duration))
          throw this.sequence._customError(
            this,
            "fadeOutAudio",
            "duration must be of type number"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "fadeOutAudio",
            "ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "fadeOutAudio",
            "delay must be of type number"
          );
        this._fadeOutAudio = {
          duration,
          ease: options.ease,
          delay: options.delay
        };
        return this;
      }
    };
    const files = {
      /**
       * Base properties
       */
      _file: "",
      _fileOptions: false,
      _baseFolder: "",
      _mustache: null,
      /**
       * Defines the base folder that will prepend to the file path. This is mainly just useful to make the file
       * path easier to manage.
       *
       * @param {string} inBaseFolder
       * @returns this
       */
      baseFolder(inBaseFolder) {
        if (typeof inBaseFolder !== "string")
          throw this.sequence._customError(
            this,
            "baseFolder",
            "inBaseFolder must be of type string"
          );
        this._baseFolder = inBaseFolder + (inBaseFolder.endsWith("/") ? "" : "/");
        return this;
      },
      /**
       * Sets the Mustache of the filepath. This is applied after the randomization of the filepath, if available.
       *
       * @param {object} inMustache
       * @returns this
       */
      setMustache(inMustache) {
        if (typeof inMustache !== "object")
          throw this.sequence._customError(
            this,
            "setMustache",
            "inMustache must be of type object"
          );
        this._mustache = inMustache;
        return this;
      },
      async _determineFile(inFile) {
        if (!Array.isArray(inFile) && typeof inFile === "object") {
          return this._validateCustomRange(inFile);
        }
        if (Array.isArray(inFile))
          inFile = random_array_element(inFile, { recurse: true });
        inFile = this._applyMustache(inFile);
        if (Sequencer.Database.entryExists(inFile)) {
          return this._determineDatabaseFile(inFile);
        }
        const determinedFile = await this._processFile(inFile);
        return { file: determinedFile, forcedIndex: false, customRange: false };
      },
      async _processFile(inFile) {
        inFile = this._applyMustache(inFile);
        inFile = this._applyBaseFolder(inFile);
        inFile = await this._applyWildcard(inFile);
        if (Array.isArray(inFile))
          inFile = random_array_element(inFile, { recurse: true });
        return inFile;
      },
      async _validateCustomRange(inFile) {
        const finalFiles = {};
        const validRanges = Object.keys(SequencerFileRangeFind.ftToDistanceMap);
        for (const [range, rangeFile] of Object.entries(inFile)) {
          if (!validRanges.includes(range)) {
            throw this.sequence._customError(
              this,
              "file",
              `a file-distance key map must only contain the following keys: ${validRanges.join(
                ", "
              )}`
            );
          }
          finalFiles[range] = await this._processFile(rangeFile);
        }
        return { file: finalFiles, forcedIndex: false, customRange: true };
      },
      _determineDatabaseFile(inFile) {
        const entries = Sequencer.Database.getEntry(inFile);
        const entry = Array.isArray(entries) ? random_array_element(entries) : entries;
        const match = inFile.match(/(\d)+$/);
        return {
          file: entry,
          forcedIndex: match ? Number(match[1]) : false,
          customRange: false
        };
      },
      _applyBaseFolder(inFile) {
        if (Array.isArray(inFile))
          return inFile.map((file) => this._applyBaseFolder(file));
        return inFile.startsWith(this._baseFolder) ? inFile : this._baseFolder + inFile;
      },
      _applyMustache(inFile) {
        if (!this._mustache) return inFile;
        let template = Handlebars.compile(inFile);
        return template(this._mustache);
      },
      async _applyWildcard(inFile) {
        if (!inFile.includes("*")) return inFile;
        if (Array.isArray(inFile))
          return inFile.map(async (file) => await this._applyWildcard(file));
        inFile = this._applyBaseFolder(inFile);
        return getFiles(inFile, {
          applyWildCard: true,
          softFail: this.sequence.softFail
        });
      }
    };
    const moves = {
      /**
       * Base properties
       */
      _moveTowards: null,
      _moveSpeed: null,
      /**
       * Sets the location to move the target object to
       *
       * @param {object|string} inTarget
       * @param {object} options
       * @returns this
       */
      moveTowards(inTarget, options = {}) {
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0,
            rotate: true,
            cacheLocation: false
          },
          options
        );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "moveTowards",
            "options.ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "moveTowards",
            "options.delay must be of type number"
          );
        if (typeof options.rotate !== "boolean")
          throw this.sequence._customError(
            this,
            "moveTowards",
            "options.rotate must be of type boolean"
          );
        if (typeof options.cacheLocation !== "boolean")
          throw this.sequence._customError(
            this,
            "moveTowards",
            "options.cacheLocation must be of type boolean"
          );
        options.target = this._validateLocation(inTarget);
        if (!options.target)
          throw this.sequence._customError(
            this,
            "moveTowards",
            "could not find position of given object"
          );
        options.target = options.cacheLocation ? get_object_position(options.cacheLocation, { measure: true }) : options.target;
        this._moveTowards = options;
        return this;
      },
      /**
       * Sets the speed (pixels per frame) to move the target object
       *
       * @param {number} inSpeed
       * @returns this
       */
      moveSpeed(inSpeed) {
        if (!is_real_number(inSpeed))
          throw this.sequence._customError(
            this,
            "moveSpeed",
            "inSpeed must be of type number"
          );
        this._moveSpeed = inSpeed;
        return this;
      }
    };
    const opacity = {
      /**
       * Base properties
       */
      _opacity: null,
      _fadeIn: null,
      _fadeOut: null,
      /**
       * Sets the opacity of the effect. If used with ._fadeIn() and/or ._fadeOut(), this defines what the effect will fade to/from
       *
       * @param {number} inOpacity
       * @returns this
       */
      opacity(inOpacity) {
        if (!is_real_number(inOpacity))
          throw this.sequence._customError(
            this,
            "opacity",
            "inOpacity must be of type number"
          );
        this._opacity = inOpacity;
        return this;
      },
      /**
       * Causes the effect to fade in when played
       *
       * @param {number} duration     How long the fade should be
       * @param {object} [options]    Additional options, such as easing and delay
       * @returns this
       */
      fadeIn(duration, options = {}) {
        if (typeof options !== "object")
          throw this.sequence._customError(
            this,
            "fadeIn",
            "options must be of type object"
          );
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0
          },
          options
        );
        if (!is_real_number(duration))
          throw this.sequence._customError(
            this,
            "fadeIn",
            "duration must be of type number"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "fadeIn",
            "options.ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "fadeIn",
            "options.delay must be of type number"
          );
        this._fadeIn = {
          duration,
          ease: options.ease,
          delay: options.delay
        };
        return this;
      },
      /**
       * Causes the effect to fade out at the end of the effect's duration
       *
       * @param {number} duration     How long the fade should be
       * @param {object} [options]    Additional options, such as easing and delay
       * @returns this
       */
      fadeOut(duration, options = {}) {
        if (typeof options !== "object")
          throw this.sequence._customError(
            this,
            "fadeOut",
            "options must be of type object"
          );
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0
          },
          options
        );
        if (!is_real_number(duration))
          throw this.sequence._customError(
            this,
            "fadeOut",
            "duration must be of type number"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "fadeOut",
            "ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "fadeOut",
            "delay must be of type number"
          );
        this._fadeOut = {
          duration,
          ease: options.ease,
          delay: options.delay
        };
        return this;
      }
    };
    const rotation = {
      /**
       * Base properties
       */
      _angle: null,
      _rotateIn: null,
      _rotateOut: null,
      _randomRotation: null,
      _rotateTowards: null,
      /**
       * The object gets a random rotation, which means it should not be used with .stretchTo()
       *
       * @param {boolean} [inBool=true] inBool
       * @returns this
       */
      randomRotation(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "randomRotation",
            "inBool must be of type boolean"
          );
        this._randomRotation = inBool;
        return this;
      },
      /**
       * Sets the rotation of the object, which is added on top of the calculated rotation after .rotateTowards() or .randomRotation()
       *
       * @param {number} inRotation
       * @returns this
       */
      rotate(inRotation) {
        if (!is_real_number(inRotation))
          throw this.sequence._customError(
            this,
            "opacity",
            "inRotation must be of type number"
          );
        this._angle = inRotation;
        return this;
      },
      /**
       *  Causes the object to rotate when it starts playing
       *
       * @param {number} degrees
       * @param {number} duration
       * @param {object} [options] options
       * @returns this
       */
      rotateIn(degrees, duration, options = {}) {
        if (typeof options !== "object")
          throw this.sequence._customError(
            this,
            "rotateIn",
            "options must be of type object"
          );
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0
          },
          options
        );
        if (!is_real_number(degrees))
          throw this.sequence._customError(
            this,
            "rotateOut",
            "degrees must be of type number"
          );
        if (!is_real_number(duration))
          throw this.sequence._customError(
            this,
            "rotateOut",
            "duration must be of type number"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "rotateIn",
            "options.ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "rotateIn",
            "options.delay must be of type number"
          );
        this._rotateIn = {
          value: degrees,
          duration,
          ease: options.ease,
          delay: options.delay
        };
        return this;
      },
      /**
       *  Causes the object to rotate at the end of the effect's duration
       *
       * @param {number} degrees
       * @param {number} duration
       * @param {object} [options] options
       * @returns this
       */
      rotateOut(degrees, duration, options = {}) {
        if (typeof options !== "object")
          throw this.sequence._customError(
            this,
            "rotateOut",
            "options must be of type object"
          );
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0
          },
          options
        );
        if (!is_real_number(degrees))
          throw this.sequence._customError(
            this,
            "rotateOut",
            "degrees must be of type number"
          );
        if (!is_real_number(duration))
          throw this.sequence._customError(
            this,
            "rotateOut",
            "duration must be of type number"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "rotateOut",
            "options.ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "rotateOut",
            "options.delay must be of type number"
          );
        this._rotateOut = {
          value: degrees,
          duration,
          ease: options.ease,
          delay: options.delay
        };
        return this;
      }
    };
    const scale = {
      _scaleMin: null,
      _scaleMax: null,
      _scaleIn: null,
      _scaleOut: null,
      /**
       *  A method that can take the following:
       *  - A number to set the scale uniformly
       *  - An object with x and y for non-uniform scaling
       *  - Two numbers which the Sequencer will randomly pick a uniform scale between
       *
       * @param {number|object} inScaleMin
       * @param {number} [inScaleMax] inScaleMax
       * @returns this
       */
      scale(inScaleMin, inScaleMax) {
        if (!is_real_number(inScaleMin) && typeof inScaleMin !== "object")
          throw this.sequence._customError(
            this,
            "scale",
            "inScale must be of type number or object"
          );
        if (is_real_number(inScaleMin)) {
          if (inScaleMax && !is_real_number(inScaleMax)) {
            throw this.sequence._customError(
              this,
              "scale",
              "if inScaleMin is a number, inScaleMax must also be of type number"
            );
          }
        }
        this._scaleMin = inScaleMin;
        this._scaleMax = inScaleMax ?? false;
        return this;
      },
      /**
       *  Causes the effect to scale when it starts playing
       *
       * @param {number|object} scale
       * @param {number} duration
       * @param {object} [options] options
       * @returns this
       */
      scaleIn(scale2, duration, options = {}) {
        if (typeof options !== "object")
          throw this.sequence._customError(
            this,
            "scaleIn",
            "options must be of type object"
          );
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0
          },
          options
        );
        if (!is_real_number(duration))
          throw this.sequence._customError(
            this,
            "scaleIn",
            "duration must be of type number"
          );
        if (!is_real_number(scale2) && typeof scale2 !== "object")
          throw this.sequence._customError(
            this,
            "scaleIn",
            "scale must be of type number or object"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "scaleIn",
            "options.ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "scaleIn",
            "options.delay must be of type number"
          );
        this._scaleIn = {
          value: scale2,
          duration,
          ease: options.ease,
          delay: options.delay
        };
        return this;
      },
      /**
       *  Causes the effect to scale at the end of the effect's duration
       *
       * @param {number|object} scale
       * @param {number} duration
       * @param {object} [options] options
       * @returns this
       */
      scaleOut(scale2, duration, options = {}) {
        if (typeof options !== "object")
          throw this.sequence._customError(
            this,
            "scaleOut",
            "options must be of type object"
          );
        options = foundry.utils.mergeObject(
          {
            ease: "linear",
            delay: 0
          },
          options
        );
        if (!is_real_number(duration))
          throw this.sequence._customError(
            this,
            "scaleOut",
            "duration must be of type number"
          );
        if (!is_real_number(scale2) && typeof scale2 !== "object")
          throw this.sequence._customError(
            this,
            "scaleOut",
            "scale must be of type number or object"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "scaleOut",
            "options.ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "scaleOut",
            "options.delay must be of type number"
          );
        this._scaleOut = {
          value: scale2,
          duration,
          ease: options.ease,
          delay: options.delay
        };
        return this;
      }
    };
    const time = {
      _hasTime: true,
      _isRange: false,
      _startTime: null,
      _startPerc: null,
      _endTime: null,
      _endPerc: null,
      /**
       * Sets the start and end time of the section, playing only that range
       *
       * @param {number} inMsStart
       * @param {number} inMsEnd
       * @returns this
       */
      timeRange(inMsStart, inMsEnd) {
        if (!is_real_number(inMsStart))
          throw this.sequence._customError(
            this,
            "timeRange",
            "inMsStart must be of type number"
          );
        if (!is_real_number(inMsEnd))
          throw this.sequence._customError(
            this,
            "timeRange",
            "inMsEnd must be of type number"
          );
        this._startTime = inMsStart;
        this._endTime = inMsEnd;
        this._isRange = true;
        return this;
      },
      /**
       * Sets the start time of the section.
       *
       * @param {number} inMs
       * @returns this
       */
      startTime(inMs) {
        if (!is_real_number(inMs))
          throw this.sequence._customError(
            this,
            "startTime",
            "inMs must be of type number"
          );
        this._startTime = inMs;
        this._startPerc = false;
        this._isRange = false;
        return this;
      },
      /**
       * Sets the start time of the section based on a percentage from its total duration.
       *
       * @param {number} inPercentage
       * @returns this
       */
      startTimePerc(inPercentage) {
        if (!is_real_number(inPercentage))
          throw this.sequence._customError(
            this,
            "startTimePerc",
            "inPercentage must be of type number"
          );
        this._startTime = inPercentage;
        this._startPerc = true;
        this._isRange = false;
        return this;
      },
      /**
       * Sets the ending time of the section (from the end).
       *
       * @param {number} inMs
       * @returns this
       */
      endTime(inMs) {
        if (!is_real_number(inMs))
          throw this.sequence._customError(
            this,
            "endTime",
            "inMs must be of type number"
          );
        this._endTime = inMs;
        this._endPerc = false;
        this._isRange = false;
        return this;
      },
      /**
       * Sets the ending time of the section based on a percentage from the total duration.
       *
       * @param {number} inPercentage
       * @returns this
       */
      endTimePerc(inPercentage) {
        if (!is_real_number(inPercentage))
          throw this.sequence._customError(
            this,
            "endTimePerc",
            "inPercentage must be of type number"
          );
        this._endTime = inPercentage;
        this._endPerc = true;
        this._isRange = false;
        return this;
      }
    };
    const users = {
      _users: null,
      _addUser(inUser) {
        if (!this._users) this._users = [];
        if (typeof inUser !== "string")
          throw this.sequence._customError(
            this,
            "_addUser",
            "inUser must be of type string"
          );
        if (!game.users.has(inUser)) {
          if (game.users.getName(inUser)) {
            inUser = game.users.getName(inUser).id;
          } else {
            throw this.sequence._customError(
              this,
              "_addUser",
              `user with id or name "${inUser}" does not exist!`
            );
          }
        }
        if (!this._users.includes(inUser)) this._users.push(inUser);
      },
      _deleteUser(inUser) {
        if (!this._users) this._users = [];
        if (this._users.includes(inUser)) {
          let index = this._users.indexOf(inUser);
          this._users.splice(index, 1);
        }
      },
      /**
       * Causes section to be executed only locally, and not push to other connected clients.
       *
       * @param {boolean} inLocally
       * @returns this
       */
      locally(inLocally = true) {
        if (inLocally) this._addUser(game.userId);
        else this._deleteUser(game.userId);
        return this;
      },
      /**
       * Causes the section to be executed for only a set of users.
       *
       * @param {string|User|array<string|User>} inUsers
       * @returns this
       */
      forUsers(inUsers) {
        if (!Array.isArray(inUsers)) {
          if (typeof inUsers !== "string")
            throw this.sequence._customError(
              this,
              "forUsers",
              "inUser must be of type string"
            );
          inUsers = [inUsers];
        }
        inUsers.forEach((u) => this._addUser(u));
        return this;
      }
    };
    const filter = {
      _filters: null,
      _addFilter(inFilterName, inData, inName = false) {
        if (!this._filters) this._filters = [];
        this._filters.push({
          className: inFilterName,
          name: inName,
          data: inData
        });
      },
      _testFilter(inFilterName, inData) {
        let filter2 = new filters[inFilterName](inData);
        if (!filter2.isValid)
          throw this.sequence._customError(
            this,
            "filter",
            `Could not create ${inFilterName} filter - data is malformed!`
          );
      },
      filter(inFilterName, inData = {}, inName = "") {
        if (typeof inFilterName !== "string")
          throw this.sequence._customError(
            this,
            "filter",
            `inFilterName must be of type string`
          );
        if (!Object.keys(filters).includes(inFilterName))
          throw this.sequence._customError(
            this,
            "filter",
            `"${inFilterName}" does not exist`
          );
        this._testFilter(inFilterName, inData);
        this._addFilter(inFilterName, inData, inName);
        return this;
      }
    };
    const tint = {
      _tint: null,
      /**
       * Tints the target of this section by the color given to the
       *
       * @param {number|string} inColor
       * @returns this
       */
      tint(inColor) {
        if (!is_real_number(inColor) && typeof inColor !== "string")
          throw this.sequence._customError(
            this,
            "tint",
            `inColor must be of type string (hexadecimal) or number (decimal)!`
          );
        this._tint = parseColor(inColor);
        return this;
      }
    };
    const location = {
      /**
       * Base properties
       */
      _source: null,
      _offset: null,
      _randomOffset: null,
      /**
       *  A smart method that can take a reference to an object, or a direct on the canvas to play the effect at,
       *  or a string reference (see .name())
       *
       * @param {Object|String} inLocation
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      atLocation(inLocation, inOptions = {}) {
        if (!inLocation || !(typeof inLocation === "object" || typeof inLocation === "string")) {
          throw this.sequence._customError(
            this,
            "atLocation",
            `inLocation is invalid, and must be of type of object, string, placeable object, or document`
          );
        }
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "atLocation",
            `inOptions must be of type object`
          );
        inOptions = foundry.utils.mergeObject(
          {
            cacheLocation: false,
            offset: false,
            randomOffset: false,
            gridUnits: false,
            local: false
          },
          inOptions
        );
        inLocation = this._validateLocation(inLocation);
        if (inLocation === void 0)
          throw this.sequence._customError(
            this,
            "atLocation",
            "could not find position of given object"
          );
        if (typeof inOptions.cacheLocation !== "boolean")
          throw this.sequence._customError(
            this,
            "atLocation",
            "inOptions.cacheLocation must be of type boolean"
          );
        if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
          throw this.sequence._customError(
            this,
            "atLocation",
            "inOptions.randomOffset must be of type boolean or number"
          );
        if (inOptions.offset) {
          const offsetData = this._validateOffset(
            "atLocation",
            inOptions.offset,
            inOptions
          );
          this._offset = {
            source: offsetData,
            target: this._offset?.target ?? false
          };
        }
        this._randomOffset = {
          source: inOptions.randomOffset,
          target: this._randomOffset?.target ?? false
        };
        this._source = inOptions.cacheLocation && typeof inLocation !== "string" ? get_object_canvas_data(inLocation, { uuid: false }) : inLocation;
        return this;
      }
    };
    const offset = {
      _offset: null,
      _randomOffset: null,
      _validateOffset(functionName, inOffset, inOptions = {}) {
        inOffset = foundry.utils.mergeObject(
          {
            x: 0,
            y: 0
          },
          inOffset
        );
        inOptions = foundry.utils.mergeObject(
          {
            gridUnits: false,
            local: false
          },
          inOptions
        );
        if (typeof inOptions.gridUnits !== "boolean")
          throw this.sequence._customError(
            this,
            functionName,
            "inOptions.gridUnits must be of type boolean"
          );
        if (typeof inOptions.local !== "boolean")
          throw this.sequence._customError(
            this,
            functionName,
            "inOptions.local must be of type boolean"
          );
        if (!is_real_number(inOffset.x))
          throw this.sequence._customError(
            this,
            functionName,
            `inOffset.x must be of type number!`
          );
        if (!is_real_number(inOffset.y))
          throw this.sequence._customError(
            this,
            functionName,
            `inOffset.y must be of type number!`
          );
        return {
          ...inOffset,
          ...inOptions
        };
      }
    };
    const text = {
      _text: null,
      /**
       *  Creates a text element, attached to the sprite. The options for the text are available here:
       *  https://pixijs.io/pixi-text-style/
       *
       * @param {String} inText
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      text(inText, inOptions = {}) {
        if (typeof inText !== "string")
          throw this.sequence._customError(
            this,
            "text",
            "inText must be of type string"
          );
        this._text = foundry.utils.mergeObject(
          {
            text: inText
          },
          inOptions
        );
        return this;
      }
    };
    const name = {
      _name: null,
      _origin: null,
      /**
       * Causes the effect or sound to be named, which affects them differently. In the case of effects, names can be used
       * in the place of 
       *
       * @param {String} inName
       * @returns {EffectSection}
       */
      name(inName) {
        if (typeof inName !== "string")
          throw this.sequence._customError(
            this,
            "name",
            "inName must be of type string"
          );
        this._name = safe_str(inName);
        return this;
      },
      /**
       *  This is for adding extra information to an sound, like the origin of the sound in the form of the item's uuid.
       *  The method accepts a string or a Document that has an UUID.
       *
       * @param {string|document} inOrigin
       * @returns {Section}
       */
      origin(inOrigin) {
        inOrigin = validate_document(inOrigin);
        if (inOrigin instanceof foundry.abstract.Document) {
          inOrigin = inOrigin?.uuid;
          if (!inOrigin)
            throw this.sequence._customError(
              this,
              "origin",
              "could not find the UUID for the given Document"
            );
        }
        if (typeof inOrigin !== "string")
          throw this.sequence._customError(
            this,
            "origin",
            "inOrigin must be of type string"
          );
        this._origin = inOrigin;
        return this;
      }
    };
    const traits = {
      animation,
      audio,
      files,
      moves,
      opacity,
      rotation,
      scale,
      time,
      users,
      filter,
      tint,
      location,
      offset,
      text,
      name
    };
    class CrosshairsDocument extends MeasuredTemplateDocument {
      constructor(config = {}, context = {}, crosshair = {}, callbacks = {}) {
        super(config, context);
        this.crosshair = foundry.utils.mergeObject(CrosshairsDocument.defaultConfig, crosshair);
        this.callbacks = callbacks;
        this.originalConfig = foundry.utils.deepClone(config);
      }
      crosshair = {};
      #layer = null;
      cachedPosition = null;
      originalConfig = {};
      static get placeableClass() {
        return CrosshairsPlaceable;
      }
      static get defaultConfig() {
        return {
          gridHighlight: true,
          icon: {
            texture: "",
            borderVisible: false
          },
          snap: {
            position: CONST.GRID_SNAPPING_MODES.CENTER,
            size: CONST.GRID_SNAPPING_MODES.CENTER,
            resolution: 1,
            direction: 0
          },
          lockDrag: true,
          distanceMin: null,
          distanceMax: null,
          label: {
            text: "",
            dx: 0,
            dy: 0
          },
          location: {
            obj: null,
            limitMinRange: null,
            limitMaxRange: null,
            showRange: false,
            displayRangePoly: false,
            rangePolyFillColor: null,
            rangePolyLineColor: null,
            rangePolyFillAlpha: null,
            rangePolyLineAlpha: null,
            lockToEdge: false,
            lockToEdgeDirection: false,
            offset: { x: 0, y: 0 },
            wallBehavior: CONSTANTS.PLACEMENT_RESTRICTIONS.ANYWHERE
          },
          lockManualRotation: false
        };
      }
      getOrientation() {
        this.cachedPosition = !this.object || this.object?.destroyed ? this.cachedPosition : {
          source: get_object_canvas_data(this.object, { uuid: false }),
          target: this.t === CONST.MEASURED_TEMPLATE_TYPES.CONE || this.t === CONST.MEASURED_TEMPLATE_TYPES.RAY ? get_object_canvas_data(this.object, { measure: true, uuid: false }) : null
        };
        return this.cachedPosition;
      }
      get documentName() {
        return "CrosshairsDocument";
      }
      get layer() {
        if (this.#layer) return this.#layer;
        const create = (doc) => new this.constructor.placeableClass(doc);
        const sink = {
          get(target, prop) {
            switch (prop) {
              case "createObject":
                return create;
              default:
                return target[prop];
            }
          }
        };
        this.#layer = new Proxy(canvas.activeLayer, sink);
        return this.#layer;
      }
      get isEmbedded() {
        return !!this.parent;
      }
      getCrosshairShape() {
        const { t, distance, direction, angle, width: width2 } = this;
        switch (t) {
          case "circle":
            return MeasuredTemplate.getCircleShape(distance);
          case "cone":
            return MeasuredTemplate.getConeShape(distance, direction, angle);
          case "rect":
            return MeasuredTemplate.getRectShape(distance, direction);
          case "ray":
            return MeasuredTemplate.getRayShape(distance, direction, width2);
        }
      }
      token = {};
      prepareDerivedData() {
        super.prepareDerivedData();
        const gridUnits = this.distance / this.parent.grid.distance;
        this.radius = gridUnits * this.parent.grid.size;
        this.token = {};
        this.token.width = gridUnits * 2;
        this.token.height = gridUnits * 2;
        switch (this.t) {
          case "rect":
            this.token.x = this.x;
            this.token.y = this.y;
            break;
          case "circle":
          default:
            this.token.x = this.x - this.radius;
            this.token.y = this.y - this.radius;
        }
      }
      show() {
        this._destroyed = false;
        this.#layer = null;
        return this.object.show();
      }
    }
    class EffectSection extends Section {
      constructor(inSequence, inFile = "") {
        super(inSequence);
        this._deserializedData = null;
        this._file = inFile;
        this._text = null;
        this._source = null;
        this._stretchTo = null;
        this._attachTo = null;
        this._copySprite = null;
        this._anchor = null;
        this._spriteAnchor = null;
        this._randomOffset = null;
        this._missed = null;
        this._private = null;
        this._randomMirrorX = null;
        this._randomMirrorY = null;
        this._mirrorX = null;
        this._mirrorY = null;
        this._playbackRate = null;
        this._template = null;
        this._overrides = [];
        this._zIndex = null;
        this._offset = null;
        this._spriteOffset = null;
        this._size = null;
        this._syncGroup = null;
        this._persist = null;
        this._persistOptions = null;
        this._zeroSpriteRotation = null;
        this._extraEndDuration = null;
        this._loopOptions = null;
        this._tilingTexture = null;
        this._snapToGrid = null;
        this._scaleToObject = null;
        this._screenSpace = null;
        this._screenSpaceAboveUI = null;
        this._screenSpaceAnchor = null;
        this._screenSpacePosition = null;
        this._screenSpaceScale = null;
        this._elevation = null;
        this._sortLayer = 800;
        this._masks = [];
        this._tiedDocuments = [];
        this._selfMask = false;
        this._temporaryEffect = false;
        this._spriteRotation = 0;
        this._randomSpriteRotation = false;
        this._isRangedEffect = null;
        this._offsetLegacy = null;
        this._randomOffsetLegacy = null;
        this._aboveLighting = null;
        this._aboveInterface = null;
        this._spriteScaleMin = 1;
        this._spriteScaleMax = null;
        this._isometric = null;
        this._shapes = [];
        this._xray = null;
        this._playEffect = true;
      }
      static niceName = "Effect";
      /**
       * @private
       */
      get _target() {
        return this._stretchTo || this._rotateTowards || this._moveTowards || false;
      }
      static debounceWarning() {
        custom_warning(
          "Sequencer",
          "Effect | This user does not have permissions to play effects. This can be configured in Sequencer's module settings."
        );
      }
      /**
       * Causes the effect to persist indefinitely on the canvas until _ended via SequencerEffectManager.endAllEffects() or
       * name the effect with .name() and then end it through SequencerEffectManager.endEffect()
       *
       * @param {Boolean} [inBool=true] inBool
       * @param {Object} [inOptions={}] inOptions
       * @returns {EffectSection}
       */
      persist(inBool = true, inOptions = {}) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "persist",
            "inBool must be of type boolean"
          );
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "persist",
            `inOptions must be of type object`
          );
        inOptions = foundry.utils.mergeObject(
          {
            id: foundry.utils.randomID(),
            persistTokenPrototype: false
          },
          inOptions
        );
        if (typeof inOptions.persistTokenPrototype !== "boolean")
          throw this.sequence._customError(
            this,
            "persist",
            "inOptions.persistTokenPrototype must be of type boolean"
          );
        this._persist = inBool;
        this._persistOptions = inOptions;
        return this;
      }
      syncGroup(inString) {
        if (typeof inString !== "string")
          throw this.sequence._customError(
            this,
            "syncGroup",
            "inString must be of type string"
          );
        this._syncGroup = inString;
        return this;
      }
      /**
       * Causes the effect to become temporary, which means it will not be stored in the flags of any object,
       * even if it .persist() is called
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      temporary(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "temporary",
            "inBool must be of type boolean"
          );
        this._temporaryEffect = inBool || this._temporaryEffect;
        return this;
      }
      /**
       * Sets the effect's playback rate. A playback rate of 2.0 would make it play 2x as fast, 0.5 would make
       * it play half as fast.
       *
       * @param {Number} inNumber
       * @returns {EffectSection}
       */
      playbackRate(inNumber = 1) {
        if (!is_real_number(inNumber))
          throw this.sequence._customError(
            this,
            "playbackRate",
            "inNumber must be of type number"
          );
        this._playbackRate = inNumber;
        return this;
      }
      /**
       * Causes the effect to target a location close to the .stretchTowards() location, but not on it.
       *
       * @param {Boolean} [inBool=true] inBool
       * @returns {EffectSection}
       */
      missed(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "missed",
            "inBool must be of type boolean"
          );
        this._missed = inBool;
        return this;
      }
      /**
       * Adds a function that will run at the end of the effect serialization step, but before it is played. Allows direct
       * modifications of effect's data. For example, it could be manipulated to change which file will be used based
       * on the distance to the target.
       *
       * @param {Function} inFunc
       * @returns {EffectSection}
       */
      addOverride(inFunc) {
        if (!is_function$1(inFunc))
          throw this.sequence._customError(
            this,
            "addOverride",
            "The given function needs to be an actual function."
          );
        this._overrides.push(inFunc);
        return this;
      }
      /**
       *  A smart method that can take a reference to an object, or a direct on the canvas to attach an effect to,
       *  or a string reference (see .name())
       *
       * @param {Object|String} inObject
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      attachTo(inObject, inOptions = {}) {
        if (!inObject || !(typeof inObject === "object" || typeof inObject === "string")) {
          throw this.sequence._customError(
            this,
            "attachTo",
            `inObject is invalid, and must be of type of object, string, placeable object, or document`
          );
        }
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "attachTo",
            `inOptions must be of type object`
          );
        inOptions = foundry.utils.mergeObject(
          {
            align: "center",
            edge: "on",
            bindVisibility: true,
            bindAlpha: true,
            bindElevation: true,
            bindScale: true,
            bindRotation: true,
            offset: false,
            randomOffset: false,
            gridUnits: false,
            local: false
          },
          inOptions
        );
        const validatedObject = this._validateLocation(inObject);
        if (validatedObject === void 0)
          throw this.sequence._customError(
            this,
            "attachTo",
            "could not find given object"
          );
        const aligns = Object.keys(alignments);
        if (typeof inOptions.align !== "string" || !aligns.includes(inOptions.align)) {
          throw this.sequence._customError(
            this,
            "attachTo",
            `inOptions.align must be of type string, one of: ${aligns.join(", ")}`
          );
        }
        if (typeof inOptions.edge !== "string" || !(inOptions.edge === "on" || inOptions.edge === "inner" || inOptions.edge === "outer")) {
          throw this.sequence._customError(
            this,
            "attachTo",
            `inOptions.edge must of type string with the value of either "on", "inner", or "outer"`
          );
        }
        if (typeof inOptions.bindVisibility !== "boolean")
          throw this.sequence._customError(
            this,
            "attachTo",
            `inOptions.bindVisibility must be of type boolean`
          );
        if (inOptions.followRotation !== void 0) {
          inOptions.bindRotation = inOptions.followRotation;
          this.sequence._showWarning(
            this,
            "attachTo",
            "inOptions.followRotation is deprecated, please use inOptions.bindRotation instead"
          );
        }
        if (typeof inOptions.bindRotation !== "boolean")
          throw this.sequence._customError(
            this,
            "attachTo",
            `inOptions.bindRotation must be of type boolean`
          );
        if (typeof inOptions.bindAlpha !== "boolean")
          throw this.sequence._customError(
            this,
            "attachTo",
            "inOptions.bindAlpha must be of type boolean"
          );
        if (typeof inOptions.bindElevation !== "boolean")
          throw this.sequence._customError(
            this,
            "attachTo",
            "inOptions.bindElevation must be of type boolean"
          );
        if (typeof inOptions.bindScale !== "boolean")
          throw this.sequence._customError(
            this,
            "attachTo",
            "inOptions.bindScale must be of type boolean"
          );
        if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
          throw this.sequence._customError(
            this,
            "attachTo",
            "inOptions.randomOffset must be of type boolean or number"
          );
        this._source = validatedObject;
        if (inOptions.offset) {
          const offsetData = this._validateOffset(
            "attachTo",
            inOptions.offset,
            inOptions
          );
          this._offset = {
            source: offsetData,
            target: this._offset?.target ?? false
          };
        }
        this._randomOffset = {
          source: inOptions.randomOffset,
          target: this._randomOffset?.target ?? false
        };
        this._attachTo = {
          active: !!validatedObject,
          align: inOptions.align,
          edge: inOptions.edge,
          bindVisibility: inOptions.bindVisibility,
          bindAlpha: inOptions.bindAlpha,
          bindScale: inOptions.bindScale,
          bindElevation: inOptions.bindElevation,
          bindRotation: inOptions.bindRotation
        };
        return this;
      }
      /**
       *  Causes the effect to be rotated and stretched towards an object, or a direct on the canvas to play the effect at, or a string reference (see .name())
       *  This effectively calculates the proper X scale for the effect to reach the target
       *
       * @param {Object|String} inLocation
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      stretchTo(inLocation, inOptions = {}) {
        if (!inLocation || !(typeof inLocation === "object" || typeof inLocation === "string")) {
          throw this.sequence._customError(
            this,
            "stretchTo",
            `inLocation is invalid, and must be of type of object, string, placeable object, or document`
          );
        }
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "stretchTo",
            `inOptions must be of type object`
          );
        inOptions = foundry.utils.mergeObject(
          {
            cacheLocation: false,
            attachTo: false,
            onlyX: false,
            tiling: false,
            offset: false,
            randomOffset: false,
            gridUnits: false,
            local: false,
            requiresLineOfSight: false,
            hideLineOfSight: false
          },
          inOptions
        );
        const validatedObject = this._validateLocation(inLocation);
        if (validatedObject === void 0)
          throw this.sequence._customError(
            this,
            "stretchTo",
            "could not find position of given object"
          );
        if (typeof inOptions.cacheLocation !== "boolean")
          throw this.sequence._customError(
            this,
            "stretchTo",
            "inOptions.cacheLocation must be of type boolean"
          );
        if (typeof inOptions.attachTo !== "boolean")
          throw this.sequence._customError(
            this,
            "stretchTo",
            "inOptions.attachTo must be of type boolean"
          );
        if (typeof inOptions.onlyX !== "boolean")
          throw this.sequence._customError(
            this,
            "stretchTo",
            "inOptions.onlyX must be of type boolean"
          );
        if (typeof inOptions.tiling !== "boolean")
          throw this.sequence._customError(
            this,
            "stretchTo",
            "inOptions.tiling must be of type boolean"
          );
        if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
          throw this.sequence._customError(
            this,
            "stretchTo",
            "inOptions.randomOffset must be of type boolean or number"
          );
        if (inOptions.cacheLocation && inOptions.attachTo) {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "cacheLocation and attachTo cannot both be true - pick one or the other"
          );
        }
        if (typeof inOptions.requiresLineOfSight !== "boolean") {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "requiresLineOfSight must be of type boolean"
          );
        }
        if (!inOptions.attachTo && inOptions.requiresLineOfSight) {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "requiresLineOfSight requires that attachTo is true"
          );
        }
        if (typeof inOptions.hideLineOfSight !== "boolean") {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "hideLineOfSight must be of type boolean"
          );
        }
        if (!inOptions.requiresLineOfSight && inOptions.hideLineOfSight) {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "hideLineOfSight requires that requiresLineOfSight is true"
          );
        }
        if (inOptions.tiling) this.tilingTexture();
        if (inOptions.offset) {
          const offsetData = this._validateOffset(
            "stretchTo",
            inOptions.offset,
            inOptions
          );
          this._offset = {
            source: this._offset?.source ?? false,
            target: offsetData
          };
        }
        this._randomOffset = {
          source: this._randomOffset?.source ?? false,
          target: inOptions.randomOffset
        };
        this._stretchTo = {
          target: inOptions.cacheLocation ? get_object_canvas_data(validatedObject, { measure: true, uuid: false }) : validatedObject,
          attachTo: inOptions.attachTo,
          onlyX: inOptions.onlyX,
          requiresLineOfSight: inOptions.requiresLineOfSight,
          hideLineOfSight: inOptions.hideLineOfSight
        };
        return this;
      }
      /**
       * Sets the location to rotate the object to
       *
       * @param {object|string} inLocation
       * @param {object} inOptions
       * @returns this
       */
      rotateTowards(inLocation, inOptions = {}) {
        if (!inLocation || !(typeof inLocation === "object" || typeof inLocation === "string")) {
          throw this.sequence._customError(
            this,
            "inLocation",
            `inLocation is invalid, and must be of type of object, string, placeable object, or document`
          );
        }
        inOptions = foundry.utils.mergeObject(
          {
            rotationOffset: 0,
            cacheLocation: false,
            attachTo: false,
            offset: false,
            randomOffset: false,
            local: false,
            gridUnits: false,
            template: false
          },
          inOptions
        );
        if (!is_real_number(inOptions.rotationOffset))
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "inOptions.rotationOffset must be of type number"
          );
        if (typeof inOptions.attachTo !== "boolean")
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "inOptions.attachTo must be of type boolean"
          );
        if (typeof inOptions.cacheLocation !== "boolean")
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "inOptions.cacheLocation must be of type boolean"
          );
        const validatedObject = this._validateLocation(inLocation);
        if (!validatedObject)
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "could not find position of given object"
          );
        if (inOptions.offset) {
          const offsetData = this._validateOffset(
            "attachTo",
            inOptions.offset,
            inOptions
          );
          this._offset = {
            target: offsetData ?? this._offset?.target ?? false
          };
        }
        if (typeof inOptions.template !== "boolean")
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "inOptions.template must be of type boolean"
          );
        this._randomOffset = {
          target: inOptions.randomOffset ?? this._randomOffset?.target ?? false
        };
        this._rotateTowards = {
          target: inOptions.cacheLocation ? get_object_canvas_data(validatedObject, { measure: true, uuid: false }) : validatedObject,
          rotationOffset: inOptions.rotationOffset,
          cacheLocation: inOptions.cacheLocation,
          attachTo: inOptions.attachTo,
          template: inOptions.template
        };
        return this;
      }
      /**
       *  Create an effect based on the given object, effectively copying the object as an effect. Useful when you want to do some effect magic on tokens or tiles.
       *  @deprecated
       */
      from(...args) {
        this.sequence._showWarning(
          this,
          "from",
          ".from() is deprecated, please use .copySprite() instead"
        );
        return this.copySprite(...args);
      }
      /**
       *  Create an effect based on the given object, effectively copying the object as an effect. Useful when you want to do some effect magic on tokens or tiles.
       *
       * @param {Object} inObject
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      copySprite(inObject, inOptions = {}) {
        if (!(inObject instanceof Token || inObject instanceof Tile || inObject instanceof TokenDocument || inObject instanceof TileDocument)) {
          throw this.sequence._customError(
            this,
            "copySprite",
            "inObject must be of type Token, Tile, TokenDocument, or TileDocument"
          );
        }
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "copySprite",
            `inOptions must be of type object`
          );
        inObject = inObject.document ?? inObject;
        if (!inObject?.texture?.src)
          throw this.sequence._customError(
            this,
            "copySprite",
            "could not find the image for the given object"
          );
        inOptions = foundry.utils.mergeObject(
          {
            cacheLocation: false,
            offset: false,
            randomOffset: false,
            local: false,
            gridUnits: false
          },
          inOptions
        );
        if (typeof inOptions.cacheLocation !== "boolean")
          throw this.sequence._customError(
            this,
            "copySprite",
            "inOptions.cacheLocation must be of type boolean"
          );
        if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
          throw this.sequence._customError(
            this,
            "copySprite",
            "inOptions.randomOffset must be of type boolean or number"
          );
        if (inOptions.offset) {
          const offsetData = this._validateOffset(
            "attachTo",
            inOptions.offset,
            inOptions
          );
          this._offset = {
            source: offsetData,
            target: this._offset?.target ?? false
          };
        }
        this._randomOffset = {
          source: inOptions.randomOffset,
          target: this._randomOffset?.target ?? false
        };
        this._copySprite = {
          object: inObject,
          options: inOptions
        };
        return this;
      }
      shape(inType, inOptions = {}) {
        if (typeof inType !== "string")
          throw this.sequence._customError(
            this,
            "shape",
            "type must be of type string"
          );
        if (!Object.values(CONSTANTS.SHAPES).includes(inType)) {
          throw this.sequence._customError(
            this,
            "shape",
            "type must be one of: " + Object.values(CONSTANTS.SHAPES).join(", ")
          );
        }
        if (inType === CONSTANTS.SHAPES.POLY) {
          if (!Array.isArray(inOptions.points)) {
            throw this.sequence._customError(
              this,
              "shape",
              "if creating polygon, inOptions.points must be of type array"
            );
          }
          inOptions.points = inOptions.points.map((point) => {
            if (Array.isArray(point)) {
              if (!is_real_number(point[0]) || !is_real_number(point[1])) {
                throw this.sequence._customError(
                  this,
                  "shape",
                  "inOptions.points must be an array, containing an array of two numbers or objects with x and y number properties"
                );
              }
              return point;
            }
            if (typeof point === "object") {
              if (!is_real_number(point?.x) || !is_real_number(point?.y)) {
                throw this.sequence._customError(
                  this,
                  "shape",
                  "inOptions.points must be an array, containing an array of two numbers or objects with x and y number properties"
                );
              }
              return [point.x, point.y];
            }
          });
        } else if (inType === CONSTANTS.SHAPES.CIRC) {
          if (typeof inOptions.radius !== "number") {
            throw this.sequence._customError(
              this,
              "shape",
              "if creating circle, inOptions.radius must be of type number"
            );
          }
        } else if (inType === CONSTANTS.SHAPES.RECT || inType === CONSTANTS.SHAPES.RREC || inType === CONSTANTS.SHAPES.ELIP) {
          if (inOptions.width ^ inOptions.height) {
            inOptions.width = inOptions.width ?? inOptions.height;
            inOptions.height = inOptions.height ?? inOptions.width;
          }
          if (typeof inOptions.width !== "number") {
            throw this.sequence._customError(
              this,
              "shape",
              `if creating rectangle, rounded rectangle, or an ellipse, inOptions.width must be of type number`
            );
          }
          if (typeof inOptions.height !== "number") {
            throw this.sequence._customError(
              this,
              "shape",
              "if creating rectangle, rounded rectangle, or an ellipse, inOptions.height must be of type number"
            );
          }
          if (inType === CONSTANTS.SHAPES.RREC && typeof inOptions.radius !== "number") {
            throw this.sequence._customError(
              this,
              "shape",
              "if creating rounded border rectangle, inOptions.radius must be of type number"
            );
          }
        }
        if (inOptions.gridUnits !== void 0 && typeof inOptions.gridUnits !== "boolean") {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.gridUnits must be of type boolean"
          );
        }
        if (inOptions.name && typeof inOptions.name !== "string") {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.name must be of type string"
          );
        }
        if (inOptions.fillColor && !is_real_number(inOptions.fillColor) && typeof inOptions.fillColor !== "string") {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.fillColor must be of type string (hexadecimal) or number (decimal)"
          );
        } else {
          inOptions.fillColor = parseColor(inOptions.fillColor).decimal;
        }
        if (inOptions.fillAlpha && !is_real_number(inOptions.fillAlpha)) {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.fillAlpha must be of type number"
          );
        }
        if (inOptions.alpha && !is_real_number(inOptions.alpha)) {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.alpha must be of type number"
          );
        }
        if (inOptions.lineSize && !is_real_number(inOptions.lineSize)) {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.lineSize must be of type number"
          );
        }
        if (inOptions.lineColor && !is_real_number(inOptions.lineColor) && typeof inOptions.lineColor !== "string") {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.lineColor must be of type string (hexadecimal) or number (decimal)"
          );
        } else {
          inOptions.lineColor = parseColor(inOptions.lineColor).decimal;
        }
        if (inOptions.offset) {
          inOptions.offset = this._validateOffset(
            "shape",
            inOptions.offset,
            inOptions.offset
          );
        }
        if (inOptions.texture !== void 0 && typeof inOptions.texture !== "string") {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.texture must be of type string"
          );
        }
        if (inOptions.isMask !== void 0 && typeof inOptions.isMask !== "boolean") {
          throw this.sequence._customError(
            this,
            "shape",
            "inOptions.isMask must be of type boolean"
          );
        }
        this._shapes.push({
          ...inOptions,
          type: inType
        });
        return this;
      }
      /**
       *  Causes the effect's sprite to be offset relative to its location based on a given vector
       *
       * @param {Object} inOffset
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      spriteOffset(inOffset, inOptions = {}) {
        if (inOffset === void 0)
          throw this.sequence._customError(
            this,
            "spriteOffset",
            "inOffset must not be undefined"
          );
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "spriteOffset",
            "options must be of type object"
          );
        this._spriteOffset = this._validateOffset(
          "spriteOffset",
          inOffset,
          inOptions
        );
        return this;
      }
      /**
       * Causes the final effect location to be snapped to the grid
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      snapToGrid(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "snapToGrid",
            "inBool must be of type boolean"
          );
        this._snapToGrid = inBool;
        return this;
      }
      /**
       * Causes the effect to be scaled to the target object's width
       *
       * @param {Number} inScale
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      scaleToObject(inScale = 1, inOptions = {}) {
        if (!is_real_number(inScale))
          throw this.sequence._customError(
            this,
            "scaleToObject",
            `inScale must be of type number!`
          );
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "scaleToObject",
            "inOptions must be of type object"
          );
        inOptions = foundry.utils.mergeObject(
          {
            scale: inScale,
            considerTokenScale: false,
            uniform: false
          },
          inOptions
        );
        if (typeof inOptions.considerTokenScale !== "boolean")
          throw this.sequence._customError(
            this,
            "scaleToObject",
            "inOptions.considerTokenScale must be of type boolean"
          );
        if (typeof inOptions.uniform !== "boolean")
          throw this.sequence._customError(
            this,
            "scaleToObject",
            "inOptions.uniform must be of type boolean"
          );
        this._scaleToObject = inOptions;
        return this;
      }
      /**
       * Sets the width and the height of the effect in pixels, this size is set before any scaling
       *
       * @param {Number|Object<{width: {Number}, height: {Number}}>} inSize
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      size(inSize, inOptions = {}) {
        if (!is_real_number(inSize) && typeof inSize !== "object")
          throw this.sequence._customError(
            this,
            "size",
            "inSize must be of type number or object"
          );
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "size",
            "inOptions must be of type object"
          );
        if (is_real_number(inSize)) {
          inSize = {
            width: inSize,
            height: inSize
          };
        }
        if (inSize.width === void 0 ^ inSize.height === void 0) {
          if (inSize.width) {
            if (!is_real_number(inSize.width))
              throw this.sequence._customError(
                this,
                "size",
                "inSize.width must be of type number or string 'auto'"
              );
            inSize["height"] = "auto";
          } else {
            if (!is_real_number(inSize.height))
              throw this.sequence._customError(
                this,
                "size",
                "inSize.height must be of type number or string 'auto'"
              );
            inSize["width"] = "auto";
          }
        }
        inOptions = foundry.utils.mergeObject(
          {
            gridUnits: false
          },
          inOptions
        );
        if (!is_real_number(inSize.width) && inSize.width !== "auto")
          throw this.sequence._customError(
            this,
            "size",
            "inSize.width must be of type number or string 'auto'"
          );
        if (!is_real_number(inSize.height) && inSize.height !== "auto")
          throw this.sequence._customError(
            this,
            "size",
            "inSize.height must be of type number or string 'auto'"
          );
        if (typeof inOptions.gridUnits !== "boolean")
          throw this.sequence._customError(
            this,
            "size",
            "inOptions.gridUnits must be of type boolean"
          );
        this._size = {
          width: inSize.width ?? canvas.grid.size,
          height: inSize.height ?? canvas.grid.size,
          ...inOptions
        };
        return this;
      }
      /**
       *  This scales the sprite of the effect, and this method can take the following:
       *  - A number to set the scale uniformly
       *  - An object with x and y for non-uniform scaling
       *  - Two numbers which the Sequencer will randomly pick a uniform scale between
       *
       * @param {number|object} inScaleMin
       * @param {number} [inScaleMax] inScaleMax
       * @returns this
       */
      spriteScale(inScaleMin, inScaleMax) {
        if (!is_real_number(inScaleMin) && typeof inScaleMin !== "object")
          throw this.sequence._customError(
            this,
            "spriteScale",
            "inScale must be of type number or object"
          );
        if (is_real_number(inScaleMin)) {
          if (inScaleMax && !is_real_number(inScaleMax)) {
            throw this.sequence._customError(
              this,
              "spriteScale",
              "if inScaleMin is a number, inScaleMax must also be of type number"
            );
          }
        }
        this._spriteScaleMin = inScaleMin;
        this._spriteScaleMax = inScaleMax ?? false;
        return this;
      }
      /**
       * This defines the internal padding of this effect. Gridsize determines the internal grid size of this effect which will determine how big it is on the canvas
       * relative to the canvas's grid size. Start and end point defines padding at the left and right of the effect
       *
       * @param {Number} gridSize
       * @param {Number} startPoint
       * @param {Number} endPoint
       * @returns {EffectSection}
       */
      template({ gridSize, startPoint, endPoint } = {}) {
        if (gridSize && !is_real_number(gridSize))
          throw this.sequence._customError(
            this,
            "template",
            "gridSize must be of type number"
          );
        if (startPoint && !is_real_number(startPoint))
          throw this.sequence._customError(
            this,
            "template",
            "startPoint must be of type number"
          );
        if (endPoint && !is_real_number(endPoint))
          throw this.sequence._customError(
            this,
            "template",
            "endPoint must be of type number"
          );
        if (!gridSize && !startPoint && !endPoint)
          throw this.sequence._customError(
            this,
            "template",
            "You need to define at least one parameter!"
          );
        if (!this._template) this._template = {};
        if (gridSize) this._template["gridSize"] = gridSize;
        if (startPoint) this._template["startPoint"] = startPoint;
        if (endPoint) this._template["endPoint"] = endPoint;
        return this;
      }
      /**
       * This makes the texture of the effect tile, effectively repeat itself within the sprite's dimensions
       *
       * @param {Object|Number} scale
       * @param {Object} position
       * @returns {EffectSection}
       */
      tilingTexture(scale2 = { x: 1, y: 1 }, position = { x: 0, y: 0 }) {
        if (is_real_number(scale2)) {
          scale2 = { x: scale2, y: scale2 };
        }
        scale2 = { x: scale2?.x ?? 1, y: scale2?.y ?? 1 };
        if (!is_real_number(scale2.x))
          throw this.sequence._customError(
            this,
            "tilingTexture",
            `scale.x must be of type number!`
          );
        if (!is_real_number(scale2.y))
          throw this.sequence._customError(
            this,
            "tilingTexture",
            `scale.y must be of type number!`
          );
        position = { x: position?.x ?? 0, y: position?.y ?? 0 };
        if (!is_real_number(position.x))
          throw this.sequence._customError(
            this,
            "tilingTexture",
            `position.x must be of type number!`
          );
        if (!is_real_number(position.y))
          throw this.sequence._customError(
            this,
            "tilingTexture",
            `position.y must be of type number!`
          );
        this._tilingTexture = {
          scale: scale2,
          position
        };
        return this;
      }
      /**
       *  Anchors the sprite's container according to the given x and y coordinates, or uniformly based on a single number
       *
       * @param {Number|Object} inAnchor
       * @returns {EffectSection}
       */
      anchor(inAnchor) {
        if (is_real_number(inAnchor)) {
          inAnchor = {
            x: inAnchor,
            y: inAnchor
          };
        }
        inAnchor = {
          x: inAnchor?.x ?? 0.5,
          y: inAnchor?.y ?? 0.5
        };
        if (!is_real_number(inAnchor.x))
          throw this.sequence._customError(
            this,
            "anchor",
            `inAnchor.x must be of type number!`
          );
        if (!is_real_number(inAnchor.y))
          throw this.sequence._customError(
            this,
            "anchor",
            `inAnchor.y must be of type number!`
          );
        this._anchor = inAnchor;
        return this;
      }
      /**
       *  Anchors the sprite according to the given x and y coordinates, or uniformly based on a single number
       *
       * @param {Number|Object} inAnchor
       * @returns {EffectSection}
       */
      spriteAnchor(inAnchor) {
        if (is_real_number(inAnchor)) {
          inAnchor = {
            x: inAnchor,
            y: inAnchor
          };
        }
        inAnchor = {
          x: inAnchor?.x ?? 0.5,
          y: inAnchor?.y ?? 0.5
        };
        if (!is_real_number(inAnchor.x))
          throw this.sequence._customError(
            this,
            "anchor",
            `inAnchor.x must be of type number!`
          );
        if (!is_real_number(inAnchor.y))
          throw this.sequence._customError(
            this,
            "anchor",
            `inAnchor.y must be of type number!`
          );
        this._spriteAnchor = inAnchor;
        return this;
      }
      /**
       *  Centers the sprite, effectively giving it an anchor of {x: 0.5, y: 0.5}
       *
       *  Note: If this is used, it will override the anchor set by Aim Towards, which sets the sprite's anchor to the
       *  outermost edge of the location the sprite is played at
       *
       * @returns {EffectSection}
       */
      center() {
        this.anchor(0.5);
        return this;
      }
      /**
       * The sprite gets a randomized flipped X scale. If the scale on that axis was 1, it can
       * become 1 or -1, effectively mirroring the sprite on its horizontal axis
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      randomizeMirrorX(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "randomizeMirrorX",
            "inBool must be of type boolean"
          );
        this._randomMirrorX = inBool;
        return this;
      }
      /**
       * The sprite gets a randomized flipped Y scale. If the scale on that axis was 1, it can
       * become 1 or -1, effectively mirroring the sprite on its vertical axis
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      randomizeMirrorY(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "randomizeMirrorY",
            "inBool must be of type boolean"
          );
        this._randomMirrorY = inBool;
        return this;
      }
      /**
       * The sprite gets a flipped X scale. If the scale on that axis was 1, it will become 1 or -1, effectively
       * mirroring the sprite on its horizontal axis
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      mirrorX(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "mirrorX",
            "inBool must be of type boolean"
          );
        this._mirrorX = inBool;
        return this;
      }
      /**
       * The sprite gets a flipped Y scale. If the scale on that axis was 1, it will become 1 or -1, effectively
       * mirroring the sprite on its vertical axis
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      mirrorY(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "mirrorY",
            "inBool must be of type boolean"
          );
        this._mirrorY = inBool;
        return this;
      }
      /**
       * Causes the effect to play beneath most tokens
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      belowTokens(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "belowTokens",
            "inBool must be of type boolean"
          );
        if (game.release.generation >= 12) {
          return this.sortLayer(inBool ? 600 : 800);
        } else {
          if (!inBool) return this;
          return this.elevation(0, { absolute: true });
        }
      }
      /**
       * Causes the effect to play beneath most tiles
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      belowTiles(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "belowTokens",
            "inBool must be of type boolean"
          );
        if (game.release.generation >= 12) {
          return this.sortLayer(inBool ? 300 : 500);
        } else {
          if (!inBool) return this;
          return this.elevation(-1, { absolute: true });
        }
      }
      /**
       * Causes the effect to be played on top of the vision mask
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      aboveLighting(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "aboveLighting",
            "inBool must be of type boolean"
          );
        this._aboveLighting = inBool;
        if (game.release.generation >= 12) {
          return this.sortLayer(inBool ? 1200 : 800);
        } else {
          if (!inBool) return this;
        }
        return this;
      }
      /**
       * Causes the effect to be played on top of interface
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      aboveInterface(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "aboveInterface",
            "inBool must be of type boolean"
          );
        this._aboveInterface = inBool;
        return this;
      }
      /**
       * Changes the effect's elevation
       *
       * @param {Number} inElevation
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      elevation(inElevation, inOptions = {}) {
        if (typeof inElevation !== "number")
          throw this.sequence._customError(
            this,
            "elevation",
            "inElevation must be of type number"
          );
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "elevation",
            `inOptions must be of type object`
          );
        inOptions = foundry.utils.mergeObject(
          {
            elevation: 1,
            absolute: false
          },
          inOptions
        );
        if (typeof inOptions.absolute !== "boolean")
          throw this.sequence._customError(
            this,
            "elevation",
            "inOptions.absolute must be of type boolean"
          );
        this._elevation = {
          elevation: inElevation,
          absolute: inOptions.absolute
        };
        return this;
      }
      /**
       * Changes the effect's sortLayer, potentially displaying effects below tiles, above tokens or even weather effects
       * in case of identical elevations
       *
       * @param {Number} inSortLayer
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      sortLayer(inSortLayer, inOptions = {}) {
        if (!is_real_number(inSortLayer))
          throw this.sequence._customError(
            this,
            "sortLayer",
            "inSortLayer must be of type number"
          );
        this._sortLayer = inSortLayer;
        return this;
      }
      /**
       * Sets the zIndex of the effect, potentially displaying it on top of other effects the same elevation and sort
       *
       * @param {Number} inZIndex
       * @returns {EffectSection}
       */
      zIndex(inZIndex) {
        if (!is_real_number(inZIndex))
          throw this.sequence._customError(
            this,
            "zIndex",
            "inZIndex must be of type number"
          );
        this._zIndex = inZIndex;
        return this;
      }
      /**
       * This method only modifies .persist()ed effects and causes them to not immediately end, but stick around for the given duration passed to this method.
       *
       * @param {Number} inExtraDuration
       * @returns {EffectSection}
       */
      extraEndDuration(inExtraDuration) {
        if (!is_real_number(inExtraDuration))
          throw this.sequence._customError(
            this,
            "extraEndDuration",
            "inExtraDuration must be of type number"
          );
        this._extraEndDuration = inExtraDuration;
        return this;
      }
      /**
       * Rotates the sprite
       *
       * @param {Number} inAngle
       * @returns {EffectSection}
       */
      spriteRotation(inAngle) {
        if (!is_real_number(inAngle))
          throw this.sequence._customError(
            this,
            "spriteRotation",
            "inAngle must be of type number"
          );
        this._spriteRotation = inAngle;
        return this;
      }
      /**
       * Rotates the sprite
       *
       * @param {Boolean} inBool
       * @returns {EffectSection}
       */
      randomSpriteRotation(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "randomSpriteRotation",
            "inBool must be of type boolean"
          );
        this._randomSpriteRotation = inBool;
        return this;
      }
      /**
       * Causes the effect to not rotate should its container rotate
       *
       * @param {Boolean} [inBool=true] inBool
       * @returns {EffectSection}
       */
      zeroSpriteRotation(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "zeroSpriteRotation",
            "inBool must be of type boolean"
          );
        this._zeroSpriteRotation = inBool;
        return this;
      }
      /**
       * @deprecated
       */
      noLoop(inBool = true) {
        this.sequence._showWarning(
          this,
          "noLoop",
          ".noLoop() is deprecated, please use .loopOptions({ loops: 1 }) instead"
        );
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "noLoop",
            "inBool must be of type boolean"
          );
        this._loopOptions = foundry.utils.mergeObject(
          this._loopOptions ?? {
            loopDelay: 0,
            loops: 0
          },
          {
            loops: 1
          }
        );
        return this;
      }
      /**
       * Allows you to control the number of loops and the delays between each loop
       *
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      loopOptions(inOptions = {}) {
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "loopOptions",
            `inOptions must be of type object`
          );
        inOptions = foundry.utils.mergeObject(
          {
            loopDelay: 0,
            loops: 0,
            endOnLastLoop: false
          },
          inOptions
        );
        if (typeof inOptions.endOnLastLoop !== "boolean")
          throw this.sequence._customError(
            this,
            "loopOptions",
            "inOptions.endOnLastLoop must be of type boolean"
          );
        if (typeof inOptions.loopDelay !== "number" || inOptions.loopDelay < 0)
          throw this.sequence._customError(
            this,
            "loopOptions",
            "inOptions.loopDelay must be of type number that is not below 0"
          );
        if (typeof inOptions.loops !== "number" || inOptions.loops < 0)
          throw this.sequence._customError(
            this,
            "loopOptions",
            "inOptions.loops must be of type number that is not below 0"
          );
        this._loopOptions = inOptions;
        return this;
      }
      /**
       * Causes the effect to not show up in the Effect Manager UI - DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING
       *
       * @param inBool
       * @returns {EffectSection}
       */
      private(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "private",
            "inBool must be of type boolean"
          );
        this._private = inBool;
        return this;
      }
      /**
       * Causes the effect to be played in screen space instead of world space (where tokens are)
       *
       * @param {Boolean} [inBool=true] inBool
       * @returns {EffectSection}
       */
      screenSpace(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "screenSpace",
            "inBool must be of type boolean"
          );
        this._screenSpace = inBool;
        this._screenSpaceAnchor = this._screenSpaceAnchor ?? { x: 0.5, y: 0.5 };
        return this;
      }
      /**
       * Causes the effect to be played above all of the UI elements
       *
       * @param {Boolean} [inBool=true] inBool
       * @returns {EffectSection}
       */
      screenSpaceAboveUI(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "screenSpaceAboveUI",
            "inBool must be of type boolean"
          );
        this._screenSpaceAboveUI = inBool;
        return this;
      }
      /**
       *  Positions the effect in a screen space position, offset from its .screenSpaceAnchor()
       *
       * @param {Object} inPosition
       * @returns {EffectSection}
       */
      screenSpacePosition(inPosition) {
        inPosition = {
          x: inPosition?.x ?? 0,
          y: inPosition?.y ?? 0
        };
        if (!is_real_number(inPosition.x))
          throw this.sequence._customError(
            this,
            "screenSpacePosition",
            `inPosition.x must be of type number!`
          );
        if (!is_real_number(inPosition.y))
          throw this.sequence._customError(
            this,
            "screenSpacePosition",
            `inPosition.y must be of type number!`
          );
        this._screenSpacePosition = inPosition;
        return this;
      }
      /**
       *  Anchors the sprite according to the given x and y coordinates, or uniformly based on a single number in screen space
       *
       * @param {Number|Object} inAnchor
       * @returns {EffectSection}
       */
      screenSpaceAnchor(inAnchor) {
        if (is_real_number(inAnchor)) {
          inAnchor = {
            x: inAnchor,
            y: inAnchor
          };
        }
        inAnchor = {
          x: inAnchor?.x ?? 0.5,
          y: inAnchor?.y ?? 0.5
        };
        if (!is_real_number(inAnchor.x))
          throw this.sequence._customError(
            this,
            "screenSpaceAnchor",
            `inAnchor.x must be of type number!`
          );
        if (!is_real_number(inAnchor.y))
          throw this.sequence._customError(
            this,
            "screenSpaceAnchor",
            `inAnchor.y must be of type number!`
          );
        this._screenSpaceAnchor = inAnchor;
        return this;
      }
      /**
       *  Sets up various properties relating to scale of the effect on the screen
       *
       * @param {Object} inOptions
       * @returns {EffectSection}
       */
      screenSpaceScale(inOptions) {
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "screenSpaceScale",
            `inOptions must be of type object`
          );
        inOptions = foundry.utils.mergeObject(
          {
            x: 1,
            y: 1,
            fitX: false,
            fitY: false,
            ratioX: false,
            ratioY: false
          },
          inOptions
        );
        if (!is_real_number(inOptions.x))
          throw this.sequence._customError(
            this,
            "screenSpaceScale",
            `inOptions.x must be of type number!`
          );
        if (!is_real_number(inOptions.y))
          throw this.sequence._customError(
            this,
            "screenSpaceScale",
            `inOptions.y must be of type number!`
          );
        if (typeof inOptions.fitX !== "boolean")
          throw this.sequence._customError(
            this,
            "screenSpaceScale",
            "inOptions.fitX must be of type boolean"
          );
        if (typeof inOptions.fitY !== "boolean")
          throw this.sequence._customError(
            this,
            "screenSpaceScale",
            "inOptions.fitY must be of type boolean"
          );
        if (typeof inOptions.ratioX !== "boolean")
          throw this.sequence._customError(
            this,
            "screenSpaceScale",
            "inOptions.ratioX must be of type boolean"
          );
        if (typeof inOptions.ratioY !== "boolean")
          throw this.sequence._customError(
            this,
            "screenSpaceScale",
            "inOptions.ratioY must be of type boolean"
          );
        if (inOptions.ratioX && inOptions.ratioY)
          throw this.sequence._customError(
            this,
            "screenSpaceScale",
            "both ratioX and ratioY cannot be true, one axis must fit or be set directly"
          );
        this._screenSpaceScale = inOptions;
        return this;
      }
      /**
       * Ties the effect to any number of documents in Foundry - if those get deleted, the effect is ended.
       *
       * @param {String|PlaceableObject|foundry.abstract.Document|Array<String|PlaceableObject|foundry.abstract.Document>} inDocuments
       * @returns {EffectSection}
       */
      tieToDocuments(inDocuments) {
        if (!Array.isArray(inDocuments)) {
          inDocuments = [inDocuments];
        }
        for (let doc of inDocuments) {
          if (typeof doc !== "string" && !(doc instanceof PlaceableObject) && !(doc instanceof foundry.abstract.Document)) {
            throw this.sequence._customError(
              this,
              "tieToDocument",
              "inOrigin must be of type string, PlaceableObject, or Document, or an array thereof"
            );
          }
          if (typeof doc === "string") {
            const obj = fromUuidSync(doc);
            if (!obj)
              throw this.sequence._customError(
                this,
                "tieToDocument",
                `could not find document with UUID "${doc}"`
              );
          } else {
            doc = validate_document(doc);
            if (doc instanceof foundry.abstract.Document) {
              doc = doc?.uuid;
              if (!doc)
                throw this.sequence._customError(
                  this,
                  "tieToDocument",
                  "could not find the UUID for the given object"
                );
            }
          }
          this._tiedDocuments.push(doc);
        }
        return this;
      }
      /**
       *  Masks the effect to the given object or objects. If no object is given, the effect will be masked to the source
       *  of the effect.
       *
       * @param {Token/TokenDocument/Tile/TileDocument/Drawing/DrawingDocument/MeasuredTemplate/MeasuredTemplateDocument/Array} inObject
       * @returns {Section}
       */
      mask(inObject) {
        if (!inObject) {
          this._selfMask = true;
          return this;
        }
        if (Array.isArray(inObject)) {
          for (let obj of inObject) {
            this.mask(obj);
          }
          return this;
        }
        const validatedObject = this._validateLocation(inObject);
        const isValidObject = validatedObject instanceof TokenDocument || validatedObject instanceof TileDocument || validatedObject instanceof DrawingDocument || validatedObject instanceof MeasuredTemplateDocument;
        if (!isValidObject) {
          throw this.sequence._customError(
            this,
            "mask",
            "A foundry object was provided, but only Tokens, Tiles, Drawings, and MeasuredTemplates may be used to create effect masks"
          );
        }
        this._masks.push(get_object_identifier(validatedObject));
        return this;
      }
      /**
       * Causes the effect to be visible through walls
       *
       * @param inBool
       * @returns {EffectSection}
       */
      xray(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "xray",
            "inBool must be of type boolean"
          );
        this._xray = inBool;
        return this;
      }
      /**
       * Declares which file to be played. This may also be an array of paths, which will be randomly picked from each
       * time the section is played.
       *
       * @param {string|array} inFile
       * @param {object} inOptions
       * @returns this
       */
      file(inFile, inOptions = {}) {
        if (typeof inOptions === "boolean") {
          inOptions = {};
          this.sequence._showWarning(
            this,
            "file",
            "passing a boolean as a second argument to .file() is deprecated, please softFail: true on the sequence itself instead"
          );
        }
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "file",
            "inOptions must be of type object"
          );
        inOptions = foundry.utils.mergeObject({
          antialiasing: PIXI.SCALE_MODES.LINEAR
        }, inOptions);
        if (typeof inOptions.antialiasing !== "number" || !(inOptions.antialiasing >= 0 && inOptions.antialiasing <= 1)) {
          throw this.sequence._customError(
            this,
            "file",
            "inOptions.antialiasing must be of type number of 0 or 1 (see PIXI.SCALE_MODES)"
          );
        }
        this._file = inFile;
        this._fileOptions = inOptions;
        return this;
      }
      /**
       * Configures the isometric configuration of this effect
       *
       * @param inOptions
       * @returns {EffectSection}
       */
      isometric(inOptions = {}) {
        if (typeof inOptions !== "object")
          throw this.sequence._customError(
            this,
            "isometric",
            `inOptions must be of type object`
          );
        inOptions = foundry.utils.mergeObject(
          {
            overlay: false
          },
          inOptions
        );
        if (typeof inOptions?.overlay !== "boolean")
          throw this.sequence._customError(
            this,
            "isometric",
            "inOptions.overlay must be of type boolean"
          );
        this._isometric = inOptions;
        return this;
      }
      /**
       * @private
       */
      _expressWarnings() {
        if (this._stretchTo && this._anchor?.x) {
          this.sequence._showWarning(
            this,
            "stretchTo",
            "you have called .stretchTo() and .anchor() - stretchTo will manually set the X axis of the anchor and may not behave like you expect.",
            true
          );
        }
        if (this._stretchTo && this._scaleToObject) {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "You're trying to stretch towards an object, while scaling to fit another??? Make up your mind!"
          );
        }
        if (this._stretchTo && this._randomRotation) {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "You're trying to stretch towards an object, while trying to randomly rotate the effect? What?"
          );
        }
        if (this._stretchTo && this._moveTowards) {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "You're trying to stretch towards an object, while moving towards it? You're insane."
          );
        }
        if (this._attachTo && this._stretchTo?.attachTo && (this._startTime || this._endTime) && this._isRangedEffect) {
          throw this.sequence._customError(
            this,
            "stretchTo",
            "Dual-attached range-finding effects combined while using any of the time methods is stable - modern web browsers cannot handle it and it may crash them, so this feature has been disabled."
          );
        }
        const source = this._getSourceObject();
        const target = this._getTargetObject();
        if (!this._screenSpace && this._persistOptions?.persistTokenPrototype && this._masks.filter((uuid) => uuid !== source).length > 0) {
          this.sequence._showWarning(
            this,
            "persist",
            "You have applied persistTokenPrototype with multiple masks from objects in the scene - these will not be persisted across scenes!",
            true
          );
        }
        if (!source && !target && !this._screenSpace) {
          throw this.sequence._customError(
            this,
            "play",
            "Could not determine where to play the effect!"
          );
        }
      }
      /**
       * @OVERRIDE
       */
      async preRun() {
        const oldSource = this._source;
        const crosshairSource = this.sequence?.crosshairs?.[this._source];
        if (typeof this._source === "string" && crosshairSource) {
          this._source = crosshairSource.uuid;
        }
        const oldTarget = this._target?.target;
        const crosshairTarget = this.sequence?.crosshairs?.[this._target?.target];
        if (typeof this._target?.target === "string" && crosshairTarget) {
          this._target.target = crosshairTarget.uuid;
        }
        if (this._attachTo && !this._attachTo.active && typeof this._source === "string" && crosshairSource) {
          this._attachTo.active = !!crosshairSource;
        }
        if (this._name && (oldSource !== this._source || oldTarget !== this._target?.target)) {
          this.sequence.nameOffsetMap[this._name].source = this._getSourceObject();
          this.sequence.nameOffsetMap[this._name].target = this._getTargetObject();
        }
        const source = this._getSourceObject();
        if (this._persistOptions?.persistTokenPrototype && !(this._attachTo?.active || is_UUID(source))) {
          this._persistOptions.persistTokenPrototype = false;
        }
      }
      /**
       * @OVERRIDE
       * @returns {Promise<void>}
       */
      async run() {
        if (!user_can_do("permissions-effect-create") || !this._playEffect) {
          if (!user_can_do("permissions-effect-create")) {
            foundry.utils.debounce(EffectSection.debounceWarning, 1e3);
          }
          return new Promise((resolve) => {
            resolve();
          });
        }
        if (!this._deserializedData) this._expressWarnings();
        const data = await this._sanitizeEffectData();
        if (Hooks.call("preCreateSequencerEffect", data) === false) return;
        let push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
        let canvasEffectData = await Sequencer.EffectManager.play(data, push);
        let totalDuration = this._currentWaitTime;
        if (this._persist) {
          totalDuration += await canvasEffectData.promise;
        } else {
          totalDuration += await canvasEffectData.duration;
        }
        await new Promise((resolve) => setTimeout(resolve, totalDuration));
      }
      /**
       * @private
       */
      _applyTraits() {
        Object.assign(this.constructor.prototype, traits.files);
        Object.assign(this.constructor.prototype, traits.audio);
        Object.assign(this.constructor.prototype, traits.moves);
        Object.assign(this.constructor.prototype, traits.opacity);
        Object.assign(this.constructor.prototype, traits.rotation);
        Object.assign(this.constructor.prototype, traits.scale);
        Object.assign(this.constructor.prototype, traits.time);
        Object.assign(this.constructor.prototype, traits.users);
        Object.assign(this.constructor.prototype, traits.animation);
        Object.assign(this.constructor.prototype, traits.filter);
        Object.assign(this.constructor.prototype, traits.tint);
        Object.assign(this.constructor.prototype, traits.location);
        Object.assign(this.constructor.prototype, traits.offset);
        Object.assign(this.constructor.prototype, traits.text);
        Object.assign(this.constructor.prototype, traits.name);
      }
      /**
       * @private
       */
      async _initialize() {
        this._mirrorX = this._mirrorX || this._randomMirrorX && Math.random() < 0.5;
        this._mirrorY = this._mirrorY || this._randomMirrorY && Math.random() < 0.5;
        if (this._copySprite) {
          this._file = this._file || this._copySprite.object?.texture?.src;
          if (this._source === null) {
            this._source = this._validateLocation(this._copySprite.object);
          }
          if (this._size === null) {
            const size = get_object_dimensions(this._copySprite.object);
            this._size = {
              width: size?.width ?? canvas.grid.size,
              height: size?.height ?? canvas.grid.size,
              gridUnits: false
            };
          }
          if (this._mirrorX === null && (this._copySprite.object.mirrorX || this._copySprite.object?.tile && this._copySprite.object?.tile.scale.x < 0)) {
            this._mirrorX = true;
          }
          if (this._mirrorY === null && (this._copySprite.object.mirrorY || this._copySprite.object?.tile && this._copySprite.object?.tile.scale.y < 0)) {
            this._mirrorY = true;
          }
          if (this._angle === null && this._copySprite.object?.rotation) {
            this._angle = -this._copySprite.object.rotation;
          }
          this._randomOffset = {
            source: this._randomOffset?.source ?? this._copySprite.options.randomOffset,
            target: this._randomOffset?.target ?? false
          };
        }
        if (this._name) {
          this.sequence.nameOffsetMap ||= {};
          if (!this.sequence.nameOffsetMap[this._name]) {
            const source = this._getSourceObject();
            const target = this._getTargetObject();
            if (this._offsetLegacy && !this._offset) {
              this._offset = {
                source: !target ? this._offsetLegacy : false,
                target: !!target ? this._offsetLegacy : false
              };
            }
            if (this._randomOffsetLegacy && !this._randomOffset) {
              this._randomOffset = {
                source: !target ? this._randomOffsetLegacy : false,
                target: !!target ? this._randomOffsetLegacy : false
              };
            }
            this.sequence.nameOffsetMap[this._name] = {
              seed: `${this._name}-${foundry.utils.randomID()}`,
              source,
              target,
              randomOffset: this._randomOffset,
              angle: this._angle,
              randomRotation: this._randomRotation,
              mirrorX: this._mirrorX,
              mirrorY: this._mirrorY,
              missed: this._missed,
              offset: this._offset,
              repetitions: this._repetitions,
              twister: {}
            };
          }
        }
        if (!this._file && !this._copySprite && !this._text && !this._shapes.length && this.sequence.softFail) {
          this._playEffect = false;
          return;
        }
        let fileData = this._file ? await this._determineFile(this._file) : {
          file: this._file,
          forcedIndex: false,
          customRange: false
        };
        this._isRangedEffect = fileData?.file?.rangeFind;
        if (fileData.customRange || fileData.file?.dbPath) return;
        let exists = false;
        try {
          exists = await SequencerFileCache.srcExists(fileData.file);
        } catch (err) {
        }
        if (!exists) {
          if (this.sequence.softFail) {
            this._playEffect = false;
            return;
          }
          throw this.sequence._customError(
            this,
            "Play",
            `Could not find file:<br>${fileData.file}`
          );
        }
      }
      /**
       * @private
       */
      _getSourceObject() {
        if (!this._source || typeof this._source !== "object") {
          return this._source;
        }
        if (this._source instanceof CrosshairsPlaceable || this._source instanceof CrosshairsDocument) {
          const doc = this._source?.document ?? this._source;
          if (this._attachTo) {
            return get_object_identifier(doc.object);
          }
          return doc.getOrientation().source;
        }
        if (this._source?.cachedLocation || !this._attachTo) {
          return get_object_canvas_data(this._source, { uuid: false });
        }
        return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
      }
      /**
       * @private
       */
      _getTargetObject() {
        if (!this._target) return this._target;
        if (typeof this._target.target !== "object") {
          return this._target.target;
        }
        if (this._target?.target instanceof CrosshairsPlaceable || this._target?.target instanceof CrosshairsDocument) {
          const doc = this._target?.target?.document ?? this._target?.target;
          if (this._stretchTo?.attachTo || this._rotateTowards?.attachTo) {
            return get_object_identifier(doc.object);
          }
          const orientation = doc.getOrientation();
          return orientation.target ?? orientation.source;
        }
        if (this._target?.target?.cachedLocation || !(this._stretchTo?.attachTo || this._rotateTowards?.attachTo)) {
          return get_object_canvas_data(this._target.target, { measure: true, uuid: false });
        }
        return get_object_identifier(this._target.target) ?? get_object_canvas_data(this._target.target, { measure: true });
      }
      /**
       * @private
       */
      async _sanitizeEffectData() {
        if (this._deserializedData) {
          this._deserializedData.creationTimestamp = +/* @__PURE__ */ new Date();
          this._deserializedData.remote = true;
          return this._deserializedData;
        }
        const { file, forcedIndex, customRange } = this._file && this._playEffect ? await this._determineFile(this._file) : {
          file: this._file,
          forcedIndex: false,
          customRange: false
        };
        let source = this._getSourceObject();
        let target = this._getTargetObject();
        this._temporaryEffect = this._temporaryEffect || (source instanceof foundry.abstract.Document || source instanceof MeasuredTemplate ? !is_UUID(source?.uuid) : this._temporaryEffect || false);
        if (this._offsetLegacy) {
          this._offset = {
            source: !target && this._offset?.source ? this._offsetLegacy : this._offset?.source,
            target: !!target && this._offset?.target ? this._offsetLegacy : this._offset?.target
          };
        }
        if (this._randomOffsetLegacy) {
          this._randomOffset = {
            source: !target && this._randomOffset?.source ? this._randomOffsetLegacy : this._randomOffset?.source,
            target: !!target && this._randomOffset?.target ? this._randomOffsetLegacy : this._randomOffset?.target
          };
        }
        if (this._selfMask) {
          this._masks.push(
            get_object_identifier(this._source) ?? get_object_canvas_data(this._source, { uuid: false })
          );
        }
        let sceneId = game.user.viewedScene;
        if (is_UUID(source)) {
          const potentialSceneId = source.split(".")[1];
          if (game.scenes.get(potentialSceneId)) {
            sceneId = potentialSceneId;
          }
        } else if (is_UUID(target)) {
          const potentialSceneId = target.split(".")[1];
          if (game.scenes.get(potentialSceneId)) {
            sceneId = potentialSceneId;
          }
        }
        let data = foundry.utils.duplicate({
          /**
           * Core properties
           */
          _id: foundry.utils.randomID(),
          flagVersion: flagManager.latestFlagVersion,
          sequenceId: this.sequence.id,
          creationTimestamp: +/* @__PURE__ */ new Date(),
          sceneId,
          creatorUserId: game.userId,
          moduleName: this.sequence.moduleName,
          users: this._users ? Array.from(this._users) : false,
          name: this._name,
          origin: this._origin,
          index: this.sequence.effectIndex,
          repetition: this._currentRepetition,
          private: this._private,
          temporary: this._temporaryEffect,
          tiedDocuments: Array.from(new Set(this._tiedDocuments)),
          /**
           * Source/target properties
           */
          source,
          target,
          rotateTowards: this._rotateTowards,
          stretchTo: this._stretchTo ? {
            attachTo: this._stretchTo.attachTo,
            onlyX: this._stretchTo.onlyX,
            requiresLineOfSight: this._stretchTo.requiresLineOfSight,
            hideLineOfSight: this._stretchTo.hideLineOfSight
          } : false,
          moveTowards: this._moveTowards ? {
            ease: this._moveTowards.ease,
            rotate: this._moveTowards.rotate
          } : false,
          attachTo: this._attachTo,
          missed: this._missed,
          /**
           * Sprite properties
           */
          file: file?.dbPath ?? file,
          customRange,
          forcedIndex,
          fileOptions: this._fileOptions,
          text: this._text,
          tilingTexture: this._tilingTexture,
          masks: Array.from(new Set(this._masks)),
          shapes: this._shapes,
          volume: this._volume,
          isometric: this._isometric,
          syncGroup: this._syncGroup,
          // Transforms
          scale: this._getCalculatedScale("scale"),
          spriteScale: this._getCalculatedScale("spriteScale"),
          angle: this._angle,
          size: this._size,
          offset: this._offset,
          anchor: this._anchor,
          spriteOffset: this._spriteOffset,
          spriteAnchor: this._spriteAnchor,
          template: this._template,
          zeroSpriteRotation: this._zeroSpriteRotation,
          randomOffset: this._randomOffset,
          randomRotation: this._randomRotation,
          scaleToObject: this._scaleToObject,
          elevation: this._elevation,
          sortLayer: this._sortLayer,
          aboveLighting: this._aboveLighting,
          aboveInterface: this._aboveInterface,
          xray: this._xray,
          // Appearance
          zIndex: this._zIndex,
          opacity: is_real_number(this._opacity) ? this._opacity : 1,
          filters: this._filters,
          loopOptions: this._loopOptions,
          spriteRotation: this._spriteRotation,
          randomSpriteRotation: this._randomSpriteRotation,
          tint: this._tint?.decimal,
          flipX: this._mirrorX,
          flipY: this._mirrorY,
          /**
           * Time properties
           */
          duration: this._duration,
          persist: this._persist,
          persistOptions: this._persistOptions,
          playbackRate: this._playbackRate,
          extraEndDuration: this._extraEndDuration,
          time: this._startTime || this._endTime ? {
            start: is_real_number(this._startTime) ? {
              value: this._startTime,
              isPerc: this._startPerc
            } : false,
            end: is_real_number(this._endTime) ? {
              value: this._endTime,
              isPerc: this._endPerc
            } : false,
            isRange: this._isRange
          } : false,
          /**
           * Animation properties
           */
          moves: this._moveTowards,
          moveSpeed: this._moveSpeed,
          fadeIn: this._fadeIn,
          fadeOut: this._fadeOut,
          scaleIn: this._scaleIn,
          scaleOut: this._scaleOut,
          rotateIn: this._rotateIn,
          rotateOut: this._rotateOut,
          fadeInAudio: this._fadeInAudio,
          fadeOutAudio: this._fadeOutAudio,
          animations: this._animations,
          /**
           * Screenspace properties
           */
          screenSpace: this._screenSpace,
          screenSpaceAboveUI: this._screenSpaceAboveUI,
          screenSpaceAnchor: this._screenSpaceAnchor,
          screenSpacePosition: this._screenSpacePosition,
          screenSpaceScale: this._screenSpaceScale,
          nameOffsetMap: this.sequence.nameOffsetMap
        });
        for (let override of this._overrides) {
          data = await override(this, data);
        }
        if ((typeof data.file !== "string" || data.file === "") && !data.text && !data.shapes && !data.customRange) {
          throw this.sequence._customError(
            this,
            "file",
            "an effect must have a file, text, or have a shape!"
          );
        }
        return data;
      }
      async _serialize() {
        const data = await super._serialize();
        await this.preRun();
        const sectionData = await this._sanitizeEffectData();
        return {
          ...data,
          type: "effect",
          sectionData
        };
      }
      async _deserialize(data) {
        this._deserializedData = data.sectionData;
        return super._deserialize(data);
      }
      /**
       * @private
       */
      _getCalculatedScale(type) {
        const min = this["_" + type + "Min"];
        const max = this["_" + type + "Max"];
        let scale2 = min;
        if (is_real_number(min)) {
          if (max && is_real_number(max)) {
            scale2 = random_float_between(min, max);
          }
          scale2 = {
            x: scale2,
            y: scale2
          };
        }
        return {
          x: scale2?.x ?? 1,
          y: scale2?.y ?? 1
        };
      }
    }
    class SoundSection extends Section {
      constructor(inSequence, inFile = "") {
        super(inSequence);
        this._file = inFile;
        this._volume = 0.8;
        this._channel = "interface";
        this._overrides = [];
        this._locationOptions = {};
      }
      static niceName = "Sound";
      /**
       * Declares which file to be played. This may also be an array of paths, which will be randomly picked from each
       * time the section is played.
       *
       * @param {string|array} inFile
       * @returns this
       */
      file(inFile) {
        this._file = inFile;
        return this;
      }
      /**
       * Adds a function that will run at the end of the sound serialization step, but before it is played. Allows direct
       * modifications of sound's data.
       *
       * @param {function} inFunc
       * @returns {SoundSection}
       */
      addOverride(inFunc) {
        if (!is_function$1(inFunc))
          throw this.sequence._customError(
            this,
            "addOverride",
            "The given function needs to be an actual function."
          );
        this._overrides.push(inFunc);
        return this;
      }
      radius(inNumber) {
        if (!is_real_number(inNumber))
          throw this.sequence._customError(
            this,
            "radius",
            "inNumber must be of type number"
          );
        this._locationOptions["radius"] = inNumber;
        return this;
      }
      constrainedByWalls(inBool) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "constrainedByWalls",
            `inBool must be of type boolean`
          );
        this._locationOptions["walls"] = inBool;
        return this;
      }
      distanceEasing(inBool) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "distanceEasing",
            `inBool must be of type boolean`
          );
        this._locationOptions["easing"] = inBool;
        return this;
      }
      audioChannel(inString) {
        if (typeof inString !== "string")
          throw this.sequence._customError(
            this,
            "audioChannel",
            `inString must be of type string`
          );
        if (!(game.audio?.[inString] instanceof AudioContext)) {
          const validChannels = Object.entries(game.audio).filter((e) => e[1] instanceof AudioContext).map((e) => e[0]);
          throw this.sequence._customError(
            this,
            "audioChannel",
            `inString must be of type ${validChannels.slice(0, -1).join(", ")}, or ${validChannels.slice(-1)}`
          );
        }
        this._channel = inString;
        return this;
      }
      alwaysForGMs(inBool) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "alwaysForGMs",
            `inBool must be of type boolean`
          );
        this._locationOptions["gmAlways"] = inBool;
        return this;
      }
      baseEffect(options = {}) {
        options = foundry.utils.mergeObject({
          type: "",
          intensity: 0
        }, options);
        if (typeof options.type !== "string" || !CONFIG.soundEffects[options.type])
          throw this.sequence._customError(
            this,
            "baseEffect",
            `options.type must be of type string, and one of ${Object.keys(CONFIG.soundEffects).join(", ")}`
          );
        if (!is_real_number(options.intensity))
          throw this.sequence._customError(
            this,
            "baseEffect",
            "options.intensity must be of type number"
          );
        this._locationOptions["baseEffect"] = {
          type: options.type,
          intensity: options.intensity
        };
        return this;
      }
      muffledEffect(options = {}) {
        options = foundry.utils.mergeObject({
          type: "",
          intensity: 0
        }, options);
        if (typeof options.type !== "string" || !CONFIG.soundEffects[options.type])
          throw this.sequence._customError(
            this,
            "baseEffect",
            `options.type must be of type string, and one of ${Object.keys(CONFIG.soundEffects).join(", ")}`
          );
        if (!is_real_number(options.intensity))
          throw this.sequence._customError(
            this,
            "baseEffect",
            "options.intensity must be of type number"
          );
        this._locationOptions["muffledEffect"] = {
          type: options.type,
          intensity: options.intensity
        };
        return this;
      }
      /**
       * @private
       */
      _applyTraits() {
        Object.assign(this.constructor.prototype, traits.files);
        Object.assign(this.constructor.prototype, traits.audio);
        Object.assign(this.constructor.prototype, traits.time);
        Object.assign(this.constructor.prototype, traits.users);
        Object.assign(this.constructor.prototype, traits.name);
        Object.assign(this.constructor.prototype, traits.location);
        Object.assign(this.constructor.prototype, traits.offset);
      }
      /**
       * @OVERRIDE
       * @returns {Promise}
       */
      async run() {
        const playData = await this._sanitizeSoundData();
        if (typeof playData.src !== "string" || playData.src === "") {
          if (this.sequence.softFail) {
            playData.play = false;
          } else {
            throw this.sequence._customError(
              this,
              "file",
              "a sound must have a src of type string!"
            );
          }
        }
        if (playData.location && !CONSTANTS.IS_V12) {
          if (this.sequence.softFail) {
            playData.play = false;
          } else {
            throw this.sequence._customError(
              this,
              "atLocation",
              "a sound cannot have a location in v11"
            );
          }
        }
        if (!playData.play && this.sequence.softFail) {
          return new Promise((reject) => {
            reject();
          });
        }
        if (!playData?.play) {
          this.sequence._customError(
            this,
            "Play",
            `File not found: ${playData.src}`
          );
          return new Promise((reject) => reject());
        }
        if (Hooks.call("preCreateSequencerSound", playData) === false) return;
        let push = !(playData?.users?.length === 1 && playData?.users?.includes(game.userId)) && !this.sequence.localOnly;
        SequencerSoundManager.play(playData, push);
        await new Promise(
          (resolve) => setTimeout(resolve, this._currentWaitTime + playData.duration)
        );
      }
      /**
       * @returns {Promise}
       * @private
       */
      async _sanitizeSoundData() {
        if (this._deserializedData) {
          return this._deserializedData;
        }
        if (!this._file) {
          return {
            play: false,
            src: false
          };
        }
        let { file, forcedIndex } = await this._determineFile(this._file);
        if (!file) {
          return {
            play: false,
            src: false
          };
        }
        if (file instanceof SequencerFileBase) {
          file.forcedIndex = forcedIndex;
          if (file.timeRange) {
            [this._startTime, this._endTime] = file.timeRange;
            this._isRange = true;
          }
          file = file.getFile();
        }
        let soundFile = await foundry.audio.AudioHelper.preloadSound(file);
        if (!soundFile || soundFile.failed) {
          return {
            play: false,
            src: this._file
          };
        }
        let duration = soundFile.duration * 1e3;
        let startTime = (this._startTime ? !this._startPerc ? this._startTime : this._startTime * duration : 0) / 1e3;
        if (this._endTime) {
          duration = !this._endPerc ? Number(
            this._isRange ? this._endTime - this._startTime : duration - this._endTime
          ) : this._endTime * duration;
        }
        let data = {
          id: foundry.utils.randomID(),
          play: true,
          src: file,
          location: this._source?.uuid || (this._source?.x && this._source?.y ? { x: this._source?.x, y: this._source?.y } : null),
          offset: this._offset,
          randomOffset: this._randomOffset,
          locationOptions: this._locationOptions,
          loop: this._duration > duration,
          volume: this._volume,
          channel: this._channel,
          fadeIn: this._fadeInAudio,
          fadeOut: this._fadeOutAudio,
          startTime,
          duration: this._duration || duration,
          sceneId: this._source?.parent?.id || game.user.viewedScene,
          users: this._users ? Array.from(this._users) : null,
          name: this._name,
          origin: this._origin,
          seed: `${this._name}-${foundry.utils.randomID()}-${this._currentRepetition}`
        };
        for (let override of this._overrides) {
          data = await override(this, data);
        }
        return data;
      }
      async _serialize() {
        const data = await super._serialize();
        const sectionData = await this._sanitizeSoundData();
        return {
          ...data,
          type: "sound",
          sectionData
        };
      }
      async _deserialize(data) {
        this._deserializedData = data.sectionData;
        return super._deserialize(data);
      }
    }
    class AnimationSection extends Section {
      static niceName = "Animation";
      constructor(inSequence, inTarget) {
        super(inSequence);
        this._teleportTo = false;
        this._originObject = false;
        this._moveSpeed = 23;
        this._offset = { x: 0, y: 0 };
        this._closestSquare = false;
        this._snapToGrid = false;
        this._hide = void 0;
        if (inTarget) this.on(inTarget);
      }
      /**
       * Sets the target object to be animated
       *
       * @param {object|string} inTarget
       * @returns {AnimationSection}
       */
      on(inTarget) {
        inTarget = this._validateLocation(inTarget);
        if (!inTarget)
          throw this.sequence._customError(
            this,
            "on",
            "could not find position of given object"
          );
        this._originObject = this._validateLocation(inTarget);
        return this;
      }
      /**
       * Sets the location to teleport the target object to
       *
       * @param {object|string} inTarget
       * @param {object} options
       * @returns {AnimationSection}
       */
      teleportTo(inTarget, options = {}) {
        options = foundry.utils.mergeObject(
          {
            delay: 0,
            relativeToCenter: false
          },
          options
        );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "teleportTo",
            "options.delay must be of type number"
          );
        inTarget = this._validateLocation(inTarget);
        if (!inTarget)
          throw this.sequence._customError(
            this,
            "teleportTo",
            "could not find position of given object"
          );
        options.target = this._validateLocation(inTarget);
        this._teleportTo = options;
        return this;
      }
      /**
       * Sets the location to rotate the object to
       *
       * @param {object|string} inLocation
       * @param {object} options
       * @returns this
       */
      rotateTowards(inLocation, options = {}) {
        options = foundry.utils.mergeObject(
          {
            duration: 0,
            ease: "linear",
            delay: 0,
            rotationOffset: 0,
            towardsCenter: true,
            cacheLocation: false
          },
          options
        );
        if (!is_real_number(options.duration))
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "options.duration must be of type number"
          );
        if (typeof options.ease !== "string")
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "options.ease must be of type string"
          );
        if (!is_real_number(options.delay))
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "options.delay must be of type number"
          );
        if (!is_real_number(options.rotationOffset))
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "options.rotationOffset must be of type number"
          );
        if (typeof options.towardsCenter !== "boolean")
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "options.towardsCenter must be of type boolean"
          );
        if (typeof options.cacheLocation !== "boolean")
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "options.cacheLocation must be of type boolean"
          );
        options.target = this._validateLocation(inLocation);
        if (!options.target)
          throw this.sequence._customError(
            this,
            "rotateTowards",
            "could not find position of given object"
          );
        options.target = options.cacheLocation ? get_object_position(options.target, { measure: true }) : options.target;
        this._rotateTowards = options;
        return this;
      }
      /**
       * Causes the movement or teleportation to be offset in the X and/or Y axis
       *
       * @param {object} inOffset
       * @returns {AnimationSection}
       */
      offset(inOffset) {
        inOffset = foundry.utils.mergeObject({ x: 0, y: 0 }, inOffset);
        this._offset = this._validateLocation(inOffset);
        return this;
      }
      /**
       * Causes the movement or teleportation to pick the closest non-intersecting square, if the target is a token or tile
       *
       * @param {boolean} inBool
       * @returns {AnimationSection}
       */
      closestSquare(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "closestSquare",
            "inBool must be of type boolean"
          );
        this._closestSquare = inBool;
        return this;
      }
      /**
       * Causes the final location to be snapped to the grid
       *
       * @param {boolean} inBool
       * @returns {AnimationSection}
       */
      snapToGrid(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "snapToGrid",
            "inBool must be of type boolean"
          );
        this._snapToGrid = inBool;
        return this;
      }
      /**
       * Causes the object to become hidden
       *
       * @param {boolean} inBool
       * @returns {AnimationSection}
       */
      hide(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "hide",
            "inBool must be of type boolean"
          );
        this._hide = inBool;
        return this;
      }
      /**
       * Causes the object to become visible
       *
       * @param {boolean} inBool
       * @returns {AnimationSection}
       */
      show(inBool = true) {
        if (typeof inBool !== "boolean")
          throw this.sequence._customError(
            this,
            "show",
            "inBool must be of type boolean"
          );
        this._hide = !inBool;
        return this;
      }
      /**
       * @private
       */
      async run() {
        return this._runAnimate();
      }
      /**
       * @private
       */
      _applyTraits() {
        Object.assign(this.constructor.prototype, traits.moves);
        Object.assign(this.constructor.prototype, traits.opacity);
        Object.assign(this.constructor.prototype, traits.rotation);
        Object.assign(this.constructor.prototype, traits.audio);
        Object.assign(this.constructor.prototype, traits.tint);
      }
      /**
       * @private
       */
      async _updateObject(obj, updates, animate = false, animation2 = {}) {
        const uuid = obj?.uuid ?? obj?.document?.uuid;
        await sequencerSocket.executeAsGM(
          SOCKET_HANDLERS.UPDATE_DOCUMENT,
          uuid,
          updates,
          { animate, animation: animation2, teleport: !!this._teleportTo }
        );
      }
      /**
       * @private
       */
      async _waitForTokenRefresh(obj) {
        let token;
        if (obj instanceof Token) {
          token = obj;
        }
        if (obj instanceof TokenDocument) {
          token = obj.object;
        }
        if (token == null || token.mesh != null) {
          return;
        }
        let refreshTokenID;
        return await new Promise((resolve) => {
          refreshTokenID = Hooks.on("refreshToken", async (tokenDoc) => {
            if (tokenDoc.document.actorId !== token.document.actorId || !token.mesh)
              return;
            Hooks.off("refreshToken", refreshTokenID);
            resolve();
          });
        });
      }
      /**
       * @private
       */
      async _execute() {
        if (!await this._shouldPlay()) return;
        let self = this;
        this._basicDelay = random_float_between(this._delayMin, this._delayMax);
        return new Promise(async (resolve) => {
          setTimeout(async () => {
            await this._waitForTokenRefresh(this._originObject);
            if (this._shouldAsync) {
              await self.run();
            } else {
              self.run();
            }
            resolve();
          }, this._basicDelay);
        });
      }
      /**
       * @private
       */
      _getClosestSquare(origin, target) {
        let originLoc = get_object_position(origin, { exact: true });
        let targetLoc = get_object_position(target, { exact: true });
        let originDimensions = get_object_dimensions(origin);
        let targetDimensions = get_object_dimensions(target);
        let originBottom = Math.max(
          originDimensions.width - canvas.grid.size,
          canvas.grid.size
        );
        let originRight = Math.max(
          originDimensions.height - canvas.grid.size,
          canvas.grid.size
        );
        let ray = new Ray(originLoc, targetLoc);
        let dx = ray.dx;
        let dy = ray.dy;
        if (dx > 0 && Math.abs(dx) > originRight) {
          dx -= originDimensions.width;
        } else if (dx < 0 && Math.abs(dx) > targetDimensions.width) {
          dx += targetDimensions.height;
        } else {
          dx = 0;
        }
        if (dy > 0 && Math.abs(dy) > originBottom) {
          dy -= originDimensions.height;
        } else if (dy < 0 && Math.abs(dy) > targetDimensions.height) {
          dy += targetDimensions.height;
        } else {
          dy = 0;
        }
        const pos = {
          x: originLoc.x + dx,
          y: originLoc.y + dy,
          elevation: targetLoc?.elevation
        };
        if (!pos.elevation) {
          delete pos["elevation"];
        }
        return pos;
      }
      /**
       * This needs a rewrite, jeesus.
       */
      async _runAnimate() {
        let animData = {
          attributes: [],
          maxFPS: 1e3 / game.settings.get("core", "maxFPS"),
          lastTimespan: performance.now(),
          totalDt: 0
        };
        if (typeof this._moveTowards?.target === "string" && this.sequence.nameOffsetMap[this._moveTowards?.target]) {
          this._moveTowards.target = this.sequence.nameOffsetMap[this._moveTowards?.target].target ?? this.sequence.nameOffsetMap[this._moveTowards?.target].source;
        }
        if (typeof this._teleportTo?.target === "string" && this.sequence.nameOffsetMap[this._teleportTo?.target]) {
          this._teleportTo.target = this.sequence.nameOffsetMap[this._teleportTo?.target].target ?? this.sequence.nameOffsetMap[this._teleportTo?.target].source;
        }
        if (typeof this._rotateTowards?.target === "string" && this.sequence.nameOffsetMap[this._rotateTowards?.target]) {
          this._rotateTowards.target = this.sequence.nameOffsetMap[this._rotateTowards?.target].target ?? this.sequence.nameOffsetMap[this._rotateTowards?.target].source;
        }
        let overallDuration = this._duration ? this._duration : 0;
        const originLocation = get_object_position(this._originObject, {
          exact: true
        });
        if (this._rotateTowards) {
          let offset2 = (this._angle ? this._angle : 0) + this._rotateTowards.rotationOffset;
          let targetLoc = this._moveTowards?.target || this._teleportTo?.target || this._originObject;
          targetLoc = this._closestSquare ? this._getClosestSquare(this._originObject, targetLoc) : get_object_position(targetLoc, { exact: true });
          targetLoc.x += this._offset.x;
          targetLoc.y += this._offset.y;
          if (this._snapToGrid) {
            targetLoc = canvas.grid.getSnappedPoint(targetLoc, { mode: CONST.GRID_SNAPPING_MODES.CENTER, resolution: 1 });
          }
          if (this._originObject instanceof TokenDocument) {
            setTimeout(async () => {
              let startLocation = this._originObject.object?.center ?? targetLoc;
              let targetLocation = this._rotateTowards.target?.object ?? this._rotateTowards.target;
              if (this._rotateTowards.towardsCenter) {
                targetLocation = targetLocation?.center ?? targetLocation;
              }
              let ray = new Ray(startLocation, targetLocation);
              let angle = Math.normalizeDegrees(ray.angle * 180 / Math.PI - 90);
              angle += offset2;
              await this._updateObject(
                this._originObject,
                { rotation: angle },
                this._rotateTowards.duration > 0,
                {
                  duration: this._rotateTowards.duration,
                  easing: this._rotateTowards.ease
                }
              );
            }, this._rotateTowards.delay ?? 0);
          } else {
            animData.attributes.push({
              name: "rotationTowards",
              offset: offset2,
              origin: this._originObject,
              originLocation: targetLoc,
              target: this._rotateTowards.target?.object ?? this._rotateTowards.target,
              towardsCenter: this._rotateTowards.towardsCenter,
              from: false,
              to: false,
              progress: 0,
              done: false,
              duration: this._rotateTowards.duration,
              durationDone: 0,
              delay: this._rotateTowards.delay,
              ease: this._rotateTowards.ease
            });
          }
          let rotateDuration = this._rotateTowards.duration + this._rotateTowards.delay;
          overallDuration = overallDuration > rotateDuration ? overallDuration : rotateDuration;
        }
        if (this._fadeIn) {
          let to = is_real_number(this._opacity) ? this._opacity : 1;
          if (this._originObject instanceof TokenDocument) {
            setTimeout(async () => {
              await this._updateObject(this._originObject, { alpha: to }, true, {
                duration: this._fadeIn.duration,
                easing: this._fadeIn.ease
              });
            }, this._fadeIn.delay ?? 0);
          } else {
            animData.attributes.push({
              name: "alpha",
              from: 0,
              to,
              progress: 0,
              done: false,
              duration: this._fadeIn.duration,
              durationDone: 0,
              delay: this._fadeIn.delay,
              ease: this._fadeIn.ease
            });
          }
          let fadeDuration = this._fadeIn.duration + this._fadeIn.delay;
          overallDuration = overallDuration > fadeDuration ? overallDuration : fadeDuration;
        }
        if (this._fadeInAudio && this._originObject?.video?.volume !== void 0) {
          let to = is_real_number(this._volume) ? this._volume : 1;
          animData.attributes.push({
            name: "video.volume",
            from: 0,
            to,
            progress: 0,
            done: false,
            duration: this._fadeInAudio.duration,
            durationDone: 0,
            delay: this._fadeInAudio.delay,
            ease: this._fadeInAudio.ease
          });
          let fadeDuration = this._fadeInAudio.duration + this._fadeInAudio.delay;
          overallDuration = overallDuration > fadeDuration ? overallDuration : fadeDuration;
        }
        if (this._rotateIn) {
          let from = this._angle ? this._angle : this._originObject.rotation;
          let to = this._rotateIn.value;
          if (Math.abs(from - to) > 180) {
            if (to < 0) {
              to += 360;
            } else if (from > to) {
              from -= 360;
            }
          }
          if (this._originObject instanceof TokenDocument) {
            setTimeout(async () => {
              if (this._originObject.rotation !== from) {
                await this._updateObject(
                  this._originObject,
                  { rotation: from },
                  false
                );
              }
              await this._updateObject(this._originObject, { rotation: to }, true, {
                duration: this._rotateIn.duration,
                easing: this._rotateIn.ease
              });
            }, this._rotateIn.delay ?? 0);
          } else {
            animData.attributes.push({
              name: "rotation",
              from,
              to,
              progress: 0,
              done: false,
              duration: this._rotateIn.duration,
              durationDone: 0,
              delay: this._rotateIn.delay,
              ease: this._rotateIn.ease
            });
          }
          let rotateDuration = this._rotateIn.duration + this._rotateIn.delay;
          overallDuration = overallDuration > rotateDuration ? overallDuration : rotateDuration;
        }
        if (this._moveTowards) {
          let originLocation2 = get_object_position(this._originObject, {
            exact: true
          });
          let targetLocation = this._closestSquare ? this._getClosestSquare(this._originObject, this._moveTowards.target) : get_object_position(this._moveTowards.target, {
            exact: true
          });
          targetLocation.x += this._offset.x;
          targetLocation.y += this._offset.y;
          targetLocation.elevation = targetLocation?.elevation ?? this._originObject?.elevation;
          if (this._moveTowards.relativeToCenter) {
            const dimensions = get_object_dimensions(this._originObject);
            targetLocation.x -= dimensions.width / 2;
            targetLocation.y -= dimensions.height / 2;
            if (this._snapToGrid) {
              targetLocation.x -= 0.01;
              targetLocation.y -= 0.01;
            }
          }
          if (this._snapToGrid) {
            targetLocation = canvas.grid.getSnappedPoint(targetLocation, {
              mode: CONST.GRID_SNAPPING_MODES.VERTEX,
              resolution: 1
            });
          }
          let originalDx = targetLocation.x - originLocation2.x;
          let originalDy = targetLocation.y - originLocation2.y;
          let originalDistance = Math.sqrt(
            originalDx * originalDx + originalDy * originalDy
          );
          let duration = this._duration ? this._duration : originalDistance / this._moveSpeed * animData.maxFPS;
          let moveDuration = duration + this._moveTowards.delay;
          overallDuration = overallDuration > moveDuration ? overallDuration : moveDuration;
          if (this._originObject instanceof TokenDocument) {
            setTimeout(async () => {
              await this._updateObject(this._originObject, targetLocation, true, {
                movementSpeed: this._moveSpeed,
                duration,
                easing: this._moveTowards.ease
              });
            }, this._moveTowards.delay ?? 0);
          } else {
            if (!(duration === 0 || originalDistance === 0)) {
              animData.attributes.push({
                name: "position",
                origin: originLocation2,
                target: targetLocation,
                originalDistance,
                currentDistance: 0,
                progress: 0,
                speed: 0,
                duration,
                done: false,
                ease: this._moveTowards.ease,
                delay: this._moveTowards.delay
              });
            }
          }
        }
        if (this._fadeOut) {
          let from = is_real_number(this._opacity) ? this._opacity : this._originObject.alpha ?? 1;
          if (this._originObject instanceof TokenDocument) {
            setTimeout(async () => {
              await this._updateObject(this._originObject, { alpha: 0 }, true, {
                duration: this._fadeOut.duration,
                easing: this._fadeOut.ease
              });
            }, this._fadeOut.delay ?? 0);
          } else {
            animData.attributes.push({
              name: "alpha",
              from,
              to: 0,
              progress: 0,
              done: false,
              duration: this._fadeOut.duration,
              durationDone: 0,
              delay: overallDuration - this._fadeOut.duration,
              ease: this._fadeOut.ease
            });
          }
          let fadeOutDuration = this._fadeOut.duration + this._fadeOut.delay;
          overallDuration = overallDuration > fadeOutDuration ? overallDuration : fadeOutDuration;
        }
        if (this._fadeOutAudio && this._originObject?.video?.volume !== void 0) {
          let from = is_real_number(this._volume) ? this._volume : this._originObject.video.volume;
          animData.attributes.push({
            name: "video.volume",
            from,
            to: 0,
            progress: 0,
            done: false,
            duration: this._fadeOutAudio.duration,
            durationDone: 0,
            delay: overallDuration - this._fadeOutAudio.duration,
            ease: this._fadeOutAudio.ease
          });
          let fadeOutAudioDuration = this._fadeOutAudio.duration + this._fadeOutAudio.delay;
          overallDuration = overallDuration > fadeOutAudioDuration ? overallDuration : fadeOutAudioDuration;
        }
        if (this._rotateOut) {
          let from = this._rotateOut.value;
          let to = this._angle ? this._angle : this._originObject.rotation;
          if (this._rotateIn) from += this._rotateIn.value;
          if (Math.abs(from - to) > 180) {
            if (to < 0) {
              to += 360;
            } else if (from > to) {
              from -= 360;
            }
          }
          if (this._originObject instanceof TokenDocument) {
            setTimeout(async () => {
              if (this._originObject.rotation !== from) {
                await this._updateObject(
                  this._originObject,
                  { rotation: from },
                  false
                );
              }
              await this._updateObject(this._originObject, { rotation: to }, true, {
                duration: this._rotateOut.duration,
                easing: this._rotateOut.ease
              });
            }, this._rotateOut.delay ?? 0);
          } else {
            animData.attributes.push({
              name: "rotation",
              from,
              to,
              progress: 0,
              done: false,
              duration: this._rotateOut.duration,
              durationDone: 0,
              delay: overallDuration - this._rotateOut.duration,
              ease: this._rotateOut.ease
            });
          }
          let rotateOutDuration = this._rotateOut.duration + this._rotateOut.delay;
          overallDuration = overallDuration > rotateOutDuration ? overallDuration : rotateOutDuration;
        }
        if (this._teleportTo) {
          setTimeout(async () => {
            let targetLocation = this._closestSquare ? this._getClosestSquare(this._originObject, this._teleportTo.target) : get_object_position(this._teleportTo.target, {
              exact: true
            });
            if (targetLocation.x === void 0) targetLocation.x = originLocation.x;
            if (targetLocation.y === void 0) targetLocation.y = originLocation.y;
            if (targetLocation.elevation === void 0)
              targetLocation.elevation = originLocation.elevation;
            targetLocation.x += this._offset.x;
            targetLocation.y += this._offset.y;
            targetLocation.elevation = targetLocation?.elevation ?? this._originObject?.elevation;
            const dimensions = get_object_dimensions(this._originObject);
            if (this._teleportTo.relativeToCenter) {
              targetLocation.x -= dimensions.width / 2;
              targetLocation.y -= dimensions.height / 2;
              if (this._snapToGrid) {
                targetLocation.x -= 0.01;
                targetLocation.y -= 0.01;
              }
            }
            if (this._snapToGrid) {
              targetLocation.x -= dimensions.width / 2;
              targetLocation.y -= dimensions.height / 2;
              targetLocation = canvas.grid.getSnappedPoint(targetLocation, {
                mode: CONST.GRID_SNAPPING_MODES.VERTEX,
                resolution: 1
              });
            }
            await this._updateObject(this._originObject, targetLocation, false);
          }, this._teleportTo.delay);
          if (overallDuration <= this._teleportTo.delay) {
            this._waitUntilFinished = true;
          }
          overallDuration = overallDuration > this._teleportTo.delay ? overallDuration : this._teleportTo.delay;
        }
        let updateAttributes = {};
        if (is_real_number(this._angle) && !this._rotateIn && !this._rotateOut) {
          updateAttributes["rotation"] = this._angle;
        }
        if (is_real_number(this._opacity) && !this._fadeIn && !this._fadeOut) {
          updateAttributes["alpha"] = this._opacity;
        }
        if (is_real_number(this._volume) && !this._fadeInAudio && !this._fadeOutAudio && this._originObject?.video?.volume !== void 0) {
          updateAttributes["video.volume"] = this._volume;
        }
        if (this._tint) {
          updateAttributes[CONSTANTS.IS_V12 ? "texture.tint" : "tint"] = this._tint.hexadecimal;
        }
        if (this._hide !== void 0) {
          updateAttributes["hidden"] = this._hide;
        }
        if (Object.keys(updateAttributes).length) {
          await wait$1(1);
          await this._updateObject(this._originObject, updateAttributes);
          await wait$1(1);
        }
        return new Promise(async (resolve) => {
          this._animate(animData, resolve);
          setTimeout(
            resolve,
            Math.max(0, overallDuration + this._currentWaitTime + animData.maxFPS)
          );
        });
      }
      /**
       * @private
       */
      async _animate(animData, resolve, timespan) {
        if (timespan) {
          let animatedAttributes = {};
          let dt = timespan - animData.lastTimespan;
          if (dt >= animData.maxFPS) {
            animData.totalDt += dt;
            for (let attribute of animData.attributes) {
              if (attribute.done) continue;
              if (animData.totalDt < attribute.delay) continue;
              if (attribute.name === "position") {
                attribute.speed = attribute.originalDistance / (attribute.duration / dt);
                attribute.currentDistance += attribute.speed;
                attribute.progress = attribute.currentDistance / attribute.originalDistance;
                let x = interpolate(
                  attribute.origin.x,
                  attribute.target.x,
                  attribute.progress,
                  attribute.ease
                );
                let y = interpolate(
                  attribute.origin.y,
                  attribute.target.y,
                  attribute.progress,
                  attribute.ease
                );
                if (attribute.currentDistance >= attribute.originalDistance) {
                  x = attribute.target.x;
                  y = attribute.target.y;
                  attribute.done = true;
                }
                animatedAttributes["x"] = x;
                animatedAttributes["y"] = y;
              } else {
                if (attribute.name === "rotationTowards" && !attribute.from && !attribute.to) {
                  let target = attribute.target;
                  if (this._rotateTowards.towardsCenter)
                    target = target?.center ?? target;
                  let ray = new Ray(attribute.originLocation, target);
                  let angle = ray.angle * 180 / Math.PI - 90;
                  angle += attribute.offset;
                  attribute.from = attribute.origin.rotation;
                  attribute.to = angle;
                  if (Math.abs(attribute.from - attribute.to) > 180) {
                    if (attribute.to < 0) {
                      attribute.to += 360;
                    } else if (attribute.from > attribute.to) {
                      attribute.from -= 360;
                    }
                  }
                  attribute.name = "rotation";
                }
                attribute.durationDone += dt;
                attribute.progress = attribute.durationDone / attribute.duration;
                let val = interpolate(
                  attribute.from,
                  attribute.to,
                  attribute.progress,
                  attribute.ease
                );
                if (attribute.progress >= 1) {
                  val = attribute.to;
                  attribute.done = true;
                }
                animatedAttributes[attribute.name] = val;
              }
            }
            if (Object.keys(animatedAttributes).length > 0) {
              await this._updateObject(this._originObject, animatedAttributes);
            }
            animData.attributes = animData.attributes.filter((a) => !a.done);
            if (animData.attributes.length === 0) return;
            animData.lastTimespan = timespan;
          }
        }
        let self = this;
        requestAnimationFrame(function(timespan2) {
          self._animate(animData, resolve, timespan2);
        });
      }
    }
    class ScrollingTextSection extends Section {
      constructor(inSequence, target, text2, textOptions) {
        super(inSequence);
        this._deserializedData = null;
        this._source = null;
        this._text = "";
        this._duration = 2e3;
        this._distance = null;
        this._jitter = 0;
        this._anchor = null;
        this._direction = null;
        this._seed = get_hash(foundry.utils.randomID());
        if (target) {
          this.atLocation(target);
        }
        if (text2) {
          this.text(text2, textOptions);
        }
      }
      static niceName = "Scrolling Text";
      /**
       * @private
       */
      _applyTraits() {
        Object.assign(this.constructor.prototype, traits.users);
        Object.assign(this.constructor.prototype, traits.location);
        Object.assign(this.constructor.prototype, traits.offset);
        Object.assign(this.constructor.prototype, traits.text);
      }
      direction(inDirection) {
        if (!(typeof inDirection === "string" || is_real_number(inDirection))) {
          throw this.sequence._customError(
            this,
            "direction",
            "inDirection must be of type string (CONST.TEXT_ANCHOR_POINTS) or number"
          );
        }
        if (typeof inDirection === "string" && !CONST.TEXT_ANCHOR_POINTS[inDirection]) {
          throw this.sequence._customError(
            this,
            "direction",
            `${inDirection} does not exist in CONST.TEXT_ANCHOR_POINTS!`
          );
        } else if (typeof inDirection === "string") {
          this._direction = CONST.TEXT_ANCHOR_POINTS[inDirection];
        } else {
          this._direction = inDirection;
        }
        return this;
      }
      anchor(inAnchor) {
        if (!(typeof inAnchor === "string" || is_real_number(inAnchor))) {
          throw this.sequence._customError(
            this,
            "direction",
            "inAnchor must be of type string (CONST.TEXT_ANCHOR_POINTS) or number"
          );
        }
        if (typeof inAnchor === "string" && !CONST.TEXT_ANCHOR_POINTS[inAnchor] || is_real_number(inAnchor) && !Object.values(CONST.TEXT_ANCHOR_POINTS).includes(inAnchor)) {
          throw this.sequence._customError(
            this,
            "direction",
            `${inAnchor} does not exist in CONST.TEXT_ANCHOR_POINTS!`
          );
        } else if (typeof inAnchor === "string") {
          this._anchor = CONST.TEXT_ANCHOR_POINTS[inAnchor];
        } else {
          this._anchor = inAnchor;
        }
        return this;
      }
      jitter(inJitter) {
        if (!(is_real_number(inJitter) && inJitter >= 0 && inJitter <= 1)) {
          throw this.sequence._customError(
            this,
            "jitter",
            "inJitter must be of type number between 0 and 1"
          );
        }
        this._jitter = inJitter;
        return this;
      }
      async preRun() {
        const oldSource = this._source;
        const crosshairSource = this.sequence?.crosshairs?.[this._source];
        if (typeof this._source === "string" && crosshairSource) {
          this._source = crosshairSource.uuid;
        }
        if (this._name && oldSource !== this._source) {
          this.sequence.nameOffsetMap[this._name].source = this._getSourceObject();
        }
      }
      async run() {
        const data = await this._sanitizeTextData();
        if (Hooks.call("preCreateScrollingText", data) === false) return;
        const push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
        const duration = SequencerFoundryReplicator.playScrollingText(data, push);
        await new Promise(
          (resolve) => setTimeout(resolve, this._currentWaitTime + duration)
        );
      }
      _getSourceObject() {
        if (!this._source || typeof this._source !== "object") return this._source;
        if (this._source instanceof CrosshairsPlaceable || this._source instanceof CrosshairsDocument) {
          const doc = this._source?.document ?? this._source;
          return doc.getOrientation().source;
        }
        return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
      }
      async _sanitizeTextData() {
        if (this._deserializedData) {
          return this._deserializedData;
        }
        return {
          sceneId: game.user.viewedScene,
          seed: this._seed,
          sequenceId: this.sequence.id,
          creatorUserId: game.userId,
          users: this._users ? Array.from(this._users) : false,
          moduleName: this.sequence.moduleName,
          source: this._getSourceObject(),
          offset: this._offset?.source ?? false,
          randomOffset: this._randomOffset?.source ?? false,
          content: this._text?.text ?? "",
          nameOffsetMap: this.sequence.nameOffsetMap,
          options: {
            anchor: this._anchor,
            direction: this._direction,
            duration: this._duration,
            distance: this._distance,
            jitter: this._jitter,
            ...this._text
          }
        };
      }
      async _serialize() {
        const data = await super._serialize();
        const sectionData = await this._sanitizeTextData();
        return {
          ...data,
          type: "scrollingText",
          sectionData
        };
      }
      async _deserialize(data) {
        this._deserializedData = data.sectionData;
        return super._deserialize(data);
      }
    }
    class CanvasPanSection extends Section {
      constructor(inSequence, target, duration, scale2) {
        super(inSequence);
        this._deserializedData = null;
        this._source = null;
        this._duration = duration ?? 250;
        this._speed = null;
        this._scale = scale2 ?? 1;
        this._lockView = null;
        this._shake = null;
        this._seed = get_hash(foundry.utils.randomID());
        if (target) {
          this.atLocation(target);
        }
      }
      static niceName = "Canvas Pan";
      /**
       * @private
       */
      _applyTraits() {
        Object.assign(this.constructor.prototype, traits.users);
        Object.assign(this.constructor.prototype, traits.location);
        Object.assign(this.constructor.prototype, traits.offset);
      }
      /**
       * Sets the speed (pixels per frame) of the canvas pan
       *
       * @param {number} inSpeed
       * @returns this
       */
      speed(inSpeed) {
        if (!is_real_number(inSpeed))
          throw this.sequence._customError(
            this,
            "speed",
            "inSpeed must be of type number"
          );
        if (inSpeed < 0) {
          throw this.sequence._customError(
            this,
            "speed",
            "inSpeed must be greater or equal to 0"
          );
        }
        this._speed = inSpeed;
        return this;
      }
      /**
       * Sets the zoom level of the canvas pan
       *
       * @param {number} inScale
       * @returns this
       */
      scale(inScale) {
        if (!is_real_number(inScale))
          throw this.sequence._customError(
            this,
            "scale",
            "inScale must be of type number"
          );
        if (inScale <= 0) {
          throw this.sequence._customError(
            this,
            "scale",
            "inScale must be greater than 0"
          );
        }
        this._scale = inScale;
        return this;
      }
      /**
       * Locks the canvas at the given location for the given duration
       *
       * @param {number} inDuration
       * @returns this
       */
      lockView(inDuration) {
        if (!is_real_number(inDuration))
          throw this.sequence._customError(
            this,
            "lockView",
            "inDuration must be of type number"
          );
        if (inDuration < 0) {
          throw this.sequence._customError(
            this,
            "lockView",
            "inDuration must be greater or equal to 0"
          );
        }
        this._lockView = inDuration;
        return this;
      }
      /**
       * Shakes the canvas
       *
       * @param {number} duration
       * @param {number} strength
       * @param {number} frequency
       * @param {number} fadeInDuration
       * @param {number} fadeOutDuration
       * @param {boolean} rotation
       * @returns this
       */
      shake({
        duration = 250,
        strength = 20,
        frequency = 10,
        fadeInDuration = 0,
        fadeOutDuration = 200,
        rotation: rotation2 = true
      } = {}) {
        if (!is_real_number(duration)) {
          throw this.sequence._customError(
            this,
            "shake",
            "duration must be of type number"
          );
        }
        if (!is_real_number(strength)) {
          throw this.sequence._customError(
            this,
            "shake",
            "strength must be of type number"
          );
        }
        if (!is_real_number(strength)) {
          throw this.sequence._customError(
            this,
            "shake",
            "frequency must be of type number"
          );
        }
        if (!is_real_number(fadeInDuration)) {
          throw this.sequence._customError(
            this,
            "shake",
            "fadeInDuration must be of type number"
          );
        }
        if (!is_real_number(fadeOutDuration)) {
          throw this.sequence._customError(
            this,
            "shake",
            "fadeOutDuration must be of type number"
          );
        }
        if (typeof rotation2 !== "boolean") {
          throw this.sequence._customError(
            this,
            "shake",
            "rotation must be of type boolean"
          );
        }
        this._shake = {
          duration,
          strength,
          frequency,
          fadeInDuration,
          fadeOutDuration,
          rotation: rotation2
        };
        return this;
      }
      async preRun() {
        const oldSource = this._source;
        const crosshairSource = this.sequence?.crosshairs?.[this._source];
        if (typeof this._source === "string" && crosshairSource) {
          this._source = crosshairSource.uuid;
        }
        if (this._name && oldSource !== this._source) {
          this.sequence.nameOffsetMap[this._name].source = this._getSourceObject();
        }
      }
      async run() {
        const data = await this._sanitizeData();
        if (Hooks.call("preCanvasPan", data) === false) return;
        const push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
        const duration = SequencerFoundryReplicator.panCanvas(data, push);
        await new Promise(
          (resolve) => setTimeout(resolve, this._currentWaitTime + duration)
        );
      }
      _getSourceObject() {
        if (!this._source || typeof this._source !== "object") return this._source;
        if (this._source instanceof CrosshairsPlaceable || this._source instanceof CrosshairsDocument) {
          const doc = this._source?.document ?? this._source;
          return doc.getOrientation().source;
        }
        return get_object_identifier(this._source) ?? get_object_canvas_data(this._source, { uuid: false });
      }
      async _sanitizeData() {
        if (this._deserializedData) {
          return this._deserializedData;
        }
        return {
          sceneId: game.user.viewedScene,
          seed: this._seed,
          sequenceId: this.sequence.id,
          creatorUserId: game.userId,
          users: this._users ? Array.from(this._users) : false,
          moduleName: this.sequence.moduleName,
          source: this._getSourceObject(),
          offset: this._offset?.source ?? false,
          randomOffset: this._randomOffset?.source ?? false,
          duration: this._duration,
          speed: this._speed,
          scale: this._scale,
          lockView: this._lockView,
          shake: this._shake,
          nameOffsetMap: this.sequence.nameOffsetMap
        };
      }
      async _serialize() {
        const data = await super._serialize();
        const sectionData = await this._sanitizeData();
        return {
          ...data,
          type: "canvasPan",
          sectionData
        };
      }
      async _deserialize(data) {
        this._deserializedData = data.sectionData;
        return super._deserialize(data);
      }
    }
    class WaitSection extends Section {
      constructor(inSequence, msMin, msMax) {
        super(inSequence);
        this._waitUntilFinished = true;
        this._waitDuration = random_int_between(msMin, Math.max(msMin, msMax));
      }
      static niceName = "Wait";
      /**
       * @returns {Promise<void>}
       */
      async run() {
        debug("Running wait");
        await new Promise(async (resolve) => {
          setTimeout(resolve, this._waitDuration);
        });
      }
      /**
       * @returns {Promise}
       * @private
       */
      async _execute() {
        await this.run();
      }
      async _serialize() {
        const data = await super._serialize();
        return {
          ...data,
          type: "wait",
          sectionData: {
            waitDuration: this._waitDuration
          }
        };
      }
      async _deserialize(data) {
        this._waitDuration = data.sectionData.waitDuration;
        return super._deserialize(data);
      }
    }
    class CrosshairSection extends Section {
      constructor(inSequence, inName = "") {
        super(inSequence);
        this._name = inName;
        this._type = CONST.MEASURED_TEMPLATE_TYPES.CIRCLE;
        this._distance = canvas.grid.distance / 2;
        this._angle = 53.13;
        this._width = canvas.grid.distance;
        this._direction = 0;
        this._borderColor = null;
        this._fillColor = null;
        this._persist = false;
        this._config = CrosshairsDocument.defaultConfig;
        this._waitUntilFinished = true;
        this._callbacks = {};
      }
      static niceName = "Crosshair";
      /**
       * Sets the type of MeasurableTemplate to create, see CONST.MEASURED_TEMPLATE_TYPES
       */
      type(inType = "") {
        if (typeof inType !== "string") {
          throw this.sequence._customError(this, "type", "inType must be of type string");
        }
        if (!Object.values(CONST.MEASURED_TEMPLATE_TYPES).includes(inType)) {
          throw this.sequence._customError(this, "type", `inType must be one of valid types: ${Object.values(CONST.MEASURED_TEMPLATE_TYPES).join(" ")} (see CONST.MEASURED_TEMPLATE_TYPES)`);
        }
        this._type = inType;
        return this;
      }
      /**
       * Sets a custom label to be set as a part of this crosshair
       */
      label(inText, inOptions = {}) {
        if (typeof inOptions !== "object") {
          throw this.sequence._customError(this, "label", "inOptions must be of type object");
        }
        inOptions = foundry.utils.mergeObject({
          dx: CrosshairsDocument.defaultConfig.label.dx,
          dy: CrosshairsDocument.defaultConfig.label.dy
        }, inOptions);
        if (typeof inText !== "string") {
          throw this.sequence._customError(this, "label", "inText must be of type string");
        }
        if (!is_real_number(inOptions.dx)) {
          throw this.sequence._customError(this, "label", "inOptions.dx must be of type number");
        }
        if (!is_real_number(inOptions.dy)) {
          throw this.sequence._customError(this, "label", "inOptions.dy must be of type number");
        }
        this._config["label"]["text"] = inText;
        this._config["label"]["dx"] = inOptions.dx;
        this._config["label"]["dy"] = inOptions.dy;
        return this;
      }
      /**
       * Sets how the position of the crosshair should snap on the grid
       */
      snapPosition(inSnap) {
        if (!is_real_number(inSnap)) {
          throw this.sequence._customError(this, "snapPosition", "inSnap must be of type number, see CONST.GRID_SNAPPING_MODES");
        }
        this._config["snap"]["position"] = inSnap;
        return this;
      }
      /**
       * Sets the distance for the crosshair - or radius for a circular crosshair
       */
      distance(inDistance, inOptions = {}) {
        if (typeof inOptions !== "object") {
          throw this.sequence._customError(this, "distance", "inOptions must be of type object");
        }
        inOptions = foundry.utils.mergeObject({
          min: CrosshairsDocument.defaultConfig.distanceMin,
          max: CrosshairsDocument.defaultConfig.distanceMax
        }, inOptions);
        if (!is_real_number(inDistance)) {
          throw this.sequence._customError(this, "distance", "inDistance must be of type number");
        }
        if (inOptions.min !== null) {
          if (!is_real_number(inOptions.min)) {
            throw this.sequence._customError(this, "distance", "inOptions.min must be of type number");
          }
          if (inOptions.min <= 0) {
            throw this.sequence._customError(this, "distance", "inOptions.min must be a non-zero number");
          }
        } else {
          inOptions.min = inDistance;
        }
        if (inOptions.max !== null) {
          if (!is_real_number(inOptions.max)) {
            throw this.sequence._customError(this, "distance", "inOptions.max must be of type number");
          }
          if (inOptions.max <= 0) {
            throw this.sequence._customError(this, "distance", "inOptions.max must be a non-zero number");
          }
        } else {
          inOptions.max = inDistance;
        }
        if (inOptions.min > inOptions.max) {
          throw this.sequence._customError(this, "distance", "inOptions.min cannot be larger than inOptions.max");
        }
        this._distance = inDistance;
        this._config["distanceMin"] = inOptions.min;
        this._config["distanceMax"] = inOptions.max;
        return this;
      }
      /**
       * Configures the angle of the crosshair - mostly used for the width of cone crosshairs
       */
      angle(inAngle) {
        if (!is_real_number(inAngle)) {
          throw this.sequence._customError(this, "angle", "inAngle must be of type number");
        }
        this._angle = inAngle;
        return this;
      }
      /**
       * Configures the width for the crosshair - used for ray crosshairs
       */
      width(inWidth) {
        if (!is_real_number(inWidth)) {
          throw this.sequence._customError(this, "width", "inWidth must be of type number");
        }
        this._width = inWidth;
        return this;
      }
      /**
       * Configures the width for the crosshair - used for ray crosshairs
       */
      direction(inDirection) {
        if (!is_real_number(inDirection)) {
          throw this.sequence._customError(this, "direction", "inDirection must be of type number");
        }
        this._direction = inDirection;
        return this;
      }
      /**
       * Configures the  increments the direction should snap along
       */
      snapDirection(inSnapDirection) {
        if (!is_real_number(inSnapDirection)) {
          throw this.sequence._customError(this, "snapDirection", "inDirectionSnap must be of type number");
        }
        this._config["snap"]["direction"] = inSnapDirection;
        return this;
      }
      /**
       * Toggles whether the crosshair can be manually rotated
       */
      lockManualRotation(inBool = true) {
        if (typeof inBool !== "boolean") {
          throw this.sequence._customError(this, "preventUserRotation", "inBool must be of type number");
        }
        this._config["lockManualRotation"] = inBool;
        return this;
      }
      /**
       * Toggles whether the crosshair's end position can be dragged
       * @param inBool
       */
      lockDrag(inBool = true) {
        if (typeof inBool !== "boolean") {
          throw this.sequence._customError(this, "lockDrag", "inBool must be of type number");
        }
        this._config["lockDrag"] = inBool;
        return this;
      }
      /**
       * Configures the custom icon to be used on the crosshair
       */
      icon(inTexture, inOptions = {}) {
        if (typeof inOptions !== "object") {
          throw this.sequence._customError(this, "icon", "inOptions must be of type object");
        }
        inOptions = foundry.utils.mergeObject({ borderVisible: CrosshairsDocument.defaultConfig.icon.borderVisible }, inOptions);
        if (typeof inTexture !== "string") {
          throw this.sequence._customError(this, "icon", "inTexture must be of type string");
        }
        if (typeof inOptions.borderVisible !== "boolean") {
          throw this.sequence._customError(this, "icon", "inOptions.borderVisible must be of type boolean");
        }
        this._config["icon"]["texture"] = inTexture;
        this._config["icon"]["borderVisible"] = inOptions.borderVisible;
        return this;
      }
      /**
       * Sets the border color of the crosshair
       */
      borderColor(inColor) {
        this._borderColor = parseColor(inColor);
        return this;
      }
      /**
       * Sets the fill color of the crosshair
       */
      fillColor(inColor) {
        this._fillColor = parseColor(inColor);
        return this;
      }
      /**
       * Configures the source location of the crosshair, usually to limit it around the target placeable object, or to
       * cause it to be limited within a certain range of the placeable object
       */
      location(inLocation, inOptions = {}) {
        if (!inLocation || typeof inLocation !== "object") {
          throw this.sequence._customError(this, "location", "inLocation is invalid, and must be of type of placeable object, or document");
        }
        if (typeof inOptions !== "object") {
          throw this.sequence._customError(this, "location", "inOptions must be of type object");
        }
        inOptions = foundry.utils.mergeObject({
          limitMinRange: CrosshairsDocument.defaultConfig.location.limitMinRange,
          limitMaxRange: CrosshairsDocument.defaultConfig.location.limitMaxRange,
          showRange: CrosshairsDocument.defaultConfig.location.showRange,
          lockToEdge: CrosshairsDocument.defaultConfig.location.lockToEdge,
          lockToEdgeDirection: CrosshairsDocument.defaultConfig.location.lockToEdgeDirection,
          offset: CrosshairsDocument.defaultConfig.location.offset,
          wallBehavior: CrosshairsDocument.defaultConfig.location.wallBehavior,
          displayRangePoly: CrosshairsDocument.defaultConfig.location.displayRangePoly,
          rangePolyFillColor: CrosshairsDocument.defaultConfig.location.rangePolyFillColor,
          rangePolyLineColor: CrosshairsDocument.defaultConfig.location.rangePolyLineColor,
          rangePolyFillAlpha: CrosshairsDocument.defaultConfig.location.rangePolyFillAlpha,
          rangePolyLineAlpha: CrosshairsDocument.defaultConfig.location.rangePolyLineAlpha
        }, inOptions);
        inLocation = this._validateLocation(inLocation);
        if (typeof inOptions.wallBehavior !== "string") {
          throw this.sequence._customError(this, "location", "inOptions.wallBehavior must be of type string");
        }
        if (inLocation === void 0) {
          throw this.sequence._customError(this, "location", "could not find position of given object");
        }
        if (inOptions.limitMinRange && !is_real_number(inOptions.limitMinRange)) {
          throw this.sequence._customError(this, "location", "inOptions.limitMinRange must be of type number");
        }
        if (inOptions.limitMaxRange && !is_real_number(inOptions.limitMaxRange)) {
          throw this.sequence._customError(this, "location", "inOptions.limitMaxRange must be of type number");
        }
        if (typeof inOptions.lockToEdge !== "boolean") {
          throw this.sequence._customError(this, "location", "inOptions.lockToEdge must be of type boolean");
        }
        if (typeof inOptions.showRange !== "boolean") {
          throw this.sequence._customError(this, "location", "inOptions.showRange must be of type boolean");
        }
        if (typeof inOptions.lockToEdgeDirection !== "boolean") {
          throw this.sequence._customError(this, "location", "inOptions.lockToEdgeDirection must be of type boolean");
        }
        if (typeof inOptions.offset !== "object") {
          throw this.sequence._customError(this, "location", "inOptions.offset must be of type of object");
        }
        if (!is_real_number(inOptions.offset.x)) {
          throw this.sequence._customError(this, "location", "inOptions.offset.x must be of type of number");
        }
        if (!is_real_number(inOptions.offset.y)) {
          throw this.sequence._customError(this, "location", "inOptions.offset.y must be of type of number");
        }
        if (typeof inOptions.displayRangePoly !== "boolean") {
          throw this.sequence._customError(this, "location", "inOptions.displayRangePoly must be of type boolean");
        }
        if (inOptions.rangePolyFillColor && !is_real_number(inOptions.rangePolyFillColor)) {
          throw this.sequence._customError(this, "location", "inOptions.rangePolyFillColor must be of type number (color, eg 0xFF0000)");
        }
        if (inOptions.rangePolyLineColor && !is_real_number(inOptions.rangePolyLineColor)) {
          throw this.sequence._customError(this, "location", "inOptions.rangePolyLineColor must be of type number (color, eg 0xFF0000)");
        }
        if (inOptions.rangePolyFillAlpha && !is_real_number(inOptions.rangePolyFillAlpha)) {
          throw this.sequence._customError(this, "location", "inOptions.rangePolyFillAlpha must be of type number");
        }
        if (inOptions.rangePolyLineAlpha && !is_real_number(inOptions.rangePolyLineAlpha)) {
          throw this.sequence._customError(this, "location", "inOptions.rangePolyLineAlpha must be of type number");
        }
        this._config["location"] = {
          obj: inLocation?.object ?? inLocation,
          limitMinRange: inOptions.limitMinRange,
          limitMaxRange: inOptions.limitMaxRange,
          lockToEdge: inOptions.lockToEdge,
          lockToEdgeDirection: inOptions.lockToEdgeDirection,
          showRange: inOptions.showRange,
          offset: inOptions.offset,
          wallBehavior: inOptions.wallBehavior,
          displayRangePoly: inOptions.displayRangePoly,
          rangePolyFillColor: inOptions.rangePolyFillColor,
          rangePolyLineColor: inOptions.rangePolyLineColor,
          rangePolyFillAlpha: inOptions.rangePolyFillAlpha,
          rangePolyLineAlpha: inOptions.rangePolyLineAlpha
        };
        return this;
      }
      /**
       * Causes the crosshair to spawn a measurable template identical to the crosshair
       */
      persist(inBool = true) {
        if (typeof inBool !== "boolean") {
          throw this.sequence._customError(this, "persist", "inBool must be of type boolean");
        }
        this._persist = inBool;
        return this;
      }
      /**
       * Toggles whether this crosshair should highlight the grid
       */
      gridHighlight(inBool = true) {
        if (typeof inBool !== "boolean") {
          throw this.sequence._customError(this, "gridHighlight", "inBool must be of type boolean");
        }
        this._config["gridHighlight"] = inBool;
        return this;
      }
      /**
       * Adds a callback function for certain crosshair events
       */
      callback(inString, inCallback) {
        if (typeof inString !== "string" || !Object.values(CONSTANTS.CALLBACKS).some((str) => str === inString)) {
          throw this.sequence._customError(this, "callback", "inString must be of type string and one of Sequencer.Crosshair.CALLBACKS");
        }
        if (!is_function$1(inCallback)) {
          throw this.sequence._customError(this, "callback", "inCallback must be of type Function");
        }
        this._callbacks[inString] = inCallback;
        return this;
      }
      async run() {
        const reticle = new CrosshairsDocument({
          t: this._type,
          distance: this._distance,
          width: this._width,
          direction: this._direction,
          angle: this._angle,
          borderColor: this._borderColor,
          fillColor: this._fillColor
        }, {
          parent: canvas.scene
        }, this._config, this._callbacks);
        return reticle.show().then(async () => {
          if (this._persist) {
            const [template] = await canvas.scene.createEmbeddedDocuments("MeasuredTemplate", [
              reticle.toObject()
            ]);
            this.sequence.crosshairs[this._name] = template;
          }
          if (this._name) {
            this.sequence.nameOffsetMap ||= {};
            this.sequence.nameOffsetMap[this._name] = {
              seed: `${this._name}-${foundry.utils.randomID()}`,
              twister: {},
              source: this.sequence.crosshairs[this._name]?.uuid ?? reticle.getOrientation().source,
              target: this.sequence.crosshairs[this._name]?.uuid ?? reticle.getOrientation().target
            };
          }
        }).catch((err) => {
          console.log(err);
          this.sequence._abort();
        });
      }
      async _execute() {
        await this.run();
      }
      /**
       * @private
       */
      _applyTraits() {
        Object.assign(this.constructor.prototype, traits.name);
      }
    }
    let Sequence$1 = class Sequence2 {
      constructor(options = {
        moduleName: "Sequencer",
        softFail: false
      }, softFail = false) {
        this.id = foundry.utils.randomID();
        this.moduleName = typeof options === "string" ? options : options?.moduleName ?? "Sequencer";
        this.softFail = options?.softFail ?? softFail;
        this.sections = [];
        this.nameOffsetMap = false;
        this.crosshairs = {};
        this.effectIndex = 0;
        this.sectionToCreate = void 0;
        this.localOnly = false;
        this._status = writable(CONSTANTS.STATUS.READY);
        return sequence_proxy_wrap(this);
      }
      /**
       * Plays all of this sequence's sections
       *
       * @returns {Promise}
       */
      async play({ remote = false, preload = false, local = false } = {}) {
        this.localOnly = local || remote;
        if (remote) {
          const data = await this.toJSON();
          sequencerSocket.executeForOthers(
            SOCKET_HANDLERS.RUN_SEQUENCE_LOCALLY,
            data
          );
          return new Sequence2().fromJSON(data).play();
        }
        Hooks.callAll("createSequencerSequence", this);
        debug("Initializing sections");
        const initializationPromises = this.sections.map((section) => section._initialize());
        await Promise.allSettled(initializationPromises);
        if (preload) {
          const preloadFiles = this.sections.filter((section) => !!section._file).map((section) => section._file);
          if (local) {
            await Sequencer.Preloader.preload(preloadFiles);
          } else {
            await Sequencer.Preloader.preloadForClients(preloadFiles);
          }
        }
        SequenceManager.RunningSequences.add(this.id, this);
        this.effectIndex = 0;
        debug("Playing sections");
        this.status = CONSTANTS.STATUS.RUNNING;
        const promises = [];
        for (let section of this.sections) {
          if (section instanceof EffectSection) this.effectIndex++;
          if (section.shouldWaitUntilFinished) {
            promises.push(await section._execute());
          } else {
            promises.push(section._execute());
          }
          if (this.status === CONSTANTS.STATUS.ABORTED) {
            continue;
          }
          if (!section._isLastSection) {
            await new Promise((resolve) => setTimeout(resolve, 3));
          }
        }
        return Promise.allSettled(promises).then(() => {
          Hooks.callAll("endedSequencerSequence");
          debug("Finished playing sections");
          this.status = CONSTANTS.STATUS.COMPLETE;
        });
      }
      /**
       * Creates a section that will run a function.
       *
       * @param {function} inFunc
       * @returns {Sequence} this
       */
      thenDo(inFunc) {
        const func2 = section_proxy_wrap(new FunctionSection(this, inFunc));
        this.sections.push(func2);
        return func2;
      }
      /**
       * Creates a section that will run a macro based on a name or a direct reference to a macro.
       *
       * @param {string|Macro} inMacro
       * @param {object} args
       * @returns {Sequence} this
       */
      macro(inMacro, ...args) {
        let macro;
        let compendium = false;
        if (typeof inMacro === "string") {
          if (inMacro.startsWith("Compendium")) {
            let packArray = inMacro.split(".");
            let pack = game.packs.get(`${packArray[1]}.${packArray[2]}`);
            if (!pack) {
              if (this.softFail) {
                return this;
              }
              throw custom_error(
                this.moduleName,
                `macro - Compendium '${packArray[1]}.${packArray[2]}' was not found`
              );
            }
            macro = packArray;
            compendium = pack;
          } else {
            macro = game.macros.getName(inMacro);
            if (!macro) {
              if (this.softFail) {
                return this;
              }
              throw custom_error(
                this.moduleName,
                `macro - Macro '${inMacro}' was not found`
              );
            }
          }
        } else if (inMacro instanceof Macro) {
          macro = inMacro;
        } else {
          throw custom_error(
            this.moduleName,
            `macro - inMacro must be of instance string or Macro`
          );
        }
        args = args.length ? args?.[0] : {};
        if (typeof args !== "object") {
          throw custom_error(
            this.moduleName,
            `macro - Secondary argument must be an object`
          );
        }
        const func2 = section_proxy_wrap(
          new FunctionSection(
            this,
            async () => {
              if (compendium) {
                const macroIndex = (await compendium.getIndex()).find((i) => {
                  if (macro[3] === "Macro") {
                    return macro[4] === i._id;
                  }
                  return i.name === macro[3];
                });
                const macroData = macroIndex ? await compendium.getDocument(macroIndex?._id) : false;
                if (!macroData) {
                  if (this.softFail) {
                    return;
                  }
                  throw custom_error(
                    this.moduleName,
                    `macro - Macro '${macro[3]}' was not found in compendium '${macro[1]}.${macro[2]}'`
                  );
                }
                macro = new Macro(macroData);
                macro.ownership.default = CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
              }
              await macro.execute(args);
            },
            true
          )
        );
        this.sections.push(func2);
        return this;
      }
      /**
       * Creates an effect section. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Effect section.
       *
       * @param {string} [inFile] inFile
       * @returns {Section}
       */
      effect(inFile = "") {
        const effect = section_proxy_wrap(new EffectSection(this, inFile));
        this.sections.push(effect);
        return effect;
      }
      /**
       * Creates a sound section. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Sound section.
       *
       * @param {string} [inFile] inFile
       * @returns {Section}
       */
      sound(inFile = "") {
        const sound = section_proxy_wrap(new SoundSection(this, inFile));
        this.sections.push(sound);
        return sound;
      }
      /**
       * Creates an animation. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Animation section.
       *
       * @param {Token|Tile} [inTarget=false] inTarget
       * @returns {AnimationSection}
       */
      animation(inTarget) {
        const animation2 = section_proxy_wrap(
          new AnimationSection(this, inTarget)
        );
        this.sections.push(animation2);
        return animation2;
      }
      /**
       * Creates a scrolling text. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Scrolling Text section.
       *
       * @param {Object|String|Boolean} [inTarget=false] inTarget
       * @param {String|Boolean} [inText=false] inText
       * @param {Object} [inTextOptions={}] inTextOptions
       * @returns {AnimationSection}
       */
      scrollingText(inTarget = false, inText = false, inTextOptions = {}) {
        const scrolling = section_proxy_wrap(
          new ScrollingTextSection(this, inTarget, inText, inTextOptions)
        );
        this.sections.push(scrolling);
        return scrolling;
      }
      /**
       * Pans the canvas text. Until you call any other sections you'll be working on the Canvas Pan section.
       *
       * @param {Object|String|Boolean} [inTarget=false] inTarget
       * @param {Boolean|Number} inDuration
       * @param {Boolean|Number} inSpeed
       * @returns {AnimationSection}
       */
      canvasPan(inTarget = false, inDuration = null, inSpeed = null) {
        const panning = section_proxy_wrap(
          new CanvasPanSection(this, inTarget)
        );
        this.sections.push(panning);
        return panning;
      }
      /**
       * Creates a crosshair section. Until you call other Sequence methods, you will be working on the Crosshair section.
       *
       * @param {string} [inName] inName
       * @returns {Section}
       */
      crosshair(inName = "") {
        const crosshair = section_proxy_wrap(new CrosshairSection(this, inName));
        this.sections.push(crosshair);
        return crosshair;
      }
      /**
       * Adds a location to the sequence and ties it to a string so that it may be used later
       * @param {string} inName
       * @param {object/PlaceableObject/Document} inLocation
       * @returns {Sequence} this
       */
      addNamedLocation(inName, inLocation) {
        if (typeof inName !== "string")
          throw custom_error(this.moduleName, `addNamedLocation - inName must be of type string`);
        if (!(typeof inLocation === "object" || inLocation instanceof PlaceableObject || inLocation instanceof Document))
          throw custom_error(this.moduleName, `addNamedLocation - inLocation must be of type object, PlaceableObject, or Document`);
        if (inLocation instanceof CrosshairsPlaceable) inLocation = inLocation.document;
        this.nameOffsetMap ||= {};
        const sourceLocation = inLocation?.source ?? inLocation?.cachedPosition?.source ?? (inLocation instanceof CrosshairsDocument ? inLocation.getOrientation().source : get_object_canvas_data(inLocation, { uuid: false }));
        const targetLocation = inLocation?.target ?? inLocation?.cachedPosition?.target ?? (inLocation instanceof CrosshairsDocument ? inLocation.getOrientation().target ?? sourceLocation : get_object_canvas_data(inLocation, { measure: true, uuid: false }));
        this.nameOffsetMap[inName] = {
          seed: `${inName}-${foundry.utils.randomID()}`,
          source: sourceLocation,
          target: targetLocation
        };
        return this;
      }
      /**
       * Causes the sequence to wait after the last section for as many milliseconds as you pass to this method. If given
       * a second number, a random wait time between the two given numbers will be generated.
       *
       * @param {number} [msMin=1] minMs
       * @param {number} [msMax=1] maxMs
       * @returns {Sequence} this
       */
      wait(msMin = 1, msMax = 1) {
        if (msMin < 1)
          throw custom_error(
            this.moduleName,
            `wait - Wait ms cannot be less than 1`
          );
        if (msMax < 1)
          throw custom_error(
            this.moduleName,
            `wait - Max wait ms cannot be less than 1`
          );
        const section = section_proxy_wrap(new WaitSection(this, msMin, msMax));
        this.sections.push(section);
        return this;
      }
      /**
       * Applies a preset to the sequence
       *
       * @param {string} presetName
       * @param {*} args
       * @returns {Sequence|FunctionSection|EffectSection|AnimationSection|SoundSection}
       */
      preset(presetName, ...args) {
        if (typeof presetName !== "string") {
          throw this._customError(this, "name", `inName must be of type string`);
        }
        const preset = SequencerPresets.get(presetName);
        if (!preset) {
          custom_warning(
            "Sequencer",
            `preset | Could not find preset with name "${presetName}"`
          );
          return this;
        }
        const lastSection = this.sections[this.sections.length - 1] || this;
        return preset(lastSection, ...args);
      }
      /**
       * Adds the sections from a given Sequence to this Sequence
       *
       * @param {Sequence|FunctionSection|EffectSection|AnimationSection|SoundSection} inSequence
       * @returns {Sequence} this
       */
      addSequence(inSequence) {
        if (inSequence instanceof Section) inSequence = inSequence.sequence;
        if (!(inSequence instanceof Sequence2)) {
          throw custom_error(
            this.moduleName,
            `addSequence - could not find the sequence from the given parameter`
          );
        }
        const newSections = inSequence.sections.map((section) => {
          const newSection = Object.assign(
            Object.create(Object.getPrototypeOf(section)),
            section
          );
          newSection.sequence = this;
          return newSection;
        });
        this.sections = this.sections.concat(newSections);
        return this;
      }
      async toJSON() {
        const data = {
          options: { moduleName: this.moduleName, softFail: this.softFail },
          sections: []
        };
        for (const section of this.sections) {
          const sectionData = await section._serialize();
          if (!sectionData.type) {
            throw new Error(
              `Sequencer | toJson | ${section.constructor.name} does not support serialization!`
            );
          }
          data.sections.push(sectionData);
        }
        return data;
      }
      fromJSON(data) {
        this.moduleName = data.options.moduleName;
        this.softFail = data.options.softFail;
        this.localOnly = true;
        for (const section of data.sections) {
          this[section.type]()._deserialize(section);
        }
        return this;
      }
      _createCustomSection(...args) {
        const func2 = section_proxy_wrap(
          new this.sectionToCreate(this, ...args)
        );
        this.sectionToCreate = void 0;
        this.sections.push(func2);
        return func2;
      }
      _showWarning(self, func2, warning, notify = false) {
        custom_warning(
          this.moduleName,
          `${self.constructor.name.replace("Section", "")} | ${func2} - ${warning}`,
          notify
        );
      }
      _customError(self, func2, error) {
        return custom_error(
          this.moduleName,
          `${self.constructor.name.replace("Section", "")} | ${func2} - ${error}`
        );
      }
      set status(inStatus) {
        this._status.update((currentStatus) => {
          if (currentStatus === CONSTANTS.STATUS.READY || currentStatus === CONSTANTS.STATUS.RUNNING) {
            return inStatus;
          }
          return currentStatus;
        });
      }
      get status() {
        return get_store_value(this._status);
      }
      get statusStore() {
        return this._status;
      }
      _abort() {
        this.status = CONSTANTS.STATUS.ABORTED;
        for (const section of this.sections) {
          section._abortSection();
        }
      }
    };
    class SequencerSectionManager {
      #sections = {};
      get externalSections() {
        return this.#sections;
      }
      /**
       * Registers a class by a name that will then be available through the Sequencer
       *
       * @param {String}      inModuleName
       * @param {String}      inMethodName
       * @param {Class}       inClass
       * @param {Boolean}     overwrite
       * @returns {Boolean}               Whether the registration succeeded
       */
      registerSection(inModuleName, inMethodName, inClass, overwrite = false) {
        if (!(inClass.prototype instanceof Section)) {
          throw custom_error(
            inModuleName,
            `inClass must be instance of Sequencer.BaseSection`
          );
        }
        let coreMethods = Object.getOwnPropertyNames(Sequence.prototype).filter(
          (method) => {
            return !method.startsWith("_") && method !== "constructor";
          }
        );
        if (coreMethods.includes(inMethodName)) {
          throw custom_error(
            inModuleName,
            `${inMethodName} is an existing protected method of the Sequence class - please register with another method name!`
          );
        }
        if (this.#sections[inMethodName] && !overwrite) {
          throw custom_error(
            inModuleName,
            `${inMethodName} is already a registered Section with the class ${this.#sections[inMethodName].constructor.name}`
          );
        }
        coreMethods = coreMethods.concat(Object.keys(this.#sections));
        const clashingMethods = Object.getOwnPropertyNames(
          inClass.prototype
        ).filter((method) => coreMethods.includes(method));
        if (clashingMethods.length) {
          let errors = clashingMethods.join(", ");
          throw custom_error(
            inModuleName,
            `${inMethodName} cannot contain the following methods: ${errors}<br>These methods are existing methods on the Sequence or from already registered Sections. Please rename these methods to avoid conflicts.`
          );
        }
        debug(
          `SectionManager | Successfully registered ${inMethodName} with Sequencer!`
        );
        this.#sections[inMethodName] = inClass;
        return true;
      }
    }
    const sectionManager = new SequencerSectionManager();
    let libWrapper = void 0;
    const TGT_SPLIT_RE = new RegExp(
      `([^.[]+|\\[('([^'\\\\]|\\\\.)+?'|"([^"\\\\]|\\\\.)+?")\\])`,
      "g"
    );
    const TGT_CLEANUP_RE = new RegExp(`(^\\['|'\\]$|^\\["|"\\]$)`, "g");
    Hooks.once("init", () => {
      if (globalThis.libWrapper && !(globalThis.libWrapper.is_fallback ?? true)) {
        libWrapper = globalThis.libWrapper;
        return;
      }
      libWrapper = class {
        static get is_fallback() {
          return true;
        }
        static get WRAPPER() {
          return "WRAPPER";
        }
        static get MIXED() {
          return "MIXED";
        }
        static get OVERRIDE() {
          return "OVERRIDE";
        }
        static register(package_id, target, fn, type = "MIXED", { chain = void 0, bind: bind2 = [] } = {}) {
          const is_setter = target.endsWith("#set");
          target = !is_setter ? target : target.slice(0, -4);
          const split = target.match(TGT_SPLIT_RE).map((x) => x.replace(/\\(.)/g, "$1").replace(TGT_CLEANUP_RE, ""));
          const root_nm = split.splice(0, 1)[0];
          let obj, fn_name;
          if (split.length == 0) {
            obj = globalThis;
            fn_name = root_nm;
          } else {
            const _eval = eval;
            fn_name = split.pop();
            obj = split.reduce(
              (x, y) => x[y],
              globalThis[root_nm] ?? _eval(root_nm)
            );
          }
          let iObj = obj;
          let descriptor = null;
          while (iObj) {
            descriptor = Object.getOwnPropertyDescriptor(iObj, fn_name);
            if (descriptor) break;
            iObj = Object.getPrototypeOf(iObj);
          }
          if (!descriptor || descriptor?.configurable === false)
            throw new Error(
              `libWrapper Shim: '${target}' does not exist, could not be found, or has a non-configurable descriptor.`
            );
          let original = null;
          const wrapper = chain ?? (type.toUpperCase?.() != "OVERRIDE" && type != 3) ? function(...args) {
            return fn.call(this, original.bind(this), ...bind2, ...args);
          } : function(...args) {
            return fn.call(this, ...bind2, ...args);
          };
          if (!is_setter) {
            if (descriptor.value) {
              original = descriptor.value;
              descriptor.value = wrapper;
            } else {
              original = descriptor.get;
              descriptor.get = wrapper;
            }
          } else {
            if (!descriptor.set)
              throw new Error(
                `libWrapper Shim: '${target}' does not have a setter`
              );
            original = descriptor.set;
            descriptor.set = wrapper;
          }
          descriptor.configurable = true;
          Object.defineProperty(obj, fn_name, descriptor);
        }
      };
    });
    function registerLibwrappers() {
      libWrapper.register(CONSTANTS.MODULE_NAME, "PIXI.BaseImageResource.prototype.upload", PIXIUPLOAD);
    }
    function PIXIUPLOAD(wrapped, ...args) {
      let baseTexture = args[1];
      if (baseTexture.sequencer_patched || !game.settings.get(CONSTANTS.MODULE_NAME, "enable-global-fix-pixi")) {
        return wrapped(...args);
      }
      let source = args[3];
      source = source || this.source;
      const isVideo = !!source.videoWidth;
      if (isVideo) {
        baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
        baseTexture.sequencer_patched = true;
      }
      return wrapped(...args);
    }
    async function runMigrations() {
      const sortedMigrations = Object.entries(migrations).sort((a, b) => {
        return foundry.utils.isNewerVersion(b[0], a[0]) ? -1 : 1;
      });
      for (const [version, migration] of sortedMigrations) {
        try {
          await migration(version);
        } catch (err) {
          custom_warning(
            "Sequencer",
            `Something went wrong when migrating to version ${version}. Please check the console for the error!`,
            true
          );
          console.error(err);
        }
      }
    }
    function getSequencerEffectTokens(version, tokenFilter = false) {
      return Array.from(game.scenes).map((scene) => [
        scene,
        Array.from(scene.tokens).filter((token) => token.isOwner).filter((token, index) => {
          if (tokenFilter) {
            return tokenFilter(token, index);
          }
          const effects = foundry.utils.getProperty(token, CONSTANTS.EFFECTS_FLAG) ?? [];
          const effectsOutOfDate = effects.filter(
            (e) => foundry.utils.isNewerVersion(version, e[1].flagVersion)
          );
          return effectsOutOfDate.length;
        })
      ]).filter(([_, tokens]) => tokens.length);
    }
    function getSequencerEffectActors(version, actorFilter = false) {
      return Array.from(game.actors).filter((actor) => actor.isOwner).filter((actor, index) => {
        if (actorFilter) {
          return actorFilter(actor, index);
        }
        const effects = foundry.utils.getProperty(actor, CONSTANTS.EFFECTS_FLAG) ?? [];
        const effectsOutOfDate = effects.filter(
          (e) => foundry.utils.isNewerVersion(version, e[1].flagVersion)
        );
        return effectsOutOfDate.length;
      });
    }
    const migrations = {
      "3.0.0": async (version) => {
        const actorsToUpdate = getSequencerEffectActors(version, (actor) => {
          const effects = foundry.utils.getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
          const effectsOutOfDate = effects.filter(
            (e) => foundry.utils.isNewerVersion(version, e[1].flagVersion)
          );
          return effectsOutOfDate.length;
        });
        const actorUpdateArray = actorsToUpdate.map((actor) => {
          const effectsToMoveFromTokenPrototype = foundry.utils.getProperty(
            actor.prototypeToken,
            CONSTANTS.EFFECTS_FLAG
          ).filter(([_, effect]) => {
            return effect.persistOptions?.persistTokenPrototype;
          }).map(([id, effect]) => {
            effect.flagVersion = version;
            return [id, effect];
          });
          const effectsToKeepOnTokenPrototype = foundry.utils.getProperty(
            actor.prototypeToken,
            CONSTANTS.EFFECTS_FLAG
          ).filter(([_, effect]) => {
            return !effect.persistOptions?.persistTokenPrototype;
          }).map(([id, effect]) => {
            effect.flagVersion = version;
            return [id, effect];
          });
          return {
            _id: actor.id,
            [CONSTANTS.EFFECTS_FLAG]: effectsToMoveFromTokenPrototype,
            ["prototypeToken." + CONSTANTS.EFFECTS_FLAG]: effectsToKeepOnTokenPrototype
          };
        });
        const tokensOnScenes = getSequencerEffectTokens(version, (t) => {
          let actor;
          try {
            actor = t.actor;
          } catch (err) {
            return false;
          }
          const effects = foundry.utils.getProperty(t, CONSTANTS.EFFECTS_FLAG) ?? [];
          const prototypeTokenEffects = foundry.utils.getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
          const effectsOutOfDate = effects.filter(
            (e) => foundry.utils.isNewerVersion(version, e[1].flagVersion)
          );
          const prototypeEffectsOutOfDate = prototypeTokenEffects.filter(
            (e) => foundry.utils.isNewerVersion(version, e[1].flagVersion)
          );
          return t.actorLink && (effectsOutOfDate.length || prototypeEffectsOutOfDate.length);
        });
        for (const [scene, tokens] of tokensOnScenes) {
          const updates = [];
          for (const token of tokens) {
            const effectsToKeepOnToken = foundry.utils.getProperty(token, CONSTANTS.EFFECTS_FLAG).filter(([_, effect]) => {
              return !effect.persistOptions?.persistTokenPrototype;
            }).map(([id, effect]) => {
              effect.flagVersion = version;
              return [id, effect];
            });
            updates.push({
              _id: token.id,
              [CONSTANTS.EFFECTS_FLAG]: effectsToKeepOnToken
            });
          }
          if (updates.length) {
            debug(
              `Sequencer | Updated ${updates.length} tokens' effects on scene ${scene.id} to version ${version}`
            );
            await scene.updateEmbeddedDocuments("Token", updates);
          }
        }
        if (actorUpdateArray.length) {
          debug(
            `Sequencer | Updated ${actorUpdateArray.length} actors' effects to version ${version}`
          );
          await Actor.updateDocuments(actorUpdateArray);
        }
      },
      "3.4.4": async (version) => {
        const actorsToUpdate = getSequencerEffectActors(version, (actor) => {
          const effects = foundry.utils.getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
          const effectsOutOfDate = effects.filter(
            (e) => foundry.utils.isNewerVersion(version, e[1].flagVersion)
          );
          return effectsOutOfDate.length;
        });
        const actorUpdateArray = actorsToUpdate.map((actor) => {
          const actorEffects = foundry.utils.getProperty(actor, CONSTANTS.EFFECTS_FLAG) ?? [];
          const prototypeTokenEffects = foundry.utils.getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
          return {
            "_id": actor.id,
            ["prototypeToken." + CONSTANTS.REMOVE_EFFECTS_FLAG]: null,
            [CONSTANTS.EFFECTS_FLAG]: actorEffects.concat(prototypeTokenEffects)
          };
        });
        if (actorUpdateArray.length) {
          debug(
            `Sequencer | Updated ${actorUpdateArray.length} actors' effects to version ${version}`
          );
          await Actor.updateDocuments(actorUpdateArray);
        }
      }
    };
    class TokenCrosshairsDocument extends CrosshairsDocument {
      /**
       * @inheritdoc
       */
      constructor(tokenPlaceable, context = {}, crosshair = {}, callbacks = {}) {
        const tokenDoc = tokenPlaceable.document;
        const { distance, size } = context.parent.grid;
        const rayBounds = new Ray(
          { x: 0, y: 0 },
          { x: tokenDoc.width * distance, y: tokenDoc.height * distance }
        );
        const initial = {
          t: "rect",
          "icon.display": false,
          direction: Math.toDegrees(rayBounds.angle),
          distance: rayBounds.distance
        };
        const defaultCrosshair = foundry.utils.mergeObject(
          TokenCrosshairsDocument.defaultConfig,
          {
            distanceMinMax: {
              min: rayBounds.distance,
              max: rayBounds.distance
            },
            location: {
              offset: {
                x: tokenDoc.width * size / -2,
                y: tokenDoc.height * size / -2
              }
            },
            token: tokenPlaceable
          }
        );
        super(initial, context, foundry.utils.mergeObject(defaultCrosshair, crosshair), callbacks);
      }
      static get defaultConfig() {
        return foundry.utils.mergeObject(super.defaultConfig, {
          gridHighlight: false,
          snap: {
            position: CONST.GRID_SNAPPING_MODES.VERTEX
          },
          label: {
            display: false
          },
          lockDrag: true,
          alpha: 0.5
        });
      }
      static get placeableClass() {
        return TokenCrosshair;
      }
    }
    class TokenCrosshair extends CrosshairsPlaceable {
      #clonedToken = false;
      #tokenRotation = 0;
      /**
       * @override
       */
      _refreshTemplate() {
        this.template.clear();
        this.#clonedToken.document.x = this.x;
        this.#clonedToken.document.y = this.y;
        this.#clonedToken.document.rotation = this.#tokenRotation;
        this.#clonedToken.document.alpha = this.crosshair.alpha;
        this.#clonedToken.renderFlags.set({ refreshPosition: true, refreshRotation: true, refreshMesh: true });
        this.#clonedToken.initializeSources();
      }
      /**
       * @override
       */
      async draw() {
        this.#clonedToken = this.crosshair.token.clone();
        this.#tokenRotation = this.#clonedToken.document.rotation;
        this.#clonedToken.visible = false;
        this.layer.preview.addChild(this.#clonedToken);
        await this.#clonedToken.draw().then((c) => c.visible = true);
        return super.draw();
      }
      _destroy(options = {}) {
        super._destroy(options);
        this.#clonedToken?.destroy();
      }
      updateDirection(evt) {
        if (this.crosshair.lockManualRotation) return;
        const scrollDelta = Math.sign(evt.deltaY);
        let delta = this.crosshair.snap.direction ? this.crosshair.snap.direction * scrollDelta : scrollDelta * 10;
        if (delta < 0) delta += 360;
        if (delta > 360) delta -= 360;
        this.#tokenRotation += delta;
      }
      _onConfirm(evt) {
        evt.preventDefault();
        canvas.mouseInteractionManager.cancel(evt);
        if (this.isDrag) {
          this.isDrag = false;
          canvas.mouseInteractionManager.reset({
            interactionData: true,
            state: false
          });
          return;
        }
        this.document.updateSource({
          direction: this.#tokenRotation
        });
        return super._onConfirm(evt);
      }
    }
    class Crosshair {
      static PLACEMENT_RESTRICTIONS = CONSTANTS.PLACEMENT_RESTRICTIONS;
      static CALLBACKS = CONSTANTS.CALLBACKS;
      /**
       * Show a configurable crosshair
       *
       * @param {CrosshairData/Object} config
       * @param {CrosshairCallbackData/Object} callbacks
       * @returns {Promise}
       */
      static show(config = {}, callbacks = {}) {
        const reticle = new CrosshairsDocument({
          t: config.t ?? CONST.MEASURED_TEMPLATE_TYPES.CIRCLE,
          distance: config?.distance ?? canvas.grid.distance / 2,
          width: config?.width ?? canvas.grid.distance,
          borderColor: config.borderColor ?? "#000000",
          fillColor: config.fillColor ?? "#000000",
          angle: config.angle ?? 53.13,
          // Default for Foundry, it seems?
          direction: config.direction ?? 0
        }, {
          parent: canvas.scene
        }, foundry.utils.mergeObject(CrosshairsDocument.defaultConfig, config), callbacks);
        return reticle.show();
      }
      /**
       * Show a configurable crosshair based a foundry PlaceableObject
       *
       * @param {PlaceableObject} obj
       * @param {CrosshairData/Object} config
       * @param {CrosshairCallbackData/Object} callbacks
       * @returns {Promise}
       */
      static showToken(obj, config = {}, callbacks = {}) {
        const placeable = obj instanceof PlaceableObject ? obj : obj.object;
        const controlled = placeable.controlled;
        placeable.release();
        const reticle = new TokenCrosshairsDocument(placeable, {
          parent: canvas.scene
        }, config, callbacks);
        return reticle.show().finally(() => {
          if (controlled) placeable.control();
        });
      }
      /**
       * @param {CrosshairsDocument} crosshair
       * @param {String/Array<string>} types
       * @param {(object: PlaceableObject, crosshair: CrosshairsDocument, shape: PIXI.Graphics) => boolean} filterMethod
       * @returns {Array<Document>/Record<String, Array<Document>>}
       */
      static collect(crosshair, types = "Token", filterMethod = this.containsCenter) {
        const typesArray = Array.isArray(types) ? types : [types];
        const crosshairDoc = crosshair instanceof CrosshairsPlaceable ? crosshair.document : crosshair;
        const shape = crosshairDoc.getCrosshairShape();
        shape.x = crosshair.x;
        shape.y = crosshair.y;
        const result = typesArray.reduce((acc, embeddedName) => {
          const collection = crosshairDoc.parent.getEmbeddedCollection(embeddedName);
          acc[embeddedName] = collection.filter((document2) => {
            return filterMethod(document2.object, crosshairDoc, shape);
          });
          return acc;
        }, {});
        return Array.isArray(types) ? result : result[types];
      }
      static containsCenter(placeable, crosshair, shape) {
        return shape.contains(placeable.center.x, placeable.center.y);
      }
    }
    let moduleValid = false;
    let moduleReady = false;
    let canvasReady = false;
    Hooks.once("init", async function() {
      if (!game.modules.get("socketlib")?.active) return;
      moduleValid = true;
      CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE = !!game.modules.get(
        CONSTANTS.INTEGRATIONS.ISOMETRIC.MODULE_NAME
      )?.active;
      CONSTANTS.IS_V12 = foundry.utils.isNewerVersion(game.version, "12");
      if (CONSTANTS.IS_V12) {
        CONFIG.Canvas.transcoders.basis = true;
      }
      initializeModule();
      registerSocket();
    });
    Hooks.once("socketlib.ready", registerSocket);
    Hooks.once("ready", async function() {
      if (!game.modules.get("socketlib")?.active) {
        ui.notifications.error(
          "Sequencer requires the SocketLib module to be active and will not work without it!",
          { console: true }
        );
        return;
      }
      for (const [name2, func2] of Object.entries(easeFunctions)) {
        if (!CanvasAnimation[name2]) {
          CanvasAnimation[name2] = func2;
        }
      }
      if (game.user.isGM) {
        await runMigrations();
        await migrateSettings();
        await PlayerSettings.migrateOldPresets();
      }
      SequencerFoundryReplicator.registerHooks();
      InteractionManager.initialize();
    });
    Hooks.on("canvasTearDown", () => {
      canvasReady = false;
      SequencerEffectManager.tearDownPersists();
    });
    const setupModule = foundry.utils.debounce(() => {
      if (!moduleValid) return;
      if (!moduleReady) {
        moduleReady = true;
        debug("Ready to go!");
        Hooks.callAll("sequencer.ready");
        Hooks.callAll("sequencerReady");
      }
      if (!canvasReady && game.canvas?.ready) {
        canvasReady = true;
        SequencerEffectManager.initializePersistentEffects();
      }
    }, 25);
    Hooks.on("canvasReady", () => {
      setTimeout(() => {
        setupModule();
      }, 450);
    });
    Hooks.on("refreshToken", setupModule);
    Hooks.on("refreshDrawing", setupModule);
    Hooks.on("refreshTile", setupModule);
    Hooks.on("refreshMeasuredTemplate", setupModule);
    function initializeModule() {
      window.Sequence = Sequence$1;
      window.Sequencer = {
        Player: EffectPlayer,
        Presets: SequencerPresets,
        Database: SequencerDatabase,
        DatabaseViewer: DatabaseViewerApp,
        Preloader: SequencerPreloader,
        EffectManager: SequencerEffectManager,
        SoundManager: SequencerSoundManager,
        SectionManager: sectionManager,
        registerEase,
        BaseSection: Section,
        CONSTANTS: {
          EASE
        },
        Helpers: {
          wait: wait$1,
          clamp: clamp$1,
          interpolate,
          random_float_between,
          random_int_between,
          shuffle_array,
          random_array_element,
          random_object_element,
          make_array_unique
        },
        Crosshair
      };
      registerSettings();
      registerLayers();
      registerHotkeys();
      registerLibwrappers();
      registerBatchShader();
      SequencerEffectManager.setup();
      SequencerAboveUILayer.setup();
    }
    Hooks.once("ready", async () => {
      if (!game.user.isGM || game.settings.get(CONSTANTS.MODULE_NAME, "welcome-shown")) return;
      await game.settings.set(CONSTANTS.MODULE_NAME, "welcome-shown", true);
      const chatMessages = game.messages.filter((message) => {
        return message.content.includes("sequencer-welcome");
      }).map((message) => message.id);
      if (chatMessages.length) {
        return ChatMessage.deleteDocuments(chatMessages.slice(0, -1));
      }
      await ChatMessage.create({
        content: `
<div class="sequencer-welcome">
<img src="modules/sequencer/images/sequencer.png"/>
<div class="sequencer-divider"></div>
<p>Sequencer remains open, free, and regularly updated with the support of the Foundry community.</p>
<p>Please consider supporting us if you enjoy Foundry & visual effects!</p>
<div class="sequencer-divider"></div>
<p><a target="_blank" href="https://fantasycomputer.works/">Website</a> | <a target="_blank" href="https://fantasycomputer.works/FoundryVTT-Sequencer/#/">Docs</a> | <a target="_blank" href="https://discord.gg/qFHQUwBZAz">Discord</a> | <a target="_blank" href="https://ko-fi.com/fantasycomputerworks">Donate</a></p>
</div>`
      });
    });
    Hooks.once("monaco-editor.ready", registerTypes);
  }
});
export default require_sequencer();
//# sourceMappingURL=sequencer.js.map
