All user data for FoundryVTT. Includes worlds, systems, modules, and any asset in the "foundryuserdata" directory. Does NOT include the FoundryVTT installation itself.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

34241 lines
1017 KiB

1 year ago
  1. const module = "";
  2. const CONSTANTS = {
  3. MODULE_NAME: "sequencer",
  4. FLAG_NAME: "effects",
  5. COLOR: {
  6. PRIMARY: 15615023,
  7. SECONDARY: 6298186,
  8. TERTIARY: 6298186
  9. },
  10. SHAPES: {
  11. POLY: "polygon",
  12. RECT: "rectangle",
  13. CIRC: "circle",
  14. ELIP: "ellipse",
  15. RREC: "roundedRect"
  16. },
  17. FEET_REGEX: new RegExp(/\.[0-9]+ft\.*/g),
  18. ARRAY_REGEX: new RegExp(/\.[0-9]$/g),
  19. STATUS: {
  20. READY: 0,
  21. RUNNING: 1,
  22. COMPLETE: 2,
  23. SKIPPED: 3,
  24. ABORTED: 4
  25. }
  26. };
  27. CONSTANTS.INTEGRATIONS = {};
  28. CONSTANTS.INTEGRATIONS.ISOMETRIC = {};
  29. CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE = false;
  30. CONSTANTS.INTEGRATIONS.ISOMETRIC.MODULE_NAME = "grape_juice-isometrics";
  31. CONSTANTS.INTEGRATIONS.ISOMETRIC.SCENE_ENABLED = `flags.${CONSTANTS.INTEGRATIONS.ISOMETRIC.MODULE_NAME}.is_isometric`;
  32. CONSTANTS.INTEGRATIONS.ISOMETRIC.PROJECTION_FLAG = `flags.${CONSTANTS.INTEGRATIONS.ISOMETRIC.MODULE_NAME}.original_image_projection_type`;
  33. CONSTANTS.INTEGRATIONS.ISOMETRIC.PROJECTION_TYPES = {
  34. TOPDOWN: "topdown",
  35. TRUE: "true_isometric",
  36. DIAMETRIC: "diametric"
  37. };
  38. CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION = Math.sqrt(3);
  39. CONSTANTS.INTEGRATIONS.ISOMETRIC.DIMETRIC_CONVERSION = 2 / CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION;
  40. CONSTANTS.INTEGRATIONS.ISOMETRIC.DUNGEON_BUILDER_CONVERSION = 278 / 154 / CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION;
  41. CONSTANTS.EFFECTS_FLAG = `flags.${CONSTANTS.MODULE_NAME}.${CONSTANTS.FLAG_NAME}`;
  42. function registerEase(easeName, easeFunction, overwrite = false) {
  43. if (typeof easeName !== "string")
  44. throw custom_error("registerEase", "easeName must be of type string");
  45. if (!is_function$1(easeFunction))
  46. throw custom_error(
  47. "registerEase",
  48. "easeFunction must be of type function"
  49. );
  50. if (easeFunctions[easeName] !== void 0 && !overwrite)
  51. return;
  52. debug(`registerEase | Registered ease function: ${easeName}`);
  53. easeFunctions[easeName] = easeFunction;
  54. }
  55. const easeFunctions = {
  56. linear,
  57. easeInSine,
  58. easeOutSine,
  59. easeInOutSine,
  60. easeInQuad,
  61. easeOutQuad,
  62. easeInOutQuad,
  63. easeInCubic,
  64. easeOutCubic,
  65. easeInOutCubic,
  66. easeInQuart,
  67. easeOutQuart,
  68. easeInOutQuart,
  69. easeInQuint,
  70. easeOutQuint,
  71. easeInOutQuint,
  72. easeInExpo,
  73. easeOutExpo,
  74. easeInOutExpo,
  75. easeInCirc,
  76. easeOutCirc,
  77. easeInOutCirc,
  78. easeInBack,
  79. easeOutBack,
  80. easeInOutBack,
  81. easeInElastic,
  82. easeOutElastic,
  83. easeInOutElastic,
  84. easeInBounce,
  85. easeOutBounce,
  86. easeInOutBounce
  87. };
  88. const EASE = {
  89. LINEAR: "linear",
  90. SINE_IN: "easeInSine",
  91. SINE_OUT: "easeOutSine",
  92. SINE_IN_OUT: "easeInOutSine",
  93. QUAD_IN: "easeInQuad",
  94. QUAD_OUT: "easeOutQuad",
  95. QUAD_IN_OUT: "easeInOutQuad",
  96. CUBIC_IN: "easeInCubic",
  97. CUBIC_OUT: "easeOutCubic",
  98. CUBIC_IN_OUT: "easeInOutCubic",
  99. QUART_IN: "easeInQuart",
  100. QUART_OUT: "easeOutQuart",
  101. QUART_IN_OUT: "easeInOutQuart",
  102. QUINT_IN: "easeInQuint",
  103. QUINT_OUT: "easeOutQuint",
  104. QUINT_IN_OUT: "easeInOutQuint",
  105. EXPO_IN: "easeInExpo",
  106. EXPO_OUT: "easeOutExpo",
  107. EXPO_IN_OUT: "easeInOutExpo",
  108. CIRC_IN: "easeInCirc",
  109. CIRC_OUT: "easeOutCirc",
  110. CIRC_IN_OUT: "easeInOutCirc",
  111. BACK_IN: "easeInBack",
  112. BACK_OUT: "easeOutBack",
  113. BACK_IN_OUT: "easeInOutBack",
  114. ELASTIC_IN: "easeInElastic",
  115. ELASTIC_OUT: "easeOutElastic",
  116. ELASTIC_IN_OUT: "easeInOutElastic",
  117. BOUNCE_IN: "easeInBounce",
  118. BOUNCE_OUT: "easeOutBounce",
  119. BOUNCE_IN_OUT: "easeInOutBounce"
  120. };
  121. function linear(x) {
  122. return x;
  123. }
  124. function easeInSine(x) {
  125. return 1 - Math.cos(x * Math.PI / 2);
  126. }
  127. function easeOutSine(x) {
  128. return Math.sin(x * Math.PI / 2);
  129. }
  130. function easeInOutSine(x) {
  131. return -(Math.cos(Math.PI * x) - 1) / 2;
  132. }
  133. function easeInQuad(x) {
  134. return x * x;
  135. }
  136. function easeOutQuad(x) {
  137. return 1 - (1 - x) * (1 - x);
  138. }
  139. function easeInOutQuad(x) {
  140. return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
  141. }
  142. function easeInCubic(x) {
  143. return x * x * x;
  144. }
  145. function easeOutCubic(x) {
  146. return 1 - Math.pow(1 - x, 3);
  147. }
  148. function easeInOutCubic(x) {
  149. return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
  150. }
  151. function easeInQuart(x) {
  152. return x * x * x * x;
  153. }
  154. function easeOutQuart(x) {
  155. return 1 - Math.pow(1 - x, 4);
  156. }
  157. function easeInOutQuart(x) {
  158. return x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2;
  159. }
  160. function easeInQuint(x) {
  161. return x * x * x * x * x;
  162. }
  163. function easeOutQuint(x) {
  164. return 1 - Math.pow(1 - x, 5);
  165. }
  166. function easeInOutQuint(x) {
  167. return x < 0.5 ? 16 * x * x * x * x * x : 1 - Math.pow(-2 * x + 2, 5) / 2;
  168. }
  169. function easeInExpo(x) {
  170. return x === 0 ? 0 : Math.pow(2, 10 * x - 10);
  171. }
  172. function easeOutExpo(x) {
  173. return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
  174. }
  175. function easeInOutExpo(x) {
  176. 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;
  177. }
  178. function easeInCirc(x) {
  179. return 1 - Math.sqrt(1 - Math.pow(x, 2));
  180. }
  181. function easeOutCirc(x) {
  182. return Math.sqrt(1 - Math.pow(x - 1, 2));
  183. }
  184. function easeInOutCirc(x) {
  185. 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;
  186. }
  187. function easeInBack(x) {
  188. const c1 = 1.70158;
  189. const c3 = c1 + 1;
  190. return c3 * x * x * x - c1 * x * x;
  191. }
  192. function easeOutBack(x) {
  193. const c1 = 1.70158;
  194. const c3 = c1 + 1;
  195. return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
  196. }
  197. function easeInOutBack(x) {
  198. const c1 = 1.70158;
  199. const c2 = c1 * 1.525;
  200. 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;
  201. }
  202. function easeInElastic(x) {
  203. const c4 = 2 * Math.PI / 3;
  204. return x === 0 ? 0 : x === 1 ? 1 : -Math.pow(2, 10 * x - 10) * Math.sin((x * 10 - 10.75) * c4);
  205. }
  206. function easeOutElastic(x) {
  207. const c4 = 2 * Math.PI / 3;
  208. return x === 0 ? 0 : x === 1 ? 1 : Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1;
  209. }
  210. function easeInOutElastic(x) {
  211. const c5 = 2 * Math.PI / 4.5;
  212. 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;
  213. }
  214. function easeInBounce(x) {
  215. return 1 - easeOutBounce(1 - x);
  216. }
  217. function easeOutBounce(x) {
  218. const n1 = 7.5625;
  219. const d1 = 2.75;
  220. if (x < 1 / d1) {
  221. return n1 * x * x;
  222. } else if (x < 2 / d1) {
  223. return n1 * (x -= 1.5 / d1) * x + 0.75;
  224. } else if (x < 2.5 / d1) {
  225. return n1 * (x -= 2.25 / d1) * x + 0.9375;
  226. } else {
  227. return n1 * (x -= 2.625 / d1) * x + 0.984375;
  228. }
  229. }
  230. function easeInOutBounce(x) {
  231. return x < 0.5 ? (1 - easeOutBounce(1 - 2 * x)) / 2 : (1 + easeOutBounce(2 * x - 1)) / 2;
  232. }
  233. async function getFiles(inFile, { applyWildCard = false, softFail = false } = {}) {
  234. let source = "data";
  235. const browseOptions = { wildcard: applyWildCard };
  236. if (/\.s3\./.test(inFile)) {
  237. source = "s3";
  238. const { bucket, keyPrefix } = FilePicker.parseS3URL(inFile);
  239. if (bucket) {
  240. browseOptions.bucket = bucket;
  241. inFile = keyPrefix;
  242. }
  243. }
  244. try {
  245. return (await FilePicker.browse(source, inFile, browseOptions)).files;
  246. } catch (err) {
  247. if (softFail)
  248. return false;
  249. throw custom_error("Sequencer", `getFiles | ${err}`);
  250. }
  251. }
  252. function interpolate(p1, p2, t, ease = "linear") {
  253. const easeFunction = is_function$1(ease) ? ease : easeFunctions[ease];
  254. return p1 + (p2 - p1) * easeFunction(t);
  255. }
  256. function random_float_between(min, max, twister = false) {
  257. const random = twister ? twister.random() : Math.random();
  258. const _max = Math.max(max, min);
  259. const _min = Math.min(max, min);
  260. return random * (_max - _min) + _min;
  261. }
  262. function random_int_between(min, max, twister = false) {
  263. return Math.floor(random_float_between(min, max, twister));
  264. }
  265. function shuffle_array(inArray, twister = false) {
  266. let shuffled = [...inArray];
  267. const randomMethod = twister?.random ?? Math.random;
  268. for (let i = shuffled.length - 1; i > 0; i--) {
  269. let j = Math.floor(randomMethod() * (i + 1));
  270. let temp = shuffled[i];
  271. shuffled[i] = shuffled[j];
  272. shuffled[j] = temp;
  273. }
  274. return shuffled;
  275. }
  276. function is_function$1(inFunc) {
  277. return inFunc && ({}.toString.call(inFunc) === "[object Function]" || {}.toString.call(inFunc) === "[object AsyncFunction]");
  278. }
  279. function random_array_element(inArray, { recurse = false, twister = false, index = false } = {}) {
  280. const chosenIndex = random_int_between(0, inArray.length, twister);
  281. let choice = inArray[chosenIndex];
  282. if (recurse && Array.isArray(choice)) {
  283. return random_array_element(choice, { recurse, twister, index });
  284. }
  285. if (index) {
  286. return chosenIndex;
  287. }
  288. return choice;
  289. }
  290. function random_object_element(inObject, { recurse = false, twister = false } = {}) {
  291. let keys = Object.keys(inObject).filter((k) => !k.startsWith("_"));
  292. let choice = inObject[random_array_element(keys, { twister })];
  293. if (typeof choice === "object" && recurse) {
  294. return random_object_element(choice, { recurse: true });
  295. }
  296. return choice;
  297. }
  298. function is_real_number(inNumber) {
  299. return !isNaN(inNumber) && typeof inNumber === "number" && isFinite(inNumber);
  300. }
  301. function deep_get(obj, path) {
  302. if (!Array.isArray(path))
  303. path = path.split(".");
  304. try {
  305. let i;
  306. for (i = 0; i < path.length - 1; i++) {
  307. obj = obj[path[i]];
  308. }
  309. return obj[path[i]];
  310. } catch (err) {
  311. }
  312. }
  313. function deep_set(obj, path, value) {
  314. if (!Array.isArray(path))
  315. path = path.split(".");
  316. try {
  317. let i;
  318. for (i = 0; i < path.length - 1; i++) {
  319. obj = obj[path[i]];
  320. }
  321. if (is_function$1(obj[path[i]])) {
  322. obj[path[i]](value);
  323. } else {
  324. obj[path[i]] = value;
  325. }
  326. } catch (err) {
  327. }
  328. }
  329. function flatten_object(obj) {
  330. let toReturn = [];
  331. for (let i in obj) {
  332. if (i.startsWith("_"))
  333. continue;
  334. if (!obj.hasOwnProperty(i))
  335. continue;
  336. if (typeof obj[i] == "object") {
  337. let flatObject = flatten_object(obj[i]);
  338. for (let x in flatObject) {
  339. if (!flatObject.hasOwnProperty(x))
  340. continue;
  341. toReturn[i + "." + x] = flatObject[x];
  342. }
  343. } else {
  344. toReturn[i] = obj[i];
  345. }
  346. }
  347. return toReturn;
  348. }
  349. function wait$1(ms) {
  350. return new Promise((resolve) => setTimeout(resolve, ms));
  351. }
  352. function clamp(num, min, max) {
  353. const _max = Math.max(min, max);
  354. const _min = Math.min(min, max);
  355. return Math.max(_min, Math.min(_max, num));
  356. }
  357. function is_UUID(inId) {
  358. return typeof inId === "string" && (inId.startsWith("Scene") || inId.startsWith("Actor") || inId.startsWith("Item")) && (inId.match(/\./g) || []).length && !inId.endsWith(".");
  359. }
  360. function get_object_from_scene(inObjectId, inSceneId = game.user.viewedScene) {
  361. let tryUUID = is_UUID(inObjectId);
  362. if (tryUUID) {
  363. const obj = from_uuid_fast(inObjectId);
  364. if (obj)
  365. return obj;
  366. tryUUID = false;
  367. }
  368. return get_all_documents_from_scene(inSceneId).find((obj) => {
  369. return get_object_identifier(obj, tryUUID) === inObjectId;
  370. });
  371. }
  372. function from_uuid_fast(uuid) {
  373. let parts = uuid.split(".");
  374. let doc;
  375. const [docName, docId] = parts.slice(0, 2);
  376. parts = parts.slice(2);
  377. const collection = CONFIG[docName].collection.instance;
  378. doc = collection.get(docId);
  379. while (doc && parts.length > 1) {
  380. const [embeddedName, embeddedId] = parts.slice(0, 2);
  381. if (embeddedName === "SequencerEffect") {
  382. if (game.user.viewedScene !== docId) {
  383. let effects = doc.getFlag(CONSTANTS.MODULE_NAME, CONSTANTS.FLAG_NAME);
  384. doc = new Map(effects).get(embeddedId);
  385. } else {
  386. doc = Sequencer.EffectManager.getEffects({ effect: docId })?.[0];
  387. }
  388. } else {
  389. doc = doc.getEmbeddedDocument(embeddedName, embeddedId);
  390. }
  391. parts = parts.slice(2);
  392. }
  393. return doc || null;
  394. }
  395. function get_all_documents_from_scene(inSceneId = false) {
  396. const scene = inSceneId ? game.scenes.get(inSceneId) : game.scenes.get(game.user?.viewedScene);
  397. if (!scene)
  398. return [];
  399. return [
  400. ...canvas.templates?.preview?.children ?? [],
  401. ...Array.from(scene?.tokens ?? []),
  402. ...Array.from(scene?.lights ?? []),
  403. ...Array.from(scene?.sounds ?? []),
  404. ...Array.from(scene?.templates ?? []),
  405. ...Array.from(scene?.tiles ?? []),
  406. ...Array.from(scene?.walls ?? []),
  407. ...Array.from(scene?.drawings ?? [])
  408. ].deepFlatten().filter(Boolean);
  409. }
  410. function validate_document(inObject) {
  411. const document2 = inObject?.document ?? inObject;
  412. return is_UUID(document2?.uuid) ? document2 : inObject;
  413. }
  414. function get_object_identifier(inObject, tryUUID = true) {
  415. const uuid = tryUUID && is_UUID(inObject?.uuid) ? inObject?.uuid : void 0;
  416. return uuid ?? inObject?.id ?? inObject?.document?.name ?? inObject?.name ?? (inObject?.tag !== "" ? inObject?.tag : void 0) ?? (inObject?.label !== "" ? inObject?.label : void 0);
  417. }
  418. function make_array_unique(inArray) {
  419. return Array.from(new Set(inArray));
  420. }
  421. function debug(msg, args = "") {
  422. if (game.settings.get(CONSTANTS.MODULE_NAME, "debug"))
  423. console.log(`DEBUG | Sequencer | ${msg}`, args);
  424. }
  425. function debug_error(msg, args) {
  426. if (game.settings.get(CONSTANTS.MODULE_NAME, "debug"))
  427. console.error(`DEBUG | Sequencer | ${msg}`, args);
  428. }
  429. function custom_warning(inClassName, warning, notify = false) {
  430. inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
  431. warning = `${inClassName} | ${warning}`;
  432. if (notify)
  433. ui.notifications.warn(warning, { console: false });
  434. console.warn(warning.replace("<br>", "\n"));
  435. }
  436. const throttledWarnings = {};
  437. function throttled_custom_warning(inClassName, warning, delay = 1e4, notify = false) {
  438. inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
  439. warning = `${inClassName} | ${warning}`;
  440. if (throttledWarnings[warning])
  441. return;
  442. throttledWarnings[warning] = true;
  443. if (notify)
  444. ui.notifications.warn(warning, { console: false });
  445. console.warn(warning.replace("<br>", "\n"));
  446. setTimeout(() => {
  447. delete throttledWarnings[warning];
  448. }, delay);
  449. }
  450. function custom_error(inClassName, error, notify = true) {
  451. inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
  452. error = `${inClassName} | ${error}`;
  453. if (notify)
  454. ui.notifications.error(error, { console: false });
  455. return new Error(error.replace("<br>", "\n"));
  456. }
  457. function user_can_do(inSetting) {
  458. return game.user.role > game.settings.get(CONSTANTS.MODULE_NAME, inSetting);
  459. }
  460. function group_by(xs, key) {
  461. return xs.reduce(function(acc, obj) {
  462. let property = getProperty(obj, key);
  463. acc[property] = acc[property] || [];
  464. acc[property].push(obj);
  465. return acc;
  466. }, {});
  467. }
  468. function objHasProperty(obj, prop) {
  469. return obj.constructor.prototype.hasOwnProperty(prop);
  470. }
  471. function sequence_proxy_wrap(inSequence) {
  472. return new Proxy(inSequence, {
  473. get: function(target, prop) {
  474. if (!objHasProperty(target, prop)) {
  475. if (Sequencer.SectionManager.externalSections[prop] === void 0) {
  476. const section = target.sections[target.sections.length - 1];
  477. if (section && objHasProperty(section, prop)) {
  478. const targetProperty = Reflect.get(section, prop);
  479. return is_function$1(targetProperty) ? targetProperty.bind(section) : targetProperty;
  480. }
  481. return Reflect.get(target, prop);
  482. }
  483. target.sectionToCreate = Sequencer.SectionManager.externalSections[prop];
  484. return Reflect.get(target, "_createCustomSection");
  485. }
  486. return Reflect.get(target, prop);
  487. }
  488. });
  489. }
  490. function section_proxy_wrap(inClass) {
  491. return new Proxy(inClass, {
  492. get: function(target, prop) {
  493. if (!objHasProperty(target, prop) && objHasProperty(target.sequence, prop)) {
  494. const targetProperty = Reflect.get(target.sequence, prop);
  495. return is_function$1(targetProperty) ? targetProperty.bind(target.sequence) : targetProperty;
  496. }
  497. return Reflect.get(target, prop);
  498. }
  499. });
  500. }
  501. function str_to_search_regex_str(str) {
  502. return str.trim().replace(/[^A-Za-z0-9 .*_-]/g, "").replace(/\*+/g, ".*?");
  503. }
  504. function safe_str(str) {
  505. return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
  506. }
  507. function get_hash(input) {
  508. let hash2 = 0;
  509. const len = input.length;
  510. for (let i = 0; i < len; i++) {
  511. hash2 = (hash2 << 5) - hash2 + input.charCodeAt(i);
  512. hash2 |= 0;
  513. }
  514. return hash2;
  515. }
  516. function parseColor(inColor) {
  517. return {
  518. hexadecimal: is_real_number(inColor) ? inColor.toString(16) : inColor,
  519. decimal: typeof inColor === "string" && inColor.startsWith("#") ? parseInt(inColor.slice(1), 16) : inColor
  520. };
  521. }
  522. function noop() {
  523. }
  524. const identity = (x) => x;
  525. function assign(tar, src) {
  526. for (const k in src)
  527. tar[k] = src[k];
  528. return tar;
  529. }
  530. function run(fn) {
  531. return fn();
  532. }
  533. function blank_object() {
  534. return /* @__PURE__ */ Object.create(null);
  535. }
  536. function run_all(fns) {
  537. fns.forEach(run);
  538. }
  539. function is_function(thing) {
  540. return typeof thing === "function";
  541. }
  542. function safe_not_equal(a, b) {
  543. return a != a ? b == b : a !== b || (a && typeof a === "object" || typeof a === "function");
  544. }
  545. let src_url_equal_anchor;
  546. function src_url_equal(element_src, url) {
  547. if (!src_url_equal_anchor) {
  548. src_url_equal_anchor = document.createElement("a");
  549. }
  550. src_url_equal_anchor.href = url;
  551. return element_src === src_url_equal_anchor.href;
  552. }
  553. function is_empty(obj) {
  554. return Object.keys(obj).length === 0;
  555. }
  556. function subscribe(store, ...callbacks) {
  557. if (store == null) {
  558. return noop;
  559. }
  560. const unsub = store.subscribe(...callbacks);
  561. return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
  562. }
  563. function get_store_value(store) {
  564. let value;
  565. subscribe(store, (_) => value = _)();
  566. return value;
  567. }
  568. function component_subscribe(component, store, callback) {
  569. component.$$.on_destroy.push(subscribe(store, callback));
  570. }
  571. function create_slot(definition, ctx, $$scope, fn) {
  572. if (definition) {
  573. const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);
  574. return definition[0](slot_ctx);
  575. }
  576. }
  577. function get_slot_context(definition, ctx, $$scope, fn) {
  578. return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx;
  579. }
  580. function get_slot_changes(definition, $$scope, dirty, fn) {
  581. if (definition[2] && fn) {
  582. const lets = definition[2](fn(dirty));
  583. if ($$scope.dirty === void 0) {
  584. return lets;
  585. }
  586. if (typeof lets === "object") {
  587. const merged = [];
  588. const len = Math.max($$scope.dirty.length, lets.length);
  589. for (let i = 0; i < len; i += 1) {
  590. merged[i] = $$scope.dirty[i] | lets[i];
  591. }
  592. return merged;
  593. }
  594. return $$scope.dirty | lets;
  595. }
  596. return $$scope.dirty;
  597. }
  598. function update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn) {
  599. if (slot_changes) {
  600. const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
  601. slot.p(slot_context, slot_changes);
  602. }
  603. }
  604. function get_all_dirty_from_scope($$scope) {
  605. if ($$scope.ctx.length > 32) {
  606. const dirty = [];
  607. const length = $$scope.ctx.length / 32;
  608. for (let i = 0; i < length; i++) {
  609. dirty[i] = -1;
  610. }
  611. return dirty;
  612. }
  613. return -1;
  614. }
  615. function exclude_internal_props(props) {
  616. const result = {};
  617. for (const k in props)
  618. if (k[0] !== "$")
  619. result[k] = props[k];
  620. return result;
  621. }
  622. function compute_slots(slots) {
  623. const result = {};
  624. for (const key in slots) {
  625. result[key] = true;
  626. }
  627. return result;
  628. }
  629. function set_store_value(store, ret, value) {
  630. store.set(value);
  631. return ret;
  632. }
  633. function action_destroyer(action_result) {
  634. return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
  635. }
  636. const is_client = typeof window !== "undefined";
  637. let now = is_client ? () => window.performance.now() : () => Date.now();
  638. let raf = is_client ? (cb) => requestAnimationFrame(cb) : noop;
  639. const tasks = /* @__PURE__ */ new Set();
  640. function run_tasks(now2) {
  641. tasks.forEach((task) => {
  642. if (!task.c(now2)) {
  643. tasks.delete(task);
  644. task.f();
  645. }
  646. });
  647. if (tasks.size !== 0)
  648. raf(run_tasks);
  649. }
  650. function loop(callback) {
  651. let task;
  652. if (tasks.size === 0)
  653. raf(run_tasks);
  654. return {
  655. promise: new Promise((fulfill) => {
  656. tasks.add(task = { c: callback, f: fulfill });
  657. }),
  658. abort() {
  659. tasks.delete(task);
  660. }
  661. };
  662. }
  663. function append(target, node) {
  664. target.appendChild(node);
  665. }
  666. function get_root_for_style(node) {
  667. if (!node)
  668. return document;
  669. const root = node.getRootNode ? node.getRootNode() : node.ownerDocument;
  670. if (root && root.host) {
  671. return root;
  672. }
  673. return node.ownerDocument;
  674. }
  675. function append_empty_stylesheet(node) {
  676. const style_element = element("style");
  677. append_stylesheet(get_root_for_style(node), style_element);
  678. return style_element.sheet;
  679. }
  680. function append_stylesheet(node, style) {
  681. append(node.head || node, style);
  682. return style.sheet;
  683. }
  684. function insert(target, node, anchor) {
  685. target.insertBefore(node, anchor || null);
  686. }
  687. function detach(node) {
  688. if (node.parentNode) {
  689. node.parentNode.removeChild(node);
  690. }
  691. }
  692. function destroy_each(iterations, detaching) {
  693. for (let i = 0; i < iterations.length; i += 1) {
  694. if (iterations[i])
  695. iterations[i].d(detaching);
  696. }
  697. }
  698. function element(name) {
  699. return document.createElement(name);
  700. }
  701. function svg_element(name) {
  702. return document.createElementNS("http://www.w3.org/2000/svg", name);
  703. }
  704. function text$1(data) {
  705. return document.createTextNode(data);
  706. }
  707. function space() {
  708. return text$1(" ");
  709. }
  710. function empty() {
  711. return text$1("");
  712. }
  713. function listen(node, event, handler, options) {
  714. node.addEventListener(event, handler, options);
  715. return () => node.removeEventListener(event, handler, options);
  716. }
  717. function prevent_default(fn) {
  718. return function(event) {
  719. event.preventDefault();
  720. return fn.call(this, event);
  721. };
  722. }
  723. function stop_propagation(fn) {
  724. return function(event) {
  725. event.stopPropagation();
  726. return fn.call(this, event);
  727. };
  728. }
  729. function attr(node, attribute, value) {
  730. if (value == null)
  731. node.removeAttribute(attribute);
  732. else if (node.getAttribute(attribute) !== value)
  733. node.setAttribute(attribute, value);
  734. }
  735. function to_number(value) {
  736. return value === "" ? null : +value;
  737. }
  738. function children(element2) {
  739. return Array.from(element2.childNodes);
  740. }
  741. function set_data(text2, data) {
  742. data = "" + data;
  743. if (text2.wholeText !== data)
  744. text2.data = data;
  745. }
  746. function set_input_value(input, value) {
  747. input.value = value == null ? "" : value;
  748. }
  749. function set_style(node, key, value, important) {
  750. if (value === null) {
  751. node.style.removeProperty(key);
  752. } else {
  753. node.style.setProperty(key, value, important ? "important" : "");
  754. }
  755. }
  756. function select_option(select, value) {
  757. for (let i = 0; i < select.options.length; i += 1) {
  758. const option = select.options[i];
  759. if (option.__value === value) {
  760. option.selected = true;
  761. return;
  762. }
  763. }
  764. select.selectedIndex = -1;
  765. }
  766. function select_options(select, value) {
  767. for (let i = 0; i < select.options.length; i += 1) {
  768. const option = select.options[i];
  769. option.selected = ~value.indexOf(option.__value);
  770. }
  771. }
  772. function select_value(select) {
  773. const selected_option = select.querySelector(":checked") || select.options[0];
  774. return selected_option && selected_option.__value;
  775. }
  776. function select_multiple_value(select) {
  777. return [].map.call(select.querySelectorAll(":checked"), (option) => option.__value);
  778. }
  779. function toggle_class(element2, name, toggle) {
  780. element2.classList[toggle ? "add" : "remove"](name);
  781. }
  782. function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
  783. const e = document.createEvent("CustomEvent");
  784. e.initCustomEvent(type, bubbles, cancelable, detail);
  785. return e;
  786. }
  787. class HtmlTag {
  788. constructor(is_svg = false) {
  789. this.is_svg = false;
  790. this.is_svg = is_svg;
  791. this.e = this.n = null;
  792. }
  793. c(html) {
  794. this.h(html);
  795. }
  796. m(html, target, anchor = null) {
  797. if (!this.e) {
  798. if (this.is_svg)
  799. this.e = svg_element(target.nodeName);
  800. else
  801. this.e = element(target.nodeName);
  802. this.t = target;
  803. this.c(html);
  804. }
  805. this.i(anchor);
  806. }
  807. h(html) {
  808. this.e.innerHTML = html;
  809. this.n = Array.from(this.e.childNodes);
  810. }
  811. i(anchor) {
  812. for (let i = 0; i < this.n.length; i += 1) {
  813. insert(this.t, this.n[i], anchor);
  814. }
  815. }
  816. p(html) {
  817. this.d();
  818. this.h(html);
  819. this.i(this.a);
  820. }
  821. d() {
  822. this.n.forEach(detach);
  823. }
  824. }
  825. function construct_svelte_component(component, props) {
  826. return new component(props);
  827. }
  828. const managed_styles = /* @__PURE__ */ new Map();
  829. let active = 0;
  830. function hash(str) {
  831. let hash2 = 5381;
  832. let i = str.length;
  833. while (i--)
  834. hash2 = (hash2 << 5) - hash2 ^ str.charCodeAt(i);
  835. return hash2 >>> 0;
  836. }
  837. function create_style_information(doc, node) {
  838. const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
  839. managed_styles.set(doc, info);
  840. return info;
  841. }
  842. function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {
  843. const step = 16.666 / duration;
  844. let keyframes = "{\n";
  845. for (let p = 0; p <= 1; p += step) {
  846. const t = a + (b - a) * ease(p);
  847. keyframes += p * 100 + `%{${fn(t, 1 - t)}}
  848. `;
  849. }
  850. const rule = keyframes + `100% {${fn(b, 1 - b)}}
  851. }`;
  852. const name = `__svelte_${hash(rule)}_${uid}`;
  853. const doc = get_root_for_style(node);
  854. const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
  855. if (!rules[name]) {
  856. rules[name] = true;
  857. stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
  858. }
  859. const animation2 = node.style.animation || "";
  860. node.style.animation = `${animation2 ? `${animation2}, ` : ""}${name} ${duration}ms linear ${delay}ms 1 both`;
  861. active += 1;
  862. return name;
  863. }
  864. function delete_rule(node, name) {
  865. const previous = (node.style.animation || "").split(", ");
  866. const next = previous.filter(
  867. name ? (anim) => anim.indexOf(name) < 0 : (anim) => anim.indexOf("__svelte") === -1
  868. // remove all Svelte animations
  869. );
  870. const deleted = previous.length - next.length;
  871. if (deleted) {
  872. node.style.animation = next.join(", ");
  873. active -= deleted;
  874. if (!active)
  875. clear_rules();
  876. }
  877. }
  878. function clear_rules() {
  879. raf(() => {
  880. if (active)
  881. return;
  882. managed_styles.forEach((info) => {
  883. const { ownerNode } = info.stylesheet;
  884. if (ownerNode)
  885. detach(ownerNode);
  886. });
  887. managed_styles.clear();
  888. });
  889. }
  890. let current_component;
  891. function set_current_component(component) {
  892. current_component = component;
  893. }
  894. function get_current_component() {
  895. if (!current_component)
  896. throw new Error("Function called outside component initialization");
  897. return current_component;
  898. }
  899. function onMount(fn) {
  900. get_current_component().$$.on_mount.push(fn);
  901. }
  902. function afterUpdate(fn) {
  903. get_current_component().$$.after_update.push(fn);
  904. }
  905. function onDestroy(fn) {
  906. get_current_component().$$.on_destroy.push(fn);
  907. }
  908. function createEventDispatcher() {
  909. const component = get_current_component();
  910. return (type, detail, { cancelable = false } = {}) => {
  911. const callbacks = component.$$.callbacks[type];
  912. if (callbacks) {
  913. const event = custom_event(type, detail, { cancelable });
  914. callbacks.slice().forEach((fn) => {
  915. fn.call(component, event);
  916. });
  917. return !event.defaultPrevented;
  918. }
  919. return true;
  920. };
  921. }
  922. function setContext(key, context) {
  923. get_current_component().$$.context.set(key, context);
  924. return context;
  925. }
  926. function getContext(key) {
  927. return get_current_component().$$.context.get(key);
  928. }
  929. const dirty_components = [];
  930. const binding_callbacks = [];
  931. const render_callbacks = [];
  932. const flush_callbacks = [];
  933. const resolved_promise = Promise.resolve();
  934. let update_scheduled = false;
  935. function schedule_update() {
  936. if (!update_scheduled) {
  937. update_scheduled = true;
  938. resolved_promise.then(flush);
  939. }
  940. }
  941. function add_render_callback(fn) {
  942. render_callbacks.push(fn);
  943. }
  944. function add_flush_callback(fn) {
  945. flush_callbacks.push(fn);
  946. }
  947. const seen_callbacks = /* @__PURE__ */ new Set();
  948. let flushidx = 0;
  949. function flush() {
  950. if (flushidx !== 0) {
  951. return;
  952. }
  953. const saved_component = current_component;
  954. do {
  955. try {
  956. while (flushidx < dirty_components.length) {
  957. const component = dirty_components[flushidx];
  958. flushidx++;
  959. set_current_component(component);
  960. update(component.$$);
  961. }
  962. } catch (e) {
  963. dirty_components.length = 0;
  964. flushidx = 0;
  965. throw e;
  966. }
  967. set_current_component(null);
  968. dirty_components.length = 0;
  969. flushidx = 0;
  970. while (binding_callbacks.length)
  971. binding_callbacks.pop()();
  972. for (let i = 0; i < render_callbacks.length; i += 1) {
  973. const callback = render_callbacks[i];
  974. if (!seen_callbacks.has(callback)) {
  975. seen_callbacks.add(callback);
  976. callback();
  977. }
  978. }
  979. render_callbacks.length = 0;
  980. } while (dirty_components.length);
  981. while (flush_callbacks.length) {
  982. flush_callbacks.pop()();
  983. }
  984. update_scheduled = false;
  985. seen_callbacks.clear();
  986. set_current_component(saved_component);
  987. }
  988. function update($$) {
  989. if ($$.fragment !== null) {
  990. $$.update();
  991. run_all($$.before_update);
  992. const dirty = $$.dirty;
  993. $$.dirty = [-1];
  994. $$.fragment && $$.fragment.p($$.ctx, dirty);
  995. $$.after_update.forEach(add_render_callback);
  996. }
  997. }
  998. let promise;
  999. function wait() {
  1000. if (!promise) {
  1001. promise = Promise.resolve();
  1002. promise.then(() => {
  1003. promise = null;
  1004. });
  1005. }
  1006. return promise;
  1007. }
  1008. function dispatch(node, direction, kind) {
  1009. node.dispatchEvent(custom_event(`${direction ? "intro" : "outro"}${kind}`));
  1010. }
  1011. const outroing = /* @__PURE__ */ new Set();
  1012. let outros;
  1013. function group_outros() {
  1014. outros = {
  1015. r: 0,
  1016. c: [],
  1017. p: outros
  1018. // parent group
  1019. };
  1020. }
  1021. function check_outros() {
  1022. if (!outros.r) {
  1023. run_all(outros.c);
  1024. }
  1025. outros = outros.p;
  1026. }
  1027. function transition_in(block, local) {
  1028. if (block && block.i) {
  1029. outroing.delete(block);
  1030. block.i(local);
  1031. }
  1032. }
  1033. function transition_out(block, local, detach2, callback) {
  1034. if (block && block.o) {
  1035. if (outroing.has(block))
  1036. return;
  1037. outroing.add(block);
  1038. outros.c.push(() => {
  1039. outroing.delete(block);
  1040. if (callback) {
  1041. if (detach2)
  1042. block.d(1);
  1043. callback();
  1044. }
  1045. });
  1046. block.o(local);
  1047. } else if (callback) {
  1048. callback();
  1049. }
  1050. }
  1051. const null_transition = { duration: 0 };
  1052. function create_in_transition(node, fn, params) {
  1053. const options = { direction: "in" };
  1054. let config = fn(node, params, options);
  1055. let running = false;
  1056. let animation_name;
  1057. let task;
  1058. let uid = 0;
  1059. function cleanup() {
  1060. if (animation_name)
  1061. delete_rule(node, animation_name);
  1062. }
  1063. function go() {
  1064. const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;
  1065. if (css)
  1066. animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++);
  1067. tick(0, 1);
  1068. const start_time = now() + delay;
  1069. const end_time = start_time + duration;
  1070. if (task)
  1071. task.abort();
  1072. running = true;
  1073. add_render_callback(() => dispatch(node, true, "start"));
  1074. task = loop((now2) => {
  1075. if (running) {
  1076. if (now2 >= end_time) {
  1077. tick(1, 0);
  1078. dispatch(node, true, "end");
  1079. cleanup();
  1080. return running = false;
  1081. }
  1082. if (now2 >= start_time) {
  1083. const t = easing((now2 - start_time) / duration);
  1084. tick(t, 1 - t);
  1085. }
  1086. }
  1087. return running;
  1088. });
  1089. }
  1090. let started = false;
  1091. return {
  1092. start() {
  1093. if (started)
  1094. return;
  1095. started = true;
  1096. delete_rule(node);
  1097. if (is_function(config)) {
  1098. config = config(options);
  1099. wait().then(go);
  1100. } else {
  1101. go();
  1102. }
  1103. },
  1104. invalidate() {
  1105. started = false;
  1106. },
  1107. end() {
  1108. if (running) {
  1109. cleanup();
  1110. running = false;
  1111. }
  1112. }
  1113. };
  1114. }
  1115. function create_out_transition(node, fn, params) {
  1116. const options = { direction: "out" };
  1117. let config = fn(node, params, options);
  1118. let running = true;
  1119. let animation_name;
  1120. const group = outros;
  1121. group.r += 1;
  1122. function go() {
  1123. const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;
  1124. if (css)
  1125. animation_name = create_rule(node, 1, 0, duration, delay, easing, css);
  1126. const start_time = now() + delay;
  1127. const end_time = start_time + duration;
  1128. add_render_callback(() => dispatch(node, false, "start"));
  1129. loop((now2) => {
  1130. if (running) {
  1131. if (now2 >= end_time) {
  1132. tick(0, 1);
  1133. dispatch(node, false, "end");
  1134. if (!--group.r) {
  1135. run_all(group.c);
  1136. }
  1137. return false;
  1138. }
  1139. if (now2 >= start_time) {
  1140. const t = easing((now2 - start_time) / duration);
  1141. tick(1 - t, t);
  1142. }
  1143. }
  1144. return running;
  1145. });
  1146. }
  1147. if (is_function(config)) {
  1148. wait().then(() => {
  1149. config = config(options);
  1150. go();
  1151. });
  1152. } else {
  1153. go();
  1154. }
  1155. return {
  1156. end(reset) {
  1157. if (reset && config.tick) {
  1158. config.tick(1, 0);
  1159. }
  1160. if (running) {
  1161. if (animation_name)
  1162. delete_rule(node, animation_name);
  1163. running = false;
  1164. }
  1165. }
  1166. };
  1167. }
  1168. function destroy_block(block, lookup) {
  1169. block.d(1);
  1170. lookup.delete(block.key);
  1171. }
  1172. function outro_and_destroy_block(block, lookup) {
  1173. transition_out(block, 1, 1, () => {
  1174. lookup.delete(block.key);
  1175. });
  1176. }
  1177. function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block2, next, get_context) {
  1178. let o = old_blocks.length;
  1179. let n = list.length;
  1180. let i = o;
  1181. const old_indexes = {};
  1182. while (i--)
  1183. old_indexes[old_blocks[i].key] = i;
  1184. const new_blocks = [];
  1185. const new_lookup = /* @__PURE__ */ new Map();
  1186. const deltas = /* @__PURE__ */ new Map();
  1187. i = n;
  1188. while (i--) {
  1189. const child_ctx = get_context(ctx, list, i);
  1190. const key = get_key(child_ctx);
  1191. let block = lookup.get(key);
  1192. if (!block) {
  1193. block = create_each_block2(key, child_ctx);
  1194. block.c();
  1195. } else if (dynamic) {
  1196. block.p(child_ctx, dirty);
  1197. }
  1198. new_lookup.set(key, new_blocks[i] = block);
  1199. if (key in old_indexes)
  1200. deltas.set(key, Math.abs(i - old_indexes[key]));
  1201. }
  1202. const will_move = /* @__PURE__ */ new Set();
  1203. const did_move = /* @__PURE__ */ new Set();
  1204. function insert2(block) {
  1205. transition_in(block, 1);
  1206. block.m(node, next);
  1207. lookup.set(block.key, block);
  1208. next = block.first;
  1209. n--;
  1210. }
  1211. while (o && n) {
  1212. const new_block = new_blocks[n - 1];
  1213. const old_block = old_blocks[o - 1];
  1214. const new_key = new_block.key;
  1215. const old_key = old_block.key;
  1216. if (new_block === old_block) {
  1217. next = new_block.first;
  1218. o--;
  1219. n--;
  1220. } else if (!new_lookup.has(old_key)) {
  1221. destroy(old_block, lookup);
  1222. o--;
  1223. } else if (!lookup.has(new_key) || will_move.has(new_key)) {
  1224. insert2(new_block);
  1225. } else if (did_move.has(old_key)) {
  1226. o--;
  1227. } else if (deltas.get(new_key) > deltas.get(old_key)) {
  1228. did_move.add(new_key);
  1229. insert2(new_block);
  1230. } else {
  1231. will_move.add(old_key);
  1232. o--;
  1233. }
  1234. }
  1235. while (o--) {
  1236. const old_block = old_blocks[o];
  1237. if (!new_lookup.has(old_block.key))
  1238. destroy(old_block, lookup);
  1239. }
  1240. while (n)
  1241. insert2(new_blocks[n - 1]);
  1242. return new_blocks;
  1243. }
  1244. function get_spread_update(levels, updates) {
  1245. const update2 = {};
  1246. const to_null_out = {};
  1247. const accounted_for = { $$scope: 1 };
  1248. let i = levels.length;
  1249. while (i--) {
  1250. const o = levels[i];
  1251. const n = updates[i];
  1252. if (n) {
  1253. for (const key in o) {
  1254. if (!(key in n))
  1255. to_null_out[key] = 1;
  1256. }
  1257. for (const key in n) {
  1258. if (!accounted_for[key]) {
  1259. update2[key] = n[key];
  1260. accounted_for[key] = 1;
  1261. }
  1262. }
  1263. levels[i] = n;
  1264. } else {
  1265. for (const key in o) {
  1266. accounted_for[key] = 1;
  1267. }
  1268. }
  1269. }
  1270. for (const key in to_null_out) {
  1271. if (!(key in update2))
  1272. update2[key] = void 0;
  1273. }
  1274. return update2;
  1275. }
  1276. function get_spread_object(spread_props) {
  1277. return typeof spread_props === "object" && spread_props !== null ? spread_props : {};
  1278. }
  1279. function bind(component, name, callback) {
  1280. const index = component.$$.props[name];
  1281. if (index !== void 0) {
  1282. component.$$.bound[index] = callback;
  1283. callback(component.$$.ctx[index]);
  1284. }
  1285. }
  1286. function create_component(block) {
  1287. block && block.c();
  1288. }
  1289. function mount_component(component, target, anchor, customElement) {
  1290. const { fragment, after_update } = component.$$;
  1291. fragment && fragment.m(target, anchor);
  1292. if (!customElement) {
  1293. add_render_callback(() => {
  1294. const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
  1295. if (component.$$.on_destroy) {
  1296. component.$$.on_destroy.push(...new_on_destroy);
  1297. } else {
  1298. run_all(new_on_destroy);
  1299. }
  1300. component.$$.on_mount = [];
  1301. });
  1302. }
  1303. after_update.forEach(add_render_callback);
  1304. }
  1305. function destroy_component(component, detaching) {
  1306. const $$ = component.$$;
  1307. if ($$.fragment !== null) {
  1308. run_all($$.on_destroy);
  1309. $$.fragment && $$.fragment.d(detaching);
  1310. $$.on_destroy = $$.fragment = null;
  1311. $$.ctx = [];
  1312. }
  1313. }
  1314. function make_dirty(component, i) {
  1315. if (component.$$.dirty[0] === -1) {
  1316. dirty_components.push(component);
  1317. schedule_update();
  1318. component.$$.dirty.fill(0);
  1319. }
  1320. component.$$.dirty[i / 31 | 0] |= 1 << i % 31;
  1321. }
  1322. function init(component, options, instance2, create_fragment2, not_equal, props, append_styles, dirty = [-1]) {
  1323. const parent_component = current_component;
  1324. set_current_component(component);
  1325. const $$ = component.$$ = {
  1326. fragment: null,
  1327. ctx: [],
  1328. // state
  1329. props,
  1330. update: noop,
  1331. not_equal,
  1332. bound: blank_object(),
  1333. // lifecycle
  1334. on_mount: [],
  1335. on_destroy: [],
  1336. on_disconnect: [],
  1337. before_update: [],
  1338. after_update: [],
  1339. context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
  1340. // everything else
  1341. callbacks: blank_object(),
  1342. dirty,
  1343. skip_bound: false,
  1344. root: options.target || parent_component.$$.root
  1345. };
  1346. append_styles && append_styles($$.root);
  1347. let ready = false;
  1348. $$.ctx = instance2 ? instance2(component, options.props || {}, (i, ret, ...rest) => {
  1349. const value = rest.length ? rest[0] : ret;
  1350. if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
  1351. if (!$$.skip_bound && $$.bound[i])
  1352. $$.bound[i](value);
  1353. if (ready)
  1354. make_dirty(component, i);
  1355. }
  1356. return ret;
  1357. }) : [];
  1358. $$.update();
  1359. ready = true;
  1360. run_all($$.before_update);
  1361. $$.fragment = create_fragment2 ? create_fragment2($$.ctx) : false;
  1362. if (options.target) {
  1363. if (options.hydrate) {
  1364. const nodes = children(options.target);
  1365. $$.fragment && $$.fragment.l(nodes);
  1366. nodes.forEach(detach);
  1367. } else {
  1368. $$.fragment && $$.fragment.c();
  1369. }
  1370. if (options.intro)
  1371. transition_in(component.$$.fragment);
  1372. mount_component(component, options.target, options.anchor, options.customElement);
  1373. flush();
  1374. }
  1375. set_current_component(parent_component);
  1376. }
  1377. class SvelteComponent {
  1378. $destroy() {
  1379. destroy_component(this, 1);
  1380. this.$destroy = noop;
  1381. }
  1382. $on(type, callback) {
  1383. if (!is_function(callback)) {
  1384. return noop;
  1385. }
  1386. const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []);
  1387. callbacks.push(callback);
  1388. return () => {
  1389. const index = callbacks.indexOf(callback);
  1390. if (index !== -1)
  1391. callbacks.splice(index, 1);
  1392. };
  1393. }
  1394. $set($$props) {
  1395. if (this.$$set && !is_empty($$props)) {
  1396. this.$$.skip_bound = true;
  1397. this.$$set($$props);
  1398. this.$$.skip_bound = false;
  1399. }
  1400. }
  1401. }
  1402. const s_TAG_OBJECT = "[object Object]";
  1403. function deepMerge(target = {}, ...sourceObj) {
  1404. if (Object.prototype.toString.call(target) !== s_TAG_OBJECT) {
  1405. throw new TypeError(`deepMerge error: 'target' is not an 'object'.`);
  1406. }
  1407. for (let cntr = 0; cntr < sourceObj.length; cntr++) {
  1408. if (Object.prototype.toString.call(sourceObj[cntr]) !== s_TAG_OBJECT) {
  1409. throw new TypeError(`deepMerge error: 'sourceObj[${cntr}]' is not an 'object'.`);
  1410. }
  1411. }
  1412. return _deepMerge(target, ...sourceObj);
  1413. }
  1414. function isIterable(value) {
  1415. if (value === null || value === void 0 || typeof value !== "object") {
  1416. return false;
  1417. }
  1418. return typeof value[Symbol.iterator] === "function";
  1419. }
  1420. function isObject(value) {
  1421. return value !== null && typeof value === "object";
  1422. }
  1423. function isPlainObject(value) {
  1424. if (Object.prototype.toString.call(value) !== s_TAG_OBJECT) {
  1425. return false;
  1426. }
  1427. const prototype = Object.getPrototypeOf(value);
  1428. return prototype === null || prototype === Object.prototype;
  1429. }
  1430. function safeAccess(data, accessor, defaultValue = void 0) {
  1431. if (typeof data !== "object") {
  1432. return defaultValue;
  1433. }
  1434. if (typeof accessor !== "string") {
  1435. return defaultValue;
  1436. }
  1437. const access = accessor.split(".");
  1438. for (let cntr = 0; cntr < access.length; cntr++) {
  1439. if (typeof data[access[cntr]] === "undefined" || data[access[cntr]] === null) {
  1440. return defaultValue;
  1441. }
  1442. data = data[access[cntr]];
  1443. }
  1444. return data;
  1445. }
  1446. function safeSet(data, accessor, value, operation = "set", createMissing = true) {
  1447. if (typeof data !== "object") {
  1448. throw new TypeError(`safeSet Error: 'data' is not an 'object'.`);
  1449. }
  1450. if (typeof accessor !== "string") {
  1451. throw new TypeError(`safeSet Error: 'accessor' is not a 'string'.`);
  1452. }
  1453. const access = accessor.split(".");
  1454. for (let cntr = 0; cntr < access.length; cntr++) {
  1455. if (Array.isArray(data)) {
  1456. const number = +access[cntr];
  1457. if (!Number.isInteger(number) || number < 0) {
  1458. return false;
  1459. }
  1460. }
  1461. if (cntr === access.length - 1) {
  1462. switch (operation) {
  1463. case "add":
  1464. data[access[cntr]] += value;
  1465. break;
  1466. case "div":
  1467. data[access[cntr]] /= value;
  1468. break;
  1469. case "mult":
  1470. data[access[cntr]] *= value;
  1471. break;
  1472. case "set":
  1473. data[access[cntr]] = value;
  1474. break;
  1475. case "set-undefined":
  1476. if (typeof data[access[cntr]] === "undefined") {
  1477. data[access[cntr]] = value;
  1478. }
  1479. break;
  1480. case "sub":
  1481. data[access[cntr]] -= value;
  1482. break;
  1483. }
  1484. } else {
  1485. if (createMissing && typeof data[access[cntr]] === "undefined") {
  1486. data[access[cntr]] = {};
  1487. }
  1488. if (data[access[cntr]] === null || typeof data[access[cntr]] !== "object") {
  1489. return false;
  1490. }
  1491. data = data[access[cntr]];
  1492. }
  1493. }
  1494. return true;
  1495. }
  1496. function _deepMerge(target = {}, ...sourceObj) {
  1497. for (let cntr = 0; cntr < sourceObj.length; cntr++) {
  1498. const obj = sourceObj[cntr];
  1499. for (const prop in obj) {
  1500. if (Object.prototype.hasOwnProperty.call(obj, prop)) {
  1501. if (prop.startsWith("-=")) {
  1502. delete target[prop.slice(2)];
  1503. continue;
  1504. }
  1505. target[prop] = Object.prototype.hasOwnProperty.call(target, prop) && target[prop]?.constructor === Object && obj[prop]?.constructor === Object ? _deepMerge({}, target[prop], obj[prop]) : obj[prop];
  1506. }
  1507. }
  1508. }
  1509. return target;
  1510. }
  1511. class A11yHelper {
  1512. /**
  1513. * Apply focus to the HTMLElement targets in a given A11yFocusSource data object. An iterable list `options.focusEl`
  1514. * can contain HTMLElements or selector strings. If multiple focus targets are provided in a list then the first
  1515. * valid target found will be focused. If focus target is a string then a lookup via `document.querySelector` is
  1516. * performed. In this case you should provide a unique selector for the desired focus target.
  1517. *
  1518. * Note: The body of this method is postponed to the next clock tick to allow any changes in the DOM to occur that
  1519. * might alter focus targets before applying.
  1520. *
  1521. * @param {A11yFocusSource|{ focusSource: A11yFocusSource }} options - The focus options instance to apply.
  1522. */
  1523. static applyFocusSource(options) {
  1524. if (!isObject(options)) {
  1525. return;
  1526. }
  1527. const focusOpts = isObject(options?.focusSource) ? options.focusSource : options;
  1528. setTimeout(() => {
  1529. const debug2 = typeof focusOpts.debug === "boolean" ? focusOpts.debug : false;
  1530. if (isIterable(focusOpts.focusEl)) {
  1531. if (debug2) {
  1532. console.debug(`A11yHelper.applyFocusSource debug - Attempting to apply focus target: `, focusOpts.focusEl);
  1533. }
  1534. for (const target of focusOpts.focusEl) {
  1535. if (target instanceof HTMLElement && target.isConnected) {
  1536. target.focus();
  1537. if (debug2) {
  1538. console.debug(`A11yHelper.applyFocusSource debug - Applied focus to target: `, target);
  1539. }
  1540. break;
  1541. } else if (typeof target === "string") {
  1542. const element2 = document.querySelector(target);
  1543. if (element2 instanceof HTMLElement && element2.isConnected) {
  1544. element2.focus();
  1545. if (debug2) {
  1546. console.debug(`A11yHelper.applyFocusSource debug - Applied focus to target: `, element2);
  1547. }
  1548. break;
  1549. } else if (debug2) {
  1550. console.debug(`A11yHelper.applyFocusSource debug - Could not query selector: `, target);
  1551. }
  1552. }
  1553. }
  1554. } else if (debug2) {
  1555. console.debug(`A11yHelper.applyFocusSource debug - No focus targets defined.`);
  1556. }
  1557. }, 0);
  1558. }
  1559. /**
  1560. * Returns first focusable element within a specified element.
  1561. *
  1562. * @param {HTMLElement|Document} [element=document] - Optional element to start query.
  1563. *
  1564. * @param {object} [options] - Optional parameters.
  1565. *
  1566. * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
  1567. *
  1568. * @param {Set<HTMLElement>} [options.ignoreElements] - Set of elements to ignore.
  1569. *
  1570. * @returns {HTMLElement} First focusable child element
  1571. */
  1572. static getFirstFocusableElement(element2 = document, options) {
  1573. const focusableElements = this.getFocusableElements(element2, options);
  1574. return focusableElements.length > 0 ? focusableElements[0] : void 0;
  1575. }
  1576. /**
  1577. * Returns all focusable elements within a specified element.
  1578. *
  1579. * @param {HTMLElement|Document} [element=document] Optional element to start query.
  1580. *
  1581. * @param {object} [options] - Optional parameters.
  1582. *
  1583. * @param {boolean} [options.anchorHref=true] - When true anchors must have an HREF.
  1584. *
  1585. * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
  1586. *
  1587. * @param {Set<HTMLElement>} [options.ignoreElements] - Set of elements to ignore.
  1588. *
  1589. * @param {string} [options.selectors] - Custom list of focusable selectors for `querySelectorAll`.
  1590. *
  1591. * @returns {Array<HTMLElement>} Child keyboard focusable
  1592. */
  1593. static getFocusableElements(element2 = document, { anchorHref = true, ignoreClasses, ignoreElements, selectors } = {}) {
  1594. if (!(element2 instanceof HTMLElement) && !(element2 instanceof Document)) {
  1595. throw new TypeError(`'element' is not a HTMLElement or Document instance.`);
  1596. }
  1597. if (typeof anchorHref !== "boolean") {
  1598. throw new TypeError(`'anchorHref' is not a boolean.`);
  1599. }
  1600. if (ignoreClasses !== void 0 && !isIterable(ignoreClasses)) {
  1601. throw new TypeError(`'ignoreClasses' is not an iterable list.`);
  1602. }
  1603. if (ignoreElements !== void 0 && !(ignoreElements instanceof Set)) {
  1604. throw new TypeError(`'ignoreElements' is not a Set.`);
  1605. }
  1606. if (selectors !== void 0 && typeof selectors !== "string") {
  1607. throw new TypeError(`'selectors' is not a string.`);
  1608. }
  1609. const selectorQuery = selectors ?? this.#getFocusableSelectors(anchorHref);
  1610. const allElements = [...element2.querySelectorAll(selectorQuery)];
  1611. if (ignoreElements && ignoreClasses) {
  1612. return allElements.filter((el) => {
  1613. let hasIgnoreClass = false;
  1614. for (const ignoreClass of ignoreClasses) {
  1615. if (el.classList.contains(ignoreClass)) {
  1616. hasIgnoreClass = true;
  1617. break;
  1618. }
  1619. }
  1620. return !hasIgnoreClass && !ignoreElements.has(el) && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1621. });
  1622. } else if (ignoreClasses) {
  1623. return allElements.filter((el) => {
  1624. let hasIgnoreClass = false;
  1625. for (const ignoreClass of ignoreClasses) {
  1626. if (el.classList.contains(ignoreClass)) {
  1627. hasIgnoreClass = true;
  1628. break;
  1629. }
  1630. }
  1631. return !hasIgnoreClass && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1632. });
  1633. } else if (ignoreElements) {
  1634. return allElements.filter((el) => {
  1635. return !ignoreElements.has(el) && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1636. });
  1637. } else {
  1638. return allElements.filter((el) => {
  1639. return el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1640. });
  1641. }
  1642. }
  1643. /**
  1644. * Returns the default focusable selectors query.
  1645. *
  1646. * @param {boolean} [anchorHref=true] - When true anchors must have an HREF.
  1647. *
  1648. * @returns {string} Focusable selectors for `querySelectorAll`.
  1649. */
  1650. static #getFocusableSelectors(anchorHref = true) {
  1651. 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"])`;
  1652. }
  1653. /**
  1654. * Gets a A11yFocusSource object from the given DOM event allowing for optional X / Y screen space overrides.
  1655. * Browsers (Firefox / Chrome) forwards a mouse event for the context menu keyboard button. Provides detection of
  1656. * when the context menu event is from the keyboard. Firefox as of (1/23) does not provide the correct screen space
  1657. * coordinates, so for keyboard context menu presses coordinates are generated from the centroid point of the
  1658. * element.
  1659. *
  1660. * A default fallback element or selector string may be provided to provide the focus target. If the event comes from
  1661. * the keyboard however the source focused element is inserted as the target with the fallback value appended to the
  1662. * list of focus targets. When A11yFocusSource is applied by {@link A11yHelper.applyFocusSource} the target focus
  1663. * list is iterated through until a connected target is found and focus applied.
  1664. *
  1665. * @param {object} options - Options
  1666. *
  1667. * @param {KeyboardEvent|MouseEvent} [options.event] - The source DOM event.
  1668. *
  1669. * @param {boolean} [options.debug] - When true {@link A11yHelper.applyFocusSource} logs focus target data.
  1670. *
  1671. * @param {HTMLElement|string} [options.focusEl] - A specific HTMLElement or selector string as the focus target.
  1672. *
  1673. * @param {number} [options.x] - Used when an event isn't provided; integer of event source in screen space.
  1674. *
  1675. * @param {number} [options.y] - Used when an event isn't provided; integer of event source in screen space.
  1676. *
  1677. * @returns {A11yFocusSource} A A11yFocusSource object.
  1678. *
  1679. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1426671
  1680. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=314314
  1681. *
  1682. * TODO: Evaluate / test against touch input devices.
  1683. */
  1684. static getFocusSource({ event, x, y, focusEl, debug: debug2 = false }) {
  1685. if (focusEl !== void 0 && !(focusEl instanceof HTMLElement) && typeof focusEl !== "string") {
  1686. throw new TypeError(
  1687. `A11yHelper.getFocusSource error: 'focusEl' is not a HTMLElement or string.`
  1688. );
  1689. }
  1690. if (debug2 !== void 0 && typeof debug2 !== "boolean") {
  1691. throw new TypeError(`A11yHelper.getFocusSource error: 'debug' is not a boolean.`);
  1692. }
  1693. if (event === void 0) {
  1694. if (typeof x !== "number") {
  1695. throw new TypeError(`A11yHelper.getFocusSource error: 'event' not defined and 'x' is not a number.`);
  1696. }
  1697. if (typeof y !== "number") {
  1698. throw new TypeError(`A11yHelper.getFocusSource error: 'event' not defined and 'y' is not a number.`);
  1699. }
  1700. return {
  1701. debug: debug2,
  1702. focusEl: focusEl !== void 0 ? [focusEl] : void 0,
  1703. x,
  1704. y
  1705. };
  1706. }
  1707. if (!(event instanceof KeyboardEvent) && !(event instanceof MouseEvent)) {
  1708. throw new TypeError(`A11yHelper.getFocusSource error: 'event' is not a KeyboardEvent or MouseEvent.`);
  1709. }
  1710. if (x !== void 0 && !Number.isInteger(x)) {
  1711. throw new TypeError(`A11yHelper.getFocusSource error: 'x' is not a number.`);
  1712. }
  1713. if (y !== void 0 && !Number.isInteger(y)) {
  1714. throw new TypeError(`A11yHelper.getFocusSource error: 'y' is not a number.`);
  1715. }
  1716. const targetEl = event.target;
  1717. if (!(targetEl instanceof HTMLElement)) {
  1718. throw new TypeError(`A11yHelper.getFocusSource error: 'event.target' is not an HTMLElement.`);
  1719. }
  1720. const result = { debug: debug2 };
  1721. if (event instanceof MouseEvent) {
  1722. if (event?.button !== 2 && event.type === "contextmenu") {
  1723. const rect = targetEl.getBoundingClientRect();
  1724. result.x = x ?? rect.left + rect.width / 2;
  1725. result.y = y ?? rect.top + rect.height / 2;
  1726. result.focusEl = focusEl !== void 0 ? [targetEl, focusEl] : [targetEl];
  1727. result.source = "keyboard";
  1728. } else {
  1729. result.x = x ?? event.pageX;
  1730. result.y = y ?? event.pageY;
  1731. result.focusEl = focusEl !== void 0 ? [focusEl] : void 0;
  1732. }
  1733. } else {
  1734. const rect = targetEl.getBoundingClientRect();
  1735. result.x = x ?? rect.left + rect.width / 2;
  1736. result.y = y ?? rect.top + rect.height / 2;
  1737. result.focusEl = focusEl !== void 0 ? [targetEl, focusEl] : [targetEl];
  1738. result.source = "keyboard";
  1739. }
  1740. return result;
  1741. }
  1742. /**
  1743. * Returns first focusable element within a specified element.
  1744. *
  1745. * @param {HTMLElement|Document} [element=document] - Optional element to start query.
  1746. *
  1747. * @param {object} [options] - Optional parameters.
  1748. *
  1749. * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
  1750. *
  1751. * @param {Set<HTMLElement>} [options.ignoreElements] - Set of elements to ignore.
  1752. *
  1753. * @returns {HTMLElement} First focusable child element
  1754. */
  1755. static getLastFocusableElement(element2 = document, options) {
  1756. const focusableElements = this.getFocusableElements(element2, options);
  1757. return focusableElements.length > 0 ? focusableElements[focusableElements.length - 1] : void 0;
  1758. }
  1759. /**
  1760. * Tests if the given element is focusable.
  1761. *
  1762. * @param {HTMLElement} [el] - Element to test.
  1763. *
  1764. * @param {object} [options] - Optional parameters.
  1765. *
  1766. * @param {boolean} [options.anchorHref=true] - When true anchors must have an HREF.
  1767. *
  1768. * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
  1769. *
  1770. * @returns {boolean} Element is focusable.
  1771. */
  1772. static isFocusable(el, { anchorHref = true, ignoreClasses } = {}) {
  1773. if (el === void 0 || el === null || !(el instanceof HTMLElement) || el?.hidden || !el?.isConnected) {
  1774. return false;
  1775. }
  1776. if (typeof anchorHref !== "boolean") {
  1777. throw new TypeError(`'anchorHref' is not a boolean.`);
  1778. }
  1779. if (ignoreClasses !== void 0 && !isIterable(ignoreClasses)) {
  1780. throw new TypeError(`'ignoreClasses' is not an iterable list.`);
  1781. }
  1782. const contenteditableAttr = el.getAttribute("contenteditable");
  1783. const contenteditableFocusable = typeof contenteditableAttr === "string" && (contenteditableAttr === "" || contenteditableAttr === "true");
  1784. const tabindexAttr = el.getAttribute("tabindex");
  1785. const tabindexFocusable = typeof tabindexAttr === "string" && tabindexAttr !== "-1";
  1786. const isAnchor = el instanceof HTMLAnchorElement;
  1787. if (contenteditableFocusable || tabindexFocusable || isAnchor || el instanceof HTMLButtonElement || el instanceof HTMLDetailsElement || el instanceof HTMLEmbedElement || el instanceof HTMLIFrameElement || el instanceof HTMLInputElement || el instanceof HTMLObjectElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) {
  1788. if (isAnchor && anchorHref && typeof el.getAttribute("href") !== "string") {
  1789. return false;
  1790. }
  1791. return el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1792. }
  1793. return false;
  1794. }
  1795. /**
  1796. * Convenience method to check if the given data is a valid focus source.
  1797. *
  1798. * @param {HTMLElement|string} data - Either an HTMLElement or selector string.
  1799. *
  1800. * @returns {boolean} Is valid focus source.
  1801. */
  1802. static isFocusSource(data) {
  1803. return data instanceof HTMLElement || typeof data === "string";
  1804. }
  1805. }
  1806. class StyleManager {
  1807. /** @type {CSSStyleRule} */
  1808. #cssRule;
  1809. /** @type {string} */
  1810. #docKey;
  1811. /** @type {string} */
  1812. #selector;
  1813. /** @type {HTMLStyleElement} */
  1814. #styleElement;
  1815. /** @type {number} */
  1816. #version;
  1817. /**
  1818. *
  1819. * @param {object} opts - Options.
  1820. *
  1821. * @param {string} opts.docKey - Required key providing a link to a specific style sheet element.
  1822. *
  1823. * @param {string} [opts.selector=:root] - Selector element.
  1824. *
  1825. * @param {Document} [opts.document] - Target document to load styles into.
  1826. *
  1827. * @param {number} [opts.version] - An integer representing the version / level of styles being managed.
  1828. *
  1829. */
  1830. constructor({ docKey, selector = ":root", document: document2 = globalThis.document, version } = {}) {
  1831. if (typeof docKey !== "string") {
  1832. throw new TypeError(`StyleManager error: 'docKey' is not a string.`);
  1833. }
  1834. if (typeof selector !== "string") {
  1835. throw new TypeError(`StyleManager error: 'selector' is not a string.`);
  1836. }
  1837. if (version !== void 0 && !Number.isSafeInteger(version) && version < 1) {
  1838. throw new TypeError(`StyleManager error: 'version' is defined and is not a positive integer >= 1.`);
  1839. }
  1840. this.#selector = selector;
  1841. this.#docKey = docKey;
  1842. this.#version = version;
  1843. if (document2[this.#docKey] === void 0) {
  1844. this.#styleElement = document2.createElement("style");
  1845. document2.head.append(this.#styleElement);
  1846. this.#styleElement._STYLE_MANAGER_VERSION = version;
  1847. this.#styleElement.sheet.insertRule(`${selector} {}`, 0);
  1848. this.#cssRule = this.#styleElement.sheet.cssRules[0];
  1849. document2[docKey] = this.#styleElement;
  1850. } else {
  1851. this.#styleElement = document2[docKey];
  1852. this.#cssRule = this.#styleElement.sheet.cssRules[0];
  1853. if (version) {
  1854. const existingVersion = this.#styleElement._STYLE_MANAGER_VERSION ?? 0;
  1855. if (version > existingVersion) {
  1856. this.#cssRule.style.cssText = "";
  1857. }
  1858. }
  1859. }
  1860. }
  1861. /**
  1862. * @returns {string} Provides an accessor to get the `cssText` for the style sheet.
  1863. */
  1864. get cssText() {
  1865. return this.#cssRule.style.cssText;
  1866. }
  1867. /**
  1868. * @returns {number} Returns the version of this instance.
  1869. */
  1870. get version() {
  1871. return this.#version;
  1872. }
  1873. /**
  1874. * Provides a copy constructor to duplicate an existing StyleManager instance into a new document.
  1875. *
  1876. * Note: This is used to support the `PopOut` module.
  1877. *
  1878. * @param {Document} [document] Target browser document to clone into.
  1879. *
  1880. * @returns {StyleManager} New style manager instance.
  1881. */
  1882. clone(document2 = globalThis.document) {
  1883. const newStyleManager = new StyleManager({
  1884. selector: this.#selector,
  1885. docKey: this.#docKey,
  1886. document: document2,
  1887. version: this.#version
  1888. });
  1889. newStyleManager.#cssRule.style.cssText = this.#cssRule.style.cssText;
  1890. return newStyleManager;
  1891. }
  1892. get() {
  1893. const cssText = this.#cssRule.style.cssText;
  1894. const result = {};
  1895. if (cssText !== "") {
  1896. for (const entry of cssText.split(";")) {
  1897. if (entry !== "") {
  1898. const values = entry.split(":");
  1899. result[values[0].trim()] = values[1];
  1900. }
  1901. }
  1902. }
  1903. return result;
  1904. }
  1905. /**
  1906. * Gets a particular CSS variable.
  1907. *
  1908. * @param {string} key - CSS variable property key.
  1909. *
  1910. * @returns {string} Returns CSS variable value.
  1911. */
  1912. getProperty(key) {
  1913. if (typeof key !== "string") {
  1914. throw new TypeError(`StyleManager error: 'key' is not a string.`);
  1915. }
  1916. return this.#cssRule.style.getPropertyValue(key);
  1917. }
  1918. /**
  1919. * Set rules by property / value; useful for CSS variables.
  1920. *
  1921. * @param {Object<string, string>} rules - An object with property / value string pairs to load.
  1922. *
  1923. * @param {boolean} [overwrite=true] - When true overwrites any existing values.
  1924. */
  1925. setProperties(rules, overwrite = true) {
  1926. if (!isObject(rules)) {
  1927. throw new TypeError(`StyleManager error: 'rules' is not an object.`);
  1928. }
  1929. if (typeof overwrite !== "boolean") {
  1930. throw new TypeError(`StyleManager error: 'overwrite' is not a boolean.`);
  1931. }
  1932. if (overwrite) {
  1933. for (const [key, value] of Object.entries(rules)) {
  1934. this.#cssRule.style.setProperty(key, value);
  1935. }
  1936. } else {
  1937. for (const [key, value] of Object.entries(rules)) {
  1938. if (this.#cssRule.style.getPropertyValue(key) === "") {
  1939. this.#cssRule.style.setProperty(key, value);
  1940. }
  1941. }
  1942. }
  1943. }
  1944. /**
  1945. * Sets a particular property.
  1946. *
  1947. * @param {string} key - CSS variable property key.
  1948. *
  1949. * @param {string} value - CSS variable value.
  1950. *
  1951. * @param {boolean} [overwrite=true] - Overwrite any existing value.
  1952. */
  1953. setProperty(key, value, overwrite = true) {
  1954. if (typeof key !== "string") {
  1955. throw new TypeError(`StyleManager error: 'key' is not a string.`);
  1956. }
  1957. if (typeof value !== "string") {
  1958. throw new TypeError(`StyleManager error: 'value' is not a string.`);
  1959. }
  1960. if (typeof overwrite !== "boolean") {
  1961. throw new TypeError(`StyleManager error: 'overwrite' is not a boolean.`);
  1962. }
  1963. if (overwrite) {
  1964. this.#cssRule.style.setProperty(key, value);
  1965. } else {
  1966. if (this.#cssRule.style.getPropertyValue(key) === "") {
  1967. this.#cssRule.style.setProperty(key, value);
  1968. }
  1969. }
  1970. }
  1971. /**
  1972. * Removes the property keys specified. If `keys` is an iterable list then all property keys in the list are removed.
  1973. *
  1974. * @param {Iterable<string>} keys - The property keys to remove.
  1975. */
  1976. removeProperties(keys) {
  1977. if (!isIterable(keys)) {
  1978. throw new TypeError(`StyleManager error: 'keys' is not an iterable list.`);
  1979. }
  1980. for (const key of keys) {
  1981. if (typeof key === "string") {
  1982. this.#cssRule.style.removeProperty(key);
  1983. }
  1984. }
  1985. }
  1986. /**
  1987. * Removes a particular CSS variable.
  1988. *
  1989. * @param {string} key - CSS variable property key.
  1990. *
  1991. * @returns {string} CSS variable value when removed.
  1992. */
  1993. removeProperty(key) {
  1994. if (typeof key !== "string") {
  1995. throw new TypeError(`StyleManager error: 'key' is not a string.`);
  1996. }
  1997. return this.#cssRule.style.removeProperty(key);
  1998. }
  1999. }
  2000. const s_REGEX = /(\d+)\s*px/;
  2001. function styleParsePixels(value) {
  2002. if (typeof value !== "string") {
  2003. return void 0;
  2004. }
  2005. const isPixels = s_REGEX.test(value);
  2006. const number = parseInt(value);
  2007. return isPixels && Number.isFinite(number) ? number : void 0;
  2008. }
  2009. const applicationShellContract = ["elementRoot"];
  2010. Object.freeze(applicationShellContract);
  2011. function isApplicationShell(component) {
  2012. if (component === null || component === void 0) {
  2013. return false;
  2014. }
  2015. let compHasContract = true;
  2016. let protoHasContract = true;
  2017. for (const accessor of applicationShellContract) {
  2018. const descriptor = Object.getOwnPropertyDescriptor(component, accessor);
  2019. if (descriptor === void 0 || descriptor.get === void 0 || descriptor.set === void 0) {
  2020. compHasContract = false;
  2021. }
  2022. }
  2023. const prototype = Object.getPrototypeOf(component);
  2024. for (const accessor of applicationShellContract) {
  2025. const descriptor = Object.getOwnPropertyDescriptor(prototype, accessor);
  2026. if (descriptor === void 0 || descriptor.get === void 0 || descriptor.set === void 0) {
  2027. protoHasContract = false;
  2028. }
  2029. }
  2030. return compHasContract || protoHasContract;
  2031. }
  2032. function isHMRProxy(comp) {
  2033. const instanceName = comp?.constructor?.name;
  2034. if (typeof instanceName === "string" && (instanceName.startsWith("Proxy<") || instanceName === "ProxyComponent")) {
  2035. return true;
  2036. }
  2037. const prototypeName = comp?.prototype?.constructor?.name;
  2038. return typeof prototypeName === "string" && (prototypeName.startsWith("Proxy<") || prototypeName === "ProxyComponent");
  2039. }
  2040. function isSvelteComponent(comp) {
  2041. if (comp === null || comp === void 0 || typeof comp !== "function") {
  2042. return false;
  2043. }
  2044. const prototypeName = comp?.prototype?.constructor?.name;
  2045. if (typeof prototypeName === "string" && (prototypeName.startsWith("Proxy<") || prototypeName === "ProxyComponent")) {
  2046. return true;
  2047. }
  2048. return typeof window !== void 0 ? typeof comp.prototype.$destroy === "function" && typeof comp.prototype.$on === "function" : (
  2049. // client-side
  2050. typeof comp.render === "function"
  2051. );
  2052. }
  2053. async function outroAndDestroy(instance2) {
  2054. return new Promise((resolve) => {
  2055. if (instance2.$$.fragment && instance2.$$.fragment.o) {
  2056. group_outros();
  2057. transition_out(instance2.$$.fragment, 0, 0, () => {
  2058. instance2.$destroy();
  2059. resolve();
  2060. });
  2061. check_outros();
  2062. } else {
  2063. instance2.$destroy();
  2064. resolve();
  2065. }
  2066. });
  2067. }
  2068. function parseSvelteConfig(config, thisArg = void 0) {
  2069. if (typeof config !== "object") {
  2070. throw new TypeError(`parseSvelteConfig - 'config' is not an object:
  2071. ${JSON.stringify(config)}.`);
  2072. }
  2073. if (!isSvelteComponent(config.class)) {
  2074. throw new TypeError(
  2075. `parseSvelteConfig - 'class' is not a Svelte component constructor for config:
  2076. ${JSON.stringify(config)}.`
  2077. );
  2078. }
  2079. if (config.hydrate !== void 0 && typeof config.hydrate !== "boolean") {
  2080. throw new TypeError(
  2081. `parseSvelteConfig - 'hydrate' is not a boolean for config:
  2082. ${JSON.stringify(config)}.`
  2083. );
  2084. }
  2085. if (config.intro !== void 0 && typeof config.intro !== "boolean") {
  2086. throw new TypeError(
  2087. `parseSvelteConfig - 'intro' is not a boolean for config:
  2088. ${JSON.stringify(config)}.`
  2089. );
  2090. }
  2091. if (config.target !== void 0 && typeof config.target !== "string" && !(config.target instanceof HTMLElement) && !(config.target instanceof ShadowRoot) && !(config.target instanceof DocumentFragment)) {
  2092. throw new TypeError(
  2093. `parseSvelteConfig - 'target' is not a string, HTMLElement, ShadowRoot, or DocumentFragment for config:
  2094. ${JSON.stringify(config)}.`
  2095. );
  2096. }
  2097. if (config.anchor !== void 0 && typeof config.anchor !== "string" && !(config.anchor instanceof HTMLElement) && !(config.anchor instanceof ShadowRoot) && !(config.anchor instanceof DocumentFragment)) {
  2098. throw new TypeError(
  2099. `parseSvelteConfig - 'anchor' is not a string, HTMLElement, ShadowRoot, or DocumentFragment for config:
  2100. ${JSON.stringify(config)}.`
  2101. );
  2102. }
  2103. if (config.context !== void 0 && typeof config.context !== "function" && !(config.context instanceof Map) && typeof config.context !== "object") {
  2104. throw new TypeError(
  2105. `parseSvelteConfig - 'context' is not a Map, function or object for config:
  2106. ${JSON.stringify(config)}.`
  2107. );
  2108. }
  2109. if (config.selectorTarget !== void 0 && typeof config.selectorTarget !== "string") {
  2110. throw new TypeError(
  2111. `parseSvelteConfig - 'selectorTarget' is not a string for config:
  2112. ${JSON.stringify(config)}.`
  2113. );
  2114. }
  2115. if (config.options !== void 0 && typeof config.options !== "object") {
  2116. throw new TypeError(
  2117. `parseSvelteConfig - 'options' is not an object for config:
  2118. ${JSON.stringify(config)}.`
  2119. );
  2120. }
  2121. if (config.options !== void 0) {
  2122. if (config.options.injectApp !== void 0 && typeof config.options.injectApp !== "boolean") {
  2123. throw new TypeError(
  2124. `parseSvelteConfig - 'options.injectApp' is not a boolean for config:
  2125. ${JSON.stringify(config)}.`
  2126. );
  2127. }
  2128. if (config.options.injectEventbus !== void 0 && typeof config.options.injectEventbus !== "boolean") {
  2129. throw new TypeError(
  2130. `parseSvelteConfig - 'options.injectEventbus' is not a boolean for config:
  2131. ${JSON.stringify(config)}.`
  2132. );
  2133. }
  2134. if (config.options.selectorElement !== void 0 && typeof config.options.selectorElement !== "string") {
  2135. throw new TypeError(
  2136. `parseSvelteConfig - 'selectorElement' is not a string for config:
  2137. ${JSON.stringify(config)}.`
  2138. );
  2139. }
  2140. }
  2141. const svelteConfig = { ...config };
  2142. delete svelteConfig.options;
  2143. let externalContext = {};
  2144. if (typeof svelteConfig.context === "function") {
  2145. const contextFunc = svelteConfig.context;
  2146. delete svelteConfig.context;
  2147. const result = contextFunc.call(thisArg);
  2148. if (isObject(result)) {
  2149. externalContext = { ...result };
  2150. } else {
  2151. throw new Error(`parseSvelteConfig - 'context' is a function that did not return an object for config:
  2152. ${JSON.stringify(config)}`);
  2153. }
  2154. } else if (svelteConfig.context instanceof Map) {
  2155. externalContext = Object.fromEntries(svelteConfig.context);
  2156. delete svelteConfig.context;
  2157. } else if (isObject(svelteConfig.context)) {
  2158. externalContext = svelteConfig.context;
  2159. delete svelteConfig.context;
  2160. }
  2161. svelteConfig.props = s_PROCESS_PROPS(svelteConfig.props, thisArg, config);
  2162. if (Array.isArray(svelteConfig.children)) {
  2163. const children2 = [];
  2164. for (let cntr = 0; cntr < svelteConfig.children.length; cntr++) {
  2165. const child = svelteConfig.children[cntr];
  2166. if (!isSvelteComponent(child.class)) {
  2167. throw new Error(`parseSvelteConfig - 'class' is not a Svelte component for child[${cntr}] for config:
  2168. ${JSON.stringify(config)}`);
  2169. }
  2170. child.props = s_PROCESS_PROPS(child.props, thisArg, config);
  2171. children2.push(child);
  2172. }
  2173. if (children2.length > 0) {
  2174. externalContext.children = children2;
  2175. }
  2176. delete svelteConfig.children;
  2177. } else if (isObject(svelteConfig.children)) {
  2178. if (!isSvelteComponent(svelteConfig.children.class)) {
  2179. throw new Error(`parseSvelteConfig - 'class' is not a Svelte component for children object for config:
  2180. ${JSON.stringify(config)}`);
  2181. }
  2182. svelteConfig.children.props = s_PROCESS_PROPS(svelteConfig.children.props, thisArg, config);
  2183. externalContext.children = [svelteConfig.children];
  2184. delete svelteConfig.children;
  2185. }
  2186. if (!(svelteConfig.context instanceof Map)) {
  2187. svelteConfig.context = /* @__PURE__ */ new Map();
  2188. }
  2189. svelteConfig.context.set("#external", externalContext);
  2190. return svelteConfig;
  2191. }
  2192. function s_PROCESS_PROPS(props, thisArg, config) {
  2193. if (typeof props === "function") {
  2194. const result = props.call(thisArg);
  2195. if (isObject(result)) {
  2196. return result;
  2197. } else {
  2198. throw new Error(`parseSvelteConfig - 'props' is a function that did not return an object for config:
  2199. ${JSON.stringify(config)}`);
  2200. }
  2201. } else if (isObject(props)) {
  2202. return props;
  2203. } else if (props !== void 0) {
  2204. throw new Error(
  2205. `parseSvelteConfig - 'props' is not a function or an object for config:
  2206. ${JSON.stringify(config)}`
  2207. );
  2208. }
  2209. return {};
  2210. }
  2211. function hasGetter(object, accessor) {
  2212. if (object === null || object === void 0) {
  2213. return false;
  2214. }
  2215. const iDescriptor = Object.getOwnPropertyDescriptor(object, accessor);
  2216. if (iDescriptor !== void 0 && iDescriptor.get !== void 0) {
  2217. return true;
  2218. }
  2219. for (let o = Object.getPrototypeOf(object); o; o = Object.getPrototypeOf(o)) {
  2220. const descriptor = Object.getOwnPropertyDescriptor(o, accessor);
  2221. if (descriptor !== void 0 && descriptor.get !== void 0) {
  2222. return true;
  2223. }
  2224. }
  2225. return false;
  2226. }
  2227. const subscriber_queue = [];
  2228. function readable(value, start) {
  2229. return {
  2230. subscribe: writable$1(value, start).subscribe
  2231. };
  2232. }
  2233. function writable$1(value, start = noop) {
  2234. let stop;
  2235. const subscribers = /* @__PURE__ */ new Set();
  2236. function set(new_value) {
  2237. if (safe_not_equal(value, new_value)) {
  2238. value = new_value;
  2239. if (stop) {
  2240. const run_queue = !subscriber_queue.length;
  2241. for (const subscriber of subscribers) {
  2242. subscriber[1]();
  2243. subscriber_queue.push(subscriber, value);
  2244. }
  2245. if (run_queue) {
  2246. for (let i = 0; i < subscriber_queue.length; i += 2) {
  2247. subscriber_queue[i][0](subscriber_queue[i + 1]);
  2248. }
  2249. subscriber_queue.length = 0;
  2250. }
  2251. }
  2252. }
  2253. }
  2254. function update2(fn) {
  2255. set(fn(value));
  2256. }
  2257. function subscribe2(run2, invalidate = noop) {
  2258. const subscriber = [run2, invalidate];
  2259. subscribers.add(subscriber);
  2260. if (subscribers.size === 1) {
  2261. stop = start(set) || noop;
  2262. }
  2263. run2(value);
  2264. return () => {
  2265. subscribers.delete(subscriber);
  2266. if (subscribers.size === 0) {
  2267. stop();
  2268. stop = null;
  2269. }
  2270. };
  2271. }
  2272. return { set, update: update2, subscribe: subscribe2 };
  2273. }
  2274. function derived(stores, fn, initial_value) {
  2275. const single = !Array.isArray(stores);
  2276. const stores_array = single ? [stores] : stores;
  2277. const auto = fn.length < 2;
  2278. return readable(initial_value, (set) => {
  2279. let inited = false;
  2280. const values = [];
  2281. let pending = 0;
  2282. let cleanup = noop;
  2283. const sync = () => {
  2284. if (pending) {
  2285. return;
  2286. }
  2287. cleanup();
  2288. const result = fn(single ? values[0] : values, set);
  2289. if (auto) {
  2290. set(result);
  2291. } else {
  2292. cleanup = is_function(result) ? result : noop;
  2293. }
  2294. };
  2295. const unsubscribers = stores_array.map((store, i) => subscribe(store, (value) => {
  2296. values[i] = value;
  2297. pending &= ~(1 << i);
  2298. if (inited) {
  2299. sync();
  2300. }
  2301. }, () => {
  2302. pending |= 1 << i;
  2303. }));
  2304. inited = true;
  2305. sync();
  2306. return function stop() {
  2307. run_all(unsubscribers);
  2308. cleanup();
  2309. };
  2310. });
  2311. }
  2312. function isSimpleDeriver(deriver) {
  2313. return deriver.length < 2;
  2314. }
  2315. function generator(storage2) {
  2316. function readable2(key, value, start) {
  2317. return {
  2318. subscribe: writable2(key, value, start).subscribe
  2319. };
  2320. }
  2321. function writable2(key, value, start = noop) {
  2322. function wrap_start(ogSet) {
  2323. return start(function wrap_set(new_value) {
  2324. if (storage2) {
  2325. storage2.setItem(key, JSON.stringify(new_value));
  2326. }
  2327. return ogSet(new_value);
  2328. });
  2329. }
  2330. if (storage2) {
  2331. const storageValue = storage2.getItem(key);
  2332. try {
  2333. if (storageValue) {
  2334. value = JSON.parse(storageValue);
  2335. }
  2336. } catch (err) {
  2337. }
  2338. storage2.setItem(key, JSON.stringify(value));
  2339. }
  2340. const ogStore = writable$1(value, start ? wrap_start : void 0);
  2341. function set(new_value) {
  2342. if (storage2) {
  2343. storage2.setItem(key, JSON.stringify(new_value));
  2344. }
  2345. ogStore.set(new_value);
  2346. }
  2347. function update2(fn) {
  2348. set(fn(get_store_value(ogStore)));
  2349. }
  2350. function subscribe2(run2, invalidate = noop) {
  2351. return ogStore.subscribe(run2, invalidate);
  2352. }
  2353. return { set, update: update2, subscribe: subscribe2 };
  2354. }
  2355. function derived2(key, stores, fn, initial_value) {
  2356. const single = !Array.isArray(stores);
  2357. const stores_array = single ? [stores] : stores;
  2358. if (storage2 && storage2.getItem(key)) {
  2359. try {
  2360. initial_value = JSON.parse(storage2.getItem(key));
  2361. } catch (err) {
  2362. }
  2363. }
  2364. return readable2(key, initial_value, (set) => {
  2365. let inited = false;
  2366. const values = [];
  2367. let pending = 0;
  2368. let cleanup = noop;
  2369. const sync = () => {
  2370. if (pending) {
  2371. return;
  2372. }
  2373. cleanup();
  2374. const input = single ? values[0] : values;
  2375. if (isSimpleDeriver(fn)) {
  2376. set(fn(input));
  2377. } else {
  2378. const result = fn(input, set);
  2379. cleanup = is_function(result) ? result : noop;
  2380. }
  2381. };
  2382. const unsubscribers = stores_array.map((store, i) => store.subscribe((value) => {
  2383. values[i] = value;
  2384. pending &= ~(1 << i);
  2385. if (inited) {
  2386. sync();
  2387. }
  2388. }, () => {
  2389. pending |= 1 << i;
  2390. }));
  2391. inited = true;
  2392. sync();
  2393. return function stop() {
  2394. run_all(unsubscribers);
  2395. cleanup();
  2396. };
  2397. });
  2398. }
  2399. return {
  2400. readable: readable2,
  2401. writable: writable2,
  2402. derived: derived2,
  2403. get: get_store_value
  2404. };
  2405. }
  2406. var storage = typeof window !== "undefined" ? window.sessionStorage : void 0;
  2407. var g = generator(storage);
  2408. var writable = g.writable;
  2409. class TJSSessionStorage {
  2410. /**
  2411. * @type {Map<string, import('svelte/store').Writable>}
  2412. */
  2413. #stores = /* @__PURE__ */ new Map();
  2414. /**
  2415. * Creates a new store for the given key.
  2416. *
  2417. * @param {string} key - Key to lookup in stores map.
  2418. *
  2419. * @param {boolean} [defaultValue] - A default value to set for the store.
  2420. *
  2421. * @returns {import('svelte/store').Writable} The new store.
  2422. */
  2423. static #createStore(key, defaultValue = void 0) {
  2424. try {
  2425. const value = sessionStorage.getItem(key);
  2426. if (value !== null) {
  2427. defaultValue = value === "undefined" ? void 0 : JSON.parse(value);
  2428. }
  2429. } catch (err) {
  2430. }
  2431. return writable(key, defaultValue);
  2432. }
  2433. /**
  2434. * Gets a store from the `stores` Map or creates a new store for the key and a given default value.
  2435. *
  2436. * @param {string} key - Key to lookup in stores map.
  2437. *
  2438. * @param {boolean} [defaultValue] - A default value to set for the store.
  2439. *
  2440. * @returns {import('svelte/store').Writable} The store for the given key.
  2441. */
  2442. #getStore(key, defaultValue = void 0) {
  2443. let store = this.#stores.get(key);
  2444. if (store === void 0) {
  2445. store = TJSSessionStorage.#createStore(key, defaultValue);
  2446. this.#stores.set(key, store);
  2447. }
  2448. return store;
  2449. }
  2450. /**
  2451. * Get value from the sessionStorage.
  2452. *
  2453. * @param {string} key - Key to lookup in sessionStorage.
  2454. *
  2455. * @param {*} [defaultValue] - A default value to return if key not present in session storage.
  2456. *
  2457. * @returns {*} Value from session storage or if not defined any default value provided.
  2458. */
  2459. getItem(key, defaultValue) {
  2460. let value = defaultValue;
  2461. const storageValue = sessionStorage.getItem(key);
  2462. if (storageValue !== null) {
  2463. try {
  2464. value = storageValue === "undefined" ? void 0 : JSON.parse(storageValue);
  2465. } catch (err) {
  2466. value = defaultValue;
  2467. }
  2468. } else if (defaultValue !== void 0) {
  2469. try {
  2470. const newValue = JSON.stringify(defaultValue);
  2471. sessionStorage.setItem(key, newValue === "undefined" ? void 0 : newValue);
  2472. } catch (err) {
  2473. }
  2474. }
  2475. return value;
  2476. }
  2477. /**
  2478. * Returns the backing Svelte store for the given key; potentially sets a default value if the key
  2479. * is not already set.
  2480. *
  2481. * @param {string} key - Key to lookup in sessionStorage.
  2482. *
  2483. * @param {*} [defaultValue] - A default value to return if key not present in session storage.
  2484. *
  2485. * @returns {import('svelte/store').Writable} The Svelte store for this key.
  2486. */
  2487. getStore(key, defaultValue) {
  2488. return this.#getStore(key, defaultValue);
  2489. }
  2490. /**
  2491. * Sets the value for the given key in sessionStorage.
  2492. *
  2493. * @param {string} key - Key to lookup in sessionStorage.
  2494. *
  2495. * @param {*} value - A value to set for this key.
  2496. */
  2497. setItem(key, value) {
  2498. const store = this.#getStore(key);
  2499. store.set(value);
  2500. }
  2501. /**
  2502. * Convenience method to swap a boolean value stored in session storage.
  2503. *
  2504. * @param {string} key - Key to lookup in sessionStorage.
  2505. *
  2506. * @param {boolean} [defaultValue] - A default value to return if key not present in session storage.
  2507. *
  2508. * @returns {boolean} The boolean swap for the given key.
  2509. */
  2510. swapItemBoolean(key, defaultValue) {
  2511. const store = this.#getStore(key, defaultValue);
  2512. let currentValue = false;
  2513. try {
  2514. currentValue = !!JSON.parse(sessionStorage.getItem(key));
  2515. } catch (err) {
  2516. }
  2517. const newValue = typeof currentValue === "boolean" ? !currentValue : false;
  2518. store.set(newValue);
  2519. return newValue;
  2520. }
  2521. }
  2522. function isUpdatableStore(store) {
  2523. if (store === null || store === void 0) {
  2524. return false;
  2525. }
  2526. switch (typeof store) {
  2527. case "function":
  2528. case "object":
  2529. return typeof store.subscribe === "function" && typeof store.update === "function";
  2530. }
  2531. return false;
  2532. }
  2533. function subscribeIgnoreFirst(store, update2) {
  2534. let firedFirst = false;
  2535. return store.subscribe((value) => {
  2536. if (!firedFirst) {
  2537. firedFirst = true;
  2538. } else {
  2539. update2(value);
  2540. }
  2541. });
  2542. }
  2543. function writableDerived(origins, derive, reflect, initial) {
  2544. var childDerivedSetter, originValues, blockNextDerive = false;
  2545. var reflectOldValues = reflect.length >= 2;
  2546. var wrappedDerive = (got, set) => {
  2547. childDerivedSetter = set;
  2548. if (reflectOldValues) {
  2549. originValues = got;
  2550. }
  2551. if (!blockNextDerive) {
  2552. let returned = derive(got, set);
  2553. if (derive.length < 2) {
  2554. set(returned);
  2555. } else {
  2556. return returned;
  2557. }
  2558. }
  2559. blockNextDerive = false;
  2560. };
  2561. var childDerived = derived(origins, wrappedDerive, initial);
  2562. var singleOrigin = !Array.isArray(origins);
  2563. function doReflect(reflecting) {
  2564. var setWith = reflect(reflecting, originValues);
  2565. if (singleOrigin) {
  2566. blockNextDerive = true;
  2567. origins.set(setWith);
  2568. } else {
  2569. setWith.forEach((value, i) => {
  2570. blockNextDerive = true;
  2571. origins[i].set(value);
  2572. });
  2573. }
  2574. blockNextDerive = false;
  2575. }
  2576. var tryingSet = false;
  2577. function update2(fn) {
  2578. var isUpdated, mutatedBySubscriptions, oldValue, newValue;
  2579. if (tryingSet) {
  2580. newValue = fn(get_store_value(childDerived));
  2581. childDerivedSetter(newValue);
  2582. return;
  2583. }
  2584. var unsubscribe = childDerived.subscribe((value) => {
  2585. if (!tryingSet) {
  2586. oldValue = value;
  2587. } else if (!isUpdated) {
  2588. isUpdated = true;
  2589. } else {
  2590. mutatedBySubscriptions = true;
  2591. }
  2592. });
  2593. newValue = fn(oldValue);
  2594. tryingSet = true;
  2595. childDerivedSetter(newValue);
  2596. unsubscribe();
  2597. tryingSet = false;
  2598. if (mutatedBySubscriptions) {
  2599. newValue = get_store_value(childDerived);
  2600. }
  2601. if (isUpdated) {
  2602. doReflect(newValue);
  2603. }
  2604. }
  2605. return {
  2606. subscribe: childDerived.subscribe,
  2607. set(value) {
  2608. update2(() => value);
  2609. },
  2610. update: update2
  2611. };
  2612. }
  2613. function propertyStore(origin, propName) {
  2614. if (!Array.isArray(propName)) {
  2615. return writableDerived(
  2616. origin,
  2617. (object) => object[propName],
  2618. (reflecting, object) => {
  2619. object[propName] = reflecting;
  2620. return object;
  2621. }
  2622. );
  2623. } else {
  2624. let props = propName.concat();
  2625. return writableDerived(
  2626. origin,
  2627. (value) => {
  2628. for (let i = 0; i < props.length; ++i) {
  2629. value = value[props[i]];
  2630. }
  2631. return value;
  2632. },
  2633. (reflecting, object) => {
  2634. let target = object;
  2635. for (let i = 0; i < props.length - 1; ++i) {
  2636. target = target[props[i]];
  2637. }
  2638. target[props[props.length - 1]] = reflecting;
  2639. return object;
  2640. }
  2641. );
  2642. }
  2643. }
  2644. const storeState = writable$1(void 0);
  2645. ({
  2646. subscribe: storeState.subscribe,
  2647. get: () => game
  2648. });
  2649. Hooks.once("ready", () => storeState.set(game));
  2650. function cubicOut(t) {
  2651. const f = t - 1;
  2652. return f * f * f + 1;
  2653. }
  2654. function lerp$5(start, end, amount) {
  2655. return (1 - amount) * start + amount * end;
  2656. }
  2657. function degToRad(deg) {
  2658. return deg * (Math.PI / 180);
  2659. }
  2660. var EPSILON = 1e-6;
  2661. var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array;
  2662. var RANDOM = Math.random;
  2663. if (!Math.hypot)
  2664. Math.hypot = function() {
  2665. var y = 0, i = arguments.length;
  2666. while (i--) {
  2667. y += arguments[i] * arguments[i];
  2668. }
  2669. return Math.sqrt(y);
  2670. };
  2671. function create$6() {
  2672. var out = new ARRAY_TYPE(9);
  2673. if (ARRAY_TYPE != Float32Array) {
  2674. out[1] = 0;
  2675. out[2] = 0;
  2676. out[3] = 0;
  2677. out[5] = 0;
  2678. out[6] = 0;
  2679. out[7] = 0;
  2680. }
  2681. out[0] = 1;
  2682. out[4] = 1;
  2683. out[8] = 1;
  2684. return out;
  2685. }
  2686. function create$5() {
  2687. var out = new ARRAY_TYPE(16);
  2688. if (ARRAY_TYPE != Float32Array) {
  2689. out[1] = 0;
  2690. out[2] = 0;
  2691. out[3] = 0;
  2692. out[4] = 0;
  2693. out[6] = 0;
  2694. out[7] = 0;
  2695. out[8] = 0;
  2696. out[9] = 0;
  2697. out[11] = 0;
  2698. out[12] = 0;
  2699. out[13] = 0;
  2700. out[14] = 0;
  2701. }
  2702. out[0] = 1;
  2703. out[5] = 1;
  2704. out[10] = 1;
  2705. out[15] = 1;
  2706. return out;
  2707. }
  2708. function clone$5(a) {
  2709. var out = new ARRAY_TYPE(16);
  2710. out[0] = a[0];
  2711. out[1] = a[1];
  2712. out[2] = a[2];
  2713. out[3] = a[3];
  2714. out[4] = a[4];
  2715. out[5] = a[5];
  2716. out[6] = a[6];
  2717. out[7] = a[7];
  2718. out[8] = a[8];
  2719. out[9] = a[9];
  2720. out[10] = a[10];
  2721. out[11] = a[11];
  2722. out[12] = a[12];
  2723. out[13] = a[13];
  2724. out[14] = a[14];
  2725. out[15] = a[15];
  2726. return out;
  2727. }
  2728. function copy$5(out, a) {
  2729. out[0] = a[0];
  2730. out[1] = a[1];
  2731. out[2] = a[2];
  2732. out[3] = a[3];
  2733. out[4] = a[4];
  2734. out[5] = a[5];
  2735. out[6] = a[6];
  2736. out[7] = a[7];
  2737. out[8] = a[8];
  2738. out[9] = a[9];
  2739. out[10] = a[10];
  2740. out[11] = a[11];
  2741. out[12] = a[12];
  2742. out[13] = a[13];
  2743. out[14] = a[14];
  2744. out[15] = a[15];
  2745. return out;
  2746. }
  2747. function fromValues$5(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
  2748. var out = new ARRAY_TYPE(16);
  2749. out[0] = m00;
  2750. out[1] = m01;
  2751. out[2] = m02;
  2752. out[3] = m03;
  2753. out[4] = m10;
  2754. out[5] = m11;
  2755. out[6] = m12;
  2756. out[7] = m13;
  2757. out[8] = m20;
  2758. out[9] = m21;
  2759. out[10] = m22;
  2760. out[11] = m23;
  2761. out[12] = m30;
  2762. out[13] = m31;
  2763. out[14] = m32;
  2764. out[15] = m33;
  2765. return out;
  2766. }
  2767. function set$5(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
  2768. out[0] = m00;
  2769. out[1] = m01;
  2770. out[2] = m02;
  2771. out[3] = m03;
  2772. out[4] = m10;
  2773. out[5] = m11;
  2774. out[6] = m12;
  2775. out[7] = m13;
  2776. out[8] = m20;
  2777. out[9] = m21;
  2778. out[10] = m22;
  2779. out[11] = m23;
  2780. out[12] = m30;
  2781. out[13] = m31;
  2782. out[14] = m32;
  2783. out[15] = m33;
  2784. return out;
  2785. }
  2786. function identity$2(out) {
  2787. out[0] = 1;
  2788. out[1] = 0;
  2789. out[2] = 0;
  2790. out[3] = 0;
  2791. out[4] = 0;
  2792. out[5] = 1;
  2793. out[6] = 0;
  2794. out[7] = 0;
  2795. out[8] = 0;
  2796. out[9] = 0;
  2797. out[10] = 1;
  2798. out[11] = 0;
  2799. out[12] = 0;
  2800. out[13] = 0;
  2801. out[14] = 0;
  2802. out[15] = 1;
  2803. return out;
  2804. }
  2805. function transpose(out, a) {
  2806. if (out === a) {
  2807. var a01 = a[1], a02 = a[2], a03 = a[3];
  2808. var a12 = a[6], a13 = a[7];
  2809. var a23 = a[11];
  2810. out[1] = a[4];
  2811. out[2] = a[8];
  2812. out[3] = a[12];
  2813. out[4] = a01;
  2814. out[6] = a[9];
  2815. out[7] = a[13];
  2816. out[8] = a02;
  2817. out[9] = a12;
  2818. out[11] = a[14];
  2819. out[12] = a03;
  2820. out[13] = a13;
  2821. out[14] = a23;
  2822. } else {
  2823. out[0] = a[0];
  2824. out[1] = a[4];
  2825. out[2] = a[8];
  2826. out[3] = a[12];
  2827. out[4] = a[1];
  2828. out[5] = a[5];
  2829. out[6] = a[9];
  2830. out[7] = a[13];
  2831. out[8] = a[2];
  2832. out[9] = a[6];
  2833. out[10] = a[10];
  2834. out[11] = a[14];
  2835. out[12] = a[3];
  2836. out[13] = a[7];
  2837. out[14] = a[11];
  2838. out[15] = a[15];
  2839. }
  2840. return out;
  2841. }
  2842. function invert$2(out, a) {
  2843. var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
  2844. var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
  2845. var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
  2846. var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
  2847. var b00 = a00 * a11 - a01 * a10;
  2848. var b01 = a00 * a12 - a02 * a10;
  2849. var b02 = a00 * a13 - a03 * a10;
  2850. var b03 = a01 * a12 - a02 * a11;
  2851. var b04 = a01 * a13 - a03 * a11;
  2852. var b05 = a02 * a13 - a03 * a12;
  2853. var b06 = a20 * a31 - a21 * a30;
  2854. var b07 = a20 * a32 - a22 * a30;
  2855. var b08 = a20 * a33 - a23 * a30;
  2856. var b09 = a21 * a32 - a22 * a31;
  2857. var b10 = a21 * a33 - a23 * a31;
  2858. var b11 = a22 * a33 - a23 * a32;
  2859. var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
  2860. if (!det) {
  2861. return null;
  2862. }
  2863. det = 1 / det;
  2864. out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
  2865. out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
  2866. out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
  2867. out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
  2868. out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
  2869. out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
  2870. out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
  2871. out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
  2872. out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
  2873. out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
  2874. out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
  2875. out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
  2876. out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
  2877. out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
  2878. out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
  2879. out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
  2880. return out;
  2881. }
  2882. function adjoint(out, a) {
  2883. var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
  2884. var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
  2885. var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
  2886. var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
  2887. out[0] = a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22);
  2888. out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
  2889. out[2] = a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12);
  2890. out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
  2891. out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
  2892. out[5] = a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22);
  2893. out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
  2894. out[7] = a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12);
  2895. out[8] = a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21);
  2896. out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
  2897. out[10] = a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11);
  2898. out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
  2899. out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
  2900. out[13] = a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21);
  2901. out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
  2902. out[15] = a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11);
  2903. return out;
  2904. }
  2905. function determinant(a) {
  2906. var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
  2907. var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
  2908. var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
  2909. var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
  2910. var b00 = a00 * a11 - a01 * a10;
  2911. var b01 = a00 * a12 - a02 * a10;
  2912. var b02 = a00 * a13 - a03 * a10;
  2913. var b03 = a01 * a12 - a02 * a11;
  2914. var b04 = a01 * a13 - a03 * a11;
  2915. var b05 = a02 * a13 - a03 * a12;
  2916. var b06 = a20 * a31 - a21 * a30;
  2917. var b07 = a20 * a32 - a22 * a30;
  2918. var b08 = a20 * a33 - a23 * a30;
  2919. var b09 = a21 * a32 - a22 * a31;
  2920. var b10 = a21 * a33 - a23 * a31;
  2921. var b11 = a22 * a33 - a23 * a32;
  2922. return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
  2923. }
  2924. function multiply$5(out, a, b) {
  2925. var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
  2926. var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
  2927. var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
  2928. var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
  2929. var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
  2930. out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  2931. out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  2932. out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  2933. out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  2934. b0 = b[4];
  2935. b1 = b[5];
  2936. b2 = b[6];
  2937. b3 = b[7];
  2938. out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  2939. out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  2940. out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  2941. out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  2942. b0 = b[8];
  2943. b1 = b[9];
  2944. b2 = b[10];
  2945. b3 = b[11];
  2946. out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  2947. out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  2948. out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  2949. out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  2950. b0 = b[12];
  2951. b1 = b[13];
  2952. b2 = b[14];
  2953. b3 = b[15];
  2954. out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  2955. out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  2956. out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  2957. out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  2958. return out;
  2959. }
  2960. function translate$1(out, a, v) {
  2961. var x = v[0], y = v[1], z = v[2];
  2962. var a00, a01, a02, a03;
  2963. var a10, a11, a12, a13;
  2964. var a20, a21, a22, a23;
  2965. if (a === out) {
  2966. out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
  2967. out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
  2968. out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
  2969. out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
  2970. } else {
  2971. a00 = a[0];
  2972. a01 = a[1];
  2973. a02 = a[2];
  2974. a03 = a[3];
  2975. a10 = a[4];
  2976. a11 = a[5];
  2977. a12 = a[6];
  2978. a13 = a[7];
  2979. a20 = a[8];
  2980. a21 = a[9];
  2981. a22 = a[10];
  2982. a23 = a[11];
  2983. out[0] = a00;
  2984. out[1] = a01;
  2985. out[2] = a02;
  2986. out[3] = a03;
  2987. out[4] = a10;
  2988. out[5] = a11;
  2989. out[6] = a12;
  2990. out[7] = a13;
  2991. out[8] = a20;
  2992. out[9] = a21;
  2993. out[10] = a22;
  2994. out[11] = a23;
  2995. out[12] = a00 * x + a10 * y + a20 * z + a[12];
  2996. out[13] = a01 * x + a11 * y + a21 * z + a[13];
  2997. out[14] = a02 * x + a12 * y + a22 * z + a[14];
  2998. out[15] = a03 * x + a13 * y + a23 * z + a[15];
  2999. }
  3000. return out;
  3001. }
  3002. function scale$5(out, a, v) {
  3003. var x = v[0], y = v[1], z = v[2];
  3004. out[0] = a[0] * x;
  3005. out[1] = a[1] * x;
  3006. out[2] = a[2] * x;
  3007. out[3] = a[3] * x;
  3008. out[4] = a[4] * y;
  3009. out[5] = a[5] * y;
  3010. out[6] = a[6] * y;
  3011. out[7] = a[7] * y;
  3012. out[8] = a[8] * z;
  3013. out[9] = a[9] * z;
  3014. out[10] = a[10] * z;
  3015. out[11] = a[11] * z;
  3016. out[12] = a[12];
  3017. out[13] = a[13];
  3018. out[14] = a[14];
  3019. out[15] = a[15];
  3020. return out;
  3021. }
  3022. function rotate$1(out, a, rad, axis) {
  3023. var x = axis[0], y = axis[1], z = axis[2];
  3024. var len = Math.hypot(x, y, z);
  3025. var s, c, t;
  3026. var a00, a01, a02, a03;
  3027. var a10, a11, a12, a13;
  3028. var a20, a21, a22, a23;
  3029. var b00, b01, b02;
  3030. var b10, b11, b12;
  3031. var b20, b21, b22;
  3032. if (len < EPSILON) {
  3033. return null;
  3034. }
  3035. len = 1 / len;
  3036. x *= len;
  3037. y *= len;
  3038. z *= len;
  3039. s = Math.sin(rad);
  3040. c = Math.cos(rad);
  3041. t = 1 - c;
  3042. a00 = a[0];
  3043. a01 = a[1];
  3044. a02 = a[2];
  3045. a03 = a[3];
  3046. a10 = a[4];
  3047. a11 = a[5];
  3048. a12 = a[6];
  3049. a13 = a[7];
  3050. a20 = a[8];
  3051. a21 = a[9];
  3052. a22 = a[10];
  3053. a23 = a[11];
  3054. b00 = x * x * t + c;
  3055. b01 = y * x * t + z * s;
  3056. b02 = z * x * t - y * s;
  3057. b10 = x * y * t - z * s;
  3058. b11 = y * y * t + c;
  3059. b12 = z * y * t + x * s;
  3060. b20 = x * z * t + y * s;
  3061. b21 = y * z * t - x * s;
  3062. b22 = z * z * t + c;
  3063. out[0] = a00 * b00 + a10 * b01 + a20 * b02;
  3064. out[1] = a01 * b00 + a11 * b01 + a21 * b02;
  3065. out[2] = a02 * b00 + a12 * b01 + a22 * b02;
  3066. out[3] = a03 * b00 + a13 * b01 + a23 * b02;
  3067. out[4] = a00 * b10 + a10 * b11 + a20 * b12;
  3068. out[5] = a01 * b10 + a11 * b11 + a21 * b12;
  3069. out[6] = a02 * b10 + a12 * b11 + a22 * b12;
  3070. out[7] = a03 * b10 + a13 * b11 + a23 * b12;
  3071. out[8] = a00 * b20 + a10 * b21 + a20 * b22;
  3072. out[9] = a01 * b20 + a11 * b21 + a21 * b22;
  3073. out[10] = a02 * b20 + a12 * b21 + a22 * b22;
  3074. out[11] = a03 * b20 + a13 * b21 + a23 * b22;
  3075. if (a !== out) {
  3076. out[12] = a[12];
  3077. out[13] = a[13];
  3078. out[14] = a[14];
  3079. out[15] = a[15];
  3080. }
  3081. return out;
  3082. }
  3083. function rotateX$3(out, a, rad) {
  3084. var s = Math.sin(rad);
  3085. var c = Math.cos(rad);
  3086. var a10 = a[4];
  3087. var a11 = a[5];
  3088. var a12 = a[6];
  3089. var a13 = a[7];
  3090. var a20 = a[8];
  3091. var a21 = a[9];
  3092. var a22 = a[10];
  3093. var a23 = a[11];
  3094. if (a !== out) {
  3095. out[0] = a[0];
  3096. out[1] = a[1];
  3097. out[2] = a[2];
  3098. out[3] = a[3];
  3099. out[12] = a[12];
  3100. out[13] = a[13];
  3101. out[14] = a[14];
  3102. out[15] = a[15];
  3103. }
  3104. out[4] = a10 * c + a20 * s;
  3105. out[5] = a11 * c + a21 * s;
  3106. out[6] = a12 * c + a22 * s;
  3107. out[7] = a13 * c + a23 * s;
  3108. out[8] = a20 * c - a10 * s;
  3109. out[9] = a21 * c - a11 * s;
  3110. out[10] = a22 * c - a12 * s;
  3111. out[11] = a23 * c - a13 * s;
  3112. return out;
  3113. }
  3114. function rotateY$3(out, a, rad) {
  3115. var s = Math.sin(rad);
  3116. var c = Math.cos(rad);
  3117. var a00 = a[0];
  3118. var a01 = a[1];
  3119. var a02 = a[2];
  3120. var a03 = a[3];
  3121. var a20 = a[8];
  3122. var a21 = a[9];
  3123. var a22 = a[10];
  3124. var a23 = a[11];
  3125. if (a !== out) {
  3126. out[4] = a[4];
  3127. out[5] = a[5];
  3128. out[6] = a[6];
  3129. out[7] = a[7];
  3130. out[12] = a[12];
  3131. out[13] = a[13];
  3132. out[14] = a[14];
  3133. out[15] = a[15];
  3134. }
  3135. out[0] = a00 * c - a20 * s;
  3136. out[1] = a01 * c - a21 * s;
  3137. out[2] = a02 * c - a22 * s;
  3138. out[3] = a03 * c - a23 * s;
  3139. out[8] = a00 * s + a20 * c;
  3140. out[9] = a01 * s + a21 * c;
  3141. out[10] = a02 * s + a22 * c;
  3142. out[11] = a03 * s + a23 * c;
  3143. return out;
  3144. }
  3145. function rotateZ$3(out, a, rad) {
  3146. var s = Math.sin(rad);
  3147. var c = Math.cos(rad);
  3148. var a00 = a[0];
  3149. var a01 = a[1];
  3150. var a02 = a[2];
  3151. var a03 = a[3];
  3152. var a10 = a[4];
  3153. var a11 = a[5];
  3154. var a12 = a[6];
  3155. var a13 = a[7];
  3156. if (a !== out) {
  3157. out[8] = a[8];
  3158. out[9] = a[9];
  3159. out[10] = a[10];
  3160. out[11] = a[11];
  3161. out[12] = a[12];
  3162. out[13] = a[13];
  3163. out[14] = a[14];
  3164. out[15] = a[15];
  3165. }
  3166. out[0] = a00 * c + a10 * s;
  3167. out[1] = a01 * c + a11 * s;
  3168. out[2] = a02 * c + a12 * s;
  3169. out[3] = a03 * c + a13 * s;
  3170. out[4] = a10 * c - a00 * s;
  3171. out[5] = a11 * c - a01 * s;
  3172. out[6] = a12 * c - a02 * s;
  3173. out[7] = a13 * c - a03 * s;
  3174. return out;
  3175. }
  3176. function fromTranslation$1(out, v) {
  3177. out[0] = 1;
  3178. out[1] = 0;
  3179. out[2] = 0;
  3180. out[3] = 0;
  3181. out[4] = 0;
  3182. out[5] = 1;
  3183. out[6] = 0;
  3184. out[7] = 0;
  3185. out[8] = 0;
  3186. out[9] = 0;
  3187. out[10] = 1;
  3188. out[11] = 0;
  3189. out[12] = v[0];
  3190. out[13] = v[1];
  3191. out[14] = v[2];
  3192. out[15] = 1;
  3193. return out;
  3194. }
  3195. function fromScaling(out, v) {
  3196. out[0] = v[0];
  3197. out[1] = 0;
  3198. out[2] = 0;
  3199. out[3] = 0;
  3200. out[4] = 0;
  3201. out[5] = v[1];
  3202. out[6] = 0;
  3203. out[7] = 0;
  3204. out[8] = 0;
  3205. out[9] = 0;
  3206. out[10] = v[2];
  3207. out[11] = 0;
  3208. out[12] = 0;
  3209. out[13] = 0;
  3210. out[14] = 0;
  3211. out[15] = 1;
  3212. return out;
  3213. }
  3214. function fromRotation$1(out, rad, axis) {
  3215. var x = axis[0], y = axis[1], z = axis[2];
  3216. var len = Math.hypot(x, y, z);
  3217. var s, c, t;
  3218. if (len < EPSILON) {
  3219. return null;
  3220. }
  3221. len = 1 / len;
  3222. x *= len;
  3223. y *= len;
  3224. z *= len;
  3225. s = Math.sin(rad);
  3226. c = Math.cos(rad);
  3227. t = 1 - c;
  3228. out[0] = x * x * t + c;
  3229. out[1] = y * x * t + z * s;
  3230. out[2] = z * x * t - y * s;
  3231. out[3] = 0;
  3232. out[4] = x * y * t - z * s;
  3233. out[5] = y * y * t + c;
  3234. out[6] = z * y * t + x * s;
  3235. out[7] = 0;
  3236. out[8] = x * z * t + y * s;
  3237. out[9] = y * z * t - x * s;
  3238. out[10] = z * z * t + c;
  3239. out[11] = 0;
  3240. out[12] = 0;
  3241. out[13] = 0;
  3242. out[14] = 0;
  3243. out[15] = 1;
  3244. return out;
  3245. }
  3246. function fromXRotation(out, rad) {
  3247. var s = Math.sin(rad);
  3248. var c = Math.cos(rad);
  3249. out[0] = 1;
  3250. out[1] = 0;
  3251. out[2] = 0;
  3252. out[3] = 0;
  3253. out[4] = 0;
  3254. out[5] = c;
  3255. out[6] = s;
  3256. out[7] = 0;
  3257. out[8] = 0;
  3258. out[9] = -s;
  3259. out[10] = c;
  3260. out[11] = 0;
  3261. out[12] = 0;
  3262. out[13] = 0;
  3263. out[14] = 0;
  3264. out[15] = 1;
  3265. return out;
  3266. }
  3267. function fromYRotation(out, rad) {
  3268. var s = Math.sin(rad);
  3269. var c = Math.cos(rad);
  3270. out[0] = c;
  3271. out[1] = 0;
  3272. out[2] = -s;
  3273. out[3] = 0;
  3274. out[4] = 0;
  3275. out[5] = 1;
  3276. out[6] = 0;
  3277. out[7] = 0;
  3278. out[8] = s;
  3279. out[9] = 0;
  3280. out[10] = c;
  3281. out[11] = 0;
  3282. out[12] = 0;
  3283. out[13] = 0;
  3284. out[14] = 0;
  3285. out[15] = 1;
  3286. return out;
  3287. }
  3288. function fromZRotation(out, rad) {
  3289. var s = Math.sin(rad);
  3290. var c = Math.cos(rad);
  3291. out[0] = c;
  3292. out[1] = s;
  3293. out[2] = 0;
  3294. out[3] = 0;
  3295. out[4] = -s;
  3296. out[5] = c;
  3297. out[6] = 0;
  3298. out[7] = 0;
  3299. out[8] = 0;
  3300. out[9] = 0;
  3301. out[10] = 1;
  3302. out[11] = 0;
  3303. out[12] = 0;
  3304. out[13] = 0;
  3305. out[14] = 0;
  3306. out[15] = 1;
  3307. return out;
  3308. }
  3309. function fromRotationTranslation$1(out, q, v) {
  3310. var x = q[0], y = q[1], z = q[2], w = q[3];
  3311. var x2 = x + x;
  3312. var y2 = y + y;
  3313. var z2 = z + z;
  3314. var xx = x * x2;
  3315. var xy = x * y2;
  3316. var xz = x * z2;
  3317. var yy = y * y2;
  3318. var yz = y * z2;
  3319. var zz = z * z2;
  3320. var wx = w * x2;
  3321. var wy = w * y2;
  3322. var wz = w * z2;
  3323. out[0] = 1 - (yy + zz);
  3324. out[1] = xy + wz;
  3325. out[2] = xz - wy;
  3326. out[3] = 0;
  3327. out[4] = xy - wz;
  3328. out[5] = 1 - (xx + zz);
  3329. out[6] = yz + wx;
  3330. out[7] = 0;
  3331. out[8] = xz + wy;
  3332. out[9] = yz - wx;
  3333. out[10] = 1 - (xx + yy);
  3334. out[11] = 0;
  3335. out[12] = v[0];
  3336. out[13] = v[1];
  3337. out[14] = v[2];
  3338. out[15] = 1;
  3339. return out;
  3340. }
  3341. function fromQuat2(out, a) {
  3342. var translation = new ARRAY_TYPE(3);
  3343. var bx = -a[0], by = -a[1], bz = -a[2], bw = a[3], ax = a[4], ay = a[5], az = a[6], aw = a[7];
  3344. var magnitude = bx * bx + by * by + bz * bz + bw * bw;
  3345. if (magnitude > 0) {
  3346. translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude;
  3347. translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude;
  3348. translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude;
  3349. } else {
  3350. translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
  3351. translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
  3352. translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
  3353. }
  3354. fromRotationTranslation$1(out, a, translation);
  3355. return out;
  3356. }
  3357. function getTranslation$1(out, mat) {
  3358. out[0] = mat[12];
  3359. out[1] = mat[13];
  3360. out[2] = mat[14];
  3361. return out;
  3362. }
  3363. function getScaling(out, mat) {
  3364. var m11 = mat[0];
  3365. var m12 = mat[1];
  3366. var m13 = mat[2];
  3367. var m21 = mat[4];
  3368. var m22 = mat[5];
  3369. var m23 = mat[6];
  3370. var m31 = mat[8];
  3371. var m32 = mat[9];
  3372. var m33 = mat[10];
  3373. out[0] = Math.hypot(m11, m12, m13);
  3374. out[1] = Math.hypot(m21, m22, m23);
  3375. out[2] = Math.hypot(m31, m32, m33);
  3376. return out;
  3377. }
  3378. function getRotation(out, mat) {
  3379. var scaling = new ARRAY_TYPE(3);
  3380. getScaling(scaling, mat);
  3381. var is1 = 1 / scaling[0];
  3382. var is2 = 1 / scaling[1];
  3383. var is3 = 1 / scaling[2];
  3384. var sm11 = mat[0] * is1;
  3385. var sm12 = mat[1] * is2;
  3386. var sm13 = mat[2] * is3;
  3387. var sm21 = mat[4] * is1;
  3388. var sm22 = mat[5] * is2;
  3389. var sm23 = mat[6] * is3;
  3390. var sm31 = mat[8] * is1;
  3391. var sm32 = mat[9] * is2;
  3392. var sm33 = mat[10] * is3;
  3393. var trace = sm11 + sm22 + sm33;
  3394. var S = 0;
  3395. if (trace > 0) {
  3396. S = Math.sqrt(trace + 1) * 2;
  3397. out[3] = 0.25 * S;
  3398. out[0] = (sm23 - sm32) / S;
  3399. out[1] = (sm31 - sm13) / S;
  3400. out[2] = (sm12 - sm21) / S;
  3401. } else if (sm11 > sm22 && sm11 > sm33) {
  3402. S = Math.sqrt(1 + sm11 - sm22 - sm33) * 2;
  3403. out[3] = (sm23 - sm32) / S;
  3404. out[0] = 0.25 * S;
  3405. out[1] = (sm12 + sm21) / S;
  3406. out[2] = (sm31 + sm13) / S;
  3407. } else if (sm22 > sm33) {
  3408. S = Math.sqrt(1 + sm22 - sm11 - sm33) * 2;
  3409. out[3] = (sm31 - sm13) / S;
  3410. out[0] = (sm12 + sm21) / S;
  3411. out[1] = 0.25 * S;
  3412. out[2] = (sm23 + sm32) / S;
  3413. } else {
  3414. S = Math.sqrt(1 + sm33 - sm11 - sm22) * 2;
  3415. out[3] = (sm12 - sm21) / S;
  3416. out[0] = (sm31 + sm13) / S;
  3417. out[1] = (sm23 + sm32) / S;
  3418. out[2] = 0.25 * S;
  3419. }
  3420. return out;
  3421. }
  3422. function fromRotationTranslationScale(out, q, v, s) {
  3423. var x = q[0], y = q[1], z = q[2], w = q[3];
  3424. var x2 = x + x;
  3425. var y2 = y + y;
  3426. var z2 = z + z;
  3427. var xx = x * x2;
  3428. var xy = x * y2;
  3429. var xz = x * z2;
  3430. var yy = y * y2;
  3431. var yz = y * z2;
  3432. var zz = z * z2;
  3433. var wx = w * x2;
  3434. var wy = w * y2;
  3435. var wz = w * z2;
  3436. var sx = s[0];
  3437. var sy = s[1];
  3438. var sz = s[2];
  3439. out[0] = (1 - (yy + zz)) * sx;
  3440. out[1] = (xy + wz) * sx;
  3441. out[2] = (xz - wy) * sx;
  3442. out[3] = 0;
  3443. out[4] = (xy - wz) * sy;
  3444. out[5] = (1 - (xx + zz)) * sy;
  3445. out[6] = (yz + wx) * sy;
  3446. out[7] = 0;
  3447. out[8] = (xz + wy) * sz;
  3448. out[9] = (yz - wx) * sz;
  3449. out[10] = (1 - (xx + yy)) * sz;
  3450. out[11] = 0;
  3451. out[12] = v[0];
  3452. out[13] = v[1];
  3453. out[14] = v[2];
  3454. out[15] = 1;
  3455. return out;
  3456. }
  3457. function fromRotationTranslationScaleOrigin(out, q, v, s, o) {
  3458. var x = q[0], y = q[1], z = q[2], w = q[3];
  3459. var x2 = x + x;
  3460. var y2 = y + y;
  3461. var z2 = z + z;
  3462. var xx = x * x2;
  3463. var xy = x * y2;
  3464. var xz = x * z2;
  3465. var yy = y * y2;
  3466. var yz = y * z2;
  3467. var zz = z * z2;
  3468. var wx = w * x2;
  3469. var wy = w * y2;
  3470. var wz = w * z2;
  3471. var sx = s[0];
  3472. var sy = s[1];
  3473. var sz = s[2];
  3474. var ox = o[0];
  3475. var oy = o[1];
  3476. var oz = o[2];
  3477. var out0 = (1 - (yy + zz)) * sx;
  3478. var out1 = (xy + wz) * sx;
  3479. var out2 = (xz - wy) * sx;
  3480. var out4 = (xy - wz) * sy;
  3481. var out5 = (1 - (xx + zz)) * sy;
  3482. var out6 = (yz + wx) * sy;
  3483. var out8 = (xz + wy) * sz;
  3484. var out9 = (yz - wx) * sz;
  3485. var out10 = (1 - (xx + yy)) * sz;
  3486. out[0] = out0;
  3487. out[1] = out1;
  3488. out[2] = out2;
  3489. out[3] = 0;
  3490. out[4] = out4;
  3491. out[5] = out5;
  3492. out[6] = out6;
  3493. out[7] = 0;
  3494. out[8] = out8;
  3495. out[9] = out9;
  3496. out[10] = out10;
  3497. out[11] = 0;
  3498. out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz);
  3499. out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz);
  3500. out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz);
  3501. out[15] = 1;
  3502. return out;
  3503. }
  3504. function fromQuat(out, q) {
  3505. var x = q[0], y = q[1], z = q[2], w = q[3];
  3506. var x2 = x + x;
  3507. var y2 = y + y;
  3508. var z2 = z + z;
  3509. var xx = x * x2;
  3510. var yx = y * x2;
  3511. var yy = y * y2;
  3512. var zx = z * x2;
  3513. var zy = z * y2;
  3514. var zz = z * z2;
  3515. var wx = w * x2;
  3516. var wy = w * y2;
  3517. var wz = w * z2;
  3518. out[0] = 1 - yy - zz;
  3519. out[1] = yx + wz;
  3520. out[2] = zx - wy;
  3521. out[3] = 0;
  3522. out[4] = yx - wz;
  3523. out[5] = 1 - xx - zz;
  3524. out[6] = zy + wx;
  3525. out[7] = 0;
  3526. out[8] = zx + wy;
  3527. out[9] = zy - wx;
  3528. out[10] = 1 - xx - yy;
  3529. out[11] = 0;
  3530. out[12] = 0;
  3531. out[13] = 0;
  3532. out[14] = 0;
  3533. out[15] = 1;
  3534. return out;
  3535. }
  3536. function frustum(out, left, right, bottom, top, near, far) {
  3537. var rl = 1 / (right - left);
  3538. var tb = 1 / (top - bottom);
  3539. var nf = 1 / (near - far);
  3540. out[0] = near * 2 * rl;
  3541. out[1] = 0;
  3542. out[2] = 0;
  3543. out[3] = 0;
  3544. out[4] = 0;
  3545. out[5] = near * 2 * tb;
  3546. out[6] = 0;
  3547. out[7] = 0;
  3548. out[8] = (right + left) * rl;
  3549. out[9] = (top + bottom) * tb;
  3550. out[10] = (far + near) * nf;
  3551. out[11] = -1;
  3552. out[12] = 0;
  3553. out[13] = 0;
  3554. out[14] = far * near * 2 * nf;
  3555. out[15] = 0;
  3556. return out;
  3557. }
  3558. function perspectiveNO(out, fovy, aspect, near, far) {
  3559. var f = 1 / Math.tan(fovy / 2), nf;
  3560. out[0] = f / aspect;
  3561. out[1] = 0;
  3562. out[2] = 0;
  3563. out[3] = 0;
  3564. out[4] = 0;
  3565. out[5] = f;
  3566. out[6] = 0;
  3567. out[7] = 0;
  3568. out[8] = 0;
  3569. out[9] = 0;
  3570. out[11] = -1;
  3571. out[12] = 0;
  3572. out[13] = 0;
  3573. out[15] = 0;
  3574. if (far != null && far !== Infinity) {
  3575. nf = 1 / (near - far);
  3576. out[10] = (far + near) * nf;
  3577. out[14] = 2 * far * near * nf;
  3578. } else {
  3579. out[10] = -1;
  3580. out[14] = -2 * near;
  3581. }
  3582. return out;
  3583. }
  3584. var perspective = perspectiveNO;
  3585. function perspectiveZO(out, fovy, aspect, near, far) {
  3586. var f = 1 / Math.tan(fovy / 2), nf;
  3587. out[0] = f / aspect;
  3588. out[1] = 0;
  3589. out[2] = 0;
  3590. out[3] = 0;
  3591. out[4] = 0;
  3592. out[5] = f;
  3593. out[6] = 0;
  3594. out[7] = 0;
  3595. out[8] = 0;
  3596. out[9] = 0;
  3597. out[11] = -1;
  3598. out[12] = 0;
  3599. out[13] = 0;
  3600. out[15] = 0;
  3601. if (far != null && far !== Infinity) {
  3602. nf = 1 / (near - far);
  3603. out[10] = far * nf;
  3604. out[14] = far * near * nf;
  3605. } else {
  3606. out[10] = -1;
  3607. out[14] = -near;
  3608. }
  3609. return out;
  3610. }
  3611. function perspectiveFromFieldOfView(out, fov, near, far) {
  3612. var upTan = Math.tan(fov.upDegrees * Math.PI / 180);
  3613. var downTan = Math.tan(fov.downDegrees * Math.PI / 180);
  3614. var leftTan = Math.tan(fov.leftDegrees * Math.PI / 180);
  3615. var rightTan = Math.tan(fov.rightDegrees * Math.PI / 180);
  3616. var xScale = 2 / (leftTan + rightTan);
  3617. var yScale = 2 / (upTan + downTan);
  3618. out[0] = xScale;
  3619. out[1] = 0;
  3620. out[2] = 0;
  3621. out[3] = 0;
  3622. out[4] = 0;
  3623. out[5] = yScale;
  3624. out[6] = 0;
  3625. out[7] = 0;
  3626. out[8] = -((leftTan - rightTan) * xScale * 0.5);
  3627. out[9] = (upTan - downTan) * yScale * 0.5;
  3628. out[10] = far / (near - far);
  3629. out[11] = -1;
  3630. out[12] = 0;
  3631. out[13] = 0;
  3632. out[14] = far * near / (near - far);
  3633. out[15] = 0;
  3634. return out;
  3635. }
  3636. function orthoNO(out, left, right, bottom, top, near, far) {
  3637. var lr = 1 / (left - right);
  3638. var bt = 1 / (bottom - top);
  3639. var nf = 1 / (near - far);
  3640. out[0] = -2 * lr;
  3641. out[1] = 0;
  3642. out[2] = 0;
  3643. out[3] = 0;
  3644. out[4] = 0;
  3645. out[5] = -2 * bt;
  3646. out[6] = 0;
  3647. out[7] = 0;
  3648. out[8] = 0;
  3649. out[9] = 0;
  3650. out[10] = 2 * nf;
  3651. out[11] = 0;
  3652. out[12] = (left + right) * lr;
  3653. out[13] = (top + bottom) * bt;
  3654. out[14] = (far + near) * nf;
  3655. out[15] = 1;
  3656. return out;
  3657. }
  3658. var ortho = orthoNO;
  3659. function orthoZO(out, left, right, bottom, top, near, far) {
  3660. var lr = 1 / (left - right);
  3661. var bt = 1 / (bottom - top);
  3662. var nf = 1 / (near - far);
  3663. out[0] = -2 * lr;
  3664. out[1] = 0;
  3665. out[2] = 0;
  3666. out[3] = 0;
  3667. out[4] = 0;
  3668. out[5] = -2 * bt;
  3669. out[6] = 0;
  3670. out[7] = 0;
  3671. out[8] = 0;
  3672. out[9] = 0;
  3673. out[10] = nf;
  3674. out[11] = 0;
  3675. out[12] = (left + right) * lr;
  3676. out[13] = (top + bottom) * bt;
  3677. out[14] = near * nf;
  3678. out[15] = 1;
  3679. return out;
  3680. }
  3681. function lookAt(out, eye, center, up) {
  3682. var x0, x1, x2, y0, y1, y2, z0, z1, z2, len;
  3683. var eyex = eye[0];
  3684. var eyey = eye[1];
  3685. var eyez = eye[2];
  3686. var upx = up[0];
  3687. var upy = up[1];
  3688. var upz = up[2];
  3689. var centerx = center[0];
  3690. var centery = center[1];
  3691. var centerz = center[2];
  3692. if (Math.abs(eyex - centerx) < EPSILON && Math.abs(eyey - centery) < EPSILON && Math.abs(eyez - centerz) < EPSILON) {
  3693. return identity$2(out);
  3694. }
  3695. z0 = eyex - centerx;
  3696. z1 = eyey - centery;
  3697. z2 = eyez - centerz;
  3698. len = 1 / Math.hypot(z0, z1, z2);
  3699. z0 *= len;
  3700. z1 *= len;
  3701. z2 *= len;
  3702. x0 = upy * z2 - upz * z1;
  3703. x1 = upz * z0 - upx * z2;
  3704. x2 = upx * z1 - upy * z0;
  3705. len = Math.hypot(x0, x1, x2);
  3706. if (!len) {
  3707. x0 = 0;
  3708. x1 = 0;
  3709. x2 = 0;
  3710. } else {
  3711. len = 1 / len;
  3712. x0 *= len;
  3713. x1 *= len;
  3714. x2 *= len;
  3715. }
  3716. y0 = z1 * x2 - z2 * x1;
  3717. y1 = z2 * x0 - z0 * x2;
  3718. y2 = z0 * x1 - z1 * x0;
  3719. len = Math.hypot(y0, y1, y2);
  3720. if (!len) {
  3721. y0 = 0;
  3722. y1 = 0;
  3723. y2 = 0;
  3724. } else {
  3725. len = 1 / len;
  3726. y0 *= len;
  3727. y1 *= len;
  3728. y2 *= len;
  3729. }
  3730. out[0] = x0;
  3731. out[1] = y0;
  3732. out[2] = z0;
  3733. out[3] = 0;
  3734. out[4] = x1;
  3735. out[5] = y1;
  3736. out[6] = z1;
  3737. out[7] = 0;
  3738. out[8] = x2;
  3739. out[9] = y2;
  3740. out[10] = z2;
  3741. out[11] = 0;
  3742. out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
  3743. out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
  3744. out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
  3745. out[15] = 1;
  3746. return out;
  3747. }
  3748. function targetTo(out, eye, target, up) {
  3749. var eyex = eye[0], eyey = eye[1], eyez = eye[2], upx = up[0], upy = up[1], upz = up[2];
  3750. var z0 = eyex - target[0], z1 = eyey - target[1], z2 = eyez - target[2];
  3751. var len = z0 * z0 + z1 * z1 + z2 * z2;
  3752. if (len > 0) {
  3753. len = 1 / Math.sqrt(len);
  3754. z0 *= len;
  3755. z1 *= len;
  3756. z2 *= len;
  3757. }
  3758. var x0 = upy * z2 - upz * z1, x1 = upz * z0 - upx * z2, x2 = upx * z1 - upy * z0;
  3759. len = x0 * x0 + x1 * x1 + x2 * x2;
  3760. if (len > 0) {
  3761. len = 1 / Math.sqrt(len);
  3762. x0 *= len;
  3763. x1 *= len;
  3764. x2 *= len;
  3765. }
  3766. out[0] = x0;
  3767. out[1] = x1;
  3768. out[2] = x2;
  3769. out[3] = 0;
  3770. out[4] = z1 * x2 - z2 * x1;
  3771. out[5] = z2 * x0 - z0 * x2;
  3772. out[6] = z0 * x1 - z1 * x0;
  3773. out[7] = 0;
  3774. out[8] = z0;
  3775. out[9] = z1;
  3776. out[10] = z2;
  3777. out[11] = 0;
  3778. out[12] = eyex;
  3779. out[13] = eyey;
  3780. out[14] = eyez;
  3781. out[15] = 1;
  3782. return out;
  3783. }
  3784. function str$5(a) {
  3785. return "mat4(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ", " + a[8] + ", " + a[9] + ", " + a[10] + ", " + a[11] + ", " + a[12] + ", " + a[13] + ", " + a[14] + ", " + a[15] + ")";
  3786. }
  3787. function frob(a) {
  3788. return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
  3789. }
  3790. function add$5(out, a, b) {
  3791. out[0] = a[0] + b[0];
  3792. out[1] = a[1] + b[1];
  3793. out[2] = a[2] + b[2];
  3794. out[3] = a[3] + b[3];
  3795. out[4] = a[4] + b[4];
  3796. out[5] = a[5] + b[5];
  3797. out[6] = a[6] + b[6];
  3798. out[7] = a[7] + b[7];
  3799. out[8] = a[8] + b[8];
  3800. out[9] = a[9] + b[9];
  3801. out[10] = a[10] + b[10];
  3802. out[11] = a[11] + b[11];
  3803. out[12] = a[12] + b[12];
  3804. out[13] = a[13] + b[13];
  3805. out[14] = a[14] + b[14];
  3806. out[15] = a[15] + b[15];
  3807. return out;
  3808. }
  3809. function subtract$3(out, a, b) {
  3810. out[0] = a[0] - b[0];
  3811. out[1] = a[1] - b[1];
  3812. out[2] = a[2] - b[2];
  3813. out[3] = a[3] - b[3];
  3814. out[4] = a[4] - b[4];
  3815. out[5] = a[5] - b[5];
  3816. out[6] = a[6] - b[6];
  3817. out[7] = a[7] - b[7];
  3818. out[8] = a[8] - b[8];
  3819. out[9] = a[9] - b[9];
  3820. out[10] = a[10] - b[10];
  3821. out[11] = a[11] - b[11];
  3822. out[12] = a[12] - b[12];
  3823. out[13] = a[13] - b[13];
  3824. out[14] = a[14] - b[14];
  3825. out[15] = a[15] - b[15];
  3826. return out;
  3827. }
  3828. function multiplyScalar(out, a, b) {
  3829. out[0] = a[0] * b;
  3830. out[1] = a[1] * b;
  3831. out[2] = a[2] * b;
  3832. out[3] = a[3] * b;
  3833. out[4] = a[4] * b;
  3834. out[5] = a[5] * b;
  3835. out[6] = a[6] * b;
  3836. out[7] = a[7] * b;
  3837. out[8] = a[8] * b;
  3838. out[9] = a[9] * b;
  3839. out[10] = a[10] * b;
  3840. out[11] = a[11] * b;
  3841. out[12] = a[12] * b;
  3842. out[13] = a[13] * b;
  3843. out[14] = a[14] * b;
  3844. out[15] = a[15] * b;
  3845. return out;
  3846. }
  3847. function multiplyScalarAndAdd(out, a, b, scale2) {
  3848. out[0] = a[0] + b[0] * scale2;
  3849. out[1] = a[1] + b[1] * scale2;
  3850. out[2] = a[2] + b[2] * scale2;
  3851. out[3] = a[3] + b[3] * scale2;
  3852. out[4] = a[4] + b[4] * scale2;
  3853. out[5] = a[5] + b[5] * scale2;
  3854. out[6] = a[6] + b[6] * scale2;
  3855. out[7] = a[7] + b[7] * scale2;
  3856. out[8] = a[8] + b[8] * scale2;
  3857. out[9] = a[9] + b[9] * scale2;
  3858. out[10] = a[10] + b[10] * scale2;
  3859. out[11] = a[11] + b[11] * scale2;
  3860. out[12] = a[12] + b[12] * scale2;
  3861. out[13] = a[13] + b[13] * scale2;
  3862. out[14] = a[14] + b[14] * scale2;
  3863. out[15] = a[15] + b[15] * scale2;
  3864. return out;
  3865. }
  3866. function exactEquals$5(a, b) {
  3867. 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];
  3868. }
  3869. function equals$5(a, b) {
  3870. var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
  3871. var a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7];
  3872. var a8 = a[8], a9 = a[9], a10 = a[10], a11 = a[11];
  3873. var a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15];
  3874. var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
  3875. var b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7];
  3876. var b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11];
  3877. var b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
  3878. return Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1, Math.abs(a8), Math.abs(b8)) && Math.abs(a9 - b9) <= EPSILON * Math.max(1, Math.abs(a9), Math.abs(b9)) && Math.abs(a10 - b10) <= EPSILON * Math.max(1, Math.abs(a10), Math.abs(b10)) && Math.abs(a11 - b11) <= EPSILON * Math.max(1, Math.abs(a11), Math.abs(b11)) && Math.abs(a12 - b12) <= EPSILON * Math.max(1, Math.abs(a12), Math.abs(b12)) && Math.abs(a13 - b13) <= EPSILON * Math.max(1, Math.abs(a13), Math.abs(b13)) && Math.abs(a14 - b14) <= EPSILON * Math.max(1, Math.abs(a14), Math.abs(b14)) && Math.abs(a15 - b15) <= EPSILON * Math.max(1, Math.abs(a15), Math.abs(b15));
  3879. }
  3880. var mul$5 = multiply$5;
  3881. var sub$3 = subtract$3;
  3882. const mat4 = /* @__PURE__ */ Object.freeze({
  3883. __proto__: null,
  3884. add: add$5,
  3885. adjoint,
  3886. clone: clone$5,
  3887. copy: copy$5,
  3888. create: create$5,
  3889. determinant,
  3890. equals: equals$5,
  3891. exactEquals: exactEquals$5,
  3892. frob,
  3893. fromQuat,
  3894. fromQuat2,
  3895. fromRotation: fromRotation$1,
  3896. fromRotationTranslation: fromRotationTranslation$1,
  3897. fromRotationTranslationScale,
  3898. fromRotationTranslationScaleOrigin,
  3899. fromScaling,
  3900. fromTranslation: fromTranslation$1,
  3901. fromValues: fromValues$5,
  3902. fromXRotation,
  3903. fromYRotation,
  3904. fromZRotation,
  3905. frustum,
  3906. getRotation,
  3907. getScaling,
  3908. getTranslation: getTranslation$1,
  3909. identity: identity$2,
  3910. invert: invert$2,
  3911. lookAt,
  3912. mul: mul$5,
  3913. multiply: multiply$5,
  3914. multiplyScalar,
  3915. multiplyScalarAndAdd,
  3916. ortho,
  3917. orthoNO,
  3918. orthoZO,
  3919. perspective,
  3920. perspectiveFromFieldOfView,
  3921. perspectiveNO,
  3922. perspectiveZO,
  3923. rotate: rotate$1,
  3924. rotateX: rotateX$3,
  3925. rotateY: rotateY$3,
  3926. rotateZ: rotateZ$3,
  3927. scale: scale$5,
  3928. set: set$5,
  3929. str: str$5,
  3930. sub: sub$3,
  3931. subtract: subtract$3,
  3932. targetTo,
  3933. translate: translate$1,
  3934. transpose
  3935. });
  3936. function create$4() {
  3937. var out = new ARRAY_TYPE(3);
  3938. if (ARRAY_TYPE != Float32Array) {
  3939. out[0] = 0;
  3940. out[1] = 0;
  3941. out[2] = 0;
  3942. }
  3943. return out;
  3944. }
  3945. function clone$4(a) {
  3946. var out = new ARRAY_TYPE(3);
  3947. out[0] = a[0];
  3948. out[1] = a[1];
  3949. out[2] = a[2];
  3950. return out;
  3951. }
  3952. function length$4(a) {
  3953. var x = a[0];
  3954. var y = a[1];
  3955. var z = a[2];
  3956. return Math.hypot(x, y, z);
  3957. }
  3958. function fromValues$4(x, y, z) {
  3959. var out = new ARRAY_TYPE(3);
  3960. out[0] = x;
  3961. out[1] = y;
  3962. out[2] = z;
  3963. return out;
  3964. }
  3965. function copy$4(out, a) {
  3966. out[0] = a[0];
  3967. out[1] = a[1];
  3968. out[2] = a[2];
  3969. return out;
  3970. }
  3971. function set$4(out, x, y, z) {
  3972. out[0] = x;
  3973. out[1] = y;
  3974. out[2] = z;
  3975. return out;
  3976. }
  3977. function add$4(out, a, b) {
  3978. out[0] = a[0] + b[0];
  3979. out[1] = a[1] + b[1];
  3980. out[2] = a[2] + b[2];
  3981. return out;
  3982. }
  3983. function subtract$2(out, a, b) {
  3984. out[0] = a[0] - b[0];
  3985. out[1] = a[1] - b[1];
  3986. out[2] = a[2] - b[2];
  3987. return out;
  3988. }
  3989. function multiply$4(out, a, b) {
  3990. out[0] = a[0] * b[0];
  3991. out[1] = a[1] * b[1];
  3992. out[2] = a[2] * b[2];
  3993. return out;
  3994. }
  3995. function divide$2(out, a, b) {
  3996. out[0] = a[0] / b[0];
  3997. out[1] = a[1] / b[1];
  3998. out[2] = a[2] / b[2];
  3999. return out;
  4000. }
  4001. function ceil$2(out, a) {
  4002. out[0] = Math.ceil(a[0]);
  4003. out[1] = Math.ceil(a[1]);
  4004. out[2] = Math.ceil(a[2]);
  4005. return out;
  4006. }
  4007. function floor$2(out, a) {
  4008. out[0] = Math.floor(a[0]);
  4009. out[1] = Math.floor(a[1]);
  4010. out[2] = Math.floor(a[2]);
  4011. return out;
  4012. }
  4013. function min$2(out, a, b) {
  4014. out[0] = Math.min(a[0], b[0]);
  4015. out[1] = Math.min(a[1], b[1]);
  4016. out[2] = Math.min(a[2], b[2]);
  4017. return out;
  4018. }
  4019. function max$2(out, a, b) {
  4020. out[0] = Math.max(a[0], b[0]);
  4021. out[1] = Math.max(a[1], b[1]);
  4022. out[2] = Math.max(a[2], b[2]);
  4023. return out;
  4024. }
  4025. function round$2(out, a) {
  4026. out[0] = Math.round(a[0]);
  4027. out[1] = Math.round(a[1]);
  4028. out[2] = Math.round(a[2]);
  4029. return out;
  4030. }
  4031. function scale$4(out, a, b) {
  4032. out[0] = a[0] * b;
  4033. out[1] = a[1] * b;
  4034. out[2] = a[2] * b;
  4035. return out;
  4036. }
  4037. function scaleAndAdd$2(out, a, b, scale2) {
  4038. out[0] = a[0] + b[0] * scale2;
  4039. out[1] = a[1] + b[1] * scale2;
  4040. out[2] = a[2] + b[2] * scale2;
  4041. return out;
  4042. }
  4043. function distance$2(a, b) {
  4044. var x = b[0] - a[0];
  4045. var y = b[1] - a[1];
  4046. var z = b[2] - a[2];
  4047. return Math.hypot(x, y, z);
  4048. }
  4049. function squaredDistance$2(a, b) {
  4050. var x = b[0] - a[0];
  4051. var y = b[1] - a[1];
  4052. var z = b[2] - a[2];
  4053. return x * x + y * y + z * z;
  4054. }
  4055. function squaredLength$4(a) {
  4056. var x = a[0];
  4057. var y = a[1];
  4058. var z = a[2];
  4059. return x * x + y * y + z * z;
  4060. }
  4061. function negate$2(out, a) {
  4062. out[0] = -a[0];
  4063. out[1] = -a[1];
  4064. out[2] = -a[2];
  4065. return out;
  4066. }
  4067. function inverse$2(out, a) {
  4068. out[0] = 1 / a[0];
  4069. out[1] = 1 / a[1];
  4070. out[2] = 1 / a[2];
  4071. return out;
  4072. }
  4073. function normalize$4(out, a) {
  4074. var x = a[0];
  4075. var y = a[1];
  4076. var z = a[2];
  4077. var len = x * x + y * y + z * z;
  4078. if (len > 0) {
  4079. len = 1 / Math.sqrt(len);
  4080. }
  4081. out[0] = a[0] * len;
  4082. out[1] = a[1] * len;
  4083. out[2] = a[2] * len;
  4084. return out;
  4085. }
  4086. function dot$4(a, b) {
  4087. return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
  4088. }
  4089. function cross$2(out, a, b) {
  4090. var ax = a[0], ay = a[1], az = a[2];
  4091. var bx = b[0], by = b[1], bz = b[2];
  4092. out[0] = ay * bz - az * by;
  4093. out[1] = az * bx - ax * bz;
  4094. out[2] = ax * by - ay * bx;
  4095. return out;
  4096. }
  4097. function lerp$4(out, a, b, t) {
  4098. var ax = a[0];
  4099. var ay = a[1];
  4100. var az = a[2];
  4101. out[0] = ax + t * (b[0] - ax);
  4102. out[1] = ay + t * (b[1] - ay);
  4103. out[2] = az + t * (b[2] - az);
  4104. return out;
  4105. }
  4106. function hermite(out, a, b, c, d, t) {
  4107. var factorTimes2 = t * t;
  4108. var factor1 = factorTimes2 * (2 * t - 3) + 1;
  4109. var factor2 = factorTimes2 * (t - 2) + t;
  4110. var factor3 = factorTimes2 * (t - 1);
  4111. var factor4 = factorTimes2 * (3 - 2 * t);
  4112. out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
  4113. out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
  4114. out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
  4115. return out;
  4116. }
  4117. function bezier(out, a, b, c, d, t) {
  4118. var inverseFactor = 1 - t;
  4119. var inverseFactorTimesTwo = inverseFactor * inverseFactor;
  4120. var factorTimes2 = t * t;
  4121. var factor1 = inverseFactorTimesTwo * inverseFactor;
  4122. var factor2 = 3 * t * inverseFactorTimesTwo;
  4123. var factor3 = 3 * factorTimes2 * inverseFactor;
  4124. var factor4 = factorTimes2 * t;
  4125. out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
  4126. out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
  4127. out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
  4128. return out;
  4129. }
  4130. function random$3(out, scale2) {
  4131. scale2 = scale2 || 1;
  4132. var r = RANDOM() * 2 * Math.PI;
  4133. var z = RANDOM() * 2 - 1;
  4134. var zScale = Math.sqrt(1 - z * z) * scale2;
  4135. out[0] = Math.cos(r) * zScale;
  4136. out[1] = Math.sin(r) * zScale;
  4137. out[2] = z * scale2;
  4138. return out;
  4139. }
  4140. function transformMat4$2(out, a, m) {
  4141. var x = a[0], y = a[1], z = a[2];
  4142. var w = m[3] * x + m[7] * y + m[11] * z + m[15];
  4143. w = w || 1;
  4144. out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
  4145. out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
  4146. out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
  4147. return out;
  4148. }
  4149. function transformMat3$1(out, a, m) {
  4150. var x = a[0], y = a[1], z = a[2];
  4151. out[0] = x * m[0] + y * m[3] + z * m[6];
  4152. out[1] = x * m[1] + y * m[4] + z * m[7];
  4153. out[2] = x * m[2] + y * m[5] + z * m[8];
  4154. return out;
  4155. }
  4156. function transformQuat$1(out, a, q) {
  4157. var qx = q[0], qy = q[1], qz = q[2], qw = q[3];
  4158. var x = a[0], y = a[1], z = a[2];
  4159. var uvx = qy * z - qz * y, uvy = qz * x - qx * z, uvz = qx * y - qy * x;
  4160. var uuvx = qy * uvz - qz * uvy, uuvy = qz * uvx - qx * uvz, uuvz = qx * uvy - qy * uvx;
  4161. var w2 = qw * 2;
  4162. uvx *= w2;
  4163. uvy *= w2;
  4164. uvz *= w2;
  4165. uuvx *= 2;
  4166. uuvy *= 2;
  4167. uuvz *= 2;
  4168. out[0] = x + uvx + uuvx;
  4169. out[1] = y + uvy + uuvy;
  4170. out[2] = z + uvz + uuvz;
  4171. return out;
  4172. }
  4173. function rotateX$2(out, a, b, rad) {
  4174. var p = [], r = [];
  4175. p[0] = a[0] - b[0];
  4176. p[1] = a[1] - b[1];
  4177. p[2] = a[2] - b[2];
  4178. r[0] = p[0];
  4179. r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad);
  4180. r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad);
  4181. out[0] = r[0] + b[0];
  4182. out[1] = r[1] + b[1];
  4183. out[2] = r[2] + b[2];
  4184. return out;
  4185. }
  4186. function rotateY$2(out, a, b, rad) {
  4187. var p = [], r = [];
  4188. p[0] = a[0] - b[0];
  4189. p[1] = a[1] - b[1];
  4190. p[2] = a[2] - b[2];
  4191. r[0] = p[2] * Math.sin(rad) + p[0] * Math.cos(rad);
  4192. r[1] = p[1];
  4193. r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad);
  4194. out[0] = r[0] + b[0];
  4195. out[1] = r[1] + b[1];
  4196. out[2] = r[2] + b[2];
  4197. return out;
  4198. }
  4199. function rotateZ$2(out, a, b, rad) {
  4200. var p = [], r = [];
  4201. p[0] = a[0] - b[0];
  4202. p[1] = a[1] - b[1];
  4203. p[2] = a[2] - b[2];
  4204. r[0] = p[0] * Math.cos(rad) - p[1] * Math.sin(rad);
  4205. r[1] = p[0] * Math.sin(rad) + p[1] * Math.cos(rad);
  4206. r[2] = p[2];
  4207. out[0] = r[0] + b[0];
  4208. out[1] = r[1] + b[1];
  4209. out[2] = r[2] + b[2];
  4210. return out;
  4211. }
  4212. function angle$1(a, b) {
  4213. var ax = a[0], ay = a[1], az = a[2], bx = b[0], by = b[1], bz = b[2], mag1 = Math.sqrt(ax * ax + ay * ay + az * az), mag2 = Math.sqrt(bx * bx + by * by + bz * bz), mag = mag1 * mag2, cosine = mag && dot$4(a, b) / mag;
  4214. return Math.acos(Math.min(Math.max(cosine, -1), 1));
  4215. }
  4216. function zero$2(out) {
  4217. out[0] = 0;
  4218. out[1] = 0;
  4219. out[2] = 0;
  4220. return out;
  4221. }
  4222. function str$4(a) {
  4223. return "vec3(" + a[0] + ", " + a[1] + ", " + a[2] + ")";
  4224. }
  4225. function exactEquals$4(a, b) {
  4226. return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
  4227. }
  4228. function equals$4(a, b) {
  4229. var a0 = a[0], a1 = a[1], a2 = a[2];
  4230. var b0 = b[0], b1 = b[1], b2 = b[2];
  4231. return Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2));
  4232. }
  4233. var sub$2 = subtract$2;
  4234. var mul$4 = multiply$4;
  4235. var div$2 = divide$2;
  4236. var dist$2 = distance$2;
  4237. var sqrDist$2 = squaredDistance$2;
  4238. var len$4 = length$4;
  4239. var sqrLen$4 = squaredLength$4;
  4240. var forEach$2 = function() {
  4241. var vec = create$4();
  4242. return function(a, stride, offset2, count, fn, arg) {
  4243. var i, l;
  4244. if (!stride) {
  4245. stride = 3;
  4246. }
  4247. if (!offset2) {
  4248. offset2 = 0;
  4249. }
  4250. if (count) {
  4251. l = Math.min(count * stride + offset2, a.length);
  4252. } else {
  4253. l = a.length;
  4254. }
  4255. for (i = offset2; i < l; i += stride) {
  4256. vec[0] = a[i];
  4257. vec[1] = a[i + 1];
  4258. vec[2] = a[i + 2];
  4259. fn(vec, vec, arg);
  4260. a[i] = vec[0];
  4261. a[i + 1] = vec[1];
  4262. a[i + 2] = vec[2];
  4263. }
  4264. return a;
  4265. };
  4266. }();
  4267. const vec3 = /* @__PURE__ */ Object.freeze({
  4268. __proto__: null,
  4269. add: add$4,
  4270. angle: angle$1,
  4271. bezier,
  4272. ceil: ceil$2,
  4273. clone: clone$4,
  4274. copy: copy$4,
  4275. create: create$4,
  4276. cross: cross$2,
  4277. dist: dist$2,
  4278. distance: distance$2,
  4279. div: div$2,
  4280. divide: divide$2,
  4281. dot: dot$4,
  4282. equals: equals$4,
  4283. exactEquals: exactEquals$4,
  4284. floor: floor$2,
  4285. forEach: forEach$2,
  4286. fromValues: fromValues$4,
  4287. hermite,
  4288. inverse: inverse$2,
  4289. len: len$4,
  4290. length: length$4,
  4291. lerp: lerp$4,
  4292. max: max$2,
  4293. min: min$2,
  4294. mul: mul$4,
  4295. multiply: multiply$4,
  4296. negate: negate$2,
  4297. normalize: normalize$4,
  4298. random: random$3,
  4299. rotateX: rotateX$2,
  4300. rotateY: rotateY$2,
  4301. rotateZ: rotateZ$2,
  4302. round: round$2,
  4303. scale: scale$4,
  4304. scaleAndAdd: scaleAndAdd$2,
  4305. set: set$4,
  4306. sqrDist: sqrDist$2,
  4307. sqrLen: sqrLen$4,
  4308. squaredDistance: squaredDistance$2,
  4309. squaredLength: squaredLength$4,
  4310. str: str$4,
  4311. sub: sub$2,
  4312. subtract: subtract$2,
  4313. transformMat3: transformMat3$1,
  4314. transformMat4: transformMat4$2,
  4315. transformQuat: transformQuat$1,
  4316. zero: zero$2
  4317. });
  4318. function create$3() {
  4319. var out = new ARRAY_TYPE(4);
  4320. if (ARRAY_TYPE != Float32Array) {
  4321. out[0] = 0;
  4322. out[1] = 0;
  4323. out[2] = 0;
  4324. out[3] = 0;
  4325. }
  4326. return out;
  4327. }
  4328. function normalize$3(out, a) {
  4329. var x = a[0];
  4330. var y = a[1];
  4331. var z = a[2];
  4332. var w = a[3];
  4333. var len = x * x + y * y + z * z + w * w;
  4334. if (len > 0) {
  4335. len = 1 / Math.sqrt(len);
  4336. }
  4337. out[0] = x * len;
  4338. out[1] = y * len;
  4339. out[2] = z * len;
  4340. out[3] = w * len;
  4341. return out;
  4342. }
  4343. (function() {
  4344. var vec = create$3();
  4345. return function(a, stride, offset2, count, fn, arg) {
  4346. var i, l;
  4347. if (!stride) {
  4348. stride = 4;
  4349. }
  4350. if (!offset2) {
  4351. offset2 = 0;
  4352. }
  4353. if (count) {
  4354. l = Math.min(count * stride + offset2, a.length);
  4355. } else {
  4356. l = a.length;
  4357. }
  4358. for (i = offset2; i < l; i += stride) {
  4359. vec[0] = a[i];
  4360. vec[1] = a[i + 1];
  4361. vec[2] = a[i + 2];
  4362. vec[3] = a[i + 3];
  4363. fn(vec, vec, arg);
  4364. a[i] = vec[0];
  4365. a[i + 1] = vec[1];
  4366. a[i + 2] = vec[2];
  4367. a[i + 3] = vec[3];
  4368. }
  4369. return a;
  4370. };
  4371. })();
  4372. function create$2() {
  4373. var out = new ARRAY_TYPE(4);
  4374. if (ARRAY_TYPE != Float32Array) {
  4375. out[0] = 0;
  4376. out[1] = 0;
  4377. out[2] = 0;
  4378. }
  4379. out[3] = 1;
  4380. return out;
  4381. }
  4382. function setAxisAngle(out, axis, rad) {
  4383. rad = rad * 0.5;
  4384. var s = Math.sin(rad);
  4385. out[0] = s * axis[0];
  4386. out[1] = s * axis[1];
  4387. out[2] = s * axis[2];
  4388. out[3] = Math.cos(rad);
  4389. return out;
  4390. }
  4391. function slerp(out, a, b, t) {
  4392. var ax = a[0], ay = a[1], az = a[2], aw = a[3];
  4393. var bx = b[0], by = b[1], bz = b[2], bw = b[3];
  4394. var omega, cosom, sinom, scale0, scale1;
  4395. cosom = ax * bx + ay * by + az * bz + aw * bw;
  4396. if (cosom < 0) {
  4397. cosom = -cosom;
  4398. bx = -bx;
  4399. by = -by;
  4400. bz = -bz;
  4401. bw = -bw;
  4402. }
  4403. if (1 - cosom > EPSILON) {
  4404. omega = Math.acos(cosom);
  4405. sinom = Math.sin(omega);
  4406. scale0 = Math.sin((1 - t) * omega) / sinom;
  4407. scale1 = Math.sin(t * omega) / sinom;
  4408. } else {
  4409. scale0 = 1 - t;
  4410. scale1 = t;
  4411. }
  4412. out[0] = scale0 * ax + scale1 * bx;
  4413. out[1] = scale0 * ay + scale1 * by;
  4414. out[2] = scale0 * az + scale1 * bz;
  4415. out[3] = scale0 * aw + scale1 * bw;
  4416. return out;
  4417. }
  4418. function fromMat3(out, m) {
  4419. var fTrace = m[0] + m[4] + m[8];
  4420. var fRoot;
  4421. if (fTrace > 0) {
  4422. fRoot = Math.sqrt(fTrace + 1);
  4423. out[3] = 0.5 * fRoot;
  4424. fRoot = 0.5 / fRoot;
  4425. out[0] = (m[5] - m[7]) * fRoot;
  4426. out[1] = (m[6] - m[2]) * fRoot;
  4427. out[2] = (m[1] - m[3]) * fRoot;
  4428. } else {
  4429. var i = 0;
  4430. if (m[4] > m[0])
  4431. i = 1;
  4432. if (m[8] > m[i * 3 + i])
  4433. i = 2;
  4434. var j = (i + 1) % 3;
  4435. var k = (i + 2) % 3;
  4436. fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1);
  4437. out[i] = 0.5 * fRoot;
  4438. fRoot = 0.5 / fRoot;
  4439. out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot;
  4440. out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot;
  4441. out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot;
  4442. }
  4443. return out;
  4444. }
  4445. var normalize$2 = normalize$3;
  4446. (function() {
  4447. var tmpvec3 = create$4();
  4448. var xUnitVec3 = fromValues$4(1, 0, 0);
  4449. var yUnitVec3 = fromValues$4(0, 1, 0);
  4450. return function(out, a, b) {
  4451. var dot = dot$4(a, b);
  4452. if (dot < -0.999999) {
  4453. cross$2(tmpvec3, xUnitVec3, a);
  4454. if (len$4(tmpvec3) < 1e-6)
  4455. cross$2(tmpvec3, yUnitVec3, a);
  4456. normalize$4(tmpvec3, tmpvec3);
  4457. setAxisAngle(out, tmpvec3, Math.PI);
  4458. return out;
  4459. } else if (dot > 0.999999) {
  4460. out[0] = 0;
  4461. out[1] = 0;
  4462. out[2] = 0;
  4463. out[3] = 1;
  4464. return out;
  4465. } else {
  4466. cross$2(tmpvec3, a, b);
  4467. out[0] = tmpvec3[0];
  4468. out[1] = tmpvec3[1];
  4469. out[2] = tmpvec3[2];
  4470. out[3] = 1 + dot;
  4471. return normalize$2(out, out);
  4472. }
  4473. };
  4474. })();
  4475. (function() {
  4476. var temp1 = create$2();
  4477. var temp2 = create$2();
  4478. return function(out, a, b, c, d, t) {
  4479. slerp(temp1, a, d, t);
  4480. slerp(temp2, b, c, t);
  4481. slerp(out, temp1, temp2, 2 * t * (1 - t));
  4482. return out;
  4483. };
  4484. })();
  4485. (function() {
  4486. var matr = create$6();
  4487. return function(out, view, right, up) {
  4488. matr[0] = right[0];
  4489. matr[3] = right[1];
  4490. matr[6] = right[2];
  4491. matr[1] = up[0];
  4492. matr[4] = up[1];
  4493. matr[7] = up[2];
  4494. matr[2] = -view[0];
  4495. matr[5] = -view[1];
  4496. matr[8] = -view[2];
  4497. return normalize$2(out, fromMat3(out, matr));
  4498. };
  4499. })();
  4500. function create() {
  4501. var out = new ARRAY_TYPE(2);
  4502. if (ARRAY_TYPE != Float32Array) {
  4503. out[0] = 0;
  4504. out[1] = 0;
  4505. }
  4506. return out;
  4507. }
  4508. (function() {
  4509. var vec = create();
  4510. return function(a, stride, offset2, count, fn, arg) {
  4511. var i, l;
  4512. if (!stride) {
  4513. stride = 2;
  4514. }
  4515. if (!offset2) {
  4516. offset2 = 0;
  4517. }
  4518. if (count) {
  4519. l = Math.min(count * stride + offset2, a.length);
  4520. } else {
  4521. l = a.length;
  4522. }
  4523. for (i = offset2; i < l; i += stride) {
  4524. vec[0] = a[i];
  4525. vec[1] = a[i + 1];
  4526. fn(vec, vec, arg);
  4527. a[i] = vec[0];
  4528. a[i + 1] = vec[1];
  4529. }
  4530. return a;
  4531. };
  4532. })();
  4533. class AnimationControl {
  4534. /** @type {object} */
  4535. #animationData;
  4536. /** @type {Promise<void>} */
  4537. #finishedPromise;
  4538. #willFinish;
  4539. /**
  4540. * Defines a static empty / void animation control.
  4541. *
  4542. * @type {AnimationControl}
  4543. */
  4544. static #voidControl = new AnimationControl(null);
  4545. /**
  4546. * Provides a static void / undefined AnimationControl that is automatically resolved.
  4547. *
  4548. * @returns {AnimationControl} Void AnimationControl
  4549. */
  4550. static get voidControl() {
  4551. return this.#voidControl;
  4552. }
  4553. /**
  4554. * @param {object|null} [animationData] - Animation data from {@link AnimationAPI}.
  4555. *
  4556. * @param {boolean} [willFinish] - Promise that tracks animation finished state.
  4557. */
  4558. constructor(animationData, willFinish = false) {
  4559. this.#animationData = animationData;
  4560. this.#willFinish = willFinish;
  4561. if (isObject(animationData)) {
  4562. animationData.control = this;
  4563. }
  4564. }
  4565. /**
  4566. * Get a promise that resolves when animation is finished.
  4567. *
  4568. * @returns {Promise<void>}
  4569. */
  4570. get finished() {
  4571. if (!(this.#finishedPromise instanceof Promise)) {
  4572. this.#finishedPromise = this.#willFinish ? new Promise((resolve) => this.#animationData.resolve = resolve) : Promise.resolve();
  4573. }
  4574. return this.#finishedPromise;
  4575. }
  4576. /**
  4577. * Returns whether this animation is currently active / animating.
  4578. *
  4579. * Note: a delayed animation may not be started / active yet. Use {@link AnimationControl.isFinished} to determine
  4580. * if an animation is actually finished.
  4581. *
  4582. * @returns {boolean} Animation active state.
  4583. */
  4584. get isActive() {
  4585. return this.#animationData.active;
  4586. }
  4587. /**
  4588. * Returns whether this animation is completely finished.
  4589. *
  4590. * @returns {boolean} Animation finished state.
  4591. */
  4592. get isFinished() {
  4593. return this.#animationData.finished;
  4594. }
  4595. /**
  4596. * Cancels the animation.
  4597. */
  4598. cancel() {
  4599. const animationData = this.#animationData;
  4600. if (animationData === null || animationData === void 0) {
  4601. return;
  4602. }
  4603. animationData.cancelled = true;
  4604. }
  4605. }
  4606. class AnimationManager {
  4607. /**
  4608. * @type {object[]}
  4609. */
  4610. static activeList = [];
  4611. /**
  4612. * @type {object[]}
  4613. */
  4614. static newList = [];
  4615. /**
  4616. * @type {number}
  4617. */
  4618. static current;
  4619. /**
  4620. * Add animation data.
  4621. *
  4622. * @param {object} data -
  4623. */
  4624. static add(data) {
  4625. const now2 = performance.now();
  4626. data.start = now2 + (AnimationManager.current - now2);
  4627. AnimationManager.newList.push(data);
  4628. }
  4629. /**
  4630. * Manage all animation
  4631. */
  4632. static animate() {
  4633. const current = AnimationManager.current = performance.now();
  4634. if (AnimationManager.activeList.length === 0 && AnimationManager.newList.length === 0) {
  4635. globalThis.requestAnimationFrame(AnimationManager.animate);
  4636. return;
  4637. }
  4638. if (AnimationManager.newList.length) {
  4639. for (let cntr = AnimationManager.newList.length; --cntr >= 0; ) {
  4640. const data = AnimationManager.newList[cntr];
  4641. if (data.cancelled) {
  4642. AnimationManager.newList.splice(cntr, 1);
  4643. data.cleanup(data);
  4644. }
  4645. if (data.active) {
  4646. AnimationManager.newList.splice(cntr, 1);
  4647. AnimationManager.activeList.push(data);
  4648. }
  4649. }
  4650. }
  4651. for (let cntr = AnimationManager.activeList.length; --cntr >= 0; ) {
  4652. const data = AnimationManager.activeList[cntr];
  4653. if (data.cancelled || data.el !== void 0 && !data.el.isConnected) {
  4654. AnimationManager.activeList.splice(cntr, 1);
  4655. data.cleanup(data);
  4656. continue;
  4657. }
  4658. data.current = current - data.start;
  4659. if (data.current >= data.duration) {
  4660. for (let dataCntr = data.keys.length; --dataCntr >= 0; ) {
  4661. const key = data.keys[dataCntr];
  4662. data.newData[key] = data.destination[key];
  4663. }
  4664. data.position.set(data.newData);
  4665. AnimationManager.activeList.splice(cntr, 1);
  4666. data.cleanup(data);
  4667. continue;
  4668. }
  4669. const easedTime = data.ease(data.current / data.duration);
  4670. for (let dataCntr = data.keys.length; --dataCntr >= 0; ) {
  4671. const key = data.keys[dataCntr];
  4672. data.newData[key] = data.interpolate(data.initial[key], data.destination[key], easedTime);
  4673. }
  4674. data.position.set(data.newData);
  4675. }
  4676. globalThis.requestAnimationFrame(AnimationManager.animate);
  4677. }
  4678. /**
  4679. * Cancels all animations for given Position instance.
  4680. *
  4681. * @param {Position} position - Position instance.
  4682. */
  4683. static cancel(position) {
  4684. for (let cntr = AnimationManager.activeList.length; --cntr >= 0; ) {
  4685. const data = AnimationManager.activeList[cntr];
  4686. if (data.position === position) {
  4687. AnimationManager.activeList.splice(cntr, 1);
  4688. data.cancelled = true;
  4689. data.cleanup(data);
  4690. }
  4691. }
  4692. for (let cntr = AnimationManager.newList.length; --cntr >= 0; ) {
  4693. const data = AnimationManager.newList[cntr];
  4694. if (data.position === position) {
  4695. AnimationManager.newList.splice(cntr, 1);
  4696. data.cancelled = true;
  4697. data.cleanup(data);
  4698. }
  4699. }
  4700. }
  4701. /**
  4702. * Cancels all active and delayed animations.
  4703. */
  4704. static cancelAll() {
  4705. for (let cntr = AnimationManager.activeList.length; --cntr >= 0; ) {
  4706. const data = AnimationManager.activeList[cntr];
  4707. data.cancelled = true;
  4708. data.cleanup(data);
  4709. }
  4710. for (let cntr = AnimationManager.newList.length; --cntr >= 0; ) {
  4711. const data = AnimationManager.newList[cntr];
  4712. data.cancelled = true;
  4713. data.cleanup(data);
  4714. }
  4715. AnimationManager.activeList.length = 0;
  4716. AnimationManager.newList.length = 0;
  4717. }
  4718. /**
  4719. * Gets all {@link AnimationControl} instances for a given Position instance.
  4720. *
  4721. * @param {Position} position - Position instance.
  4722. *
  4723. * @returns {AnimationControl[]} All scheduled AnimationControl instances for the given Position instance.
  4724. */
  4725. static getScheduled(position) {
  4726. const results = [];
  4727. for (let cntr = AnimationManager.activeList.length; --cntr >= 0; ) {
  4728. const data = AnimationManager.activeList[cntr];
  4729. if (data.position === position) {
  4730. results.push(data.control);
  4731. }
  4732. }
  4733. for (let cntr = AnimationManager.newList.length; --cntr >= 0; ) {
  4734. const data = AnimationManager.newList[cntr];
  4735. if (data.position === position) {
  4736. results.push(data.control);
  4737. }
  4738. }
  4739. return results;
  4740. }
  4741. }
  4742. AnimationManager.animate();
  4743. const animateKeys = /* @__PURE__ */ new Set([
  4744. // Main keys
  4745. "left",
  4746. "top",
  4747. "maxWidth",
  4748. "maxHeight",
  4749. "minWidth",
  4750. "minHeight",
  4751. "width",
  4752. "height",
  4753. "rotateX",
  4754. "rotateY",
  4755. "rotateZ",
  4756. "scale",
  4757. "translateX",
  4758. "translateY",
  4759. "translateZ",
  4760. "zIndex",
  4761. // Aliases
  4762. "rotation"
  4763. ]);
  4764. const transformKeys = ["rotateX", "rotateY", "rotateZ", "scale", "translateX", "translateY", "translateZ"];
  4765. Object.freeze(transformKeys);
  4766. const relativeRegex = /^([-+*])=(-?[\d]*\.?[\d]+)$/;
  4767. const numericDefaults = {
  4768. // Other keys
  4769. height: 0,
  4770. left: 0,
  4771. maxHeight: null,
  4772. maxWidth: null,
  4773. minHeight: null,
  4774. minWidth: null,
  4775. top: 0,
  4776. transformOrigin: null,
  4777. width: 0,
  4778. zIndex: null,
  4779. rotateX: 0,
  4780. rotateY: 0,
  4781. rotateZ: 0,
  4782. scale: 1,
  4783. translateX: 0,
  4784. translateY: 0,
  4785. translateZ: 0,
  4786. rotation: 0
  4787. };
  4788. Object.freeze(numericDefaults);
  4789. function setNumericDefaults(data) {
  4790. if (data.rotateX === null) {
  4791. data.rotateX = 0;
  4792. }
  4793. if (data.rotateY === null) {
  4794. data.rotateY = 0;
  4795. }
  4796. if (data.rotateZ === null) {
  4797. data.rotateZ = 0;
  4798. }
  4799. if (data.translateX === null) {
  4800. data.translateX = 0;
  4801. }
  4802. if (data.translateY === null) {
  4803. data.translateY = 0;
  4804. }
  4805. if (data.translateZ === null) {
  4806. data.translateZ = 0;
  4807. }
  4808. if (data.scale === null) {
  4809. data.scale = 1;
  4810. }
  4811. if (data.rotation === null) {
  4812. data.rotation = 0;
  4813. }
  4814. }
  4815. const transformKeysBitwise = {
  4816. rotateX: 1,
  4817. rotateY: 2,
  4818. rotateZ: 4,
  4819. scale: 8,
  4820. translateX: 16,
  4821. translateY: 32,
  4822. translateZ: 64
  4823. };
  4824. Object.freeze(transformKeysBitwise);
  4825. const transformOriginDefault = "top left";
  4826. const transformOrigins = [
  4827. "top left",
  4828. "top center",
  4829. "top right",
  4830. "center left",
  4831. "center",
  4832. "center right",
  4833. "bottom left",
  4834. "bottom center",
  4835. "bottom right"
  4836. ];
  4837. Object.freeze(transformOrigins);
  4838. function convertRelative(positionData, position) {
  4839. for (const key in positionData) {
  4840. if (animateKeys.has(key)) {
  4841. const value = positionData[key];
  4842. if (typeof value !== "string") {
  4843. continue;
  4844. }
  4845. if (value === "auto" || value === "inherit") {
  4846. continue;
  4847. }
  4848. const regexResults = relativeRegex.exec(value);
  4849. if (!regexResults) {
  4850. throw new Error(
  4851. `convertRelative error: malformed relative key (${key}) with value (${value})`
  4852. );
  4853. }
  4854. const current = position[key];
  4855. switch (regexResults[1]) {
  4856. case "-":
  4857. positionData[key] = current - parseFloat(regexResults[2]);
  4858. break;
  4859. case "+":
  4860. positionData[key] = current + parseFloat(regexResults[2]);
  4861. break;
  4862. case "*":
  4863. positionData[key] = current * parseFloat(regexResults[2]);
  4864. break;
  4865. }
  4866. }
  4867. }
  4868. }
  4869. class AnimationAPI {
  4870. /** @type {PositionData} */
  4871. #data;
  4872. /** @type {Position} */
  4873. #position;
  4874. /**
  4875. * Tracks the number of animation control instances that are active.
  4876. *
  4877. * @type {number}
  4878. */
  4879. #instanceCount = 0;
  4880. /**
  4881. * Provides a bound function to pass as data to AnimationManager to invoke
  4882. *
  4883. * @type {Function}
  4884. * @see {AnimationAPI.#cleanupInstance}
  4885. */
  4886. #cleanup;
  4887. constructor(position, data) {
  4888. this.#position = position;
  4889. this.#data = data;
  4890. this.#cleanup = this.#cleanupInstance.bind(this);
  4891. }
  4892. /**
  4893. * Returns whether there are scheduled animations whether active or delayed for this Position.
  4894. *
  4895. * @returns {boolean} Are there active animation instances.
  4896. */
  4897. get isScheduled() {
  4898. return this.#instanceCount > 0;
  4899. }
  4900. /**
  4901. * Adds / schedules an animation w/ the AnimationManager. This contains the final steps common to all tweens.
  4902. *
  4903. * @param {object} initial -
  4904. *
  4905. * @param {object} destination -
  4906. *
  4907. * @param {number} duration -
  4908. *
  4909. * @param {HTMLElement} el -
  4910. *
  4911. * @param {number} delay -
  4912. *
  4913. * @param {Function} ease -
  4914. *
  4915. * @param {Function} interpolate -
  4916. *
  4917. * @returns {AnimationControl} The associated animation control.
  4918. */
  4919. #addAnimation(initial, destination, duration, el, delay, ease, interpolate2) {
  4920. setNumericDefaults(initial);
  4921. setNumericDefaults(destination);
  4922. for (const key in initial) {
  4923. if (!Number.isFinite(initial[key])) {
  4924. delete initial[key];
  4925. }
  4926. }
  4927. const keys = Object.keys(initial);
  4928. const newData = Object.assign({ immediateElementUpdate: true }, initial);
  4929. if (keys.length === 0) {
  4930. return AnimationControl.voidControl;
  4931. }
  4932. const animationData = {
  4933. active: true,
  4934. cleanup: this.#cleanup,
  4935. cancelled: false,
  4936. control: void 0,
  4937. current: 0,
  4938. destination,
  4939. duration: duration * 1e3,
  4940. // Internally the AnimationManager works in ms.
  4941. ease,
  4942. el,
  4943. finished: false,
  4944. initial,
  4945. interpolate: interpolate2,
  4946. keys,
  4947. newData,
  4948. position: this.#position,
  4949. resolve: void 0,
  4950. start: void 0
  4951. };
  4952. if (delay > 0) {
  4953. animationData.active = false;
  4954. setTimeout(() => {
  4955. if (!animationData.cancelled) {
  4956. animationData.active = true;
  4957. const now2 = performance.now();
  4958. animationData.start = now2 + (AnimationManager.current - now2);
  4959. }
  4960. }, delay * 1e3);
  4961. }
  4962. this.#instanceCount++;
  4963. AnimationManager.add(animationData);
  4964. return new AnimationControl(animationData, true);
  4965. }
  4966. /**
  4967. * Cancels all animation instances for this Position instance.
  4968. */
  4969. cancel() {
  4970. AnimationManager.cancel(this.#position);
  4971. }
  4972. /**
  4973. * Cleans up an animation instance.
  4974. *
  4975. * @param {object} data - Animation data for an animation instance.
  4976. */
  4977. #cleanupInstance(data) {
  4978. this.#instanceCount--;
  4979. data.active = false;
  4980. data.finished = true;
  4981. if (typeof data.resolve === "function") {
  4982. data.resolve(data.cancelled);
  4983. }
  4984. }
  4985. /**
  4986. * Returns all currently scheduled AnimationControl instances for this Position instance.
  4987. *
  4988. * @returns {AnimationControl[]} All currently scheduled animation controls for this Position instance.
  4989. */
  4990. getScheduled() {
  4991. return AnimationManager.getScheduled(this.#position);
  4992. }
  4993. /**
  4994. * Provides a tween from given position data to the current position.
  4995. *
  4996. * @param {PositionDataExtended} fromData - The starting position.
  4997. *
  4998. * @param {object} [opts] - Optional parameters.
  4999. *
  5000. * @param {number} [opts.delay=0] - Delay in seconds before animation starts.
  5001. *
  5002. * @param {number} [opts.duration=1] - Duration in seconds.
  5003. *
  5004. * @param {Function} [opts.ease=cubicOut] - Easing function.
  5005. *
  5006. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  5007. *
  5008. * @returns {AnimationControl} A control object that can cancel animation and provides a `finished` Promise.
  5009. */
  5010. from(fromData, { delay = 0, duration = 1, ease = cubicOut, interpolate: interpolate2 = lerp$5 } = {}) {
  5011. if (!isObject(fromData)) {
  5012. throw new TypeError(`AnimationAPI.from error: 'fromData' is not an object.`);
  5013. }
  5014. const position = this.#position;
  5015. const parent = position.parent;
  5016. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  5017. return AnimationControl.voidControl;
  5018. }
  5019. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  5020. const el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  5021. if (!Number.isFinite(delay) || delay < 0) {
  5022. throw new TypeError(`AnimationAPI.from error: 'delay' is not a positive number.`);
  5023. }
  5024. if (!Number.isFinite(duration) || duration < 0) {
  5025. throw new TypeError(`AnimationAPI.from error: 'duration' is not a positive number.`);
  5026. }
  5027. if (typeof ease !== "function") {
  5028. throw new TypeError(`AnimationAPI.from error: 'ease' is not a function.`);
  5029. }
  5030. if (typeof interpolate2 !== "function") {
  5031. throw new TypeError(`AnimationAPI.from error: 'interpolate' is not a function.`);
  5032. }
  5033. const initial = {};
  5034. const destination = {};
  5035. const data = this.#data;
  5036. for (const key in fromData) {
  5037. if (data[key] !== void 0 && fromData[key] !== data[key]) {
  5038. initial[key] = fromData[key];
  5039. destination[key] = data[key];
  5040. }
  5041. }
  5042. convertRelative(initial, data);
  5043. return this.#addAnimation(initial, destination, duration, el, delay, ease, interpolate2);
  5044. }
  5045. /**
  5046. * Provides a tween from given position data to the current position.
  5047. *
  5048. * @param {PositionDataExtended} fromData - The starting position.
  5049. *
  5050. * @param {PositionDataExtended} toData - The ending position.
  5051. *
  5052. * @param {object} [opts] - Optional parameters.
  5053. *
  5054. * @param {number} [opts.delay=0] - Delay in seconds before animation starts.
  5055. *
  5056. * @param {number} [opts.duration=1] - Duration in seconds.
  5057. *
  5058. * @param {Function} [opts.ease=cubicOut] - Easing function.
  5059. *
  5060. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  5061. *
  5062. * @returns {AnimationControl} A control object that can cancel animation and provides a `finished` Promise.
  5063. */
  5064. fromTo(fromData, toData, { delay = 0, duration = 1, ease = cubicOut, interpolate: interpolate2 = lerp$5 } = {}) {
  5065. if (!isObject(fromData)) {
  5066. throw new TypeError(`AnimationAPI.fromTo error: 'fromData' is not an object.`);
  5067. }
  5068. if (!isObject(toData)) {
  5069. throw new TypeError(`AnimationAPI.fromTo error: 'toData' is not an object.`);
  5070. }
  5071. const parent = this.#position.parent;
  5072. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  5073. return AnimationControl.voidControl;
  5074. }
  5075. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  5076. const el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  5077. if (!Number.isFinite(delay) || delay < 0) {
  5078. throw new TypeError(`AnimationAPI.fromTo error: 'delay' is not a positive number.`);
  5079. }
  5080. if (!Number.isFinite(duration) || duration < 0) {
  5081. throw new TypeError(`AnimationAPI.fromTo error: 'duration' is not a positive number.`);
  5082. }
  5083. if (typeof ease !== "function") {
  5084. throw new TypeError(`AnimationAPI.fromTo error: 'ease' is not a function.`);
  5085. }
  5086. if (typeof interpolate2 !== "function") {
  5087. throw new TypeError(`AnimationAPI.fromTo error: 'interpolate' is not a function.`);
  5088. }
  5089. const initial = {};
  5090. const destination = {};
  5091. const data = this.#data;
  5092. for (const key in fromData) {
  5093. if (toData[key] === void 0) {
  5094. console.warn(
  5095. `AnimationAPI.fromTo warning: key ('${key}') from 'fromData' missing in 'toData'; skipping this key.`
  5096. );
  5097. continue;
  5098. }
  5099. if (data[key] !== void 0) {
  5100. initial[key] = fromData[key];
  5101. destination[key] = toData[key];
  5102. }
  5103. }
  5104. convertRelative(initial, data);
  5105. convertRelative(destination, data);
  5106. return this.#addAnimation(initial, destination, duration, el, delay, ease, interpolate2);
  5107. }
  5108. /**
  5109. * Provides a tween to given position data from the current position.
  5110. *
  5111. * @param {PositionDataExtended} toData - The destination position.
  5112. *
  5113. * @param {object} [opts] - Optional parameters.
  5114. *
  5115. * @param {number} [opts.delay=0] - Delay in seconds before animation starts.
  5116. *
  5117. * @param {number} [opts.duration=1] - Duration in seconds.
  5118. *
  5119. * @param {Function} [opts.ease=cubicOut] - Easing function.
  5120. *
  5121. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  5122. *
  5123. * @returns {AnimationControl} A control object that can cancel animation and provides a `finished` Promise.
  5124. */
  5125. to(toData, { delay = 0, duration = 1, ease = cubicOut, interpolate: interpolate2 = lerp$5 } = {}) {
  5126. if (!isObject(toData)) {
  5127. throw new TypeError(`AnimationAPI.to error: 'toData' is not an object.`);
  5128. }
  5129. const parent = this.#position.parent;
  5130. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  5131. return AnimationControl.voidControl;
  5132. }
  5133. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  5134. const el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  5135. if (!Number.isFinite(delay) || delay < 0) {
  5136. throw new TypeError(`AnimationAPI.to error: 'delay' is not a positive number.`);
  5137. }
  5138. if (!Number.isFinite(duration) || duration < 0) {
  5139. throw new TypeError(`AnimationAPI.to error: 'duration' is not a positive number.`);
  5140. }
  5141. if (typeof ease !== "function") {
  5142. throw new TypeError(`AnimationAPI.to error: 'ease' is not a function.`);
  5143. }
  5144. if (typeof interpolate2 !== "function") {
  5145. throw new TypeError(`AnimationAPI.to error: 'interpolate' is not a function.`);
  5146. }
  5147. const initial = {};
  5148. const destination = {};
  5149. const data = this.#data;
  5150. for (const key in toData) {
  5151. if (data[key] !== void 0 && toData[key] !== data[key]) {
  5152. destination[key] = toData[key];
  5153. initial[key] = data[key];
  5154. }
  5155. }
  5156. convertRelative(destination, data);
  5157. return this.#addAnimation(initial, destination, duration, el, delay, ease, interpolate2);
  5158. }
  5159. /**
  5160. * Returns a function that provides an optimized way to constantly update a to-tween.
  5161. *
  5162. * @param {Iterable<string>} keys - The keys for quickTo.
  5163. *
  5164. * @param {object} [opts] - Optional parameters.
  5165. *
  5166. * @param {number} [opts.duration=1] - Duration in seconds.
  5167. *
  5168. * @param {Function} [opts.ease=cubicOut] - Easing function.
  5169. *
  5170. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  5171. *
  5172. * @returns {quickToCallback} quick-to tween function.
  5173. */
  5174. quickTo(keys, { duration = 1, ease = cubicOut, interpolate: interpolate2 = lerp$5 } = {}) {
  5175. if (!isIterable(keys)) {
  5176. throw new TypeError(`AnimationAPI.quickTo error: 'keys' is not an iterable list.`);
  5177. }
  5178. const parent = this.#position.parent;
  5179. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  5180. throw new Error(`AnimationAPI.quickTo error: 'parent' is not positionable.`);
  5181. }
  5182. if (!Number.isFinite(duration) || duration < 0) {
  5183. throw new TypeError(`AnimationAPI.quickTo error: 'duration' is not a positive number.`);
  5184. }
  5185. if (typeof ease !== "function") {
  5186. throw new TypeError(`AnimationAPI.quickTo error: 'ease' is not a function.`);
  5187. }
  5188. if (typeof interpolate2 !== "function") {
  5189. throw new TypeError(`AnimationAPI.quickTo error: 'interpolate' is not a function.`);
  5190. }
  5191. const initial = {};
  5192. const destination = {};
  5193. const data = this.#data;
  5194. for (const key of keys) {
  5195. if (typeof key !== "string") {
  5196. throw new TypeError(`AnimationAPI.quickTo error: key is not a string.`);
  5197. }
  5198. if (!animateKeys.has(key)) {
  5199. throw new Error(`AnimationAPI.quickTo error: key ('${key}') is not animatable.`);
  5200. }
  5201. if (data[key] !== void 0) {
  5202. destination[key] = data[key];
  5203. initial[key] = data[key];
  5204. }
  5205. }
  5206. const keysArray = [...keys];
  5207. Object.freeze(keysArray);
  5208. const newData = Object.assign({ immediateElementUpdate: true }, initial);
  5209. const animationData = {
  5210. active: true,
  5211. cleanup: this.#cleanup,
  5212. cancelled: false,
  5213. control: void 0,
  5214. current: 0,
  5215. destination,
  5216. duration: duration * 1e3,
  5217. // Internally the AnimationManager works in ms.
  5218. ease,
  5219. el: void 0,
  5220. finished: true,
  5221. // Note: start in finished state to add to AnimationManager on first callback.
  5222. initial,
  5223. interpolate: interpolate2,
  5224. keys,
  5225. newData,
  5226. position: this.#position,
  5227. resolve: void 0,
  5228. start: void 0
  5229. };
  5230. const quickToCB = (...args) => {
  5231. const argsLength = args.length;
  5232. if (argsLength === 0) {
  5233. return;
  5234. }
  5235. for (let cntr = keysArray.length; --cntr >= 0; ) {
  5236. const key = keysArray[cntr];
  5237. if (data[key] !== void 0) {
  5238. initial[key] = data[key];
  5239. }
  5240. }
  5241. if (isObject(args[0])) {
  5242. const objData = args[0];
  5243. for (const key in objData) {
  5244. if (destination[key] !== void 0) {
  5245. destination[key] = objData[key];
  5246. }
  5247. }
  5248. } else {
  5249. for (let cntr = 0; cntr < argsLength && cntr < keysArray.length; cntr++) {
  5250. const key = keysArray[cntr];
  5251. if (destination[key] !== void 0) {
  5252. destination[key] = args[cntr];
  5253. }
  5254. }
  5255. }
  5256. convertRelative(destination, data);
  5257. setNumericDefaults(initial);
  5258. setNumericDefaults(destination);
  5259. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  5260. animationData.el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  5261. if (animationData.finished) {
  5262. animationData.finished = false;
  5263. animationData.active = true;
  5264. animationData.current = 0;
  5265. this.#instanceCount++;
  5266. AnimationManager.add(animationData);
  5267. } else {
  5268. const now2 = performance.now();
  5269. animationData.start = now2 + (AnimationManager.current - now2);
  5270. animationData.current = 0;
  5271. }
  5272. };
  5273. quickToCB.keys = keysArray;
  5274. quickToCB.options = ({ duration: duration2, ease: ease2, interpolate: interpolate3 } = {}) => {
  5275. if (duration2 !== void 0 && (!Number.isFinite(duration2) || duration2 < 0)) {
  5276. throw new TypeError(`AnimationAPI.quickTo.options error: 'duration' is not a positive number.`);
  5277. }
  5278. if (ease2 !== void 0 && typeof ease2 !== "function") {
  5279. throw new TypeError(`AnimationAPI.quickTo.options error: 'ease' is not a function.`);
  5280. }
  5281. if (interpolate3 !== void 0 && typeof interpolate3 !== "function") {
  5282. throw new TypeError(`AnimationAPI.quickTo.options error: 'interpolate' is not a function.`);
  5283. }
  5284. if (duration2 >= 0) {
  5285. animationData.duration = duration2 * 1e3;
  5286. }
  5287. if (ease2) {
  5288. animationData.ease = ease2;
  5289. }
  5290. if (interpolate3) {
  5291. animationData.interpolate = interpolate3;
  5292. }
  5293. return quickToCB;
  5294. };
  5295. return quickToCB;
  5296. }
  5297. }
  5298. class AnimationGroupControl {
  5299. /** @type {AnimationControl[]} */
  5300. #animationControls;
  5301. /** @type {Promise<Awaited<unknown>[]>} */
  5302. #finishedPromise;
  5303. /**
  5304. * Defines a static empty / void animation control.
  5305. *
  5306. * @type {AnimationGroupControl}
  5307. */
  5308. static #voidControl = new AnimationGroupControl(null);
  5309. /**
  5310. * Provides a static void / undefined AnimationGroupControl that is automatically resolved.
  5311. *
  5312. * @returns {AnimationGroupControl} Void AnimationGroupControl
  5313. */
  5314. static get voidControl() {
  5315. return this.#voidControl;
  5316. }
  5317. /**
  5318. * @param {AnimationControl[]} animationControls - An array of AnimationControl instances.
  5319. */
  5320. constructor(animationControls) {
  5321. this.#animationControls = animationControls;
  5322. }
  5323. /**
  5324. * Get a promise that resolves when all animations are finished.
  5325. *
  5326. * @returns {Promise<Awaited<unknown>[]>|Promise<void>} Finished Promise for all animations.
  5327. */
  5328. get finished() {
  5329. const animationControls = this.#animationControls;
  5330. if (animationControls === null || animationControls === void 0) {
  5331. return Promise.resolve();
  5332. }
  5333. if (!(this.#finishedPromise instanceof Promise)) {
  5334. const promises = [];
  5335. for (let cntr = animationControls.length; --cntr >= 0; ) {
  5336. promises.push(animationControls[cntr].finished);
  5337. }
  5338. this.#finishedPromise = Promise.all(promises);
  5339. }
  5340. return this.#finishedPromise;
  5341. }
  5342. /**
  5343. * Returns whether there are active animation instances for this group.
  5344. *
  5345. * Note: a delayed animation may not be started / active yet. Use {@link AnimationGroupControl.isFinished} to
  5346. * determine if all animations in the group are finished.
  5347. *
  5348. * @returns {boolean} Are there active animation instances.
  5349. */
  5350. get isActive() {
  5351. const animationControls = this.#animationControls;
  5352. if (animationControls === null || animationControls === void 0) {
  5353. return false;
  5354. }
  5355. for (let cntr = animationControls.length; --cntr >= 0; ) {
  5356. if (animationControls[cntr].isActive) {
  5357. return true;
  5358. }
  5359. }
  5360. return false;
  5361. }
  5362. /**
  5363. * Returns whether all animations in the group are finished.
  5364. *
  5365. * @returns {boolean} Are all animation instances finished.
  5366. */
  5367. get isFinished() {
  5368. const animationControls = this.#animationControls;
  5369. if (animationControls === null || animationControls === void 0) {
  5370. return true;
  5371. }
  5372. for (let cntr = animationControls.length; --cntr >= 0; ) {
  5373. if (!animationControls[cntr].isFinished) {
  5374. return false;
  5375. }
  5376. }
  5377. return false;
  5378. }
  5379. /**
  5380. * Cancels the all animations.
  5381. */
  5382. cancel() {
  5383. const animationControls = this.#animationControls;
  5384. if (animationControls === null || animationControls === void 0) {
  5385. return;
  5386. }
  5387. for (let cntr = this.#animationControls.length; --cntr >= 0; ) {
  5388. this.#animationControls[cntr].cancel();
  5389. }
  5390. }
  5391. }
  5392. class AnimationGroupAPI {
  5393. /**
  5394. * Checks of the given object is a Position instance by checking for AnimationAPI.
  5395. *
  5396. * @param {*} object - Any data.
  5397. *
  5398. * @returns {boolean} Is Position.
  5399. */
  5400. static #isPosition(object) {
  5401. return isObject(object) && object.animate instanceof AnimationAPI;
  5402. }
  5403. /**
  5404. * Cancels any animation for given Position data.
  5405. *
  5406. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5407. */
  5408. static cancel(position) {
  5409. if (isIterable(position)) {
  5410. let index = -1;
  5411. for (const entry of position) {
  5412. index++;
  5413. const actualPosition = this.#isPosition(entry) ? entry : entry.position;
  5414. if (!this.#isPosition(actualPosition)) {
  5415. console.warn(`AnimationGroupAPI.cancel warning: No Position instance found at index: ${index}.`);
  5416. continue;
  5417. }
  5418. AnimationManager.cancel(actualPosition);
  5419. }
  5420. } else {
  5421. const actualPosition = this.#isPosition(position) ? position : position.position;
  5422. if (!this.#isPosition(actualPosition)) {
  5423. console.warn(`AnimationGroupAPI.cancel warning: No Position instance found.`);
  5424. return;
  5425. }
  5426. AnimationManager.cancel(actualPosition);
  5427. }
  5428. }
  5429. /**
  5430. * Cancels all Position animation.
  5431. */
  5432. static cancelAll() {
  5433. AnimationManager.cancelAll();
  5434. }
  5435. /**
  5436. * Gets all animation controls for the given position data.
  5437. *
  5438. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5439. *
  5440. * @returns {{position: Position, data: object|void, controls: AnimationControl[]}[]} Results array.
  5441. */
  5442. static getScheduled(position) {
  5443. const results = [];
  5444. if (isIterable(position)) {
  5445. let index = -1;
  5446. for (const entry of position) {
  5447. index++;
  5448. const isPosition = this.#isPosition(entry);
  5449. const actualPosition = isPosition ? entry : entry.position;
  5450. if (!this.#isPosition(actualPosition)) {
  5451. console.warn(`AnimationGroupAPI.getScheduled warning: No Position instance found at index: ${index}.`);
  5452. continue;
  5453. }
  5454. const controls = AnimationManager.getScheduled(actualPosition);
  5455. results.push({ position: actualPosition, data: isPosition ? void 0 : entry, controls });
  5456. }
  5457. } else {
  5458. const isPosition = this.#isPosition(position);
  5459. const actualPosition = isPosition ? position : position.position;
  5460. if (!this.#isPosition(actualPosition)) {
  5461. console.warn(`AnimationGroupAPI.getScheduled warning: No Position instance found.`);
  5462. return results;
  5463. }
  5464. const controls = AnimationManager.getScheduled(actualPosition);
  5465. results.push({ position: actualPosition, data: isPosition ? void 0 : position, controls });
  5466. }
  5467. return results;
  5468. }
  5469. /**
  5470. * Provides the `from` animation tween for one or more Position instances as a group.
  5471. *
  5472. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5473. *
  5474. * @param {object|Function} fromData -
  5475. *
  5476. * @param {object|Function} options -
  5477. *
  5478. * @returns {TJSBasicAnimation} Basic animation control.
  5479. */
  5480. static from(position, fromData, options) {
  5481. if (!isObject(fromData) && typeof fromData !== "function") {
  5482. throw new TypeError(`AnimationGroupAPI.from error: 'fromData' is not an object or function.`);
  5483. }
  5484. if (options !== void 0 && !isObject(options) && typeof options !== "function") {
  5485. throw new TypeError(`AnimationGroupAPI.from error: 'options' is not an object or function.`);
  5486. }
  5487. const animationControls = [];
  5488. let index = -1;
  5489. let callbackOptions;
  5490. const hasDataCallback = typeof fromData === "function";
  5491. const hasOptionCallback = typeof options === "function";
  5492. const hasCallback = hasDataCallback || hasOptionCallback;
  5493. if (hasCallback) {
  5494. callbackOptions = { index, position: void 0, data: void 0 };
  5495. }
  5496. let actualFromData = fromData;
  5497. let actualOptions = options;
  5498. if (isIterable(position)) {
  5499. for (const entry of position) {
  5500. index++;
  5501. const isPosition = this.#isPosition(entry);
  5502. const actualPosition = isPosition ? entry : entry.position;
  5503. if (!this.#isPosition(actualPosition)) {
  5504. console.warn(`AnimationGroupAPI.from warning: No Position instance found at index: ${index}.`);
  5505. continue;
  5506. }
  5507. if (hasCallback) {
  5508. callbackOptions.index = index;
  5509. callbackOptions.position = position;
  5510. callbackOptions.data = isPosition ? void 0 : entry;
  5511. }
  5512. if (hasDataCallback) {
  5513. actualFromData = fromData(callbackOptions);
  5514. if (actualFromData === null || actualFromData === void 0) {
  5515. continue;
  5516. }
  5517. if (typeof actualFromData !== "object") {
  5518. throw new TypeError(`AnimationGroupAPI.from error: fromData callback function iteration(${index}) failed to return an object.`);
  5519. }
  5520. }
  5521. if (hasOptionCallback) {
  5522. actualOptions = options(callbackOptions);
  5523. if (actualOptions === null || actualOptions === void 0) {
  5524. continue;
  5525. }
  5526. if (typeof actualOptions !== "object") {
  5527. throw new TypeError(`AnimationGroupAPI.from error: options callback function iteration(${index}) failed to return an object.`);
  5528. }
  5529. }
  5530. animationControls.push(actualPosition.animate.from(actualFromData, actualOptions));
  5531. }
  5532. } else {
  5533. const isPosition = this.#isPosition(position);
  5534. const actualPosition = isPosition ? position : position.position;
  5535. if (!this.#isPosition(actualPosition)) {
  5536. console.warn(`AnimationGroupAPI.from warning: No Position instance found.`);
  5537. return AnimationGroupControl.voidControl;
  5538. }
  5539. if (hasCallback) {
  5540. callbackOptions.index = 0;
  5541. callbackOptions.position = position;
  5542. callbackOptions.data = isPosition ? void 0 : position;
  5543. }
  5544. if (hasDataCallback) {
  5545. actualFromData = fromData(callbackOptions);
  5546. if (typeof actualFromData !== "object") {
  5547. throw new TypeError(
  5548. `AnimationGroupAPI.from error: fromData callback function failed to return an object.`
  5549. );
  5550. }
  5551. }
  5552. if (hasOptionCallback) {
  5553. actualOptions = options(callbackOptions);
  5554. if (typeof actualOptions !== "object") {
  5555. throw new TypeError(
  5556. `AnimationGroupAPI.from error: options callback function failed to return an object.`
  5557. );
  5558. }
  5559. }
  5560. animationControls.push(actualPosition.animate.from(actualFromData, actualOptions));
  5561. }
  5562. return new AnimationGroupControl(animationControls);
  5563. }
  5564. /**
  5565. * Provides the `fromTo` animation tween for one or more Position instances as a group.
  5566. *
  5567. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5568. *
  5569. * @param {object|Function} fromData -
  5570. *
  5571. * @param {object|Function} toData -
  5572. *
  5573. * @param {object|Function} options -
  5574. *
  5575. * @returns {TJSBasicAnimation} Basic animation control.
  5576. */
  5577. static fromTo(position, fromData, toData, options) {
  5578. if (!isObject(fromData) && typeof fromData !== "function") {
  5579. throw new TypeError(`AnimationGroupAPI.fromTo error: 'fromData' is not an object or function.`);
  5580. }
  5581. if (!isObject(toData) && typeof toData !== "function") {
  5582. throw new TypeError(`AnimationGroupAPI.fromTo error: 'toData' is not an object or function.`);
  5583. }
  5584. if (options !== void 0 && !isObject(options) && typeof options !== "function") {
  5585. throw new TypeError(`AnimationGroupAPI.fromTo error: 'options' is not an object or function.`);
  5586. }
  5587. const animationControls = [];
  5588. let index = -1;
  5589. let callbackOptions;
  5590. const hasFromCallback = typeof fromData === "function";
  5591. const hasToCallback = typeof toData === "function";
  5592. const hasOptionCallback = typeof options === "function";
  5593. const hasCallback = hasFromCallback || hasToCallback || hasOptionCallback;
  5594. if (hasCallback) {
  5595. callbackOptions = { index, position: void 0, data: void 0 };
  5596. }
  5597. let actualFromData = fromData;
  5598. let actualToData = toData;
  5599. let actualOptions = options;
  5600. if (isIterable(position)) {
  5601. for (const entry of position) {
  5602. index++;
  5603. const isPosition = this.#isPosition(entry);
  5604. const actualPosition = isPosition ? entry : entry.position;
  5605. if (!this.#isPosition(actualPosition)) {
  5606. console.warn(`AnimationGroupAPI.fromTo warning: No Position instance found at index: ${index}.`);
  5607. continue;
  5608. }
  5609. if (hasCallback) {
  5610. callbackOptions.index = index;
  5611. callbackOptions.position = position;
  5612. callbackOptions.data = isPosition ? void 0 : entry;
  5613. }
  5614. if (hasFromCallback) {
  5615. actualFromData = fromData(callbackOptions);
  5616. if (actualFromData === null || actualFromData === void 0) {
  5617. continue;
  5618. }
  5619. if (typeof actualFromData !== "object") {
  5620. throw new TypeError(`AnimationGroupAPI.fromTo error: fromData callback function iteration(${index}) failed to return an object.`);
  5621. }
  5622. }
  5623. if (hasToCallback) {
  5624. actualToData = toData(callbackOptions);
  5625. if (actualToData === null || actualToData === void 0) {
  5626. continue;
  5627. }
  5628. if (typeof actualToData !== "object") {
  5629. throw new TypeError(`AnimationGroupAPI.fromTo error: toData callback function iteration(${index}) failed to return an object.`);
  5630. }
  5631. }
  5632. if (hasOptionCallback) {
  5633. actualOptions = options(callbackOptions);
  5634. if (actualOptions === null || actualOptions === void 0) {
  5635. continue;
  5636. }
  5637. if (typeof actualOptions !== "object") {
  5638. throw new TypeError(`AnimationGroupAPI.fromTo error: options callback function iteration(${index}) failed to return an object.`);
  5639. }
  5640. }
  5641. animationControls.push(actualPosition.animate.fromTo(actualFromData, actualToData, actualOptions));
  5642. }
  5643. } else {
  5644. const isPosition = this.#isPosition(position);
  5645. const actualPosition = isPosition ? position : position.position;
  5646. if (!this.#isPosition(actualPosition)) {
  5647. console.warn(`AnimationGroupAPI.fromTo warning: No Position instance found.`);
  5648. return AnimationGroupControl.voidControl;
  5649. }
  5650. if (hasCallback) {
  5651. callbackOptions.index = 0;
  5652. callbackOptions.position = position;
  5653. callbackOptions.data = isPosition ? void 0 : position;
  5654. }
  5655. if (hasFromCallback) {
  5656. actualFromData = fromData(callbackOptions);
  5657. if (typeof actualFromData !== "object") {
  5658. throw new TypeError(
  5659. `AnimationGroupAPI.fromTo error: fromData callback function failed to return an object.`
  5660. );
  5661. }
  5662. }
  5663. if (hasToCallback) {
  5664. actualToData = toData(callbackOptions);
  5665. if (typeof actualToData !== "object") {
  5666. throw new TypeError(
  5667. `AnimationGroupAPI.fromTo error: toData callback function failed to return an object.`
  5668. );
  5669. }
  5670. }
  5671. if (hasOptionCallback) {
  5672. actualOptions = options(callbackOptions);
  5673. if (typeof actualOptions !== "object") {
  5674. throw new TypeError(
  5675. `AnimationGroupAPI.fromTo error: options callback function failed to return an object.`
  5676. );
  5677. }
  5678. }
  5679. animationControls.push(actualPosition.animate.fromTo(actualFromData, actualToData, actualOptions));
  5680. }
  5681. return new AnimationGroupControl(animationControls);
  5682. }
  5683. /**
  5684. * Provides the `to` animation tween for one or more Position instances as a group.
  5685. *
  5686. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5687. *
  5688. * @param {object|Function} toData -
  5689. *
  5690. * @param {object|Function} options -
  5691. *
  5692. * @returns {TJSBasicAnimation} Basic animation control.
  5693. */
  5694. static to(position, toData, options) {
  5695. if (!isObject(toData) && typeof toData !== "function") {
  5696. throw new TypeError(`AnimationGroupAPI.to error: 'toData' is not an object or function.`);
  5697. }
  5698. if (options !== void 0 && !isObject(options) && typeof options !== "function") {
  5699. throw new TypeError(`AnimationGroupAPI.to error: 'options' is not an object or function.`);
  5700. }
  5701. const animationControls = [];
  5702. let index = -1;
  5703. let callbackOptions;
  5704. const hasDataCallback = typeof toData === "function";
  5705. const hasOptionCallback = typeof options === "function";
  5706. const hasCallback = hasDataCallback || hasOptionCallback;
  5707. if (hasCallback) {
  5708. callbackOptions = { index, position: void 0, data: void 0 };
  5709. }
  5710. let actualToData = toData;
  5711. let actualOptions = options;
  5712. if (isIterable(position)) {
  5713. for (const entry of position) {
  5714. index++;
  5715. const isPosition = this.#isPosition(entry);
  5716. const actualPosition = isPosition ? entry : entry.position;
  5717. if (!this.#isPosition(actualPosition)) {
  5718. console.warn(`AnimationGroupAPI.to warning: No Position instance found at index: ${index}.`);
  5719. continue;
  5720. }
  5721. if (hasCallback) {
  5722. callbackOptions.index = index;
  5723. callbackOptions.position = position;
  5724. callbackOptions.data = isPosition ? void 0 : entry;
  5725. }
  5726. if (hasDataCallback) {
  5727. actualToData = toData(callbackOptions);
  5728. if (actualToData === null || actualToData === void 0) {
  5729. continue;
  5730. }
  5731. if (typeof actualToData !== "object") {
  5732. throw new TypeError(`AnimationGroupAPI.to error: toData callback function iteration(${index}) failed to return an object.`);
  5733. }
  5734. }
  5735. if (hasOptionCallback) {
  5736. actualOptions = options(callbackOptions);
  5737. if (actualOptions === null || actualOptions === void 0) {
  5738. continue;
  5739. }
  5740. if (typeof actualOptions !== "object") {
  5741. throw new TypeError(`AnimationGroupAPI.to error: options callback function iteration(${index}) failed to return an object.`);
  5742. }
  5743. }
  5744. animationControls.push(actualPosition.animate.to(actualToData, actualOptions));
  5745. }
  5746. } else {
  5747. const isPosition = this.#isPosition(position);
  5748. const actualPosition = isPosition ? position : position.position;
  5749. if (!this.#isPosition(actualPosition)) {
  5750. console.warn(`AnimationGroupAPI.to warning: No Position instance found.`);
  5751. return AnimationGroupControl.voidControl;
  5752. }
  5753. if (hasCallback) {
  5754. callbackOptions.index = 0;
  5755. callbackOptions.position = position;
  5756. callbackOptions.data = isPosition ? void 0 : position;
  5757. }
  5758. if (hasDataCallback) {
  5759. actualToData = toData(callbackOptions);
  5760. if (typeof actualToData !== "object") {
  5761. throw new TypeError(
  5762. `AnimationGroupAPI.to error: toData callback function failed to return an object.`
  5763. );
  5764. }
  5765. }
  5766. if (hasOptionCallback) {
  5767. actualOptions = options(callbackOptions);
  5768. if (typeof actualOptions !== "object") {
  5769. throw new TypeError(
  5770. `AnimationGroupAPI.to error: options callback function failed to return an object.`
  5771. );
  5772. }
  5773. }
  5774. animationControls.push(actualPosition.animate.to(actualToData, actualOptions));
  5775. }
  5776. return new AnimationGroupControl(animationControls);
  5777. }
  5778. /**
  5779. * Provides the `to` animation tween for one or more Position instances as a group.
  5780. *
  5781. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5782. *
  5783. * @param {Iterable<string>} keys -
  5784. *
  5785. * @param {object|Function} options -
  5786. *
  5787. * @returns {quickToCallback} Basic animation control.
  5788. */
  5789. static quickTo(position, keys, options) {
  5790. if (!isIterable(keys)) {
  5791. throw new TypeError(`AnimationGroupAPI.quickTo error: 'keys' is not an iterable list.`);
  5792. }
  5793. if (options !== void 0 && !isObject(options) && typeof options !== "function") {
  5794. throw new TypeError(`AnimationGroupAPI.quickTo error: 'options' is not an object or function.`);
  5795. }
  5796. const quickToCallbacks = [];
  5797. let index = -1;
  5798. const hasOptionCallback = typeof options === "function";
  5799. const callbackOptions = { index, position: void 0, data: void 0 };
  5800. let actualOptions = options;
  5801. if (isIterable(position)) {
  5802. for (const entry of position) {
  5803. index++;
  5804. const isPosition = this.#isPosition(entry);
  5805. const actualPosition = isPosition ? entry : entry.position;
  5806. if (!this.#isPosition(actualPosition)) {
  5807. console.warn(`AnimationGroupAPI.quickTo warning: No Position instance found at index: ${index}.`);
  5808. continue;
  5809. }
  5810. callbackOptions.index = index;
  5811. callbackOptions.position = position;
  5812. callbackOptions.data = isPosition ? void 0 : entry;
  5813. if (hasOptionCallback) {
  5814. actualOptions = options(callbackOptions);
  5815. if (actualOptions === null || actualOptions === void 0) {
  5816. continue;
  5817. }
  5818. if (typeof actualOptions !== "object") {
  5819. throw new TypeError(`AnimationGroupAPI.quickTo error: options callback function iteration(${index}) failed to return an object.`);
  5820. }
  5821. }
  5822. quickToCallbacks.push(actualPosition.animate.quickTo(keys, actualOptions));
  5823. }
  5824. } else {
  5825. const isPosition = this.#isPosition(position);
  5826. const actualPosition = isPosition ? position : position.position;
  5827. if (!this.#isPosition(actualPosition)) {
  5828. console.warn(`AnimationGroupAPI.quickTo warning: No Position instance found.`);
  5829. return () => null;
  5830. }
  5831. callbackOptions.index = 0;
  5832. callbackOptions.position = position;
  5833. callbackOptions.data = isPosition ? void 0 : position;
  5834. if (hasOptionCallback) {
  5835. actualOptions = options(callbackOptions);
  5836. if (typeof actualOptions !== "object") {
  5837. throw new TypeError(
  5838. `AnimationGroupAPI.quickTo error: options callback function failed to return an object.`
  5839. );
  5840. }
  5841. }
  5842. quickToCallbacks.push(actualPosition.animate.quickTo(keys, actualOptions));
  5843. }
  5844. const keysArray = [...keys];
  5845. Object.freeze(keysArray);
  5846. const quickToCB = (...args) => {
  5847. const argsLength = args.length;
  5848. if (argsLength === 0) {
  5849. return;
  5850. }
  5851. if (typeof args[0] === "function") {
  5852. const dataCallback = args[0];
  5853. index = -1;
  5854. let cntr = 0;
  5855. if (isIterable(position)) {
  5856. for (const entry of position) {
  5857. index++;
  5858. const isPosition = this.#isPosition(entry);
  5859. const actualPosition = isPosition ? entry : entry.position;
  5860. if (!this.#isPosition(actualPosition)) {
  5861. continue;
  5862. }
  5863. callbackOptions.index = index;
  5864. callbackOptions.position = position;
  5865. callbackOptions.data = isPosition ? void 0 : entry;
  5866. const toData = dataCallback(callbackOptions);
  5867. if (toData === null || toData === void 0) {
  5868. continue;
  5869. }
  5870. const toDataIterable = isIterable(toData);
  5871. if (!Number.isFinite(toData) && !toDataIterable && typeof toData !== "object") {
  5872. throw new TypeError(`AnimationGroupAPI.quickTo error: toData callback function iteration(${index}) failed to return a finite number, iterable list, or object.`);
  5873. }
  5874. if (toDataIterable) {
  5875. quickToCallbacks[cntr++](...toData);
  5876. } else {
  5877. quickToCallbacks[cntr++](toData);
  5878. }
  5879. }
  5880. } else {
  5881. const isPosition = this.#isPosition(position);
  5882. const actualPosition = isPosition ? position : position.position;
  5883. if (!this.#isPosition(actualPosition)) {
  5884. return;
  5885. }
  5886. callbackOptions.index = 0;
  5887. callbackOptions.position = position;
  5888. callbackOptions.data = isPosition ? void 0 : position;
  5889. const toData = dataCallback(callbackOptions);
  5890. if (toData === null || toData === void 0) {
  5891. return;
  5892. }
  5893. const toDataIterable = isIterable(toData);
  5894. if (!Number.isFinite(toData) && !toDataIterable && typeof toData !== "object") {
  5895. throw new TypeError(`AnimationGroupAPI.quickTo error: toData callback function iteration(${index}) failed to return a finite number, iterable list, or object.`);
  5896. }
  5897. if (toDataIterable) {
  5898. quickToCallbacks[cntr++](...toData);
  5899. } else {
  5900. quickToCallbacks[cntr++](toData);
  5901. }
  5902. }
  5903. } else {
  5904. for (let cntr = quickToCallbacks.length; --cntr >= 0; ) {
  5905. quickToCallbacks[cntr](...args);
  5906. }
  5907. }
  5908. };
  5909. quickToCB.keys = keysArray;
  5910. quickToCB.options = (options2) => {
  5911. if (options2 !== void 0 && !isObject(options2) && typeof options2 !== "function") {
  5912. throw new TypeError(`AnimationGroupAPI.quickTo error: 'options' is not an object or function.`);
  5913. }
  5914. if (isObject(options2)) {
  5915. for (let cntr = quickToCallbacks.length; --cntr >= 0; ) {
  5916. quickToCallbacks[cntr].options(options2);
  5917. }
  5918. } else if (typeof options2 === "function") {
  5919. if (isIterable(position)) {
  5920. index = -1;
  5921. let cntr = 0;
  5922. for (const entry of position) {
  5923. index++;
  5924. const isPosition = this.#isPosition(entry);
  5925. const actualPosition = isPosition ? entry : entry.position;
  5926. if (!this.#isPosition(actualPosition)) {
  5927. console.warn(
  5928. `AnimationGroupAPI.quickTo.options warning: No Position instance found at index: ${index}.`
  5929. );
  5930. continue;
  5931. }
  5932. callbackOptions.index = index;
  5933. callbackOptions.position = position;
  5934. callbackOptions.data = isPosition ? void 0 : entry;
  5935. actualOptions = options2(callbackOptions);
  5936. if (actualOptions === null || actualOptions === void 0) {
  5937. continue;
  5938. }
  5939. if (typeof actualOptions !== "object") {
  5940. throw new TypeError(
  5941. `AnimationGroupAPI.quickTo.options error: options callback function iteration(${index}) failed to return an object.`
  5942. );
  5943. }
  5944. quickToCallbacks[cntr++].options(actualOptions);
  5945. }
  5946. } else {
  5947. const isPosition = this.#isPosition(position);
  5948. const actualPosition = isPosition ? position : position.position;
  5949. if (!this.#isPosition(actualPosition)) {
  5950. console.warn(`AnimationGroupAPI.quickTo.options warning: No Position instance found.`);
  5951. return quickToCB;
  5952. }
  5953. callbackOptions.index = 0;
  5954. callbackOptions.position = position;
  5955. callbackOptions.data = isPosition ? void 0 : position;
  5956. actualOptions = options2(callbackOptions);
  5957. if (typeof actualOptions !== "object") {
  5958. throw new TypeError(
  5959. `AnimationGroupAPI.quickTo error: options callback function failed to return an object.`
  5960. );
  5961. }
  5962. quickToCallbacks[0].options(actualOptions);
  5963. }
  5964. }
  5965. return quickToCB;
  5966. };
  5967. return quickToCB;
  5968. }
  5969. }
  5970. class Centered {
  5971. /**
  5972. * @type {HTMLElement}
  5973. */
  5974. #element;
  5975. /**
  5976. * Provides a manual setting of the element height. As things go `offsetHeight` causes a browser layout and is not
  5977. * performance oriented. If manually set this height is used instead of `offsetHeight`.
  5978. *
  5979. * @type {number}
  5980. */
  5981. #height;
  5982. /**
  5983. * Set from an optional value in the constructor to lock accessors preventing modification.
  5984. */
  5985. #lock;
  5986. /**
  5987. * Provides a manual setting of the element width. As things go `offsetWidth` causes a browser layout and is not
  5988. * performance oriented. If manually set this width is used instead of `offsetWidth`.
  5989. *
  5990. * @type {number}
  5991. */
  5992. #width;
  5993. constructor({ element: element2, lock = false, width: width2, height } = {}) {
  5994. this.element = element2;
  5995. this.width = width2;
  5996. this.height = height;
  5997. this.#lock = typeof lock === "boolean" ? lock : false;
  5998. }
  5999. get element() {
  6000. return this.#element;
  6001. }
  6002. get height() {
  6003. return this.#height;
  6004. }
  6005. get width() {
  6006. return this.#width;
  6007. }
  6008. set element(element2) {
  6009. if (this.#lock) {
  6010. return;
  6011. }
  6012. if (element2 === void 0 || element2 === null || element2 instanceof HTMLElement) {
  6013. this.#element = element2;
  6014. } else {
  6015. throw new TypeError(`'element' is not a HTMLElement, undefined, or null.`);
  6016. }
  6017. }
  6018. set height(height) {
  6019. if (this.#lock) {
  6020. return;
  6021. }
  6022. if (height === void 0 || Number.isFinite(height)) {
  6023. this.#height = height;
  6024. } else {
  6025. throw new TypeError(`'height' is not a finite number or undefined.`);
  6026. }
  6027. }
  6028. set width(width2) {
  6029. if (this.#lock) {
  6030. return;
  6031. }
  6032. if (width2 === void 0 || Number.isFinite(width2)) {
  6033. this.#width = width2;
  6034. } else {
  6035. throw new TypeError(`'width' is not a finite number or undefined.`);
  6036. }
  6037. }
  6038. setDimension(width2, height) {
  6039. if (this.#lock) {
  6040. return;
  6041. }
  6042. if (width2 === void 0 || Number.isFinite(width2)) {
  6043. this.#width = width2;
  6044. } else {
  6045. throw new TypeError(`'width' is not a finite number or undefined.`);
  6046. }
  6047. if (height === void 0 || Number.isFinite(height)) {
  6048. this.#height = height;
  6049. } else {
  6050. throw new TypeError(`'height' is not a finite number or undefined.`);
  6051. }
  6052. }
  6053. getLeft(width2) {
  6054. const boundsWidth = this.#width ?? this.#element?.offsetWidth ?? globalThis.innerWidth;
  6055. return (boundsWidth - width2) / 2;
  6056. }
  6057. getTop(height) {
  6058. const boundsHeight = this.#height ?? this.#element?.offsetHeight ?? globalThis.innerHeight;
  6059. return (boundsHeight - height) / 2;
  6060. }
  6061. }
  6062. const browserCentered = new Centered();
  6063. const positionInitial = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  6064. __proto__: null,
  6065. Centered,
  6066. browserCentered
  6067. }, Symbol.toStringTag, { value: "Module" }));
  6068. class PositionChangeSet {
  6069. constructor() {
  6070. this.left = false;
  6071. this.top = false;
  6072. this.width = false;
  6073. this.height = false;
  6074. this.maxHeight = false;
  6075. this.maxWidth = false;
  6076. this.minHeight = false;
  6077. this.minWidth = false;
  6078. this.zIndex = false;
  6079. this.transform = false;
  6080. this.transformOrigin = false;
  6081. }
  6082. hasChange() {
  6083. return this.left || this.top || this.width || this.height || this.maxHeight || this.maxWidth || this.minHeight || this.minWidth || this.zIndex || this.transform || this.transformOrigin;
  6084. }
  6085. set(value) {
  6086. this.left = value;
  6087. this.top = value;
  6088. this.width = value;
  6089. this.height = value;
  6090. this.maxHeight = value;
  6091. this.maxWidth = value;
  6092. this.minHeight = value;
  6093. this.minWidth = value;
  6094. this.zIndex = value;
  6095. this.transform = value;
  6096. this.transformOrigin = value;
  6097. }
  6098. }
  6099. class PositionData {
  6100. constructor({
  6101. height = null,
  6102. left = null,
  6103. maxHeight = null,
  6104. maxWidth = null,
  6105. minHeight = null,
  6106. minWidth = null,
  6107. rotateX = null,
  6108. rotateY = null,
  6109. rotateZ = null,
  6110. scale: scale2 = null,
  6111. translateX = null,
  6112. translateY = null,
  6113. translateZ = null,
  6114. top = null,
  6115. transformOrigin = null,
  6116. width: width2 = null,
  6117. zIndex = null
  6118. } = {}) {
  6119. this.height = height;
  6120. this.left = left;
  6121. this.maxHeight = maxHeight;
  6122. this.maxWidth = maxWidth;
  6123. this.minHeight = minHeight;
  6124. this.minWidth = minWidth;
  6125. this.rotateX = rotateX;
  6126. this.rotateY = rotateY;
  6127. this.rotateZ = rotateZ;
  6128. this.scale = scale2;
  6129. this.top = top;
  6130. this.transformOrigin = transformOrigin;
  6131. this.translateX = translateX;
  6132. this.translateY = translateY;
  6133. this.translateZ = translateZ;
  6134. this.width = width2;
  6135. this.zIndex = zIndex;
  6136. Object.seal(this);
  6137. }
  6138. /**
  6139. * Copies given data to this instance.
  6140. *
  6141. * @param {PositionData} data - Copy from this instance.
  6142. *
  6143. * @returns {PositionData} This instance.
  6144. */
  6145. copy(data) {
  6146. this.height = data.height;
  6147. this.left = data.left;
  6148. this.maxHeight = data.maxHeight;
  6149. this.maxWidth = data.maxWidth;
  6150. this.minHeight = data.minHeight;
  6151. this.minWidth = data.minWidth;
  6152. this.rotateX = data.rotateX;
  6153. this.rotateY = data.rotateY;
  6154. this.rotateZ = data.rotateZ;
  6155. this.scale = data.scale;
  6156. this.top = data.top;
  6157. this.transformOrigin = data.transformOrigin;
  6158. this.translateX = data.translateX;
  6159. this.translateY = data.translateY;
  6160. this.translateZ = data.translateZ;
  6161. this.width = data.width;
  6162. this.zIndex = data.zIndex;
  6163. return this;
  6164. }
  6165. }
  6166. class PositionStateAPI {
  6167. /** @type {PositionData} */
  6168. #data;
  6169. /**
  6170. * @type {Map<string, PositionDataExtended>}
  6171. */
  6172. #dataSaved = /* @__PURE__ */ new Map();
  6173. /** @type {Position} */
  6174. #position;
  6175. /** @type {Transforms} */
  6176. #transforms;
  6177. constructor(position, data, transforms) {
  6178. this.#position = position;
  6179. this.#data = data;
  6180. this.#transforms = transforms;
  6181. }
  6182. /**
  6183. * Returns any stored save state by name.
  6184. *
  6185. * @param {string} name - Saved data set name.
  6186. *
  6187. * @returns {PositionDataExtended} The saved data set.
  6188. */
  6189. get({ name }) {
  6190. if (typeof name !== "string") {
  6191. throw new TypeError(`Position - getSave error: 'name' is not a string.`);
  6192. }
  6193. return this.#dataSaved.get(name);
  6194. }
  6195. /**
  6196. * Returns any associated default data.
  6197. *
  6198. * @returns {PositionDataExtended} Associated default data.
  6199. */
  6200. getDefault() {
  6201. return this.#dataSaved.get("#defaultData");
  6202. }
  6203. /**
  6204. * Removes and returns any position state by name.
  6205. *
  6206. * @param {object} options - Options.
  6207. *
  6208. * @param {string} options.name - Name to remove and retrieve.
  6209. *
  6210. * @returns {PositionDataExtended} Saved position data.
  6211. */
  6212. remove({ name }) {
  6213. if (typeof name !== "string") {
  6214. throw new TypeError(`Position - remove: 'name' is not a string.`);
  6215. }
  6216. const data = this.#dataSaved.get(name);
  6217. this.#dataSaved.delete(name);
  6218. return data;
  6219. }
  6220. /**
  6221. * Resets data to default values and invokes set.
  6222. *
  6223. * @param {object} [opts] - Optional parameters.
  6224. *
  6225. * @param {boolean} [opts.keepZIndex=false] - When true keeps current z-index.
  6226. *
  6227. * @param {boolean} [opts.invokeSet=true] - When true invokes set method.
  6228. *
  6229. * @returns {boolean} Operation successful.
  6230. */
  6231. reset({ keepZIndex = false, invokeSet = true } = {}) {
  6232. const defaultData = this.#dataSaved.get("#defaultData");
  6233. if (typeof defaultData !== "object") {
  6234. return false;
  6235. }
  6236. if (this.#position.animate.isScheduled) {
  6237. this.#position.animate.cancel();
  6238. }
  6239. const zIndex = this.#position.zIndex;
  6240. const data = Object.assign({}, defaultData);
  6241. if (keepZIndex) {
  6242. data.zIndex = zIndex;
  6243. }
  6244. this.#transforms.reset(data);
  6245. if (this.#position.parent?.reactive?.minimized) {
  6246. this.#position.parent?.maximize?.({ animate: false, duration: 0 });
  6247. }
  6248. if (invokeSet) {
  6249. setTimeout(() => this.#position.set(data), 0);
  6250. }
  6251. return true;
  6252. }
  6253. /**
  6254. * Restores a saved positional state returning the data. Several optional parameters are available
  6255. * to control whether the restore action occurs silently (no store / inline styles updates), animates
  6256. * to the stored data, or simply sets the stored data. Restoring via {@link AnimationAPI.to} allows
  6257. * specification of the duration, easing, and interpolate functions along with configuring a Promise to be
  6258. * returned if awaiting the end of the animation.
  6259. *
  6260. * @param {object} params - Parameters
  6261. *
  6262. * @param {string} params.name - Saved data set name.
  6263. *
  6264. * @param {boolean} [params.remove=false] - Remove data set.
  6265. *
  6266. * @param {Iterable<string>} [params.properties] - Specific properties to set / animate.
  6267. *
  6268. * @param {boolean} [params.silent] - Set position data directly; no store or style updates.
  6269. *
  6270. * @param {boolean} [params.async=false] - If animating return a Promise that resolves with any saved data.
  6271. *
  6272. * @param {boolean} [params.animateTo=false] - Animate to restore data.
  6273. *
  6274. * @param {number} [params.duration=0.1] - Duration in seconds.
  6275. *
  6276. * @param {Function} [params.ease=linear] - Easing function.
  6277. *
  6278. * @param {Function} [params.interpolate=lerp] - Interpolation function.
  6279. *
  6280. * @returns {PositionDataExtended|Promise<PositionDataExtended>} Saved position data.
  6281. */
  6282. restore({
  6283. name,
  6284. remove = false,
  6285. properties,
  6286. silent = false,
  6287. async = false,
  6288. animateTo = false,
  6289. duration = 0.1,
  6290. ease = identity,
  6291. interpolate: interpolate2 = lerp$5
  6292. }) {
  6293. if (typeof name !== "string") {
  6294. throw new TypeError(`Position - restore error: 'name' is not a string.`);
  6295. }
  6296. const dataSaved = this.#dataSaved.get(name);
  6297. if (dataSaved) {
  6298. if (remove) {
  6299. this.#dataSaved.delete(name);
  6300. }
  6301. let data = dataSaved;
  6302. if (isIterable(properties)) {
  6303. data = {};
  6304. for (const property of properties) {
  6305. data[property] = dataSaved[property];
  6306. }
  6307. }
  6308. if (silent) {
  6309. for (const property in data) {
  6310. this.#data[property] = data[property];
  6311. }
  6312. return dataSaved;
  6313. } else if (animateTo) {
  6314. if (data.transformOrigin !== this.#position.transformOrigin) {
  6315. this.#position.transformOrigin = data.transformOrigin;
  6316. }
  6317. if (async) {
  6318. return this.#position.animate.to(data, { duration, ease, interpolate: interpolate2 }).finished.then(() => dataSaved);
  6319. } else {
  6320. this.#position.animate.to(data, { duration, ease, interpolate: interpolate2 });
  6321. }
  6322. } else {
  6323. this.#position.set(data);
  6324. }
  6325. }
  6326. return dataSaved;
  6327. }
  6328. /**
  6329. * Saves current position state with the opportunity to add extra data to the saved state.
  6330. *
  6331. * @param {object} opts - Options.
  6332. *
  6333. * @param {string} opts.name - name to index this saved data.
  6334. *
  6335. * @param {...*} [opts.extra] - Extra data to add to saved data.
  6336. *
  6337. * @returns {PositionData} Current position data
  6338. */
  6339. save({ name, ...extra }) {
  6340. if (typeof name !== "string") {
  6341. throw new TypeError(`Position - save error: 'name' is not a string.`);
  6342. }
  6343. const data = this.#position.get(extra);
  6344. this.#dataSaved.set(name, data);
  6345. return data;
  6346. }
  6347. /**
  6348. * Directly sets a position state.
  6349. *
  6350. * @param {object} opts - Options.
  6351. *
  6352. * @param {string} opts.name - name to index this saved data.
  6353. *
  6354. * @param {...*} [opts.data] - Position data to set.
  6355. */
  6356. set({ name, ...data }) {
  6357. if (typeof name !== "string") {
  6358. throw new TypeError(`Position - set error: 'name' is not a string.`);
  6359. }
  6360. this.#dataSaved.set(name, data);
  6361. }
  6362. }
  6363. class StyleCache {
  6364. constructor() {
  6365. this.el = void 0;
  6366. this.computed = void 0;
  6367. this.marginLeft = void 0;
  6368. this.marginTop = void 0;
  6369. this.maxHeight = void 0;
  6370. this.maxWidth = void 0;
  6371. this.minHeight = void 0;
  6372. this.minWidth = void 0;
  6373. this.hasWillChange = false;
  6374. this.resizeObserved = {
  6375. contentHeight: void 0,
  6376. contentWidth: void 0,
  6377. offsetHeight: void 0,
  6378. offsetWidth: void 0
  6379. };
  6380. const storeResizeObserved = writable$1(this.resizeObserved);
  6381. this.stores = {
  6382. element: writable$1(this.el),
  6383. resizeContentHeight: propertyStore(storeResizeObserved, "contentHeight"),
  6384. resizeContentWidth: propertyStore(storeResizeObserved, "contentWidth"),
  6385. resizeObserved: storeResizeObserved,
  6386. resizeOffsetHeight: propertyStore(storeResizeObserved, "offsetHeight"),
  6387. resizeOffsetWidth: propertyStore(storeResizeObserved, "offsetWidth")
  6388. };
  6389. }
  6390. /**
  6391. * Returns the cached offsetHeight from any attached `resizeObserver` action otherwise gets the offsetHeight from
  6392. * the element directly. The more optimized path is using `resizeObserver` as getting it from the element
  6393. * directly is more expensive and alters the execution order of an animation frame.
  6394. *
  6395. * @returns {number} The element offsetHeight.
  6396. */
  6397. get offsetHeight() {
  6398. if (this.el instanceof HTMLElement) {
  6399. return this.resizeObserved.offsetHeight !== void 0 ? this.resizeObserved.offsetHeight : this.el.offsetHeight;
  6400. }
  6401. throw new Error(`StyleCache - get offsetHeight error: no element assigned.`);
  6402. }
  6403. /**
  6404. * Returns the cached offsetWidth from any attached `resizeObserver` action otherwise gets the offsetWidth from
  6405. * the element directly. The more optimized path is using `resizeObserver` as getting it from the element
  6406. * directly is more expensive and alters the execution order of an animation frame.
  6407. *
  6408. * @returns {number} The element offsetHeight.
  6409. */
  6410. get offsetWidth() {
  6411. if (this.el instanceof HTMLElement) {
  6412. return this.resizeObserved.offsetWidth !== void 0 ? this.resizeObserved.offsetWidth : this.el.offsetWidth;
  6413. }
  6414. throw new Error(`StyleCache - get offsetWidth error: no element assigned.`);
  6415. }
  6416. /**
  6417. * @param {HTMLElement} el -
  6418. *
  6419. * @returns {boolean} Does element match cached element.
  6420. */
  6421. hasData(el) {
  6422. return this.el === el;
  6423. }
  6424. /**
  6425. * Resets the style cache.
  6426. */
  6427. reset() {
  6428. if (this.el instanceof HTMLElement && this.el.isConnected && !this.hasWillChange) {
  6429. this.el.style.willChange = null;
  6430. }
  6431. this.el = void 0;
  6432. this.computed = void 0;
  6433. this.marginLeft = void 0;
  6434. this.marginTop = void 0;
  6435. this.maxHeight = void 0;
  6436. this.maxWidth = void 0;
  6437. this.minHeight = void 0;
  6438. this.minWidth = void 0;
  6439. this.hasWillChange = false;
  6440. this.resizeObserved.contentHeight = void 0;
  6441. this.resizeObserved.contentWidth = void 0;
  6442. this.resizeObserved.offsetHeight = void 0;
  6443. this.resizeObserved.offsetWidth = void 0;
  6444. this.stores.element.set(void 0);
  6445. }
  6446. /**
  6447. * Updates the style cache with new data from the given element.
  6448. *
  6449. * @param {HTMLElement} el - An HTML element.
  6450. */
  6451. update(el) {
  6452. this.el = el;
  6453. this.computed = globalThis.getComputedStyle(el);
  6454. this.marginLeft = styleParsePixels(el.style.marginLeft) ?? styleParsePixels(this.computed.marginLeft);
  6455. this.marginTop = styleParsePixels(el.style.marginTop) ?? styleParsePixels(this.computed.marginTop);
  6456. this.maxHeight = styleParsePixels(el.style.maxHeight) ?? styleParsePixels(this.computed.maxHeight);
  6457. this.maxWidth = styleParsePixels(el.style.maxWidth) ?? styleParsePixels(this.computed.maxWidth);
  6458. this.minHeight = styleParsePixels(el.style.minHeight) ?? styleParsePixels(this.computed.minHeight);
  6459. this.minWidth = styleParsePixels(el.style.minWidth) ?? styleParsePixels(this.computed.minWidth);
  6460. const willChange = el.style.willChange !== "" ? el.style.willChange : this.computed.willChange;
  6461. this.hasWillChange = willChange !== "" && willChange !== "auto";
  6462. this.stores.element.set(el);
  6463. }
  6464. }
  6465. class TransformData {
  6466. constructor() {
  6467. Object.seal(this);
  6468. }
  6469. /**
  6470. * Stores the calculated bounding rectangle.
  6471. *
  6472. * @type {DOMRect}
  6473. */
  6474. #boundingRect = new DOMRect();
  6475. /**
  6476. * Stores the individual transformed corner points of the window in screenspace clockwise from:
  6477. * top left -> top right -> bottom right -> bottom left.
  6478. *
  6479. * @type {Vector3[]}
  6480. */
  6481. #corners = [vec3.create(), vec3.create(), vec3.create(), vec3.create()];
  6482. /**
  6483. * Stores the current gl-matrix mat4 data.
  6484. *
  6485. * @type {Matrix4}
  6486. */
  6487. #mat4 = mat4.create();
  6488. /**
  6489. * Stores the pre & post origin translations to apply to matrix transforms.
  6490. *
  6491. * @type {Matrix4[]}
  6492. */
  6493. #originTranslations = [mat4.create(), mat4.create()];
  6494. /**
  6495. * @returns {DOMRect} The bounding rectangle.
  6496. */
  6497. get boundingRect() {
  6498. return this.#boundingRect;
  6499. }
  6500. /**
  6501. * @returns {Vector3[]} The transformed corner points as vec3 in screen space.
  6502. */
  6503. get corners() {
  6504. return this.#corners;
  6505. }
  6506. /**
  6507. * @returns {string} Returns the CSS style string for the transform matrix.
  6508. */
  6509. get css() {
  6510. return `matrix3d(${this.mat4.join(",")})`;
  6511. }
  6512. /**
  6513. * @returns {Matrix4} The transform matrix.
  6514. */
  6515. get mat4() {
  6516. return this.#mat4;
  6517. }
  6518. /**
  6519. * @returns {Matrix4[]} The pre / post translation matrices for origin translation.
  6520. */
  6521. get originTranslations() {
  6522. return this.#originTranslations;
  6523. }
  6524. }
  6525. class AdapterValidators {
  6526. /** @type {boolean} */
  6527. #enabled = true;
  6528. /**
  6529. * @type {ValidatorData[]}
  6530. */
  6531. #validatorData;
  6532. #mapUnsubscribe = /* @__PURE__ */ new Map();
  6533. /**
  6534. * @returns {[AdapterValidators, ValidatorData[]]} Returns this and internal storage for validator adapter.
  6535. */
  6536. constructor() {
  6537. this.#validatorData = [];
  6538. Object.seal(this);
  6539. return [this, this.#validatorData];
  6540. }
  6541. /**
  6542. * @returns {boolean} Returns the enabled state.s
  6543. */
  6544. get enabled() {
  6545. return this.#enabled;
  6546. }
  6547. /**
  6548. * @returns {number} Returns the length of the validators array.
  6549. */
  6550. get length() {
  6551. return this.#validatorData.length;
  6552. }
  6553. /**
  6554. * @param {boolean} enabled - Sets enabled state.
  6555. */
  6556. set enabled(enabled) {
  6557. if (typeof enabled !== "boolean") {
  6558. throw new TypeError(`'enabled' is not a boolean.`);
  6559. }
  6560. this.#enabled = enabled;
  6561. }
  6562. /**
  6563. * Provides an iterator for validators.
  6564. *
  6565. * @returns {Generator<ValidatorData|undefined>} Generator / iterator of validators.
  6566. * @yields {ValidatorData}
  6567. */
  6568. *[Symbol.iterator]() {
  6569. if (this.#validatorData.length === 0) {
  6570. return;
  6571. }
  6572. for (const entry of this.#validatorData) {
  6573. yield { ...entry };
  6574. }
  6575. }
  6576. /**
  6577. * @param {...(ValidatorFn|ValidatorData)} validators -
  6578. */
  6579. add(...validators) {
  6580. for (const validator of validators) {
  6581. const validatorType = typeof validator;
  6582. if (validatorType !== "function" && validatorType !== "object" || validator === null) {
  6583. throw new TypeError(`AdapterValidator error: 'validator' is not a function or object.`);
  6584. }
  6585. let data = void 0;
  6586. let subscribeFn = void 0;
  6587. switch (validatorType) {
  6588. case "function":
  6589. data = {
  6590. id: void 0,
  6591. validator,
  6592. weight: 1
  6593. };
  6594. subscribeFn = validator.subscribe;
  6595. break;
  6596. case "object":
  6597. if (typeof validator.validator !== "function") {
  6598. throw new TypeError(`AdapterValidator error: 'validator' attribute is not a function.`);
  6599. }
  6600. if (validator.weight !== void 0 && typeof validator.weight !== "number" || (validator.weight < 0 || validator.weight > 1)) {
  6601. throw new TypeError(
  6602. `AdapterValidator error: 'weight' attribute is not a number between '0 - 1' inclusive.`
  6603. );
  6604. }
  6605. data = {
  6606. id: validator.id !== void 0 ? validator.id : void 0,
  6607. validator: validator.validator.bind(validator),
  6608. weight: validator.weight || 1,
  6609. instance: validator
  6610. };
  6611. subscribeFn = validator.validator.subscribe ?? validator.subscribe;
  6612. break;
  6613. }
  6614. const index = this.#validatorData.findIndex((value) => {
  6615. return data.weight < value.weight;
  6616. });
  6617. if (index >= 0) {
  6618. this.#validatorData.splice(index, 0, data);
  6619. } else {
  6620. this.#validatorData.push(data);
  6621. }
  6622. if (typeof subscribeFn === "function") {
  6623. const unsubscribe = subscribeFn();
  6624. if (typeof unsubscribe !== "function") {
  6625. throw new TypeError(
  6626. "AdapterValidator error: Filter has subscribe function, but no unsubscribe function is returned."
  6627. );
  6628. }
  6629. if (this.#mapUnsubscribe.has(data.validator)) {
  6630. throw new Error(
  6631. "AdapterValidator error: Filter added already has an unsubscribe function registered."
  6632. );
  6633. }
  6634. this.#mapUnsubscribe.set(data.validator, unsubscribe);
  6635. }
  6636. }
  6637. }
  6638. clear() {
  6639. this.#validatorData.length = 0;
  6640. for (const unsubscribe of this.#mapUnsubscribe.values()) {
  6641. unsubscribe();
  6642. }
  6643. this.#mapUnsubscribe.clear();
  6644. }
  6645. /**
  6646. * @param {...(ValidatorFn|ValidatorData)} validators -
  6647. */
  6648. remove(...validators) {
  6649. const length = this.#validatorData.length;
  6650. if (length === 0) {
  6651. return;
  6652. }
  6653. for (const data of validators) {
  6654. const actualValidator = typeof data === "function" ? data : isObject(data) ? data.validator : void 0;
  6655. if (!actualValidator) {
  6656. continue;
  6657. }
  6658. for (let cntr = this.#validatorData.length; --cntr >= 0; ) {
  6659. if (this.#validatorData[cntr].validator === actualValidator) {
  6660. this.#validatorData.splice(cntr, 1);
  6661. let unsubscribe = void 0;
  6662. if (typeof (unsubscribe = this.#mapUnsubscribe.get(actualValidator)) === "function") {
  6663. unsubscribe();
  6664. this.#mapUnsubscribe.delete(actualValidator);
  6665. }
  6666. }
  6667. }
  6668. }
  6669. }
  6670. /**
  6671. * Remove validators by the provided callback. The callback takes 3 parameters: `id`, `validator`, and `weight`.
  6672. * Any truthy value returned will remove that validator.
  6673. *
  6674. * @param {function(*, ValidatorFn, number): boolean} callback - Callback function to evaluate each validator
  6675. * entry.
  6676. */
  6677. removeBy(callback) {
  6678. const length = this.#validatorData.length;
  6679. if (length === 0) {
  6680. return;
  6681. }
  6682. if (typeof callback !== "function") {
  6683. throw new TypeError(`AdapterValidator error: 'callback' is not a function.`);
  6684. }
  6685. this.#validatorData = this.#validatorData.filter((data) => {
  6686. const remove = callback.call(callback, { ...data });
  6687. if (remove) {
  6688. let unsubscribe;
  6689. if (typeof (unsubscribe = this.#mapUnsubscribe.get(data.validator)) === "function") {
  6690. unsubscribe();
  6691. this.#mapUnsubscribe.delete(data.validator);
  6692. }
  6693. }
  6694. return !remove;
  6695. });
  6696. }
  6697. removeById(...ids) {
  6698. const length = this.#validatorData.length;
  6699. if (length === 0) {
  6700. return;
  6701. }
  6702. this.#validatorData = this.#validatorData.filter((data) => {
  6703. let remove = false;
  6704. for (const id of ids) {
  6705. remove |= data.id === id;
  6706. }
  6707. if (remove) {
  6708. let unsubscribe;
  6709. if (typeof (unsubscribe = this.#mapUnsubscribe.get(data.validator)) === "function") {
  6710. unsubscribe();
  6711. this.#mapUnsubscribe.delete(data.validator);
  6712. }
  6713. }
  6714. return !remove;
  6715. });
  6716. }
  6717. }
  6718. class BasicBounds {
  6719. /**
  6720. * When true constrains the min / max width or height to element.
  6721. *
  6722. * @type {boolean}
  6723. */
  6724. #constrain;
  6725. /**
  6726. * @type {HTMLElement}
  6727. */
  6728. #element;
  6729. /**
  6730. * When true the validator is active.
  6731. *
  6732. * @type {boolean}
  6733. */
  6734. #enabled;
  6735. /**
  6736. * Provides a manual setting of the element height. As things go `offsetHeight` causes a browser layout and is not
  6737. * performance oriented. If manually set this height is used instead of `offsetHeight`.
  6738. *
  6739. * @type {number}
  6740. */
  6741. #height;
  6742. /**
  6743. * Set from an optional value in the constructor to lock accessors preventing modification.
  6744. */
  6745. #lock;
  6746. /**
  6747. * Provides a manual setting of the element width. As things go `offsetWidth` causes a browser layout and is not
  6748. * performance oriented. If manually set this width is used instead of `offsetWidth`.
  6749. *
  6750. * @type {number}
  6751. */
  6752. #width;
  6753. constructor({ constrain = true, element: element2, enabled = true, lock = false, width: width2, height } = {}) {
  6754. this.element = element2;
  6755. this.constrain = constrain;
  6756. this.enabled = enabled;
  6757. this.width = width2;
  6758. this.height = height;
  6759. this.#lock = typeof lock === "boolean" ? lock : false;
  6760. }
  6761. get constrain() {
  6762. return this.#constrain;
  6763. }
  6764. get element() {
  6765. return this.#element;
  6766. }
  6767. get enabled() {
  6768. return this.#enabled;
  6769. }
  6770. get height() {
  6771. return this.#height;
  6772. }
  6773. get width() {
  6774. return this.#width;
  6775. }
  6776. set constrain(constrain) {
  6777. if (this.#lock) {
  6778. return;
  6779. }
  6780. if (typeof constrain !== "boolean") {
  6781. throw new TypeError(`'constrain' is not a boolean.`);
  6782. }
  6783. this.#constrain = constrain;
  6784. }
  6785. set element(element2) {
  6786. if (this.#lock) {
  6787. return;
  6788. }
  6789. if (element2 === void 0 || element2 === null || element2 instanceof HTMLElement) {
  6790. this.#element = element2;
  6791. } else {
  6792. throw new TypeError(`'element' is not a HTMLElement, undefined, or null.`);
  6793. }
  6794. }
  6795. set enabled(enabled) {
  6796. if (this.#lock) {
  6797. return;
  6798. }
  6799. if (typeof enabled !== "boolean") {
  6800. throw new TypeError(`'enabled' is not a boolean.`);
  6801. }
  6802. this.#enabled = enabled;
  6803. }
  6804. set height(height) {
  6805. if (this.#lock) {
  6806. return;
  6807. }
  6808. if (height === void 0 || Number.isFinite(height)) {
  6809. this.#height = height;
  6810. } else {
  6811. throw new TypeError(`'height' is not a finite number or undefined.`);
  6812. }
  6813. }
  6814. set width(width2) {
  6815. if (this.#lock) {
  6816. return;
  6817. }
  6818. if (width2 === void 0 || Number.isFinite(width2)) {
  6819. this.#width = width2;
  6820. } else {
  6821. throw new TypeError(`'width' is not a finite number or undefined.`);
  6822. }
  6823. }
  6824. setDimension(width2, height) {
  6825. if (this.#lock) {
  6826. return;
  6827. }
  6828. if (width2 === void 0 || Number.isFinite(width2)) {
  6829. this.#width = width2;
  6830. } else {
  6831. throw new TypeError(`'width' is not a finite number or undefined.`);
  6832. }
  6833. if (height === void 0 || Number.isFinite(height)) {
  6834. this.#height = height;
  6835. } else {
  6836. throw new TypeError(`'height' is not a finite number or undefined.`);
  6837. }
  6838. }
  6839. /**
  6840. * Provides a validator that respects transforms in positional data constraining the position to within the target
  6841. * elements bounds.
  6842. *
  6843. * @param {ValidationData} valData - The associated validation data for position updates.
  6844. *
  6845. * @returns {PositionData} Potentially adjusted position data.
  6846. */
  6847. validator(valData) {
  6848. if (!this.#enabled) {
  6849. return valData.position;
  6850. }
  6851. const boundsWidth = this.#width ?? this.#element?.offsetWidth ?? globalThis.innerWidth;
  6852. const boundsHeight = this.#height ?? this.#element?.offsetHeight ?? globalThis.innerHeight;
  6853. if (typeof valData.position.width === "number") {
  6854. const maxW = valData.maxWidth ?? (this.#constrain ? boundsWidth : Number.MAX_SAFE_INTEGER);
  6855. valData.position.width = valData.width = Math.clamped(valData.position.width, valData.minWidth, maxW);
  6856. if (valData.width + valData.position.left + valData.marginLeft > boundsWidth) {
  6857. valData.position.left = boundsWidth - valData.width - valData.marginLeft;
  6858. }
  6859. }
  6860. if (typeof valData.position.height === "number") {
  6861. const maxH = valData.maxHeight ?? (this.#constrain ? boundsHeight : Number.MAX_SAFE_INTEGER);
  6862. valData.position.height = valData.height = Math.clamped(valData.position.height, valData.minHeight, maxH);
  6863. if (valData.height + valData.position.top + valData.marginTop > boundsHeight) {
  6864. valData.position.top = boundsHeight - valData.height - valData.marginTop;
  6865. }
  6866. }
  6867. const maxL = Math.max(boundsWidth - valData.width - valData.marginLeft, 0);
  6868. valData.position.left = Math.round(Math.clamped(valData.position.left, 0, maxL));
  6869. const maxT = Math.max(boundsHeight - valData.height - valData.marginTop, 0);
  6870. valData.position.top = Math.round(Math.clamped(valData.position.top, 0, maxT));
  6871. return valData.position;
  6872. }
  6873. }
  6874. const s_TRANSFORM_DATA = new TransformData();
  6875. class TransformBounds {
  6876. /**
  6877. * When true constrains the min / max width or height to element.
  6878. *
  6879. * @type {boolean}
  6880. */
  6881. #constrain;
  6882. /**
  6883. * @type {HTMLElement}
  6884. */
  6885. #element;
  6886. /**
  6887. * When true the validator is active.
  6888. *
  6889. * @type {boolean}
  6890. */
  6891. #enabled;
  6892. /**
  6893. * Provides a manual setting of the element height. As things go `offsetHeight` causes a browser layout and is not
  6894. * performance oriented. If manually set this height is used instead of `offsetHeight`.
  6895. *
  6896. * @type {number}
  6897. */
  6898. #height;
  6899. /**
  6900. * Set from an optional value in the constructor to lock accessors preventing modification.
  6901. */
  6902. #lock;
  6903. /**
  6904. * Provides a manual setting of the element width. As things go `offsetWidth` causes a browser layout and is not
  6905. * performance oriented. If manually set this width is used instead of `offsetWidth`.
  6906. *
  6907. * @type {number}
  6908. */
  6909. #width;
  6910. constructor({ constrain = true, element: element2, enabled = true, lock = false, width: width2, height } = {}) {
  6911. this.element = element2;
  6912. this.constrain = constrain;
  6913. this.enabled = enabled;
  6914. this.width = width2;
  6915. this.height = height;
  6916. this.#lock = typeof lock === "boolean" ? lock : false;
  6917. }
  6918. get constrain() {
  6919. return this.#constrain;
  6920. }
  6921. get element() {
  6922. return this.#element;
  6923. }
  6924. get enabled() {
  6925. return this.#enabled;
  6926. }
  6927. get height() {
  6928. return this.#height;
  6929. }
  6930. get width() {
  6931. return this.#width;
  6932. }
  6933. set constrain(constrain) {
  6934. if (this.#lock) {
  6935. return;
  6936. }
  6937. if (typeof constrain !== "boolean") {
  6938. throw new TypeError(`'constrain' is not a boolean.`);
  6939. }
  6940. this.#constrain = constrain;
  6941. }
  6942. set element(element2) {
  6943. if (this.#lock) {
  6944. return;
  6945. }
  6946. if (element2 === void 0 || element2 === null || element2 instanceof HTMLElement) {
  6947. this.#element = element2;
  6948. } else {
  6949. throw new TypeError(`'element' is not a HTMLElement, undefined, or null.`);
  6950. }
  6951. }
  6952. set enabled(enabled) {
  6953. if (this.#lock) {
  6954. return;
  6955. }
  6956. if (typeof enabled !== "boolean") {
  6957. throw new TypeError(`'enabled' is not a boolean.`);
  6958. }
  6959. this.#enabled = enabled;
  6960. }
  6961. set height(height) {
  6962. if (this.#lock) {
  6963. return;
  6964. }
  6965. if (height === void 0 || Number.isFinite(height)) {
  6966. this.#height = height;
  6967. } else {
  6968. throw new TypeError(`'height' is not a finite number or undefined.`);
  6969. }
  6970. }
  6971. set width(width2) {
  6972. if (this.#lock) {
  6973. return;
  6974. }
  6975. if (width2 === void 0 || Number.isFinite(width2)) {
  6976. this.#width = width2;
  6977. } else {
  6978. throw new TypeError(`'width' is not a finite number or undefined.`);
  6979. }
  6980. }
  6981. setDimension(width2, height) {
  6982. if (this.#lock) {
  6983. return;
  6984. }
  6985. if (width2 === void 0 || Number.isFinite(width2)) {
  6986. this.#width = width2;
  6987. } else {
  6988. throw new TypeError(`'width' is not a finite number or undefined.`);
  6989. }
  6990. if (height === void 0 || Number.isFinite(height)) {
  6991. this.#height = height;
  6992. } else {
  6993. throw new TypeError(`'height' is not a finite number or undefined.`);
  6994. }
  6995. }
  6996. /**
  6997. * Provides a validator that respects transforms in positional data constraining the position to within the target
  6998. * elements bounds.
  6999. *
  7000. * @param {ValidationData} valData - The associated validation data for position updates.
  7001. *
  7002. * @returns {PositionData} Potentially adjusted position data.
  7003. */
  7004. validator(valData) {
  7005. if (!this.#enabled) {
  7006. return valData.position;
  7007. }
  7008. const boundsWidth = this.#width ?? this.#element?.offsetWidth ?? globalThis.innerWidth;
  7009. const boundsHeight = this.#height ?? this.#element?.offsetHeight ?? globalThis.innerHeight;
  7010. if (typeof valData.position.width === "number") {
  7011. const maxW = valData.maxWidth ?? (this.#constrain ? boundsWidth : Number.MAX_SAFE_INTEGER);
  7012. valData.position.width = Math.clamped(valData.width, valData.minWidth, maxW);
  7013. }
  7014. if (typeof valData.position.height === "number") {
  7015. const maxH = valData.maxHeight ?? (this.#constrain ? boundsHeight : Number.MAX_SAFE_INTEGER);
  7016. valData.position.height = Math.clamped(valData.height, valData.minHeight, maxH);
  7017. }
  7018. const data = valData.transforms.getData(valData.position, s_TRANSFORM_DATA, valData);
  7019. const initialX = data.boundingRect.x;
  7020. const initialY = data.boundingRect.y;
  7021. if (data.boundingRect.bottom + valData.marginTop > boundsHeight) {
  7022. data.boundingRect.y += boundsHeight - data.boundingRect.bottom - valData.marginTop;
  7023. }
  7024. if (data.boundingRect.right + valData.marginLeft > boundsWidth) {
  7025. data.boundingRect.x += boundsWidth - data.boundingRect.right - valData.marginLeft;
  7026. }
  7027. if (data.boundingRect.top - valData.marginTop < 0) {
  7028. data.boundingRect.y += Math.abs(data.boundingRect.top - valData.marginTop);
  7029. }
  7030. if (data.boundingRect.left - valData.marginLeft < 0) {
  7031. data.boundingRect.x += Math.abs(data.boundingRect.left - valData.marginLeft);
  7032. }
  7033. valData.position.left -= initialX - data.boundingRect.x;
  7034. valData.position.top -= initialY - data.boundingRect.y;
  7035. return valData.position;
  7036. }
  7037. }
  7038. const basicWindow = new BasicBounds({ lock: true });
  7039. const transformWindow = new TransformBounds({ lock: true });
  7040. const positionValidators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  7041. __proto__: null,
  7042. BasicBounds,
  7043. TransformBounds,
  7044. basicWindow,
  7045. transformWindow
  7046. }, Symbol.toStringTag, { value: "Module" }));
  7047. const s_SCALE_VECTOR = [1, 1, 1];
  7048. const s_TRANSLATE_VECTOR = [0, 0, 0];
  7049. const s_MAT4_RESULT = mat4.create();
  7050. const s_MAT4_TEMP = mat4.create();
  7051. const s_VEC3_TEMP = vec3.create();
  7052. class Transforms {
  7053. /**
  7054. * Stores the transform keys in the order added.
  7055. *
  7056. * @type {string[]}
  7057. */
  7058. #orderList = [];
  7059. constructor() {
  7060. this._data = {};
  7061. }
  7062. /**
  7063. * @returns {boolean} Whether there are active transforms in local data.
  7064. */
  7065. get isActive() {
  7066. return this.#orderList.length > 0;
  7067. }
  7068. /**
  7069. * @returns {number|undefined} Any local rotateX data.
  7070. */
  7071. get rotateX() {
  7072. return this._data.rotateX;
  7073. }
  7074. /**
  7075. * @returns {number|undefined} Any local rotateY data.
  7076. */
  7077. get rotateY() {
  7078. return this._data.rotateY;
  7079. }
  7080. /**
  7081. * @returns {number|undefined} Any local rotateZ data.
  7082. */
  7083. get rotateZ() {
  7084. return this._data.rotateZ;
  7085. }
  7086. /**
  7087. * @returns {number|undefined} Any local rotateZ scale.
  7088. */
  7089. get scale() {
  7090. return this._data.scale;
  7091. }
  7092. /**
  7093. * @returns {number|undefined} Any local translateZ data.
  7094. */
  7095. get translateX() {
  7096. return this._data.translateX;
  7097. }
  7098. /**
  7099. * @returns {number|undefined} Any local translateZ data.
  7100. */
  7101. get translateY() {
  7102. return this._data.translateY;
  7103. }
  7104. /**
  7105. * @returns {number|undefined} Any local translateZ data.
  7106. */
  7107. get translateZ() {
  7108. return this._data.translateZ;
  7109. }
  7110. /**
  7111. * Sets the local rotateX data if the value is a finite number otherwise removes the local data.
  7112. *
  7113. * @param {number|null|undefined} value - A value to set.
  7114. */
  7115. set rotateX(value) {
  7116. if (Number.isFinite(value)) {
  7117. if (this._data.rotateX === void 0) {
  7118. this.#orderList.push("rotateX");
  7119. }
  7120. this._data.rotateX = value;
  7121. } else {
  7122. if (this._data.rotateX !== void 0) {
  7123. const index = this.#orderList.findIndex((entry) => entry === "rotateX");
  7124. if (index >= 0) {
  7125. this.#orderList.splice(index, 1);
  7126. }
  7127. }
  7128. delete this._data.rotateX;
  7129. }
  7130. }
  7131. /**
  7132. * Sets the local rotateY data if the value is a finite number otherwise removes the local data.
  7133. *
  7134. * @param {number|null|undefined} value - A value to set.
  7135. */
  7136. set rotateY(value) {
  7137. if (Number.isFinite(value)) {
  7138. if (this._data.rotateY === void 0) {
  7139. this.#orderList.push("rotateY");
  7140. }
  7141. this._data.rotateY = value;
  7142. } else {
  7143. if (this._data.rotateY !== void 0) {
  7144. const index = this.#orderList.findIndex((entry) => entry === "rotateY");
  7145. if (index >= 0) {
  7146. this.#orderList.splice(index, 1);
  7147. }
  7148. }
  7149. delete this._data.rotateY;
  7150. }
  7151. }
  7152. /**
  7153. * Sets the local rotateZ data if the value is a finite number otherwise removes the local data.
  7154. *
  7155. * @param {number|null|undefined} value - A value to set.
  7156. */
  7157. set rotateZ(value) {
  7158. if (Number.isFinite(value)) {
  7159. if (this._data.rotateZ === void 0) {
  7160. this.#orderList.push("rotateZ");
  7161. }
  7162. this._data.rotateZ = value;
  7163. } else {
  7164. if (this._data.rotateZ !== void 0) {
  7165. const index = this.#orderList.findIndex((entry) => entry === "rotateZ");
  7166. if (index >= 0) {
  7167. this.#orderList.splice(index, 1);
  7168. }
  7169. }
  7170. delete this._data.rotateZ;
  7171. }
  7172. }
  7173. /**
  7174. * Sets the local scale data if the value is a finite number otherwise removes the local data.
  7175. *
  7176. * @param {number|null|undefined} value - A value to set.
  7177. */
  7178. set scale(value) {
  7179. if (Number.isFinite(value)) {
  7180. if (this._data.scale === void 0) {
  7181. this.#orderList.push("scale");
  7182. }
  7183. this._data.scale = value;
  7184. } else {
  7185. if (this._data.scale !== void 0) {
  7186. const index = this.#orderList.findIndex((entry) => entry === "scale");
  7187. if (index >= 0) {
  7188. this.#orderList.splice(index, 1);
  7189. }
  7190. }
  7191. delete this._data.scale;
  7192. }
  7193. }
  7194. /**
  7195. * Sets the local translateX data if the value is a finite number otherwise removes the local data.
  7196. *
  7197. * @param {number|null|undefined} value - A value to set.
  7198. */
  7199. set translateX(value) {
  7200. if (Number.isFinite(value)) {
  7201. if (this._data.translateX === void 0) {
  7202. this.#orderList.push("translateX");
  7203. }
  7204. this._data.translateX = value;
  7205. } else {
  7206. if (this._data.translateX !== void 0) {
  7207. const index = this.#orderList.findIndex((entry) => entry === "translateX");
  7208. if (index >= 0) {
  7209. this.#orderList.splice(index, 1);
  7210. }
  7211. }
  7212. delete this._data.translateX;
  7213. }
  7214. }
  7215. /**
  7216. * Sets the local translateY data if the value is a finite number otherwise removes the local data.
  7217. *
  7218. * @param {number|null|undefined} value - A value to set.
  7219. */
  7220. set translateY(value) {
  7221. if (Number.isFinite(value)) {
  7222. if (this._data.translateY === void 0) {
  7223. this.#orderList.push("translateY");
  7224. }
  7225. this._data.translateY = value;
  7226. } else {
  7227. if (this._data.translateY !== void 0) {
  7228. const index = this.#orderList.findIndex((entry) => entry === "translateY");
  7229. if (index >= 0) {
  7230. this.#orderList.splice(index, 1);
  7231. }
  7232. }
  7233. delete this._data.translateY;
  7234. }
  7235. }
  7236. /**
  7237. * Sets the local translateZ data if the value is a finite number otherwise removes the local data.
  7238. *
  7239. * @param {number|null|undefined} value - A value to set.
  7240. */
  7241. set translateZ(value) {
  7242. if (Number.isFinite(value)) {
  7243. if (this._data.translateZ === void 0) {
  7244. this.#orderList.push("translateZ");
  7245. }
  7246. this._data.translateZ = value;
  7247. } else {
  7248. if (this._data.translateZ !== void 0) {
  7249. const index = this.#orderList.findIndex((entry) => entry === "translateZ");
  7250. if (index >= 0) {
  7251. this.#orderList.splice(index, 1);
  7252. }
  7253. }
  7254. delete this._data.translateZ;
  7255. }
  7256. }
  7257. /**
  7258. * Returns the matrix3d CSS transform for the given position / transform data.
  7259. *
  7260. * @param {object} [data] - Optional position data otherwise use local stored transform data.
  7261. *
  7262. * @returns {string} The CSS matrix3d string.
  7263. */
  7264. getCSS(data = this._data) {
  7265. return `matrix3d(${this.getMat4(data, s_MAT4_RESULT).join(",")})`;
  7266. }
  7267. /**
  7268. * Returns the matrix3d CSS transform for the given position / transform data.
  7269. *
  7270. * @param {object} [data] - Optional position data otherwise use local stored transform data.
  7271. *
  7272. * @returns {string} The CSS matrix3d string.
  7273. */
  7274. getCSSOrtho(data = this._data) {
  7275. return `matrix3d(${this.getMat4Ortho(data, s_MAT4_RESULT).join(",")})`;
  7276. }
  7277. /**
  7278. * Collects all data including a bounding rect, transform matrix, and points array of the given {@link PositionData}
  7279. * instance with the applied local transform data.
  7280. *
  7281. * @param {PositionData} position - The position data to process.
  7282. *
  7283. * @param {TransformData} [output] - Optional TransformData output instance.
  7284. *
  7285. * @param {object} [validationData] - Optional validation data for adjustment parameters.
  7286. *
  7287. * @returns {TransformData} The output TransformData instance.
  7288. */
  7289. getData(position, output = new TransformData(), validationData = {}) {
  7290. const valWidth = validationData.width ?? 0;
  7291. const valHeight = validationData.height ?? 0;
  7292. const valOffsetTop = validationData.offsetTop ?? validationData.marginTop ?? 0;
  7293. const valOffsetLeft = validationData.offsetLeft ?? validationData.offsetLeft ?? 0;
  7294. position.top += valOffsetTop;
  7295. position.left += valOffsetLeft;
  7296. const width2 = Number.isFinite(position.width) ? position.width : valWidth;
  7297. const height = Number.isFinite(position.height) ? position.height : valHeight;
  7298. const rect = output.corners;
  7299. if (this.hasTransform(position)) {
  7300. rect[0][0] = rect[0][1] = rect[0][2] = 0;
  7301. rect[1][0] = width2;
  7302. rect[1][1] = rect[1][2] = 0;
  7303. rect[2][0] = width2;
  7304. rect[2][1] = height;
  7305. rect[2][2] = 0;
  7306. rect[3][0] = 0;
  7307. rect[3][1] = height;
  7308. rect[3][2] = 0;
  7309. const matrix = this.getMat4(position, output.mat4);
  7310. const translate = s_GET_ORIGIN_TRANSLATION(position.transformOrigin, width2, height, output.originTranslations);
  7311. if (transformOriginDefault === position.transformOrigin) {
  7312. vec3.transformMat4(rect[0], rect[0], matrix);
  7313. vec3.transformMat4(rect[1], rect[1], matrix);
  7314. vec3.transformMat4(rect[2], rect[2], matrix);
  7315. vec3.transformMat4(rect[3], rect[3], matrix);
  7316. } else {
  7317. vec3.transformMat4(rect[0], rect[0], translate[0]);
  7318. vec3.transformMat4(rect[0], rect[0], matrix);
  7319. vec3.transformMat4(rect[0], rect[0], translate[1]);
  7320. vec3.transformMat4(rect[1], rect[1], translate[0]);
  7321. vec3.transformMat4(rect[1], rect[1], matrix);
  7322. vec3.transformMat4(rect[1], rect[1], translate[1]);
  7323. vec3.transformMat4(rect[2], rect[2], translate[0]);
  7324. vec3.transformMat4(rect[2], rect[2], matrix);
  7325. vec3.transformMat4(rect[2], rect[2], translate[1]);
  7326. vec3.transformMat4(rect[3], rect[3], translate[0]);
  7327. vec3.transformMat4(rect[3], rect[3], matrix);
  7328. vec3.transformMat4(rect[3], rect[3], translate[1]);
  7329. }
  7330. rect[0][0] = position.left + rect[0][0];
  7331. rect[0][1] = position.top + rect[0][1];
  7332. rect[1][0] = position.left + rect[1][0];
  7333. rect[1][1] = position.top + rect[1][1];
  7334. rect[2][0] = position.left + rect[2][0];
  7335. rect[2][1] = position.top + rect[2][1];
  7336. rect[3][0] = position.left + rect[3][0];
  7337. rect[3][1] = position.top + rect[3][1];
  7338. } else {
  7339. rect[0][0] = position.left;
  7340. rect[0][1] = position.top;
  7341. rect[1][0] = position.left + width2;
  7342. rect[1][1] = position.top;
  7343. rect[2][0] = position.left + width2;
  7344. rect[2][1] = position.top + height;
  7345. rect[3][0] = position.left;
  7346. rect[3][1] = position.top + height;
  7347. mat4.identity(output.mat4);
  7348. }
  7349. let maxX = Number.MIN_SAFE_INTEGER;
  7350. let maxY = Number.MIN_SAFE_INTEGER;
  7351. let minX = Number.MAX_SAFE_INTEGER;
  7352. let minY = Number.MAX_SAFE_INTEGER;
  7353. for (let cntr = 4; --cntr >= 0; ) {
  7354. if (rect[cntr][0] > maxX) {
  7355. maxX = rect[cntr][0];
  7356. }
  7357. if (rect[cntr][0] < minX) {
  7358. minX = rect[cntr][0];
  7359. }
  7360. if (rect[cntr][1] > maxY) {
  7361. maxY = rect[cntr][1];
  7362. }
  7363. if (rect[cntr][1] < minY) {
  7364. minY = rect[cntr][1];
  7365. }
  7366. }
  7367. const boundingRect = output.boundingRect;
  7368. boundingRect.x = minX;
  7369. boundingRect.y = minY;
  7370. boundingRect.width = maxX - minX;
  7371. boundingRect.height = maxY - minY;
  7372. position.top -= valOffsetTop;
  7373. position.left -= valOffsetLeft;
  7374. return output;
  7375. }
  7376. /**
  7377. * Creates a transform matrix based on local data applied in order it was added.
  7378. *
  7379. * If no data object is provided then the source is the local transform data. If another data object is supplied
  7380. * then the stored local transform order is applied then all remaining transform keys are applied. This allows the
  7381. * construction of a transform matrix in advance of setting local data and is useful in collision detection.
  7382. *
  7383. * @param {object} [data] - PositionData instance or local transform data.
  7384. *
  7385. * @param {Matrix4} [output] - The output mat4 instance.
  7386. *
  7387. * @returns {Matrix4} Transform matrix.
  7388. */
  7389. getMat4(data = this._data, output = mat4.create()) {
  7390. const matrix = mat4.identity(output);
  7391. let seenKeys = 0;
  7392. const orderList = this.#orderList;
  7393. for (let cntr = 0; cntr < orderList.length; cntr++) {
  7394. const key = orderList[cntr];
  7395. switch (key) {
  7396. case "rotateX":
  7397. seenKeys |= transformKeysBitwise.rotateX;
  7398. mat4.multiply(matrix, matrix, mat4.fromXRotation(s_MAT4_TEMP, degToRad(data[key])));
  7399. break;
  7400. case "rotateY":
  7401. seenKeys |= transformKeysBitwise.rotateY;
  7402. mat4.multiply(matrix, matrix, mat4.fromYRotation(s_MAT4_TEMP, degToRad(data[key])));
  7403. break;
  7404. case "rotateZ":
  7405. seenKeys |= transformKeysBitwise.rotateZ;
  7406. mat4.multiply(matrix, matrix, mat4.fromZRotation(s_MAT4_TEMP, degToRad(data[key])));
  7407. break;
  7408. case "scale":
  7409. seenKeys |= transformKeysBitwise.scale;
  7410. s_SCALE_VECTOR[0] = s_SCALE_VECTOR[1] = data[key];
  7411. mat4.multiply(matrix, matrix, mat4.fromScaling(s_MAT4_TEMP, s_SCALE_VECTOR));
  7412. break;
  7413. case "translateX":
  7414. seenKeys |= transformKeysBitwise.translateX;
  7415. s_TRANSLATE_VECTOR[0] = data.translateX;
  7416. s_TRANSLATE_VECTOR[1] = 0;
  7417. s_TRANSLATE_VECTOR[2] = 0;
  7418. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7419. break;
  7420. case "translateY":
  7421. seenKeys |= transformKeysBitwise.translateY;
  7422. s_TRANSLATE_VECTOR[0] = 0;
  7423. s_TRANSLATE_VECTOR[1] = data.translateY;
  7424. s_TRANSLATE_VECTOR[2] = 0;
  7425. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7426. break;
  7427. case "translateZ":
  7428. seenKeys |= transformKeysBitwise.translateZ;
  7429. s_TRANSLATE_VECTOR[0] = 0;
  7430. s_TRANSLATE_VECTOR[1] = 0;
  7431. s_TRANSLATE_VECTOR[2] = data.translateZ;
  7432. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7433. break;
  7434. }
  7435. }
  7436. if (data !== this._data) {
  7437. for (let cntr = 0; cntr < transformKeys.length; cntr++) {
  7438. const key = transformKeys[cntr];
  7439. if (data[key] === null || (seenKeys & transformKeysBitwise[key]) > 0) {
  7440. continue;
  7441. }
  7442. switch (key) {
  7443. case "rotateX":
  7444. mat4.multiply(matrix, matrix, mat4.fromXRotation(s_MAT4_TEMP, degToRad(data[key])));
  7445. break;
  7446. case "rotateY":
  7447. mat4.multiply(matrix, matrix, mat4.fromYRotation(s_MAT4_TEMP, degToRad(data[key])));
  7448. break;
  7449. case "rotateZ":
  7450. mat4.multiply(matrix, matrix, mat4.fromZRotation(s_MAT4_TEMP, degToRad(data[key])));
  7451. break;
  7452. case "scale":
  7453. s_SCALE_VECTOR[0] = s_SCALE_VECTOR[1] = data[key];
  7454. mat4.multiply(matrix, matrix, mat4.fromScaling(s_MAT4_TEMP, s_SCALE_VECTOR));
  7455. break;
  7456. case "translateX":
  7457. s_TRANSLATE_VECTOR[0] = data[key];
  7458. s_TRANSLATE_VECTOR[1] = 0;
  7459. s_TRANSLATE_VECTOR[2] = 0;
  7460. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7461. break;
  7462. case "translateY":
  7463. s_TRANSLATE_VECTOR[0] = 0;
  7464. s_TRANSLATE_VECTOR[1] = data[key];
  7465. s_TRANSLATE_VECTOR[2] = 0;
  7466. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7467. break;
  7468. case "translateZ":
  7469. s_TRANSLATE_VECTOR[0] = 0;
  7470. s_TRANSLATE_VECTOR[1] = 0;
  7471. s_TRANSLATE_VECTOR[2] = data[key];
  7472. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7473. break;
  7474. }
  7475. }
  7476. }
  7477. return matrix;
  7478. }
  7479. /**
  7480. * Provides an orthographic enhancement to convert left / top positional data to a translate operation.
  7481. *
  7482. * This transform matrix takes into account that the remaining operations are , but adds any left / top attributes from passed in data to
  7483. * translate X / Y.
  7484. *
  7485. * If no data object is provided then the source is the local transform data. If another data object is supplied
  7486. * then the stored local transform order is applied then all remaining transform keys are applied. This allows the
  7487. * construction of a transform matrix in advance of setting local data and is useful in collision detection.
  7488. *
  7489. * @param {object} [data] - PositionData instance or local transform data.
  7490. *
  7491. * @param {Matrix4} [output] - The output mat4 instance.
  7492. *
  7493. * @returns {Matrix4} Transform matrix.
  7494. */
  7495. getMat4Ortho(data = this._data, output = mat4.create()) {
  7496. const matrix = mat4.identity(output);
  7497. s_TRANSLATE_VECTOR[0] = (data.left ?? 0) + (data.translateX ?? 0);
  7498. s_TRANSLATE_VECTOR[1] = (data.top ?? 0) + (data.translateY ?? 0);
  7499. s_TRANSLATE_VECTOR[2] = data.translateZ ?? 0;
  7500. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7501. if (data.scale !== null) {
  7502. s_SCALE_VECTOR[0] = s_SCALE_VECTOR[1] = data.scale;
  7503. mat4.multiply(matrix, matrix, mat4.fromScaling(s_MAT4_TEMP, s_SCALE_VECTOR));
  7504. }
  7505. if (data.rotateX === null && data.rotateY === null && data.rotateZ === null) {
  7506. return matrix;
  7507. }
  7508. let seenKeys = 0;
  7509. const orderList = this.#orderList;
  7510. for (let cntr = 0; cntr < orderList.length; cntr++) {
  7511. const key = orderList[cntr];
  7512. switch (key) {
  7513. case "rotateX":
  7514. seenKeys |= transformKeysBitwise.rotateX;
  7515. mat4.multiply(matrix, matrix, mat4.fromXRotation(s_MAT4_TEMP, degToRad(data[key])));
  7516. break;
  7517. case "rotateY":
  7518. seenKeys |= transformKeysBitwise.rotateY;
  7519. mat4.multiply(matrix, matrix, mat4.fromYRotation(s_MAT4_TEMP, degToRad(data[key])));
  7520. break;
  7521. case "rotateZ":
  7522. seenKeys |= transformKeysBitwise.rotateZ;
  7523. mat4.multiply(matrix, matrix, mat4.fromZRotation(s_MAT4_TEMP, degToRad(data[key])));
  7524. break;
  7525. }
  7526. }
  7527. if (data !== this._data) {
  7528. for (let cntr = 0; cntr < transformKeys.length; cntr++) {
  7529. const key = transformKeys[cntr];
  7530. if (data[key] === null || (seenKeys & transformKeysBitwise[key]) > 0) {
  7531. continue;
  7532. }
  7533. switch (key) {
  7534. case "rotateX":
  7535. mat4.multiply(matrix, matrix, mat4.fromXRotation(s_MAT4_TEMP, degToRad(data[key])));
  7536. break;
  7537. case "rotateY":
  7538. mat4.multiply(matrix, matrix, mat4.fromYRotation(s_MAT4_TEMP, degToRad(data[key])));
  7539. break;
  7540. case "rotateZ":
  7541. mat4.multiply(matrix, matrix, mat4.fromZRotation(s_MAT4_TEMP, degToRad(data[key])));
  7542. break;
  7543. }
  7544. }
  7545. }
  7546. return matrix;
  7547. }
  7548. /**
  7549. * Tests an object if it contains transform keys and the values are finite numbers.
  7550. *
  7551. * @param {object} data - An object to test for transform data.
  7552. *
  7553. * @returns {boolean} Whether the given PositionData has transforms.
  7554. */
  7555. hasTransform(data) {
  7556. for (const key of transformKeys) {
  7557. if (Number.isFinite(data[key])) {
  7558. return true;
  7559. }
  7560. }
  7561. return false;
  7562. }
  7563. /**
  7564. * Resets internal data from the given object containing valid transform keys.
  7565. *
  7566. * @param {object} data - An object with transform data.
  7567. */
  7568. reset(data) {
  7569. for (const key in data) {
  7570. if (transformKeys.includes(key)) {
  7571. if (Number.isFinite(data[key])) {
  7572. this._data[key] = data[key];
  7573. } else {
  7574. const index = this.#orderList.findIndex((entry) => entry === key);
  7575. if (index >= 0) {
  7576. this.#orderList.splice(index, 1);
  7577. }
  7578. delete this._data[key];
  7579. }
  7580. }
  7581. }
  7582. }
  7583. }
  7584. function s_GET_ORIGIN_TRANSLATION(transformOrigin, width2, height, output) {
  7585. const vector = s_VEC3_TEMP;
  7586. switch (transformOrigin) {
  7587. case "top left":
  7588. vector[0] = vector[1] = 0;
  7589. mat4.fromTranslation(output[0], vector);
  7590. mat4.fromTranslation(output[1], vector);
  7591. break;
  7592. case "top center":
  7593. vector[0] = -width2 * 0.5;
  7594. vector[1] = 0;
  7595. mat4.fromTranslation(output[0], vector);
  7596. vector[0] = width2 * 0.5;
  7597. mat4.fromTranslation(output[1], vector);
  7598. break;
  7599. case "top right":
  7600. vector[0] = -width2;
  7601. vector[1] = 0;
  7602. mat4.fromTranslation(output[0], vector);
  7603. vector[0] = width2;
  7604. mat4.fromTranslation(output[1], vector);
  7605. break;
  7606. case "center left":
  7607. vector[0] = 0;
  7608. vector[1] = -height * 0.5;
  7609. mat4.fromTranslation(output[0], vector);
  7610. vector[1] = height * 0.5;
  7611. mat4.fromTranslation(output[1], vector);
  7612. break;
  7613. case null:
  7614. case "center":
  7615. vector[0] = -width2 * 0.5;
  7616. vector[1] = -height * 0.5;
  7617. mat4.fromTranslation(output[0], vector);
  7618. vector[0] = width2 * 0.5;
  7619. vector[1] = height * 0.5;
  7620. mat4.fromTranslation(output[1], vector);
  7621. break;
  7622. case "center right":
  7623. vector[0] = -width2;
  7624. vector[1] = -height * 0.5;
  7625. mat4.fromTranslation(output[0], vector);
  7626. vector[0] = width2;
  7627. vector[1] = height * 0.5;
  7628. mat4.fromTranslation(output[1], vector);
  7629. break;
  7630. case "bottom left":
  7631. vector[0] = 0;
  7632. vector[1] = -height;
  7633. mat4.fromTranslation(output[0], vector);
  7634. vector[1] = height;
  7635. mat4.fromTranslation(output[1], vector);
  7636. break;
  7637. case "bottom center":
  7638. vector[0] = -width2 * 0.5;
  7639. vector[1] = -height;
  7640. mat4.fromTranslation(output[0], vector);
  7641. vector[0] = width2 * 0.5;
  7642. vector[1] = height;
  7643. mat4.fromTranslation(output[1], vector);
  7644. break;
  7645. case "bottom right":
  7646. vector[0] = -width2;
  7647. vector[1] = -height;
  7648. mat4.fromTranslation(output[0], vector);
  7649. vector[0] = width2;
  7650. vector[1] = height;
  7651. mat4.fromTranslation(output[1], vector);
  7652. break;
  7653. default:
  7654. mat4.identity(output[0]);
  7655. mat4.identity(output[1]);
  7656. break;
  7657. }
  7658. return output;
  7659. }
  7660. class UpdateElementData {
  7661. constructor() {
  7662. this.data = void 0;
  7663. this.dataSubscribers = new PositionData();
  7664. this.dimensionData = { width: 0, height: 0 };
  7665. this.changeSet = void 0;
  7666. this.options = void 0;
  7667. this.queued = false;
  7668. this.styleCache = void 0;
  7669. this.transforms = void 0;
  7670. this.transformData = new TransformData();
  7671. this.subscriptions = void 0;
  7672. this.storeDimension = writable$1(this.dimensionData);
  7673. this.storeTransform = writable$1(this.transformData, () => {
  7674. this.options.transformSubscribed = true;
  7675. return () => this.options.transformSubscribed = false;
  7676. });
  7677. this.queued = false;
  7678. Object.seal(this.dimensionData);
  7679. }
  7680. }
  7681. async function nextAnimationFrame(cntr = 1) {
  7682. if (!Number.isInteger(cntr) || cntr < 1) {
  7683. throw new TypeError(`nextAnimationFrame error: 'cntr' must be a positive integer greater than 0.`);
  7684. }
  7685. let currentTime = performance.now();
  7686. for (; --cntr >= 0; ) {
  7687. currentTime = await new Promise((resolve) => requestAnimationFrame(resolve));
  7688. }
  7689. return currentTime;
  7690. }
  7691. class UpdateElementManager {
  7692. static list = [];
  7693. static listCntr = 0;
  7694. static updatePromise;
  7695. static get promise() {
  7696. return this.updatePromise;
  7697. }
  7698. /**
  7699. * Potentially adds the given element and internal updateData instance to the list.
  7700. *
  7701. * @param {HTMLElement} el - An HTMLElement instance.
  7702. *
  7703. * @param {UpdateElementData} updateData - An UpdateElementData instance.
  7704. *
  7705. * @returns {Promise<number>} The unified next frame update promise. Returns `currentTime`.
  7706. */
  7707. static add(el, updateData) {
  7708. if (this.listCntr < this.list.length) {
  7709. const entry = this.list[this.listCntr];
  7710. entry[0] = el;
  7711. entry[1] = updateData;
  7712. } else {
  7713. this.list.push([el, updateData]);
  7714. }
  7715. this.listCntr++;
  7716. updateData.queued = true;
  7717. if (!this.updatePromise) {
  7718. this.updatePromise = this.wait();
  7719. }
  7720. return this.updatePromise;
  7721. }
  7722. /**
  7723. * Await on `nextAnimationFrame` and iterate over list map invoking callback functions.
  7724. *
  7725. * @returns {Promise<number>} The next frame Promise / currentTime from nextAnimationFrame.
  7726. */
  7727. static async wait() {
  7728. const currentTime = await nextAnimationFrame();
  7729. this.updatePromise = void 0;
  7730. for (let cntr = this.listCntr; --cntr >= 0; ) {
  7731. const entry = this.list[cntr];
  7732. const el = entry[0];
  7733. const updateData = entry[1];
  7734. entry[0] = void 0;
  7735. entry[1] = void 0;
  7736. updateData.queued = false;
  7737. if (!el.isConnected) {
  7738. continue;
  7739. }
  7740. if (updateData.options.ortho) {
  7741. s_UPDATE_ELEMENT_ORTHO(el, updateData);
  7742. } else {
  7743. s_UPDATE_ELEMENT(el, updateData);
  7744. }
  7745. if (updateData.options.calculateTransform || updateData.options.transformSubscribed) {
  7746. s_UPDATE_TRANSFORM(el, updateData);
  7747. }
  7748. this.updateSubscribers(updateData);
  7749. }
  7750. this.listCntr = 0;
  7751. return currentTime;
  7752. }
  7753. /**
  7754. * Potentially immediately updates the given element.
  7755. *
  7756. * @param {HTMLElement} el - An HTMLElement instance.
  7757. *
  7758. * @param {UpdateElementData} updateData - An UpdateElementData instance.
  7759. */
  7760. static immediate(el, updateData) {
  7761. if (!el.isConnected) {
  7762. return;
  7763. }
  7764. if (updateData.options.ortho) {
  7765. s_UPDATE_ELEMENT_ORTHO(el, updateData);
  7766. } else {
  7767. s_UPDATE_ELEMENT(el, updateData);
  7768. }
  7769. if (updateData.options.calculateTransform || updateData.options.transformSubscribed) {
  7770. s_UPDATE_TRANSFORM(el, updateData);
  7771. }
  7772. this.updateSubscribers(updateData);
  7773. }
  7774. /**
  7775. * @param {UpdateElementData} updateData - Data change set.
  7776. */
  7777. static updateSubscribers(updateData) {
  7778. const data = updateData.data;
  7779. const changeSet = updateData.changeSet;
  7780. if (!changeSet.hasChange()) {
  7781. return;
  7782. }
  7783. const output = updateData.dataSubscribers.copy(data);
  7784. const subscriptions = updateData.subscriptions;
  7785. if (subscriptions.length > 0) {
  7786. for (let cntr = 0; cntr < subscriptions.length; cntr++) {
  7787. subscriptions[cntr](output);
  7788. }
  7789. }
  7790. if (changeSet.width || changeSet.height) {
  7791. updateData.dimensionData.width = data.width;
  7792. updateData.dimensionData.height = data.height;
  7793. updateData.storeDimension.set(updateData.dimensionData);
  7794. }
  7795. changeSet.set(false);
  7796. }
  7797. }
  7798. function s_UPDATE_ELEMENT(el, updateData) {
  7799. const changeSet = updateData.changeSet;
  7800. const data = updateData.data;
  7801. if (changeSet.left) {
  7802. el.style.left = `${data.left}px`;
  7803. }
  7804. if (changeSet.top) {
  7805. el.style.top = `${data.top}px`;
  7806. }
  7807. if (changeSet.zIndex) {
  7808. el.style.zIndex = typeof data.zIndex === "number" ? `${data.zIndex}` : null;
  7809. }
  7810. if (changeSet.width) {
  7811. el.style.width = typeof data.width === "number" ? `${data.width}px` : data.width;
  7812. }
  7813. if (changeSet.height) {
  7814. el.style.height = typeof data.height === "number" ? `${data.height}px` : data.height;
  7815. }
  7816. if (changeSet.transformOrigin) {
  7817. el.style.transformOrigin = data.transformOrigin === "center" ? null : data.transformOrigin;
  7818. }
  7819. if (changeSet.transform) {
  7820. el.style.transform = updateData.transforms.isActive ? updateData.transforms.getCSS() : null;
  7821. }
  7822. }
  7823. function s_UPDATE_ELEMENT_ORTHO(el, updateData) {
  7824. const changeSet = updateData.changeSet;
  7825. const data = updateData.data;
  7826. if (changeSet.zIndex) {
  7827. el.style.zIndex = typeof data.zIndex === "number" ? `${data.zIndex}` : null;
  7828. }
  7829. if (changeSet.width) {
  7830. el.style.width = typeof data.width === "number" ? `${data.width}px` : data.width;
  7831. }
  7832. if (changeSet.height) {
  7833. el.style.height = typeof data.height === "number" ? `${data.height}px` : data.height;
  7834. }
  7835. if (changeSet.transformOrigin) {
  7836. el.style.transformOrigin = data.transformOrigin === "center" ? null : data.transformOrigin;
  7837. }
  7838. if (changeSet.left || changeSet.top || changeSet.transform) {
  7839. el.style.transform = updateData.transforms.getCSSOrtho(data);
  7840. }
  7841. }
  7842. function s_UPDATE_TRANSFORM(el, updateData) {
  7843. s_VALIDATION_DATA$1.height = updateData.data.height !== "auto" ? updateData.data.height : updateData.styleCache.offsetHeight;
  7844. s_VALIDATION_DATA$1.width = updateData.data.width !== "auto" ? updateData.data.width : updateData.styleCache.offsetWidth;
  7845. s_VALIDATION_DATA$1.marginLeft = updateData.styleCache.marginLeft;
  7846. s_VALIDATION_DATA$1.marginTop = updateData.styleCache.marginTop;
  7847. updateData.transforms.getData(updateData.data, updateData.transformData, s_VALIDATION_DATA$1);
  7848. updateData.storeTransform.set(updateData.transformData);
  7849. }
  7850. const s_VALIDATION_DATA$1 = {
  7851. height: void 0,
  7852. width: void 0,
  7853. marginLeft: void 0,
  7854. marginTop: void 0
  7855. };
  7856. class Position {
  7857. /**
  7858. * @type {PositionData}
  7859. */
  7860. #data = new PositionData();
  7861. /**
  7862. * Provides the animation API.
  7863. *
  7864. * @type {AnimationAPI}
  7865. */
  7866. #animate = new AnimationAPI(this, this.#data);
  7867. /**
  7868. * Provides a way to turn on / off the position handling.
  7869. *
  7870. * @type {boolean}
  7871. */
  7872. #enabled = true;
  7873. /**
  7874. * Stores the style attributes that changed on update.
  7875. *
  7876. * @type {PositionChangeSet}
  7877. */
  7878. #positionChangeSet = new PositionChangeSet();
  7879. /**
  7880. * Stores ongoing options that are set in the constructor or by transform store subscription.
  7881. *
  7882. * @type {PositionOptions}
  7883. */
  7884. #options = {
  7885. calculateTransform: false,
  7886. initialHelper: void 0,
  7887. ortho: true,
  7888. transformSubscribed: false
  7889. };
  7890. /**
  7891. * The associated parent for positional data tracking. Used in validators.
  7892. *
  7893. * @type {PositionParent}
  7894. */
  7895. #parent;
  7896. /**
  7897. * @type {StorePosition}
  7898. */
  7899. #stores;
  7900. /**
  7901. * Stores an instance of the computer styles for the target element.
  7902. *
  7903. * @type {StyleCache}
  7904. */
  7905. #styleCache;
  7906. /**
  7907. * Stores the subscribers.
  7908. *
  7909. * @type {(function(PositionData): void)[]}
  7910. */
  7911. #subscriptions = [];
  7912. /**
  7913. * @type {Transforms}
  7914. */
  7915. #transforms = new Transforms();
  7916. /**
  7917. * @type {UpdateElementData}
  7918. */
  7919. #updateElementData;
  7920. /**
  7921. * Stores the UpdateElementManager wait promise.
  7922. *
  7923. * @type {Promise}
  7924. */
  7925. #updateElementPromise;
  7926. /**
  7927. * @type {AdapterValidators}
  7928. */
  7929. #validators;
  7930. /**
  7931. * @type {ValidatorData[]}
  7932. */
  7933. #validatorData;
  7934. /**
  7935. * @type {PositionStateAPI}
  7936. */
  7937. #state = new PositionStateAPI(this, this.#data, this.#transforms);
  7938. /**
  7939. * @returns {AnimationGroupAPI} Public Animation API.
  7940. */
  7941. static get Animate() {
  7942. return AnimationGroupAPI;
  7943. }
  7944. /**
  7945. * @returns {{browserCentered?: Centered, Centered?: *}} Initial position helpers.
  7946. */
  7947. static get Initial() {
  7948. return positionInitial;
  7949. }
  7950. /**
  7951. * Returns TransformData class / constructor.
  7952. *
  7953. * @returns {TransformData} TransformData class / constructor.
  7954. */
  7955. static get TransformData() {
  7956. return TransformData;
  7957. }
  7958. /**
  7959. * Returns default validators.
  7960. *
  7961. * Note: `basicWindow` and `BasicBounds` will eventually be removed.
  7962. *
  7963. * @returns {{basicWindow?: BasicBounds, transformWindow?: TransformBounds, TransformBounds?: *, BasicBounds?: *}}
  7964. * Available validators.
  7965. */
  7966. static get Validators() {
  7967. return positionValidators;
  7968. }
  7969. /**
  7970. * Returns a duplicate of a given position instance copying any options and validators.
  7971. *
  7972. * // TODO: Consider more safety over options processing.
  7973. *
  7974. * @param {Position} position - A position instance.
  7975. *
  7976. * @param {PositionOptions} options - Position options.
  7977. *
  7978. * @returns {Position} A duplicate position instance.
  7979. */
  7980. static duplicate(position, options) {
  7981. if (!(position instanceof Position)) {
  7982. throw new TypeError(`'position' is not an instance of Position.`);
  7983. }
  7984. const newPosition = new Position(options);
  7985. newPosition.#options = Object.assign({}, position.#options, options);
  7986. newPosition.#validators.add(...position.#validators);
  7987. newPosition.set(position.#data);
  7988. return newPosition;
  7989. }
  7990. /**
  7991. * @param {PositionParent|PositionOptionsAll} [parent] - A potential parent element or object w/ `elementTarget`
  7992. * getter. May also be the PositionOptions object w/ 1 argument.
  7993. *
  7994. * @param {PositionOptionsAll} [options] - Default values.
  7995. */
  7996. constructor(parent, options) {
  7997. if (isPlainObject(parent)) {
  7998. options = parent;
  7999. } else {
  8000. this.#parent = parent;
  8001. }
  8002. const data = this.#data;
  8003. const transforms = this.#transforms;
  8004. this.#styleCache = new StyleCache();
  8005. const updateData = new UpdateElementData();
  8006. updateData.changeSet = this.#positionChangeSet;
  8007. updateData.data = this.#data;
  8008. updateData.options = this.#options;
  8009. updateData.styleCache = this.#styleCache;
  8010. updateData.subscriptions = this.#subscriptions;
  8011. updateData.transforms = this.#transforms;
  8012. this.#updateElementData = updateData;
  8013. if (isObject(options)) {
  8014. if (typeof options.calculateTransform === "boolean") {
  8015. this.#options.calculateTransform = options.calculateTransform;
  8016. }
  8017. if (typeof options.ortho === "boolean") {
  8018. this.#options.ortho = options.ortho;
  8019. }
  8020. if (Number.isFinite(options.height) || options.height === "auto" || options.height === "inherit" || options.height === null) {
  8021. data.height = updateData.dimensionData.height = typeof options.height === "number" ? Math.round(options.height) : options.height;
  8022. }
  8023. if (Number.isFinite(options.left) || options.left === null) {
  8024. data.left = typeof options.left === "number" ? Math.round(options.left) : options.left;
  8025. }
  8026. if (Number.isFinite(options.maxHeight) || options.maxHeight === null) {
  8027. data.maxHeight = typeof options.maxHeight === "number" ? Math.round(options.maxHeight) : options.maxHeight;
  8028. }
  8029. if (Number.isFinite(options.maxWidth) || options.maxWidth === null) {
  8030. data.maxWidth = typeof options.maxWidth === "number" ? Math.round(options.maxWidth) : options.maxWidth;
  8031. }
  8032. if (Number.isFinite(options.minHeight) || options.minHeight === null) {
  8033. data.minHeight = typeof options.minHeight === "number" ? Math.round(options.minHeight) : options.minHeight;
  8034. }
  8035. if (Number.isFinite(options.minWidth) || options.minWidth === null) {
  8036. data.minWidth = typeof options.minWidth === "number" ? Math.round(options.minWidth) : options.minWidth;
  8037. }
  8038. if (Number.isFinite(options.rotateX) || options.rotateX === null) {
  8039. transforms.rotateX = data.rotateX = options.rotateX;
  8040. }
  8041. if (Number.isFinite(options.rotateY) || options.rotateY === null) {
  8042. transforms.rotateY = data.rotateY = options.rotateY;
  8043. }
  8044. if (Number.isFinite(options.rotateZ) || options.rotateZ === null) {
  8045. transforms.rotateZ = data.rotateZ = options.rotateZ;
  8046. }
  8047. if (Number.isFinite(options.scale) || options.scale === null) {
  8048. transforms.scale = data.scale = options.scale;
  8049. }
  8050. if (Number.isFinite(options.top) || options.top === null) {
  8051. data.top = typeof options.top === "number" ? Math.round(options.top) : options.top;
  8052. }
  8053. if (typeof options.transformOrigin === "string" || options.transformOrigin === null) {
  8054. data.transformOrigin = transformOrigins.includes(options.transformOrigin) ? options.transformOrigin : null;
  8055. }
  8056. if (Number.isFinite(options.translateX) || options.translateX === null) {
  8057. transforms.translateX = data.translateX = options.translateX;
  8058. }
  8059. if (Number.isFinite(options.translateY) || options.translateY === null) {
  8060. transforms.translateY = data.translateY = options.translateY;
  8061. }
  8062. if (Number.isFinite(options.translateZ) || options.translateZ === null) {
  8063. transforms.translateZ = data.translateZ = options.translateZ;
  8064. }
  8065. if (Number.isFinite(options.width) || options.width === "auto" || options.width === "inherit" || options.width === null) {
  8066. data.width = updateData.dimensionData.width = typeof options.width === "number" ? Math.round(options.width) : options.width;
  8067. }
  8068. if (Number.isFinite(options.zIndex) || options.zIndex === null) {
  8069. data.zIndex = typeof options.zIndex === "number" ? Math.round(options.zIndex) : options.zIndex;
  8070. }
  8071. }
  8072. this.#stores = {
  8073. // The main properties for manipulating Position.
  8074. height: propertyStore(this, "height"),
  8075. left: propertyStore(this, "left"),
  8076. rotateX: propertyStore(this, "rotateX"),
  8077. rotateY: propertyStore(this, "rotateY"),
  8078. rotateZ: propertyStore(this, "rotateZ"),
  8079. scale: propertyStore(this, "scale"),
  8080. top: propertyStore(this, "top"),
  8081. transformOrigin: propertyStore(this, "transformOrigin"),
  8082. translateX: propertyStore(this, "translateX"),
  8083. translateY: propertyStore(this, "translateY"),
  8084. translateZ: propertyStore(this, "translateZ"),
  8085. width: propertyStore(this, "width"),
  8086. zIndex: propertyStore(this, "zIndex"),
  8087. // Stores that control validation when width / height is not `auto`.
  8088. maxHeight: propertyStore(this, "maxHeight"),
  8089. maxWidth: propertyStore(this, "maxWidth"),
  8090. minHeight: propertyStore(this, "minHeight"),
  8091. minWidth: propertyStore(this, "minWidth"),
  8092. // Readable stores based on updates or from resize observer changes.
  8093. dimension: { subscribe: updateData.storeDimension.subscribe },
  8094. element: { subscribe: this.#styleCache.stores.element.subscribe },
  8095. resizeContentHeight: { subscribe: this.#styleCache.stores.resizeContentHeight.subscribe },
  8096. resizeContentWidth: { subscribe: this.#styleCache.stores.resizeContentWidth.subscribe },
  8097. resizeOffsetHeight: { subscribe: this.#styleCache.stores.resizeOffsetHeight.subscribe },
  8098. resizeOffsetWidth: { subscribe: this.#styleCache.stores.resizeOffsetWidth.subscribe },
  8099. transform: { subscribe: updateData.storeTransform.subscribe },
  8100. // Protected store that should only be set by resizeObserver action.
  8101. resizeObserved: this.#styleCache.stores.resizeObserved
  8102. };
  8103. subscribeIgnoreFirst(this.#stores.resizeObserved, (resizeData) => {
  8104. const parent2 = this.#parent;
  8105. const el = parent2 instanceof HTMLElement ? parent2 : parent2?.elementTarget;
  8106. if (el instanceof HTMLElement && Number.isFinite(resizeData?.offsetWidth) && Number.isFinite(resizeData?.offsetHeight)) {
  8107. this.set(data);
  8108. }
  8109. });
  8110. this.#stores.transformOrigin.values = transformOrigins;
  8111. [this.#validators, this.#validatorData] = new AdapterValidators();
  8112. if (options?.initial || options?.positionInitial) {
  8113. const initialHelper = options.initial ?? options.positionInitial;
  8114. if (typeof initialHelper?.getLeft !== "function" || typeof initialHelper?.getTop !== "function") {
  8115. throw new Error(
  8116. `'options.initial' position helper does not contain 'getLeft' and / or 'getTop' functions.`
  8117. );
  8118. }
  8119. this.#options.initialHelper = options.initial;
  8120. }
  8121. if (options?.validator) {
  8122. if (isIterable(options?.validator)) {
  8123. this.validators.add(...options.validator);
  8124. } else {
  8125. this.validators.add(options.validator);
  8126. }
  8127. }
  8128. }
  8129. /**
  8130. * Returns the animation API.
  8131. *
  8132. * @returns {AnimationAPI} Animation API.
  8133. */
  8134. get animate() {
  8135. return this.#animate;
  8136. }
  8137. /**
  8138. * Returns the dimension data for the readable store.
  8139. *
  8140. * @returns {{width: number | 'auto', height: number | 'auto'}} Dimension data.
  8141. */
  8142. get dimension() {
  8143. return this.#updateElementData.dimensionData;
  8144. }
  8145. /**
  8146. * Returns the enabled state.
  8147. *
  8148. * @returns {boolean} Enabled state.
  8149. */
  8150. get enabled() {
  8151. return this.#enabled;
  8152. }
  8153. /**
  8154. * Returns the current HTMLElement being positioned.
  8155. *
  8156. * @returns {HTMLElement|undefined} Current HTMLElement being positioned.
  8157. */
  8158. get element() {
  8159. return this.#styleCache.el;
  8160. }
  8161. /**
  8162. * Returns a promise that is resolved on the next element update with the time of the update.
  8163. *
  8164. * @returns {Promise<number>} Promise resolved on element update.
  8165. */
  8166. get elementUpdated() {
  8167. return this.#updateElementPromise;
  8168. }
  8169. /**
  8170. * Returns the associated {@link PositionParent} instance.
  8171. *
  8172. * @returns {PositionParent} The PositionParent instance.
  8173. */
  8174. get parent() {
  8175. return this.#parent;
  8176. }
  8177. /**
  8178. * Returns the state API.
  8179. *
  8180. * @returns {PositionStateAPI} Position state API.
  8181. */
  8182. get state() {
  8183. return this.#state;
  8184. }
  8185. /**
  8186. * Returns the derived writable stores for individual data variables.
  8187. *
  8188. * @returns {StorePosition} Derived / writable stores.
  8189. */
  8190. get stores() {
  8191. return this.#stores;
  8192. }
  8193. /**
  8194. * Returns the transform data for the readable store.
  8195. *
  8196. * @returns {TransformData} Transform Data.
  8197. */
  8198. get transform() {
  8199. return this.#updateElementData.transformData;
  8200. }
  8201. /**
  8202. * Returns the validators.
  8203. *
  8204. * @returns {AdapterValidators} validators.
  8205. */
  8206. get validators() {
  8207. return this.#validators;
  8208. }
  8209. /**
  8210. * Sets the enabled state.
  8211. *
  8212. * @param {boolean} enabled - New enabled state.
  8213. */
  8214. set enabled(enabled) {
  8215. if (typeof enabled !== "boolean") {
  8216. throw new TypeError(`'enabled' is not a boolean.`);
  8217. }
  8218. this.#enabled = enabled;
  8219. }
  8220. /**
  8221. * Sets the associated {@link PositionParent} instance. Resets the style cache and default data.
  8222. *
  8223. * @param {PositionParent|void} parent - A PositionParent instance.
  8224. */
  8225. set parent(parent) {
  8226. if (parent !== void 0 && !(parent instanceof HTMLElement) && !isObject(parent)) {
  8227. throw new TypeError(`'parent' is not an HTMLElement, object, or undefined.`);
  8228. }
  8229. this.#parent = parent;
  8230. this.#state.remove({ name: "#defaultData" });
  8231. this.#styleCache.reset();
  8232. if (parent) {
  8233. this.set(this.#data);
  8234. }
  8235. }
  8236. // Data accessors ----------------------------------------------------------------------------------------------------
  8237. /**
  8238. * @returns {number|'auto'|'inherit'|null} height
  8239. */
  8240. get height() {
  8241. return this.#data.height;
  8242. }
  8243. /**
  8244. * @returns {number|null} left
  8245. */
  8246. get left() {
  8247. return this.#data.left;
  8248. }
  8249. /**
  8250. * @returns {number|null} maxHeight
  8251. */
  8252. get maxHeight() {
  8253. return this.#data.maxHeight;
  8254. }
  8255. /**
  8256. * @returns {number|null} maxWidth
  8257. */
  8258. get maxWidth() {
  8259. return this.#data.maxWidth;
  8260. }
  8261. /**
  8262. * @returns {number|null} minHeight
  8263. */
  8264. get minHeight() {
  8265. return this.#data.minHeight;
  8266. }
  8267. /**
  8268. * @returns {number|null} minWidth
  8269. */
  8270. get minWidth() {
  8271. return this.#data.minWidth;
  8272. }
  8273. /**
  8274. * @returns {number|null} rotateX
  8275. */
  8276. get rotateX() {
  8277. return this.#data.rotateX;
  8278. }
  8279. /**
  8280. * @returns {number|null} rotateY
  8281. */
  8282. get rotateY() {
  8283. return this.#data.rotateY;
  8284. }
  8285. /**
  8286. * @returns {number|null} rotateZ
  8287. */
  8288. get rotateZ() {
  8289. return this.#data.rotateZ;
  8290. }
  8291. /**
  8292. * @returns {number|null} alias for rotateZ
  8293. */
  8294. get rotation() {
  8295. return this.#data.rotateZ;
  8296. }
  8297. /**
  8298. * @returns {number|null} scale
  8299. */
  8300. get scale() {
  8301. return this.#data.scale;
  8302. }
  8303. /**
  8304. * @returns {number|null} top
  8305. */
  8306. get top() {
  8307. return this.#data.top;
  8308. }
  8309. /**
  8310. * @returns {string} transformOrigin
  8311. */
  8312. get transformOrigin() {
  8313. return this.#data.transformOrigin;
  8314. }
  8315. /**
  8316. * @returns {number|null} translateX
  8317. */
  8318. get translateX() {
  8319. return this.#data.translateX;
  8320. }
  8321. /**
  8322. * @returns {number|null} translateY
  8323. */
  8324. get translateY() {
  8325. return this.#data.translateY;
  8326. }
  8327. /**
  8328. * @returns {number|null} translateZ
  8329. */
  8330. get translateZ() {
  8331. return this.#data.translateZ;
  8332. }
  8333. /**
  8334. * @returns {number|'auto'|'inherit'|null} width
  8335. */
  8336. get width() {
  8337. return this.#data.width;
  8338. }
  8339. /**
  8340. * @returns {number|null} z-index
  8341. */
  8342. get zIndex() {
  8343. return this.#data.zIndex;
  8344. }
  8345. /**
  8346. * @param {number|string|null} height -
  8347. */
  8348. set height(height) {
  8349. this.#stores.height.set(height);
  8350. }
  8351. /**
  8352. * @param {number|string|null} left -
  8353. */
  8354. set left(left) {
  8355. this.#stores.left.set(left);
  8356. }
  8357. /**
  8358. * @param {number|string|null} maxHeight -
  8359. */
  8360. set maxHeight(maxHeight) {
  8361. this.#stores.maxHeight.set(maxHeight);
  8362. }
  8363. /**
  8364. * @param {number|string|null} maxWidth -
  8365. */
  8366. set maxWidth(maxWidth) {
  8367. this.#stores.maxWidth.set(maxWidth);
  8368. }
  8369. /**
  8370. * @param {number|string|null} minHeight -
  8371. */
  8372. set minHeight(minHeight) {
  8373. this.#stores.minHeight.set(minHeight);
  8374. }
  8375. /**
  8376. * @param {number|string|null} minWidth -
  8377. */
  8378. set minWidth(minWidth) {
  8379. this.#stores.minWidth.set(minWidth);
  8380. }
  8381. /**
  8382. * @param {number|string|null} rotateX -
  8383. */
  8384. set rotateX(rotateX) {
  8385. this.#stores.rotateX.set(rotateX);
  8386. }
  8387. /**
  8388. * @param {number|string|null} rotateY -
  8389. */
  8390. set rotateY(rotateY) {
  8391. this.#stores.rotateY.set(rotateY);
  8392. }
  8393. /**
  8394. * @param {number|string|null} rotateZ -
  8395. */
  8396. set rotateZ(rotateZ) {
  8397. this.#stores.rotateZ.set(rotateZ);
  8398. }
  8399. /**
  8400. * @param {number|string|null} rotateZ - alias for rotateZ
  8401. */
  8402. set rotation(rotateZ) {
  8403. this.#stores.rotateZ.set(rotateZ);
  8404. }
  8405. /**
  8406. * @param {number|string|null} scale -
  8407. */
  8408. set scale(scale2) {
  8409. this.#stores.scale.set(scale2);
  8410. }
  8411. /**
  8412. * @param {number|string|null} top -
  8413. */
  8414. set top(top) {
  8415. this.#stores.top.set(top);
  8416. }
  8417. /**
  8418. * @param {string} transformOrigin -
  8419. */
  8420. set transformOrigin(transformOrigin) {
  8421. if (transformOrigins.includes(transformOrigin)) {
  8422. this.#stores.transformOrigin.set(transformOrigin);
  8423. }
  8424. }
  8425. /**
  8426. * @param {number|string|null} translateX -
  8427. */
  8428. set translateX(translateX) {
  8429. this.#stores.translateX.set(translateX);
  8430. }
  8431. /**
  8432. * @param {number|string|null} translateY -
  8433. */
  8434. set translateY(translateY) {
  8435. this.#stores.translateY.set(translateY);
  8436. }
  8437. /**
  8438. * @param {number|string|null} translateZ -
  8439. */
  8440. set translateZ(translateZ) {
  8441. this.#stores.translateZ.set(translateZ);
  8442. }
  8443. /**
  8444. * @param {number|string|null} width -
  8445. */
  8446. set width(width2) {
  8447. this.#stores.width.set(width2);
  8448. }
  8449. /**
  8450. * @param {number|string|null} zIndex -
  8451. */
  8452. set zIndex(zIndex) {
  8453. this.#stores.zIndex.set(zIndex);
  8454. }
  8455. /**
  8456. * Assigns current position to object passed into method.
  8457. *
  8458. * @param {object|PositionData} [position] - Target to assign current position data.
  8459. *
  8460. * @param {PositionGetOptions} [options] - Defines options for specific keys and substituting null for numeric
  8461. * default values.
  8462. *
  8463. * @returns {PositionData} Passed in object with current position data.
  8464. */
  8465. get(position = {}, options) {
  8466. const keys = options?.keys;
  8467. const excludeKeys = options?.exclude;
  8468. const numeric = options?.numeric ?? false;
  8469. if (isIterable(keys)) {
  8470. if (numeric) {
  8471. for (const key of keys) {
  8472. position[key] = this[key] ?? numericDefaults[key];
  8473. }
  8474. } else {
  8475. for (const key of keys) {
  8476. position[key] = this[key];
  8477. }
  8478. }
  8479. if (isIterable(excludeKeys)) {
  8480. for (const key of excludeKeys) {
  8481. delete position[key];
  8482. }
  8483. }
  8484. return position;
  8485. } else {
  8486. const data = Object.assign(position, this.#data);
  8487. if (isIterable(excludeKeys)) {
  8488. for (const key of excludeKeys) {
  8489. delete data[key];
  8490. }
  8491. }
  8492. if (numeric) {
  8493. setNumericDefaults(data);
  8494. }
  8495. return data;
  8496. }
  8497. }
  8498. /**
  8499. * @returns {PositionData} Current position data.
  8500. */
  8501. toJSON() {
  8502. return Object.assign({}, this.#data);
  8503. }
  8504. /**
  8505. * All calculation and updates of position are implemented in {@link Position}. This allows position to be fully
  8506. * reactive and in control of updating inline styles for the application.
  8507. *
  8508. * Note: the logic for updating position is improved and changes a few aspects from the default
  8509. * {@link Application.setPosition}. The gate on `popOut` is removed, so to ensure no positional application occurs
  8510. * popOut applications can set `this.options.positionable` to false ensuring no positional inline styles are
  8511. * applied.
  8512. *
  8513. * The initial set call on an application with a target element will always set width / height as this is
  8514. * necessary for correct calculations.
  8515. *
  8516. * When a target element is present updated styles are applied after validation. To modify the behavior of set
  8517. * implement one or more validator functions and add them from the application via
  8518. * `this.position.validators.add(<Function>)`.
  8519. *
  8520. * Updates to any target element are decoupled from the underlying Position data. This method returns this instance
  8521. * that you can then await on the target element inline style update by using {@link Position.elementUpdated}.
  8522. *
  8523. * @param {PositionDataExtended} [position] - Position data to set.
  8524. *
  8525. * @returns {Position} This Position instance.
  8526. */
  8527. set(position = {}) {
  8528. if (typeof position !== "object") {
  8529. throw new TypeError(`Position - set error: 'position' is not an object.`);
  8530. }
  8531. const parent = this.#parent;
  8532. if (!this.#enabled) {
  8533. return this;
  8534. }
  8535. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  8536. return this;
  8537. }
  8538. const immediateElementUpdate = position.immediateElementUpdate === true;
  8539. const data = this.#data;
  8540. const transforms = this.#transforms;
  8541. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  8542. const el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  8543. const changeSet = this.#positionChangeSet;
  8544. const styleCache = this.#styleCache;
  8545. if (el) {
  8546. if (!styleCache.hasData(el)) {
  8547. styleCache.update(el);
  8548. if (!styleCache.hasWillChange)
  8549. ;
  8550. changeSet.set(true);
  8551. this.#updateElementData.queued = false;
  8552. }
  8553. convertRelative(position, this);
  8554. position = this.#updatePosition(position, parent, el, styleCache);
  8555. if (position === null) {
  8556. return this;
  8557. }
  8558. }
  8559. if (Number.isFinite(position.left)) {
  8560. position.left = Math.round(position.left);
  8561. if (data.left !== position.left) {
  8562. data.left = position.left;
  8563. changeSet.left = true;
  8564. }
  8565. }
  8566. if (Number.isFinite(position.top)) {
  8567. position.top = Math.round(position.top);
  8568. if (data.top !== position.top) {
  8569. data.top = position.top;
  8570. changeSet.top = true;
  8571. }
  8572. }
  8573. if (Number.isFinite(position.maxHeight) || position.maxHeight === null) {
  8574. position.maxHeight = typeof position.maxHeight === "number" ? Math.round(position.maxHeight) : null;
  8575. if (data.maxHeight !== position.maxHeight) {
  8576. data.maxHeight = position.maxHeight;
  8577. changeSet.maxHeight = true;
  8578. }
  8579. }
  8580. if (Number.isFinite(position.maxWidth) || position.maxWidth === null) {
  8581. position.maxWidth = typeof position.maxWidth === "number" ? Math.round(position.maxWidth) : null;
  8582. if (data.maxWidth !== position.maxWidth) {
  8583. data.maxWidth = position.maxWidth;
  8584. changeSet.maxWidth = true;
  8585. }
  8586. }
  8587. if (Number.isFinite(position.minHeight) || position.minHeight === null) {
  8588. position.minHeight = typeof position.minHeight === "number" ? Math.round(position.minHeight) : null;
  8589. if (data.minHeight !== position.minHeight) {
  8590. data.minHeight = position.minHeight;
  8591. changeSet.minHeight = true;
  8592. }
  8593. }
  8594. if (Number.isFinite(position.minWidth) || position.minWidth === null) {
  8595. position.minWidth = typeof position.minWidth === "number" ? Math.round(position.minWidth) : null;
  8596. if (data.minWidth !== position.minWidth) {
  8597. data.minWidth = position.minWidth;
  8598. changeSet.minWidth = true;
  8599. }
  8600. }
  8601. if (Number.isFinite(position.rotateX) || position.rotateX === null) {
  8602. if (data.rotateX !== position.rotateX) {
  8603. data.rotateX = transforms.rotateX = position.rotateX;
  8604. changeSet.transform = true;
  8605. }
  8606. }
  8607. if (Number.isFinite(position.rotateY) || position.rotateY === null) {
  8608. if (data.rotateY !== position.rotateY) {
  8609. data.rotateY = transforms.rotateY = position.rotateY;
  8610. changeSet.transform = true;
  8611. }
  8612. }
  8613. if (Number.isFinite(position.rotateZ) || position.rotateZ === null) {
  8614. if (data.rotateZ !== position.rotateZ) {
  8615. data.rotateZ = transforms.rotateZ = position.rotateZ;
  8616. changeSet.transform = true;
  8617. }
  8618. }
  8619. if (Number.isFinite(position.scale) || position.scale === null) {
  8620. position.scale = typeof position.scale === "number" ? Math.max(0, Math.min(position.scale, 1e3)) : null;
  8621. if (data.scale !== position.scale) {
  8622. data.scale = transforms.scale = position.scale;
  8623. changeSet.transform = true;
  8624. }
  8625. }
  8626. if (typeof position.transformOrigin === "string" && transformOrigins.includes(
  8627. position.transformOrigin
  8628. ) || position.transformOrigin === null) {
  8629. if (data.transformOrigin !== position.transformOrigin) {
  8630. data.transformOrigin = position.transformOrigin;
  8631. changeSet.transformOrigin = true;
  8632. }
  8633. }
  8634. if (Number.isFinite(position.translateX) || position.translateX === null) {
  8635. if (data.translateX !== position.translateX) {
  8636. data.translateX = transforms.translateX = position.translateX;
  8637. changeSet.transform = true;
  8638. }
  8639. }
  8640. if (Number.isFinite(position.translateY) || position.translateY === null) {
  8641. if (data.translateY !== position.translateY) {
  8642. data.translateY = transforms.translateY = position.translateY;
  8643. changeSet.transform = true;
  8644. }
  8645. }
  8646. if (Number.isFinite(position.translateZ) || position.translateZ === null) {
  8647. if (data.translateZ !== position.translateZ) {
  8648. data.translateZ = transforms.translateZ = position.translateZ;
  8649. changeSet.transform = true;
  8650. }
  8651. }
  8652. if (Number.isFinite(position.zIndex)) {
  8653. position.zIndex = Math.round(position.zIndex);
  8654. if (data.zIndex !== position.zIndex) {
  8655. data.zIndex = position.zIndex;
  8656. changeSet.zIndex = true;
  8657. }
  8658. }
  8659. if (Number.isFinite(position.width) || position.width === "auto" || position.width === "inherit" || position.width === null) {
  8660. position.width = typeof position.width === "number" ? Math.round(position.width) : position.width;
  8661. if (data.width !== position.width) {
  8662. data.width = position.width;
  8663. changeSet.width = true;
  8664. }
  8665. }
  8666. if (Number.isFinite(position.height) || position.height === "auto" || position.height === "inherit" || position.height === null) {
  8667. position.height = typeof position.height === "number" ? Math.round(position.height) : position.height;
  8668. if (data.height !== position.height) {
  8669. data.height = position.height;
  8670. changeSet.height = true;
  8671. }
  8672. }
  8673. if (el) {
  8674. const defaultData = this.#state.getDefault();
  8675. if (typeof defaultData !== "object") {
  8676. this.#state.save({ name: "#defaultData", ...Object.assign({}, data) });
  8677. }
  8678. if (immediateElementUpdate) {
  8679. UpdateElementManager.immediate(el, this.#updateElementData);
  8680. this.#updateElementPromise = Promise.resolve(performance.now());
  8681. } else if (!this.#updateElementData.queued) {
  8682. this.#updateElementPromise = UpdateElementManager.add(el, this.#updateElementData);
  8683. }
  8684. } else {
  8685. UpdateElementManager.updateSubscribers(this.#updateElementData);
  8686. }
  8687. return this;
  8688. }
  8689. /**
  8690. *
  8691. * @param {function(PositionData): void} handler - Callback function that is invoked on update / changes. Receives
  8692. * a copy of the PositionData.
  8693. *
  8694. * @returns {(function(): void)} Unsubscribe function.
  8695. */
  8696. subscribe(handler) {
  8697. this.#subscriptions.push(handler);
  8698. handler(Object.assign({}, this.#data));
  8699. return () => {
  8700. const index = this.#subscriptions.findIndex((sub) => sub === handler);
  8701. if (index >= 0) {
  8702. this.#subscriptions.splice(index, 1);
  8703. }
  8704. };
  8705. }
  8706. /**
  8707. * @param {PositionDataExtended} opts -
  8708. *
  8709. * @param {number|null} opts.left -
  8710. *
  8711. * @param {number|null} opts.top -
  8712. *
  8713. * @param {number|null} opts.maxHeight -
  8714. *
  8715. * @param {number|null} opts.maxWidth -
  8716. *
  8717. * @param {number|null} opts.minHeight -
  8718. *
  8719. * @param {number|null} opts.minWidth -
  8720. *
  8721. * @param {number|'auto'|null} opts.width -
  8722. *
  8723. * @param {number|'auto'|null} opts.height -
  8724. *
  8725. * @param {number|null} opts.rotateX -
  8726. *
  8727. * @param {number|null} opts.rotateY -
  8728. *
  8729. * @param {number|null} opts.rotateZ -
  8730. *
  8731. * @param {number|null} opts.scale -
  8732. *
  8733. * @param {string} opts.transformOrigin -
  8734. *
  8735. * @param {number|null} opts.translateX -
  8736. *
  8737. * @param {number|null} opts.translateY -
  8738. *
  8739. * @param {number|null} opts.translateZ -
  8740. *
  8741. * @param {number|null} opts.zIndex -
  8742. *
  8743. * @param {number|null} opts.rotation - alias for rotateZ
  8744. *
  8745. * @param {*} opts.rest -
  8746. *
  8747. * @param {object} parent -
  8748. *
  8749. * @param {HTMLElement} el -
  8750. *
  8751. * @param {StyleCache} styleCache -
  8752. *
  8753. * @returns {null|PositionData} Updated position data or null if validation fails.
  8754. */
  8755. #updatePosition({
  8756. // Directly supported parameters
  8757. left,
  8758. top,
  8759. maxWidth,
  8760. maxHeight,
  8761. minWidth,
  8762. minHeight,
  8763. width: width2,
  8764. height,
  8765. rotateX,
  8766. rotateY,
  8767. rotateZ,
  8768. scale: scale2,
  8769. transformOrigin,
  8770. translateX,
  8771. translateY,
  8772. translateZ,
  8773. zIndex,
  8774. // Aliased parameters
  8775. rotation: rotation2,
  8776. ...rest
  8777. } = {}, parent, el, styleCache) {
  8778. let currentPosition = s_DATA_UPDATE.copy(this.#data);
  8779. if (el.style.width === "" || width2 !== void 0) {
  8780. if (width2 === "auto" || currentPosition.width === "auto" && width2 !== null) {
  8781. currentPosition.width = "auto";
  8782. width2 = styleCache.offsetWidth;
  8783. } else if (width2 === "inherit" || currentPosition.width === "inherit" && width2 !== null) {
  8784. currentPosition.width = "inherit";
  8785. width2 = styleCache.offsetWidth;
  8786. } else {
  8787. const newWidth = Number.isFinite(width2) ? width2 : currentPosition.width;
  8788. currentPosition.width = width2 = Number.isFinite(newWidth) ? Math.round(newWidth) : styleCache.offsetWidth;
  8789. }
  8790. } else {
  8791. width2 = Number.isFinite(currentPosition.width) ? currentPosition.width : styleCache.offsetWidth;
  8792. }
  8793. if (el.style.height === "" || height !== void 0) {
  8794. if (height === "auto" || currentPosition.height === "auto" && height !== null) {
  8795. currentPosition.height = "auto";
  8796. height = styleCache.offsetHeight;
  8797. } else if (height === "inherit" || currentPosition.height === "inherit" && height !== null) {
  8798. currentPosition.height = "inherit";
  8799. height = styleCache.offsetHeight;
  8800. } else {
  8801. const newHeight = Number.isFinite(height) ? height : currentPosition.height;
  8802. currentPosition.height = height = Number.isFinite(newHeight) ? Math.round(newHeight) : styleCache.offsetHeight;
  8803. }
  8804. } else {
  8805. height = Number.isFinite(currentPosition.height) ? currentPosition.height : styleCache.offsetHeight;
  8806. }
  8807. if (Number.isFinite(left)) {
  8808. currentPosition.left = left;
  8809. } else if (!Number.isFinite(currentPosition.left)) {
  8810. currentPosition.left = typeof this.#options.initialHelper?.getLeft === "function" ? this.#options.initialHelper.getLeft(width2) : 0;
  8811. }
  8812. if (Number.isFinite(top)) {
  8813. currentPosition.top = top;
  8814. } else if (!Number.isFinite(currentPosition.top)) {
  8815. currentPosition.top = typeof this.#options.initialHelper?.getTop === "function" ? this.#options.initialHelper.getTop(height) : 0;
  8816. }
  8817. if (Number.isFinite(maxHeight) || maxHeight === null) {
  8818. currentPosition.maxHeight = Number.isFinite(maxHeight) ? Math.round(maxHeight) : null;
  8819. }
  8820. if (Number.isFinite(maxWidth) || maxWidth === null) {
  8821. currentPosition.maxWidth = Number.isFinite(maxWidth) ? Math.round(maxWidth) : null;
  8822. }
  8823. if (Number.isFinite(minHeight) || minHeight === null) {
  8824. currentPosition.minHeight = Number.isFinite(minHeight) ? Math.round(minHeight) : null;
  8825. }
  8826. if (Number.isFinite(minWidth) || minWidth === null) {
  8827. currentPosition.minWidth = Number.isFinite(minWidth) ? Math.round(minWidth) : null;
  8828. }
  8829. if (Number.isFinite(rotateX) || rotateX === null) {
  8830. currentPosition.rotateX = rotateX;
  8831. }
  8832. if (Number.isFinite(rotateY) || rotateY === null) {
  8833. currentPosition.rotateY = rotateY;
  8834. }
  8835. if (rotateZ !== currentPosition.rotateZ && (Number.isFinite(rotateZ) || rotateZ === null)) {
  8836. currentPosition.rotateZ = rotateZ;
  8837. } else if (rotation2 !== currentPosition.rotateZ && (Number.isFinite(rotation2) || rotation2 === null)) {
  8838. currentPosition.rotateZ = rotation2;
  8839. }
  8840. if (Number.isFinite(translateX) || translateX === null) {
  8841. currentPosition.translateX = translateX;
  8842. }
  8843. if (Number.isFinite(translateY) || translateY === null) {
  8844. currentPosition.translateY = translateY;
  8845. }
  8846. if (Number.isFinite(translateZ) || translateZ === null) {
  8847. currentPosition.translateZ = translateZ;
  8848. }
  8849. if (Number.isFinite(scale2) || scale2 === null) {
  8850. currentPosition.scale = typeof scale2 === "number" ? Math.max(0, Math.min(scale2, 1e3)) : null;
  8851. }
  8852. if (typeof transformOrigin === "string" || transformOrigin === null) {
  8853. currentPosition.transformOrigin = transformOrigins.includes(transformOrigin) ? transformOrigin : null;
  8854. }
  8855. if (Number.isFinite(zIndex) || zIndex === null) {
  8856. currentPosition.zIndex = typeof zIndex === "number" ? Math.round(zIndex) : zIndex;
  8857. }
  8858. const validatorData = this.#validatorData;
  8859. if (this.#validators.enabled && validatorData.length) {
  8860. s_VALIDATION_DATA.parent = parent;
  8861. s_VALIDATION_DATA.el = el;
  8862. s_VALIDATION_DATA.computed = styleCache.computed;
  8863. s_VALIDATION_DATA.transforms = this.#transforms;
  8864. s_VALIDATION_DATA.height = height;
  8865. s_VALIDATION_DATA.width = width2;
  8866. s_VALIDATION_DATA.marginLeft = styleCache.marginLeft;
  8867. s_VALIDATION_DATA.marginTop = styleCache.marginTop;
  8868. s_VALIDATION_DATA.maxHeight = styleCache.maxHeight ?? currentPosition.maxHeight;
  8869. s_VALIDATION_DATA.maxWidth = styleCache.maxWidth ?? currentPosition.maxWidth;
  8870. const isMinimized = parent?.reactive?.minimized ?? false;
  8871. s_VALIDATION_DATA.minHeight = isMinimized ? currentPosition.minHeight ?? 0 : styleCache.minHeight || (currentPosition.minHeight ?? 0);
  8872. s_VALIDATION_DATA.minWidth = isMinimized ? currentPosition.minWidth ?? 0 : styleCache.minWidth || (currentPosition.minWidth ?? 0);
  8873. for (let cntr = 0; cntr < validatorData.length; cntr++) {
  8874. s_VALIDATION_DATA.position = currentPosition;
  8875. s_VALIDATION_DATA.rest = rest;
  8876. currentPosition = validatorData[cntr].validator(s_VALIDATION_DATA);
  8877. if (currentPosition === null) {
  8878. return null;
  8879. }
  8880. }
  8881. }
  8882. return currentPosition;
  8883. }
  8884. }
  8885. const s_DATA_UPDATE = new PositionData();
  8886. const s_VALIDATION_DATA = {
  8887. position: void 0,
  8888. parent: void 0,
  8889. el: void 0,
  8890. computed: void 0,
  8891. transforms: void 0,
  8892. height: void 0,
  8893. width: void 0,
  8894. marginLeft: void 0,
  8895. marginTop: void 0,
  8896. maxHeight: void 0,
  8897. maxWidth: void 0,
  8898. minHeight: void 0,
  8899. minWidth: void 0,
  8900. rest: void 0
  8901. };
  8902. Object.seal(s_VALIDATION_DATA);
  8903. class ApplicationState {
  8904. /** @type {ApplicationShellExt} */
  8905. #application;
  8906. /** @type {Map<string, ApplicationStateData>} */
  8907. #dataSaved = /* @__PURE__ */ new Map();
  8908. /**
  8909. * @param {ApplicationShellExt} application - The application.
  8910. */
  8911. constructor(application) {
  8912. this.#application = application;
  8913. Object.seal(this);
  8914. }
  8915. /**
  8916. * Returns current application state along with any extra data passed into method.
  8917. *
  8918. * @param {object} [extra] - Extra data to add to application state.
  8919. *
  8920. * @returns {ApplicationStateData} Passed in object with current application state.
  8921. */
  8922. get(extra = {}) {
  8923. return Object.assign(extra, {
  8924. position: this.#application?.position?.get(),
  8925. beforeMinimized: this.#application?.position?.state.get({ name: "#beforeMinimized" }),
  8926. options: Object.assign({}, this.#application?.options),
  8927. ui: { minimized: this.#application?.reactive?.minimized }
  8928. });
  8929. }
  8930. /**
  8931. * Returns any stored save state by name.
  8932. *
  8933. * @param {string} name - Saved data set name.
  8934. *
  8935. * @returns {ApplicationStateData} The saved data set.
  8936. */
  8937. getSave({ name }) {
  8938. if (typeof name !== "string") {
  8939. throw new TypeError(`ApplicationState - getSave error: 'name' is not a string.`);
  8940. }
  8941. return this.#dataSaved.get(name);
  8942. }
  8943. /**
  8944. * Removes and returns any application state by name.
  8945. *
  8946. * @param {object} options - Options.
  8947. *
  8948. * @param {string} options.name - Name to remove and retrieve.
  8949. *
  8950. * @returns {ApplicationStateData} Saved application data.
  8951. */
  8952. remove({ name }) {
  8953. if (typeof name !== "string") {
  8954. throw new TypeError(`ApplicationState - remove: 'name' is not a string.`);
  8955. }
  8956. const data = this.#dataSaved.get(name);
  8957. this.#dataSaved.delete(name);
  8958. return data;
  8959. }
  8960. /**
  8961. * Restores a saved application state returning the data. Several optional parameters are available
  8962. * to control whether the restore action occurs silently (no store / inline styles updates), animates
  8963. * to the stored data, or simply sets the stored data. Restoring via {@link AnimationAPI.to} allows
  8964. * specification of the duration, easing, and interpolate functions along with configuring a Promise to be
  8965. * returned if awaiting the end of the animation.
  8966. *
  8967. * @param {object} params - Parameters
  8968. *
  8969. * @param {string} params.name - Saved data set name.
  8970. *
  8971. * @param {boolean} [params.remove=false] - Remove data set.
  8972. *
  8973. * @param {boolean} [params.async=false] - If animating return a Promise that resolves with any saved data.
  8974. *
  8975. * @param {boolean} [params.animateTo=false] - Animate to restore data.
  8976. *
  8977. * @param {number} [params.duration=0.1] - Duration in seconds.
  8978. *
  8979. * @param {Function} [params.ease=linear] - Easing function.
  8980. *
  8981. * @param {Function} [params.interpolate=lerp] - Interpolation function.
  8982. *
  8983. * @returns {ApplicationStateData|Promise<ApplicationStateData>} Saved application data.
  8984. */
  8985. restore({
  8986. name,
  8987. remove = false,
  8988. async = false,
  8989. animateTo = false,
  8990. duration = 0.1,
  8991. ease = identity,
  8992. interpolate: interpolate2 = lerp$5
  8993. }) {
  8994. if (typeof name !== "string") {
  8995. throw new TypeError(`ApplicationState - restore error: 'name' is not a string.`);
  8996. }
  8997. const dataSaved = this.#dataSaved.get(name);
  8998. if (dataSaved) {
  8999. if (remove) {
  9000. this.#dataSaved.delete(name);
  9001. }
  9002. if (async) {
  9003. return this.set(dataSaved, { async, animateTo, duration, ease, interpolate: interpolate2 }).then(() => dataSaved);
  9004. } else {
  9005. this.set(dataSaved, { async, animateTo, duration, ease, interpolate: interpolate2 });
  9006. }
  9007. }
  9008. return dataSaved;
  9009. }
  9010. /**
  9011. * Saves current application state with the opportunity to add extra data to the saved state.
  9012. *
  9013. * @param {object} options - Options.
  9014. *
  9015. * @param {string} options.name - name to index this saved data.
  9016. *
  9017. * @param {...*} [options.extra] - Extra data to add to saved data.
  9018. *
  9019. * @returns {ApplicationStateData} Current application data
  9020. */
  9021. save({ name, ...extra }) {
  9022. if (typeof name !== "string") {
  9023. throw new TypeError(`ApplicationState - save error: 'name' is not a string.`);
  9024. }
  9025. const data = this.get(extra);
  9026. this.#dataSaved.set(name, data);
  9027. return data;
  9028. }
  9029. /**
  9030. * Restores a saved application state returning the data. Several optional parameters are available
  9031. * to control whether the restore action occurs silently (no store / inline styles updates), animates
  9032. * to the stored data, or simply sets the stored data. Restoring via {@link AnimationAPI.to} allows
  9033. * specification of the duration, easing, and interpolate functions along with configuring a Promise to be
  9034. * returned if awaiting the end of the animation.
  9035. *
  9036. * Note: If serializing application state any minimized apps will use the before minimized state on initial render
  9037. * of the app as it is currently not possible to render apps with Foundry VTT core API in the minimized state.
  9038. *
  9039. * TODO: THIS METHOD NEEDS TO BE REFACTORED WHEN TRL IS MADE INTO A STANDALONE FRAMEWORK.
  9040. *
  9041. * @param {ApplicationStateData} data - Saved data set name.
  9042. *
  9043. * @param {object} [opts] - Optional parameters
  9044. *
  9045. * @param {boolean} [opts.async=false] - If animating return a Promise that resolves with any saved data.
  9046. *
  9047. * @param {boolean} [opts.animateTo=false] - Animate to restore data.
  9048. *
  9049. * @param {number} [opts.duration=0.1] - Duration in seconds.
  9050. *
  9051. * @param {Function} [opts.ease=linear] - Easing function.
  9052. *
  9053. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  9054. *
  9055. * @returns {ApplicationShellExt|Promise<ApplicationShellExt>} When synchronous the application or Promise when
  9056. * animating resolving with application.
  9057. */
  9058. set(data, { async = false, animateTo = false, duration = 0.1, ease = identity, interpolate: interpolate2 = lerp$5 } = {}) {
  9059. if (!isObject(data)) {
  9060. throw new TypeError(`ApplicationState - restore error: 'data' is not an object.`);
  9061. }
  9062. const application = this.#application;
  9063. if (!isObject(data?.position)) {
  9064. console.warn(`ApplicationState.set warning: 'data.position' is not an object.`);
  9065. return application;
  9066. }
  9067. const rendered = application.rendered;
  9068. if (animateTo && !rendered) {
  9069. console.warn(`ApplicationState.set warning: Application is not rendered and 'animateTo' is true.`);
  9070. return application;
  9071. }
  9072. if (animateTo) {
  9073. if (data.position.transformOrigin !== application.position.transformOrigin) {
  9074. application.position.transformOrigin = data.position.transformOrigin;
  9075. }
  9076. if (isObject(data?.ui)) {
  9077. const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
  9078. if (application?.reactive?.minimized && !minimized) {
  9079. application.maximize({ animate: false, duration: 0 });
  9080. }
  9081. }
  9082. const promise2 = application.position.animate.to(
  9083. data.position,
  9084. { duration, ease, interpolate: interpolate2 }
  9085. ).finished.then((cancelled) => {
  9086. if (cancelled) {
  9087. return application;
  9088. }
  9089. if (isObject(data?.options)) {
  9090. application?.reactive.mergeOptions(data.options);
  9091. }
  9092. if (isObject(data?.ui)) {
  9093. const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
  9094. if (!application?.reactive?.minimized && minimized) {
  9095. application.minimize({ animate: false, duration: 0 });
  9096. }
  9097. }
  9098. if (isObject(data?.beforeMinimized)) {
  9099. application.position.state.set({ name: "#beforeMinimized", ...data.beforeMinimized });
  9100. }
  9101. return application;
  9102. });
  9103. if (async) {
  9104. return promise2;
  9105. }
  9106. } else {
  9107. if (rendered) {
  9108. if (isObject(data?.options)) {
  9109. application?.reactive.mergeOptions(data.options);
  9110. }
  9111. if (isObject(data?.ui)) {
  9112. const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
  9113. if (application?.reactive?.minimized && !minimized) {
  9114. application.maximize({ animate: false, duration: 0 });
  9115. } else if (!application?.reactive?.minimized && minimized) {
  9116. application.minimize({ animate: false, duration });
  9117. }
  9118. }
  9119. if (isObject(data?.beforeMinimized)) {
  9120. application.position.state.set({ name: "#beforeMinimized", ...data.beforeMinimized });
  9121. }
  9122. application.position.set(data.position);
  9123. } else {
  9124. let positionData = data.position;
  9125. if (isObject(data.beforeMinimized)) {
  9126. positionData = data.beforeMinimized;
  9127. positionData.left = data.position.left;
  9128. positionData.top = data.position.top;
  9129. }
  9130. application.position.set(positionData);
  9131. }
  9132. }
  9133. return application;
  9134. }
  9135. }
  9136. class GetSvelteData {
  9137. /**
  9138. * @type {MountedAppShell[]|null[]}
  9139. */
  9140. #applicationShellHolder;
  9141. /**
  9142. * @type {SvelteData[]}
  9143. */
  9144. #svelteData;
  9145. /**
  9146. * Keep a direct reference to the SvelteData array in an associated {@link SvelteApplication}.
  9147. *
  9148. * @param {MountedAppShell[]|null[]} applicationShellHolder - A reference to the MountedAppShell array.
  9149. *
  9150. * @param {SvelteData[]} svelteData - A reference to the SvelteData array of mounted components.
  9151. */
  9152. constructor(applicationShellHolder, svelteData) {
  9153. this.#applicationShellHolder = applicationShellHolder;
  9154. this.#svelteData = svelteData;
  9155. }
  9156. /**
  9157. * Returns any mounted {@link MountedAppShell}.
  9158. *
  9159. * @returns {MountedAppShell|null} Any mounted application shell.
  9160. */
  9161. get applicationShell() {
  9162. return this.#applicationShellHolder[0];
  9163. }
  9164. /**
  9165. * Returns the indexed Svelte component.
  9166. *
  9167. * @param {number} index -
  9168. *
  9169. * @returns {object} The loaded Svelte component.
  9170. */
  9171. component(index) {
  9172. const data = this.#svelteData[index];
  9173. return isObject(data) ? data?.component : void 0;
  9174. }
  9175. /**
  9176. * Returns the Svelte component entries iterator.
  9177. *
  9178. * @returns {Generator<Array<number|SvelteComponent>>} Svelte component entries iterator.
  9179. * @yields
  9180. */
  9181. *componentEntries() {
  9182. for (let cntr = 0; cntr < this.#svelteData.length; cntr++) {
  9183. yield [cntr, this.#svelteData[cntr].component];
  9184. }
  9185. }
  9186. /**
  9187. * Returns the Svelte component values iterator.
  9188. *
  9189. * @returns {Generator<SvelteComponent>} Svelte component values iterator.
  9190. * @yields
  9191. */
  9192. *componentValues() {
  9193. for (let cntr = 0; cntr < this.#svelteData.length; cntr++) {
  9194. yield this.#svelteData[cntr].component;
  9195. }
  9196. }
  9197. /**
  9198. * Returns the indexed SvelteData entry.
  9199. *
  9200. * @param {number} index -
  9201. *
  9202. * @returns {SvelteData} The loaded Svelte config + component.
  9203. */
  9204. data(index) {
  9205. return this.#svelteData[index];
  9206. }
  9207. /**
  9208. * Returns the {@link SvelteData} instance for a given component.
  9209. *
  9210. * @param {object} component - Svelte component.
  9211. *
  9212. * @returns {SvelteData} - The loaded Svelte config + component.
  9213. */
  9214. dataByComponent(component) {
  9215. for (const data of this.#svelteData) {
  9216. if (data.component === component) {
  9217. return data;
  9218. }
  9219. }
  9220. return void 0;
  9221. }
  9222. /**
  9223. * Returns the SvelteData entries iterator.
  9224. *
  9225. * @returns {IterableIterator<[number, SvelteData]>} SvelteData entries iterator.
  9226. */
  9227. dataEntries() {
  9228. return this.#svelteData.entries();
  9229. }
  9230. /**
  9231. * Returns the SvelteData values iterator.
  9232. *
  9233. * @returns {IterableIterator<SvelteData>} SvelteData values iterator.
  9234. */
  9235. dataValues() {
  9236. return this.#svelteData.values();
  9237. }
  9238. /**
  9239. * Returns the length of the mounted Svelte component list.
  9240. *
  9241. * @returns {number} Length of mounted Svelte component list.
  9242. */
  9243. get length() {
  9244. return this.#svelteData.length;
  9245. }
  9246. }
  9247. function loadSvelteConfig({ app, template, config, elementRootUpdate } = {}) {
  9248. const svelteOptions = isObject(config.options) ? config.options : {};
  9249. let target;
  9250. if (config.target instanceof HTMLElement) {
  9251. target = config.target;
  9252. } else if (template instanceof HTMLElement && typeof config.target === "string") {
  9253. target = template.querySelector(config.target);
  9254. } else {
  9255. target = document.createDocumentFragment();
  9256. }
  9257. if (target === void 0) {
  9258. console.log(
  9259. `%c[TRL] loadSvelteConfig error - could not find target selector, '${config.target}', for config:
  9260. `,
  9261. "background: rgb(57,34,34)",
  9262. config
  9263. );
  9264. throw new Error();
  9265. }
  9266. const NewSvelteComponent = config.class;
  9267. const svelteConfig = parseSvelteConfig({ ...config, target }, app);
  9268. const externalContext = svelteConfig.context.get("#external");
  9269. externalContext.application = app;
  9270. externalContext.elementRootUpdate = elementRootUpdate;
  9271. externalContext.sessionStorage = app.reactive.sessionStorage;
  9272. let eventbus;
  9273. if (isObject(app._eventbus) && typeof app._eventbus.createProxy === "function") {
  9274. eventbus = app._eventbus.createProxy();
  9275. externalContext.eventbus = eventbus;
  9276. }
  9277. Object.seal(externalContext);
  9278. svelteConfig.context.set("external", new Proxy({}, {
  9279. get(targetUnused, prop) {
  9280. console.warn(`[TRL] Deprecation warning: Please change getContext('external') to getContext('#external').`);
  9281. return externalContext[prop];
  9282. }
  9283. }));
  9284. const component = new NewSvelteComponent(svelteConfig);
  9285. svelteConfig.eventbus = eventbus;
  9286. let element2;
  9287. if (isApplicationShell(component)) {
  9288. element2 = component.elementRoot;
  9289. }
  9290. if (target instanceof DocumentFragment && target.firstElementChild) {
  9291. if (element2 === void 0) {
  9292. element2 = target.firstElementChild;
  9293. }
  9294. template.append(target);
  9295. } else if (config.target instanceof HTMLElement && element2 === void 0) {
  9296. if (config.target instanceof HTMLElement && typeof svelteOptions.selectorElement !== "string") {
  9297. console.log(
  9298. `%c[TRL] loadSvelteConfig error - HTMLElement target with no 'selectorElement' defined.
  9299. Note: If configuring an application shell and directly targeting a HTMLElement did you bind an'elementRoot' and include '<svelte:options accessors={true}/>'?
  9300. Offending config:
  9301. `,
  9302. "background: rgb(57,34,34)",
  9303. config
  9304. );
  9305. throw new Error();
  9306. }
  9307. element2 = target.querySelector(svelteOptions.selectorElement);
  9308. if (element2 === null || element2 === void 0) {
  9309. console.log(
  9310. `%c[TRL] loadSvelteConfig error - HTMLElement target with 'selectorElement', '${svelteOptions.selectorElement}', not found for config:
  9311. `,
  9312. "background: rgb(57,34,34)",
  9313. config
  9314. );
  9315. throw new Error();
  9316. }
  9317. }
  9318. const injectHTML = !(config.target instanceof HTMLElement);
  9319. return { config: svelteConfig, component, element: element2, injectHTML };
  9320. }
  9321. class SvelteReactive {
  9322. /**
  9323. * @type {SvelteApplication}
  9324. */
  9325. #application;
  9326. /**
  9327. * @type {boolean}
  9328. */
  9329. #initialized = false;
  9330. /** @type {TJSSessionStorage} */
  9331. #sessionStorage;
  9332. /**
  9333. * The Application option store which is injected into mounted Svelte component context under the `external` key.
  9334. *
  9335. * @type {StoreAppOptions}
  9336. */
  9337. #storeAppOptions;
  9338. /**
  9339. * Stores the update function for `#storeAppOptions`.
  9340. *
  9341. * @type {import('svelte/store').Writable.update}
  9342. */
  9343. #storeAppOptionsUpdate;
  9344. /**
  9345. * Stores the UI state data to make it accessible via getters.
  9346. *
  9347. * @type {object}
  9348. */
  9349. #dataUIState;
  9350. /**
  9351. * The UI option store which is injected into mounted Svelte component context under the `external` key.
  9352. *
  9353. * @type {StoreUIOptions}
  9354. */
  9355. #storeUIState;
  9356. /**
  9357. * Stores the update function for `#storeUIState`.
  9358. *
  9359. * @type {import('svelte/store').Writable.update}
  9360. */
  9361. #storeUIStateUpdate;
  9362. /**
  9363. * Stores the unsubscribe functions from local store subscriptions.
  9364. *
  9365. * @type {import('svelte/store').Unsubscriber[]}
  9366. */
  9367. #storeUnsubscribe = [];
  9368. /**
  9369. * @param {SvelteApplication} application - The host Foundry application.
  9370. */
  9371. constructor(application) {
  9372. this.#application = application;
  9373. const optionsSessionStorage = application?.options?.sessionStorage;
  9374. if (optionsSessionStorage !== void 0 && !(optionsSessionStorage instanceof TJSSessionStorage)) {
  9375. throw new TypeError(`'options.sessionStorage' is not an instance of TJSSessionStorage.`);
  9376. }
  9377. this.#sessionStorage = optionsSessionStorage !== void 0 ? optionsSessionStorage : new TJSSessionStorage();
  9378. }
  9379. /**
  9380. * Initializes reactive support. Package private for internal use.
  9381. *
  9382. * @returns {SvelteStores|void} Internal methods to interact with Svelte stores.
  9383. * @package
  9384. */
  9385. initialize() {
  9386. if (this.#initialized) {
  9387. return;
  9388. }
  9389. this.#initialized = true;
  9390. this.#storesInitialize();
  9391. return {
  9392. appOptionsUpdate: this.#storeAppOptionsUpdate,
  9393. uiOptionsUpdate: this.#storeUIStateUpdate,
  9394. subscribe: this.#storesSubscribe.bind(this),
  9395. unsubscribe: this.#storesUnsubscribe.bind(this)
  9396. };
  9397. }
  9398. // Store getters -----------------------------------------------------------------------------------------------------
  9399. /**
  9400. * @returns {TJSSessionStorage} Returns TJSSessionStorage instance.
  9401. */
  9402. get sessionStorage() {
  9403. return this.#sessionStorage;
  9404. }
  9405. /**
  9406. * Returns the store for app options.
  9407. *
  9408. * @returns {StoreAppOptions} App options store.
  9409. */
  9410. get storeAppOptions() {
  9411. return this.#storeAppOptions;
  9412. }
  9413. /**
  9414. * Returns the store for UI options.
  9415. *
  9416. * @returns {StoreUIOptions} UI options store.
  9417. */
  9418. get storeUIState() {
  9419. return this.#storeUIState;
  9420. }
  9421. // Only reactive getters ---------------------------------------------------------------------------------------------
  9422. /**
  9423. * Returns the current dragging UI state.
  9424. *
  9425. * @returns {boolean} Dragging UI state.
  9426. */
  9427. get dragging() {
  9428. return this.#dataUIState.dragging;
  9429. }
  9430. /**
  9431. * Returns the current minimized UI state.
  9432. *
  9433. * @returns {boolean} Minimized UI state.
  9434. */
  9435. get minimized() {
  9436. return this.#dataUIState.minimized;
  9437. }
  9438. /**
  9439. * Returns the current resizing UI state.
  9440. *
  9441. * @returns {boolean} Resizing UI state.
  9442. */
  9443. get resizing() {
  9444. return this.#dataUIState.resizing;
  9445. }
  9446. // Reactive getter / setters -----------------------------------------------------------------------------------------
  9447. /**
  9448. * Returns the draggable app option.
  9449. *
  9450. * @returns {boolean} Draggable app option.
  9451. */
  9452. get draggable() {
  9453. return this.#application?.options?.draggable;
  9454. }
  9455. /**
  9456. * Returns the focusAuto app option.
  9457. *
  9458. * @returns {boolean} When true auto-management of app focus is enabled.
  9459. */
  9460. get focusAuto() {
  9461. return this.#application?.options?.focusAuto;
  9462. }
  9463. /**
  9464. * Returns the focusKeep app option.
  9465. *
  9466. * @returns {boolean} When `focusAuto` and `focusKeep` is true; keeps internal focus.
  9467. */
  9468. get focusKeep() {
  9469. return this.#application?.options?.focusKeep;
  9470. }
  9471. /**
  9472. * Returns the focusTrap app option.
  9473. *
  9474. * @returns {boolean} When true focus trapping / wrapping is enabled keeping focus inside app.
  9475. */
  9476. get focusTrap() {
  9477. return this.#application?.options?.focusTrap;
  9478. }
  9479. /**
  9480. * Returns the headerButtonNoClose app option.
  9481. *
  9482. * @returns {boolean} Remove the close the button in header app option.
  9483. */
  9484. get headerButtonNoClose() {
  9485. return this.#application?.options?.headerButtonNoClose;
  9486. }
  9487. /**
  9488. * Returns the headerButtonNoLabel app option.
  9489. *
  9490. * @returns {boolean} Remove the labels from buttons in header app option.
  9491. */
  9492. get headerButtonNoLabel() {
  9493. return this.#application?.options?.headerButtonNoLabel;
  9494. }
  9495. /**
  9496. * Returns the headerIcon app option.
  9497. *
  9498. * @returns {string|void} URL for header app icon.
  9499. */
  9500. get headerIcon() {
  9501. return this.#application?.options?.headerIcon;
  9502. }
  9503. /**
  9504. * Returns the headerNoTitleMinimized app option.
  9505. *
  9506. * @returns {boolean} When true removes the header title when minimized.
  9507. */
  9508. get headerNoTitleMinimized() {
  9509. return this.#application?.options?.headerNoTitleMinimized;
  9510. }
  9511. /**
  9512. * Returns the minimizable app option.
  9513. *
  9514. * @returns {boolean} Minimizable app option.
  9515. */
  9516. get minimizable() {
  9517. return this.#application?.options?.minimizable;
  9518. }
  9519. /**
  9520. * Returns the Foundry popOut state; {@link Application.popOut}
  9521. *
  9522. * @returns {boolean} Positionable app option.
  9523. */
  9524. get popOut() {
  9525. return this.#application.popOut;
  9526. }
  9527. /**
  9528. * Returns the positionable app option; {@link SvelteApplicationOptions.positionable}
  9529. *
  9530. * @returns {boolean} Positionable app option.
  9531. */
  9532. get positionable() {
  9533. return this.#application?.options?.positionable;
  9534. }
  9535. /**
  9536. * Returns the resizable option.
  9537. *
  9538. * @returns {boolean} Resizable app option.
  9539. */
  9540. get resizable() {
  9541. return this.#application?.options?.resizable;
  9542. }
  9543. /**
  9544. * Returns the title accessor from the parent Application class; {@link Application.title}
  9545. * TODO: Application v2; note that super.title localizes `this.options.title`; IMHO it shouldn't.
  9546. *
  9547. * @returns {string} Title.
  9548. */
  9549. get title() {
  9550. return this.#application.title;
  9551. }
  9552. /**
  9553. * Sets `this.options.draggable` which is reactive for application shells.
  9554. *
  9555. * @param {boolean} draggable - Sets the draggable option.
  9556. */
  9557. set draggable(draggable2) {
  9558. if (typeof draggable2 === "boolean") {
  9559. this.setOptions("draggable", draggable2);
  9560. }
  9561. }
  9562. /**
  9563. * Sets `this.options.focusAuto` which is reactive for application shells.
  9564. *
  9565. * @param {boolean} focusAuto - Sets the focusAuto option.
  9566. */
  9567. set focusAuto(focusAuto) {
  9568. if (typeof focusAuto === "boolean") {
  9569. this.setOptions("focusAuto", focusAuto);
  9570. }
  9571. }
  9572. /**
  9573. * Sets `this.options.focusKeep` which is reactive for application shells.
  9574. *
  9575. * @param {boolean} focusKeep - Sets the focusKeep option.
  9576. */
  9577. set focusKeep(focusKeep) {
  9578. if (typeof focusKeep === "boolean") {
  9579. this.setOptions("focusKeep", focusKeep);
  9580. }
  9581. }
  9582. /**
  9583. * Sets `this.options.focusTrap` which is reactive for application shells.
  9584. *
  9585. * @param {boolean} focusTrap - Sets the focusTrap option.
  9586. */
  9587. set focusTrap(focusTrap) {
  9588. if (typeof focusTrap === "boolean") {
  9589. this.setOptions("focusTrap", focusTrap);
  9590. }
  9591. }
  9592. /**
  9593. * Sets `this.options.headerButtonNoClose` which is reactive for application shells.
  9594. *
  9595. * @param {boolean} headerButtonNoClose - Sets the headerButtonNoClose option.
  9596. */
  9597. set headerButtonNoClose(headerButtonNoClose) {
  9598. if (typeof headerButtonNoClose === "boolean") {
  9599. this.setOptions("headerButtonNoClose", headerButtonNoClose);
  9600. }
  9601. }
  9602. /**
  9603. * Sets `this.options.headerButtonNoLabel` which is reactive for application shells.
  9604. *
  9605. * @param {boolean} headerButtonNoLabel - Sets the headerButtonNoLabel option.
  9606. */
  9607. set headerButtonNoLabel(headerButtonNoLabel) {
  9608. if (typeof headerButtonNoLabel === "boolean") {
  9609. this.setOptions("headerButtonNoLabel", headerButtonNoLabel);
  9610. }
  9611. }
  9612. /**
  9613. * Sets `this.options.headerIcon` which is reactive for application shells.
  9614. *
  9615. * @param {string|void} headerIcon - Sets the headerButtonNoLabel option.
  9616. */
  9617. set headerIcon(headerIcon) {
  9618. if (headerIcon === void 0 || typeof headerIcon === "string") {
  9619. this.setOptions("headerIcon", headerIcon);
  9620. }
  9621. }
  9622. /**
  9623. * Sets `this.options.headerNoTitleMinimized` which is reactive for application shells.
  9624. *
  9625. * @param {boolean} headerNoTitleMinimized - Sets the headerNoTitleMinimized option.
  9626. */
  9627. set headerNoTitleMinimized(headerNoTitleMinimized) {
  9628. if (typeof headerNoTitleMinimized === "boolean") {
  9629. this.setOptions("headerNoTitleMinimized", headerNoTitleMinimized);
  9630. }
  9631. }
  9632. /**
  9633. * Sets `this.options.minimizable` which is reactive for application shells that are also pop out.
  9634. *
  9635. * @param {boolean} minimizable - Sets the minimizable option.
  9636. */
  9637. set minimizable(minimizable) {
  9638. if (typeof minimizable === "boolean") {
  9639. this.setOptions("minimizable", minimizable);
  9640. }
  9641. }
  9642. /**
  9643. * Sets `this.options.popOut` which is reactive for application shells. This will add / remove this application
  9644. * from `ui.windows`.
  9645. *
  9646. * @param {boolean} popOut - Sets the popOut option.
  9647. */
  9648. set popOut(popOut) {
  9649. if (typeof popOut === "boolean") {
  9650. this.setOptions("popOut", popOut);
  9651. }
  9652. }
  9653. /**
  9654. * Sets `this.options.positionable` enabling / disabling {@link SvelteApplication.position.set}.
  9655. *
  9656. * @param {boolean} positionable - Sets the positionable option.
  9657. */
  9658. set positionable(positionable) {
  9659. if (typeof positionable === "boolean") {
  9660. this.setOptions("positionable", positionable);
  9661. }
  9662. }
  9663. /**
  9664. * Sets `this.options.resizable` which is reactive for application shells.
  9665. *
  9666. * @param {boolean} resizable - Sets the resizable option.
  9667. */
  9668. set resizable(resizable) {
  9669. if (typeof resizable === "boolean") {
  9670. this.setOptions("resizable", resizable);
  9671. }
  9672. }
  9673. /**
  9674. * Sets `this.options.title` which is reactive for application shells.
  9675. *
  9676. * Note: Will set empty string if title is undefined or null.
  9677. *
  9678. * @param {string|undefined|null} title - Application title; will be localized, so a translation key is fine.
  9679. */
  9680. set title(title) {
  9681. if (typeof title === "string") {
  9682. this.setOptions("title", title);
  9683. } else if (title === void 0 || title === null) {
  9684. this.setOptions("title", "");
  9685. }
  9686. }
  9687. // Reactive Options API -------------------------------------------------------------------------------------------
  9688. /**
  9689. * Provides a way to safely get this applications options given an accessor string which describes the
  9690. * entries to walk. To access deeper entries into the object format the accessor string with `.` between entries
  9691. * to walk.
  9692. *
  9693. * // TODO DOCUMENT the accessor in more detail.
  9694. *
  9695. * @param {string} accessor - The path / key to set. You can set multiple levels.
  9696. *
  9697. * @param {*} [defaultValue] - A default value returned if the accessor is not found.
  9698. *
  9699. * @returns {*} Value at the accessor.
  9700. */
  9701. getOptions(accessor, defaultValue) {
  9702. return safeAccess(this.#application.options, accessor, defaultValue);
  9703. }
  9704. /**
  9705. * Provides a way to merge `options` into this applications options and update the appOptions store.
  9706. *
  9707. * @param {object} options - The options object to merge with `this.options`.
  9708. */
  9709. mergeOptions(options) {
  9710. this.#storeAppOptionsUpdate((instanceOptions) => deepMerge(instanceOptions, options));
  9711. }
  9712. /**
  9713. * Provides a way to safely set this applications options given an accessor string which describes the
  9714. * entries to walk. To access deeper entries into the object format the accessor string with `.` between entries
  9715. * to walk.
  9716. *
  9717. * Additionally if an application shell Svelte component is mounted and exports the `appOptions` property then
  9718. * the application options is set to `appOptions` potentially updating the application shell / Svelte component.
  9719. *
  9720. * // TODO DOCUMENT the accessor in more detail.
  9721. *
  9722. * @param {string} accessor - The path / key to set. You can set multiple levels.
  9723. *
  9724. * @param {*} value - Value to set.
  9725. */
  9726. setOptions(accessor, value) {
  9727. const success = safeSet(this.#application.options, accessor, value);
  9728. if (success) {
  9729. this.#storeAppOptionsUpdate(() => this.#application.options);
  9730. }
  9731. }
  9732. /**
  9733. * Initializes the Svelte stores and derived stores for the application options and UI state.
  9734. *
  9735. * While writable stores are created the update method is stored in private variables locally and derived Readable
  9736. * stores are provided for essential options which are commonly used.
  9737. *
  9738. * These stores are injected into all Svelte components mounted under the `external` context: `storeAppOptions` and
  9739. * ` storeUIState`.
  9740. */
  9741. #storesInitialize() {
  9742. const writableAppOptions = writable$1(this.#application.options);
  9743. this.#storeAppOptionsUpdate = writableAppOptions.update;
  9744. const storeAppOptions = {
  9745. subscribe: writableAppOptions.subscribe,
  9746. draggable: propertyStore(writableAppOptions, "draggable"),
  9747. focusAuto: propertyStore(writableAppOptions, "focusAuto"),
  9748. focusKeep: propertyStore(writableAppOptions, "focusKeep"),
  9749. focusTrap: propertyStore(writableAppOptions, "focusTrap"),
  9750. headerButtonNoClose: propertyStore(writableAppOptions, "headerButtonNoClose"),
  9751. headerButtonNoLabel: propertyStore(writableAppOptions, "headerButtonNoLabel"),
  9752. headerIcon: propertyStore(writableAppOptions, "headerIcon"),
  9753. headerNoTitleMinimized: propertyStore(writableAppOptions, "headerNoTitleMinimized"),
  9754. minimizable: propertyStore(writableAppOptions, "minimizable"),
  9755. popOut: propertyStore(writableAppOptions, "popOut"),
  9756. positionable: propertyStore(writableAppOptions, "positionable"),
  9757. resizable: propertyStore(writableAppOptions, "resizable"),
  9758. title: propertyStore(writableAppOptions, "title")
  9759. };
  9760. Object.freeze(storeAppOptions);
  9761. this.#storeAppOptions = storeAppOptions;
  9762. this.#dataUIState = {
  9763. dragging: false,
  9764. headerButtons: [],
  9765. minimized: this.#application._minimized,
  9766. resizing: false
  9767. };
  9768. const writableUIOptions = writable$1(this.#dataUIState);
  9769. this.#storeUIStateUpdate = writableUIOptions.update;
  9770. const storeUIState = {
  9771. subscribe: writableUIOptions.subscribe,
  9772. dragging: propertyStore(writableUIOptions, "dragging"),
  9773. headerButtons: derived(writableUIOptions, ($options, set) => set($options.headerButtons)),
  9774. minimized: derived(writableUIOptions, ($options, set) => set($options.minimized)),
  9775. resizing: propertyStore(writableUIOptions, "resizing")
  9776. };
  9777. Object.freeze(storeUIState);
  9778. this.#storeUIState = storeUIState;
  9779. }
  9780. /**
  9781. * Registers local store subscriptions for app options. `popOut` controls registering this app with `ui.windows`.
  9782. *
  9783. * @see SvelteApplication._injectHTML
  9784. */
  9785. #storesSubscribe() {
  9786. this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.headerButtonNoClose, (value) => {
  9787. this.updateHeaderButtons({ headerButtonNoClose: value });
  9788. }));
  9789. this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.headerButtonNoLabel, (value) => {
  9790. this.updateHeaderButtons({ headerButtonNoLabel: value });
  9791. }));
  9792. this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.popOut, (value) => {
  9793. if (value && this.#application.rendered) {
  9794. globalThis.ui.windows[this.#application.appId] = this.#application;
  9795. } else {
  9796. delete globalThis.ui.windows[this.#application.appId];
  9797. }
  9798. }));
  9799. }
  9800. /**
  9801. * Unsubscribes from any locally monitored stores.
  9802. *
  9803. * @see SvelteApplication.close
  9804. */
  9805. #storesUnsubscribe() {
  9806. this.#storeUnsubscribe.forEach((unsubscribe) => unsubscribe());
  9807. this.#storeUnsubscribe = [];
  9808. }
  9809. /**
  9810. * Updates the UI Options store with the current header buttons. You may dynamically add / remove header buttons
  9811. * if using an application shell Svelte component. In either overriding `_getHeaderButtons` or responding to the
  9812. * Hooks fired return a new button array and the uiOptions store is updated and the application shell will render
  9813. * the new buttons.
  9814. *
  9815. * Optionally you can set in the SvelteApplication app options {@link SvelteApplicationOptions.headerButtonNoClose}
  9816. * to remove the close button and {@link SvelteApplicationOptions.headerButtonNoLabel} to true and labels will be
  9817. * removed from the header buttons.
  9818. *
  9819. * @param {object} opts - Optional parameters (for internal use)
  9820. *
  9821. * @param {boolean} opts.headerButtonNoClose - The value for `headerButtonNoClose`.
  9822. *
  9823. * @param {boolean} opts.headerButtonNoLabel - The value for `headerButtonNoLabel`.
  9824. */
  9825. updateHeaderButtons({
  9826. headerButtonNoClose = this.#application.options.headerButtonNoClose,
  9827. headerButtonNoLabel = this.#application.options.headerButtonNoLabel
  9828. } = {}) {
  9829. let buttons = this.#application._getHeaderButtons();
  9830. if (typeof headerButtonNoClose === "boolean" && headerButtonNoClose) {
  9831. buttons = buttons.filter((button) => button.class !== "close");
  9832. }
  9833. if (typeof headerButtonNoLabel === "boolean" && headerButtonNoLabel) {
  9834. for (const button of buttons) {
  9835. button.label = void 0;
  9836. }
  9837. }
  9838. this.#storeUIStateUpdate((options) => {
  9839. options.headerButtons = buttons;
  9840. return options;
  9841. });
  9842. }
  9843. }
  9844. class SvelteApplication extends Application {
  9845. /**
  9846. * Stores the first mounted component which follows the application shell contract.
  9847. *
  9848. * @type {MountedAppShell[]|null[]} Application shell.
  9849. */
  9850. #applicationShellHolder = [null];
  9851. /**
  9852. * Stores and manages application state for saving / restoring / serializing.
  9853. *
  9854. * @type {ApplicationState}
  9855. */
  9856. #applicationState;
  9857. /**
  9858. * Stores the target element which may not necessarily be the main element.
  9859. *
  9860. * @type {HTMLElement}
  9861. */
  9862. #elementTarget = null;
  9863. /**
  9864. * Stores the content element which is set for application shells.
  9865. *
  9866. * @type {HTMLElement}
  9867. */
  9868. #elementContent = null;
  9869. /**
  9870. * Stores initial z-index from `_renderOuter` to set to target element / Svelte component.
  9871. *
  9872. * @type {number}
  9873. */
  9874. #initialZIndex = 95;
  9875. /**
  9876. * Stores on mount state which is checked in _render to trigger onSvelteMount callback.
  9877. *
  9878. * @type {boolean}
  9879. */
  9880. #onMount = false;
  9881. /**
  9882. * The position store.
  9883. *
  9884. * @type {Position}
  9885. */
  9886. #position;
  9887. /**
  9888. * Contains the Svelte stores and reactive accessors.
  9889. *
  9890. * @type {SvelteReactive}
  9891. */
  9892. #reactive;
  9893. /**
  9894. * Stores SvelteData entries with instantiated Svelte components.
  9895. *
  9896. * @type {SvelteData[]}
  9897. */
  9898. #svelteData = [];
  9899. /**
  9900. * Provides a helper class that combines multiple methods for interacting with the mounted components tracked in
  9901. * {@link SvelteData}.
  9902. *
  9903. * @type {GetSvelteData}
  9904. */
  9905. #getSvelteData = new GetSvelteData(this.#applicationShellHolder, this.#svelteData);
  9906. /**
  9907. * Contains methods to interact with the Svelte stores.
  9908. *
  9909. * @type {SvelteStores}
  9910. */
  9911. #stores;
  9912. /**
  9913. * @param {SvelteApplicationOptions} options - The options for the application.
  9914. *
  9915. * @inheritDoc
  9916. */
  9917. constructor(options = {}) {
  9918. super(options);
  9919. this.#applicationState = new ApplicationState(this);
  9920. this.#position = new Position(this, {
  9921. ...this.position,
  9922. ...this.options,
  9923. initial: this.options.positionInitial,
  9924. ortho: this.options.positionOrtho,
  9925. validator: this.options.positionValidator
  9926. });
  9927. delete this.position;
  9928. Object.defineProperty(this, "position", {
  9929. get: () => this.#position,
  9930. set: (position) => {
  9931. if (isObject(position)) {
  9932. this.#position.set(position);
  9933. }
  9934. }
  9935. });
  9936. this.#reactive = new SvelteReactive(this);
  9937. this.#stores = this.#reactive.initialize();
  9938. }
  9939. /**
  9940. * Specifies the default options that SvelteApplication supports.
  9941. *
  9942. * @returns {SvelteApplicationOptions} options - Application options.
  9943. * @see https://foundryvtt.com/api/interfaces/client.ApplicationOptions.html
  9944. */
  9945. static get defaultOptions() {
  9946. return deepMerge(super.defaultOptions, {
  9947. defaultCloseAnimation: true,
  9948. // If false the default slide close animation is not run.
  9949. draggable: true,
  9950. // If true then application shells are draggable.
  9951. focusAuto: true,
  9952. // When true auto-management of app focus is enabled.
  9953. focusKeep: false,
  9954. // When `focusAuto` and `focusKeep` is true; keeps internal focus.
  9955. focusSource: void 0,
  9956. // Stores any A11yFocusSource data that is applied when app is closed.
  9957. focusTrap: true,
  9958. // When true focus trapping / wrapping is enabled keeping focus inside app.
  9959. headerButtonNoClose: false,
  9960. // If true then the close header button is removed.
  9961. headerButtonNoLabel: false,
  9962. // If true then header button labels are removed for application shells.
  9963. headerIcon: void 0,
  9964. // Sets a header icon given an image URL.
  9965. headerNoTitleMinimized: false,
  9966. // If true then header title is hidden when application is minimized.
  9967. minHeight: MIN_WINDOW_HEIGHT,
  9968. // Assigned to position. Number specifying minimum window height.
  9969. minWidth: MIN_WINDOW_WIDTH,
  9970. // Assigned to position. Number specifying minimum window width.
  9971. positionable: true,
  9972. // If false then `position.set` does not take effect.
  9973. positionInitial: Position.Initial.browserCentered,
  9974. // A helper for initial position placement.
  9975. positionOrtho: true,
  9976. // When true Position is optimized for orthographic use.
  9977. positionValidator: Position.Validators.transformWindow,
  9978. // A function providing the default validator.
  9979. sessionStorage: void 0,
  9980. // An instance of SessionStorage to share across SvelteApplications.
  9981. svelte: void 0,
  9982. // A Svelte configuration object.
  9983. transformOrigin: "top left"
  9984. // By default, 'top / left' respects rotation when minimizing.
  9985. });
  9986. }
  9987. /**
  9988. * Returns the content element if an application shell is mounted.
  9989. *
  9990. * @returns {HTMLElement} Content element.
  9991. */
  9992. get elementContent() {
  9993. return this.#elementContent;
  9994. }
  9995. /**
  9996. * Returns the target element or main element if no target defined.
  9997. *
  9998. * @returns {HTMLElement} Target element.
  9999. */
  10000. get elementTarget() {
  10001. return this.#elementTarget;
  10002. }
  10003. /**
  10004. * Returns the reactive accessors & Svelte stores for SvelteApplication.
  10005. *
  10006. * @returns {SvelteReactive} The reactive accessors & Svelte stores.
  10007. */
  10008. get reactive() {
  10009. return this.#reactive;
  10010. }
  10011. /**
  10012. * Returns the application state manager.
  10013. *
  10014. * @returns {ApplicationState} The application state manager.
  10015. */
  10016. get state() {
  10017. return this.#applicationState;
  10018. }
  10019. /**
  10020. * Returns the Svelte helper class w/ various methods to access mounted Svelte components.
  10021. *
  10022. * @returns {GetSvelteData} GetSvelteData
  10023. */
  10024. get svelte() {
  10025. return this.#getSvelteData;
  10026. }
  10027. /**
  10028. * In this case of when a template is defined in app options `html` references the inner HTML / template. However,
  10029. * to activate classic v1 tabs for a Svelte component the element target is passed as an array simulating JQuery as
  10030. * the element is retrieved immediately and the core listeners use standard DOM queries.
  10031. *
  10032. * @inheritDoc
  10033. * @protected
  10034. * @ignore
  10035. */
  10036. _activateCoreListeners(html) {
  10037. super._activateCoreListeners(typeof this.options.template === "string" ? html : [this.#elementTarget]);
  10038. }
  10039. /**
  10040. * Provide an override to set this application as the active window regardless of z-index. Changes behaviour from
  10041. * Foundry core. This is important / used for instance in dialog key handling for left / right button selection.
  10042. *
  10043. * @param {object} [opts] - Optional parameters.
  10044. *
  10045. * @param {boolean} [opts.force=false] - Force bring to top; will increment z-index by popOut order.
  10046. *
  10047. */
  10048. bringToTop({ force = false } = {}) {
  10049. if (force || this.popOut) {
  10050. super.bringToTop();
  10051. }
  10052. if (document.activeElement !== document.body && !this.elementTarget.contains(document.activeElement)) {
  10053. if (document.activeElement instanceof HTMLElement) {
  10054. document.activeElement.blur();
  10055. }
  10056. document.body.focus();
  10057. }
  10058. globalThis.ui.activeWindow = this;
  10059. }
  10060. /**
  10061. * Note: This method is fully overridden and duplicated as Svelte components need to be destroyed manually and the
  10062. * best visual result is to destroy them after the default slide up animation occurs, but before the element
  10063. * is removed from the DOM.
  10064. *
  10065. * If you destroy the Svelte components before the slide up animation the Svelte elements are removed immediately
  10066. * from the DOM. The purpose of overriding ensures the slide up animation is always completed before
  10067. * the Svelte components are destroyed and then the element is removed from the DOM.
  10068. *
  10069. * Close the application and un-register references to it within UI mappings.
  10070. * This function returns a Promise which resolves once the window closing animation concludes
  10071. *
  10072. * @param {object} [options] - Optional parameters.
  10073. *
  10074. * @param {boolean} [options.force] - Force close regardless of render state.
  10075. *
  10076. * @returns {Promise<void>} A Promise which resolves once the application is closed.
  10077. * @ignore
  10078. */
  10079. async close(options = {}) {
  10080. const states = Application.RENDER_STATES;
  10081. if (!options.force && ![states.RENDERED, states.ERROR].includes(this._state)) {
  10082. return;
  10083. }
  10084. this.#stores.unsubscribe();
  10085. this._state = states.CLOSING;
  10086. const el = this.#elementTarget;
  10087. if (!el) {
  10088. return this._state = states.CLOSED;
  10089. }
  10090. const content = el.querySelector(".window-content");
  10091. if (content) {
  10092. content.style.overflow = "hidden";
  10093. for (let cntr = content.children.length; --cntr >= 0; ) {
  10094. content.children[cntr].style.overflow = "hidden";
  10095. }
  10096. }
  10097. for (const cls of this.constructor._getInheritanceChain()) {
  10098. Hooks.call(`close${cls.name}`, this, el);
  10099. }
  10100. const animate = typeof this.options.defaultCloseAnimation === "boolean" ? this.options.defaultCloseAnimation : true;
  10101. if (animate) {
  10102. el.style.minHeight = "0";
  10103. const { paddingBottom, paddingTop } = globalThis.getComputedStyle(el);
  10104. await el.animate([
  10105. { maxHeight: `${el.clientHeight}px`, paddingTop, paddingBottom },
  10106. { maxHeight: 0, paddingTop: 0, paddingBottom: 0 }
  10107. ], { duration: 250, easing: "ease-in", fill: "forwards" }).finished;
  10108. }
  10109. const svelteDestroyPromises = [];
  10110. for (const entry of this.#svelteData) {
  10111. svelteDestroyPromises.push(outroAndDestroy(entry.component));
  10112. const eventbus = entry.config.eventbus;
  10113. if (isObject(eventbus) && typeof eventbus.off === "function") {
  10114. eventbus.off();
  10115. entry.config.eventbus = void 0;
  10116. }
  10117. }
  10118. await Promise.all(svelteDestroyPromises);
  10119. this.#svelteData.length = 0;
  10120. el.remove();
  10121. this.position.state.restore({
  10122. name: "#beforeMinimized",
  10123. properties: ["width", "height"],
  10124. silent: true,
  10125. remove: true
  10126. });
  10127. this.#applicationShellHolder[0] = null;
  10128. this._element = null;
  10129. this.#elementContent = null;
  10130. this.#elementTarget = null;
  10131. delete globalThis.ui.windows[this.appId];
  10132. this._minimized = false;
  10133. this._scrollPositions = null;
  10134. this._state = states.CLOSED;
  10135. this.#onMount = false;
  10136. this.#stores.uiOptionsUpdate((storeOptions) => deepMerge(storeOptions, { minimized: this._minimized }));
  10137. A11yHelper.applyFocusSource(this.options.focusSource);
  10138. delete this.options.focusSource;
  10139. }
  10140. /**
  10141. * Inject the Svelte components defined in `this.options.svelte`. The Svelte component can attach to the existing
  10142. * pop-out of Application or provide no template and render into a document fragment which is then attached to the
  10143. * DOM.
  10144. *
  10145. * @param {JQuery} html -
  10146. *
  10147. * @inheritDoc
  10148. * @ignore
  10149. */
  10150. _injectHTML(html) {
  10151. if (this.popOut && html.length === 0 && Array.isArray(this.options.svelte)) {
  10152. throw new Error(
  10153. "SvelteApplication - _injectHTML - A popout app with no template can only support one Svelte component."
  10154. );
  10155. }
  10156. this.reactive.updateHeaderButtons();
  10157. const elementRootUpdate = () => {
  10158. let cntr = 0;
  10159. return (elementRoot) => {
  10160. if (elementRoot !== null && elementRoot !== void 0 && cntr++ > 0) {
  10161. this.#updateApplicationShell();
  10162. return true;
  10163. }
  10164. return false;
  10165. };
  10166. };
  10167. if (Array.isArray(this.options.svelte)) {
  10168. for (const svelteConfig of this.options.svelte) {
  10169. const svelteData = loadSvelteConfig({
  10170. app: this,
  10171. template: html[0],
  10172. config: svelteConfig,
  10173. elementRootUpdate
  10174. });
  10175. if (isApplicationShell(svelteData.component)) {
  10176. if (this.svelte.applicationShell !== null) {
  10177. throw new Error(
  10178. `SvelteApplication - _injectHTML - An application shell is already mounted; offending config:
  10179. ${JSON.stringify(svelteConfig)}`
  10180. );
  10181. }
  10182. this.#applicationShellHolder[0] = svelteData.component;
  10183. if (isHMRProxy(svelteData.component) && Array.isArray(svelteData.component?.$$?.on_hmr)) {
  10184. svelteData.component.$$.on_hmr.push(() => () => this.#updateApplicationShell());
  10185. }
  10186. }
  10187. this.#svelteData.push(svelteData);
  10188. }
  10189. } else if (isObject(this.options.svelte)) {
  10190. const svelteData = loadSvelteConfig({
  10191. app: this,
  10192. template: html[0],
  10193. config: this.options.svelte,
  10194. elementRootUpdate
  10195. });
  10196. if (isApplicationShell(svelteData.component)) {
  10197. if (this.svelte.applicationShell !== null) {
  10198. throw new Error(
  10199. `SvelteApplication - _injectHTML - An application shell is already mounted; offending config:
  10200. ${JSON.stringify(this.options.svelte)}`
  10201. );
  10202. }
  10203. this.#applicationShellHolder[0] = svelteData.component;
  10204. if (isHMRProxy(svelteData.component) && Array.isArray(svelteData.component?.$$?.on_hmr)) {
  10205. svelteData.component.$$.on_hmr.push(() => () => this.#updateApplicationShell());
  10206. }
  10207. }
  10208. this.#svelteData.push(svelteData);
  10209. }
  10210. const isDocumentFragment = html.length && html[0] instanceof DocumentFragment;
  10211. let injectHTML = true;
  10212. for (const svelteData of this.#svelteData) {
  10213. if (!svelteData.injectHTML) {
  10214. injectHTML = false;
  10215. break;
  10216. }
  10217. }
  10218. if (injectHTML) {
  10219. super._injectHTML(html);
  10220. }
  10221. if (this.svelte.applicationShell !== null) {
  10222. this._element = $(this.svelte.applicationShell.elementRoot);
  10223. this.#elementContent = hasGetter(this.svelte.applicationShell, "elementContent") ? this.svelte.applicationShell.elementContent : null;
  10224. this.#elementTarget = hasGetter(this.svelte.applicationShell, "elementTarget") ? this.svelte.applicationShell.elementTarget : null;
  10225. } else if (isDocumentFragment) {
  10226. for (const svelteData of this.#svelteData) {
  10227. if (svelteData.element instanceof HTMLElement) {
  10228. this._element = $(svelteData.element);
  10229. break;
  10230. }
  10231. }
  10232. }
  10233. if (this.#elementTarget === null) {
  10234. this.#elementTarget = typeof this.options.selectorTarget === "string" ? this._element[0].querySelector(this.options.selectorTarget) : this._element[0];
  10235. }
  10236. if (this.#elementTarget === null || this.#elementTarget === void 0) {
  10237. throw new Error(`SvelteApplication - _injectHTML: Target element '${this.options.selectorTarget}' not found.`);
  10238. }
  10239. if (typeof this.options.positionable === "boolean" && this.options.positionable) {
  10240. this.#elementTarget.style.zIndex = typeof this.options.zIndex === "number" ? this.options.zIndex : this.#initialZIndex ?? 95;
  10241. }
  10242. this.#stores.subscribe();
  10243. }
  10244. /**
  10245. * Provides a mechanism to update the UI options store for maximized.
  10246. *
  10247. * Note: the sanity check is duplicated from {@link Application.maximize} the store is updated _before_
  10248. * performing the rest of animations. This allows application shells to remove / show any resize handlers
  10249. * correctly. Extra constraint data is stored in a saved position state in {@link SvelteApplication.minimize}
  10250. * to animate the content area.
  10251. *
  10252. * @param {object} [opts] - Optional parameters.
  10253. *
  10254. * @param {boolean} [opts.animate=true] - When true perform default maximizing animation.
  10255. *
  10256. * @param {number} [opts.duration=0.1] - Controls content area animation duration in seconds.
  10257. */
  10258. async maximize({ animate = true, duration = 0.1 } = {}) {
  10259. if (!this.popOut || [false, null].includes(this._minimized)) {
  10260. return;
  10261. }
  10262. this._minimized = null;
  10263. const durationMS = duration * 1e3;
  10264. const element2 = this.elementTarget;
  10265. const header = element2.querySelector(".window-header");
  10266. const content = element2.querySelector(".window-content");
  10267. const positionBefore = this.position.state.get({ name: "#beforeMinimized" });
  10268. if (animate) {
  10269. await this.position.state.restore({
  10270. name: "#beforeMinimized",
  10271. async: true,
  10272. animateTo: true,
  10273. properties: ["width"],
  10274. duration: 0.1
  10275. });
  10276. }
  10277. element2.classList.remove("minimized");
  10278. for (let cntr = header.children.length; --cntr >= 0; ) {
  10279. header.children[cntr].style.display = null;
  10280. }
  10281. content.style.display = null;
  10282. let constraints;
  10283. if (animate) {
  10284. ({ constraints } = this.position.state.restore({
  10285. name: "#beforeMinimized",
  10286. animateTo: true,
  10287. properties: ["height"],
  10288. remove: true,
  10289. duration
  10290. }));
  10291. } else {
  10292. ({ constraints } = this.position.state.remove({ name: "#beforeMinimized" }));
  10293. }
  10294. await content.animate([
  10295. { maxHeight: 0, paddingTop: 0, paddingBottom: 0, offset: 0 },
  10296. { ...constraints, offset: 1 },
  10297. { maxHeight: "100%", offset: 1 }
  10298. ], { duration: durationMS, fill: "forwards" }).finished;
  10299. this.position.set({
  10300. minHeight: positionBefore.minHeight ?? this.options?.minHeight ?? MIN_WINDOW_HEIGHT,
  10301. minWidth: positionBefore.minWidth ?? this.options?.minWidth ?? MIN_WINDOW_WIDTH
  10302. });
  10303. element2.style.minWidth = null;
  10304. element2.style.minHeight = null;
  10305. this._minimized = false;
  10306. setTimeout(() => {
  10307. content.style.overflow = null;
  10308. for (let cntr = content.children.length; --cntr >= 0; ) {
  10309. content.children[cntr].style.overflow = null;
  10310. }
  10311. }, 50);
  10312. this.#stores.uiOptionsUpdate((options) => deepMerge(options, { minimized: false }));
  10313. }
  10314. /**
  10315. * Provides a mechanism to update the UI options store for minimized.
  10316. *
  10317. * Note: the sanity check is duplicated from {@link Application.minimize} the store is updated _before_
  10318. * performing the rest of animations. This allows application shells to remove / show any resize handlers
  10319. * correctly. Extra constraint data is stored in a saved position state in {@link SvelteApplication.minimize}
  10320. * to animate the content area.
  10321. *
  10322. * @param {object} [opts] - Optional parameters
  10323. *
  10324. * @param {boolean} [opts.animate=true] - When true perform default minimizing animation.
  10325. *
  10326. * @param {number} [opts.duration=0.1] - Controls content area animation duration in seconds.
  10327. */
  10328. async minimize({ animate = true, duration = 0.1 } = {}) {
  10329. if (!this.rendered || !this.popOut || [true, null].includes(this._minimized)) {
  10330. return;
  10331. }
  10332. this.#stores.uiOptionsUpdate((options) => deepMerge(options, { minimized: true }));
  10333. this._minimized = null;
  10334. const durationMS = duration * 1e3;
  10335. const element2 = this.elementTarget;
  10336. const header = element2.querySelector(".window-header");
  10337. const content = element2.querySelector(".window-content");
  10338. const beforeMinWidth = this.position.minWidth;
  10339. const beforeMinHeight = this.position.minHeight;
  10340. this.position.set({ minWidth: 100, minHeight: 30 });
  10341. element2.style.minWidth = "100px";
  10342. element2.style.minHeight = "30px";
  10343. if (content) {
  10344. content.style.overflow = "hidden";
  10345. for (let cntr = content.children.length; --cntr >= 0; ) {
  10346. content.children[cntr].style.overflow = "hidden";
  10347. }
  10348. }
  10349. const { paddingBottom, paddingTop } = globalThis.getComputedStyle(content);
  10350. const constraints = {
  10351. maxHeight: `${content.clientHeight}px`,
  10352. paddingTop,
  10353. paddingBottom
  10354. };
  10355. if (animate) {
  10356. const animation2 = content.animate([
  10357. constraints,
  10358. { maxHeight: 0, paddingTop: 0, paddingBottom: 0 }
  10359. ], { duration: durationMS, fill: "forwards" });
  10360. animation2.finished.then(() => content.style.display = "none");
  10361. } else {
  10362. setTimeout(() => content.style.display = "none", durationMS);
  10363. }
  10364. const saved = this.position.state.save({ name: "#beforeMinimized", constraints });
  10365. saved.minWidth = beforeMinWidth;
  10366. saved.minHeight = beforeMinHeight;
  10367. const headerOffsetHeight = header.offsetHeight;
  10368. this.position.minHeight = headerOffsetHeight;
  10369. if (animate) {
  10370. await this.position.animate.to({ height: headerOffsetHeight }, { duration }).finished;
  10371. }
  10372. for (let cntr = header.children.length; --cntr >= 0; ) {
  10373. const className = header.children[cntr].className;
  10374. if (className.includes("window-title") || className.includes("close")) {
  10375. continue;
  10376. }
  10377. if (className.includes("keep-minimized")) {
  10378. header.children[cntr].style.display = "block";
  10379. continue;
  10380. }
  10381. header.children[cntr].style.display = "none";
  10382. }
  10383. if (animate) {
  10384. await this.position.animate.to({ width: MIN_WINDOW_WIDTH }, { duration: 0.1 }).finished;
  10385. }
  10386. element2.classList.add("minimized");
  10387. this._minimized = true;
  10388. }
  10389. /**
  10390. * Provides a callback after all Svelte components are initialized.
  10391. *
  10392. * @param {object} [opts] - Optional parameters.
  10393. *
  10394. * @param {HTMLElement} [opts.element] - HTMLElement container for main application element.
  10395. *
  10396. * @param {HTMLElement} [opts.elementContent] - HTMLElement container for content area of application shells.
  10397. *
  10398. * @param {HTMLElement} [opts.elementTarget] - HTMLElement container for main application target element.
  10399. */
  10400. onSvelteMount({ element: element2, elementContent, elementTarget } = {}) {
  10401. }
  10402. // eslint-disable-line no-unused-vars
  10403. /**
  10404. * Provides a callback after the main application shell is remounted. This may occur during HMR / hot module
  10405. * replacement or directly invoked from the `elementRootUpdate` callback passed to the application shell component
  10406. * context.
  10407. *
  10408. * @param {object} [opts] - Optional parameters.
  10409. *
  10410. * @param {HTMLElement} [opts.element] - HTMLElement container for main application element.
  10411. *
  10412. * @param {HTMLElement} [opts.elementContent] - HTMLElement container for content area of application shells.
  10413. *
  10414. * @param {HTMLElement} [opts.elementTarget] - HTMLElement container for main application target element.
  10415. */
  10416. onSvelteRemount({ element: element2, elementContent, elementTarget } = {}) {
  10417. }
  10418. // eslint-disable-line no-unused-vars
  10419. /**
  10420. * Override replacing HTML as Svelte components control the rendering process. Only potentially change the outer
  10421. * application frame / title for pop-out applications.
  10422. *
  10423. * @inheritDoc
  10424. * @ignore
  10425. */
  10426. _replaceHTML(element2, html) {
  10427. if (!element2.length) {
  10428. return;
  10429. }
  10430. this.reactive.updateHeaderButtons();
  10431. }
  10432. /**
  10433. * Provides an override verifying that a new Application being rendered for the first time doesn't have a
  10434. * corresponding DOM element already loaded. This is a check that only occurs when `this._state` is
  10435. * `Application.RENDER_STATES.NONE`. It is useful in particular when SvelteApplication has a static ID
  10436. * explicitly set in `this.options.id` and long intro / outro transitions are assigned. If a new application
  10437. * sharing this static ID attempts to open / render for the first time while an existing DOM element sharing
  10438. * this static ID exists then the initial render is cancelled below rather than crashing later in the render
  10439. * cycle {@link Position.set}.
  10440. *
  10441. * @inheritDoc
  10442. * @protected
  10443. * @ignore
  10444. */
  10445. async _render(force = false, options = {}) {
  10446. if (isObject(options?.focusSource)) {
  10447. this.options.focusSource = options.focusSource;
  10448. }
  10449. if (this._state === Application.RENDER_STATES.NONE && document.querySelector(`#${this.id}`) instanceof HTMLElement) {
  10450. console.warn(`SvelteApplication - _render: A DOM element already exists for CSS ID '${this.id}'. Cancelling initial render for new application with appId '${this.appId}'.`);
  10451. return;
  10452. }
  10453. await super._render(force, options);
  10454. if (!this.#onMount) {
  10455. this.onSvelteMount({ element: this._element[0], elementContent: this.#elementContent, elementTarget: this.#elementTarget });
  10456. this.#onMount = true;
  10457. }
  10458. }
  10459. /**
  10460. * Render the inner application content. Only render a template if one is defined otherwise provide an empty
  10461. * JQuery element per the core Foundry API.
  10462. *
  10463. * @param {object} data The data used to render the inner template
  10464. *
  10465. * @returns {Promise.<JQuery>} A promise resolving to the constructed jQuery object
  10466. *
  10467. * @protected
  10468. * @ignore
  10469. */
  10470. async _renderInner(data) {
  10471. const html = typeof this.template === "string" ? await renderTemplate(this.template, data) : document.createDocumentFragment();
  10472. return $(html);
  10473. }
  10474. /**
  10475. * Stores the initial z-index set in `_renderOuter` which is used in `_injectHTML` to set the target element
  10476. * z-index after the Svelte component is mounted.
  10477. *
  10478. * @returns {Promise<JQuery>} Outer frame / unused.
  10479. * @protected
  10480. * @ignore
  10481. */
  10482. async _renderOuter() {
  10483. const html = await super._renderOuter();
  10484. this.#initialZIndex = html[0].style.zIndex;
  10485. return html;
  10486. }
  10487. /**
  10488. * All calculation and updates of position are implemented in {@link Position.set}. This allows position to be fully
  10489. * reactive and in control of updating inline styles for the application.
  10490. *
  10491. * This method remains for backward compatibility with Foundry. If you have a custom override quite likely you need
  10492. * to update to using the {@link Position.validators} functionality.
  10493. *
  10494. * @param {PositionDataExtended} [position] - Position data.
  10495. *
  10496. * @returns {Position} The updated position object for the application containing the new values
  10497. */
  10498. setPosition(position) {
  10499. return this.position.set(position);
  10500. }
  10501. /**
  10502. * This method is invoked by the `elementRootUpdate` callback that is added to the external context passed to
  10503. * Svelte components. When invoked it updates the local element roots tracked by SvelteApplication.
  10504. *
  10505. * This method may also be invoked by HMR / hot module replacement via `svelte-hmr`.
  10506. */
  10507. #updateApplicationShell() {
  10508. const applicationShell = this.svelte.applicationShell;
  10509. if (applicationShell !== null) {
  10510. this._element = $(applicationShell.elementRoot);
  10511. this.#elementContent = hasGetter(applicationShell, "elementContent") ? applicationShell.elementContent : null;
  10512. this.#elementTarget = hasGetter(applicationShell, "elementTarget") ? applicationShell.elementTarget : null;
  10513. if (this.#elementTarget === null) {
  10514. this.#elementTarget = typeof this.options.selectorTarget === "string" ? this._element[0].querySelector(this.options.selectorTarget) : this._element[0];
  10515. }
  10516. if (typeof this.options.positionable === "boolean" && this.options.positionable) {
  10517. this.#elementTarget.style.zIndex = typeof this.options.zIndex === "number" ? this.options.zIndex : this.#initialZIndex ?? 95;
  10518. super.bringToTop();
  10519. this.position.set(this.position.get());
  10520. }
  10521. super._activateCoreListeners([this.#elementTarget]);
  10522. this.onSvelteRemount({ element: this._element[0], elementContent: this.#elementContent, elementTarget: this.#elementTarget });
  10523. }
  10524. }
  10525. }
  10526. const s_STYLE_KEY = "#__trl-root-styles";
  10527. const cssVariables = new StyleManager({ docKey: s_STYLE_KEY, version: 1 });
  10528. const TJSContainer_svelte_svelte_type_style_lang = "";
  10529. function resizeObserver(node, target) {
  10530. ResizeObserverManager.add(node, target);
  10531. return {
  10532. update: (newTarget) => {
  10533. ResizeObserverManager.remove(node, target);
  10534. target = newTarget;
  10535. ResizeObserverManager.add(node, target);
  10536. },
  10537. destroy: () => {
  10538. ResizeObserverManager.remove(node, target);
  10539. }
  10540. };
  10541. }
  10542. resizeObserver.updateCache = function(el) {
  10543. if (!(el instanceof HTMLElement)) {
  10544. throw new TypeError(`resizeObserverUpdate error: 'el' is not an HTMLElement.`);
  10545. }
  10546. const subscribers = s_MAP.get(el);
  10547. if (Array.isArray(subscribers)) {
  10548. const computed = globalThis.getComputedStyle(el);
  10549. const borderBottom = styleParsePixels(el.style.borderBottom) ?? styleParsePixels(computed.borderBottom) ?? 0;
  10550. const borderLeft = styleParsePixels(el.style.borderLeft) ?? styleParsePixels(computed.borderLeft) ?? 0;
  10551. const borderRight = styleParsePixels(el.style.borderRight) ?? styleParsePixels(computed.borderRight) ?? 0;
  10552. const borderTop = styleParsePixels(el.style.borderTop) ?? styleParsePixels(computed.borderTop) ?? 0;
  10553. const paddingBottom = styleParsePixels(el.style.paddingBottom) ?? styleParsePixels(computed.paddingBottom) ?? 0;
  10554. const paddingLeft = styleParsePixels(el.style.paddingLeft) ?? styleParsePixels(computed.paddingLeft) ?? 0;
  10555. const paddingRight = styleParsePixels(el.style.paddingRight) ?? styleParsePixels(computed.paddingRight) ?? 0;
  10556. const paddingTop = styleParsePixels(el.style.paddingTop) ?? styleParsePixels(computed.paddingTop) ?? 0;
  10557. const additionalWidth = borderLeft + borderRight + paddingLeft + paddingRight;
  10558. const additionalHeight = borderTop + borderBottom + paddingTop + paddingBottom;
  10559. for (const subscriber of subscribers) {
  10560. subscriber.styles.additionalWidth = additionalWidth;
  10561. subscriber.styles.additionalHeight = additionalHeight;
  10562. s_UPDATE_SUBSCRIBER(subscriber, subscriber.contentWidth, subscriber.contentHeight);
  10563. }
  10564. }
  10565. };
  10566. const s_MAP = /* @__PURE__ */ new Map();
  10567. class ResizeObserverManager {
  10568. /**
  10569. * Add an HTMLElement and ResizeObserverTarget instance for monitoring. Create cached style attributes for the
  10570. * given element include border & padding dimensions for offset width / height calculations.
  10571. *
  10572. * @param {HTMLElement} el - The element to observe.
  10573. *
  10574. * @param {ResizeObserverTarget} target - A target that contains one of several mechanisms for updating resize data.
  10575. */
  10576. static add(el, target) {
  10577. const updateType = s_GET_UPDATE_TYPE(target);
  10578. if (updateType === 0) {
  10579. throw new Error(`'target' does not match supported ResizeObserverManager update mechanisms.`);
  10580. }
  10581. const computed = globalThis.getComputedStyle(el);
  10582. const borderBottom = styleParsePixels(el.style.borderBottom) ?? styleParsePixels(computed.borderBottom) ?? 0;
  10583. const borderLeft = styleParsePixels(el.style.borderLeft) ?? styleParsePixels(computed.borderLeft) ?? 0;
  10584. const borderRight = styleParsePixels(el.style.borderRight) ?? styleParsePixels(computed.borderRight) ?? 0;
  10585. const borderTop = styleParsePixels(el.style.borderTop) ?? styleParsePixels(computed.borderTop) ?? 0;
  10586. const paddingBottom = styleParsePixels(el.style.paddingBottom) ?? styleParsePixels(computed.paddingBottom) ?? 0;
  10587. const paddingLeft = styleParsePixels(el.style.paddingLeft) ?? styleParsePixels(computed.paddingLeft) ?? 0;
  10588. const paddingRight = styleParsePixels(el.style.paddingRight) ?? styleParsePixels(computed.paddingRight) ?? 0;
  10589. const paddingTop = styleParsePixels(el.style.paddingTop) ?? styleParsePixels(computed.paddingTop) ?? 0;
  10590. const data = {
  10591. updateType,
  10592. target,
  10593. // Stores most recent contentRect.width and contentRect.height values from ResizeObserver.
  10594. contentWidth: 0,
  10595. contentHeight: 0,
  10596. // Convenience data for total border & padding for offset width & height calculations.
  10597. styles: {
  10598. additionalWidth: borderLeft + borderRight + paddingLeft + paddingRight,
  10599. additionalHeight: borderTop + borderBottom + paddingTop + paddingBottom
  10600. }
  10601. };
  10602. if (s_MAP.has(el)) {
  10603. const subscribers = s_MAP.get(el);
  10604. subscribers.push(data);
  10605. } else {
  10606. s_MAP.set(el, [data]);
  10607. }
  10608. s_RESIZE_OBSERVER.observe(el);
  10609. }
  10610. /**
  10611. * Removes all targets from monitoring when just an element is provided otherwise removes a specific target
  10612. * from the monitoring map. If no more targets remain then the element is removed from monitoring.
  10613. *
  10614. * @param {HTMLElement} el - Element to remove from monitoring.
  10615. *
  10616. * @param {ResizeObserverTarget} [target] - A specific target to remove from monitoring.
  10617. */
  10618. static remove(el, target = void 0) {
  10619. const subscribers = s_MAP.get(el);
  10620. if (Array.isArray(subscribers)) {
  10621. const index = subscribers.findIndex((entry) => entry.target === target);
  10622. if (index >= 0) {
  10623. s_UPDATE_SUBSCRIBER(subscribers[index], void 0, void 0);
  10624. subscribers.splice(index, 1);
  10625. }
  10626. if (subscribers.length === 0) {
  10627. s_MAP.delete(el);
  10628. s_RESIZE_OBSERVER.unobserve(el);
  10629. }
  10630. }
  10631. }
  10632. }
  10633. const s_UPDATE_TYPES = {
  10634. none: 0,
  10635. attribute: 1,
  10636. function: 2,
  10637. resizeObserved: 3,
  10638. setContentBounds: 4,
  10639. setDimension: 5,
  10640. storeObject: 6,
  10641. storesObject: 7
  10642. };
  10643. const s_RESIZE_OBSERVER = new ResizeObserver((entries) => {
  10644. for (const entry of entries) {
  10645. const subscribers = s_MAP.get(entry?.target);
  10646. if (Array.isArray(subscribers)) {
  10647. const contentWidth = entry.contentRect.width;
  10648. const contentHeight = entry.contentRect.height;
  10649. for (const subscriber of subscribers) {
  10650. s_UPDATE_SUBSCRIBER(subscriber, contentWidth, contentHeight);
  10651. }
  10652. }
  10653. }
  10654. });
  10655. function s_GET_UPDATE_TYPE(target) {
  10656. if (target?.resizeObserved instanceof Function) {
  10657. return s_UPDATE_TYPES.resizeObserved;
  10658. }
  10659. if (target?.setDimension instanceof Function) {
  10660. return s_UPDATE_TYPES.setDimension;
  10661. }
  10662. if (target?.setContentBounds instanceof Function) {
  10663. return s_UPDATE_TYPES.setContentBounds;
  10664. }
  10665. const targetType = typeof target;
  10666. if (targetType !== null && (targetType === "object" || targetType === "function")) {
  10667. if (isUpdatableStore(target.resizeObserved)) {
  10668. return s_UPDATE_TYPES.storeObject;
  10669. }
  10670. const stores = target?.stores;
  10671. if (isObject(stores) || typeof stores === "function") {
  10672. if (isUpdatableStore(stores.resizeObserved)) {
  10673. return s_UPDATE_TYPES.storesObject;
  10674. }
  10675. }
  10676. }
  10677. if (targetType !== null && targetType === "object") {
  10678. return s_UPDATE_TYPES.attribute;
  10679. }
  10680. if (targetType === "function") {
  10681. return s_UPDATE_TYPES.function;
  10682. }
  10683. return s_UPDATE_TYPES.none;
  10684. }
  10685. function s_UPDATE_SUBSCRIBER(subscriber, contentWidth, contentHeight) {
  10686. const styles = subscriber.styles;
  10687. subscriber.contentWidth = contentWidth;
  10688. subscriber.contentHeight = contentHeight;
  10689. const offsetWidth = Number.isFinite(contentWidth) ? contentWidth + styles.additionalWidth : void 0;
  10690. const offsetHeight = Number.isFinite(contentHeight) ? contentHeight + styles.additionalHeight : void 0;
  10691. const target = subscriber.target;
  10692. switch (subscriber.updateType) {
  10693. case s_UPDATE_TYPES.attribute:
  10694. target.contentWidth = contentWidth;
  10695. target.contentHeight = contentHeight;
  10696. target.offsetWidth = offsetWidth;
  10697. target.offsetHeight = offsetHeight;
  10698. break;
  10699. case s_UPDATE_TYPES.function:
  10700. target?.(offsetWidth, offsetHeight, contentWidth, contentHeight);
  10701. break;
  10702. case s_UPDATE_TYPES.resizeObserved:
  10703. target.resizeObserved?.(offsetWidth, offsetHeight, contentWidth, contentHeight);
  10704. break;
  10705. case s_UPDATE_TYPES.setContentBounds:
  10706. target.setContentBounds?.(contentWidth, contentHeight);
  10707. break;
  10708. case s_UPDATE_TYPES.setDimension:
  10709. target.setDimension?.(offsetWidth, offsetHeight);
  10710. break;
  10711. case s_UPDATE_TYPES.storeObject:
  10712. target.resizeObserved.update((object) => {
  10713. object.contentHeight = contentHeight;
  10714. object.contentWidth = contentWidth;
  10715. object.offsetHeight = offsetHeight;
  10716. object.offsetWidth = offsetWidth;
  10717. return object;
  10718. });
  10719. break;
  10720. case s_UPDATE_TYPES.storesObject:
  10721. target.stores.resizeObserved.update((object) => {
  10722. object.contentHeight = contentHeight;
  10723. object.contentWidth = contentWidth;
  10724. object.offsetHeight = offsetHeight;
  10725. object.offsetWidth = offsetWidth;
  10726. return object;
  10727. });
  10728. break;
  10729. }
  10730. }
  10731. function applyStyles(node, properties) {
  10732. function setProperties() {
  10733. if (typeof properties !== "object") {
  10734. return;
  10735. }
  10736. for (const prop of Object.keys(properties)) {
  10737. node.style.setProperty(`${prop}`, properties[prop]);
  10738. }
  10739. }
  10740. setProperties();
  10741. return {
  10742. update(newProperties) {
  10743. properties = newProperties;
  10744. setProperties();
  10745. }
  10746. };
  10747. }
  10748. function draggable(node, {
  10749. position,
  10750. active: active2 = true,
  10751. button = 0,
  10752. storeDragging = void 0,
  10753. ease = false,
  10754. easeOptions = { duration: 0.1, ease: cubicOut },
  10755. hasTargetClassList,
  10756. ignoreTargetClassList
  10757. }) {
  10758. if (hasTargetClassList !== void 0 && !isIterable(hasTargetClassList)) {
  10759. throw new TypeError(`'hasTargetClassList' is not iterable.`);
  10760. }
  10761. if (ignoreTargetClassList !== void 0 && !isIterable(ignoreTargetClassList)) {
  10762. throw new TypeError(`'ignoreTargetClassList' is not iterable.`);
  10763. }
  10764. let initialPosition = null;
  10765. let initialDragPoint = {};
  10766. let dragging = false;
  10767. let quickTo = position.animate.quickTo(["top", "left"], easeOptions);
  10768. const handlers = {
  10769. dragDown: ["pointerdown", (e) => onDragPointerDown(e), false],
  10770. dragMove: ["pointermove", (e) => onDragPointerChange(e), false],
  10771. dragUp: ["pointerup", (e) => onDragPointerUp(e), false]
  10772. };
  10773. function activateListeners() {
  10774. node.addEventListener(...handlers.dragDown);
  10775. node.classList.add("draggable");
  10776. }
  10777. function removeListeners() {
  10778. if (typeof storeDragging?.set === "function") {
  10779. storeDragging.set(false);
  10780. }
  10781. node.removeEventListener(...handlers.dragDown);
  10782. node.removeEventListener(...handlers.dragMove);
  10783. node.removeEventListener(...handlers.dragUp);
  10784. node.classList.remove("draggable");
  10785. }
  10786. if (active2) {
  10787. activateListeners();
  10788. }
  10789. function onDragPointerDown(event) {
  10790. if (event.button !== button || !event.isPrimary) {
  10791. return;
  10792. }
  10793. if (!position.enabled) {
  10794. return;
  10795. }
  10796. if (ignoreTargetClassList !== void 0 && event.target instanceof HTMLElement) {
  10797. for (const targetClass of ignoreTargetClassList) {
  10798. if (event.target.classList.contains(targetClass)) {
  10799. return;
  10800. }
  10801. }
  10802. }
  10803. if (hasTargetClassList !== void 0 && event.target instanceof HTMLElement) {
  10804. let foundTarget = false;
  10805. for (const targetClass of hasTargetClassList) {
  10806. if (event.target.classList.contains(targetClass)) {
  10807. foundTarget = true;
  10808. break;
  10809. }
  10810. }
  10811. if (!foundTarget) {
  10812. return;
  10813. }
  10814. }
  10815. event.preventDefault();
  10816. dragging = false;
  10817. initialPosition = position.get();
  10818. initialDragPoint = { x: event.clientX, y: event.clientY };
  10819. node.addEventListener(...handlers.dragMove);
  10820. node.addEventListener(...handlers.dragUp);
  10821. node.setPointerCapture(event.pointerId);
  10822. }
  10823. function onDragPointerChange(event) {
  10824. if ((event.buttons & 1) === 0) {
  10825. onDragPointerUp(event);
  10826. return;
  10827. }
  10828. if (event.button !== -1 || !event.isPrimary) {
  10829. return;
  10830. }
  10831. event.preventDefault();
  10832. if (!dragging && typeof storeDragging?.set === "function") {
  10833. dragging = true;
  10834. storeDragging.set(true);
  10835. }
  10836. const newLeft = initialPosition.left + (event.clientX - initialDragPoint.x);
  10837. const newTop = initialPosition.top + (event.clientY - initialDragPoint.y);
  10838. if (ease) {
  10839. quickTo(newTop, newLeft);
  10840. } else {
  10841. s_POSITION_DATA.left = newLeft;
  10842. s_POSITION_DATA.top = newTop;
  10843. position.set(s_POSITION_DATA);
  10844. }
  10845. }
  10846. function onDragPointerUp(event) {
  10847. event.preventDefault();
  10848. dragging = false;
  10849. if (typeof storeDragging?.set === "function") {
  10850. storeDragging.set(false);
  10851. }
  10852. node.removeEventListener(...handlers.dragMove);
  10853. node.removeEventListener(...handlers.dragUp);
  10854. }
  10855. return {
  10856. // The default of active being true won't automatically add listeners twice.
  10857. update: (options) => {
  10858. if (typeof options.active === "boolean") {
  10859. active2 = options.active;
  10860. if (active2) {
  10861. activateListeners();
  10862. } else {
  10863. removeListeners();
  10864. }
  10865. }
  10866. if (typeof options.button === "number") {
  10867. button = options.button;
  10868. }
  10869. if (options.position !== void 0 && options.position !== position) {
  10870. position = options.position;
  10871. quickTo = position.animate.quickTo(["top", "left"], easeOptions);
  10872. }
  10873. if (typeof options.ease === "boolean") {
  10874. ease = options.ease;
  10875. }
  10876. if (isObject(options.easeOptions)) {
  10877. easeOptions = options.easeOptions;
  10878. quickTo.options(easeOptions);
  10879. }
  10880. if (options.hasTargetClassList !== void 0) {
  10881. if (!isIterable(options.hasTargetClassList)) {
  10882. throw new TypeError(`'hasTargetClassList' is not iterable.`);
  10883. } else {
  10884. hasTargetClassList = options.hasTargetClassList;
  10885. }
  10886. }
  10887. if (options.ignoreTargetClassList !== void 0) {
  10888. if (!isIterable(options.ignoreTargetClassList)) {
  10889. throw new TypeError(`'ignoreTargetClassList' is not iterable.`);
  10890. } else {
  10891. ignoreTargetClassList = options.ignoreTargetClassList;
  10892. }
  10893. }
  10894. },
  10895. destroy: () => removeListeners()
  10896. };
  10897. }
  10898. class DraggableOptions {
  10899. #ease = false;
  10900. #easeOptions = { duration: 0.1, ease: cubicOut };
  10901. /**
  10902. * Stores the subscribers.
  10903. *
  10904. * @type {(function(DraggableOptions): void)[]}
  10905. */
  10906. #subscriptions = [];
  10907. constructor({ ease, easeOptions } = {}) {
  10908. Object.defineProperty(this, "ease", {
  10909. get: () => {
  10910. return this.#ease;
  10911. },
  10912. set: (newEase) => {
  10913. if (typeof newEase !== "boolean") {
  10914. throw new TypeError(`'ease' is not a boolean.`);
  10915. }
  10916. this.#ease = newEase;
  10917. this.#updateSubscribers();
  10918. },
  10919. enumerable: true
  10920. });
  10921. Object.defineProperty(this, "easeOptions", {
  10922. get: () => {
  10923. return this.#easeOptions;
  10924. },
  10925. set: (newEaseOptions) => {
  10926. if (newEaseOptions === null || typeof newEaseOptions !== "object") {
  10927. throw new TypeError(`'easeOptions' is not an object.`);
  10928. }
  10929. if (newEaseOptions.duration !== void 0) {
  10930. if (!Number.isFinite(newEaseOptions.duration)) {
  10931. throw new TypeError(`'easeOptions.duration' is not a finite number.`);
  10932. }
  10933. if (newEaseOptions.duration < 0) {
  10934. throw new Error(`'easeOptions.duration' is less than 0.`);
  10935. }
  10936. this.#easeOptions.duration = newEaseOptions.duration;
  10937. }
  10938. if (newEaseOptions.ease !== void 0) {
  10939. if (typeof newEaseOptions.ease !== "function" && typeof newEaseOptions.ease !== "string") {
  10940. throw new TypeError(`'easeOptions.ease' is not a function or string.`);
  10941. }
  10942. this.#easeOptions.ease = newEaseOptions.ease;
  10943. }
  10944. this.#updateSubscribers();
  10945. },
  10946. enumerable: true
  10947. });
  10948. if (ease !== void 0) {
  10949. this.ease = ease;
  10950. }
  10951. if (easeOptions !== void 0) {
  10952. this.easeOptions = easeOptions;
  10953. }
  10954. }
  10955. /**
  10956. * @returns {number} Get ease duration
  10957. */
  10958. get easeDuration() {
  10959. return this.#easeOptions.duration;
  10960. }
  10961. /**
  10962. * @returns {string|Function} Get easing function value.
  10963. */
  10964. get easeValue() {
  10965. return this.#easeOptions.ease;
  10966. }
  10967. /**
  10968. * @param {number} duration - Set ease duration.
  10969. */
  10970. set easeDuration(duration) {
  10971. if (!Number.isFinite(duration)) {
  10972. throw new TypeError(`'duration' is not a finite number.`);
  10973. }
  10974. if (duration < 0) {
  10975. throw new Error(`'duration' is less than 0.`);
  10976. }
  10977. this.#easeOptions.duration = duration;
  10978. this.#updateSubscribers();
  10979. }
  10980. /**
  10981. * @param {string|Function} value - Get easing function value.
  10982. */
  10983. set easeValue(value) {
  10984. if (typeof value !== "function" && typeof value !== "string") {
  10985. throw new TypeError(`'value' is not a function or string.`);
  10986. }
  10987. this.#easeOptions.ease = value;
  10988. this.#updateSubscribers();
  10989. }
  10990. /**
  10991. * Resets all options data to default values.
  10992. */
  10993. reset() {
  10994. this.#ease = false;
  10995. this.#easeOptions = { duration: 0.1, ease: cubicOut };
  10996. this.#updateSubscribers();
  10997. }
  10998. /**
  10999. * Resets easing options to default values.
  11000. */
  11001. resetEase() {
  11002. this.#easeOptions = { duration: 0.1, ease: cubicOut };
  11003. this.#updateSubscribers();
  11004. }
  11005. /**
  11006. *
  11007. * @param {function(DraggableOptions): void} handler - Callback function that is invoked on update / changes.
  11008. * Receives the DraggableOptions object / instance.
  11009. *
  11010. * @returns {(function(): void)} Unsubscribe function.
  11011. */
  11012. subscribe(handler) {
  11013. this.#subscriptions.push(handler);
  11014. handler(this);
  11015. return () => {
  11016. const index = this.#subscriptions.findIndex((sub) => sub === handler);
  11017. if (index >= 0) {
  11018. this.#subscriptions.splice(index, 1);
  11019. }
  11020. };
  11021. }
  11022. #updateSubscribers() {
  11023. const subscriptions = this.#subscriptions;
  11024. if (subscriptions.length > 0) {
  11025. for (let cntr = 0; cntr < subscriptions.length; cntr++) {
  11026. subscriptions[cntr](this);
  11027. }
  11028. }
  11029. }
  11030. }
  11031. draggable.options = (options) => new DraggableOptions(options);
  11032. const s_POSITION_DATA = { left: 0, top: 0 };
  11033. const s_DEFAULT_TRANSITION_OPTIONS = {};
  11034. const TJSGlassPane_svelte_svelte_type_style_lang = "";
  11035. class AppShellContextInternal {
  11036. /** @type {InternalAppStores} */
  11037. #stores;
  11038. constructor() {
  11039. this.#stores = {
  11040. elementContent: writable$1(void 0),
  11041. elementRoot: writable$1(void 0)
  11042. };
  11043. Object.freeze(this.#stores);
  11044. Object.seal(this);
  11045. }
  11046. /**
  11047. * @returns {InternalAppStores} The internal context stores for elementContent / elementRoot
  11048. */
  11049. get stores() {
  11050. return this.#stores;
  11051. }
  11052. }
  11053. function localize(stringId, data) {
  11054. const result = typeof data !== "object" ? globalThis.game.i18n.localize(stringId) : globalThis.game.i18n.format(stringId, data);
  11055. return result !== void 0 ? result : "";
  11056. }
  11057. const TJSHeaderButton_svelte_svelte_type_style_lang = "";
  11058. function create_if_block$a(ctx) {
  11059. let span;
  11060. let t;
  11061. return {
  11062. c() {
  11063. span = element("span");
  11064. t = text$1(
  11065. /*label*/
  11066. ctx[3]
  11067. );
  11068. attr(span, "class", "svelte-ese-166l8wd");
  11069. toggle_class(
  11070. span,
  11071. "has-icon",
  11072. /*icon*/
  11073. ctx[4] !== void 0
  11074. );
  11075. },
  11076. m(target, anchor) {
  11077. insert(target, span, anchor);
  11078. append(span, t);
  11079. },
  11080. p(ctx2, dirty) {
  11081. if (dirty & /*label*/
  11082. 8)
  11083. set_data(
  11084. t,
  11085. /*label*/
  11086. ctx2[3]
  11087. );
  11088. if (dirty & /*icon*/
  11089. 16) {
  11090. toggle_class(
  11091. span,
  11092. "has-icon",
  11093. /*icon*/
  11094. ctx2[4] !== void 0
  11095. );
  11096. }
  11097. },
  11098. d(detaching) {
  11099. if (detaching)
  11100. detach(span);
  11101. }
  11102. };
  11103. }
  11104. function create_fragment$p(ctx) {
  11105. let a;
  11106. let html_tag;
  11107. let html_anchor;
  11108. let a_class_value;
  11109. let applyStyles_action;
  11110. let mounted;
  11111. let dispose;
  11112. let if_block = (
  11113. /*label*/
  11114. ctx[3] && create_if_block$a(ctx)
  11115. );
  11116. return {
  11117. c() {
  11118. a = element("a");
  11119. html_tag = new HtmlTag(false);
  11120. html_anchor = empty();
  11121. if (if_block)
  11122. if_block.c();
  11123. html_tag.a = html_anchor;
  11124. attr(a, "class", a_class_value = "header-button " + /*button*/
  11125. ctx[0].class + " svelte-ese-166l8wd");
  11126. attr(
  11127. a,
  11128. "aria-label",
  11129. /*label*/
  11130. ctx[3]
  11131. );
  11132. attr(a, "tabindex", "0");
  11133. attr(a, "role", "button");
  11134. toggle_class(
  11135. a,
  11136. "keep-minimized",
  11137. /*keepMinimized*/
  11138. ctx[2]
  11139. );
  11140. },
  11141. m(target, anchor) {
  11142. insert(target, a, anchor);
  11143. html_tag.m(
  11144. /*icon*/
  11145. ctx[4],
  11146. a
  11147. );
  11148. append(a, html_anchor);
  11149. if (if_block)
  11150. if_block.m(a, null);
  11151. if (!mounted) {
  11152. dispose = [
  11153. listen(a, "click", stop_propagation(prevent_default(
  11154. /*onClick*/
  11155. ctx[5]
  11156. ))),
  11157. listen(a, "contextmenu", stop_propagation(prevent_default(
  11158. /*onContextMenu*/
  11159. ctx[6]
  11160. ))),
  11161. listen(
  11162. a,
  11163. "keydown",
  11164. /*onKeydown*/
  11165. ctx[7]
  11166. ),
  11167. listen(
  11168. a,
  11169. "keyup",
  11170. /*onKeyup*/
  11171. ctx[8]
  11172. ),
  11173. action_destroyer(applyStyles_action = applyStyles.call(
  11174. null,
  11175. a,
  11176. /*styles*/
  11177. ctx[1]
  11178. ))
  11179. ];
  11180. mounted = true;
  11181. }
  11182. },
  11183. p(ctx2, [dirty]) {
  11184. if (dirty & /*icon*/
  11185. 16)
  11186. html_tag.p(
  11187. /*icon*/
  11188. ctx2[4]
  11189. );
  11190. if (
  11191. /*label*/
  11192. ctx2[3]
  11193. ) {
  11194. if (if_block) {
  11195. if_block.p(ctx2, dirty);
  11196. } else {
  11197. if_block = create_if_block$a(ctx2);
  11198. if_block.c();
  11199. if_block.m(a, null);
  11200. }
  11201. } else if (if_block) {
  11202. if_block.d(1);
  11203. if_block = null;
  11204. }
  11205. if (dirty & /*button*/
  11206. 1 && a_class_value !== (a_class_value = "header-button " + /*button*/
  11207. ctx2[0].class + " svelte-ese-166l8wd")) {
  11208. attr(a, "class", a_class_value);
  11209. }
  11210. if (dirty & /*label*/
  11211. 8) {
  11212. attr(
  11213. a,
  11214. "aria-label",
  11215. /*label*/
  11216. ctx2[3]
  11217. );
  11218. }
  11219. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
  11220. 2)
  11221. applyStyles_action.update.call(
  11222. null,
  11223. /*styles*/
  11224. ctx2[1]
  11225. );
  11226. if (dirty & /*button, keepMinimized*/
  11227. 5) {
  11228. toggle_class(
  11229. a,
  11230. "keep-minimized",
  11231. /*keepMinimized*/
  11232. ctx2[2]
  11233. );
  11234. }
  11235. },
  11236. i: noop,
  11237. o: noop,
  11238. d(detaching) {
  11239. if (detaching)
  11240. detach(a);
  11241. if (if_block)
  11242. if_block.d();
  11243. mounted = false;
  11244. run_all(dispose);
  11245. }
  11246. };
  11247. }
  11248. const s_REGEX_HTML = /^\s*<.*>$/;
  11249. function instance$p($$self, $$props, $$invalidate) {
  11250. let title;
  11251. let icon;
  11252. let label;
  11253. let keepMinimized;
  11254. let keyCode;
  11255. let styles;
  11256. let { button = void 0 } = $$props;
  11257. function onClick(event) {
  11258. const invoke = button?.onPress ?? button?.onclick;
  11259. if (typeof invoke === "function") {
  11260. invoke.call(button, event);
  11261. $$invalidate(0, button);
  11262. }
  11263. }
  11264. function onContextMenu(event) {
  11265. const invoke = button?.onContextMenu;
  11266. if (typeof invoke === "function") {
  11267. invoke.call(button, event);
  11268. $$invalidate(0, button);
  11269. }
  11270. }
  11271. function onKeydown(event) {
  11272. if (event.code === keyCode) {
  11273. event.preventDefault();
  11274. event.stopPropagation();
  11275. }
  11276. }
  11277. function onKeyup(event) {
  11278. if (event.code === keyCode) {
  11279. const invoke = button.onPress ?? button.onclick;
  11280. if (typeof invoke === "function") {
  11281. invoke.call(button, event);
  11282. $$invalidate(0, button);
  11283. }
  11284. event.preventDefault();
  11285. event.stopPropagation();
  11286. }
  11287. }
  11288. $$self.$$set = ($$props2) => {
  11289. if ("button" in $$props2)
  11290. $$invalidate(0, button = $$props2.button);
  11291. };
  11292. $$self.$$.update = () => {
  11293. if ($$self.$$.dirty & /*button*/
  11294. 1) {
  11295. $$invalidate(9, title = isObject(button) && typeof button.title === "string" ? localize(button.title) : "");
  11296. }
  11297. if ($$self.$$.dirty & /*button, title*/
  11298. 513) {
  11299. $$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>`);
  11300. }
  11301. if ($$self.$$.dirty & /*button*/
  11302. 1) {
  11303. $$invalidate(3, label = isObject(button) && typeof button.label === "string" ? localize(button.label) : void 0);
  11304. }
  11305. if ($$self.$$.dirty & /*button*/
  11306. 1) {
  11307. $$invalidate(2, keepMinimized = isObject(button) && typeof button.keepMinimized === "boolean" ? button.keepMinimized : false);
  11308. }
  11309. if ($$self.$$.dirty & /*button*/
  11310. 1) {
  11311. keyCode = isObject(button) && typeof button.keyCode === "string" ? button.keyCode : "Enter";
  11312. }
  11313. if ($$self.$$.dirty & /*button*/
  11314. 1) {
  11315. $$invalidate(1, styles = isObject(button) && isObject(button.styles) ? button.styles : void 0);
  11316. }
  11317. };
  11318. return [
  11319. button,
  11320. styles,
  11321. keepMinimized,
  11322. label,
  11323. icon,
  11324. onClick,
  11325. onContextMenu,
  11326. onKeydown,
  11327. onKeyup,
  11328. title
  11329. ];
  11330. }
  11331. class TJSHeaderButton extends SvelteComponent {
  11332. constructor(options) {
  11333. super();
  11334. init(this, options, instance$p, create_fragment$p, safe_not_equal, { button: 0 });
  11335. }
  11336. get button() {
  11337. return this.$$.ctx[0];
  11338. }
  11339. set button(button) {
  11340. this.$$set({ button });
  11341. flush();
  11342. }
  11343. }
  11344. const TJSApplicationHeader_svelte_svelte_type_style_lang = "";
  11345. function get_each_context$7(ctx, list, i) {
  11346. const child_ctx = ctx.slice();
  11347. child_ctx[31] = list[i];
  11348. return child_ctx;
  11349. }
  11350. function get_each_context_1$2(ctx, list, i) {
  11351. const child_ctx = ctx.slice();
  11352. child_ctx[31] = list[i];
  11353. return child_ctx;
  11354. }
  11355. function create_if_block$9(ctx) {
  11356. let img;
  11357. let img_src_value;
  11358. return {
  11359. c() {
  11360. img = element("img");
  11361. attr(img, "class", "tjs-app-icon keep-minimized svelte-ese-1wviwl9");
  11362. if (!src_url_equal(img.src, img_src_value = /*$storeHeaderIcon*/
  11363. ctx[6]))
  11364. attr(img, "src", img_src_value);
  11365. attr(img, "alt", "icon");
  11366. },
  11367. m(target, anchor) {
  11368. insert(target, img, anchor);
  11369. },
  11370. p(ctx2, dirty) {
  11371. if (dirty[0] & /*$storeHeaderIcon*/
  11372. 64 && !src_url_equal(img.src, img_src_value = /*$storeHeaderIcon*/
  11373. ctx2[6])) {
  11374. attr(img, "src", img_src_value);
  11375. }
  11376. },
  11377. d(detaching) {
  11378. if (detaching)
  11379. detach(img);
  11380. }
  11381. };
  11382. }
  11383. function create_each_block_1$2(ctx) {
  11384. let switch_instance;
  11385. let switch_instance_anchor;
  11386. let current;
  11387. const switch_instance_spread_levels = [
  11388. /*button*/
  11389. ctx[31].props
  11390. ];
  11391. var switch_value = (
  11392. /*button*/
  11393. ctx[31].class
  11394. );
  11395. function switch_props(ctx2) {
  11396. let switch_instance_props = {};
  11397. for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
  11398. switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
  11399. }
  11400. return { props: switch_instance_props };
  11401. }
  11402. if (switch_value) {
  11403. switch_instance = construct_svelte_component(switch_value, switch_props());
  11404. }
  11405. return {
  11406. c() {
  11407. if (switch_instance)
  11408. create_component(switch_instance.$$.fragment);
  11409. switch_instance_anchor = empty();
  11410. },
  11411. m(target, anchor) {
  11412. if (switch_instance)
  11413. mount_component(switch_instance, target, anchor);
  11414. insert(target, switch_instance_anchor, anchor);
  11415. current = true;
  11416. },
  11417. p(ctx2, dirty) {
  11418. const switch_instance_changes = dirty[0] & /*buttonsLeft*/
  11419. 2 ? get_spread_update(switch_instance_spread_levels, [get_spread_object(
  11420. /*button*/
  11421. ctx2[31].props
  11422. )]) : {};
  11423. if (switch_value !== (switch_value = /*button*/
  11424. ctx2[31].class)) {
  11425. if (switch_instance) {
  11426. group_outros();
  11427. const old_component = switch_instance;
  11428. transition_out(old_component.$$.fragment, 1, 0, () => {
  11429. destroy_component(old_component, 1);
  11430. });
  11431. check_outros();
  11432. }
  11433. if (switch_value) {
  11434. switch_instance = construct_svelte_component(switch_value, switch_props());
  11435. create_component(switch_instance.$$.fragment);
  11436. transition_in(switch_instance.$$.fragment, 1);
  11437. mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
  11438. } else {
  11439. switch_instance = null;
  11440. }
  11441. } else if (switch_value) {
  11442. switch_instance.$set(switch_instance_changes);
  11443. }
  11444. },
  11445. i(local) {
  11446. if (current)
  11447. return;
  11448. if (switch_instance)
  11449. transition_in(switch_instance.$$.fragment, local);
  11450. current = true;
  11451. },
  11452. o(local) {
  11453. if (switch_instance)
  11454. transition_out(switch_instance.$$.fragment, local);
  11455. current = false;
  11456. },
  11457. d(detaching) {
  11458. if (detaching)
  11459. detach(switch_instance_anchor);
  11460. if (switch_instance)
  11461. destroy_component(switch_instance, detaching);
  11462. }
  11463. };
  11464. }
  11465. function create_each_block$7(ctx) {
  11466. let switch_instance;
  11467. let switch_instance_anchor;
  11468. let current;
  11469. const switch_instance_spread_levels = [
  11470. /*button*/
  11471. ctx[31].props
  11472. ];
  11473. var switch_value = (
  11474. /*button*/
  11475. ctx[31].class
  11476. );
  11477. function switch_props(ctx2) {
  11478. let switch_instance_props = {};
  11479. for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
  11480. switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
  11481. }
  11482. return { props: switch_instance_props };
  11483. }
  11484. if (switch_value) {
  11485. switch_instance = construct_svelte_component(switch_value, switch_props());
  11486. }
  11487. return {
  11488. c() {
  11489. if (switch_instance)
  11490. create_component(switch_instance.$$.fragment);
  11491. switch_instance_anchor = empty();
  11492. },
  11493. m(target, anchor) {
  11494. if (switch_instance)
  11495. mount_component(switch_instance, target, anchor);
  11496. insert(target, switch_instance_anchor, anchor);
  11497. current = true;
  11498. },
  11499. p(ctx2, dirty) {
  11500. const switch_instance_changes = dirty[0] & /*buttonsRight*/
  11501. 4 ? get_spread_update(switch_instance_spread_levels, [get_spread_object(
  11502. /*button*/
  11503. ctx2[31].props
  11504. )]) : {};
  11505. if (switch_value !== (switch_value = /*button*/
  11506. ctx2[31].class)) {
  11507. if (switch_instance) {
  11508. group_outros();
  11509. const old_component = switch_instance;
  11510. transition_out(old_component.$$.fragment, 1, 0, () => {
  11511. destroy_component(old_component, 1);
  11512. });
  11513. check_outros();
  11514. }
  11515. if (switch_value) {
  11516. switch_instance = construct_svelte_component(switch_value, switch_props());
  11517. create_component(switch_instance.$$.fragment);
  11518. transition_in(switch_instance.$$.fragment, 1);
  11519. mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
  11520. } else {
  11521. switch_instance = null;
  11522. }
  11523. } else if (switch_value) {
  11524. switch_instance.$set(switch_instance_changes);
  11525. }
  11526. },
  11527. i(local) {
  11528. if (current)
  11529. return;
  11530. if (switch_instance)
  11531. transition_in(switch_instance.$$.fragment, local);
  11532. current = true;
  11533. },
  11534. o(local) {
  11535. if (switch_instance)
  11536. transition_out(switch_instance.$$.fragment, local);
  11537. current = false;
  11538. },
  11539. d(detaching) {
  11540. if (detaching)
  11541. detach(switch_instance_anchor);
  11542. if (switch_instance)
  11543. destroy_component(switch_instance, detaching);
  11544. }
  11545. };
  11546. }
  11547. function create_key_block(ctx) {
  11548. let header;
  11549. let t0;
  11550. let h4;
  11551. let t1_value = localize(
  11552. /*$storeTitle*/
  11553. ctx[7]
  11554. ) + "";
  11555. let t1;
  11556. let t2;
  11557. let t3;
  11558. let span;
  11559. let t4;
  11560. let draggable_action;
  11561. let minimizable_action;
  11562. let current;
  11563. let mounted;
  11564. let dispose;
  11565. let if_block = typeof /*$storeHeaderIcon*/
  11566. ctx[6] === "string" && create_if_block$9(ctx);
  11567. let each_value_1 = (
  11568. /*buttonsLeft*/
  11569. ctx[1]
  11570. );
  11571. let each_blocks_1 = [];
  11572. for (let i = 0; i < each_value_1.length; i += 1) {
  11573. each_blocks_1[i] = create_each_block_1$2(get_each_context_1$2(ctx, each_value_1, i));
  11574. }
  11575. const out = (i) => transition_out(each_blocks_1[i], 1, 1, () => {
  11576. each_blocks_1[i] = null;
  11577. });
  11578. let each_value = (
  11579. /*buttonsRight*/
  11580. ctx[2]
  11581. );
  11582. let each_blocks = [];
  11583. for (let i = 0; i < each_value.length; i += 1) {
  11584. each_blocks[i] = create_each_block$7(get_each_context$7(ctx, each_value, i));
  11585. }
  11586. const out_1 = (i) => transition_out(each_blocks[i], 1, 1, () => {
  11587. each_blocks[i] = null;
  11588. });
  11589. return {
  11590. c() {
  11591. header = element("header");
  11592. if (if_block)
  11593. if_block.c();
  11594. t0 = space();
  11595. h4 = element("h4");
  11596. t1 = text$1(t1_value);
  11597. t2 = space();
  11598. for (let i = 0; i < each_blocks_1.length; i += 1) {
  11599. each_blocks_1[i].c();
  11600. }
  11601. t3 = space();
  11602. span = element("span");
  11603. t4 = space();
  11604. for (let i = 0; i < each_blocks.length; i += 1) {
  11605. each_blocks[i].c();
  11606. }
  11607. attr(h4, "class", "window-title svelte-ese-1wviwl9");
  11608. set_style(
  11609. h4,
  11610. "display",
  11611. /*displayHeaderTitle*/
  11612. ctx[4]
  11613. );
  11614. attr(span, "class", "tjs-window-header-spacer keep-minimized svelte-ese-1wviwl9");
  11615. attr(header, "class", "window-header flexrow svelte-ese-1wviwl9");
  11616. },
  11617. m(target, anchor) {
  11618. insert(target, header, anchor);
  11619. if (if_block)
  11620. if_block.m(header, null);
  11621. append(header, t0);
  11622. append(header, h4);
  11623. append(h4, t1);
  11624. append(header, t2);
  11625. for (let i = 0; i < each_blocks_1.length; i += 1) {
  11626. each_blocks_1[i].m(header, null);
  11627. }
  11628. append(header, t3);
  11629. append(header, span);
  11630. append(header, t4);
  11631. for (let i = 0; i < each_blocks.length; i += 1) {
  11632. each_blocks[i].m(header, null);
  11633. }
  11634. current = true;
  11635. if (!mounted) {
  11636. dispose = [
  11637. action_destroyer(draggable_action = /*draggable*/
  11638. ctx[0].call(
  11639. null,
  11640. header,
  11641. /*dragOptions*/
  11642. ctx[3]
  11643. )),
  11644. action_destroyer(minimizable_action = /*minimizable*/
  11645. ctx[18].call(
  11646. null,
  11647. header,
  11648. /*$storeMinimizable*/
  11649. ctx[5]
  11650. )),
  11651. listen(
  11652. header,
  11653. "pointerdown",
  11654. /*onPointerdown*/
  11655. ctx[19]
  11656. )
  11657. ];
  11658. mounted = true;
  11659. }
  11660. },
  11661. p(ctx2, dirty) {
  11662. if (typeof /*$storeHeaderIcon*/
  11663. ctx2[6] === "string") {
  11664. if (if_block) {
  11665. if_block.p(ctx2, dirty);
  11666. } else {
  11667. if_block = create_if_block$9(ctx2);
  11668. if_block.c();
  11669. if_block.m(header, t0);
  11670. }
  11671. } else if (if_block) {
  11672. if_block.d(1);
  11673. if_block = null;
  11674. }
  11675. if ((!current || dirty[0] & /*$storeTitle*/
  11676. 128) && t1_value !== (t1_value = localize(
  11677. /*$storeTitle*/
  11678. ctx2[7]
  11679. ) + ""))
  11680. set_data(t1, t1_value);
  11681. if (dirty[0] & /*displayHeaderTitle*/
  11682. 16) {
  11683. set_style(
  11684. h4,
  11685. "display",
  11686. /*displayHeaderTitle*/
  11687. ctx2[4]
  11688. );
  11689. }
  11690. if (dirty[0] & /*buttonsLeft*/
  11691. 2) {
  11692. each_value_1 = /*buttonsLeft*/
  11693. ctx2[1];
  11694. let i;
  11695. for (i = 0; i < each_value_1.length; i += 1) {
  11696. const child_ctx = get_each_context_1$2(ctx2, each_value_1, i);
  11697. if (each_blocks_1[i]) {
  11698. each_blocks_1[i].p(child_ctx, dirty);
  11699. transition_in(each_blocks_1[i], 1);
  11700. } else {
  11701. each_blocks_1[i] = create_each_block_1$2(child_ctx);
  11702. each_blocks_1[i].c();
  11703. transition_in(each_blocks_1[i], 1);
  11704. each_blocks_1[i].m(header, t3);
  11705. }
  11706. }
  11707. group_outros();
  11708. for (i = each_value_1.length; i < each_blocks_1.length; i += 1) {
  11709. out(i);
  11710. }
  11711. check_outros();
  11712. }
  11713. if (dirty[0] & /*buttonsRight*/
  11714. 4) {
  11715. each_value = /*buttonsRight*/
  11716. ctx2[2];
  11717. let i;
  11718. for (i = 0; i < each_value.length; i += 1) {
  11719. const child_ctx = get_each_context$7(ctx2, each_value, i);
  11720. if (each_blocks[i]) {
  11721. each_blocks[i].p(child_ctx, dirty);
  11722. transition_in(each_blocks[i], 1);
  11723. } else {
  11724. each_blocks[i] = create_each_block$7(child_ctx);
  11725. each_blocks[i].c();
  11726. transition_in(each_blocks[i], 1);
  11727. each_blocks[i].m(header, null);
  11728. }
  11729. }
  11730. group_outros();
  11731. for (i = each_value.length; i < each_blocks.length; i += 1) {
  11732. out_1(i);
  11733. }
  11734. check_outros();
  11735. }
  11736. if (draggable_action && is_function(draggable_action.update) && dirty[0] & /*dragOptions*/
  11737. 8)
  11738. draggable_action.update.call(
  11739. null,
  11740. /*dragOptions*/
  11741. ctx2[3]
  11742. );
  11743. if (minimizable_action && is_function(minimizable_action.update) && dirty[0] & /*$storeMinimizable*/
  11744. 32)
  11745. minimizable_action.update.call(
  11746. null,
  11747. /*$storeMinimizable*/
  11748. ctx2[5]
  11749. );
  11750. },
  11751. i(local) {
  11752. if (current)
  11753. return;
  11754. for (let i = 0; i < each_value_1.length; i += 1) {
  11755. transition_in(each_blocks_1[i]);
  11756. }
  11757. for (let i = 0; i < each_value.length; i += 1) {
  11758. transition_in(each_blocks[i]);
  11759. }
  11760. current = true;
  11761. },
  11762. o(local) {
  11763. each_blocks_1 = each_blocks_1.filter(Boolean);
  11764. for (let i = 0; i < each_blocks_1.length; i += 1) {
  11765. transition_out(each_blocks_1[i]);
  11766. }
  11767. each_blocks = each_blocks.filter(Boolean);
  11768. for (let i = 0; i < each_blocks.length; i += 1) {
  11769. transition_out(each_blocks[i]);
  11770. }
  11771. current = false;
  11772. },
  11773. d(detaching) {
  11774. if (detaching)
  11775. detach(header);
  11776. if (if_block)
  11777. if_block.d();
  11778. destroy_each(each_blocks_1, detaching);
  11779. destroy_each(each_blocks, detaching);
  11780. mounted = false;
  11781. run_all(dispose);
  11782. }
  11783. };
  11784. }
  11785. function create_fragment$o(ctx) {
  11786. let previous_key = (
  11787. /*draggable*/
  11788. ctx[0]
  11789. );
  11790. let key_block_anchor;
  11791. let current;
  11792. let key_block = create_key_block(ctx);
  11793. return {
  11794. c() {
  11795. key_block.c();
  11796. key_block_anchor = empty();
  11797. },
  11798. m(target, anchor) {
  11799. key_block.m(target, anchor);
  11800. insert(target, key_block_anchor, anchor);
  11801. current = true;
  11802. },
  11803. p(ctx2, dirty) {
  11804. if (dirty[0] & /*draggable*/
  11805. 1 && safe_not_equal(previous_key, previous_key = /*draggable*/
  11806. ctx2[0])) {
  11807. group_outros();
  11808. transition_out(key_block, 1, 1, noop);
  11809. check_outros();
  11810. key_block = create_key_block(ctx2);
  11811. key_block.c();
  11812. transition_in(key_block, 1);
  11813. key_block.m(key_block_anchor.parentNode, key_block_anchor);
  11814. } else {
  11815. key_block.p(ctx2, dirty);
  11816. }
  11817. },
  11818. i(local) {
  11819. if (current)
  11820. return;
  11821. transition_in(key_block);
  11822. current = true;
  11823. },
  11824. o(local) {
  11825. transition_out(key_block);
  11826. current = false;
  11827. },
  11828. d(detaching) {
  11829. if (detaching)
  11830. detach(key_block_anchor);
  11831. key_block.d(detaching);
  11832. }
  11833. };
  11834. }
  11835. function instance$o($$self, $$props, $$invalidate) {
  11836. let $focusKeep;
  11837. let $focusAuto;
  11838. let $elementRoot;
  11839. let $storeHeaderButtons;
  11840. let $storeMinimized;
  11841. let $storeHeaderNoTitleMinimized;
  11842. let $storeDraggable;
  11843. let $storeMinimizable;
  11844. let $storeHeaderIcon;
  11845. let $storeTitle;
  11846. let { draggable: draggable$1 = void 0 } = $$props;
  11847. let { draggableOptions = void 0 } = $$props;
  11848. const { application } = getContext("#external");
  11849. const { focusAuto, focusKeep } = application.reactive.storeAppOptions;
  11850. component_subscribe($$self, focusAuto, (value) => $$invalidate(26, $focusAuto = value));
  11851. component_subscribe($$self, focusKeep, (value) => $$invalidate(25, $focusKeep = value));
  11852. const { elementRoot } = getContext("#internal").stores;
  11853. component_subscribe($$self, elementRoot, (value) => $$invalidate(27, $elementRoot = value));
  11854. const storeTitle = application.reactive.storeAppOptions.title;
  11855. component_subscribe($$self, storeTitle, (value) => $$invalidate(7, $storeTitle = value));
  11856. const storeDraggable = application.reactive.storeAppOptions.draggable;
  11857. component_subscribe($$self, storeDraggable, (value) => $$invalidate(24, $storeDraggable = value));
  11858. const storeDragging = application.reactive.storeUIState.dragging;
  11859. const storeHeaderButtons = application.reactive.storeUIState.headerButtons;
  11860. component_subscribe($$self, storeHeaderButtons, (value) => $$invalidate(21, $storeHeaderButtons = value));
  11861. const storeHeaderIcon = application.reactive.storeAppOptions.headerIcon;
  11862. component_subscribe($$self, storeHeaderIcon, (value) => $$invalidate(6, $storeHeaderIcon = value));
  11863. const storeHeaderNoTitleMinimized = application.reactive.storeAppOptions.headerNoTitleMinimized;
  11864. component_subscribe($$self, storeHeaderNoTitleMinimized, (value) => $$invalidate(23, $storeHeaderNoTitleMinimized = value));
  11865. const storeMinimizable = application.reactive.storeAppOptions.minimizable;
  11866. component_subscribe($$self, storeMinimizable, (value) => $$invalidate(5, $storeMinimizable = value));
  11867. const storeMinimized = application.reactive.storeUIState.minimized;
  11868. component_subscribe($$self, storeMinimized, (value) => $$invalidate(22, $storeMinimized = value));
  11869. const s_DRAG_TARGET_CLASSLIST = Object.freeze(["tjs-app-icon", "tjs-window-header-spacer", "window-header", "window-title"]);
  11870. let dragOptions;
  11871. let displayHeaderTitle;
  11872. let buttonsLeft;
  11873. let buttonsRight;
  11874. function minimizable(node, booleanStore) {
  11875. const callback = (event) => {
  11876. if (event.target.classList.contains("window-title") || event.target.classList.contains("window-header") || event.target.classList.contains("keep-minimized")) {
  11877. application._onToggleMinimize(event);
  11878. }
  11879. };
  11880. function activateListeners() {
  11881. node.addEventListener("dblclick", callback);
  11882. }
  11883. function removeListeners() {
  11884. node.removeEventListener("dblclick", callback);
  11885. }
  11886. if (booleanStore) {
  11887. activateListeners();
  11888. }
  11889. return {
  11890. update: (booleanStore2) => {
  11891. if (booleanStore2) {
  11892. activateListeners();
  11893. } else {
  11894. removeListeners();
  11895. }
  11896. },
  11897. destroy: () => removeListeners()
  11898. };
  11899. }
  11900. function onPointerdown(event) {
  11901. const rootEl = $elementRoot;
  11902. if ($focusAuto && rootEl instanceof HTMLElement && rootEl?.isConnected) {
  11903. if ($focusKeep) {
  11904. const focusOutside = document.activeElement instanceof HTMLElement && !rootEl.contains(document.activeElement);
  11905. if (focusOutside) {
  11906. rootEl.focus();
  11907. } else {
  11908. event.preventDefault();
  11909. }
  11910. } else {
  11911. rootEl.focus();
  11912. }
  11913. }
  11914. }
  11915. $$self.$$set = ($$props2) => {
  11916. if ("draggable" in $$props2)
  11917. $$invalidate(0, draggable$1 = $$props2.draggable);
  11918. if ("draggableOptions" in $$props2)
  11919. $$invalidate(20, draggableOptions = $$props2.draggableOptions);
  11920. };
  11921. $$self.$$.update = () => {
  11922. if ($$self.$$.dirty[0] & /*draggable*/
  11923. 1) {
  11924. $$invalidate(0, draggable$1 = typeof draggable$1 === "function" ? draggable$1 : draggable);
  11925. }
  11926. if ($$self.$$.dirty[0] & /*draggableOptions, $storeDraggable*/
  11927. 17825792) {
  11928. $$invalidate(3, dragOptions = Object.assign(
  11929. {},
  11930. {
  11931. ease: true,
  11932. easeOptions: { duration: 0.08, ease: cubicOut }
  11933. },
  11934. isObject(draggableOptions) ? draggableOptions : {},
  11935. {
  11936. position: application.position,
  11937. active: $storeDraggable,
  11938. storeDragging,
  11939. hasTargetClassList: s_DRAG_TARGET_CLASSLIST
  11940. }
  11941. ));
  11942. }
  11943. if ($$self.$$.dirty[0] & /*$storeHeaderNoTitleMinimized, $storeMinimized*/
  11944. 12582912) {
  11945. $$invalidate(4, displayHeaderTitle = $storeHeaderNoTitleMinimized && $storeMinimized ? "none" : null);
  11946. }
  11947. if ($$self.$$.dirty[0] & /*$storeHeaderButtons, buttonsLeft, buttonsRight*/
  11948. 2097158) {
  11949. {
  11950. $$invalidate(1, buttonsLeft = []);
  11951. $$invalidate(2, buttonsRight = []);
  11952. for (const button of $storeHeaderButtons) {
  11953. const buttonsList = typeof button?.alignLeft === "boolean" && button?.alignLeft ? buttonsLeft : buttonsRight;
  11954. buttonsList.push(isSvelteComponent(button) ? { class: button, props: {} } : {
  11955. class: TJSHeaderButton,
  11956. props: { button }
  11957. });
  11958. }
  11959. }
  11960. }
  11961. };
  11962. return [
  11963. draggable$1,
  11964. buttonsLeft,
  11965. buttonsRight,
  11966. dragOptions,
  11967. displayHeaderTitle,
  11968. $storeMinimizable,
  11969. $storeHeaderIcon,
  11970. $storeTitle,
  11971. focusAuto,
  11972. focusKeep,
  11973. elementRoot,
  11974. storeTitle,
  11975. storeDraggable,
  11976. storeHeaderButtons,
  11977. storeHeaderIcon,
  11978. storeHeaderNoTitleMinimized,
  11979. storeMinimizable,
  11980. storeMinimized,
  11981. minimizable,
  11982. onPointerdown,
  11983. draggableOptions,
  11984. $storeHeaderButtons,
  11985. $storeMinimized,
  11986. $storeHeaderNoTitleMinimized,
  11987. $storeDraggable
  11988. ];
  11989. }
  11990. class TJSApplicationHeader extends SvelteComponent {
  11991. constructor(options) {
  11992. super();
  11993. init(this, options, instance$o, create_fragment$o, safe_not_equal, { draggable: 0, draggableOptions: 20 }, null, [-1, -1]);
  11994. }
  11995. }
  11996. const TJSFocusWrap_svelte_svelte_type_style_lang = "";
  11997. function create_fragment$n(ctx) {
  11998. let div;
  11999. let mounted;
  12000. let dispose;
  12001. return {
  12002. c() {
  12003. div = element("div");
  12004. attr(div, "class", "tjs-focus-wrap svelte-ese-kjcljd");
  12005. attr(div, "tabindex", "0");
  12006. },
  12007. m(target, anchor) {
  12008. insert(target, div, anchor);
  12009. ctx[4](div);
  12010. if (!mounted) {
  12011. dispose = listen(
  12012. div,
  12013. "focus",
  12014. /*onFocus*/
  12015. ctx[1]
  12016. );
  12017. mounted = true;
  12018. }
  12019. },
  12020. p: noop,
  12021. i: noop,
  12022. o: noop,
  12023. d(detaching) {
  12024. if (detaching)
  12025. detach(div);
  12026. ctx[4](null);
  12027. mounted = false;
  12028. dispose();
  12029. }
  12030. };
  12031. }
  12032. function instance$n($$self, $$props, $$invalidate) {
  12033. let { elementRoot = void 0 } = $$props;
  12034. let { enabled = true } = $$props;
  12035. let ignoreElements, wrapEl;
  12036. function onFocus() {
  12037. if (!enabled) {
  12038. return;
  12039. }
  12040. if (elementRoot instanceof HTMLElement) {
  12041. const firstFocusEl = A11yHelper.getFirstFocusableElement(elementRoot, ignoreElements);
  12042. if (firstFocusEl instanceof HTMLElement && firstFocusEl !== wrapEl) {
  12043. firstFocusEl.focus();
  12044. } else {
  12045. elementRoot.focus();
  12046. }
  12047. }
  12048. }
  12049. function div_binding($$value) {
  12050. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12051. wrapEl = $$value;
  12052. $$invalidate(0, wrapEl);
  12053. });
  12054. }
  12055. $$self.$$set = ($$props2) => {
  12056. if ("elementRoot" in $$props2)
  12057. $$invalidate(2, elementRoot = $$props2.elementRoot);
  12058. if ("enabled" in $$props2)
  12059. $$invalidate(3, enabled = $$props2.enabled);
  12060. };
  12061. $$self.$$.update = () => {
  12062. if ($$self.$$.dirty & /*wrapEl*/
  12063. 1) {
  12064. if (wrapEl) {
  12065. ignoreElements = /* @__PURE__ */ new Set([wrapEl]);
  12066. }
  12067. }
  12068. };
  12069. return [wrapEl, onFocus, elementRoot, enabled, div_binding];
  12070. }
  12071. class TJSFocusWrap extends SvelteComponent {
  12072. constructor(options) {
  12073. super();
  12074. init(this, options, instance$n, create_fragment$n, safe_not_equal, { elementRoot: 2, enabled: 3 });
  12075. }
  12076. }
  12077. function create_fragment$m(ctx) {
  12078. let div;
  12079. let resizable_action;
  12080. let mounted;
  12081. let dispose;
  12082. return {
  12083. c() {
  12084. div = element("div");
  12085. div.innerHTML = `<i class="fas fa-arrows-alt-h"></i>`;
  12086. attr(div, "class", "window-resizable-handle");
  12087. },
  12088. m(target, anchor) {
  12089. insert(target, div, anchor);
  12090. ctx[10](div);
  12091. if (!mounted) {
  12092. dispose = action_destroyer(resizable_action = /*resizable*/
  12093. ctx[6].call(null, div, {
  12094. active: (
  12095. /*$storeResizable*/
  12096. ctx[1]
  12097. ),
  12098. storeResizing: (
  12099. /*storeResizing*/
  12100. ctx[5]
  12101. )
  12102. }));
  12103. mounted = true;
  12104. }
  12105. },
  12106. p(ctx2, [dirty]) {
  12107. if (resizable_action && is_function(resizable_action.update) && dirty & /*$storeResizable*/
  12108. 2)
  12109. resizable_action.update.call(null, {
  12110. active: (
  12111. /*$storeResizable*/
  12112. ctx2[1]
  12113. ),
  12114. storeResizing: (
  12115. /*storeResizing*/
  12116. ctx2[5]
  12117. )
  12118. });
  12119. },
  12120. i: noop,
  12121. o: noop,
  12122. d(detaching) {
  12123. if (detaching)
  12124. detach(div);
  12125. ctx[10](null);
  12126. mounted = false;
  12127. dispose();
  12128. }
  12129. };
  12130. }
  12131. function instance$m($$self, $$props, $$invalidate) {
  12132. let $storeElementRoot;
  12133. let $storeMinimized;
  12134. let $storeResizable;
  12135. let { isResizable = false } = $$props;
  12136. const application = getContext("#external").application;
  12137. const storeElementRoot = getContext("storeElementRoot");
  12138. component_subscribe($$self, storeElementRoot, (value) => $$invalidate(8, $storeElementRoot = value));
  12139. const storeResizable = application.reactive.storeAppOptions.resizable;
  12140. component_subscribe($$self, storeResizable, (value) => $$invalidate(1, $storeResizable = value));
  12141. const storeMinimized = application.reactive.storeUIState.minimized;
  12142. component_subscribe($$self, storeMinimized, (value) => $$invalidate(9, $storeMinimized = value));
  12143. const storeResizing = application.reactive.storeUIState.resizing;
  12144. let elementResize;
  12145. function resizable(node, { active: active2 = true, storeResizing: storeResizing2 = void 0 } = {}) {
  12146. let position = null;
  12147. let initialPosition = {};
  12148. let resizing = false;
  12149. const handlers = {
  12150. resizeDown: ["pointerdown", (e) => onResizePointerDown(e), false],
  12151. resizeMove: ["pointermove", (e) => onResizePointerMove(e), false],
  12152. resizeUp: ["pointerup", (e) => onResizePointerUp(e), false]
  12153. };
  12154. function activateListeners() {
  12155. node.addEventListener(...handlers.resizeDown);
  12156. $$invalidate(7, isResizable = true);
  12157. node.style.display = "block";
  12158. }
  12159. function removeListeners() {
  12160. if (typeof storeResizing2?.set === "function") {
  12161. storeResizing2.set(false);
  12162. }
  12163. node.removeEventListener(...handlers.resizeDown);
  12164. node.removeEventListener(...handlers.resizeMove);
  12165. node.removeEventListener(...handlers.resizeUp);
  12166. node.style.display = "none";
  12167. $$invalidate(7, isResizable = false);
  12168. }
  12169. if (active2) {
  12170. activateListeners();
  12171. } else {
  12172. node.style.display = "none";
  12173. }
  12174. function onResizePointerDown(event) {
  12175. event.preventDefault();
  12176. resizing = false;
  12177. position = application.position.get();
  12178. if (position.height === "auto") {
  12179. position.height = $storeElementRoot.clientHeight;
  12180. }
  12181. if (position.width === "auto") {
  12182. position.width = $storeElementRoot.clientWidth;
  12183. }
  12184. initialPosition = { x: event.clientX, y: event.clientY };
  12185. node.addEventListener(...handlers.resizeMove);
  12186. node.addEventListener(...handlers.resizeUp);
  12187. node.setPointerCapture(event.pointerId);
  12188. }
  12189. function onResizePointerMove(event) {
  12190. event.preventDefault();
  12191. if (!resizing && typeof storeResizing2?.set === "function") {
  12192. resizing = true;
  12193. storeResizing2.set(true);
  12194. }
  12195. application.position.set({
  12196. width: position.width + (event.clientX - initialPosition.x),
  12197. height: position.height + (event.clientY - initialPosition.y)
  12198. });
  12199. }
  12200. function onResizePointerUp(event) {
  12201. resizing = false;
  12202. if (typeof storeResizing2?.set === "function") {
  12203. storeResizing2.set(false);
  12204. }
  12205. event.preventDefault();
  12206. node.removeEventListener(...handlers.resizeMove);
  12207. node.removeEventListener(...handlers.resizeUp);
  12208. application._onResize(event);
  12209. }
  12210. return {
  12211. update: ({ active: active3 }) => {
  12212. if (active3) {
  12213. activateListeners();
  12214. } else {
  12215. removeListeners();
  12216. }
  12217. },
  12218. destroy: () => removeListeners()
  12219. };
  12220. }
  12221. function div_binding($$value) {
  12222. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12223. elementResize = $$value;
  12224. $$invalidate(0, elementResize), $$invalidate(7, isResizable), $$invalidate(9, $storeMinimized), $$invalidate(8, $storeElementRoot);
  12225. });
  12226. }
  12227. $$self.$$set = ($$props2) => {
  12228. if ("isResizable" in $$props2)
  12229. $$invalidate(7, isResizable = $$props2.isResizable);
  12230. };
  12231. $$self.$$.update = () => {
  12232. if ($$self.$$.dirty & /*elementResize, isResizable, $storeMinimized, $storeElementRoot*/
  12233. 897) {
  12234. if (elementResize) {
  12235. $$invalidate(0, elementResize.style.display = isResizable && !$storeMinimized ? "block" : "none", elementResize);
  12236. const elementRoot = $storeElementRoot;
  12237. if (elementRoot) {
  12238. elementRoot.classList[isResizable ? "add" : "remove"]("resizable");
  12239. }
  12240. }
  12241. }
  12242. };
  12243. return [
  12244. elementResize,
  12245. $storeResizable,
  12246. storeElementRoot,
  12247. storeResizable,
  12248. storeMinimized,
  12249. storeResizing,
  12250. resizable,
  12251. isResizable,
  12252. $storeElementRoot,
  12253. $storeMinimized,
  12254. div_binding
  12255. ];
  12256. }
  12257. class ResizableHandle extends SvelteComponent {
  12258. constructor(options) {
  12259. super();
  12260. init(this, options, instance$m, create_fragment$m, safe_not_equal, { isResizable: 7 });
  12261. }
  12262. }
  12263. const ApplicationShell_svelte_svelte_type_style_lang = "";
  12264. function create_else_block$2(ctx) {
  12265. let div;
  12266. let tjsapplicationheader;
  12267. let t0;
  12268. let section;
  12269. let applyStyles_action;
  12270. let t1;
  12271. let resizablehandle;
  12272. let t2;
  12273. let tjsfocuswrap;
  12274. let div_id_value;
  12275. let div_class_value;
  12276. let div_data_appid_value;
  12277. let applyStyles_action_1;
  12278. let current;
  12279. let mounted;
  12280. let dispose;
  12281. tjsapplicationheader = new TJSApplicationHeader({
  12282. props: {
  12283. draggable: (
  12284. /*draggable*/
  12285. ctx[6]
  12286. ),
  12287. draggableOptions: (
  12288. /*draggableOptions*/
  12289. ctx[7]
  12290. )
  12291. }
  12292. });
  12293. const default_slot_template = (
  12294. /*#slots*/
  12295. ctx[36].default
  12296. );
  12297. const default_slot = create_slot(
  12298. default_slot_template,
  12299. ctx,
  12300. /*$$scope*/
  12301. ctx[35],
  12302. null
  12303. );
  12304. resizablehandle = new ResizableHandle({});
  12305. tjsfocuswrap = new TJSFocusWrap({
  12306. props: {
  12307. elementRoot: (
  12308. /*elementRoot*/
  12309. ctx[1]
  12310. ),
  12311. enabled: (
  12312. /*focusWrapEnabled*/
  12313. ctx[11]
  12314. )
  12315. }
  12316. });
  12317. return {
  12318. c() {
  12319. div = element("div");
  12320. create_component(tjsapplicationheader.$$.fragment);
  12321. t0 = space();
  12322. section = element("section");
  12323. if (default_slot)
  12324. default_slot.c();
  12325. t1 = space();
  12326. create_component(resizablehandle.$$.fragment);
  12327. t2 = space();
  12328. create_component(tjsfocuswrap.$$.fragment);
  12329. attr(section, "class", "window-content svelte-ese-oz81f7");
  12330. attr(section, "tabindex", "-1");
  12331. attr(div, "id", div_id_value = /*application*/
  12332. ctx[10].id);
  12333. attr(div, "class", div_class_value = "app window-app " + /*application*/
  12334. ctx[10].options.classes.join(" ") + " svelte-ese-oz81f7");
  12335. attr(div, "data-appid", div_data_appid_value = /*application*/
  12336. ctx[10].appId);
  12337. attr(div, "tabindex", "-1");
  12338. },
  12339. m(target, anchor) {
  12340. insert(target, div, anchor);
  12341. mount_component(tjsapplicationheader, div, null);
  12342. append(div, t0);
  12343. append(div, section);
  12344. if (default_slot) {
  12345. default_slot.m(section, null);
  12346. }
  12347. ctx[39](section);
  12348. append(div, t1);
  12349. mount_component(resizablehandle, div, null);
  12350. append(div, t2);
  12351. mount_component(tjsfocuswrap, div, null);
  12352. ctx[40](div);
  12353. current = true;
  12354. if (!mounted) {
  12355. dispose = [
  12356. listen(
  12357. section,
  12358. "pointerdown",
  12359. /*onPointerdownContent*/
  12360. ctx[21]
  12361. ),
  12362. action_destroyer(applyStyles_action = applyStyles.call(
  12363. null,
  12364. section,
  12365. /*stylesContent*/
  12366. ctx[9]
  12367. )),
  12368. action_destroyer(
  12369. /*contentResizeObserver*/
  12370. ctx[13].call(
  12371. null,
  12372. section,
  12373. /*resizeObservedContent*/
  12374. ctx[22]
  12375. )
  12376. ),
  12377. listen(div, "close:popup", stop_propagation(prevent_default(
  12378. /*onClosePopup*/
  12379. ctx[18]
  12380. ))),
  12381. listen(
  12382. div,
  12383. "keydown",
  12384. /*onKeydown*/
  12385. ctx[19],
  12386. true
  12387. ),
  12388. listen(
  12389. div,
  12390. "pointerdown",
  12391. /*onPointerdownApp*/
  12392. ctx[20]
  12393. ),
  12394. action_destroyer(applyStyles_action_1 = applyStyles.call(
  12395. null,
  12396. div,
  12397. /*stylesApp*/
  12398. ctx[8]
  12399. )),
  12400. action_destroyer(
  12401. /*appResizeObserver*/
  12402. ctx[12].call(
  12403. null,
  12404. div,
  12405. /*resizeObservedApp*/
  12406. ctx[23]
  12407. )
  12408. )
  12409. ];
  12410. mounted = true;
  12411. }
  12412. },
  12413. p(ctx2, dirty) {
  12414. const tjsapplicationheader_changes = {};
  12415. if (dirty[0] & /*draggable*/
  12416. 64)
  12417. tjsapplicationheader_changes.draggable = /*draggable*/
  12418. ctx2[6];
  12419. if (dirty[0] & /*draggableOptions*/
  12420. 128)
  12421. tjsapplicationheader_changes.draggableOptions = /*draggableOptions*/
  12422. ctx2[7];
  12423. tjsapplicationheader.$set(tjsapplicationheader_changes);
  12424. if (default_slot) {
  12425. if (default_slot.p && (!current || dirty[1] & /*$$scope*/
  12426. 16)) {
  12427. update_slot_base(
  12428. default_slot,
  12429. default_slot_template,
  12430. ctx2,
  12431. /*$$scope*/
  12432. ctx2[35],
  12433. !current ? get_all_dirty_from_scope(
  12434. /*$$scope*/
  12435. ctx2[35]
  12436. ) : get_slot_changes(
  12437. default_slot_template,
  12438. /*$$scope*/
  12439. ctx2[35],
  12440. dirty,
  12441. null
  12442. ),
  12443. null
  12444. );
  12445. }
  12446. }
  12447. if (applyStyles_action && is_function(applyStyles_action.update) && dirty[0] & /*stylesContent*/
  12448. 512)
  12449. applyStyles_action.update.call(
  12450. null,
  12451. /*stylesContent*/
  12452. ctx2[9]
  12453. );
  12454. const tjsfocuswrap_changes = {};
  12455. if (dirty[0] & /*elementRoot*/
  12456. 2)
  12457. tjsfocuswrap_changes.elementRoot = /*elementRoot*/
  12458. ctx2[1];
  12459. if (dirty[0] & /*focusWrapEnabled*/
  12460. 2048)
  12461. tjsfocuswrap_changes.enabled = /*focusWrapEnabled*/
  12462. ctx2[11];
  12463. tjsfocuswrap.$set(tjsfocuswrap_changes);
  12464. if (!current || dirty[0] & /*application*/
  12465. 1024 && div_id_value !== (div_id_value = /*application*/
  12466. ctx2[10].id)) {
  12467. attr(div, "id", div_id_value);
  12468. }
  12469. if (!current || dirty[0] & /*application*/
  12470. 1024 && div_class_value !== (div_class_value = "app window-app " + /*application*/
  12471. ctx2[10].options.classes.join(" ") + " svelte-ese-oz81f7")) {
  12472. attr(div, "class", div_class_value);
  12473. }
  12474. if (!current || dirty[0] & /*application*/
  12475. 1024 && div_data_appid_value !== (div_data_appid_value = /*application*/
  12476. ctx2[10].appId)) {
  12477. attr(div, "data-appid", div_data_appid_value);
  12478. }
  12479. if (applyStyles_action_1 && is_function(applyStyles_action_1.update) && dirty[0] & /*stylesApp*/
  12480. 256)
  12481. applyStyles_action_1.update.call(
  12482. null,
  12483. /*stylesApp*/
  12484. ctx2[8]
  12485. );
  12486. },
  12487. i(local) {
  12488. if (current)
  12489. return;
  12490. transition_in(tjsapplicationheader.$$.fragment, local);
  12491. transition_in(default_slot, local);
  12492. transition_in(resizablehandle.$$.fragment, local);
  12493. transition_in(tjsfocuswrap.$$.fragment, local);
  12494. current = true;
  12495. },
  12496. o(local) {
  12497. transition_out(tjsapplicationheader.$$.fragment, local);
  12498. transition_out(default_slot, local);
  12499. transition_out(resizablehandle.$$.fragment, local);
  12500. transition_out(tjsfocuswrap.$$.fragment, local);
  12501. current = false;
  12502. },
  12503. d(detaching) {
  12504. if (detaching)
  12505. detach(div);
  12506. destroy_component(tjsapplicationheader);
  12507. if (default_slot)
  12508. default_slot.d(detaching);
  12509. ctx[39](null);
  12510. destroy_component(resizablehandle);
  12511. destroy_component(tjsfocuswrap);
  12512. ctx[40](null);
  12513. mounted = false;
  12514. run_all(dispose);
  12515. }
  12516. };
  12517. }
  12518. function create_if_block$8(ctx) {
  12519. let div;
  12520. let tjsapplicationheader;
  12521. let t0;
  12522. let section;
  12523. let applyStyles_action;
  12524. let t1;
  12525. let resizablehandle;
  12526. let t2;
  12527. let tjsfocuswrap;
  12528. let div_id_value;
  12529. let div_class_value;
  12530. let div_data_appid_value;
  12531. let applyStyles_action_1;
  12532. let div_intro;
  12533. let div_outro;
  12534. let current;
  12535. let mounted;
  12536. let dispose;
  12537. tjsapplicationheader = new TJSApplicationHeader({
  12538. props: {
  12539. draggable: (
  12540. /*draggable*/
  12541. ctx[6]
  12542. ),
  12543. draggableOptions: (
  12544. /*draggableOptions*/
  12545. ctx[7]
  12546. )
  12547. }
  12548. });
  12549. const default_slot_template = (
  12550. /*#slots*/
  12551. ctx[36].default
  12552. );
  12553. const default_slot = create_slot(
  12554. default_slot_template,
  12555. ctx,
  12556. /*$$scope*/
  12557. ctx[35],
  12558. null
  12559. );
  12560. resizablehandle = new ResizableHandle({});
  12561. tjsfocuswrap = new TJSFocusWrap({
  12562. props: { elementRoot: (
  12563. /*elementRoot*/
  12564. ctx[1]
  12565. ) }
  12566. });
  12567. return {
  12568. c() {
  12569. div = element("div");
  12570. create_component(tjsapplicationheader.$$.fragment);
  12571. t0 = space();
  12572. section = element("section");
  12573. if (default_slot)
  12574. default_slot.c();
  12575. t1 = space();
  12576. create_component(resizablehandle.$$.fragment);
  12577. t2 = space();
  12578. create_component(tjsfocuswrap.$$.fragment);
  12579. attr(section, "class", "window-content svelte-ese-oz81f7");
  12580. attr(section, "tabindex", "-1");
  12581. attr(div, "id", div_id_value = /*application*/
  12582. ctx[10].id);
  12583. attr(div, "class", div_class_value = "app window-app " + /*application*/
  12584. ctx[10].options.classes.join(" ") + " svelte-ese-oz81f7");
  12585. attr(div, "data-appid", div_data_appid_value = /*application*/
  12586. ctx[10].appId);
  12587. attr(div, "tabindex", "-1");
  12588. },
  12589. m(target, anchor) {
  12590. insert(target, div, anchor);
  12591. mount_component(tjsapplicationheader, div, null);
  12592. append(div, t0);
  12593. append(div, section);
  12594. if (default_slot) {
  12595. default_slot.m(section, null);
  12596. }
  12597. ctx[37](section);
  12598. append(div, t1);
  12599. mount_component(resizablehandle, div, null);
  12600. append(div, t2);
  12601. mount_component(tjsfocuswrap, div, null);
  12602. ctx[38](div);
  12603. current = true;
  12604. if (!mounted) {
  12605. dispose = [
  12606. listen(
  12607. section,
  12608. "pointerdown",
  12609. /*onPointerdownContent*/
  12610. ctx[21]
  12611. ),
  12612. action_destroyer(applyStyles_action = applyStyles.call(
  12613. null,
  12614. section,
  12615. /*stylesContent*/
  12616. ctx[9]
  12617. )),
  12618. action_destroyer(
  12619. /*contentResizeObserver*/
  12620. ctx[13].call(
  12621. null,
  12622. section,
  12623. /*resizeObservedContent*/
  12624. ctx[22]
  12625. )
  12626. ),
  12627. listen(div, "close:popup", stop_propagation(prevent_default(
  12628. /*onClosePopup*/
  12629. ctx[18]
  12630. ))),
  12631. listen(
  12632. div,
  12633. "keydown",
  12634. /*onKeydown*/
  12635. ctx[19],
  12636. true
  12637. ),
  12638. listen(
  12639. div,
  12640. "pointerdown",
  12641. /*onPointerdownApp*/
  12642. ctx[20]
  12643. ),
  12644. action_destroyer(applyStyles_action_1 = applyStyles.call(
  12645. null,
  12646. div,
  12647. /*stylesApp*/
  12648. ctx[8]
  12649. )),
  12650. action_destroyer(
  12651. /*appResizeObserver*/
  12652. ctx[12].call(
  12653. null,
  12654. div,
  12655. /*resizeObservedApp*/
  12656. ctx[23]
  12657. )
  12658. )
  12659. ];
  12660. mounted = true;
  12661. }
  12662. },
  12663. p(new_ctx, dirty) {
  12664. ctx = new_ctx;
  12665. const tjsapplicationheader_changes = {};
  12666. if (dirty[0] & /*draggable*/
  12667. 64)
  12668. tjsapplicationheader_changes.draggable = /*draggable*/
  12669. ctx[6];
  12670. if (dirty[0] & /*draggableOptions*/
  12671. 128)
  12672. tjsapplicationheader_changes.draggableOptions = /*draggableOptions*/
  12673. ctx[7];
  12674. tjsapplicationheader.$set(tjsapplicationheader_changes);
  12675. if (default_slot) {
  12676. if (default_slot.p && (!current || dirty[1] & /*$$scope*/
  12677. 16)) {
  12678. update_slot_base(
  12679. default_slot,
  12680. default_slot_template,
  12681. ctx,
  12682. /*$$scope*/
  12683. ctx[35],
  12684. !current ? get_all_dirty_from_scope(
  12685. /*$$scope*/
  12686. ctx[35]
  12687. ) : get_slot_changes(
  12688. default_slot_template,
  12689. /*$$scope*/
  12690. ctx[35],
  12691. dirty,
  12692. null
  12693. ),
  12694. null
  12695. );
  12696. }
  12697. }
  12698. if (applyStyles_action && is_function(applyStyles_action.update) && dirty[0] & /*stylesContent*/
  12699. 512)
  12700. applyStyles_action.update.call(
  12701. null,
  12702. /*stylesContent*/
  12703. ctx[9]
  12704. );
  12705. const tjsfocuswrap_changes = {};
  12706. if (dirty[0] & /*elementRoot*/
  12707. 2)
  12708. tjsfocuswrap_changes.elementRoot = /*elementRoot*/
  12709. ctx[1];
  12710. tjsfocuswrap.$set(tjsfocuswrap_changes);
  12711. if (!current || dirty[0] & /*application*/
  12712. 1024 && div_id_value !== (div_id_value = /*application*/
  12713. ctx[10].id)) {
  12714. attr(div, "id", div_id_value);
  12715. }
  12716. if (!current || dirty[0] & /*application*/
  12717. 1024 && div_class_value !== (div_class_value = "app window-app " + /*application*/
  12718. ctx[10].options.classes.join(" ") + " svelte-ese-oz81f7")) {
  12719. attr(div, "class", div_class_value);
  12720. }
  12721. if (!current || dirty[0] & /*application*/
  12722. 1024 && div_data_appid_value !== (div_data_appid_value = /*application*/
  12723. ctx[10].appId)) {
  12724. attr(div, "data-appid", div_data_appid_value);
  12725. }
  12726. if (applyStyles_action_1 && is_function(applyStyles_action_1.update) && dirty[0] & /*stylesApp*/
  12727. 256)
  12728. applyStyles_action_1.update.call(
  12729. null,
  12730. /*stylesApp*/
  12731. ctx[8]
  12732. );
  12733. },
  12734. i(local) {
  12735. if (current)
  12736. return;
  12737. transition_in(tjsapplicationheader.$$.fragment, local);
  12738. transition_in(default_slot, local);
  12739. transition_in(resizablehandle.$$.fragment, local);
  12740. transition_in(tjsfocuswrap.$$.fragment, local);
  12741. add_render_callback(() => {
  12742. if (div_outro)
  12743. div_outro.end(1);
  12744. div_intro = create_in_transition(
  12745. div,
  12746. /*inTransition*/
  12747. ctx[2],
  12748. /*inTransitionOptions*/
  12749. ctx[4]
  12750. );
  12751. div_intro.start();
  12752. });
  12753. current = true;
  12754. },
  12755. o(local) {
  12756. transition_out(tjsapplicationheader.$$.fragment, local);
  12757. transition_out(default_slot, local);
  12758. transition_out(resizablehandle.$$.fragment, local);
  12759. transition_out(tjsfocuswrap.$$.fragment, local);
  12760. if (div_intro)
  12761. div_intro.invalidate();
  12762. div_outro = create_out_transition(
  12763. div,
  12764. /*outTransition*/
  12765. ctx[3],
  12766. /*outTransitionOptions*/
  12767. ctx[5]
  12768. );
  12769. current = false;
  12770. },
  12771. d(detaching) {
  12772. if (detaching)
  12773. detach(div);
  12774. destroy_component(tjsapplicationheader);
  12775. if (default_slot)
  12776. default_slot.d(detaching);
  12777. ctx[37](null);
  12778. destroy_component(resizablehandle);
  12779. destroy_component(tjsfocuswrap);
  12780. ctx[38](null);
  12781. if (detaching && div_outro)
  12782. div_outro.end();
  12783. mounted = false;
  12784. run_all(dispose);
  12785. }
  12786. };
  12787. }
  12788. function create_fragment$l(ctx) {
  12789. let current_block_type_index;
  12790. let if_block;
  12791. let if_block_anchor;
  12792. let current;
  12793. const if_block_creators = [create_if_block$8, create_else_block$2];
  12794. const if_blocks = [];
  12795. function select_block_type(ctx2, dirty) {
  12796. if (
  12797. /*inTransition*/
  12798. ctx2[2] || /*outTransition*/
  12799. ctx2[3]
  12800. )
  12801. return 0;
  12802. return 1;
  12803. }
  12804. current_block_type_index = select_block_type(ctx);
  12805. if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
  12806. return {
  12807. c() {
  12808. if_block.c();
  12809. if_block_anchor = empty();
  12810. },
  12811. m(target, anchor) {
  12812. if_blocks[current_block_type_index].m(target, anchor);
  12813. insert(target, if_block_anchor, anchor);
  12814. current = true;
  12815. },
  12816. p(ctx2, dirty) {
  12817. let previous_block_index = current_block_type_index;
  12818. current_block_type_index = select_block_type(ctx2);
  12819. if (current_block_type_index === previous_block_index) {
  12820. if_blocks[current_block_type_index].p(ctx2, dirty);
  12821. } else {
  12822. group_outros();
  12823. transition_out(if_blocks[previous_block_index], 1, 1, () => {
  12824. if_blocks[previous_block_index] = null;
  12825. });
  12826. check_outros();
  12827. if_block = if_blocks[current_block_type_index];
  12828. if (!if_block) {
  12829. if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
  12830. if_block.c();
  12831. } else {
  12832. if_block.p(ctx2, dirty);
  12833. }
  12834. transition_in(if_block, 1);
  12835. if_block.m(if_block_anchor.parentNode, if_block_anchor);
  12836. }
  12837. },
  12838. i(local) {
  12839. if (current)
  12840. return;
  12841. transition_in(if_block);
  12842. current = true;
  12843. },
  12844. o(local) {
  12845. transition_out(if_block);
  12846. current = false;
  12847. },
  12848. d(detaching) {
  12849. if_blocks[current_block_type_index].d(detaching);
  12850. if (detaching)
  12851. detach(if_block_anchor);
  12852. }
  12853. };
  12854. }
  12855. function instance$l($$self, $$props, $$invalidate) {
  12856. let $focusKeep;
  12857. let $focusAuto;
  12858. let $minimized;
  12859. let $focusTrap;
  12860. let { $$slots: slots = {}, $$scope } = $$props;
  12861. let { elementContent = void 0 } = $$props;
  12862. let { elementRoot = void 0 } = $$props;
  12863. let { draggable: draggable2 = void 0 } = $$props;
  12864. let { draggableOptions = void 0 } = $$props;
  12865. let { stylesApp = void 0 } = $$props;
  12866. let { stylesContent = void 0 } = $$props;
  12867. let { appOffsetHeight = false } = $$props;
  12868. let { appOffsetWidth = false } = $$props;
  12869. const appResizeObserver = !!appOffsetHeight || !!appOffsetWidth ? resizeObserver : () => null;
  12870. let { contentOffsetHeight = false } = $$props;
  12871. let { contentOffsetWidth = false } = $$props;
  12872. const contentResizeObserver = !!contentOffsetHeight || !!contentOffsetWidth ? resizeObserver : () => null;
  12873. const internal = new AppShellContextInternal();
  12874. const s_IGNORE_CLASSES = { ignoreClasses: ["tjs-focus-wrap"] };
  12875. setContext("#internal", internal);
  12876. const { application } = getContext("#external");
  12877. const { focusAuto, focusKeep, focusTrap } = application.reactive.storeAppOptions;
  12878. component_subscribe($$self, focusAuto, (value) => $$invalidate(32, $focusAuto = value));
  12879. component_subscribe($$self, focusKeep, (value) => $$invalidate(41, $focusKeep = value));
  12880. component_subscribe($$self, focusTrap, (value) => $$invalidate(34, $focusTrap = value));
  12881. const { minimized } = application.reactive.storeUIState;
  12882. component_subscribe($$self, minimized, (value) => $$invalidate(33, $minimized = value));
  12883. let focusWrapEnabled;
  12884. let { transition = void 0 } = $$props;
  12885. let { inTransition = void 0 } = $$props;
  12886. let { outTransition = void 0 } = $$props;
  12887. let { transitionOptions = void 0 } = $$props;
  12888. let { inTransitionOptions = s_DEFAULT_TRANSITION_OPTIONS } = $$props;
  12889. let { outTransitionOptions = s_DEFAULT_TRANSITION_OPTIONS } = $$props;
  12890. let oldTransition = void 0;
  12891. let oldTransitionOptions = void 0;
  12892. onMount(() => elementRoot.focus());
  12893. function onClosePopup(event) {
  12894. if (!$focusAuto) {
  12895. return;
  12896. }
  12897. const targetEl = event?.detail?.target;
  12898. if (!(targetEl instanceof HTMLElement)) {
  12899. return;
  12900. }
  12901. if (A11yHelper.isFocusable(targetEl)) {
  12902. return;
  12903. }
  12904. const elementRootContains = elementRoot.contains(targetEl);
  12905. if (targetEl === elementRoot) {
  12906. elementRoot.focus();
  12907. } else if (targetEl === elementContent) {
  12908. elementContent.focus();
  12909. } else if (elementRootContains) {
  12910. if (elementContent.contains(targetEl)) {
  12911. elementContent.focus();
  12912. } else {
  12913. elementRoot.focus();
  12914. }
  12915. }
  12916. }
  12917. function onKeydown(event) {
  12918. if (focusWrapEnabled && event.shiftKey && event.code === "Tab") {
  12919. const allFocusable = A11yHelper.getFocusableElements(elementRoot, s_IGNORE_CLASSES);
  12920. const firstFocusEl = allFocusable.length > 0 ? allFocusable[0] : void 0;
  12921. const lastFocusEl = allFocusable.length > 0 ? allFocusable[allFocusable.length - 1] : void 0;
  12922. if (elementRoot === document.activeElement || firstFocusEl === document.activeElement) {
  12923. if (lastFocusEl instanceof HTMLElement && firstFocusEl !== lastFocusEl) {
  12924. lastFocusEl.focus();
  12925. }
  12926. event.preventDefault();
  12927. event.stopPropagation();
  12928. }
  12929. }
  12930. if (typeof application?.options?.popOut === "boolean" && application.options.popOut && application !== globalThis.ui?.activeWindow) {
  12931. application.bringToTop.call(application);
  12932. }
  12933. }
  12934. function onPointerdownApp() {
  12935. if (typeof application?.options?.popOut === "boolean" && application.options.popOut && application !== globalThis.ui?.activeWindow) {
  12936. application.bringToTop.call(application);
  12937. }
  12938. }
  12939. function onPointerdownContent(event) {
  12940. const focusable = A11yHelper.isFocusable(event.target);
  12941. if (!focusable && $focusAuto) {
  12942. if ($focusKeep) {
  12943. const focusOutside = document.activeElement instanceof HTMLElement && !elementRoot.contains(document.activeElement);
  12944. if (focusOutside) {
  12945. elementContent.focus();
  12946. } else {
  12947. event.preventDefault();
  12948. }
  12949. } else {
  12950. elementContent.focus();
  12951. }
  12952. }
  12953. }
  12954. function resizeObservedContent(offsetWidth, offsetHeight) {
  12955. $$invalidate(27, contentOffsetWidth = offsetWidth);
  12956. $$invalidate(26, contentOffsetHeight = offsetHeight);
  12957. }
  12958. function resizeObservedApp(offsetWidth, offsetHeight, contentWidth, contentHeight) {
  12959. application.position.stores.resizeObserved.update((object) => {
  12960. object.contentWidth = contentWidth;
  12961. object.contentHeight = contentHeight;
  12962. object.offsetWidth = offsetWidth;
  12963. object.offsetHeight = offsetHeight;
  12964. return object;
  12965. });
  12966. $$invalidate(24, appOffsetHeight = offsetHeight);
  12967. $$invalidate(25, appOffsetWidth = offsetWidth);
  12968. }
  12969. function section_binding($$value) {
  12970. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12971. elementContent = $$value;
  12972. $$invalidate(0, elementContent);
  12973. });
  12974. }
  12975. function div_binding($$value) {
  12976. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12977. elementRoot = $$value;
  12978. $$invalidate(1, elementRoot);
  12979. });
  12980. }
  12981. function section_binding_1($$value) {
  12982. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12983. elementContent = $$value;
  12984. $$invalidate(0, elementContent);
  12985. });
  12986. }
  12987. function div_binding_1($$value) {
  12988. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12989. elementRoot = $$value;
  12990. $$invalidate(1, elementRoot);
  12991. });
  12992. }
  12993. $$self.$$set = ($$props2) => {
  12994. if ("elementContent" in $$props2)
  12995. $$invalidate(0, elementContent = $$props2.elementContent);
  12996. if ("elementRoot" in $$props2)
  12997. $$invalidate(1, elementRoot = $$props2.elementRoot);
  12998. if ("draggable" in $$props2)
  12999. $$invalidate(6, draggable2 = $$props2.draggable);
  13000. if ("draggableOptions" in $$props2)
  13001. $$invalidate(7, draggableOptions = $$props2.draggableOptions);
  13002. if ("stylesApp" in $$props2)
  13003. $$invalidate(8, stylesApp = $$props2.stylesApp);
  13004. if ("stylesContent" in $$props2)
  13005. $$invalidate(9, stylesContent = $$props2.stylesContent);
  13006. if ("appOffsetHeight" in $$props2)
  13007. $$invalidate(24, appOffsetHeight = $$props2.appOffsetHeight);
  13008. if ("appOffsetWidth" in $$props2)
  13009. $$invalidate(25, appOffsetWidth = $$props2.appOffsetWidth);
  13010. if ("contentOffsetHeight" in $$props2)
  13011. $$invalidate(26, contentOffsetHeight = $$props2.contentOffsetHeight);
  13012. if ("contentOffsetWidth" in $$props2)
  13013. $$invalidate(27, contentOffsetWidth = $$props2.contentOffsetWidth);
  13014. if ("transition" in $$props2)
  13015. $$invalidate(28, transition = $$props2.transition);
  13016. if ("inTransition" in $$props2)
  13017. $$invalidate(2, inTransition = $$props2.inTransition);
  13018. if ("outTransition" in $$props2)
  13019. $$invalidate(3, outTransition = $$props2.outTransition);
  13020. if ("transitionOptions" in $$props2)
  13021. $$invalidate(29, transitionOptions = $$props2.transitionOptions);
  13022. if ("inTransitionOptions" in $$props2)
  13023. $$invalidate(4, inTransitionOptions = $$props2.inTransitionOptions);
  13024. if ("outTransitionOptions" in $$props2)
  13025. $$invalidate(5, outTransitionOptions = $$props2.outTransitionOptions);
  13026. if ("$$scope" in $$props2)
  13027. $$invalidate(35, $$scope = $$props2.$$scope);
  13028. };
  13029. $$self.$$.update = () => {
  13030. if ($$self.$$.dirty[0] & /*elementContent*/
  13031. 1) {
  13032. if (elementContent !== void 0 && elementContent !== null) {
  13033. getContext("#internal").stores.elementContent.set(elementContent);
  13034. }
  13035. }
  13036. if ($$self.$$.dirty[0] & /*elementRoot*/
  13037. 2) {
  13038. if (elementRoot !== void 0 && elementRoot !== null) {
  13039. getContext("#internal").stores.elementRoot.set(elementRoot);
  13040. }
  13041. }
  13042. if ($$self.$$.dirty[1] & /*$focusAuto, $focusTrap, $minimized*/
  13043. 14) {
  13044. $$invalidate(11, focusWrapEnabled = $focusAuto && $focusTrap && !$minimized);
  13045. }
  13046. if ($$self.$$.dirty[0] & /*oldTransition, transition*/
  13047. 1342177280) {
  13048. if (oldTransition !== transition) {
  13049. const newTransition = typeof transition === "function" ? transition : void 0;
  13050. $$invalidate(2, inTransition = newTransition);
  13051. $$invalidate(3, outTransition = newTransition);
  13052. $$invalidate(30, oldTransition = newTransition);
  13053. }
  13054. }
  13055. if ($$self.$$.dirty[0] & /*transitionOptions*/
  13056. 536870912 | $$self.$$.dirty[1] & /*oldTransitionOptions*/
  13057. 1) {
  13058. if (oldTransitionOptions !== transitionOptions) {
  13059. const newOptions = transitionOptions !== s_DEFAULT_TRANSITION_OPTIONS && isObject(transitionOptions) ? transitionOptions : s_DEFAULT_TRANSITION_OPTIONS;
  13060. $$invalidate(4, inTransitionOptions = newOptions);
  13061. $$invalidate(5, outTransitionOptions = newOptions);
  13062. $$invalidate(31, oldTransitionOptions = newOptions);
  13063. }
  13064. }
  13065. if ($$self.$$.dirty[0] & /*inTransition*/
  13066. 4) {
  13067. if (typeof inTransition !== "function") {
  13068. $$invalidate(2, inTransition = void 0);
  13069. }
  13070. }
  13071. if ($$self.$$.dirty[0] & /*outTransition, application*/
  13072. 1032) {
  13073. {
  13074. if (typeof outTransition !== "function") {
  13075. $$invalidate(3, outTransition = void 0);
  13076. }
  13077. if (application && typeof application?.options?.defaultCloseAnimation === "boolean") {
  13078. $$invalidate(10, application.options.defaultCloseAnimation = outTransition === void 0, application);
  13079. }
  13080. }
  13081. }
  13082. if ($$self.$$.dirty[0] & /*inTransitionOptions*/
  13083. 16) {
  13084. if (typeof inTransitionOptions !== "object") {
  13085. $$invalidate(4, inTransitionOptions = s_DEFAULT_TRANSITION_OPTIONS);
  13086. }
  13087. }
  13088. if ($$self.$$.dirty[0] & /*outTransitionOptions*/
  13089. 32) {
  13090. if (typeof outTransitionOptions !== "object") {
  13091. $$invalidate(5, outTransitionOptions = s_DEFAULT_TRANSITION_OPTIONS);
  13092. }
  13093. }
  13094. };
  13095. return [
  13096. elementContent,
  13097. elementRoot,
  13098. inTransition,
  13099. outTransition,
  13100. inTransitionOptions,
  13101. outTransitionOptions,
  13102. draggable2,
  13103. draggableOptions,
  13104. stylesApp,
  13105. stylesContent,
  13106. application,
  13107. focusWrapEnabled,
  13108. appResizeObserver,
  13109. contentResizeObserver,
  13110. focusAuto,
  13111. focusKeep,
  13112. focusTrap,
  13113. minimized,
  13114. onClosePopup,
  13115. onKeydown,
  13116. onPointerdownApp,
  13117. onPointerdownContent,
  13118. resizeObservedContent,
  13119. resizeObservedApp,
  13120. appOffsetHeight,
  13121. appOffsetWidth,
  13122. contentOffsetHeight,
  13123. contentOffsetWidth,
  13124. transition,
  13125. transitionOptions,
  13126. oldTransition,
  13127. oldTransitionOptions,
  13128. $focusAuto,
  13129. $minimized,
  13130. $focusTrap,
  13131. $$scope,
  13132. slots,
  13133. section_binding,
  13134. div_binding,
  13135. section_binding_1,
  13136. div_binding_1
  13137. ];
  13138. }
  13139. class ApplicationShell extends SvelteComponent {
  13140. constructor(options) {
  13141. super();
  13142. init(
  13143. this,
  13144. options,
  13145. instance$l,
  13146. create_fragment$l,
  13147. safe_not_equal,
  13148. {
  13149. elementContent: 0,
  13150. elementRoot: 1,
  13151. draggable: 6,
  13152. draggableOptions: 7,
  13153. stylesApp: 8,
  13154. stylesContent: 9,
  13155. appOffsetHeight: 24,
  13156. appOffsetWidth: 25,
  13157. contentOffsetHeight: 26,
  13158. contentOffsetWidth: 27,
  13159. transition: 28,
  13160. inTransition: 2,
  13161. outTransition: 3,
  13162. transitionOptions: 29,
  13163. inTransitionOptions: 4,
  13164. outTransitionOptions: 5
  13165. },
  13166. null,
  13167. [-1, -1]
  13168. );
  13169. }
  13170. get elementContent() {
  13171. return this.$$.ctx[0];
  13172. }
  13173. set elementContent(elementContent) {
  13174. this.$$set({ elementContent });
  13175. flush();
  13176. }
  13177. get elementRoot() {
  13178. return this.$$.ctx[1];
  13179. }
  13180. set elementRoot(elementRoot) {
  13181. this.$$set({ elementRoot });
  13182. flush();
  13183. }
  13184. get draggable() {
  13185. return this.$$.ctx[6];
  13186. }
  13187. set draggable(draggable2) {
  13188. this.$$set({ draggable: draggable2 });
  13189. flush();
  13190. }
  13191. get draggableOptions() {
  13192. return this.$$.ctx[7];
  13193. }
  13194. set draggableOptions(draggableOptions) {
  13195. this.$$set({ draggableOptions });
  13196. flush();
  13197. }
  13198. get stylesApp() {
  13199. return this.$$.ctx[8];
  13200. }
  13201. set stylesApp(stylesApp) {
  13202. this.$$set({ stylesApp });
  13203. flush();
  13204. }
  13205. get stylesContent() {
  13206. return this.$$.ctx[9];
  13207. }
  13208. set stylesContent(stylesContent) {
  13209. this.$$set({ stylesContent });
  13210. flush();
  13211. }
  13212. get appOffsetHeight() {
  13213. return this.$$.ctx[24];
  13214. }
  13215. set appOffsetHeight(appOffsetHeight) {
  13216. this.$$set({ appOffsetHeight });
  13217. flush();
  13218. }
  13219. get appOffsetWidth() {
  13220. return this.$$.ctx[25];
  13221. }
  13222. set appOffsetWidth(appOffsetWidth) {
  13223. this.$$set({ appOffsetWidth });
  13224. flush();
  13225. }
  13226. get contentOffsetHeight() {
  13227. return this.$$.ctx[26];
  13228. }
  13229. set contentOffsetHeight(contentOffsetHeight) {
  13230. this.$$set({ contentOffsetHeight });
  13231. flush();
  13232. }
  13233. get contentOffsetWidth() {
  13234. return this.$$.ctx[27];
  13235. }
  13236. set contentOffsetWidth(contentOffsetWidth) {
  13237. this.$$set({ contentOffsetWidth });
  13238. flush();
  13239. }
  13240. get transition() {
  13241. return this.$$.ctx[28];
  13242. }
  13243. set transition(transition) {
  13244. this.$$set({ transition });
  13245. flush();
  13246. }
  13247. get inTransition() {
  13248. return this.$$.ctx[2];
  13249. }
  13250. set inTransition(inTransition) {
  13251. this.$$set({ inTransition });
  13252. flush();
  13253. }
  13254. get outTransition() {
  13255. return this.$$.ctx[3];
  13256. }
  13257. set outTransition(outTransition) {
  13258. this.$$set({ outTransition });
  13259. flush();
  13260. }
  13261. get transitionOptions() {
  13262. return this.$$.ctx[29];
  13263. }
  13264. set transitionOptions(transitionOptions) {
  13265. this.$$set({ transitionOptions });
  13266. flush();
  13267. }
  13268. get inTransitionOptions() {
  13269. return this.$$.ctx[4];
  13270. }
  13271. set inTransitionOptions(inTransitionOptions) {
  13272. this.$$set({ inTransitionOptions });
  13273. flush();
  13274. }
  13275. get outTransitionOptions() {
  13276. return this.$$.ctx[5];
  13277. }
  13278. set outTransitionOptions(outTransitionOptions) {
  13279. this.$$set({ outTransitionOptions });
  13280. flush();
  13281. }
  13282. }
  13283. const EmptyApplicationShell_svelte_svelte_type_style_lang = "";
  13284. const TJSApplicationShell_svelte_svelte_type_style_lang = "";
  13285. const DialogContent_svelte_svelte_type_style_lang = "";
  13286. cssVariables.setProperties({
  13287. // Anchor text shadow / header buttons
  13288. "--tjs-default-text-shadow-focus-hover": "0 0 8px var(--color-shadow-primary)",
  13289. // TJSApplicationShell app background.
  13290. "--tjs-app-background": `url("${globalThis.foundry.utils.getRoute("/ui/denim075.png")}")`
  13291. }, false);
  13292. Hooks.on("PopOut:loading", (app) => {
  13293. if (app instanceof SvelteApplication) {
  13294. app.position.enabled = false;
  13295. }
  13296. });
  13297. Hooks.on("PopOut:popin", (app) => {
  13298. if (app instanceof SvelteApplication) {
  13299. app.position.enabled = true;
  13300. }
  13301. });
  13302. Hooks.on("PopOut:close", (app) => {
  13303. if (app instanceof SvelteApplication) {
  13304. app.position.enabled = true;
  13305. }
  13306. });
  13307. class loading_bar {
  13308. constructor() {
  13309. this.total = 0;
  13310. this.current = 0;
  13311. this.lastPct = 0;
  13312. }
  13313. init(context, total) {
  13314. this.context = context;
  13315. this.total = total;
  13316. this.current = 0;
  13317. this.lastPct = 0;
  13318. SceneNavigation.displayProgressBar({ label: this.context, pct: 1 });
  13319. }
  13320. incrementProgress() {
  13321. this.current += 1;
  13322. const pct = Math.round(this.current / this.total * 100);
  13323. if (pct !== this.lastPct) {
  13324. debug(`${pct}% loaded...`);
  13325. SceneNavigation.displayProgressBar({ label: this.context, pct });
  13326. }
  13327. this.lastPct = pct;
  13328. }
  13329. }
  13330. const LoadingBar = new loading_bar();
  13331. const SequencerFileCache = {
  13332. _videos: {},
  13333. _preloadedFiles: /* @__PURE__ */ new Set(),
  13334. _totalCacheSize: 0,
  13335. _validTypes: ["video/webm", "video/x-webm", "application/octet-stream"],
  13336. async loadVideo(inSrc) {
  13337. if (!this._videos[inSrc]) {
  13338. const blob = await fetch(inSrc, {
  13339. mode: "cors",
  13340. credentials: "same-origin"
  13341. }).then((r) => r.blob()).catch((err) => {
  13342. console.error(err);
  13343. });
  13344. if (this._validTypes.indexOf(blob?.type) === -1)
  13345. return false;
  13346. while (this._totalCacheSize + blob.size > 524288e3) {
  13347. const entries = Object.entries(this._videos);
  13348. entries.sort((a, b) => {
  13349. return b[1].lastUsed - a[1].lastUsed;
  13350. });
  13351. const [oldSrc] = entries[0];
  13352. this._preloadedFiles.delete(oldSrc);
  13353. this._totalCacheSize -= this._videos[oldSrc].blob.size;
  13354. delete this._videos[oldSrc];
  13355. }
  13356. this._totalCacheSize += blob.size;
  13357. this._preloadedFiles.add(inSrc);
  13358. this._videos[inSrc] = {
  13359. blob,
  13360. lastUsed: +new Date()
  13361. };
  13362. }
  13363. this._videos[inSrc].lastUsed = +new Date();
  13364. return this._videos[inSrc].blob;
  13365. },
  13366. srcExists(inSrc) {
  13367. if (this._preloadedFiles.has(inSrc)) {
  13368. return true;
  13369. }
  13370. return srcExists(inSrc);
  13371. },
  13372. async loadFile(inSrc, preload = false) {
  13373. if (inSrc.toLowerCase().endsWith(".webm")) {
  13374. let blob = await this.loadVideo(inSrc);
  13375. if (!blob)
  13376. return false;
  13377. this._preloadedFiles.add(inSrc);
  13378. if (preload)
  13379. return true;
  13380. return get_video_texture(blob);
  13381. } else if (AudioHelper.hasAudioExtension(inSrc)) {
  13382. try {
  13383. const audio2 = await AudioHelper.preloadSound(inSrc);
  13384. if (audio2) {
  13385. this._preloadedFiles.add(inSrc);
  13386. }
  13387. return audio2;
  13388. } catch (err) {
  13389. console.error(`Failed to load audio: ${inSrc}`);
  13390. return false;
  13391. }
  13392. }
  13393. const texture = await loadTexture(inSrc);
  13394. if (texture) {
  13395. this._preloadedFiles.add(inSrc);
  13396. }
  13397. return texture;
  13398. }
  13399. };
  13400. async function get_video_texture(inBlob) {
  13401. return new Promise(async (resolve) => {
  13402. const video = document.createElement("video");
  13403. video.preload = "auto";
  13404. video.crossOrigin = "anonymous";
  13405. video.controls = true;
  13406. video.autoplay = false;
  13407. video.autoload = true;
  13408. video.muted = true;
  13409. video.src = URL.createObjectURL(inBlob);
  13410. let canplay = true;
  13411. video.oncanplay = async () => {
  13412. if (!canplay)
  13413. return;
  13414. canplay = false;
  13415. video.height = video.videoHeight;
  13416. video.width = video.videoWidth;
  13417. const baseTexture = PIXI.BaseTexture.from(video, {
  13418. resourceOptions: { autoPlay: false }
  13419. });
  13420. if (game.settings.get(CONSTANTS.MODULE_NAME, "enable-pixi-fix")) {
  13421. baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
  13422. }
  13423. const texture = new PIXI.Texture(baseTexture);
  13424. resolve(texture);
  13425. };
  13426. video.onerror = () => {
  13427. URL.revokeObjectURL(video.src);
  13428. reject();
  13429. };
  13430. });
  13431. }
  13432. const flipBookTextureCache = {};
  13433. class SequencerFileBase {
  13434. static make(inData, inDBPath, inMetadata) {
  13435. const originalFile = inData?.file ?? inData;
  13436. const file = foundry.utils.duplicate(originalFile);
  13437. const isRangeFind = typeof file !== "string" && !Array.isArray(originalFile) ? Object.keys(originalFile).filter((key) => key.endsWith("ft")).length > 0 : false;
  13438. return isRangeFind ? new SequencerFileRangeFind(inData, inDBPath, inMetadata) : new SequencerFile(inData, inDBPath, inMetadata);
  13439. }
  13440. }
  13441. class SequencerFile extends SequencerFileBase {
  13442. rangeFind = false;
  13443. constructor(inData, inDBPath, inMetadata) {
  13444. super();
  13445. inData = foundry.utils.duplicate(inData);
  13446. inMetadata = foundry.utils.duplicate(inMetadata);
  13447. this.originalData = inData;
  13448. this.originalMetadata = inMetadata;
  13449. for (let [key, value] of Object.entries(inMetadata)) {
  13450. this[key] = value;
  13451. }
  13452. this.dbPath = inDBPath;
  13453. this.moduleName = inDBPath.split(".")[0];
  13454. this.originalFile = inData?.file ?? inData;
  13455. this.file = foundry.utils.duplicate(this.originalFile);
  13456. this.fileIndex = null;
  13457. this.fileTextureMap = Object.fromEntries(
  13458. this.getAllFiles().map((file) => {
  13459. return [file, false];
  13460. })
  13461. );
  13462. this.twister = false;
  13463. }
  13464. clone() {
  13465. return SequencerFile.make(
  13466. this.originalData,
  13467. this.dbPath,
  13468. this.originalMetadata
  13469. );
  13470. }
  13471. async validate() {
  13472. let isValid = true;
  13473. const directories = {};
  13474. const allFiles = this.getAllFiles();
  13475. for (const file of allFiles) {
  13476. let directory = file.split("/");
  13477. directory.pop();
  13478. directory = directory.join("/");
  13479. if (directories[directory] === void 0) {
  13480. directories[directory] = await getFiles(directory);
  13481. }
  13482. }
  13483. for (const file of allFiles) {
  13484. let directory = file.split("/");
  13485. directory.pop();
  13486. directory = directory.join("/");
  13487. if (directories[directory].indexOf(file) === -1) {
  13488. console.warn(
  13489. `"${this.dbPath}" has an incorrect file path, could not find file. Points to:
  13490. ${file}`
  13491. );
  13492. isValid = false;
  13493. }
  13494. }
  13495. return isValid;
  13496. }
  13497. getAllFiles() {
  13498. return [this.file].deepFlatten();
  13499. }
  13500. getFile() {
  13501. if (Array.isArray(this.file)) {
  13502. this.fileIndex = is_real_number(this.fileIndex) ? this.fileIndex : random_array_element(this.file, {
  13503. twister: this.twister,
  13504. index: true
  13505. });
  13506. return this.file[this.fileIndex];
  13507. }
  13508. return this.file;
  13509. }
  13510. getTimestamps() {
  13511. if (Array.isArray(this.originalMetadata?.timestamps)) {
  13512. return this.originalMetadata?.timestamps?.[this.fileIndex] ?? this.originalMetadata?.timestamps[0];
  13513. }
  13514. return this.originalMetadata?.timestamps;
  13515. }
  13516. getPreviewFile(entry) {
  13517. let parts = entry.split(".");
  13518. let files2 = this.getAllFiles();
  13519. if (Array.isArray(files2)) {
  13520. if (is_real_number(parts[parts.length - 1])) {
  13521. files2 = files2[parts[parts.length - 1]];
  13522. } else {
  13523. const index = Math.floor(interpolate(0, files2.length - 1, 0.5));
  13524. files2 = files2?.[index - 1] ?? files2[index];
  13525. }
  13526. }
  13527. return files2;
  13528. }
  13529. destroy() {
  13530. if (this.originalMetadata?.flipbook)
  13531. return;
  13532. for (let texture of Object.values(this.fileTextureMap)) {
  13533. if (!texture)
  13534. continue;
  13535. try {
  13536. texture?.baseTexture?.resource?.source?.removeAttribute("src");
  13537. } catch (err) {
  13538. }
  13539. try {
  13540. texture?.baseTexture?.resource?.source?.pause();
  13541. } catch (err) {
  13542. }
  13543. try {
  13544. texture?.baseTexture?.resource?.source?.remove();
  13545. } catch (err) {
  13546. }
  13547. try {
  13548. texture?.baseTexture?.resource?.source?.load();
  13549. } catch (err) {
  13550. }
  13551. texture.destroy();
  13552. }
  13553. }
  13554. async _getTexture(file) {
  13555. if (this.fileTextureMap[file])
  13556. return this.fileTextureMap[file];
  13557. this.fileTextureMap[file] = await SequencerFileCache.loadFile(file);
  13558. return this.fileTextureMap[file];
  13559. }
  13560. _adjustScaleForPadding(distance, width2) {
  13561. return distance / (width2 - (this.template ? this.template[1] + this.template[2] : 0));
  13562. }
  13563. _adjustAnchorForPadding(width2) {
  13564. return this.template ? this.template[1] / width2 : void 0;
  13565. }
  13566. async _getFlipBookSheet(filePath) {
  13567. if (!this.originalMetadata?.flipbook)
  13568. return false;
  13569. if (flipBookTextureCache[filePath]) {
  13570. return flipBookTextureCache[filePath];
  13571. }
  13572. flipBookTextureCache[filePath] = this.file.map((file) => {
  13573. return PIXI.Texture.from(file);
  13574. });
  13575. return flipBookTextureCache[filePath];
  13576. }
  13577. async getTexture(distance) {
  13578. const filePath = this.getFile();
  13579. const texture = await this._getTexture(this.getFile());
  13580. const sheet = await this._getFlipBookSheet(filePath);
  13581. return {
  13582. filePath,
  13583. texture,
  13584. sheet,
  13585. spriteScale: this._adjustScaleForPadding(distance, texture.width),
  13586. spriteAnchor: this._adjustAnchorForPadding(texture.width)
  13587. };
  13588. }
  13589. }
  13590. class SequencerFileRangeFind extends SequencerFile {
  13591. rangeFind = true;
  13592. constructor(...args) {
  13593. super(...args);
  13594. this._fileDistanceMap = false;
  13595. }
  13596. static get ftToDistanceMap() {
  13597. return {
  13598. "90ft": canvas.grid.size * 15,
  13599. "60ft": canvas.grid.size * 9,
  13600. "30ft": canvas.grid.size * 5,
  13601. "15ft": canvas.grid.size * 2,
  13602. "05ft": 0
  13603. };
  13604. }
  13605. get _gridSizeDiff() {
  13606. return canvas.grid.size / this.template[0];
  13607. }
  13608. getAllFiles() {
  13609. return Object.values(this.file).deepFlatten();
  13610. }
  13611. getFile(inFt) {
  13612. if (inFt && this.file[inFt]) {
  13613. if (Array.isArray(this.file[inFt])) {
  13614. const fileIndex = is_real_number(this.fileIndex) ? this.fileIndex : random_array_element(this.file[inFt], {
  13615. twister: this.twister,
  13616. index: true
  13617. });
  13618. return this.file[inFt][fileIndex];
  13619. }
  13620. return this.file[inFt];
  13621. }
  13622. return this.file;
  13623. }
  13624. getPreviewFile(entry) {
  13625. let parts = entry.split(".");
  13626. const ft = parts.find(
  13627. (part) => Object.keys(SequencerFileRangeFind.ftToDistanceMap).indexOf(part) > -1
  13628. );
  13629. if (!ft) {
  13630. return super.getPreviewFile(entry);
  13631. }
  13632. const fileIndex = parts.slice(parts.indexOf(ft) + 1)?.[0];
  13633. if (is_real_number(Number(fileIndex))) {
  13634. this.fileIndex = Number(fileIndex);
  13635. }
  13636. return this.getFile(ft);
  13637. }
  13638. async getTexture(distance = 400) {
  13639. const { filePath, texture } = await this._getTextureForDistance(distance);
  13640. return {
  13641. filePath,
  13642. texture,
  13643. spriteScale: this._adjustScaleForPadding(distance, texture.width),
  13644. spriteAnchor: this._adjustAnchorForPadding(texture.width)
  13645. };
  13646. }
  13647. _getMatchingDistance(inEntry) {
  13648. return SequencerFileRangeFind.ftToDistanceMap[inEntry] / this._gridSizeDiff;
  13649. }
  13650. _rangeFind(inDistance) {
  13651. if (!this._fileDistanceMap) {
  13652. let distances = Object.keys(this.file).filter(
  13653. (entry) => Object.keys(SequencerFileRangeFind.ftToDistanceMap).indexOf(entry) > -1
  13654. ).map((ft) => {
  13655. return {
  13656. file: this.getFile(ft),
  13657. minDistance: this._getMatchingDistance(ft)
  13658. };
  13659. });
  13660. let uniqueDistances = [
  13661. ...new Set(distances.map((item) => item.minDistance))
  13662. ];
  13663. uniqueDistances.sort((a, b) => a - b);
  13664. let max = Math.max(...uniqueDistances);
  13665. let min = Math.min(...uniqueDistances);
  13666. this._fileDistanceMap = distances.map((entry) => {
  13667. entry.distances = {
  13668. min: entry.minDistance === min ? 0 : entry.minDistance,
  13669. max: entry.minDistance === max ? Infinity : uniqueDistances[uniqueDistances.indexOf(entry.minDistance) + 1]
  13670. };
  13671. return entry;
  13672. });
  13673. }
  13674. const possibleFiles = this._fileDistanceMap.filter((entry) => {
  13675. const relativeDistance = inDistance / this._gridSizeDiff;
  13676. return relativeDistance >= entry.distances.min && relativeDistance < entry.distances.max;
  13677. }).map((entry) => entry.file).flat();
  13678. return possibleFiles.length > 1 ? random_array_element(possibleFiles, { twister: this.twister }) : possibleFiles[0];
  13679. }
  13680. async _getTextureForDistance(distance) {
  13681. const filePath = this._rangeFind(distance);
  13682. const texture = await this._getTexture(filePath);
  13683. return { filePath, texture };
  13684. }
  13685. }
  13686. class Database {
  13687. #entriesStore = writable$1({});
  13688. privateModules = [];
  13689. flattenedEntries = [];
  13690. inverseFlattenedEntries = /* @__PURE__ */ new Map();
  13691. get entries() {
  13692. return get_store_value(this.#entriesStore);
  13693. }
  13694. set entries(entries) {
  13695. this.#entriesStore.set(entries);
  13696. }
  13697. get entriesStore() {
  13698. return this.#entriesStore;
  13699. }
  13700. get publicModules() {
  13701. return Object.keys(this.entries).filter(
  13702. (module2) => !this.privateModules.includes(module2)
  13703. );
  13704. }
  13705. get publicFlattenedEntries() {
  13706. return this.flattenedEntries.filter((entry) => {
  13707. return this.privateModules.indexOf(entry.split(".")[0]) === -1;
  13708. });
  13709. }
  13710. get publicFlattenedSimpleEntries() {
  13711. return make_array_unique(
  13712. this.publicFlattenedEntries.map((entry) => {
  13713. return entry.split(CONSTANTS.FEET_REGEX)[0];
  13714. })
  13715. );
  13716. }
  13717. /**
  13718. * Retrieves an object of every public entry
  13719. *
  13720. * @return {object}
  13721. */
  13722. get filePathDatabasePaths() {
  13723. const fileDatabaseObject = {};
  13724. Object.entries(this.entries).map((entry) => entry[1]).deepFlatten().forEach((sequencerFile) => {
  13725. if (sequencerFile?.rangeFind) {
  13726. Object.entries(sequencerFile.file).forEach((entry) => {
  13727. fileDatabaseObject[entry[1]] = sequencerFile.dbPath + "." + entry[0];
  13728. });
  13729. } else {
  13730. fileDatabaseObject[sequencerFile.file] = sequencerFile.dbPath;
  13731. }
  13732. });
  13733. return fileDatabaseObject;
  13734. }
  13735. /**
  13736. * Registers a set of entries to the database on the given module name
  13737. *
  13738. * @param {string} inModuleName The namespace to assign to the inserted entries
  13739. * @param {object} inEntries The entries to merge into the database
  13740. * @param {boolean} isPrivate Whether to mark these entries as private and not show in Effect Player or Database Viewer
  13741. * @return {boolean}
  13742. */
  13743. registerEntries(inModuleName, inEntries, isPrivate = false) {
  13744. if (inModuleName.includes("."))
  13745. return this._throwError(
  13746. "registerEntries",
  13747. "module name must not contain periods"
  13748. );
  13749. if (this.entries[inModuleName])
  13750. custom_warning(
  13751. "Sequencer",
  13752. `registerEntries | module "${inModuleName}" has already been registered to the database! Do you have two similar modules active?`,
  13753. true
  13754. );
  13755. this._flatten(inEntries, inModuleName);
  13756. const processedEntries = this._processEntries(inModuleName, inEntries);
  13757. this.entries = foundry.utils.mergeObject(this.entries, {
  13758. [inModuleName]: processedEntries
  13759. });
  13760. if (isPrivate)
  13761. this.privateModules.push(inModuleName);
  13762. console.log(
  13763. `Sequencer | Database | Entries for "${inModuleName}" registered`
  13764. );
  13765. Hooks.callAll("registerSequencerDatabaseEntries", inModuleName);
  13766. return true;
  13767. }
  13768. /**
  13769. * Validates the entries under a certain module name, checking whether paths to assets are correct or not
  13770. *
  13771. * @param {string} inModuleName The namespace to assign to the inserted entries
  13772. * @return {boolean}
  13773. */
  13774. async validateEntries(inModuleName) {
  13775. let entries = this.getEntry(inModuleName);
  13776. if (!Array.isArray(entries)) {
  13777. entries = [entries];
  13778. }
  13779. ui.notifications.info(
  13780. `Validating paths registered to "${inModuleName}"...`
  13781. );
  13782. let isValid = true;
  13783. LoadingBar.init(
  13784. `Validating paths registered to "${inModuleName}"...`,
  13785. entries.length
  13786. );
  13787. for (let entry of entries) {
  13788. const result = await entry.validate();
  13789. LoadingBar.incrementProgress();
  13790. isValid = !(!result || !isValid);
  13791. }
  13792. if (!isValid) {
  13793. ui.notifications.error(
  13794. `Validation of paths registered to "${inModuleName}" failed! Errors logged in console.`
  13795. );
  13796. } else {
  13797. ui.notifications.info(
  13798. `Validation of paths registered to "${inModuleName}" complete! No errors found!`
  13799. );
  13800. }
  13801. }
  13802. /**
  13803. * Quickly checks if the entry exists in the database
  13804. *
  13805. * @param {string} inString The entry to find in the database
  13806. * @return {boolean} If the entry exists in the database
  13807. */
  13808. entryExists(inString) {
  13809. if (typeof inString !== "string")
  13810. return this._throwError("entryExists", "inString must be of type string");
  13811. inString = inString.trim();
  13812. if (inString === "")
  13813. return this._throwError("entryExists", "inString cannot be empty");
  13814. inString = inString.replace(/\[[0-9]+]$/, "");
  13815. return this.flattenedEntries.find((entry) => entry.startsWith(inString));
  13816. }
  13817. /**
  13818. * Gets the entry in the database by a dot-notated string
  13819. *
  13820. * @param {string} inString The entry to find in the database
  13821. * @param {boolean} softFail Whether it should soft fail (no error) when no entry was found
  13822. * @return {array|SequencerFile|boolean} The found entry in the database, or false if not found (with warning)
  13823. */
  13824. getEntry(inString, { softFail = false } = {}) {
  13825. if (typeof inString !== "string") {
  13826. if (softFail)
  13827. return false;
  13828. return this._throwError("getEntry", "inString must be of type string");
  13829. }
  13830. inString = inString.trim();
  13831. if (inString === "") {
  13832. if (softFail)
  13833. return false;
  13834. return this._throwError("getEntry", "inString cannot be empty");
  13835. }
  13836. inString = inString.replace(/\[[0-9]+]$/, "");
  13837. if (!this.entryExists(inString)) {
  13838. if (softFail)
  13839. return false;
  13840. return this._throwError(
  13841. "getEntry",
  13842. `Could not find ${inString} in database`
  13843. );
  13844. }
  13845. let ft = false;
  13846. let index = false;
  13847. if (CONSTANTS.FEET_REGEX.test(inString)) {
  13848. ft = inString.match(CONSTANTS.FEET_REGEX)[0];
  13849. const split = inString.split(ft).filter((str) => str !== "");
  13850. if (split.length > 1) {
  13851. index = split[1].split(".")[0];
  13852. }
  13853. inString = split[0];
  13854. }
  13855. const module2 = inString.split(".")[0];
  13856. const exactEntries = this.entries[module2].filter((entry) => {
  13857. return entry.dbPath === inString;
  13858. });
  13859. let filteredEntries = (exactEntries.length ? exactEntries : this.entries[module2].filter((entry) => {
  13860. return entry.dbPath.startsWith(inString);
  13861. })).map((entry) => {
  13862. let foundFile = entry;
  13863. if (ft)
  13864. foundFile = entry.file?.[ft] ?? foundFile;
  13865. if (index)
  13866. foundFile = foundFile?.[index] ?? foundFile;
  13867. return foundFile;
  13868. });
  13869. if (!filteredEntries.length)
  13870. return this._throwError(
  13871. "getEntry",
  13872. `Could not find ${inString} in database`
  13873. );
  13874. const foundEntry = filteredEntries.length === 1 ? filteredEntries[0] : filteredEntries;
  13875. if (index && filteredEntries.length === 1) {
  13876. foundEntry.fileIndex = Number(index);
  13877. }
  13878. return foundEntry;
  13879. }
  13880. /**
  13881. * Gets all files under a database path
  13882. *
  13883. * @param {string} inDBPath The module to get all files from
  13884. * @return {array|boolean} The found entries in the database under the module's name, or false if not found (with warning)
  13885. */
  13886. getAllFileEntries(inDBPath) {
  13887. if (typeof inDBPath !== "string")
  13888. return this._throwError(
  13889. "getAllFileEntries",
  13890. "inDBPath must be of type string"
  13891. );
  13892. inDBPath = inDBPath.trim();
  13893. if (inDBPath === "")
  13894. return this._throwError("getAllFileEntries", "inDBPath cannot be empty");
  13895. if (!this.entryExists(inDBPath))
  13896. return this._throwError(
  13897. "getAllFileEntries",
  13898. `Could not find ${inDBPath} in database`
  13899. );
  13900. const entries = this._recurseGetFilePaths(inDBPath);
  13901. return make_array_unique(entries.flat());
  13902. }
  13903. /**
  13904. * Get all valid entries under a certain path
  13905. *
  13906. * @param {string} inPath The database path to get entries under
  13907. * @return {array|boolean} An array containing the next layer of valid paths
  13908. */
  13909. getPathsUnder(inPath, { ranges = false, arrays = false, match = false } = {}) {
  13910. if (typeof inPath !== "string")
  13911. return this._throwError("getPathsUnder", "inPath must be of type string");
  13912. inPath = inPath.trim();
  13913. if (inPath === "")
  13914. return this._throwError("getPathsUnder", "inPath cannot be empty");
  13915. inPath = inPath.replace(/\[[0-9]+]$/, "");
  13916. if (!this.entryExists(inPath))
  13917. return this._throwError(
  13918. "getPathsUnder",
  13919. `Could not find ${inPath} in database`
  13920. );
  13921. let entries = this.flattenedEntries.filter((e) => {
  13922. return (e.startsWith(inPath + ".") || e === inPath) && (!match || match && e.match(match));
  13923. });
  13924. if (entries.length === 0)
  13925. return [];
  13926. return make_array_unique(
  13927. entries.map((e) => !arrays ? e.split(CONSTANTS.ARRAY_REGEX)[0] : e).map((e) => !ranges ? e.split(CONSTANTS.FEET_REGEX)[0] : e).map((e) => e.split(inPath)[1]).map((e) => e ? e.split(".")[1] : "").filter(Boolean)
  13928. );
  13929. }
  13930. /**
  13931. * Get all valid entries under a certain path
  13932. *
  13933. * @param {string} inPath The database path to search for
  13934. * @param {boolean} publicOnly Whether to only search for public modules
  13935. * @return {array|boolean} An array containing potential database paths
  13936. */
  13937. searchFor(inPath, publicOnly = true) {
  13938. const modules = publicOnly ? this.publicModules : Object.keys(this.entries);
  13939. const originalEntries = publicOnly ? this.publicFlattenedEntries : this.flattenedEntries;
  13940. if ((!inPath || inPath === "") && !modules.includes(inPath))
  13941. return modules;
  13942. if (typeof inPath !== "string") {
  13943. return this._throwError("searchFor", "inString must be of type string");
  13944. }
  13945. inPath = inPath.trim();
  13946. if (inPath === "")
  13947. return this._throwError("searchFor", "inString cannot be empty");
  13948. inPath = inPath.replace(/\[[0-9]+]$/, "");
  13949. inPath = inPath.trim();
  13950. let entries = originalEntries.filter(
  13951. (e) => e.startsWith(inPath) && e !== inPath
  13952. );
  13953. if (inPath.endsWith("."))
  13954. inPath = inPath.substring(0, inPath.length - 1);
  13955. let length = inPath.split(".").length + 1;
  13956. let foundEntries = entries.map((e) => {
  13957. let path = e.split(CONSTANTS.FEET_REGEX)[0];
  13958. return path.split(".").slice(0, length).join(".");
  13959. });
  13960. if (foundEntries.length === 0) {
  13961. const regexString = str_to_search_regex_str(inPath).replace(/\s+/g, "|");
  13962. const searchParts = regexString.split("|").length;
  13963. const regexSearch = new RegExp(regexString, "gu");
  13964. foundEntries = originalEntries.filter((e) => {
  13965. return e.match(regexSearch)?.length >= searchParts;
  13966. }).map((e) => {
  13967. return e.split(CONSTANTS.FEET_REGEX)[0];
  13968. });
  13969. }
  13970. return make_array_unique(foundEntries);
  13971. }
  13972. /**
  13973. * Throws an error without THROWING one. Duh.
  13974. *
  13975. * @param inFunctionName
  13976. * @param inError
  13977. * @returns {boolean}
  13978. * @private
  13979. */
  13980. _throwError(inFunctionName, inError) {
  13981. let error = `Sequencer | Database | ${inFunctionName} - ${inError}`;
  13982. ui.notifications.error(error);
  13983. return false;
  13984. }
  13985. /**
  13986. * Gets all file paths from the entirety of
  13987. *
  13988. * @param inDBPath
  13989. * @returns {Array}
  13990. * @private
  13991. */
  13992. _recurseGetFilePaths(inDBPath) {
  13993. const module2 = inDBPath.split(".")[0];
  13994. return this.entries[module2].filter((entry) => entry.dbPath.startsWith(inDBPath)).map((entry) => {
  13995. return entry.getAllFiles();
  13996. }).flat();
  13997. }
  13998. /**
  13999. * Flattens a given object to just their db path and file path
  14000. *
  14001. * @param entries
  14002. * @param inModule
  14003. * @private
  14004. */
  14005. _flatten(entries, inModule) {
  14006. let flattened = flatten_object(
  14007. foundry.utils.duplicate({ [inModule]: entries })
  14008. );
  14009. this.flattenedEntries = make_array_unique(
  14010. this.flattenedEntries.concat(
  14011. Object.keys(flattened).map((file) => file.split(".file")[0])
  14012. )
  14013. );
  14014. this.inverseFlattenedEntries = Object.keys(flattened).reduce(
  14015. (acc, entry) => {
  14016. return acc.set(flattened[entry], entry.split(".file")[0]);
  14017. },
  14018. this.inverseFlattenedEntries
  14019. );
  14020. }
  14021. /**
  14022. * Processes and recurse into a large object containing file paths at any given depth
  14023. *
  14024. * @param moduleName
  14025. * @param entries
  14026. * @returns {object}
  14027. * @private
  14028. */
  14029. _processEntries(moduleName, entries) {
  14030. const allPaths = new Set(
  14031. this.flattenedEntries.filter((e) => e.split(".")[0] === moduleName).map((e) => e.split(CONSTANTS.FEET_REGEX)[0])
  14032. );
  14033. const allTemplates = foundry.utils.mergeObject(entries?._templates ?? {}, {
  14034. default: [100, 0, 0]
  14035. });
  14036. if (entries?._templates) {
  14037. delete entries?._templates;
  14038. }
  14039. const moduleEntries = [];
  14040. const mediaFileExtensions = Object.keys(CONST.FILE_CATEGORIES.IMAGE).concat(Object.keys(CONST.FILE_CATEGORIES.VIDEO)).concat(Object.keys(CONST.FILE_CATEGORIES.AUDIO));
  14041. for (let wholeDBPath of allPaths) {
  14042. let metadata = this._getCleanData(entries);
  14043. let dbPath = wholeDBPath.split(".");
  14044. dbPath.shift();
  14045. let combinedPath = "";
  14046. for (let part of dbPath) {
  14047. combinedPath = combinedPath ? combinedPath + "." + part : part;
  14048. const entry = getProperty(entries, combinedPath);
  14049. if (Array.isArray(entry) || typeof entry === "string" || entry?.file) {
  14050. metadata = this._getCleanData(entry, { existingData: metadata });
  14051. break;
  14052. }
  14053. metadata = this._getCleanData(entry, { existingData: metadata });
  14054. }
  14055. if (!metadata.template)
  14056. metadata.template = "default";
  14057. if (typeof metadata.template === "string") {
  14058. metadata.template = allTemplates?.[metadata.template] ?? allTemplates?.["default"];
  14059. }
  14060. let data = getProperty(entries, dbPath.join("."));
  14061. if (!Array.isArray(data) && !(typeof data === "string")) {
  14062. data = this._getCleanData(data, { metadata: false });
  14063. }
  14064. if (typeof data === "string") {
  14065. const existingEntry = this.entryExists(data);
  14066. const extension = data.split(".")[data.split(".").length - 1].toLowerCase();
  14067. if (!existingEntry && extension && !mediaFileExtensions.includes(extension)) {
  14068. console.warn(
  14069. `Sequencer | Database | registerEntries - failed to register ${wholeDBPath} to ${data}!`
  14070. );
  14071. this.flattenedEntries.splice(
  14072. this.flattenedEntries.indexOf(wholeDBPath),
  14073. 1
  14074. );
  14075. continue;
  14076. } else if (existingEntry) {
  14077. moduleEntries.push(new SequencerFileProxy(wholeDBPath, data));
  14078. continue;
  14079. }
  14080. }
  14081. moduleEntries.push(SequencerFileBase.make(data, wholeDBPath, metadata));
  14082. }
  14083. return moduleEntries;
  14084. }
  14085. _getCleanData(data, { existingData = {}, metadata = true } = {}) {
  14086. data = Object.entries(data).filter((entry) => {
  14087. return metadata === entry[0].startsWith("_");
  14088. });
  14089. if (metadata) {
  14090. data = data.map((entry) => [entry[0].slice(1), entry[1]]);
  14091. }
  14092. return foundry.utils.mergeObject(existingData, Object.fromEntries(data));
  14093. }
  14094. }
  14095. const SequencerDatabase = new Database();
  14096. const TreeViewEntry_svelte_svelte_type_style_lang = "";
  14097. function create_if_block_2$1(ctx) {
  14098. let a;
  14099. let i;
  14100. let mounted;
  14101. let dispose;
  14102. return {
  14103. c() {
  14104. a = element("a");
  14105. i = element("i");
  14106. attr(i, "class", "fas svelte-ese-uyryhb");
  14107. toggle_class(
  14108. i,
  14109. "fa-angle-down",
  14110. /*data*/
  14111. ctx[0].open
  14112. );
  14113. toggle_class(i, "fa-angle-right", !/*data*/
  14114. ctx[0].open);
  14115. attr(a, "class", "svelte-ese-uyryhb");
  14116. },
  14117. m(target, anchor) {
  14118. insert(target, a, anchor);
  14119. append(a, i);
  14120. if (!mounted) {
  14121. dispose = listen(
  14122. a,
  14123. "click",
  14124. /*click_handler*/
  14125. ctx[6]
  14126. );
  14127. mounted = true;
  14128. }
  14129. },
  14130. p(ctx2, dirty) {
  14131. if (dirty & /*data*/
  14132. 1) {
  14133. toggle_class(
  14134. i,
  14135. "fa-angle-down",
  14136. /*data*/
  14137. ctx2[0].open
  14138. );
  14139. }
  14140. if (dirty & /*data*/
  14141. 1) {
  14142. toggle_class(i, "fa-angle-right", !/*data*/
  14143. ctx2[0].open);
  14144. }
  14145. },
  14146. d(detaching) {
  14147. if (detaching)
  14148. detach(a);
  14149. mounted = false;
  14150. dispose();
  14151. }
  14152. };
  14153. }
  14154. function create_if_block$7(ctx) {
  14155. let t0;
  14156. let a0;
  14157. let i0;
  14158. let t1;
  14159. let a1;
  14160. let mounted;
  14161. let dispose;
  14162. let if_block = !/*data*/
  14163. ctx[0].hasChildren && create_if_block_1$4(ctx);
  14164. return {
  14165. c() {
  14166. if (if_block)
  14167. if_block.c();
  14168. t0 = space();
  14169. a0 = element("a");
  14170. i0 = element("i");
  14171. t1 = space();
  14172. a1 = element("a");
  14173. a1.innerHTML = `<i class="fas fa-play svelte-ese-uyryhb"></i>`;
  14174. attr(i0, "class", "fas fa-database svelte-ese-uyryhb");
  14175. toggle_class(
  14176. i0,
  14177. "flash-it",
  14178. /*flashDBPath*/
  14179. ctx[2]
  14180. );
  14181. attr(a0, "class", "database-entry-button svelte-ese-uyryhb");
  14182. attr(a1, "class", "database-entry-button svelte-ese-uyryhb");
  14183. },
  14184. m(target, anchor) {
  14185. if (if_block)
  14186. if_block.m(target, anchor);
  14187. insert(target, t0, anchor);
  14188. insert(target, a0, anchor);
  14189. append(a0, i0);
  14190. insert(target, t1, anchor);
  14191. insert(target, a1, anchor);
  14192. if (!mounted) {
  14193. dispose = [
  14194. listen(
  14195. a0,
  14196. "click",
  14197. /*click_handler_2*/
  14198. ctx[8]
  14199. ),
  14200. listen(
  14201. a1,
  14202. "click",
  14203. /*click_handler_3*/
  14204. ctx[9]
  14205. )
  14206. ];
  14207. mounted = true;
  14208. }
  14209. },
  14210. p(ctx2, dirty) {
  14211. if (!/*data*/
  14212. ctx2[0].hasChildren) {
  14213. if (if_block) {
  14214. if_block.p(ctx2, dirty);
  14215. } else {
  14216. if_block = create_if_block_1$4(ctx2);
  14217. if_block.c();
  14218. if_block.m(t0.parentNode, t0);
  14219. }
  14220. } else if (if_block) {
  14221. if_block.d(1);
  14222. if_block = null;
  14223. }
  14224. if (dirty & /*flashDBPath*/
  14225. 4) {
  14226. toggle_class(
  14227. i0,
  14228. "flash-it",
  14229. /*flashDBPath*/
  14230. ctx2[2]
  14231. );
  14232. }
  14233. },
  14234. d(detaching) {
  14235. if (if_block)
  14236. if_block.d(detaching);
  14237. if (detaching)
  14238. detach(t0);
  14239. if (detaching)
  14240. detach(a0);
  14241. if (detaching)
  14242. detach(t1);
  14243. if (detaching)
  14244. detach(a1);
  14245. mounted = false;
  14246. run_all(dispose);
  14247. }
  14248. };
  14249. }
  14250. function create_if_block_1$4(ctx) {
  14251. let a;
  14252. let i;
  14253. let mounted;
  14254. let dispose;
  14255. return {
  14256. c() {
  14257. a = element("a");
  14258. i = element("i");
  14259. attr(i, "class", "fas fa-file svelte-ese-uyryhb");
  14260. toggle_class(
  14261. i,
  14262. "flash-it",
  14263. /*flashFilePath*/
  14264. ctx[1]
  14265. );
  14266. attr(a, "class", "database-entry-button svelte-ese-uyryhb");
  14267. },
  14268. m(target, anchor) {
  14269. insert(target, a, anchor);
  14270. append(a, i);
  14271. if (!mounted) {
  14272. dispose = listen(
  14273. a,
  14274. "click",
  14275. /*click_handler_1*/
  14276. ctx[7]
  14277. );
  14278. mounted = true;
  14279. }
  14280. },
  14281. p(ctx2, dirty) {
  14282. if (dirty & /*flashFilePath*/
  14283. 2) {
  14284. toggle_class(
  14285. i,
  14286. "flash-it",
  14287. /*flashFilePath*/
  14288. ctx2[1]
  14289. );
  14290. }
  14291. },
  14292. d(detaching) {
  14293. if (detaching)
  14294. detach(a);
  14295. mounted = false;
  14296. dispose();
  14297. }
  14298. };
  14299. }
  14300. function create_fragment$k(ctx) {
  14301. let div3;
  14302. let div2;
  14303. let t0;
  14304. let show_if = (
  14305. /*data*/
  14306. ctx[0].fullPath.includes(".")
  14307. );
  14308. let t1;
  14309. let div1;
  14310. let div0;
  14311. let t2;
  14312. let t3_value = (
  14313. /*data*/
  14314. ctx[0].path + ""
  14315. );
  14316. let t3;
  14317. let div1_title_value;
  14318. let if_block0 = (
  14319. /*data*/
  14320. ctx[0].hasChildren && create_if_block_2$1(ctx)
  14321. );
  14322. let if_block1 = show_if && create_if_block$7(ctx);
  14323. return {
  14324. c() {
  14325. div3 = element("div");
  14326. div2 = element("div");
  14327. if (if_block0)
  14328. if_block0.c();
  14329. t0 = space();
  14330. if (if_block1)
  14331. if_block1.c();
  14332. t1 = space();
  14333. div1 = element("div");
  14334. div0 = element("div");
  14335. t2 = space();
  14336. t3 = text$1(t3_value);
  14337. attr(div0, "class", "database-entry-text-highlight svelte-ese-uyryhb");
  14338. attr(div1, "class", "database-entry-text svelte-ese-uyryhb");
  14339. attr(div1, "title", div1_title_value = /*data*/
  14340. ctx[0].path);
  14341. attr(div2, "class", "database-entry-text-container svelte-ese-uyryhb");
  14342. attr(div3, "class", "database-entry svelte-ese-uyryhb");
  14343. set_style(
  14344. div3,
  14345. "margin-left",
  14346. /*data*/
  14347. ctx[0].depth * 15 + "px"
  14348. );
  14349. },
  14350. m(target, anchor) {
  14351. insert(target, div3, anchor);
  14352. append(div3, div2);
  14353. if (if_block0)
  14354. if_block0.m(div2, null);
  14355. append(div2, t0);
  14356. if (if_block1)
  14357. if_block1.m(div2, null);
  14358. append(div2, t1);
  14359. append(div2, div1);
  14360. append(div1, div0);
  14361. div0.innerHTML = /*highlight*/
  14362. ctx[3];
  14363. append(div1, t2);
  14364. append(div1, t3);
  14365. },
  14366. p(ctx2, [dirty]) {
  14367. if (
  14368. /*data*/
  14369. ctx2[0].hasChildren
  14370. ) {
  14371. if (if_block0) {
  14372. if_block0.p(ctx2, dirty);
  14373. } else {
  14374. if_block0 = create_if_block_2$1(ctx2);
  14375. if_block0.c();
  14376. if_block0.m(div2, t0);
  14377. }
  14378. } else if (if_block0) {
  14379. if_block0.d(1);
  14380. if_block0 = null;
  14381. }
  14382. if (dirty & /*data*/
  14383. 1)
  14384. show_if = /*data*/
  14385. ctx2[0].fullPath.includes(".");
  14386. if (show_if) {
  14387. if (if_block1) {
  14388. if_block1.p(ctx2, dirty);
  14389. } else {
  14390. if_block1 = create_if_block$7(ctx2);
  14391. if_block1.c();
  14392. if_block1.m(div2, t1);
  14393. }
  14394. } else if (if_block1) {
  14395. if_block1.d(1);
  14396. if_block1 = null;
  14397. }
  14398. if (dirty & /*highlight*/
  14399. 8)
  14400. div0.innerHTML = /*highlight*/
  14401. ctx2[3];
  14402. if (dirty & /*data*/
  14403. 1 && t3_value !== (t3_value = /*data*/
  14404. ctx2[0].path + ""))
  14405. set_data(t3, t3_value);
  14406. if (dirty & /*data*/
  14407. 1 && div1_title_value !== (div1_title_value = /*data*/
  14408. ctx2[0].path)) {
  14409. attr(div1, "title", div1_title_value);
  14410. }
  14411. if (dirty & /*data*/
  14412. 1) {
  14413. set_style(
  14414. div3,
  14415. "margin-left",
  14416. /*data*/
  14417. ctx2[0].depth * 15 + "px"
  14418. );
  14419. }
  14420. },
  14421. i: noop,
  14422. o: noop,
  14423. d(detaching) {
  14424. if (detaching)
  14425. detach(div3);
  14426. if (if_block0)
  14427. if_block0.d();
  14428. if (if_block1)
  14429. if_block1.d();
  14430. }
  14431. };
  14432. }
  14433. function instance$k($$self, $$props, $$invalidate) {
  14434. let highlight;
  14435. let $searchRegex;
  14436. let { data } = $$props;
  14437. const searchRegex = databaseStore.searchRegex;
  14438. component_subscribe($$self, searchRegex, (value) => $$invalidate(5, $searchRegex = value));
  14439. let flashFilePath = false;
  14440. let flashDBPath = false;
  14441. const click_handler = (e) => {
  14442. databaseStore.openTreePath(data.fullPath, !data.open, e.ctrlKey);
  14443. };
  14444. const click_handler_1 = (e) => {
  14445. databaseStore.copyPath(data.fullPath, true, e.ctrlKey);
  14446. $$invalidate(1, flashFilePath = true);
  14447. setTimeout(
  14448. () => {
  14449. $$invalidate(1, flashFilePath = false);
  14450. },
  14451. 400
  14452. );
  14453. };
  14454. const click_handler_2 = (e) => {
  14455. databaseStore.copyPath(data.fullPath, false, e.ctrlKey);
  14456. $$invalidate(2, flashDBPath = true);
  14457. setTimeout(
  14458. () => {
  14459. $$invalidate(2, flashDBPath = false);
  14460. },
  14461. 400
  14462. );
  14463. };
  14464. const click_handler_3 = () => {
  14465. databaseStore.playFile(data.fullPath);
  14466. };
  14467. $$self.$$set = ($$props2) => {
  14468. if ("data" in $$props2)
  14469. $$invalidate(0, data = $$props2.data);
  14470. };
  14471. $$self.$$.update = () => {
  14472. if ($$self.$$.dirty & /*data, $searchRegex*/
  14473. 33) {
  14474. $$invalidate(3, highlight = data.path.replace($searchRegex, "<mark>$&</mark>"));
  14475. }
  14476. };
  14477. return [
  14478. data,
  14479. flashFilePath,
  14480. flashDBPath,
  14481. highlight,
  14482. searchRegex,
  14483. $searchRegex,
  14484. click_handler,
  14485. click_handler_1,
  14486. click_handler_2,
  14487. click_handler_3
  14488. ];
  14489. }
  14490. class TreeViewEntry extends SvelteComponent {
  14491. constructor(options) {
  14492. super();
  14493. init(this, options, instance$k, create_fragment$k, safe_not_equal, { data: 0 });
  14494. }
  14495. }
  14496. function create_fragment$j(ctx) {
  14497. let div;
  14498. return {
  14499. c() {
  14500. div = element("div");
  14501. set_style(div, "margin-bottom", "0.3rem");
  14502. },
  14503. m(target, anchor) {
  14504. insert(target, div, anchor);
  14505. },
  14506. p: noop,
  14507. i: noop,
  14508. o: noop,
  14509. d(detaching) {
  14510. if (detaching)
  14511. detach(div);
  14512. }
  14513. };
  14514. }
  14515. function instance$j($$self, $$props, $$invalidate) {
  14516. let { data } = $$props;
  14517. $$self.$$set = ($$props2) => {
  14518. if ("data" in $$props2)
  14519. $$invalidate(0, data = $$props2.data);
  14520. };
  14521. return [data];
  14522. }
  14523. class TreeViewSeparator extends SvelteComponent {
  14524. constructor(options) {
  14525. super();
  14526. init(this, options, instance$j, create_fragment$j, safe_not_equal, { data: 0 });
  14527. }
  14528. }
  14529. let lastFile = false;
  14530. function getFileData(entryText) {
  14531. let entry = SequencerDatabase.getEntry(entryText);
  14532. if (Array.isArray(entry)) {
  14533. if (entry.includes(lastFile) && entry.length > 1) {
  14534. entry.splice(entry.indexOf(lastFile), 1);
  14535. }
  14536. entry = random_array_element(entry);
  14537. lastFile = entry;
  14538. }
  14539. let previewFile = entry?.file ?? entry;
  14540. if (entry instanceof SequencerFileBase) {
  14541. previewFile = entry.clone().getPreviewFile(entryText);
  14542. }
  14543. let lowerCaseEntry = previewFile ? previewFile.toLowerCase() : "unknown.jpg";
  14544. const isAudio = lowerCaseEntry.endsWith("ogg") || lowerCaseEntry.endsWith("mp3") || lowerCaseEntry.endsWith("wav");
  14545. const isImage = !lowerCaseEntry.endsWith("webm") && !isAudio;
  14546. const isVideo = !isAudio && !isImage;
  14547. const icon = previewFile ? isVideo ? "fa-film" : isAudio ? "fa-volume-high" : "fa-image" : "fa-question-mark";
  14548. const title = previewFile ? isVideo ? "Animated WebM" : isAudio ? "Audio" : "Image" : "Unknown";
  14549. return {
  14550. file: previewFile ?? "unknown.jpg",
  14551. dbEntry: entry,
  14552. icon,
  14553. title,
  14554. isAudio,
  14555. isImage,
  14556. isVideo
  14557. };
  14558. }
  14559. function copyPath(dbPath, getFilepath, quotes = false) {
  14560. const tempInput = document.createElement("input");
  14561. tempInput.value = `${dbPath}`;
  14562. let entry;
  14563. if (getFilepath) {
  14564. entry = Sequencer.Database.getEntry(dbPath);
  14565. if (entry instanceof SequencerFileBase) {
  14566. const specificFt = dbPath.match(CONSTANTS.FEET_REGEX);
  14567. if (specificFt) {
  14568. const ft = specificFt[0].replaceAll(".", "");
  14569. entry = entry.getFile(ft);
  14570. } else {
  14571. const files2 = entry.getAllFiles();
  14572. if (Array.isArray(files2)) {
  14573. const index = Math.floor(interpolate(0, files2.length - 1, 0.5));
  14574. entry = files2[index];
  14575. }
  14576. }
  14577. }
  14578. tempInput.value = `${entry?.file ?? entry}`;
  14579. }
  14580. if (quotes) {
  14581. tempInput.value = `"${tempInput.value}"`;
  14582. }
  14583. document.body.appendChild(tempInput);
  14584. tempInput.select();
  14585. document.execCommand("copy");
  14586. document.body.removeChild(tempInput);
  14587. document.execCommand("copy");
  14588. }
  14589. function playFile(entry) {
  14590. const { file, isAudio, isImage, isVideo } = getFileData(entry);
  14591. databaseStore.elements.audio.classList.toggle("hidden", !isAudio);
  14592. databaseStore.elements.image.classList.toggle("hidden", !isImage);
  14593. databaseStore.elements.player.classList.toggle("hidden", !isVideo);
  14594. if (isImage) {
  14595. databaseStore.elements.image.src = file;
  14596. databaseStore.metadata.set({
  14597. type: "Image",
  14598. duration: "n/a"
  14599. });
  14600. return;
  14601. }
  14602. const element2 = isAudio ? databaseStore.elements.audio : databaseStore.elements.player;
  14603. element2.onerror = () => {
  14604. const error = `Sequencer Database Viewer | Could not play file: ${file}`;
  14605. ui.notifications.error(error);
  14606. console.error(error);
  14607. };
  14608. element2.oncanplay = () => {
  14609. element2.play();
  14610. };
  14611. element2.onloadedmetadata = () => {
  14612. databaseStore.metadata.set({
  14613. type: isVideo ? "Video" : isAudio ? "Audio" : "Image",
  14614. duration: isImage ? "n/a" : element2.duration * 1e3 + "ms"
  14615. });
  14616. };
  14617. element2.src = file;
  14618. }
  14619. const treeStore = writable$1({});
  14620. const visibleTreeStore = writable$1([]);
  14621. let flattenedEntries = [];
  14622. const entriesStore = SequencerDatabase.entriesStore;
  14623. const packStore = writable$1(SequencerDatabase.publicModules);
  14624. const selectedPackStore = writable$1("all");
  14625. const searchStore = writable$1("");
  14626. const cleanSearchStore = writable$1("");
  14627. const searchRegexStore = writable$1(new RegExp("", "gu"));
  14628. SequencerDatabase.entriesStore.subscribe(() => {
  14629. packStore.set(SequencerDatabase.publicModules);
  14630. });
  14631. const databaseStore = {
  14632. metadata: writable$1(false),
  14633. allRanges: writable$1(false),
  14634. subLists: writable$1(false),
  14635. listView: writable$1(false),
  14636. packStore,
  14637. selectedPackStore,
  14638. visibleTreeStore,
  14639. search: searchStore,
  14640. cleanSearchStore,
  14641. searchRegex: searchRegexStore,
  14642. elements: {},
  14643. copyPath,
  14644. playFile,
  14645. openTreePath
  14646. };
  14647. entriesStore.subscribe(() => {
  14648. filterFlattenedEntries();
  14649. });
  14650. databaseStore.allRanges.subscribe(() => {
  14651. filterFlattenedEntries();
  14652. });
  14653. databaseStore.subLists.subscribe(() => {
  14654. filterFlattenedEntries();
  14655. });
  14656. databaseStore.selectedPackStore.subscribe(() => {
  14657. filterFlattenedEntries();
  14658. });
  14659. searchStore.subscribe((val) => {
  14660. const cleanSearch = str_to_search_regex_str(val).replace(/\s+/g, "|");
  14661. cleanSearchStore.set(cleanSearch);
  14662. searchRegexStore.set(new RegExp(cleanSearch, "gu"));
  14663. updateVisualTree();
  14664. });
  14665. function filterFlattenedEntries() {
  14666. const selectedPack = get_store_value(selectedPackStore);
  14667. const search = get_store_value(searchStore);
  14668. const searchRegex = get_store_value(searchRegexStore);
  14669. const subLists = get_store_value(databaseStore.subLists);
  14670. const allRanges = get_store_value(databaseStore.allRanges);
  14671. flattenedEntries = make_array_unique(
  14672. SequencerDatabase.publicFlattenedEntries.filter((e) => {
  14673. return (selectedPack === "all" || e.startsWith(selectedPack + ".")) && (!search || e.match(searchRegex));
  14674. }).map((e) => !subLists ? e.split(CONSTANTS.ARRAY_REGEX)[0] : e).map((e) => !allRanges ? e.split(CONSTANTS.FEET_REGEX)[0] : e)
  14675. );
  14676. treeStore.set(
  14677. flattenedEntries.reduce((acc, entry) => {
  14678. let path = "";
  14679. for (const part of entry.split(".")) {
  14680. const fullPath = path ? path + "." + part : part;
  14681. path = path ? path + ".children." + part : part;
  14682. if (!getProperty(acc, path)) {
  14683. setProperty(
  14684. acc,
  14685. path,
  14686. foundry.utils.mergeObject(
  14687. {
  14688. path: part,
  14689. fullPath,
  14690. open: false,
  14691. children: {}
  14692. },
  14693. getProperty(acc, path)
  14694. )
  14695. );
  14696. }
  14697. }
  14698. return acc;
  14699. }, {})
  14700. );
  14701. }
  14702. function openTreePath(fullPath, open, openAll = false) {
  14703. treeStore.update((tree) => {
  14704. const fullTreePath = fullPath.split(".").join(".children.");
  14705. const node = getProperty(tree, fullTreePath);
  14706. setProperty(tree, fullTreePath + ".open", open);
  14707. if ((!open || openAll) && !foundry.utils.isEmpty(node.children)) {
  14708. recurseOpenTree(node.children, open);
  14709. }
  14710. return tree;
  14711. });
  14712. }
  14713. function recurseOpenTree(children2, open) {
  14714. for (const node of Object.values(children2)) {
  14715. node.open = open;
  14716. if (!foundry.utils.isEmpty(node.children)) {
  14717. recurseOpenTree(node.children, open);
  14718. }
  14719. }
  14720. }
  14721. treeStore.subscribe(() => {
  14722. updateVisualTree();
  14723. });
  14724. function updateVisualTree() {
  14725. const tree = get_store_value(treeStore);
  14726. const visibleTree = recurseTree(tree).deepFlatten().filter((e) => e.visible);
  14727. visibleTreeStore.set(visibleTree);
  14728. }
  14729. function recurseTree(tree, path = "", depth = 0) {
  14730. const search = get_store_value(searchStore);
  14731. const searchRegex = get_store_value(searchRegexStore);
  14732. const searchParts = get_store_value(cleanSearchStore).split("|");
  14733. return Object.entries(tree).map(([key, data]) => {
  14734. const fullPath = path ? path + "." + key : key;
  14735. const children2 = recurseTree(
  14736. data.children,
  14737. fullPath,
  14738. depth + 1
  14739. ).deepFlatten();
  14740. const matchParts = make_array_unique(fullPath.match(searchRegex) || []);
  14741. const open = data.open || search && (matchParts.length >= searchParts.length || children2.filter((e) => e.visible).length);
  14742. let visible = !search || matchParts.length >= searchParts.length;
  14743. if (visible) {
  14744. children2.forEach((e) => e.visible = true);
  14745. } else {
  14746. visible = children2.filter((e) => e.visible).length;
  14747. }
  14748. const entry = {
  14749. class: TreeViewEntry,
  14750. path: key,
  14751. fullPath,
  14752. open,
  14753. visible,
  14754. hasChildren: !foundry.utils.isEmpty(data.children),
  14755. depth
  14756. };
  14757. const leaf = [entry];
  14758. if ((data.open || entry.open) && entry.hasChildren) {
  14759. leaf.push(...children2, {
  14760. fullPath: randomID(),
  14761. class: TreeViewSeparator
  14762. });
  14763. }
  14764. return leaf;
  14765. });
  14766. }
  14767. const DatabaseEntry_svelte_svelte_type_style_lang = "";
  14768. function create_fragment$i(ctx) {
  14769. let div2;
  14770. let button0;
  14771. let t0;
  14772. let button1;
  14773. let i1;
  14774. let t1;
  14775. let button2;
  14776. let i2;
  14777. let t2;
  14778. let div1;
  14779. let div0;
  14780. let t3;
  14781. let t4;
  14782. let mounted;
  14783. let dispose;
  14784. return {
  14785. c() {
  14786. div2 = element("div");
  14787. button0 = element("button");
  14788. button0.innerHTML = `<i class="fas fa-play svelte-ese-flzvpb"></i>`;
  14789. t0 = space();
  14790. button1 = element("button");
  14791. i1 = element("i");
  14792. t1 = space();
  14793. button2 = element("button");
  14794. i2 = element("i");
  14795. t2 = space();
  14796. div1 = element("div");
  14797. div0 = element("div");
  14798. t3 = space();
  14799. t4 = text$1(
  14800. /*entry*/
  14801. ctx[0]
  14802. );
  14803. attr(button0, "type", "button");
  14804. attr(button0, "class", "btn_play svelte-ese-flzvpb");
  14805. attr(i1, "class", "fas fa-file svelte-ese-flzvpb");
  14806. toggle_class(
  14807. i1,
  14808. "flash-it",
  14809. /*flashFilePath*/
  14810. ctx[1]
  14811. );
  14812. attr(button1, "type", "button");
  14813. attr(button1, "class", "btn_copy_filepath svelte-ese-flzvpb");
  14814. attr(i2, "class", "fas fa-database svelte-ese-flzvpb");
  14815. toggle_class(
  14816. i2,
  14817. "flash-it",
  14818. /*flashDBPath*/
  14819. ctx[2]
  14820. );
  14821. attr(button2, "type", "button");
  14822. attr(button2, "class", "btn_copy_databasepath svelte-ese-flzvpb");
  14823. attr(div0, "class", "database-entry-text-highlight svelte-ese-flzvpb");
  14824. attr(div1, "class", "database-entry-text svelte-ese-flzvpb");
  14825. attr(
  14826. div1,
  14827. "title",
  14828. /*entry*/
  14829. ctx[0]
  14830. );
  14831. attr(div2, "class", "database-entry svelte-ese-flzvpb");
  14832. attr(
  14833. div2,
  14834. "data-id",
  14835. /*entry*/
  14836. ctx[0]
  14837. );
  14838. },
  14839. m(target, anchor) {
  14840. insert(target, div2, anchor);
  14841. append(div2, button0);
  14842. append(div2, t0);
  14843. append(div2, button1);
  14844. append(button1, i1);
  14845. append(div2, t1);
  14846. append(div2, button2);
  14847. append(button2, i2);
  14848. append(div2, t2);
  14849. append(div2, div1);
  14850. append(div1, div0);
  14851. div0.innerHTML = /*highlight*/
  14852. ctx[3];
  14853. append(div1, t3);
  14854. append(div1, t4);
  14855. if (!mounted) {
  14856. dispose = [
  14857. listen(
  14858. button0,
  14859. "click",
  14860. /*click_handler*/
  14861. ctx[6]
  14862. ),
  14863. listen(
  14864. button1,
  14865. "click",
  14866. /*click_handler_1*/
  14867. ctx[7]
  14868. ),
  14869. listen(
  14870. button2,
  14871. "click",
  14872. /*click_handler_2*/
  14873. ctx[8]
  14874. )
  14875. ];
  14876. mounted = true;
  14877. }
  14878. },
  14879. p(ctx2, [dirty]) {
  14880. if (dirty & /*flashFilePath*/
  14881. 2) {
  14882. toggle_class(
  14883. i1,
  14884. "flash-it",
  14885. /*flashFilePath*/
  14886. ctx2[1]
  14887. );
  14888. }
  14889. if (dirty & /*flashDBPath*/
  14890. 4) {
  14891. toggle_class(
  14892. i2,
  14893. "flash-it",
  14894. /*flashDBPath*/
  14895. ctx2[2]
  14896. );
  14897. }
  14898. if (dirty & /*highlight*/
  14899. 8)
  14900. div0.innerHTML = /*highlight*/
  14901. ctx2[3];
  14902. if (dirty & /*entry*/
  14903. 1)
  14904. set_data(
  14905. t4,
  14906. /*entry*/
  14907. ctx2[0]
  14908. );
  14909. if (dirty & /*entry*/
  14910. 1) {
  14911. attr(
  14912. div1,
  14913. "title",
  14914. /*entry*/
  14915. ctx2[0]
  14916. );
  14917. }
  14918. if (dirty & /*entry*/
  14919. 1) {
  14920. attr(
  14921. div2,
  14922. "data-id",
  14923. /*entry*/
  14924. ctx2[0]
  14925. );
  14926. }
  14927. },
  14928. i: noop,
  14929. o: noop,
  14930. d(detaching) {
  14931. if (detaching)
  14932. detach(div2);
  14933. mounted = false;
  14934. run_all(dispose);
  14935. }
  14936. };
  14937. }
  14938. function instance$i($$self, $$props, $$invalidate) {
  14939. let highlight;
  14940. let $searchRegex;
  14941. createEventDispatcher();
  14942. let { entry } = $$props;
  14943. const searchRegex = databaseStore.searchRegex;
  14944. component_subscribe($$self, searchRegex, (value) => $$invalidate(5, $searchRegex = value));
  14945. let flashFilePath = false;
  14946. let flashDBPath = false;
  14947. const click_handler = () => {
  14948. databaseStore.playFile(entry);
  14949. };
  14950. const click_handler_1 = (e) => {
  14951. databaseStore.copyPath(entry, true, e.ctrlKey);
  14952. $$invalidate(1, flashFilePath = true);
  14953. setTimeout(
  14954. () => {
  14955. $$invalidate(1, flashFilePath = false);
  14956. },
  14957. 400
  14958. );
  14959. };
  14960. const click_handler_2 = (e) => {
  14961. databaseStore.copyPath(entry, false, e.ctrlKey);
  14962. $$invalidate(2, flashDBPath = true);
  14963. setTimeout(
  14964. () => {
  14965. $$invalidate(2, flashDBPath = false);
  14966. },
  14967. 400
  14968. );
  14969. };
  14970. $$self.$$set = ($$props2) => {
  14971. if ("entry" in $$props2)
  14972. $$invalidate(0, entry = $$props2.entry);
  14973. };
  14974. $$self.$$.update = () => {
  14975. if ($$self.$$.dirty & /*entry, $searchRegex*/
  14976. 33) {
  14977. $$invalidate(3, highlight = entry.replace($searchRegex, "<mark>$&</mark>"));
  14978. }
  14979. };
  14980. return [
  14981. entry,
  14982. flashFilePath,
  14983. flashDBPath,
  14984. highlight,
  14985. searchRegex,
  14986. $searchRegex,
  14987. click_handler,
  14988. click_handler_1,
  14989. click_handler_2
  14990. ];
  14991. }
  14992. class DatabaseEntry extends SvelteComponent {
  14993. constructor(options) {
  14994. super();
  14995. init(this, options, instance$i, create_fragment$i, safe_not_equal, { entry: 0 });
  14996. }
  14997. }
  14998. const DIRECTION_TYPE = {
  14999. FRONT: "FRONT",
  15000. // scroll up or left
  15001. BEHIND: "BEHIND"
  15002. // scroll down or right
  15003. };
  15004. const CALC_TYPE = {
  15005. INIT: "INIT",
  15006. FIXED: "FIXED",
  15007. DYNAMIC: "DYNAMIC"
  15008. };
  15009. const LEADING_BUFFER = 2;
  15010. class Virtual {
  15011. param;
  15012. callUpdate;
  15013. firstRangeTotalSize = 0;
  15014. firstRangeAverageSize = 0;
  15015. lastCalcIndex = 0;
  15016. fixedSizeValue = 0;
  15017. calcType = CALC_TYPE.INIT;
  15018. offset = 0;
  15019. direction = "";
  15020. range;
  15021. constructor(param, callUpdate) {
  15022. this.init(param, callUpdate);
  15023. }
  15024. init(param, callUpdate) {
  15025. this.param = param;
  15026. this.callUpdate = callUpdate;
  15027. this.sizes = /* @__PURE__ */ new Map();
  15028. this.firstRangeTotalSize = 0;
  15029. this.firstRangeAverageSize = 0;
  15030. this.lastCalcIndex = 0;
  15031. this.fixedSizeValue = 0;
  15032. this.calcType = CALC_TYPE.INIT;
  15033. this.offset = 0;
  15034. this.direction = "";
  15035. this.range = /* @__PURE__ */ Object.create(null);
  15036. if (param) {
  15037. this.checkRange(0, param.keeps - 1);
  15038. }
  15039. }
  15040. destroy() {
  15041. this.init(null, null);
  15042. }
  15043. // return current render range
  15044. getRange() {
  15045. const range = /* @__PURE__ */ Object.create(null);
  15046. range.start = this.range.start;
  15047. range.end = this.range.end;
  15048. range.padFront = this.range.padFront;
  15049. range.padBehind = this.range.padBehind;
  15050. return range;
  15051. }
  15052. isBehind() {
  15053. return this.direction === DIRECTION_TYPE.BEHIND;
  15054. }
  15055. isFront() {
  15056. return this.direction === DIRECTION_TYPE.FRONT;
  15057. }
  15058. // return start index offset
  15059. getOffset(start) {
  15060. return (start < 1 ? 0 : this.getIndexOffset(start)) + this.param.slotHeaderSize;
  15061. }
  15062. updateParam(key, value) {
  15063. if (this.param && key in this.param) {
  15064. if (key === "uniqueIds") {
  15065. this.sizes.forEach((v, key2) => {
  15066. if (!value.includes(key2)) {
  15067. this.sizes.delete(key2);
  15068. }
  15069. });
  15070. }
  15071. this.param[key] = value;
  15072. }
  15073. }
  15074. // save each size map by id
  15075. saveSize(id, size) {
  15076. this.sizes.set(id, size);
  15077. if (this.calcType === CALC_TYPE.INIT) {
  15078. this.fixedSizeValue = size;
  15079. this.calcType = CALC_TYPE.FIXED;
  15080. } else if (this.calcType === CALC_TYPE.FIXED && this.fixedSizeValue !== size) {
  15081. this.calcType = CALC_TYPE.DYNAMIC;
  15082. delete this.fixedSizeValue;
  15083. }
  15084. if (this.calcType !== CALC_TYPE.FIXED && typeof this.firstRangeTotalSize !== "undefined") {
  15085. if (this.sizes.size < Math.min(this.param.keeps, this.param.uniqueIds.length)) {
  15086. this.firstRangeTotalSize = [...this.sizes.values()].reduce((acc, val) => acc + val, 0);
  15087. this.firstRangeAverageSize = Math.round(this.firstRangeTotalSize / this.sizes.size);
  15088. } else {
  15089. delete this.firstRangeTotalSize;
  15090. }
  15091. }
  15092. }
  15093. // in some special situation (e.g. length change) we need to update in a row
  15094. // try going to render next range by a leading buffer according to current direction
  15095. handleDataSourcesChange() {
  15096. let start = this.range.start;
  15097. if (this.isFront()) {
  15098. start = start - LEADING_BUFFER;
  15099. } else if (this.isBehind()) {
  15100. start = start + LEADING_BUFFER;
  15101. }
  15102. start = Math.max(start, 0);
  15103. this.updateRange(this.range.start, this.getEndByStart(start));
  15104. }
  15105. // when slot size change, we also need force update
  15106. handleSlotSizeChange() {
  15107. this.handleDataSourcesChange();
  15108. }
  15109. // calculating range on scroll
  15110. handleScroll(offset2) {
  15111. this.direction = offset2 < this.offset ? DIRECTION_TYPE.FRONT : DIRECTION_TYPE.BEHIND;
  15112. this.offset = offset2;
  15113. if (!this.param) {
  15114. return;
  15115. }
  15116. if (this.direction === DIRECTION_TYPE.FRONT) {
  15117. this.handleFront();
  15118. } else if (this.direction === DIRECTION_TYPE.BEHIND) {
  15119. this.handleBehind();
  15120. }
  15121. }
  15122. // ----------- public method end -----------
  15123. handleFront() {
  15124. const overs = this.getScrollOvers();
  15125. if (overs > this.range.start) {
  15126. return;
  15127. }
  15128. const start = Math.max(overs - this.param.buffer, 0);
  15129. this.checkRange(start, this.getEndByStart(start));
  15130. }
  15131. handleBehind() {
  15132. const overs = this.getScrollOvers();
  15133. if (overs < this.range.start + this.param.buffer) {
  15134. return;
  15135. }
  15136. this.checkRange(overs, this.getEndByStart(overs));
  15137. }
  15138. // return the pass overs according to current scroll offset
  15139. getScrollOvers() {
  15140. const offset2 = this.offset - this.param.slotHeaderSize;
  15141. if (offset2 <= 0) {
  15142. return 0;
  15143. }
  15144. if (this.isFixedType()) {
  15145. return Math.floor(offset2 / this.fixedSizeValue);
  15146. }
  15147. let low = 0;
  15148. let middle = 0;
  15149. let middleOffset = 0;
  15150. let high = this.param.uniqueIds.length;
  15151. while (low <= high) {
  15152. middle = low + Math.floor((high - low) / 2);
  15153. middleOffset = this.getIndexOffset(middle);
  15154. if (middleOffset === offset2) {
  15155. return middle;
  15156. } else if (middleOffset < offset2) {
  15157. low = middle + 1;
  15158. } else if (middleOffset > offset2) {
  15159. high = middle - 1;
  15160. }
  15161. }
  15162. return low > 0 ? --low : 0;
  15163. }
  15164. // return a scroll offset from given index, can efficiency be improved more here?
  15165. // although the call frequency is very high, its only a superposition of numbers
  15166. getIndexOffset(givenIndex) {
  15167. if (!givenIndex) {
  15168. return 0;
  15169. }
  15170. let offset2 = 0;
  15171. let indexSize = 0;
  15172. for (let index = 0; index < givenIndex; index++) {
  15173. indexSize = this.sizes.get(this.param.uniqueIds[index]);
  15174. offset2 = offset2 + (typeof indexSize === "number" ? indexSize : this.getEstimateSize());
  15175. }
  15176. this.lastCalcIndex = Math.max(this.lastCalcIndex, givenIndex - 1);
  15177. this.lastCalcIndex = Math.min(this.lastCalcIndex, this.getLastIndex());
  15178. return offset2;
  15179. }
  15180. // is fixed size type
  15181. isFixedType() {
  15182. return this.calcType === CALC_TYPE.FIXED;
  15183. }
  15184. // return the real last index
  15185. getLastIndex() {
  15186. return this.param.uniqueIds.length - 1;
  15187. }
  15188. // in some conditions range is broke, we need correct it
  15189. // and then decide whether need update to next range
  15190. checkRange(start, end) {
  15191. const keeps = this.param.keeps;
  15192. const total = this.param.uniqueIds.length;
  15193. if (total <= keeps) {
  15194. start = 0;
  15195. end = this.getLastIndex();
  15196. } else if (end - start < keeps - 1) {
  15197. start = end - keeps + 1;
  15198. }
  15199. if (this.range.start !== start) {
  15200. this.updateRange(start, end);
  15201. }
  15202. }
  15203. // setting to a new range and rerender
  15204. updateRange(start, end) {
  15205. this.range.start = start;
  15206. this.range.end = end;
  15207. this.range.padFront = this.getPadFront();
  15208. this.range.padBehind = this.getPadBehind();
  15209. this.callUpdate(this.getRange());
  15210. }
  15211. // return end base on start
  15212. getEndByStart(start) {
  15213. const theoryEnd = start + this.param.keeps - 1;
  15214. const truelyEnd = Math.min(theoryEnd, this.getLastIndex());
  15215. return truelyEnd;
  15216. }
  15217. // return total front offset
  15218. getPadFront() {
  15219. if (this.isFixedType()) {
  15220. return this.fixedSizeValue * this.range.start;
  15221. } else {
  15222. return this.getIndexOffset(this.range.start);
  15223. }
  15224. }
  15225. // return total behind offset
  15226. getPadBehind() {
  15227. const end = this.range.end;
  15228. const lastIndex = this.getLastIndex();
  15229. if (this.isFixedType()) {
  15230. return (lastIndex - end) * this.fixedSizeValue;
  15231. }
  15232. if (this.lastCalcIndex === lastIndex) {
  15233. return this.getIndexOffset(lastIndex) - this.getIndexOffset(end);
  15234. } else {
  15235. return (lastIndex - end) * this.getEstimateSize();
  15236. }
  15237. }
  15238. // get the item estimate size
  15239. getEstimateSize() {
  15240. return this.isFixedType() ? this.fixedSizeValue : this.firstRangeAverageSize || this.param.estimateSize;
  15241. }
  15242. }
  15243. function create_fragment$h(ctx) {
  15244. let div;
  15245. let current;
  15246. const default_slot_template = (
  15247. /*#slots*/
  15248. ctx[5].default
  15249. );
  15250. const default_slot = create_slot(
  15251. default_slot_template,
  15252. ctx,
  15253. /*$$scope*/
  15254. ctx[4],
  15255. null
  15256. );
  15257. return {
  15258. c() {
  15259. div = element("div");
  15260. if (default_slot)
  15261. default_slot.c();
  15262. attr(div, "class", "virtual-scroll-item");
  15263. },
  15264. m(target, anchor) {
  15265. insert(target, div, anchor);
  15266. if (default_slot) {
  15267. default_slot.m(div, null);
  15268. }
  15269. ctx[6](div);
  15270. current = true;
  15271. },
  15272. p(ctx2, [dirty]) {
  15273. if (default_slot) {
  15274. if (default_slot.p && (!current || dirty & /*$$scope*/
  15275. 16)) {
  15276. update_slot_base(
  15277. default_slot,
  15278. default_slot_template,
  15279. ctx2,
  15280. /*$$scope*/
  15281. ctx2[4],
  15282. !current ? get_all_dirty_from_scope(
  15283. /*$$scope*/
  15284. ctx2[4]
  15285. ) : get_slot_changes(
  15286. default_slot_template,
  15287. /*$$scope*/
  15288. ctx2[4],
  15289. dirty,
  15290. null
  15291. ),
  15292. null
  15293. );
  15294. }
  15295. }
  15296. },
  15297. i(local) {
  15298. if (current)
  15299. return;
  15300. transition_in(default_slot, local);
  15301. current = true;
  15302. },
  15303. o(local) {
  15304. transition_out(default_slot, local);
  15305. current = false;
  15306. },
  15307. d(detaching) {
  15308. if (detaching)
  15309. detach(div);
  15310. if (default_slot)
  15311. default_slot.d(detaching);
  15312. ctx[6](null);
  15313. }
  15314. };
  15315. }
  15316. function instance$h($$self, $$props, $$invalidate) {
  15317. let { $$slots: slots = {}, $$scope } = $$props;
  15318. let { horizontal = false } = $$props;
  15319. let { uniqueKey } = $$props;
  15320. let { type = "item" } = $$props;
  15321. let resizeObserver2;
  15322. let itemDiv;
  15323. let previousSize;
  15324. const dispatch2 = createEventDispatcher();
  15325. const shapeKey = horizontal ? "offsetWidth" : "offsetHeight";
  15326. onMount(() => {
  15327. if (typeof ResizeObserver !== "undefined") {
  15328. resizeObserver2 = new ResizeObserver(dispatchSizeChange);
  15329. resizeObserver2.observe(itemDiv);
  15330. }
  15331. });
  15332. afterUpdate(dispatchSizeChange);
  15333. onDestroy(() => {
  15334. if (resizeObserver2) {
  15335. resizeObserver2.disconnect();
  15336. resizeObserver2 = null;
  15337. }
  15338. });
  15339. function dispatchSizeChange() {
  15340. const size = itemDiv ? itemDiv[shapeKey] : 0;
  15341. if (size === previousSize)
  15342. return;
  15343. previousSize = size;
  15344. dispatch2("resize", { id: uniqueKey, size, type });
  15345. }
  15346. function div_binding($$value) {
  15347. binding_callbacks[$$value ? "unshift" : "push"](() => {
  15348. itemDiv = $$value;
  15349. $$invalidate(0, itemDiv);
  15350. });
  15351. }
  15352. $$self.$$set = ($$props2) => {
  15353. if ("horizontal" in $$props2)
  15354. $$invalidate(1, horizontal = $$props2.horizontal);
  15355. if ("uniqueKey" in $$props2)
  15356. $$invalidate(2, uniqueKey = $$props2.uniqueKey);
  15357. if ("type" in $$props2)
  15358. $$invalidate(3, type = $$props2.type);
  15359. if ("$$scope" in $$props2)
  15360. $$invalidate(4, $$scope = $$props2.$$scope);
  15361. };
  15362. return [itemDiv, horizontal, uniqueKey, type, $$scope, slots, div_binding];
  15363. }
  15364. class Item extends SvelteComponent {
  15365. constructor(options) {
  15366. super();
  15367. init(this, options, instance$h, create_fragment$h, safe_not_equal, { horizontal: 1, uniqueKey: 2, type: 3 });
  15368. }
  15369. }
  15370. const get_footer_slot_changes = (dirty) => ({ data: dirty[0] & /*displayItems*/
  15371. 4 });
  15372. const get_footer_slot_context = (ctx) => ({ data: (
  15373. /*dataItem*/
  15374. ctx[39]
  15375. ) });
  15376. function get_each_context$6(ctx, list, i) {
  15377. const child_ctx = ctx.slice();
  15378. child_ctx[39] = list[i];
  15379. return child_ctx;
  15380. }
  15381. const get_default_slot_changes = (dirty) => ({ data: dirty[0] & /*displayItems*/
  15382. 4 });
  15383. const get_default_slot_context = (ctx) => ({ data: (
  15384. /*dataItem*/
  15385. ctx[39]
  15386. ) });
  15387. const get_header_slot_changes = (dirty) => ({ data: dirty[0] & /*displayItems*/
  15388. 4 });
  15389. const get_header_slot_context = (ctx) => ({ data: (
  15390. /*dataItem*/
  15391. ctx[39]
  15392. ) });
  15393. function create_if_block_1$3(ctx) {
  15394. let item;
  15395. let current;
  15396. item = new Item({
  15397. props: {
  15398. type: "slot",
  15399. uniqueKey: "header",
  15400. $$slots: { default: [create_default_slot_2$1] },
  15401. $$scope: { ctx }
  15402. }
  15403. });
  15404. item.$on(
  15405. "resize",
  15406. /*onItemResized*/
  15407. ctx[6]
  15408. );
  15409. return {
  15410. c() {
  15411. create_component(item.$$.fragment);
  15412. },
  15413. m(target, anchor) {
  15414. mount_component(item, target, anchor);
  15415. current = true;
  15416. },
  15417. p(ctx2, dirty) {
  15418. const item_changes = {};
  15419. if (dirty[0] & /*$$scope, displayItems*/
  15420. 536870916) {
  15421. item_changes.$$scope = { dirty, ctx: ctx2 };
  15422. }
  15423. item.$set(item_changes);
  15424. },
  15425. i(local) {
  15426. if (current)
  15427. return;
  15428. transition_in(item.$$.fragment, local);
  15429. current = true;
  15430. },
  15431. o(local) {
  15432. transition_out(item.$$.fragment, local);
  15433. current = false;
  15434. },
  15435. d(detaching) {
  15436. destroy_component(item, detaching);
  15437. }
  15438. };
  15439. }
  15440. function create_default_slot_2$1(ctx) {
  15441. let current;
  15442. const header_slot_template = (
  15443. /*#slots*/
  15444. ctx[26].header
  15445. );
  15446. const header_slot = create_slot(
  15447. header_slot_template,
  15448. ctx,
  15449. /*$$scope*/
  15450. ctx[29],
  15451. get_header_slot_context
  15452. );
  15453. return {
  15454. c() {
  15455. if (header_slot)
  15456. header_slot.c();
  15457. },
  15458. m(target, anchor) {
  15459. if (header_slot) {
  15460. header_slot.m(target, anchor);
  15461. }
  15462. current = true;
  15463. },
  15464. p(ctx2, dirty) {
  15465. if (header_slot) {
  15466. if (header_slot.p && (!current || dirty[0] & /*$$scope, displayItems*/
  15467. 536870916)) {
  15468. update_slot_base(
  15469. header_slot,
  15470. header_slot_template,
  15471. ctx2,
  15472. /*$$scope*/
  15473. ctx2[29],
  15474. !current ? get_all_dirty_from_scope(
  15475. /*$$scope*/
  15476. ctx2[29]
  15477. ) : get_slot_changes(
  15478. header_slot_template,
  15479. /*$$scope*/
  15480. ctx2[29],
  15481. dirty,
  15482. get_header_slot_changes
  15483. ),
  15484. get_header_slot_context
  15485. );
  15486. }
  15487. }
  15488. },
  15489. i(local) {
  15490. if (current)
  15491. return;
  15492. transition_in(header_slot, local);
  15493. current = true;
  15494. },
  15495. o(local) {
  15496. transition_out(header_slot, local);
  15497. current = false;
  15498. },
  15499. d(detaching) {
  15500. if (header_slot)
  15501. header_slot.d(detaching);
  15502. }
  15503. };
  15504. }
  15505. function create_default_slot_1$1(ctx) {
  15506. let t;
  15507. let current;
  15508. const default_slot_template = (
  15509. /*#slots*/
  15510. ctx[26].default
  15511. );
  15512. const default_slot = create_slot(
  15513. default_slot_template,
  15514. ctx,
  15515. /*$$scope*/
  15516. ctx[29],
  15517. get_default_slot_context
  15518. );
  15519. return {
  15520. c() {
  15521. if (default_slot)
  15522. default_slot.c();
  15523. t = space();
  15524. },
  15525. m(target, anchor) {
  15526. if (default_slot) {
  15527. default_slot.m(target, anchor);
  15528. }
  15529. insert(target, t, anchor);
  15530. current = true;
  15531. },
  15532. p(ctx2, dirty) {
  15533. if (default_slot) {
  15534. if (default_slot.p && (!current || dirty[0] & /*$$scope, displayItems*/
  15535. 536870916)) {
  15536. update_slot_base(
  15537. default_slot,
  15538. default_slot_template,
  15539. ctx2,
  15540. /*$$scope*/
  15541. ctx2[29],
  15542. !current ? get_all_dirty_from_scope(
  15543. /*$$scope*/
  15544. ctx2[29]
  15545. ) : get_slot_changes(
  15546. default_slot_template,
  15547. /*$$scope*/
  15548. ctx2[29],
  15549. dirty,
  15550. get_default_slot_changes
  15551. ),
  15552. get_default_slot_context
  15553. );
  15554. }
  15555. }
  15556. },
  15557. i(local) {
  15558. if (current)
  15559. return;
  15560. transition_in(default_slot, local);
  15561. current = true;
  15562. },
  15563. o(local) {
  15564. transition_out(default_slot, local);
  15565. current = false;
  15566. },
  15567. d(detaching) {
  15568. if (default_slot)
  15569. default_slot.d(detaching);
  15570. if (detaching)
  15571. detach(t);
  15572. }
  15573. };
  15574. }
  15575. function create_each_block$6(key_2, ctx) {
  15576. let first;
  15577. let item;
  15578. let current;
  15579. item = new Item({
  15580. props: {
  15581. uniqueKey: (
  15582. /*dataItem*/
  15583. ctx[39][
  15584. /*key*/
  15585. ctx[0]
  15586. ]
  15587. ),
  15588. horizontal: (
  15589. /*isHorizontal*/
  15590. ctx[1]
  15591. ),
  15592. type: "item",
  15593. $$slots: { default: [create_default_slot_1$1] },
  15594. $$scope: { ctx }
  15595. }
  15596. });
  15597. item.$on(
  15598. "resize",
  15599. /*onItemResized*/
  15600. ctx[6]
  15601. );
  15602. return {
  15603. key: key_2,
  15604. first: null,
  15605. c() {
  15606. first = empty();
  15607. create_component(item.$$.fragment);
  15608. this.first = first;
  15609. },
  15610. m(target, anchor) {
  15611. insert(target, first, anchor);
  15612. mount_component(item, target, anchor);
  15613. current = true;
  15614. },
  15615. p(new_ctx, dirty) {
  15616. ctx = new_ctx;
  15617. const item_changes = {};
  15618. if (dirty[0] & /*displayItems, key*/
  15619. 5)
  15620. item_changes.uniqueKey = /*dataItem*/
  15621. ctx[39][
  15622. /*key*/
  15623. ctx[0]
  15624. ];
  15625. if (dirty[0] & /*isHorizontal*/
  15626. 2)
  15627. item_changes.horizontal = /*isHorizontal*/
  15628. ctx[1];
  15629. if (dirty[0] & /*$$scope, displayItems*/
  15630. 536870916) {
  15631. item_changes.$$scope = { dirty, ctx };
  15632. }
  15633. item.$set(item_changes);
  15634. },
  15635. i(local) {
  15636. if (current)
  15637. return;
  15638. transition_in(item.$$.fragment, local);
  15639. current = true;
  15640. },
  15641. o(local) {
  15642. transition_out(item.$$.fragment, local);
  15643. current = false;
  15644. },
  15645. d(detaching) {
  15646. if (detaching)
  15647. detach(first);
  15648. destroy_component(item, detaching);
  15649. }
  15650. };
  15651. }
  15652. function create_if_block$6(ctx) {
  15653. let item;
  15654. let current;
  15655. item = new Item({
  15656. props: {
  15657. type: "slot",
  15658. uniqueKey: "footer",
  15659. $$slots: { default: [create_default_slot$2] },
  15660. $$scope: { ctx }
  15661. }
  15662. });
  15663. item.$on(
  15664. "resize",
  15665. /*onItemResized*/
  15666. ctx[6]
  15667. );
  15668. return {
  15669. c() {
  15670. create_component(item.$$.fragment);
  15671. },
  15672. m(target, anchor) {
  15673. mount_component(item, target, anchor);
  15674. current = true;
  15675. },
  15676. p(ctx2, dirty) {
  15677. const item_changes = {};
  15678. if (dirty[0] & /*$$scope, displayItems*/
  15679. 536870916) {
  15680. item_changes.$$scope = { dirty, ctx: ctx2 };
  15681. }
  15682. item.$set(item_changes);
  15683. },
  15684. i(local) {
  15685. if (current)
  15686. return;
  15687. transition_in(item.$$.fragment, local);
  15688. current = true;
  15689. },
  15690. o(local) {
  15691. transition_out(item.$$.fragment, local);
  15692. current = false;
  15693. },
  15694. d(detaching) {
  15695. destroy_component(item, detaching);
  15696. }
  15697. };
  15698. }
  15699. function create_default_slot$2(ctx) {
  15700. let current;
  15701. const footer_slot_template = (
  15702. /*#slots*/
  15703. ctx[26].footer
  15704. );
  15705. const footer_slot = create_slot(
  15706. footer_slot_template,
  15707. ctx,
  15708. /*$$scope*/
  15709. ctx[29],
  15710. get_footer_slot_context
  15711. );
  15712. return {
  15713. c() {
  15714. if (footer_slot)
  15715. footer_slot.c();
  15716. },
  15717. m(target, anchor) {
  15718. if (footer_slot) {
  15719. footer_slot.m(target, anchor);
  15720. }
  15721. current = true;
  15722. },
  15723. p(ctx2, dirty) {
  15724. if (footer_slot) {
  15725. if (footer_slot.p && (!current || dirty[0] & /*$$scope, displayItems*/
  15726. 536870916)) {
  15727. update_slot_base(
  15728. footer_slot,
  15729. footer_slot_template,
  15730. ctx2,
  15731. /*$$scope*/
  15732. ctx2[29],
  15733. !current ? get_all_dirty_from_scope(
  15734. /*$$scope*/
  15735. ctx2[29]
  15736. ) : get_slot_changes(
  15737. footer_slot_template,
  15738. /*$$scope*/
  15739. ctx2[29],
  15740. dirty,
  15741. get_footer_slot_changes
  15742. ),
  15743. get_footer_slot_context
  15744. );
  15745. }
  15746. }
  15747. },
  15748. i(local) {
  15749. if (current)
  15750. return;
  15751. transition_in(footer_slot, local);
  15752. current = true;
  15753. },
  15754. o(local) {
  15755. transition_out(footer_slot, local);
  15756. current = false;
  15757. },
  15758. d(detaching) {
  15759. if (footer_slot)
  15760. footer_slot.d(detaching);
  15761. }
  15762. };
  15763. }
  15764. function create_fragment$g(ctx) {
  15765. let div2;
  15766. let t0;
  15767. let div0;
  15768. let each_blocks = [];
  15769. let each_1_lookup = /* @__PURE__ */ new Map();
  15770. let t1;
  15771. let t2;
  15772. let div1;
  15773. let current;
  15774. let mounted;
  15775. let dispose;
  15776. let if_block0 = (
  15777. /*$$slots*/
  15778. ctx[8].header && create_if_block_1$3(ctx)
  15779. );
  15780. let each_value = (
  15781. /*displayItems*/
  15782. ctx[2]
  15783. );
  15784. const get_key = (ctx2) => (
  15785. /*dataItem*/
  15786. ctx2[39][
  15787. /*key*/
  15788. ctx2[0]
  15789. ]
  15790. );
  15791. for (let i = 0; i < each_value.length; i += 1) {
  15792. let child_ctx = get_each_context$6(ctx, each_value, i);
  15793. let key = get_key(child_ctx);
  15794. each_1_lookup.set(key, each_blocks[i] = create_each_block$6(key, child_ctx));
  15795. }
  15796. let if_block1 = (
  15797. /*$$slots*/
  15798. ctx[8].footer && create_if_block$6(ctx)
  15799. );
  15800. return {
  15801. c() {
  15802. div2 = element("div");
  15803. if (if_block0)
  15804. if_block0.c();
  15805. t0 = space();
  15806. div0 = element("div");
  15807. for (let i = 0; i < each_blocks.length; i += 1) {
  15808. each_blocks[i].c();
  15809. }
  15810. t1 = space();
  15811. if (if_block1)
  15812. if_block1.c();
  15813. t2 = space();
  15814. div1 = element("div");
  15815. set_style(
  15816. div0,
  15817. "padding",
  15818. /*paddingStyle*/
  15819. ctx[3]
  15820. );
  15821. attr(div1, "class", "shepherd");
  15822. set_style(
  15823. div1,
  15824. "width",
  15825. /*isHorizontal*/
  15826. ctx[1] ? "0px" : "100%"
  15827. );
  15828. set_style(
  15829. div1,
  15830. "height",
  15831. /*isHorizontal*/
  15832. ctx[1] ? "100%" : "0px"
  15833. );
  15834. set_style(div2, "overflow-y", "auto");
  15835. set_style(div2, "height", "inherit");
  15836. },
  15837. m(target, anchor) {
  15838. insert(target, div2, anchor);
  15839. if (if_block0)
  15840. if_block0.m(div2, null);
  15841. append(div2, t0);
  15842. append(div2, div0);
  15843. for (let i = 0; i < each_blocks.length; i += 1) {
  15844. each_blocks[i].m(div0, null);
  15845. }
  15846. append(div2, t1);
  15847. if (if_block1)
  15848. if_block1.m(div2, null);
  15849. append(div2, t2);
  15850. append(div2, div1);
  15851. ctx[27](div1);
  15852. ctx[28](div2);
  15853. current = true;
  15854. if (!mounted) {
  15855. dispose = listen(
  15856. div2,
  15857. "scroll",
  15858. /*onScroll*/
  15859. ctx[7]
  15860. );
  15861. mounted = true;
  15862. }
  15863. },
  15864. p(ctx2, dirty) {
  15865. if (
  15866. /*$$slots*/
  15867. ctx2[8].header
  15868. ) {
  15869. if (if_block0) {
  15870. if_block0.p(ctx2, dirty);
  15871. if (dirty[0] & /*$$slots*/
  15872. 256) {
  15873. transition_in(if_block0, 1);
  15874. }
  15875. } else {
  15876. if_block0 = create_if_block_1$3(ctx2);
  15877. if_block0.c();
  15878. transition_in(if_block0, 1);
  15879. if_block0.m(div2, t0);
  15880. }
  15881. } else if (if_block0) {
  15882. group_outros();
  15883. transition_out(if_block0, 1, 1, () => {
  15884. if_block0 = null;
  15885. });
  15886. check_outros();
  15887. }
  15888. if (dirty[0] & /*displayItems, key, isHorizontal, onItemResized, $$scope*/
  15889. 536870983) {
  15890. each_value = /*displayItems*/
  15891. ctx2[2];
  15892. group_outros();
  15893. 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);
  15894. check_outros();
  15895. }
  15896. if (!current || dirty[0] & /*paddingStyle*/
  15897. 8) {
  15898. set_style(
  15899. div0,
  15900. "padding",
  15901. /*paddingStyle*/
  15902. ctx2[3]
  15903. );
  15904. }
  15905. if (
  15906. /*$$slots*/
  15907. ctx2[8].footer
  15908. ) {
  15909. if (if_block1) {
  15910. if_block1.p(ctx2, dirty);
  15911. if (dirty[0] & /*$$slots*/
  15912. 256) {
  15913. transition_in(if_block1, 1);
  15914. }
  15915. } else {
  15916. if_block1 = create_if_block$6(ctx2);
  15917. if_block1.c();
  15918. transition_in(if_block1, 1);
  15919. if_block1.m(div2, t2);
  15920. }
  15921. } else if (if_block1) {
  15922. group_outros();
  15923. transition_out(if_block1, 1, 1, () => {
  15924. if_block1 = null;
  15925. });
  15926. check_outros();
  15927. }
  15928. if (!current || dirty[0] & /*isHorizontal*/
  15929. 2) {
  15930. set_style(
  15931. div1,
  15932. "width",
  15933. /*isHorizontal*/
  15934. ctx2[1] ? "0px" : "100%"
  15935. );
  15936. }
  15937. if (!current || dirty[0] & /*isHorizontal*/
  15938. 2) {
  15939. set_style(
  15940. div1,
  15941. "height",
  15942. /*isHorizontal*/
  15943. ctx2[1] ? "100%" : "0px"
  15944. );
  15945. }
  15946. },
  15947. i(local) {
  15948. if (current)
  15949. return;
  15950. transition_in(if_block0);
  15951. for (let i = 0; i < each_value.length; i += 1) {
  15952. transition_in(each_blocks[i]);
  15953. }
  15954. transition_in(if_block1);
  15955. current = true;
  15956. },
  15957. o(local) {
  15958. transition_out(if_block0);
  15959. for (let i = 0; i < each_blocks.length; i += 1) {
  15960. transition_out(each_blocks[i]);
  15961. }
  15962. transition_out(if_block1);
  15963. current = false;
  15964. },
  15965. d(detaching) {
  15966. if (detaching)
  15967. detach(div2);
  15968. if (if_block0)
  15969. if_block0.d();
  15970. for (let i = 0; i < each_blocks.length; i += 1) {
  15971. each_blocks[i].d();
  15972. }
  15973. if (if_block1)
  15974. if_block1.d();
  15975. ctx[27](null);
  15976. ctx[28](null);
  15977. mounted = false;
  15978. dispose();
  15979. }
  15980. };
  15981. }
  15982. function instance$g($$self, $$props, $$invalidate) {
  15983. let { $$slots: slots = {}, $$scope } = $$props;
  15984. const $$slots = compute_slots(slots);
  15985. let { key = "id" } = $$props;
  15986. let { data } = $$props;
  15987. let { keeps = 30 } = $$props;
  15988. let { estimateSize = 50 } = $$props;
  15989. let { isHorizontal = false } = $$props;
  15990. let { start = 0 } = $$props;
  15991. let { offset: offset2 = 0 } = $$props;
  15992. let { pageMode = false } = $$props;
  15993. let { topThreshold = 0 } = $$props;
  15994. let { bottomThreshold = 0 } = $$props;
  15995. let displayItems = [];
  15996. let paddingStyle;
  15997. let directionKey = isHorizontal ? "scrollLeft" : "scrollTop";
  15998. let range = null;
  15999. let virtual = new Virtual(
  16000. {
  16001. slotHeaderSize: 0,
  16002. slotFooterSize: 0,
  16003. keeps,
  16004. estimateSize,
  16005. buffer: Math.round(keeps / 3),
  16006. // recommend for a third of keeps
  16007. uniqueIds: getUniqueIdFromDataSources()
  16008. },
  16009. onRangeChanged
  16010. );
  16011. let root;
  16012. let shepherd;
  16013. const dispatch2 = createEventDispatcher();
  16014. function getSize(id) {
  16015. return virtual.sizes.get(id);
  16016. }
  16017. function getSizes() {
  16018. return virtual.sizes.size;
  16019. }
  16020. function getOffset() {
  16021. if (pageMode) {
  16022. return document.documentElement[directionKey] || document.body[directionKey];
  16023. } else {
  16024. return root ? Math.ceil(root[directionKey]) : 0;
  16025. }
  16026. }
  16027. function getClientSize() {
  16028. const key2 = isHorizontal ? "clientWidth" : "clientHeight";
  16029. if (pageMode) {
  16030. return document.documentElement[key2] || document.body[key2];
  16031. } else {
  16032. return root ? Math.ceil(root[key2]) : 0;
  16033. }
  16034. }
  16035. function getScrollSize() {
  16036. const key2 = isHorizontal ? "scrollWidth" : "scrollHeight";
  16037. if (pageMode) {
  16038. return document.documentElement[key2] || document.body[key2];
  16039. } else {
  16040. return root ? Math.ceil(root[key2]) : 0;
  16041. }
  16042. }
  16043. function updatePageModeFront() {
  16044. if (root) {
  16045. const rect = root.getBoundingClientRect();
  16046. const { defaultView } = root.ownerDocument;
  16047. const offsetFront = isHorizontal ? rect.left + defaultView.pageXOffset : rect.top + defaultView.pageYOffset;
  16048. virtual.updateParam("slotHeaderSize", offsetFront);
  16049. }
  16050. }
  16051. function scrollToOffset(offset3) {
  16052. if (pageMode) {
  16053. document.body[directionKey] = offset3;
  16054. document.documentElement[directionKey] = offset3;
  16055. } else if (root) {
  16056. $$invalidate(4, root[directionKey] = offset3, root);
  16057. }
  16058. }
  16059. function scrollToIndex(index) {
  16060. if (index >= data.length - 1) {
  16061. scrollToBottom();
  16062. } else {
  16063. const offset3 = virtual.getOffset(index);
  16064. scrollToOffset(offset3);
  16065. }
  16066. }
  16067. function scrollToBottom() {
  16068. if (shepherd) {
  16069. const offset3 = shepherd[isHorizontal ? "offsetLeft" : "offsetTop"];
  16070. scrollToOffset(offset3);
  16071. setTimeout(
  16072. () => {
  16073. if (getOffset() + getClientSize() + 1 < getScrollSize()) {
  16074. scrollToBottom();
  16075. }
  16076. },
  16077. 3
  16078. );
  16079. }
  16080. }
  16081. onMount(() => {
  16082. if (start) {
  16083. scrollToIndex(start);
  16084. } else if (offset2) {
  16085. scrollToOffset(offset2);
  16086. }
  16087. if (pageMode) {
  16088. updatePageModeFront();
  16089. document.addEventListener("scroll", onScroll, { passive: false });
  16090. }
  16091. });
  16092. onDestroy(() => {
  16093. virtual.destroy();
  16094. if (pageMode) {
  16095. document.removeEventListener("scroll", onScroll);
  16096. }
  16097. });
  16098. function getUniqueIdFromDataSources() {
  16099. return data.map((dataSource) => dataSource[key]);
  16100. }
  16101. function onItemResized(event) {
  16102. const { id, size, type } = event.detail;
  16103. if (type === "item")
  16104. virtual.saveSize(id, size);
  16105. else if (type === "slot") {
  16106. if (id === "header")
  16107. virtual.updateParam("slotHeaderSize", size);
  16108. else if (id === "footer")
  16109. virtual.updateParam("slotFooterSize", size);
  16110. }
  16111. }
  16112. function onRangeChanged(range_) {
  16113. range = range_;
  16114. $$invalidate(3, paddingStyle = $$invalidate(3, paddingStyle = isHorizontal ? `0px ${range.padBehind}px 0px ${range.padFront}px` : `${range.padFront}px 0px ${range.padBehind}px`));
  16115. $$invalidate(2, displayItems = data.slice(range.start, range.end + 1));
  16116. }
  16117. function onScroll(event) {
  16118. const offset3 = getOffset();
  16119. const clientSize = getClientSize();
  16120. const scrollSize = getScrollSize();
  16121. if (offset3 < 0 || offset3 + clientSize > scrollSize || !scrollSize) {
  16122. return;
  16123. }
  16124. virtual.handleScroll(offset3);
  16125. emitEvent(offset3, clientSize, scrollSize, event);
  16126. }
  16127. function emitEvent(offset3, clientSize, scrollSize, event) {
  16128. dispatch2("scroll", { event, range: virtual.getRange() });
  16129. if (virtual.isFront() && !!data.length && offset3 - topThreshold <= 0) {
  16130. dispatch2("top");
  16131. } else if (virtual.isBehind() && offset3 + clientSize + bottomThreshold >= scrollSize) {
  16132. dispatch2("bottom");
  16133. }
  16134. }
  16135. function handleKeepsChange(keeps2) {
  16136. virtual.updateParam("keeps", keeps2);
  16137. virtual.handleSlotSizeChange();
  16138. }
  16139. async function handleDataSourcesChange(data2) {
  16140. virtual.updateParam("uniqueIds", getUniqueIdFromDataSources());
  16141. virtual.handleDataSourcesChange();
  16142. }
  16143. function div1_binding($$value) {
  16144. binding_callbacks[$$value ? "unshift" : "push"](() => {
  16145. shepherd = $$value;
  16146. $$invalidate(5, shepherd);
  16147. });
  16148. }
  16149. function div2_binding($$value) {
  16150. binding_callbacks[$$value ? "unshift" : "push"](() => {
  16151. root = $$value;
  16152. $$invalidate(4, root);
  16153. });
  16154. }
  16155. $$self.$$set = ($$props2) => {
  16156. if ("key" in $$props2)
  16157. $$invalidate(0, key = $$props2.key);
  16158. if ("data" in $$props2)
  16159. $$invalidate(9, data = $$props2.data);
  16160. if ("keeps" in $$props2)
  16161. $$invalidate(10, keeps = $$props2.keeps);
  16162. if ("estimateSize" in $$props2)
  16163. $$invalidate(11, estimateSize = $$props2.estimateSize);
  16164. if ("isHorizontal" in $$props2)
  16165. $$invalidate(1, isHorizontal = $$props2.isHorizontal);
  16166. if ("start" in $$props2)
  16167. $$invalidate(12, start = $$props2.start);
  16168. if ("offset" in $$props2)
  16169. $$invalidate(13, offset2 = $$props2.offset);
  16170. if ("pageMode" in $$props2)
  16171. $$invalidate(14, pageMode = $$props2.pageMode);
  16172. if ("topThreshold" in $$props2)
  16173. $$invalidate(15, topThreshold = $$props2.topThreshold);
  16174. if ("bottomThreshold" in $$props2)
  16175. $$invalidate(16, bottomThreshold = $$props2.bottomThreshold);
  16176. if ("$$scope" in $$props2)
  16177. $$invalidate(29, $$scope = $$props2.$$scope);
  16178. };
  16179. $$self.$$.update = () => {
  16180. if ($$self.$$.dirty[0] & /*offset*/
  16181. 8192) {
  16182. scrollToOffset(offset2);
  16183. }
  16184. if ($$self.$$.dirty[0] & /*start*/
  16185. 4096) {
  16186. scrollToIndex(start);
  16187. }
  16188. if ($$self.$$.dirty[0] & /*keeps*/
  16189. 1024) {
  16190. handleKeepsChange(keeps);
  16191. }
  16192. if ($$self.$$.dirty[0] & /*data*/
  16193. 512) {
  16194. handleDataSourcesChange();
  16195. }
  16196. };
  16197. return [
  16198. key,
  16199. isHorizontal,
  16200. displayItems,
  16201. paddingStyle,
  16202. root,
  16203. shepherd,
  16204. onItemResized,
  16205. onScroll,
  16206. $$slots,
  16207. data,
  16208. keeps,
  16209. estimateSize,
  16210. start,
  16211. offset2,
  16212. pageMode,
  16213. topThreshold,
  16214. bottomThreshold,
  16215. getSize,
  16216. getSizes,
  16217. getOffset,
  16218. getClientSize,
  16219. getScrollSize,
  16220. updatePageModeFront,
  16221. scrollToOffset,
  16222. scrollToIndex,
  16223. scrollToBottom,
  16224. slots,
  16225. div1_binding,
  16226. div2_binding,
  16227. $$scope
  16228. ];
  16229. }
  16230. class VirtualScroll extends SvelteComponent {
  16231. constructor(options) {
  16232. super();
  16233. init(
  16234. this,
  16235. options,
  16236. instance$g,
  16237. create_fragment$g,
  16238. safe_not_equal,
  16239. {
  16240. key: 0,
  16241. data: 9,
  16242. keeps: 10,
  16243. estimateSize: 11,
  16244. isHorizontal: 1,
  16245. start: 12,
  16246. offset: 13,
  16247. pageMode: 14,
  16248. topThreshold: 15,
  16249. bottomThreshold: 16,
  16250. getSize: 17,
  16251. getSizes: 18,
  16252. getOffset: 19,
  16253. getClientSize: 20,
  16254. getScrollSize: 21,
  16255. updatePageModeFront: 22,
  16256. scrollToOffset: 23,
  16257. scrollToIndex: 24,
  16258. scrollToBottom: 25
  16259. },
  16260. null,
  16261. [-1, -1]
  16262. );
  16263. }
  16264. get getSize() {
  16265. return this.$$.ctx[17];
  16266. }
  16267. get getSizes() {
  16268. return this.$$.ctx[18];
  16269. }
  16270. get getOffset() {
  16271. return this.$$.ctx[19];
  16272. }
  16273. get getClientSize() {
  16274. return this.$$.ctx[20];
  16275. }
  16276. get getScrollSize() {
  16277. return this.$$.ctx[21];
  16278. }
  16279. get updatePageModeFront() {
  16280. return this.$$.ctx[22];
  16281. }
  16282. get scrollToOffset() {
  16283. return this.$$.ctx[23];
  16284. }
  16285. get scrollToIndex() {
  16286. return this.$$.ctx[24];
  16287. }
  16288. get scrollToBottom() {
  16289. return this.$$.ctx[25];
  16290. }
  16291. }
  16292. const databaseShell_svelte_svelte_type_style_lang = "";
  16293. function get_each_context$5(ctx, list, i) {
  16294. const child_ctx = ctx.slice();
  16295. child_ctx[41] = list[i];
  16296. child_ctx[43] = i;
  16297. return child_ctx;
  16298. }
  16299. function create_each_block$5(ctx) {
  16300. let option;
  16301. let t_value = (
  16302. /*pack*/
  16303. ctx[41] + ""
  16304. );
  16305. let t;
  16306. let option_value_value;
  16307. return {
  16308. c() {
  16309. option = element("option");
  16310. t = text$1(t_value);
  16311. option.__value = option_value_value = /*pack*/
  16312. ctx[41];
  16313. option.value = option.__value;
  16314. },
  16315. m(target, anchor) {
  16316. insert(target, option, anchor);
  16317. append(option, t);
  16318. },
  16319. p(ctx2, dirty) {
  16320. if (dirty[0] & /*$packStore*/
  16321. 128 && t_value !== (t_value = /*pack*/
  16322. ctx2[41] + ""))
  16323. set_data(t, t_value);
  16324. if (dirty[0] & /*$packStore*/
  16325. 128 && option_value_value !== (option_value_value = /*pack*/
  16326. ctx2[41])) {
  16327. option.__value = option_value_value;
  16328. option.value = option.__value;
  16329. }
  16330. },
  16331. d(detaching) {
  16332. if (detaching)
  16333. detach(option);
  16334. }
  16335. };
  16336. }
  16337. function create_else_block_1(ctx) {
  16338. let div;
  16339. let virtualscroll;
  16340. let current;
  16341. virtualscroll = new VirtualScroll({
  16342. props: {
  16343. data: (
  16344. /*$visibleTreeStore*/
  16345. ctx[9]
  16346. ),
  16347. key: "fullPath",
  16348. $$slots: {
  16349. default: [
  16350. create_default_slot_2,
  16351. ({ data }) => ({ 40: data }),
  16352. ({ data }) => [0, data ? 512 : 0]
  16353. ]
  16354. },
  16355. $$scope: { ctx }
  16356. }
  16357. });
  16358. return {
  16359. c() {
  16360. div = element("div");
  16361. create_component(virtualscroll.$$.fragment);
  16362. attr(div, "class", "sequencer-database-entries-tree svelte-ese-mpsiyo");
  16363. },
  16364. m(target, anchor) {
  16365. insert(target, div, anchor);
  16366. mount_component(virtualscroll, div, null);
  16367. current = true;
  16368. },
  16369. p(ctx2, dirty) {
  16370. const virtualscroll_changes = {};
  16371. if (dirty[0] & /*$visibleTreeStore*/
  16372. 512)
  16373. virtualscroll_changes.data = /*$visibleTreeStore*/
  16374. ctx2[9];
  16375. if (dirty[1] & /*$$scope, data*/
  16376. 8704) {
  16377. virtualscroll_changes.$$scope = { dirty, ctx: ctx2 };
  16378. }
  16379. virtualscroll.$set(virtualscroll_changes);
  16380. },
  16381. i(local) {
  16382. if (current)
  16383. return;
  16384. transition_in(virtualscroll.$$.fragment, local);
  16385. current = true;
  16386. },
  16387. o(local) {
  16388. transition_out(virtualscroll.$$.fragment, local);
  16389. current = false;
  16390. },
  16391. d(detaching) {
  16392. if (detaching)
  16393. detach(div);
  16394. destroy_component(virtualscroll);
  16395. }
  16396. };
  16397. }
  16398. function create_if_block_1$2(ctx) {
  16399. let div;
  16400. let virtualscroll;
  16401. let current;
  16402. virtualscroll = new VirtualScroll({
  16403. props: {
  16404. data: (
  16405. /*filteredEntries*/
  16406. ctx[6]
  16407. ),
  16408. key: "entry",
  16409. $$slots: {
  16410. default: [
  16411. create_default_slot_1,
  16412. ({ data }) => ({ 40: data }),
  16413. ({ data }) => [0, data ? 512 : 0]
  16414. ]
  16415. },
  16416. $$scope: { ctx }
  16417. }
  16418. });
  16419. return {
  16420. c() {
  16421. div = element("div");
  16422. create_component(virtualscroll.$$.fragment);
  16423. attr(div, "class", "sequencer-database-entries svelte-ese-mpsiyo");
  16424. },
  16425. m(target, anchor) {
  16426. insert(target, div, anchor);
  16427. mount_component(virtualscroll, div, null);
  16428. current = true;
  16429. },
  16430. p(ctx2, dirty) {
  16431. const virtualscroll_changes = {};
  16432. if (dirty[0] & /*filteredEntries*/
  16433. 64)
  16434. virtualscroll_changes.data = /*filteredEntries*/
  16435. ctx2[6];
  16436. if (dirty[1] & /*$$scope, data*/
  16437. 8704) {
  16438. virtualscroll_changes.$$scope = { dirty, ctx: ctx2 };
  16439. }
  16440. virtualscroll.$set(virtualscroll_changes);
  16441. },
  16442. i(local) {
  16443. if (current)
  16444. return;
  16445. transition_in(virtualscroll.$$.fragment, local);
  16446. current = true;
  16447. },
  16448. o(local) {
  16449. transition_out(virtualscroll.$$.fragment, local);
  16450. current = false;
  16451. },
  16452. d(detaching) {
  16453. if (detaching)
  16454. detach(div);
  16455. destroy_component(virtualscroll);
  16456. }
  16457. };
  16458. }
  16459. function create_default_slot_2(ctx) {
  16460. let switch_instance;
  16461. let switch_instance_anchor;
  16462. let current;
  16463. var switch_value = (
  16464. /*data*/
  16465. ctx[40].class
  16466. );
  16467. function switch_props(ctx2) {
  16468. return { props: { data: (
  16469. /*data*/
  16470. ctx2[40]
  16471. ) } };
  16472. }
  16473. if (switch_value) {
  16474. switch_instance = construct_svelte_component(switch_value, switch_props(ctx));
  16475. }
  16476. return {
  16477. c() {
  16478. if (switch_instance)
  16479. create_component(switch_instance.$$.fragment);
  16480. switch_instance_anchor = empty();
  16481. },
  16482. m(target, anchor) {
  16483. if (switch_instance)
  16484. mount_component(switch_instance, target, anchor);
  16485. insert(target, switch_instance_anchor, anchor);
  16486. current = true;
  16487. },
  16488. p(ctx2, dirty) {
  16489. const switch_instance_changes = {};
  16490. if (dirty[1] & /*data*/
  16491. 512)
  16492. switch_instance_changes.data = /*data*/
  16493. ctx2[40];
  16494. if (switch_value !== (switch_value = /*data*/
  16495. ctx2[40].class)) {
  16496. if (switch_instance) {
  16497. group_outros();
  16498. const old_component = switch_instance;
  16499. transition_out(old_component.$$.fragment, 1, 0, () => {
  16500. destroy_component(old_component, 1);
  16501. });
  16502. check_outros();
  16503. }
  16504. if (switch_value) {
  16505. switch_instance = construct_svelte_component(switch_value, switch_props(ctx2));
  16506. create_component(switch_instance.$$.fragment);
  16507. transition_in(switch_instance.$$.fragment, 1);
  16508. mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
  16509. } else {
  16510. switch_instance = null;
  16511. }
  16512. } else if (switch_value) {
  16513. switch_instance.$set(switch_instance_changes);
  16514. }
  16515. },
  16516. i(local) {
  16517. if (current)
  16518. return;
  16519. if (switch_instance)
  16520. transition_in(switch_instance.$$.fragment, local);
  16521. current = true;
  16522. },
  16523. o(local) {
  16524. if (switch_instance)
  16525. transition_out(switch_instance.$$.fragment, local);
  16526. current = false;
  16527. },
  16528. d(detaching) {
  16529. if (detaching)
  16530. detach(switch_instance_anchor);
  16531. if (switch_instance)
  16532. destroy_component(switch_instance, detaching);
  16533. }
  16534. };
  16535. }
  16536. function create_default_slot_1(ctx) {
  16537. let databaseentry;
  16538. let current;
  16539. databaseentry = new DatabaseEntry({ props: { entry: (
  16540. /*data*/
  16541. ctx[40].entry
  16542. ) } });
  16543. return {
  16544. c() {
  16545. create_component(databaseentry.$$.fragment);
  16546. },
  16547. m(target, anchor) {
  16548. mount_component(databaseentry, target, anchor);
  16549. current = true;
  16550. },
  16551. p(ctx2, dirty) {
  16552. const databaseentry_changes = {};
  16553. if (dirty[1] & /*data*/
  16554. 512)
  16555. databaseentry_changes.entry = /*data*/
  16556. ctx2[40].entry;
  16557. databaseentry.$set(databaseentry_changes);
  16558. },
  16559. i(local) {
  16560. if (current)
  16561. return;
  16562. transition_in(databaseentry.$$.fragment, local);
  16563. current = true;
  16564. },
  16565. o(local) {
  16566. transition_out(databaseentry.$$.fragment, local);
  16567. current = false;
  16568. },
  16569. d(detaching) {
  16570. destroy_component(databaseentry, detaching);
  16571. }
  16572. };
  16573. }
  16574. function create_else_block$1(ctx) {
  16575. let t;
  16576. return {
  16577. c() {
  16578. t = text$1("No file loaded...");
  16579. },
  16580. m(target, anchor) {
  16581. insert(target, t, anchor);
  16582. },
  16583. p: noop,
  16584. d(detaching) {
  16585. if (detaching)
  16586. detach(t);
  16587. }
  16588. };
  16589. }
  16590. function create_if_block$5(ctx) {
  16591. let t0;
  16592. let t1_value = (
  16593. /*$metadata*/
  16594. ctx[10].type + ""
  16595. );
  16596. let t1;
  16597. let t2;
  16598. let t3_value = (
  16599. /*$metadata*/
  16600. ctx[10].duration + ""
  16601. );
  16602. let t3;
  16603. return {
  16604. c() {
  16605. t0 = text$1("Type: ");
  16606. t1 = text$1(t1_value);
  16607. t2 = text$1(" | Duration: ");
  16608. t3 = text$1(t3_value);
  16609. },
  16610. m(target, anchor) {
  16611. insert(target, t0, anchor);
  16612. insert(target, t1, anchor);
  16613. insert(target, t2, anchor);
  16614. insert(target, t3, anchor);
  16615. },
  16616. p(ctx2, dirty) {
  16617. if (dirty[0] & /*$metadata*/
  16618. 1024 && t1_value !== (t1_value = /*$metadata*/
  16619. ctx2[10].type + ""))
  16620. set_data(t1, t1_value);
  16621. if (dirty[0] & /*$metadata*/
  16622. 1024 && t3_value !== (t3_value = /*$metadata*/
  16623. ctx2[10].duration + ""))
  16624. set_data(t3, t3_value);
  16625. },
  16626. d(detaching) {
  16627. if (detaching)
  16628. detach(t0);
  16629. if (detaching)
  16630. detach(t1);
  16631. if (detaching)
  16632. detach(t2);
  16633. if (detaching)
  16634. detach(t3);
  16635. }
  16636. };
  16637. }
  16638. function create_default_slot$1(ctx) {
  16639. let div4;
  16640. let div0;
  16641. let select;
  16642. let option;
  16643. let t1;
  16644. let input0;
  16645. let t2;
  16646. let input1;
  16647. let t3;
  16648. let label0;
  16649. let t5;
  16650. let input2;
  16651. let t6;
  16652. let label1;
  16653. let t8;
  16654. let input3;
  16655. let t9;
  16656. let label2;
  16657. let t11;
  16658. let div3;
  16659. let current_block_type_index;
  16660. let if_block0;
  16661. let t12;
  16662. let div2;
  16663. let video;
  16664. let t13;
  16665. let img;
  16666. let t14;
  16667. let audio2;
  16668. let t15;
  16669. let div1;
  16670. let current;
  16671. let mounted;
  16672. let dispose;
  16673. let each_value = (
  16674. /*$packStore*/
  16675. ctx[7]
  16676. );
  16677. let each_blocks = [];
  16678. for (let i = 0; i < each_value.length; i += 1) {
  16679. each_blocks[i] = create_each_block$5(get_each_context$5(ctx, each_value, i));
  16680. }
  16681. const if_block_creators = [create_if_block_1$2, create_else_block_1];
  16682. const if_blocks = [];
  16683. function select_block_type(ctx2, dirty) {
  16684. if (
  16685. /*$listView*/
  16686. ctx2[4]
  16687. )
  16688. return 0;
  16689. return 1;
  16690. }
  16691. current_block_type_index = select_block_type(ctx);
  16692. if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
  16693. function select_block_type_1(ctx2, dirty) {
  16694. if (
  16695. /*$metadata*/
  16696. ctx2[10]
  16697. )
  16698. return create_if_block$5;
  16699. return create_else_block$1;
  16700. }
  16701. let current_block_type = select_block_type_1(ctx);
  16702. let if_block1 = current_block_type(ctx);
  16703. return {
  16704. c() {
  16705. div4 = element("div");
  16706. div0 = element("div");
  16707. select = element("select");
  16708. option = element("option");
  16709. option.textContent = `${localize("SEQUENCER.Database.AllPacks")}`;
  16710. for (let i = 0; i < each_blocks.length; i += 1) {
  16711. each_blocks[i].c();
  16712. }
  16713. t1 = space();
  16714. input0 = element("input");
  16715. t2 = space();
  16716. input1 = element("input");
  16717. t3 = space();
  16718. label0 = element("label");
  16719. label0.textContent = `${localize("SEQUENCER.Database.ShowAllRanges")}`;
  16720. t5 = space();
  16721. input2 = element("input");
  16722. t6 = space();
  16723. label1 = element("label");
  16724. label1.textContent = `${localize("SEQUENCER.Database.ShowSubLists")}`;
  16725. t8 = space();
  16726. input3 = element("input");
  16727. t9 = space();
  16728. label2 = element("label");
  16729. label2.textContent = `${localize("SEQUENCER.Database.ListView")}`;
  16730. t11 = space();
  16731. div3 = element("div");
  16732. if_block0.c();
  16733. t12 = space();
  16734. div2 = element("div");
  16735. video = element("video");
  16736. t13 = space();
  16737. img = element("img");
  16738. t14 = space();
  16739. audio2 = element("audio");
  16740. audio2.innerHTML = `<source type="audio/ogg"/>`;
  16741. t15 = space();
  16742. div1 = element("div");
  16743. if_block1.c();
  16744. option.__value = "all";
  16745. option.value = option.__value;
  16746. attr(select, "name", "pack-select");
  16747. attr(select, "class", "svelte-ese-mpsiyo");
  16748. if (
  16749. /*$selectedPackStore*/
  16750. ctx[2] === void 0
  16751. )
  16752. add_render_callback(() => (
  16753. /*select_change_handler*/
  16754. ctx[26].call(select)
  16755. ));
  16756. attr(input0, "class", "ml-2 svelte-ese-mpsiyo");
  16757. attr(input0, "placeholder", localize("SEQUENCER.Database.Search"));
  16758. attr(input0, "type", "text");
  16759. attr(input1, "class", "ml-2");
  16760. attr(input1, "id", "database-all-ranges");
  16761. attr(input1, "type", "checkbox");
  16762. attr(label0, "class", "all-ranges-label svelte-ese-mpsiyo");
  16763. attr(label0, "for", "database-all-ranges");
  16764. attr(input2, "class", "ml-2");
  16765. attr(input2, "id", "include-sub-lists");
  16766. attr(input2, "type", "checkbox");
  16767. attr(label1, "class", "all-ranges-label svelte-ese-mpsiyo");
  16768. attr(label1, "for", "include-sub-lists");
  16769. attr(input3, "class", "ml-2");
  16770. attr(input3, "id", "treeview");
  16771. attr(input3, "type", "checkbox");
  16772. attr(label2, "class", "all-ranges-label svelte-ese-mpsiyo");
  16773. attr(label2, "for", "treeview");
  16774. attr(div0, "class", "sequencer-database-header svelte-ese-mpsiyo");
  16775. video.autoplay = true;
  16776. attr(video, "class", "database-player svelte-ese-mpsiyo");
  16777. attr(video, "height", "335");
  16778. video.loop = true;
  16779. attr(video, "preload", "");
  16780. attr(video, "width", "335");
  16781. attr(img, "class", "database-image hidden svelte-ese-mpsiyo");
  16782. attr(audio2, "class", "database-audio hidden svelte-ese-mpsiyo");
  16783. attr(div1, "class", "sequencer-database-metadata-container svelte-ese-mpsiyo");
  16784. attr(div2, "class", "sequencer-database-player-container svelte-ese-mpsiyo");
  16785. attr(div3, "class", "sequencer-database-entries-container svelte-ese-mpsiyo");
  16786. attr(div4, "class", "sequencer-database-content svelte-ese-mpsiyo");
  16787. },
  16788. m(target, anchor) {
  16789. insert(target, div4, anchor);
  16790. append(div4, div0);
  16791. append(div0, select);
  16792. append(select, option);
  16793. for (let i = 0; i < each_blocks.length; i += 1) {
  16794. each_blocks[i].m(select, null);
  16795. }
  16796. select_option(
  16797. select,
  16798. /*$selectedPackStore*/
  16799. ctx[2]
  16800. );
  16801. append(div0, t1);
  16802. append(div0, input0);
  16803. set_input_value(
  16804. input0,
  16805. /*$search*/
  16806. ctx[1]
  16807. );
  16808. append(div0, t2);
  16809. append(div0, input1);
  16810. input1.checked = /*$allRanges*/
  16811. ctx[3];
  16812. append(div0, t3);
  16813. append(div0, label0);
  16814. append(div0, t5);
  16815. append(div0, input2);
  16816. input2.checked = /*$subLists*/
  16817. ctx[8];
  16818. append(div0, t6);
  16819. append(div0, label1);
  16820. append(div0, t8);
  16821. append(div0, input3);
  16822. input3.checked = /*$listView*/
  16823. ctx[4];
  16824. append(div0, t9);
  16825. append(div0, label2);
  16826. append(div4, t11);
  16827. append(div4, div3);
  16828. if_blocks[current_block_type_index].m(div3, null);
  16829. append(div3, t12);
  16830. append(div3, div2);
  16831. append(div2, video);
  16832. ctx[31](video);
  16833. append(div2, t13);
  16834. append(div2, img);
  16835. ctx[34](img);
  16836. append(div2, t14);
  16837. append(div2, audio2);
  16838. ctx[35](audio2);
  16839. append(div2, t15);
  16840. append(div2, div1);
  16841. if_block1.m(div1, null);
  16842. current = true;
  16843. if (!mounted) {
  16844. dispose = [
  16845. listen(
  16846. select,
  16847. "change",
  16848. /*select_change_handler*/
  16849. ctx[26]
  16850. ),
  16851. listen(
  16852. input0,
  16853. "input",
  16854. /*input0_input_handler*/
  16855. ctx[27]
  16856. ),
  16857. listen(
  16858. input1,
  16859. "change",
  16860. /*input1_change_handler*/
  16861. ctx[28]
  16862. ),
  16863. listen(
  16864. input2,
  16865. "change",
  16866. /*input2_change_handler*/
  16867. ctx[29]
  16868. ),
  16869. listen(
  16870. input3,
  16871. "change",
  16872. /*input3_change_handler*/
  16873. ctx[30]
  16874. ),
  16875. listen(
  16876. video,
  16877. "mouseenter",
  16878. /*mouseenter_handler*/
  16879. ctx[32]
  16880. ),
  16881. listen(
  16882. video,
  16883. "mouseleave",
  16884. /*mouseleave_handler*/
  16885. ctx[33]
  16886. ),
  16887. listen(
  16888. audio2,
  16889. "mouseenter",
  16890. /*mouseenter_handler_1*/
  16891. ctx[36]
  16892. ),
  16893. listen(
  16894. audio2,
  16895. "mouseleave",
  16896. /*mouseleave_handler_1*/
  16897. ctx[37]
  16898. )
  16899. ];
  16900. mounted = true;
  16901. }
  16902. },
  16903. p(ctx2, dirty) {
  16904. if (dirty[0] & /*$packStore*/
  16905. 128) {
  16906. each_value = /*$packStore*/
  16907. ctx2[7];
  16908. let i;
  16909. for (i = 0; i < each_value.length; i += 1) {
  16910. const child_ctx = get_each_context$5(ctx2, each_value, i);
  16911. if (each_blocks[i]) {
  16912. each_blocks[i].p(child_ctx, dirty);
  16913. } else {
  16914. each_blocks[i] = create_each_block$5(child_ctx);
  16915. each_blocks[i].c();
  16916. each_blocks[i].m(select, null);
  16917. }
  16918. }
  16919. for (; i < each_blocks.length; i += 1) {
  16920. each_blocks[i].d(1);
  16921. }
  16922. each_blocks.length = each_value.length;
  16923. }
  16924. if (dirty[0] & /*$selectedPackStore, $packStore*/
  16925. 132) {
  16926. select_option(
  16927. select,
  16928. /*$selectedPackStore*/
  16929. ctx2[2]
  16930. );
  16931. }
  16932. if (dirty[0] & /*$search*/
  16933. 2 && input0.value !== /*$search*/
  16934. ctx2[1]) {
  16935. set_input_value(
  16936. input0,
  16937. /*$search*/
  16938. ctx2[1]
  16939. );
  16940. }
  16941. if (dirty[0] & /*$allRanges*/
  16942. 8) {
  16943. input1.checked = /*$allRanges*/
  16944. ctx2[3];
  16945. }
  16946. if (dirty[0] & /*$subLists*/
  16947. 256) {
  16948. input2.checked = /*$subLists*/
  16949. ctx2[8];
  16950. }
  16951. if (dirty[0] & /*$listView*/
  16952. 16) {
  16953. input3.checked = /*$listView*/
  16954. ctx2[4];
  16955. }
  16956. let previous_block_index = current_block_type_index;
  16957. current_block_type_index = select_block_type(ctx2);
  16958. if (current_block_type_index === previous_block_index) {
  16959. if_blocks[current_block_type_index].p(ctx2, dirty);
  16960. } else {
  16961. group_outros();
  16962. transition_out(if_blocks[previous_block_index], 1, 1, () => {
  16963. if_blocks[previous_block_index] = null;
  16964. });
  16965. check_outros();
  16966. if_block0 = if_blocks[current_block_type_index];
  16967. if (!if_block0) {
  16968. if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
  16969. if_block0.c();
  16970. } else {
  16971. if_block0.p(ctx2, dirty);
  16972. }
  16973. transition_in(if_block0, 1);
  16974. if_block0.m(div3, t12);
  16975. }
  16976. if (current_block_type === (current_block_type = select_block_type_1(ctx2)) && if_block1) {
  16977. if_block1.p(ctx2, dirty);
  16978. } else {
  16979. if_block1.d(1);
  16980. if_block1 = current_block_type(ctx2);
  16981. if (if_block1) {
  16982. if_block1.c();
  16983. if_block1.m(div1, null);
  16984. }
  16985. }
  16986. },
  16987. i(local) {
  16988. if (current)
  16989. return;
  16990. transition_in(if_block0);
  16991. current = true;
  16992. },
  16993. o(local) {
  16994. transition_out(if_block0);
  16995. current = false;
  16996. },
  16997. d(detaching) {
  16998. if (detaching)
  16999. detach(div4);
  17000. destroy_each(each_blocks, detaching);
  17001. if_blocks[current_block_type_index].d();
  17002. ctx[31](null);
  17003. ctx[34](null);
  17004. ctx[35](null);
  17005. if_block1.d();
  17006. mounted = false;
  17007. run_all(dispose);
  17008. }
  17009. };
  17010. }
  17011. function create_fragment$f(ctx) {
  17012. let applicationshell;
  17013. let updating_elementRoot;
  17014. let current;
  17015. function applicationshell_elementRoot_binding(value) {
  17016. ctx[38](value);
  17017. }
  17018. let applicationshell_props = {
  17019. $$slots: { default: [create_default_slot$1] },
  17020. $$scope: { ctx }
  17021. };
  17022. if (
  17023. /*elementRoot*/
  17024. ctx[0] !== void 0
  17025. ) {
  17026. applicationshell_props.elementRoot = /*elementRoot*/
  17027. ctx[0];
  17028. }
  17029. applicationshell = new ApplicationShell({ props: applicationshell_props });
  17030. binding_callbacks.push(() => bind(applicationshell, "elementRoot", applicationshell_elementRoot_binding));
  17031. return {
  17032. c() {
  17033. create_component(applicationshell.$$.fragment);
  17034. },
  17035. m(target, anchor) {
  17036. mount_component(applicationshell, target, anchor);
  17037. current = true;
  17038. },
  17039. p(ctx2, dirty) {
  17040. const applicationshell_changes = {};
  17041. if (dirty[0] & /*$metadata, databaseStore, filteredEntries, $listView, $visibleTreeStore, $subLists, $allRanges, $search, $selectedPackStore, $packStore*/
  17042. 2046 | dirty[1] & /*$$scope*/
  17043. 8192) {
  17044. applicationshell_changes.$$scope = { dirty, ctx: ctx2 };
  17045. }
  17046. if (!updating_elementRoot && dirty[0] & /*elementRoot*/
  17047. 1) {
  17048. updating_elementRoot = true;
  17049. applicationshell_changes.elementRoot = /*elementRoot*/
  17050. ctx2[0];
  17051. add_flush_callback(() => updating_elementRoot = false);
  17052. }
  17053. applicationshell.$set(applicationshell_changes);
  17054. },
  17055. i(local) {
  17056. if (current)
  17057. return;
  17058. transition_in(applicationshell.$$.fragment, local);
  17059. current = true;
  17060. },
  17061. o(local) {
  17062. transition_out(applicationshell.$$.fragment, local);
  17063. current = false;
  17064. },
  17065. d(detaching) {
  17066. destroy_component(applicationshell, detaching);
  17067. }
  17068. };
  17069. }
  17070. function instance$f($$self, $$props, $$invalidate) {
  17071. let $search;
  17072. let $selectedPackStore;
  17073. let $searchRegex;
  17074. let $cleanSearchStore;
  17075. let $allRanges;
  17076. let $entriesStore;
  17077. let $listView;
  17078. let $packStore;
  17079. let $subLists;
  17080. let $visibleTreeStore;
  17081. let $metadata;
  17082. getContext("#external");
  17083. let { elementRoot } = $$props;
  17084. let entries = [];
  17085. let filteredEntries = [];
  17086. const selectedPackStore2 = databaseStore.selectedPackStore;
  17087. component_subscribe($$self, selectedPackStore2, (value) => $$invalidate(2, $selectedPackStore = value));
  17088. const packStore2 = databaseStore.packStore;
  17089. component_subscribe($$self, packStore2, (value) => $$invalidate(7, $packStore = value));
  17090. const metadata = databaseStore.metadata;
  17091. component_subscribe($$self, metadata, (value) => $$invalidate(10, $metadata = value));
  17092. const allRanges = databaseStore.allRanges;
  17093. component_subscribe($$self, allRanges, (value) => $$invalidate(3, $allRanges = value));
  17094. const subLists = databaseStore.subLists;
  17095. component_subscribe($$self, subLists, (value) => $$invalidate(8, $subLists = value));
  17096. const listView = databaseStore.listView;
  17097. component_subscribe($$self, listView, (value) => $$invalidate(4, $listView = value));
  17098. const visibleTreeStore2 = databaseStore.visibleTreeStore;
  17099. component_subscribe($$self, visibleTreeStore2, (value) => $$invalidate(9, $visibleTreeStore = value));
  17100. const search = databaseStore.search;
  17101. component_subscribe($$self, search, (value) => $$invalidate(1, $search = value));
  17102. const cleanSearchStore2 = databaseStore.cleanSearchStore;
  17103. component_subscribe($$self, cleanSearchStore2, (value) => $$invalidate(24, $cleanSearchStore = value));
  17104. const searchRegex = databaseStore.searchRegex;
  17105. component_subscribe($$self, searchRegex, (value) => $$invalidate(23, $searchRegex = value));
  17106. const entriesStore2 = SequencerDatabase.entriesStore;
  17107. component_subscribe($$self, entriesStore2, (value) => $$invalidate(25, $entriesStore = value));
  17108. listView.set(game.settings.get(CONSTANTS.MODULE_NAME, "db-list-view"));
  17109. function select_change_handler() {
  17110. $selectedPackStore = select_value(this);
  17111. selectedPackStore2.set($selectedPackStore);
  17112. }
  17113. function input0_input_handler() {
  17114. $search = this.value;
  17115. search.set($search);
  17116. }
  17117. function input1_change_handler() {
  17118. $allRanges = this.checked;
  17119. allRanges.set($allRanges);
  17120. }
  17121. function input2_change_handler() {
  17122. $subLists = this.checked;
  17123. subLists.set($subLists);
  17124. }
  17125. function input3_change_handler() {
  17126. $listView = this.checked;
  17127. listView.set($listView);
  17128. }
  17129. function video_binding($$value) {
  17130. binding_callbacks[$$value ? "unshift" : "push"](() => {
  17131. databaseStore.elements.player = $$value;
  17132. $$invalidate(5, databaseStore);
  17133. });
  17134. }
  17135. const mouseenter_handler = () => {
  17136. $$invalidate(5, databaseStore.elements.player.controls = true, databaseStore);
  17137. };
  17138. const mouseleave_handler = () => {
  17139. $$invalidate(5, databaseStore.elements.player.controls = false, databaseStore);
  17140. };
  17141. function img_binding($$value) {
  17142. binding_callbacks[$$value ? "unshift" : "push"](() => {
  17143. databaseStore.elements.image = $$value;
  17144. $$invalidate(5, databaseStore);
  17145. });
  17146. }
  17147. function audio_binding($$value) {
  17148. binding_callbacks[$$value ? "unshift" : "push"](() => {
  17149. databaseStore.elements.audio = $$value;
  17150. $$invalidate(5, databaseStore);
  17151. });
  17152. }
  17153. const mouseenter_handler_1 = () => {
  17154. $$invalidate(5, databaseStore.elements.audio.controls = true, databaseStore);
  17155. };
  17156. const mouseleave_handler_1 = () => {
  17157. $$invalidate(5, databaseStore.elements.audio.controls = false, databaseStore);
  17158. };
  17159. function applicationshell_elementRoot_binding(value) {
  17160. elementRoot = value;
  17161. $$invalidate(0, elementRoot);
  17162. }
  17163. $$self.$$set = ($$props2) => {
  17164. if ("elementRoot" in $$props2)
  17165. $$invalidate(0, elementRoot = $$props2.elementRoot);
  17166. };
  17167. $$self.$$.update = () => {
  17168. if ($$self.$$.dirty[0] & /*$listView*/
  17169. 16) {
  17170. game.settings.set(CONSTANTS.MODULE_NAME, "db-list-view", $listView);
  17171. }
  17172. if ($$self.$$.dirty[0] & /*$entriesStore, $allRanges*/
  17173. 33554440) {
  17174. {
  17175. const specificRanges = $allRanges ? Sequencer.Database.publicFlattenedEntries : Sequencer.Database.publicFlattenedSimpleEntries;
  17176. $$invalidate(22, entries = specificRanges.map((entry) => {
  17177. return { pack: entry.split(".")[0], entry };
  17178. }));
  17179. }
  17180. }
  17181. if ($$self.$$.dirty[0] & /*$cleanSearchStore, entries, $searchRegex, $selectedPackStore, $search*/
  17182. 29360134) {
  17183. {
  17184. const searchParts = $cleanSearchStore.split("|");
  17185. $$invalidate(6, filteredEntries = entries.filter((part) => {
  17186. const matchParts = make_array_unique(part.entry.match($searchRegex) || []);
  17187. return ($selectedPackStore === "all" || $selectedPackStore === part.pack) && ($search === "" || matchParts.length >= searchParts.length);
  17188. }));
  17189. }
  17190. }
  17191. };
  17192. return [
  17193. elementRoot,
  17194. $search,
  17195. $selectedPackStore,
  17196. $allRanges,
  17197. $listView,
  17198. databaseStore,
  17199. filteredEntries,
  17200. $packStore,
  17201. $subLists,
  17202. $visibleTreeStore,
  17203. $metadata,
  17204. selectedPackStore2,
  17205. packStore2,
  17206. metadata,
  17207. allRanges,
  17208. subLists,
  17209. listView,
  17210. visibleTreeStore2,
  17211. search,
  17212. cleanSearchStore2,
  17213. searchRegex,
  17214. entriesStore2,
  17215. entries,
  17216. $searchRegex,
  17217. $cleanSearchStore,
  17218. $entriesStore,
  17219. select_change_handler,
  17220. input0_input_handler,
  17221. input1_change_handler,
  17222. input2_change_handler,
  17223. input3_change_handler,
  17224. video_binding,
  17225. mouseenter_handler,
  17226. mouseleave_handler,
  17227. img_binding,
  17228. audio_binding,
  17229. mouseenter_handler_1,
  17230. mouseleave_handler_1,
  17231. applicationshell_elementRoot_binding
  17232. ];
  17233. }
  17234. class Database_shell extends SvelteComponent {
  17235. constructor(options) {
  17236. super();
  17237. init(this, options, instance$f, create_fragment$f, safe_not_equal, { elementRoot: 0 }, null, [-1, -1]);
  17238. }
  17239. get elementRoot() {
  17240. return this.$$.ctx[0];
  17241. }
  17242. set elementRoot(elementRoot) {
  17243. this.$$set({ elementRoot });
  17244. flush();
  17245. }
  17246. }
  17247. class DatabaseViewerApp extends SvelteApplication {
  17248. static get defaultOptions() {
  17249. return foundry.utils.mergeObject(super.defaultOptions, {
  17250. title: game.i18n.localize("SEQUENCER.Database.Title"),
  17251. classes: ["dialog"],
  17252. width: 900,
  17253. height: 425,
  17254. svelte: {
  17255. class: Database_shell,
  17256. target: document.body
  17257. }
  17258. });
  17259. }
  17260. static getActiveApp() {
  17261. return Object.values(ui.windows).find((app) => {
  17262. return app instanceof this && app._state > Application.RENDER_STATES.CLOSED;
  17263. });
  17264. }
  17265. static async show(options = {}) {
  17266. const existingApp = this.getActiveApp();
  17267. if (existingApp)
  17268. return existingApp.render(false, { focus: true });
  17269. return new Promise((resolve) => {
  17270. options.resolve = resolve;
  17271. new this(options).render(true, { focus: true });
  17272. });
  17273. }
  17274. }
  17275. const HowTo_svelte_svelte_type_style_lang = "";
  17276. function create_if_block$4(ctx) {
  17277. let p;
  17278. let raw0_value = localize("SEQUENCER.HowTo.PermissionsExplanation") + "";
  17279. let t0;
  17280. let button;
  17281. let i;
  17282. let t1;
  17283. let html_tag;
  17284. let raw1_value = localize("SEQUENCER.HowTo.OpenModuleSettings") + "";
  17285. let mounted;
  17286. let dispose;
  17287. return {
  17288. c() {
  17289. p = element("p");
  17290. t0 = space();
  17291. button = element("button");
  17292. i = element("i");
  17293. t1 = space();
  17294. html_tag = new HtmlTag(false);
  17295. attr(i, "class", "fas fa-plug");
  17296. html_tag.a = null;
  17297. attr(button, "type", "button");
  17298. attr(button, "class", "w-100 open-module-settings");
  17299. },
  17300. m(target, anchor) {
  17301. insert(target, p, anchor);
  17302. p.innerHTML = raw0_value;
  17303. insert(target, t0, anchor);
  17304. insert(target, button, anchor);
  17305. append(button, i);
  17306. append(button, t1);
  17307. html_tag.m(raw1_value, button);
  17308. if (!mounted) {
  17309. dispose = listen(
  17310. button,
  17311. "click",
  17312. /*openSettings*/
  17313. ctx[0]
  17314. );
  17315. mounted = true;
  17316. }
  17317. },
  17318. p: noop,
  17319. d(detaching) {
  17320. if (detaching)
  17321. detach(p);
  17322. if (detaching)
  17323. detach(t0);
  17324. if (detaching)
  17325. detach(button);
  17326. mounted = false;
  17327. dispose();
  17328. }
  17329. };
  17330. }
  17331. function create_fragment$e(ctx) {
  17332. let div;
  17333. let p0;
  17334. let raw0_value = localize("SEQUENCER.HowTo.Welcome") + "";
  17335. let t0;
  17336. let p1;
  17337. let raw1_value = localize("SEQUENCER.HowTo.Explanation") + "";
  17338. let t1;
  17339. let t2;
  17340. let p2;
  17341. let raw2_value = localize("SEQUENCER.HowTo.PlayerExplanation") + "";
  17342. let t3;
  17343. let p3;
  17344. let raw3_value = localize("SEQUENCER.HowTo.LayerExplanation") + "";
  17345. let t4;
  17346. let ol;
  17347. let li0;
  17348. let strong0;
  17349. let raw4_value = localize("SEQUENCER.HowTo.Click") + "";
  17350. let br0;
  17351. let t5;
  17352. let html_tag;
  17353. let raw5_value = localize("SEQUENCER.HowTo.ClickLabel") + "";
  17354. let t6;
  17355. let li1;
  17356. let strong1;
  17357. let raw6_value = localize("SEQUENCER.HowTo.ClickDrag") + "";
  17358. let br1;
  17359. let t7;
  17360. let html_tag_1;
  17361. let raw7_value = localize("SEQUENCER.HowTo.ClickDragLabel") + "";
  17362. let t8;
  17363. let li2;
  17364. let strong2;
  17365. let raw8_value = localize("SEQUENCER.HowTo.Shift") + "";
  17366. let br2;
  17367. let t9;
  17368. let html_tag_2;
  17369. let raw9_value = localize("SEQUENCER.HowTo.ShiftLabel") + "";
  17370. let t10;
  17371. let li3;
  17372. let strong3;
  17373. let raw10_value = localize("SEQUENCER.HowTo.ShiftControl") + "";
  17374. let br3;
  17375. let t11;
  17376. let html_tag_3;
  17377. let raw11_value = localize("SEQUENCER.HowTo.ShiftControlLabel") + "";
  17378. let t12;
  17379. let li4;
  17380. let strong4;
  17381. let raw12_value = localize("SEQUENCER.HowTo.MoreToCome") + "";
  17382. let if_block = game.user.isGM && create_if_block$4(ctx);
  17383. return {
  17384. c() {
  17385. div = element("div");
  17386. p0 = element("p");
  17387. t0 = space();
  17388. p1 = element("p");
  17389. t1 = space();
  17390. if (if_block)
  17391. if_block.c();
  17392. t2 = space();
  17393. p2 = element("p");
  17394. t3 = space();
  17395. p3 = element("p");
  17396. t4 = space();
  17397. ol = element("ol");
  17398. li0 = element("li");
  17399. strong0 = element("strong");
  17400. br0 = element("br");
  17401. t5 = space();
  17402. html_tag = new HtmlTag(false);
  17403. t6 = space();
  17404. li1 = element("li");
  17405. strong1 = element("strong");
  17406. br1 = element("br");
  17407. t7 = space();
  17408. html_tag_1 = new HtmlTag(false);
  17409. t8 = space();
  17410. li2 = element("li");
  17411. strong2 = element("strong");
  17412. br2 = element("br");
  17413. t9 = space();
  17414. html_tag_2 = new HtmlTag(false);
  17415. t10 = space();
  17416. li3 = element("li");
  17417. strong3 = element("strong");
  17418. br3 = element("br");
  17419. t11 = space();
  17420. html_tag_3 = new HtmlTag(false);
  17421. t12 = space();
  17422. li4 = element("li");
  17423. strong4 = element("strong");
  17424. html_tag.a = null;
  17425. attr(li0, "class", "svelte-ese-1gfsmnk");
  17426. html_tag_1.a = null;
  17427. attr(li1, "class", "svelte-ese-1gfsmnk");
  17428. html_tag_2.a = null;
  17429. attr(li2, "class", "svelte-ese-1gfsmnk");
  17430. html_tag_3.a = null;
  17431. attr(li3, "class", "svelte-ese-1gfsmnk");
  17432. attr(li4, "class", "svelte-ese-1gfsmnk");
  17433. attr(div, "class", "howto-container svelte-ese-1gfsmnk");
  17434. },
  17435. m(target, anchor) {
  17436. insert(target, div, anchor);
  17437. append(div, p0);
  17438. p0.innerHTML = raw0_value;
  17439. append(div, t0);
  17440. append(div, p1);
  17441. p1.innerHTML = raw1_value;
  17442. append(div, t1);
  17443. if (if_block)
  17444. if_block.m(div, null);
  17445. append(div, t2);
  17446. append(div, p2);
  17447. p2.innerHTML = raw2_value;
  17448. append(div, t3);
  17449. append(div, p3);
  17450. p3.innerHTML = raw3_value;
  17451. append(div, t4);
  17452. append(div, ol);
  17453. append(ol, li0);
  17454. append(li0, strong0);
  17455. strong0.innerHTML = raw4_value;
  17456. append(li0, br0);
  17457. append(li0, t5);
  17458. html_tag.m(raw5_value, li0);
  17459. append(ol, t6);
  17460. append(ol, li1);
  17461. append(li1, strong1);
  17462. strong1.innerHTML = raw6_value;
  17463. append(li1, br1);
  17464. append(li1, t7);
  17465. html_tag_1.m(raw7_value, li1);
  17466. append(ol, t8);
  17467. append(ol, li2);
  17468. append(li2, strong2);
  17469. strong2.innerHTML = raw8_value;
  17470. append(li2, br2);
  17471. append(li2, t9);
  17472. html_tag_2.m(raw9_value, li2);
  17473. append(ol, t10);
  17474. append(ol, li3);
  17475. append(li3, strong3);
  17476. strong3.innerHTML = raw10_value;
  17477. append(li3, br3);
  17478. append(li3, t11);
  17479. html_tag_3.m(raw11_value, li3);
  17480. append(ol, t12);
  17481. append(ol, li4);
  17482. append(li4, strong4);
  17483. strong4.innerHTML = raw12_value;
  17484. },
  17485. p(ctx2, [dirty]) {
  17486. if (game.user.isGM)
  17487. if_block.p(ctx2, dirty);
  17488. },
  17489. i: noop,
  17490. o: noop,
  17491. d(detaching) {
  17492. if (detaching)
  17493. detach(div);
  17494. if (if_block)
  17495. if_block.d();
  17496. }
  17497. };
  17498. }
  17499. function instance$e($$self) {
  17500. async function openSettings() {
  17501. const settings = new SettingsConfig();
  17502. await settings.render(true);
  17503. await wait$1(75);
  17504. const focusElement = settings.element.find('select[name="sequencer.permissions-effect-create"]');
  17505. settings.element.find("div.scrollable").scrollTop(focusElement.offset().top);
  17506. await wait$1(250);
  17507. focusElement.css("box-shadow", "rgba(200, 64, 67, 0.75) inset 0 0 10px");
  17508. await wait$1(5e3);
  17509. focusElement.css("box-shadow", "none");
  17510. }
  17511. return [openSettings];
  17512. }
  17513. class HowTo extends SvelteComponent {
  17514. constructor(options) {
  17515. super();
  17516. init(this, options, instance$e, create_fragment$e, safe_not_equal, {});
  17517. }
  17518. }
  17519. const Tabs_svelte_svelte_type_style_lang = "";
  17520. function get_each_context$4(ctx, list, i) {
  17521. const child_ctx = ctx.slice();
  17522. child_ctx[6] = list[i];
  17523. child_ctx[8] = i;
  17524. return child_ctx;
  17525. }
  17526. function create_if_block_1$1(ctx) {
  17527. let div;
  17528. return {
  17529. c() {
  17530. div = element("div");
  17531. set_style(div, "border-right", "1px solid rgba(0,0,0,0.5)");
  17532. set_style(div, "margin", "0 10px");
  17533. },
  17534. m(target, anchor) {
  17535. insert(target, div, anchor);
  17536. },
  17537. d(detaching) {
  17538. if (detaching)
  17539. detach(div);
  17540. }
  17541. };
  17542. }
  17543. function create_if_block$3(ctx) {
  17544. let i;
  17545. let i_class_value;
  17546. return {
  17547. c() {
  17548. i = element("i");
  17549. attr(i, "class", i_class_value = "icon " + /*tab*/
  17550. ctx[6].icon + " svelte-ese-123fyp");
  17551. },
  17552. m(target, anchor) {
  17553. insert(target, i, anchor);
  17554. },
  17555. p(ctx2, dirty) {
  17556. if (dirty & /*tabs*/
  17557. 2 && i_class_value !== (i_class_value = "icon " + /*tab*/
  17558. ctx2[6].icon + " svelte-ese-123fyp")) {
  17559. attr(i, "class", i_class_value);
  17560. }
  17561. },
  17562. d(detaching) {
  17563. if (detaching)
  17564. detach(i);
  17565. }
  17566. };
  17567. }
  17568. function create_each_block$4(key_1, ctx) {
  17569. let first;
  17570. let t0;
  17571. let div;
  17572. let t1;
  17573. let t2_value = localize(
  17574. /*tab*/
  17575. ctx[6].label
  17576. ) + "";
  17577. let t2;
  17578. let t3;
  17579. let mounted;
  17580. let dispose;
  17581. let if_block0 = (
  17582. /*separateElements*/
  17583. ctx[3] && /*index*/
  17584. ctx[8] > 0 && create_if_block_1$1()
  17585. );
  17586. let if_block1 = (
  17587. /*tab*/
  17588. ctx[6].icon && create_if_block$3(ctx)
  17589. );
  17590. function click_handler() {
  17591. return (
  17592. /*click_handler*/
  17593. ctx[5](
  17594. /*tab*/
  17595. ctx[6]
  17596. )
  17597. );
  17598. }
  17599. return {
  17600. key: key_1,
  17601. first: null,
  17602. c() {
  17603. first = empty();
  17604. if (if_block0)
  17605. if_block0.c();
  17606. t0 = space();
  17607. div = element("div");
  17608. if (if_block1)
  17609. if_block1.c();
  17610. t1 = space();
  17611. t2 = text$1(t2_value);
  17612. t3 = space();
  17613. attr(div, "class", "item item-piles-flexrow item-piles-clickable-link svelte-ese-123fyp");
  17614. attr(div, "data-tab", "rest");
  17615. toggle_class(
  17616. div,
  17617. "underscore",
  17618. /*underscore*/
  17619. ctx[2]
  17620. );
  17621. toggle_class(
  17622. div,
  17623. "active",
  17624. /*activeTab*/
  17625. ctx[0] === /*tab*/
  17626. ctx[6].value
  17627. );
  17628. this.first = first;
  17629. },
  17630. m(target, anchor) {
  17631. insert(target, first, anchor);
  17632. if (if_block0)
  17633. if_block0.m(target, anchor);
  17634. insert(target, t0, anchor);
  17635. insert(target, div, anchor);
  17636. if (if_block1)
  17637. if_block1.m(div, null);
  17638. append(div, t1);
  17639. append(div, t2);
  17640. append(div, t3);
  17641. if (!mounted) {
  17642. dispose = listen(div, "click", click_handler);
  17643. mounted = true;
  17644. }
  17645. },
  17646. p(new_ctx, dirty) {
  17647. ctx = new_ctx;
  17648. if (
  17649. /*separateElements*/
  17650. ctx[3] && /*index*/
  17651. ctx[8] > 0
  17652. ) {
  17653. if (if_block0)
  17654. ;
  17655. else {
  17656. if_block0 = create_if_block_1$1();
  17657. if_block0.c();
  17658. if_block0.m(t0.parentNode, t0);
  17659. }
  17660. } else if (if_block0) {
  17661. if_block0.d(1);
  17662. if_block0 = null;
  17663. }
  17664. if (
  17665. /*tab*/
  17666. ctx[6].icon
  17667. ) {
  17668. if (if_block1) {
  17669. if_block1.p(ctx, dirty);
  17670. } else {
  17671. if_block1 = create_if_block$3(ctx);
  17672. if_block1.c();
  17673. if_block1.m(div, t1);
  17674. }
  17675. } else if (if_block1) {
  17676. if_block1.d(1);
  17677. if_block1 = null;
  17678. }
  17679. if (dirty & /*tabs*/
  17680. 2 && t2_value !== (t2_value = localize(
  17681. /*tab*/
  17682. ctx[6].label
  17683. ) + ""))
  17684. set_data(t2, t2_value);
  17685. if (dirty & /*underscore*/
  17686. 4) {
  17687. toggle_class(
  17688. div,
  17689. "underscore",
  17690. /*underscore*/
  17691. ctx[2]
  17692. );
  17693. }
  17694. if (dirty & /*activeTab, tabs*/
  17695. 3) {
  17696. toggle_class(
  17697. div,
  17698. "active",
  17699. /*activeTab*/
  17700. ctx[0] === /*tab*/
  17701. ctx[6].value
  17702. );
  17703. }
  17704. },
  17705. d(detaching) {
  17706. if (detaching)
  17707. detach(first);
  17708. if (if_block0)
  17709. if_block0.d(detaching);
  17710. if (detaching)
  17711. detach(t0);
  17712. if (detaching)
  17713. detach(div);
  17714. if (if_block1)
  17715. if_block1.d();
  17716. mounted = false;
  17717. dispose();
  17718. }
  17719. };
  17720. }
  17721. function create_fragment$d(ctx) {
  17722. let nav;
  17723. let each_blocks = [];
  17724. let each_1_lookup = /* @__PURE__ */ new Map();
  17725. let nav_style_value;
  17726. let each_value = (
  17727. /*tabs*/
  17728. ctx[1].filter(func)
  17729. );
  17730. const get_key = (ctx2) => (
  17731. /*tab*/
  17732. ctx2[6].value
  17733. );
  17734. for (let i = 0; i < each_value.length; i += 1) {
  17735. let child_ctx = get_each_context$4(ctx, each_value, i);
  17736. let key = get_key(child_ctx);
  17737. each_1_lookup.set(key, each_blocks[i] = create_each_block$4(key, child_ctx));
  17738. }
  17739. return {
  17740. c() {
  17741. nav = element("nav");
  17742. for (let i = 0; i < each_blocks.length; i += 1) {
  17743. each_blocks[i].c();
  17744. }
  17745. attr(nav, "class", "tabs svelte-ese-123fyp");
  17746. attr(nav, "data-group", "primary");
  17747. attr(nav, "style", nav_style_value = /*$$props*/
  17748. ctx[4].style);
  17749. },
  17750. m(target, anchor) {
  17751. insert(target, nav, anchor);
  17752. for (let i = 0; i < each_blocks.length; i += 1) {
  17753. each_blocks[i].m(nav, null);
  17754. }
  17755. },
  17756. p(ctx2, [dirty]) {
  17757. if (dirty & /*underscore, activeTab, tabs, localize, separateElements*/
  17758. 15) {
  17759. each_value = /*tabs*/
  17760. ctx2[1].filter(func);
  17761. 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);
  17762. }
  17763. if (dirty & /*$$props*/
  17764. 16 && nav_style_value !== (nav_style_value = /*$$props*/
  17765. ctx2[4].style)) {
  17766. attr(nav, "style", nav_style_value);
  17767. }
  17768. },
  17769. i: noop,
  17770. o: noop,
  17771. d(detaching) {
  17772. if (detaching)
  17773. detach(nav);
  17774. for (let i = 0; i < each_blocks.length; i += 1) {
  17775. each_blocks[i].d();
  17776. }
  17777. }
  17778. };
  17779. }
  17780. const func = (tab) => !tab.hidden;
  17781. function instance$d($$self, $$props, $$invalidate) {
  17782. let { activeTab } = $$props;
  17783. let { tabs } = $$props;
  17784. let { underscore = false } = $$props;
  17785. let { separateElements = false } = $$props;
  17786. const click_handler = (tab) => {
  17787. $$invalidate(0, activeTab = tab.value);
  17788. };
  17789. $$self.$$set = ($$new_props) => {
  17790. $$invalidate(4, $$props = assign(assign({}, $$props), exclude_internal_props($$new_props)));
  17791. if ("activeTab" in $$new_props)
  17792. $$invalidate(0, activeTab = $$new_props.activeTab);
  17793. if ("tabs" in $$new_props)
  17794. $$invalidate(1, tabs = $$new_props.tabs);
  17795. if ("underscore" in $$new_props)
  17796. $$invalidate(2, underscore = $$new_props.underscore);
  17797. if ("separateElements" in $$new_props)
  17798. $$invalidate(3, separateElements = $$new_props.separateElements);
  17799. };
  17800. $$props = exclude_internal_props($$props);
  17801. return [activeTab, tabs, underscore, separateElements, $$props, click_handler];
  17802. }
  17803. class Tabs extends SvelteComponent {
  17804. constructor(options) {
  17805. super();
  17806. init(this, options, instance$d, create_fragment$d, safe_not_equal, {
  17807. activeTab: 0,
  17808. tabs: 1,
  17809. underscore: 2,
  17810. separateElements: 3
  17811. });
  17812. }
  17813. }
  17814. const SequenceManager = {
  17815. VisibleEffects: writable$1({}),
  17816. RunningSounds: writable$1({}),
  17817. RunningSequences: writable$1({})
  17818. };
  17819. SequenceManager.VisibleEffects.get = (id) => {
  17820. return get_store_value(SequenceManager.VisibleEffects)[id];
  17821. };
  17822. SequenceManager.VisibleEffects.add = (id, data) => {
  17823. SequenceManager.VisibleEffects.update((effects) => {
  17824. effects[id] = data;
  17825. return effects;
  17826. });
  17827. };
  17828. SequenceManager.VisibleEffects.delete = (id) => {
  17829. SequenceManager.VisibleEffects.update((effects) => {
  17830. delete effects[id];
  17831. return effects;
  17832. });
  17833. };
  17834. SequenceManager.VisibleEffects.values = () => {
  17835. return Object.values(get_store_value(SequenceManager.VisibleEffects));
  17836. };
  17837. SequenceManager.RunningSounds.get = (id) => {
  17838. return get_store_value(SequenceManager.RunningSounds)[id];
  17839. };
  17840. SequenceManager.RunningSounds.add = (id, data) => {
  17841. SequenceManager.RunningSounds.update((effects) => {
  17842. effects[id] = data;
  17843. return effects;
  17844. });
  17845. };
  17846. SequenceManager.RunningSounds.delete = (id) => {
  17847. SequenceManager.RunningSounds.update((effects) => {
  17848. delete effects[id];
  17849. return effects;
  17850. });
  17851. };
  17852. SequenceManager.RunningSounds.values = () => {
  17853. return Object.values(get_store_value(SequenceManager.RunningSounds));
  17854. };
  17855. SequenceManager.RunningSounds.keys = () => {
  17856. return Object.keys(get_store_value(SequenceManager.RunningSounds));
  17857. };
  17858. SequenceManager.RunningSequences.get = (id) => {
  17859. return get_store_value(SequenceManager.RunningSequences)[id];
  17860. };
  17861. SequenceManager.RunningSequences.add = (id, sequence) => {
  17862. SequenceManager.RunningSequences.update((sequences) => {
  17863. sequences[id] = sequence;
  17864. return sequences;
  17865. });
  17866. };
  17867. SequenceManager.RunningSequences.delete = (id) => {
  17868. SequenceManager.RunningSequences.update((sequences) => {
  17869. delete sequences[id];
  17870. return sequences;
  17871. });
  17872. };
  17873. SequenceManager.RunningSequences.clearFinishedSequences = () => {
  17874. SequenceManager.RunningSequences.update((sequences) => {
  17875. for (const sequence of Object.values(sequences)) {
  17876. if (get_store_value(sequence.status) === CONSTANTS.STATUS.COMPLETE || get_store_value(sequence.status) === CONSTANTS.STATUS.ABORTED) {
  17877. delete sequences[sequence.id];
  17878. }
  17879. }
  17880. return sequences;
  17881. });
  17882. };
  17883. SequenceManager.RunningSequences.stopAll = () => {
  17884. SequenceManager.RunningSequences.update((sequences) => {
  17885. for (const sequence of Object.values(sequences)) {
  17886. sequence._abort();
  17887. }
  17888. return sequences;
  17889. });
  17890. };
  17891. SequenceManager.RunningSequences.values = () => {
  17892. return Object.values(get_store_value(SequenceManager.RunningSequences));
  17893. };
  17894. class ColorMatrixFilter extends globalThis.PIXI.filters.ColorMatrixFilter {
  17895. /**
  17896. * Properties & default values:
  17897. * - hue [false]
  17898. * - brightness [1]
  17899. * - contrast [1]
  17900. * - saturate [1]
  17901. */
  17902. constructor(inData) {
  17903. super();
  17904. this.isValid = true;
  17905. this.values = {};
  17906. for (let [key, value] of Object.entries(inData)) {
  17907. this.setValue(key, value);
  17908. if (!this.isValid)
  17909. break;
  17910. }
  17911. }
  17912. setValue(key, value) {
  17913. try {
  17914. this.values[key] = value;
  17915. this[key](value, true);
  17916. } catch (err) {
  17917. ui.notifications.warn(
  17918. `Sequencer | ${this.constructor.name} | Could not set property ${key}`
  17919. );
  17920. }
  17921. }
  17922. }
  17923. class BlurFilter extends globalThis.PIXI.filters.BlurFilter {
  17924. /**
  17925. * Properties & default values:
  17926. * - strength [8]
  17927. * - blur [2]
  17928. * - blurX [2]
  17929. * - blurY [2]
  17930. * - quality [4]
  17931. * - resolution [PIXI.settings.FILTER_RESOLUTION]
  17932. * - kernelSize [5]
  17933. */
  17934. constructor(inData = {}) {
  17935. inData = foundry.utils.mergeObject(
  17936. {
  17937. strength: 1,
  17938. quality: 4,
  17939. resolution: PIXI.settings.FILTER_RESOLUTION,
  17940. kernelSize: 5
  17941. },
  17942. inData
  17943. );
  17944. super(...Object.values(inData));
  17945. this.isValid = true;
  17946. for (let [key, value] of Object.entries(inData)) {
  17947. try {
  17948. this[key] = value;
  17949. } catch (err) {
  17950. ui.notifications.warn(
  17951. `Sequencer | ${this.constructor.name} | Could not set property ${key}`
  17952. );
  17953. this.isValid = false;
  17954. }
  17955. }
  17956. }
  17957. }
  17958. class NoiseFilter extends globalThis.PIXI.filters.NoiseFilter {
  17959. /**
  17960. * Properties & default values:
  17961. * - noise [0.5]
  17962. * - seed [Math.random()]
  17963. */
  17964. constructor(inData = {}) {
  17965. super();
  17966. inData = foundry.utils.mergeObject(
  17967. {
  17968. noise: 0.5,
  17969. seed: Math.random()
  17970. },
  17971. inData
  17972. );
  17973. this.isValid = true;
  17974. for (let [key, value] of Object.entries(inData)) {
  17975. try {
  17976. this[key] = value;
  17977. } catch (err) {
  17978. ui.notifications.warn(
  17979. `Sequencer | ${this.constructor.name} | Could not set property ${key}`
  17980. );
  17981. this.isValid = false;
  17982. }
  17983. }
  17984. }
  17985. }
  17986. class GlowFilter extends globalThis.PIXI.filters.GlowFilter {
  17987. /**
  17988. * Properties & default values:
  17989. * - distance [10]
  17990. * - outerStrength [4]
  17991. * - innerStrength [0]
  17992. * - color [0xffffff]
  17993. * - quality [0.1]
  17994. * - knockout [false]
  17995. */
  17996. constructor(inData = {}) {
  17997. inData = foundry.utils.mergeObject(
  17998. {
  17999. distance: 10,
  18000. outerStrength: 4,
  18001. innerStrength: 0,
  18002. color: 16777215,
  18003. quality: 0.1,
  18004. knockout: false
  18005. },
  18006. inData
  18007. );
  18008. super(inData);
  18009. this.isValid = true;
  18010. }
  18011. }
  18012. let shader = `
  18013. uniform sampler2D uSampler;
  18014. varying vec2 vTextureCoord;
  18015. float alpha;
  18016. void main() {
  18017. vec4 pixel = texture2D(uSampler, vTextureCoord);
  18018. alpha = smoothstep(0.6,1.0,pixel.a);
  18019. gl_FragColor = vec4(alpha, alpha, alpha, pixel.a);
  18020. }
  18021. `;
  18022. class ClipFilter extends PIXI.Filter {
  18023. constructor() {
  18024. super(null, shader);
  18025. }
  18026. }
  18027. const filters = {
  18028. ColorMatrix: ColorMatrixFilter,
  18029. Blur: BlurFilter,
  18030. Noise: NoiseFilter,
  18031. Glow: GlowFilter,
  18032. Clip: ClipFilter
  18033. };
  18034. const SequencerAnimationEngine = {
  18035. _animations: [],
  18036. _debug: void 0,
  18037. _deltas: [],
  18038. ticker: false,
  18039. dt: 0,
  18040. isRunning: false,
  18041. addAnimation(origin, attributes = [], timeDifference = 0) {
  18042. if (!Array.isArray(attributes))
  18043. attributes = [attributes];
  18044. return new Promise((resolve) => {
  18045. this._animations.push({
  18046. origin,
  18047. attributes: attributes.map((attribute) => {
  18048. attribute.targetId = get_object_identifier(attribute.target) + "-" + attribute.propertyName;
  18049. attribute.started = false;
  18050. attribute.initialized = false;
  18051. attribute.finishing = false;
  18052. attribute.complete = false;
  18053. attribute.progress = 0;
  18054. attribute.value = 0;
  18055. attribute.duration = attribute.duration ?? 0;
  18056. attribute.durationDone = timeDifference ?? 0;
  18057. if (attribute?.looping) {
  18058. attribute.loopDuration = attribute.loopDuration ?? attribute.duration ?? 0;
  18059. attribute.loopDurationDone = timeDifference % attribute.loopDuration;
  18060. attribute.loops = attribute.loops ?? 0;
  18061. attribute.loopsDone = Math.floor(
  18062. attribute.durationDone / attribute.duration
  18063. );
  18064. attribute.index = attribute.loopsDone % attribute.values.length;
  18065. attribute.nextIndex = (attribute.loopsDone + 1) % attribute.values.length;
  18066. if (!attribute.pingPong && attribute.nextIndex === 0) {
  18067. attribute.index = 0;
  18068. attribute.nextIndex = 1;
  18069. }
  18070. }
  18071. return attribute;
  18072. }),
  18073. complete: false,
  18074. totalDt: timeDifference,
  18075. resolve
  18076. });
  18077. if (!this.ticker || !this.ticker.started) {
  18078. this.start();
  18079. }
  18080. debug(`Added animations to Animation Engine`);
  18081. });
  18082. },
  18083. endAnimations(target) {
  18084. this._animations = this._animations.filter(
  18085. (animation2) => animation2.origin !== target
  18086. );
  18087. },
  18088. start() {
  18089. debug(`Animation Engine Started`);
  18090. if (!this.ticker) {
  18091. this.ticker = new PIXI.Ticker();
  18092. this.ticker.add(this.nextFrame.bind(this));
  18093. }
  18094. this.ticker.start();
  18095. },
  18096. nextFrame() {
  18097. if (this._animations.length === 0) {
  18098. debug(`Animation Engine Paused`);
  18099. this.ticker.stop();
  18100. this._startingValues = {};
  18101. return;
  18102. }
  18103. this._animations.forEach((animation2) => this._animate(animation2));
  18104. this._animations = this._animations.filter(
  18105. (animation2) => !animation2.complete
  18106. );
  18107. this._applyDeltas();
  18108. for (const targetId of Object.keys(this._startingValues)) {
  18109. if (!this._animations.some(
  18110. (_a) => _a.attributes.some((_b) => _b.targetId === targetId)
  18111. )) {
  18112. delete this._startingValues[targetId];
  18113. }
  18114. }
  18115. },
  18116. _startingValues: {},
  18117. _applyDeltas() {
  18118. const deltas = [];
  18119. for (const animation2 of this._animations) {
  18120. for (const attribute of animation2.attributes) {
  18121. if (!attribute.started || attribute.complete)
  18122. continue;
  18123. if (attribute.finishing) {
  18124. attribute.complete = true;
  18125. }
  18126. let delta = deltas.find(
  18127. (delta2) => attribute.targetId === delta2.targetId && attribute.setPropertyName === delta2.setPropertyName
  18128. );
  18129. if (!delta) {
  18130. delta = {
  18131. targetId: attribute.targetId,
  18132. target: attribute.target,
  18133. getPropertyName: attribute.getPropertyName ?? attribute.propertyName,
  18134. setPropertyName: attribute.propertyName,
  18135. value: 0
  18136. };
  18137. if (attribute.target instanceof PIXI.filters.ColorMatrixFilter) {
  18138. delta.value = attribute.previousValue;
  18139. }
  18140. deltas.push(delta);
  18141. }
  18142. delta.value += attribute.delta;
  18143. }
  18144. }
  18145. for (let delta of deltas) {
  18146. const finalValue = deep_get(delta.target, delta.getPropertyName) + delta.value;
  18147. try {
  18148. deep_set(delta.target, delta.setPropertyName, finalValue);
  18149. } catch (err) {
  18150. }
  18151. }
  18152. },
  18153. _animate(animation2) {
  18154. animation2.totalDt += this.ticker.elapsedMS;
  18155. animation2.attributes.filter((attribute) => !attribute.complete).forEach(
  18156. (attribute) => this._animateAttribute(animation2.totalDt, attribute)
  18157. );
  18158. animation2.complete = animation2.attributes.filter((attribute) => !attribute.complete).length === 0;
  18159. if (animation2.complete) {
  18160. animation2.resolve();
  18161. }
  18162. },
  18163. _animateAttribute(totalDt, attribute) {
  18164. if (totalDt < attribute.delay)
  18165. return;
  18166. if (!attribute.started) {
  18167. const funkyProperty = attribute.propertyName.includes("scale") || attribute.propertyName.includes("alpha");
  18168. if (this._startingValues[attribute.targetId] === void 0 || attribute.absolute) {
  18169. const getProperty2 = funkyProperty || attribute.from === void 0;
  18170. this._startingValues[attribute.targetId] = getProperty2 ? deep_get(
  18171. attribute.target,
  18172. attribute.getPropertyName ?? attribute.propertyName
  18173. ) : attribute.from;
  18174. if (!attribute.propertyName.includes("scale")) {
  18175. deep_set(
  18176. attribute.target,
  18177. attribute.propertyName,
  18178. this._startingValues[attribute.targetId]
  18179. );
  18180. }
  18181. }
  18182. attribute.previousValue = this._startingValues[attribute.targetId];
  18183. if (attribute?.looping) {
  18184. attribute.values = attribute.values.map((value) => {
  18185. return value + attribute.previousValue - (funkyProperty ? 1 : 0);
  18186. });
  18187. } else if (attribute.from === void 0) {
  18188. attribute.from = attribute.previousValue;
  18189. }
  18190. }
  18191. attribute.started = true;
  18192. if (attribute?.looping && attribute?.indefinite) {
  18193. this._handleIndefiniteLoop(attribute);
  18194. } else if (attribute?.looping) {
  18195. this._handleLoops(attribute);
  18196. } else {
  18197. this._handleDefault(attribute);
  18198. }
  18199. attribute.delta = attribute.value - attribute.previousValue;
  18200. attribute.previousValue = attribute.value;
  18201. },
  18202. _handleBaseLoop(attribute) {
  18203. if (!attribute.initialized) {
  18204. if (attribute.values.length === 1) {
  18205. attribute.values.unshift(
  18206. deep_get(attribute.target, attribute.propertyName)
  18207. );
  18208. }
  18209. attribute.initialized = true;
  18210. }
  18211. attribute.loopDurationDone += this.ticker.deltaMS;
  18212. attribute.progress = attribute.loopDurationDone / attribute.loopDuration;
  18213. attribute.value = interpolate(
  18214. attribute.values[attribute.index],
  18215. attribute.values[attribute.nextIndex],
  18216. attribute.progress,
  18217. attribute.ease
  18218. );
  18219. if (attribute.progress >= 1) {
  18220. attribute.loopDurationDone -= attribute.loopDuration;
  18221. attribute.index = (attribute.index + 1) % attribute.values.length;
  18222. attribute.nextIndex = (attribute.nextIndex + 1) % attribute.values.length;
  18223. if (!attribute.pingPong && attribute.nextIndex === 0) {
  18224. attribute.index = 0;
  18225. attribute.nextIndex = 1;
  18226. }
  18227. attribute.loopsDone++;
  18228. attribute.value = interpolate(
  18229. attribute.values[attribute.index],
  18230. attribute.values[attribute.nextIndex],
  18231. attribute.progress % 1,
  18232. attribute.ease
  18233. );
  18234. }
  18235. },
  18236. _handleIndefiniteLoop(attribute) {
  18237. return this._handleBaseLoop(attribute);
  18238. },
  18239. _handleLoops(attribute) {
  18240. this._handleBaseLoop(attribute);
  18241. attribute.durationDone += this.ticker.deltaMS;
  18242. attribute.overallProgress = attribute.durationDone / attribute.duration;
  18243. if (attribute.progress >= 1 && attribute.loopsDone === attribute.loops * 2) {
  18244. attribute.finishing = true;
  18245. attribute.value = attribute.values[attribute.index];
  18246. }
  18247. if (attribute.overallProgress >= 1) {
  18248. attribute.finishing = true;
  18249. }
  18250. },
  18251. _handleDefault(attribute) {
  18252. attribute.durationDone += this.ticker.deltaMS;
  18253. attribute.progress = attribute.durationDone / attribute.duration;
  18254. attribute.value = interpolate(
  18255. attribute.from,
  18256. attribute.to,
  18257. attribute.progress,
  18258. attribute.ease
  18259. );
  18260. if (attribute.progress >= 1) {
  18261. attribute.value = attribute.to;
  18262. attribute.finishing = true;
  18263. }
  18264. }
  18265. };
  18266. let SequencerAudioHelper$1 = class SequencerAudioHelper2 {
  18267. /**
  18268. * Play an audio file.
  18269. *
  18270. * @param {{src: string, loop?: boolean, volume?: number, _fadeIn?: {duration: number}, _fadeOut?: {duration: number}, duration?: number}} data The data that describes the audio to play.
  18271. * @param {boolean} [push=false] A flag indicating whether or not to make other clients play the audio, too.
  18272. * @returns {Number} A promise that resolves when the audio file has finished playing.
  18273. */
  18274. static async play(data, push = true) {
  18275. if (push)
  18276. sequencerSocket.executeForOthers(SOCKET_HANDLERS.PLAY_SOUND, data);
  18277. return this._play(data);
  18278. }
  18279. /**
  18280. * @param {{src: string, loop?: boolean, volume: number, _fadeIn?: {duration: number}, _fadeOut?: {duration: number}, duration?: number}} data
  18281. * @returns {Number}
  18282. * @private
  18283. */
  18284. static async _play(data) {
  18285. if (!game.settings.get("sequencer", "soundsEnabled") || game.user.viewedScene !== data.sceneId || data?.users?.length && !data?.users?.includes(game.userId)) {
  18286. return new Promise((resolve) => setTimeout(resolve, data.duration));
  18287. }
  18288. Hooks.callAll("createSequencerSound", data);
  18289. debug(`Playing sound:`, data);
  18290. data.volume = (data.volume ?? 0.8) * game.settings.get("core", "globalInterfaceVolume");
  18291. const sound = await game.audio.play(data.src, {
  18292. volume: data.fadeIn ? 0 : data.volume,
  18293. loop: data.loop,
  18294. offset: data.startTime
  18295. });
  18296. SequenceManager.RunningSounds.add(data.id, sound);
  18297. if (data.fadeIn) {
  18298. SequencerAnimationEngine.addAnimation(data.id, {
  18299. target: sound,
  18300. propertyName: "volume",
  18301. from: 0,
  18302. to: data.volume,
  18303. duration: Math.min(data.fadeIn.duration, data.duration),
  18304. ease: data.fadeIn.ease,
  18305. delay: Math.min(data.fadeIn.delay, data.duration)
  18306. });
  18307. }
  18308. if (data.fadeOut) {
  18309. SequencerAnimationEngine.addAnimation(data.id, {
  18310. target: sound,
  18311. propertyName: "volume",
  18312. from: data.volume,
  18313. to: 0,
  18314. duration: Math.min(data.fadeOut.duration, data.duration),
  18315. ease: data.fadeOut.ease,
  18316. delay: Math.max(
  18317. data.duration - data.fadeOut.duration + data.fadeOut.delay,
  18318. 0
  18319. )
  18320. });
  18321. }
  18322. if (data.duration) {
  18323. setTimeout(() => {
  18324. sound.stop();
  18325. }, data.duration);
  18326. }
  18327. new Promise((resolve) => {
  18328. sound.on("stop", resolve);
  18329. sound.on("end", resolve);
  18330. }).then(() => {
  18331. SequenceManager.RunningSounds.delete(data.id);
  18332. Hooks.callAll("endedSequencerSound", data);
  18333. });
  18334. return data.duration;
  18335. }
  18336. static stop(ids, push = true) {
  18337. if (push && game.user.isGM)
  18338. sequencerSocket.executeForOthers(SOCKET_HANDLERS.STOP_SOUNDS, ids);
  18339. return this._stop(ids);
  18340. }
  18341. static _stop(ids) {
  18342. for (const id of ids) {
  18343. const sound = SequenceManager.RunningSounds.get(id);
  18344. if (sound) {
  18345. sound.stop();
  18346. }
  18347. }
  18348. }
  18349. };
  18350. let lockedView = false;
  18351. class SequencerFoundryReplicator {
  18352. static registerHooks() {
  18353. Hooks.on("canvasPan", () => {
  18354. if (!lockedView)
  18355. return;
  18356. canvas.stage.pivot.set(lockedView.x, lockedView.y);
  18357. canvas.stage.scale.set(lockedView.scale, lockedView.scale);
  18358. canvas.updateBlur(lockedView.scale);
  18359. canvas.controls._onCanvasPan();
  18360. canvas.hud.align();
  18361. });
  18362. }
  18363. static _validateObject(inObject, sceneId) {
  18364. if (is_UUID(inObject) || !is_object_canvas_data(inObject)) {
  18365. inObject = get_object_from_scene(inObject, sceneId);
  18366. }
  18367. return inObject?._object ?? inObject;
  18368. }
  18369. static _getPositionFromData(data) {
  18370. const source = this._validateObject(data.source, data.sceneId);
  18371. const position = source instanceof PlaceableObject ? get_object_position(source) : source?.worldPosition || source?.center || source;
  18372. const multiplier = data.randomOffset;
  18373. const twister = new MersenneTwister(data.seed);
  18374. if (source && multiplier) {
  18375. let randomOffset = get_random_offset(
  18376. source,
  18377. multiplier,
  18378. twister
  18379. );
  18380. position.x -= randomOffset.x;
  18381. position.y -= randomOffset.y;
  18382. }
  18383. let extraOffset = data.offset;
  18384. if (extraOffset) {
  18385. let newOffset = {
  18386. x: extraOffset.x,
  18387. y: extraOffset.y
  18388. };
  18389. if (extraOffset.gridUnits) {
  18390. newOffset.x *= canvas.grid.size;
  18391. newOffset.y *= canvas.grid.size;
  18392. }
  18393. if (extraOffset.local) {
  18394. newOffset = rotateAroundPoint(
  18395. 0,
  18396. 0,
  18397. newOffset.x,
  18398. newOffset.y,
  18399. source?.rotation ?? 0
  18400. );
  18401. }
  18402. position.x -= newOffset.x;
  18403. position.y -= newOffset.y;
  18404. }
  18405. return position;
  18406. }
  18407. static playScrollingText(data, push = true) {
  18408. if (push) {
  18409. sequencerSocket.executeForOthers(
  18410. SOCKET_HANDLERS.CREATE_SCROLLING_TEXT,
  18411. data
  18412. );
  18413. }
  18414. return this._playScrollingText(data);
  18415. }
  18416. static _playScrollingText(data) {
  18417. if (game.user.viewedScene !== data.sceneId)
  18418. return;
  18419. if (data.users.length && !data.users.includes(game.userId))
  18420. return;
  18421. canvas.interface.createScrollingText(
  18422. this._getPositionFromData(data),
  18423. data.content,
  18424. data.options
  18425. );
  18426. return data.options?.duration ?? 2e3;
  18427. }
  18428. static panCanvas(data, push = true) {
  18429. if (push) {
  18430. sequencerSocket.executeForOthers(
  18431. SOCKET_HANDLERS.CREATE_SCROLLING_TEXT,
  18432. data
  18433. );
  18434. }
  18435. return this._panCanvas(data);
  18436. }
  18437. static _panCanvas(data) {
  18438. if (game.user.viewedScene !== data.sceneId)
  18439. return;
  18440. if (data.users.length && !data.users.includes(game.userId))
  18441. return;
  18442. const position = this._getPositionFromData(data);
  18443. canvas.animatePan({
  18444. x: position.x,
  18445. y: position.y,
  18446. scale: data.scale,
  18447. duration: data.duration,
  18448. speed: data.speed
  18449. });
  18450. if (data.speed) {
  18451. let ray = new Ray(canvas.stage.pivot, {
  18452. x: position.x,
  18453. y: position.y
  18454. });
  18455. data.duration = Math.round(ray.distance * 1e3 / data.speed);
  18456. }
  18457. if (data.lockView > 0) {
  18458. setTimeout(() => {
  18459. lockedView = {
  18460. x: position.x,
  18461. y: position.y,
  18462. scale: data.scale
  18463. };
  18464. }, data.duration);
  18465. setTimeout(() => {
  18466. lockedView = false;
  18467. }, data.lockView + data.duration);
  18468. }
  18469. return data.duration + (data.lockView ?? 0);
  18470. }
  18471. }
  18472. const SOCKET_HANDLERS = {
  18473. PLAY_EFFECT: "playEffect",
  18474. END_EFFECTS: "endEffects",
  18475. UPDATE_EFFECT: "updateEffects",
  18476. ADD_EFFECT_ANIMATIONS: "addEffectAnimations",
  18477. PLAY_SOUND: "playSound",
  18478. STOP_SOUNDS: "stopSounds",
  18479. PRELOAD: "preload",
  18480. PRELOAD_RESPONSE: "preloadResponse",
  18481. PRELOAD_DONE: "preloadDone",
  18482. UPDATE_DOCUMENT: "updateDocument",
  18483. ADD_FLAGS: "addFlags",
  18484. REMOVE_FLAGS: "removeFlags",
  18485. UPDATE_POSITION: "updatePosition",
  18486. CREATE_SCROLLING_TEXT: "createScrollingText",
  18487. PAN_CANVAS: "panCanvas",
  18488. RUN_SEQUENCE_LOCALLY: "runSequenceLocally"
  18489. };
  18490. let sequencerSocket;
  18491. function registerSocket() {
  18492. console.log("Sequencer | Registered sequencerSocket");
  18493. sequencerSocket = socketlib.registerModule(CONSTANTS.MODULE_NAME);
  18494. sequencerSocket.register(
  18495. SOCKET_HANDLERS.PLAY_EFFECT,
  18496. (...args) => Sequencer.EffectManager._playEffect(...args)
  18497. );
  18498. sequencerSocket.register(
  18499. SOCKET_HANDLERS.END_EFFECTS,
  18500. (...args) => Sequencer.EffectManager._endManyEffects(...args)
  18501. );
  18502. sequencerSocket.register(
  18503. SOCKET_HANDLERS.UPDATE_EFFECT,
  18504. (...args) => Sequencer.EffectManager._updateEffect(...args)
  18505. );
  18506. sequencerSocket.register(
  18507. SOCKET_HANDLERS.ADD_EFFECT_ANIMATIONS,
  18508. (...args) => Sequencer.EffectManager._addEffectAnimations(...args)
  18509. );
  18510. sequencerSocket.register(
  18511. SOCKET_HANDLERS.PLAY_SOUND,
  18512. (...args) => SequencerAudioHelper$1._play(...args)
  18513. );
  18514. sequencerSocket.register(
  18515. SOCKET_HANDLERS.STOP_SOUNDS,
  18516. (...args) => SequencerAudioHelper$1._stop(...args)
  18517. );
  18518. sequencerSocket.register(
  18519. SOCKET_HANDLERS.PRELOAD,
  18520. (...args) => Sequencer.Preloader.respond(...args)
  18521. );
  18522. sequencerSocket.register(
  18523. SOCKET_HANDLERS.PRELOAD_RESPONSE,
  18524. (...args) => Sequencer.Preloader.handleResponse(...args)
  18525. );
  18526. sequencerSocket.register(
  18527. SOCKET_HANDLERS.UPDATE_DOCUMENT,
  18528. (...args) => updateDocument(...args)
  18529. );
  18530. sequencerSocket.register(
  18531. SOCKET_HANDLERS.ADD_FLAGS,
  18532. (...args) => flagManager._addFlags(...args)
  18533. );
  18534. sequencerSocket.register(
  18535. SOCKET_HANDLERS.REMOVE_FLAGS,
  18536. (...args) => flagManager._removeFlags(...args)
  18537. );
  18538. sequencerSocket.register(
  18539. SOCKET_HANDLERS.UPDATE_POSITION,
  18540. (...args) => Sequencer.EffectManager._updatePosition(...args)
  18541. );
  18542. sequencerSocket.register(
  18543. SOCKET_HANDLERS.CREATE_SCROLLING_TEXT,
  18544. (data) => SequencerFoundryReplicator._playScrollingText(data)
  18545. );
  18546. sequencerSocket.register(
  18547. SOCKET_HANDLERS.PAN_CANVAS,
  18548. (data) => SequencerFoundryReplicator._panCanvas(data)
  18549. );
  18550. sequencerSocket.register(SOCKET_HANDLERS.RUN_SEQUENCE_LOCALLY, (data) => {
  18551. debug("Playing remote Sequence");
  18552. new Sequence().fromJSON(data).play();
  18553. });
  18554. }
  18555. async function updateDocument(documentUuid, updates, animate) {
  18556. const document2 = await fromUuid(documentUuid);
  18557. return document2.update(updates, animate);
  18558. }
  18559. const flagManager = {
  18560. flagAddBuffer: /* @__PURE__ */ new Map(),
  18561. flagRemoveBuffer: /* @__PURE__ */ new Map(),
  18562. _latestFlagVersion: false,
  18563. get latestFlagVersion() {
  18564. if (!this._latestFlagVersion) {
  18565. const versions = Object.keys(this.migrations);
  18566. versions.sort((a, b) => {
  18567. return isNewerVersion(a, b) ? -1 : 1;
  18568. });
  18569. this._latestFlagVersion = versions[0];
  18570. }
  18571. return this._latestFlagVersion;
  18572. },
  18573. /**
  18574. * Sanitizes the effect data, accounting for changes to the structure in previous versions
  18575. *
  18576. * @param inDocument
  18577. * @returns {array}
  18578. */
  18579. getFlags(inDocument) {
  18580. let effects = getProperty(inDocument, CONSTANTS.EFFECTS_FLAG);
  18581. if (!effects?.length)
  18582. return [];
  18583. effects = foundry.utils.deepClone(effects);
  18584. const changes = [];
  18585. for (let [effectId, effectData] of effects) {
  18586. let effectVersion = effectData?.flagVersion ?? "1.0.0";
  18587. if (effectData.flagVersion === this.latestFlagVersion)
  18588. continue;
  18589. for (let [version, migration] of Object.entries(this.migrations)) {
  18590. if (!isNewerVersion(version, effectVersion))
  18591. continue;
  18592. effectData = migration(inDocument, effectData);
  18593. }
  18594. debug(
  18595. `Migrated effect with ID ${effectId} from version ${effectVersion} to version ${this.latestFlagVersion}`
  18596. );
  18597. effectData.flagVersion = this.latestFlagVersion;
  18598. changes.push(effectData);
  18599. }
  18600. if (changes.length) {
  18601. flagManager.addFlags(inDocument.uuid, changes);
  18602. }
  18603. return effects;
  18604. },
  18605. migrations: {
  18606. "2.0.0": (inDocument, effectData) => {
  18607. effectData._id = effectData.id;
  18608. effectData.creationTimestamp = effectData.timestamp;
  18609. if (effectData.template) {
  18610. effectData.template = {
  18611. gridSize: effectData.template[0],
  18612. startPoint: effectData.template[1],
  18613. endPoint: effectData.template[2]
  18614. };
  18615. }
  18616. if (effectData.attachTo) {
  18617. effectData.attachTo = {
  18618. active: true,
  18619. align: "center",
  18620. rotation: true,
  18621. bindVisibility: true,
  18622. bindAlpha: true
  18623. };
  18624. effectData.source = inDocument.uuid;
  18625. const objectSize = get_object_dimensions(inDocument, true);
  18626. effectData.offset = {
  18627. x: effectData.position.x - objectSize.width,
  18628. y: effectData.position.y - objectSize.height
  18629. };
  18630. } else if (effectData.position) {
  18631. effectData.source = effectData.position;
  18632. }
  18633. if (effectData.reachTowards) {
  18634. effectData.stretchTo = {
  18635. attachTo: false,
  18636. onlyX: false
  18637. };
  18638. }
  18639. if (effectData.filters) {
  18640. effectData.filters = Object.entries(effectData.filters).map((entry) => {
  18641. return {
  18642. className: entry[0],
  18643. ...entry[1]
  18644. };
  18645. });
  18646. }
  18647. effectData.moveSpeed = effectData.speed;
  18648. effectData.target = null;
  18649. effectData.forcedIndex = null;
  18650. effectData.flipX = false;
  18651. effectData.flipY = false;
  18652. effectData.nameOffsetMap = {};
  18653. effectData.sequenceId = 0;
  18654. delete effectData.id;
  18655. delete effectData.timestamp;
  18656. delete effectData.position;
  18657. delete effectData.reachTowards;
  18658. delete effectData.speed;
  18659. delete effectData.audioVolume;
  18660. delete effectData.gridSizeDifference;
  18661. delete effectData.template;
  18662. if (effectData.animatedProperties) {
  18663. delete effectData.animatedProperties.fadeInAudio;
  18664. delete effectData.animatedProperties.fadeOutAudio;
  18665. }
  18666. effectData = foundry.utils.mergeObject(
  18667. effectData,
  18668. effectData.animatedProperties
  18669. );
  18670. delete effectData.animatedProperties;
  18671. return effectData;
  18672. },
  18673. "2.0.6": (inDocument, effectData) => {
  18674. effectData.private = null;
  18675. return effectData;
  18676. },
  18677. "2.0.8": (inDocument, effectData) => {
  18678. if (effectData.stretchTo) {
  18679. effectData.stretchTo.tiling = false;
  18680. }
  18681. return effectData;
  18682. },
  18683. "2.0.9": (inDocument, effectData) => {
  18684. effectData.tilingTexture = false;
  18685. if (effectData.stretchTo?.tiling !== void 0) {
  18686. if (effectData.stretchTo.tiling) {
  18687. effectData.tilingTexture = {
  18688. scale: { x: 1, y: 1 },
  18689. position: { x: 0, y: 0 }
  18690. };
  18691. }
  18692. delete effectData.stretchTo.tiling;
  18693. }
  18694. return effectData;
  18695. },
  18696. "2.1.0": (inDocument, effectData) => {
  18697. if (effectData.randomOffset) {
  18698. effectData.randomOffset = {
  18699. source: !effectData.target ? effectData.randomOffset : false,
  18700. target: !!effectData.target ? effectData.randomOffset : false
  18701. };
  18702. }
  18703. if (effectData.nameOffsetMap) {
  18704. Object.values(effectData.nameOffsetMap).forEach((offsetMap) => {
  18705. if (offsetMap.randomOffset) {
  18706. offsetMap.randomOffset = {
  18707. source: !offsetMap.target ? offsetMap.randomOffset : false,
  18708. target: !!offsetMap.target ? offsetMap.randomOffset : false
  18709. };
  18710. }
  18711. });
  18712. }
  18713. return effectData;
  18714. }
  18715. },
  18716. /**
  18717. * Adds effects to a given document
  18718. *
  18719. * @param inObjectUUID
  18720. * @param inEffects
  18721. */
  18722. addFlags: (inObjectUUID, inEffects) => {
  18723. if (!Array.isArray(inEffects))
  18724. inEffects = [inEffects];
  18725. sequencerSocket.executeAsGM(
  18726. SOCKET_HANDLERS.ADD_FLAGS,
  18727. inObjectUUID,
  18728. inEffects
  18729. );
  18730. },
  18731. /**
  18732. * Removes effects from a given document
  18733. *
  18734. * @param inObjectUUID
  18735. * @param inEffects
  18736. * @param removeAll
  18737. */
  18738. removeFlags: (inObjectUUID, inEffects, removeAll = false) => {
  18739. sequencerSocket.executeAsGM(
  18740. SOCKET_HANDLERS.REMOVE_FLAGS,
  18741. inObjectUUID,
  18742. inEffects,
  18743. removeAll
  18744. );
  18745. },
  18746. _addFlags: (inObjectUUID, inEffects) => {
  18747. if (!Array.isArray(inEffects))
  18748. inEffects = [inEffects];
  18749. let flagsToSet = flagManager.flagAddBuffer.get(inObjectUUID) ?? {
  18750. effects: []
  18751. };
  18752. flagsToSet.effects.push(...inEffects);
  18753. flagManager.flagAddBuffer.set(inObjectUUID, flagsToSet);
  18754. flagManager.updateFlags();
  18755. },
  18756. _removeFlags: (inObjectUUID, inEffects, removeAll = false) => {
  18757. if (inEffects && !Array.isArray(inEffects))
  18758. inEffects = [inEffects];
  18759. let flagsToSet = flagManager.flagRemoveBuffer.get(inObjectUUID) ?? {
  18760. effects: [],
  18761. removeAll
  18762. };
  18763. if (inEffects)
  18764. flagsToSet.effects.push(...inEffects);
  18765. flagManager.flagRemoveBuffer.set(inObjectUUID, flagsToSet);
  18766. flagManager.updateFlags();
  18767. },
  18768. updateFlags: debounce(async () => {
  18769. let flagsToAdd = Array.from(flagManager.flagAddBuffer);
  18770. let flagsToRemove = Array.from(flagManager.flagRemoveBuffer);
  18771. flagManager.flagAddBuffer.clear();
  18772. flagManager.flagRemoveBuffer.clear();
  18773. flagsToAdd.forEach((entry) => entry[1].original = true);
  18774. flagsToRemove.forEach((entry) => entry[1].original = true);
  18775. const objects = /* @__PURE__ */ new Set([
  18776. ...flagsToAdd.map((effect) => effect[0]).filter(Boolean),
  18777. ...flagsToRemove.map((effect) => effect[0]).filter(Boolean)
  18778. ]);
  18779. flagsToAdd = new Map(flagsToAdd);
  18780. flagsToRemove = new Map(flagsToRemove);
  18781. const actorUpdates = {};
  18782. const sceneObjectsToUpdate = {};
  18783. for (let objectUUID of objects) {
  18784. let object = from_uuid_fast(objectUUID);
  18785. if (!object) {
  18786. custom_warning(
  18787. "Sequencer",
  18788. `Failed to set flags on non-existent object with UUID: ${objectUUID}`
  18789. );
  18790. continue;
  18791. }
  18792. let toAdd = flagsToAdd.get(objectUUID) ?? { effects: [] };
  18793. let toRemove = flagsToRemove.get(objectUUID) ?? {
  18794. effects: [],
  18795. removeAll: false
  18796. };
  18797. const existingFlags = new Map(
  18798. getProperty(object, CONSTANTS.EFFECTS_FLAG) ?? []
  18799. );
  18800. if (toRemove?.removeAll) {
  18801. toRemove.effects = Array.from(existingFlags).map((entry) => entry[0]);
  18802. }
  18803. for (let effect of toAdd.effects) {
  18804. if (typeof effect === "string") {
  18805. effect = existingFlags.get(effect);
  18806. if (!effect)
  18807. continue;
  18808. }
  18809. existingFlags.set(effect._id, effect);
  18810. }
  18811. for (let effect of toRemove.effects) {
  18812. if (typeof effect === "string") {
  18813. effect = existingFlags.get(effect);
  18814. if (!effect)
  18815. continue;
  18816. }
  18817. existingFlags.delete(effect._id);
  18818. }
  18819. let flagsToSet = Array.from(existingFlags);
  18820. const options = {};
  18821. const isLinkedToken = object instanceof TokenDocument && object.actorLink;
  18822. const isLinkedActor = object instanceof Actor && object.prototypeToken.actorLink;
  18823. if ((isLinkedToken || isLinkedActor) && (toAdd.original || toRemove.original)) {
  18824. const actor = isLinkedActor ? object : object.actor;
  18825. actorUpdates[actor.id] = flagsToSet.filter(
  18826. (effect) => effect[1]?.persistOptions?.persistTokenPrototype
  18827. );
  18828. flagsToSet = flagsToSet.filter(
  18829. (effect) => !effect[1]?.persistOptions?.persistTokenPrototype
  18830. );
  18831. if (isLinkedToken && game.modules.get("multilevel-tokens")?.active && getProperty(object, "flags.multilevel-tokens.stoken")) {
  18832. options["mlt_bypass"] = true;
  18833. }
  18834. }
  18835. if (object?.documentName === "Scene") {
  18836. const sceneId = object.id;
  18837. sceneObjectsToUpdate[sceneId] = sceneObjectsToUpdate[sceneId] ?? {
  18838. updates: {},
  18839. documents: {}
  18840. };
  18841. sceneObjectsToUpdate[sceneId].updates[CONSTANTS.EFFECTS_FLAG] = flagsToSet;
  18842. } else if (!(object instanceof Actor)) {
  18843. const sceneId = object.parent.id;
  18844. const docName = object.documentName;
  18845. sceneObjectsToUpdate[sceneId] = sceneObjectsToUpdate[sceneId] ?? {
  18846. updates: {},
  18847. documents: {}
  18848. };
  18849. sceneObjectsToUpdate[sceneId].documents[docName] = sceneObjectsToUpdate[sceneId].documents[docName] ?? {
  18850. options: {},
  18851. updates: []
  18852. };
  18853. sceneObjectsToUpdate[sceneId].documents[docName].options = options;
  18854. sceneObjectsToUpdate[sceneId].documents[docName].updates.push({
  18855. _id: object.id,
  18856. [CONSTANTS.EFFECTS_FLAG]: flagsToSet
  18857. });
  18858. }
  18859. }
  18860. for (const [sceneId, sceneData] of Object.entries(sceneObjectsToUpdate)) {
  18861. const scene = game.scenes.get(sceneId);
  18862. if (!foundry.utils.isEmpty(sceneData.updates)) {
  18863. await scene.update(sceneData.updates);
  18864. }
  18865. for (const [documentType, documentData] of Object.entries(
  18866. sceneData.documents
  18867. )) {
  18868. await scene.updateEmbeddedDocuments(
  18869. documentType,
  18870. documentData.updates,
  18871. documentData.options
  18872. );
  18873. debug(
  18874. `Flags set for documents of type "${documentType}" in scene with ID "${sceneId}"`
  18875. );
  18876. }
  18877. }
  18878. await Actor.updateDocuments(
  18879. Object.entries(actorUpdates).map(([actorId, effects]) => ({
  18880. _id: actorId,
  18881. [CONSTANTS.EFFECTS_FLAG]: effects
  18882. }))
  18883. );
  18884. }, 250)
  18885. };
  18886. const PositionContainer = /* @__PURE__ */ new Map();
  18887. const TemporaryPositionsContainer = /* @__PURE__ */ new Map();
  18888. class SequencerEffectManager {
  18889. /**
  18890. * Returns all of the currently running effects on the canvas
  18891. *
  18892. * @returns {Array}
  18893. */
  18894. static get effects() {
  18895. return Array.from(SequenceManager.VisibleEffects.values());
  18896. }
  18897. static _updatePosition(uuid, position) {
  18898. TemporaryPositionsContainer.set(uuid, position);
  18899. }
  18900. static getPositionForUUID(uuid) {
  18901. return TemporaryPositionsContainer.get(uuid);
  18902. }
  18903. /**
  18904. * Opens the Sequencer Effects UI with the effects tab open
  18905. */
  18906. static show() {
  18907. return EffectsUIApp.show({ tab: "manager" });
  18908. }
  18909. /**
  18910. * Play an effect on the canvas.
  18911. *
  18912. * @param {object} data The data that describes the audio to play
  18913. * @param {boolean} [push=true] A flag indicating whether or not to make other clients play the effect
  18914. * @returns {CanvasEffect} A CanvasEffect object
  18915. */
  18916. static async play(data, push = true) {
  18917. if (!user_can_do("permissions-effect-create")) {
  18918. custom_warning(
  18919. "Sequencer",
  18920. "EffectManager | play | Players do not have permissions to play effects. This can be configured in Sequencer's module settings."
  18921. );
  18922. return;
  18923. }
  18924. if (push)
  18925. sequencerSocket.executeForOthers(SOCKET_HANDLERS.PLAY_EFFECT, data);
  18926. if (data?.persistOptions?.persistTokenPrototype) {
  18927. this._playPrototypeTokenEffects(data, push);
  18928. }
  18929. return this._playEffect(data);
  18930. }
  18931. /**
  18932. * Get effects that are playing on the canvas based on a set of filters
  18933. *
  18934. * @param {object} inFilter An object containing filters that determine which effects to return
  18935. * - object: An ID or a PlaceableObject
  18936. * - name: The name of the effect
  18937. * - sceneId: the ID of the scene to search within
  18938. * @returns {Array} An array containing effects that match the given filter
  18939. */
  18940. static getEffects(inFilter = {}) {
  18941. const filters2 = this._validateFilters(inFilter);
  18942. if (!inFilter)
  18943. throw custom_error(
  18944. "Sequencer",
  18945. "EffectManager | getEffects | Incorrect or incomplete parameters provided"
  18946. );
  18947. return this._filterEffects(filters2);
  18948. }
  18949. /**
  18950. * Updates effects based on a set of filters
  18951. *
  18952. * @param {object} inFilter An object containing filters that determine which effects to return
  18953. * - object: An ID or a PlaceableObject
  18954. * - name: The name of the effect
  18955. * - sceneId: the ID of the scene to search within
  18956. * - effects: a single CanvasEffect or its ID, or an array of such
  18957. * @param {object} inUpdates
  18958. * @returns {promise}
  18959. */
  18960. static updateEffects(inFilter, inUpdates) {
  18961. inFilter = this._validateFilters(inFilter);
  18962. if (!inFilter)
  18963. throw custom_error(
  18964. "Sequencer",
  18965. "EffectManager | updateEffects | Incorrect or incomplete parameters provided"
  18966. );
  18967. CanvasEffect.validateUpdate(inUpdates);
  18968. const effectsToUpdate = this._filterEffects(inFilter).filter(
  18969. (effect) => effect.userCanUpdate
  18970. );
  18971. return Promise.allSettled(
  18972. effectsToUpdate.map((effect) => effect.update(inUpdates))
  18973. );
  18974. }
  18975. /**
  18976. * End effects that are playing on the canvas based on a set of filters
  18977. *
  18978. * @param {object} inFilter An object containing filters that determine which effects to end
  18979. * - object: An ID or a PlaceableObject
  18980. * - name: The name of the effect
  18981. * - sceneId: the ID of the scene to search within
  18982. * - effects: a single CanvasEffect or its ID, or an array of such
  18983. * @param {boolean} [push=true] A flag indicating whether or not to make other clients end the effects
  18984. * @returns {promise} A promise that resolves when the effects have ended
  18985. */
  18986. static async endEffects(inFilter = {}, push = true) {
  18987. inFilter = this._validateFilters(inFilter);
  18988. if (!inFilter)
  18989. throw custom_error(
  18990. "Sequencer",
  18991. "EffectManager | endEffects | Incorrect or incomplete parameters provided"
  18992. );
  18993. const effectsToEnd = this._getEffectsByFilter(inFilter);
  18994. if (!effectsToEnd.length)
  18995. return;
  18996. if (push)
  18997. sequencerSocket.executeForOthers(
  18998. SOCKET_HANDLERS.END_EFFECTS,
  18999. effectsToEnd
  19000. );
  19001. return this._endManyEffects(effectsToEnd);
  19002. }
  19003. /**
  19004. * End all effects that are playing on the canvas
  19005. *
  19006. * @param {string} [inSceneId] A parameter which determines which scene to end all effects on, defaults to current viewed scene
  19007. * @param {boolean} [push=true] A flag indicating whether or not to make other clients end all effects
  19008. * @returns {promise} A promise that resolves when all of the effects have _ended
  19009. */
  19010. static async endAllEffects(inSceneId = game.user.viewedScene, push = true) {
  19011. const inFilter = this._validateFilters({ sceneId: inSceneId });
  19012. if (!inFilter)
  19013. throw custom_error(
  19014. "Sequencer",
  19015. "EffectManager | endAllEffects | Incorrect or incomplete parameters provided"
  19016. );
  19017. const effectsToEnd = this._getEffectsByFilter(inFilter);
  19018. if (!effectsToEnd.length)
  19019. return;
  19020. if (push)
  19021. sequencerSocket.executeForOthers(
  19022. SOCKET_HANDLERS.END_EFFECTS,
  19023. effectsToEnd
  19024. );
  19025. return this._endManyEffects(effectsToEnd);
  19026. }
  19027. static _getEffectsByFilter(inFilter) {
  19028. return make_array_unique(
  19029. this._filterEffects(inFilter).filter((effect) => effect.userCanDelete).map((effect) => {
  19030. return effect.data?.persistOptions?.persistTokenPrototype ? effect.data?.persistOptions?.id ?? effect.id : effect.id;
  19031. })
  19032. );
  19033. }
  19034. /**
  19035. * If an effect has been named its position will be cached, which can be retrieved with this method
  19036. *
  19037. * @param {string} inName
  19038. * @returns {object|boolean}
  19039. * @private
  19040. */
  19041. static getEffectPositionByName(inName) {
  19042. if (!(typeof inName === "string"))
  19043. throw custom_error(
  19044. "Sequencer",
  19045. "EffectManager | getEffectPositionByName | inName must be of type string"
  19046. );
  19047. return PositionContainer.get(inName) ?? false;
  19048. }
  19049. /**
  19050. * Filters the existing effects based on the given filter
  19051. *
  19052. * @param inFilter
  19053. * @returns {array}
  19054. * @private
  19055. */
  19056. static _filterEffects(inFilter) {
  19057. if (inFilter.name) {
  19058. inFilter.name = new RegExp(
  19059. str_to_search_regex_str(safe_str(inFilter.name)),
  19060. "gu"
  19061. );
  19062. }
  19063. let effects = this.effects;
  19064. if (inFilter.sceneId && inFilter.sceneId !== canvas.scene.id) {
  19065. effects = get_all_documents_from_scene(inFilter.sceneId).map((doc) => {
  19066. return getProperty(doc, CONSTANTS.EFFECTS_FLAG);
  19067. }).filter((flags) => !!flags).map((flags) => {
  19068. return flags.map((flag) => CanvasEffect.make(flag[1]));
  19069. }).deepFlatten();
  19070. }
  19071. return effects.filter((effect) => {
  19072. 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);
  19073. });
  19074. }
  19075. /**
  19076. * Validates an object actually exists, and gets its UUID
  19077. *
  19078. * @param object
  19079. * @param sceneId
  19080. * @returns {string}
  19081. * @private
  19082. */
  19083. static _validateObject(object, sceneId) {
  19084. if (!(object instanceof foundry.abstract.Document || object instanceof PlaceableObject || typeof object === "string")) {
  19085. throw custom_error(
  19086. "Sequencer",
  19087. "EffectManager | object must be instance of PlaceableObject or of type string"
  19088. );
  19089. } else if (object instanceof PlaceableObject || object instanceof foundry.abstract.Document) {
  19090. object = get_object_identifier(object?.document ?? object);
  19091. } else if (typeof object === "string") {
  19092. const actualObject = get_object_from_scene(object, sceneId);
  19093. if (!actualObject) {
  19094. throw custom_error(
  19095. "Sequencer",
  19096. `EffectManager | could not find object with ID: ${object}`
  19097. );
  19098. }
  19099. const uuid = get_object_identifier(actualObject);
  19100. if (!uuid) {
  19101. throw custom_error(
  19102. "Sequencer",
  19103. `EffectManager | could could not establish identifier of object with ID: ${object}`
  19104. );
  19105. }
  19106. object = uuid;
  19107. }
  19108. return object;
  19109. }
  19110. /**
  19111. * Validates the filter given to any of the above public methods
  19112. *
  19113. * @param inFilter
  19114. * @returns {boolean}
  19115. * @private
  19116. */
  19117. static _validateFilters(inFilter) {
  19118. if (inFilter?.sceneId) {
  19119. if (typeof inFilter.sceneId !== "string")
  19120. throw custom_error(
  19121. "Sequencer",
  19122. "EffectManager | inFilter.sceneId must be of type string"
  19123. );
  19124. if (!game.scenes.get(inFilter.sceneId))
  19125. throw custom_error(
  19126. "Sequencer",
  19127. "EffectManager | inFilter.sceneId must be a valid scene id (could not find scene)"
  19128. );
  19129. } else {
  19130. inFilter.sceneId = game.user.viewedScene;
  19131. }
  19132. if (inFilter?.object) {
  19133. inFilter.source = this._validateObject(inFilter.object, inFilter.sceneId);
  19134. delete inFilter.object;
  19135. }
  19136. if (inFilter?.source) {
  19137. inFilter.source = this._validateObject(inFilter.source, inFilter.sceneId);
  19138. }
  19139. if (inFilter?.target) {
  19140. inFilter.target = this._validateObject(inFilter.target, inFilter.sceneId);
  19141. }
  19142. if (inFilter?.name && typeof inFilter?.name !== "string")
  19143. throw custom_error(
  19144. "Sequencer",
  19145. "EffectManager | inFilter.name must be of type string"
  19146. );
  19147. if (inFilter?.origin && typeof inFilter?.origin !== "string")
  19148. throw custom_error(
  19149. "Sequencer",
  19150. "EffectManager | inFilter.origin must be of type string"
  19151. );
  19152. if (inFilter?.effects) {
  19153. if (!Array.isArray(inFilter.effects))
  19154. inFilter.effects = [inFilter.effects];
  19155. inFilter.effects = inFilter.effects.map((effect) => {
  19156. if (!(typeof effect === "string" || effect instanceof CanvasEffect))
  19157. throw custom_error(
  19158. "Sequencer",
  19159. "EffectManager | collections in inFilter.effects must be of type string or CanvasEffect"
  19160. );
  19161. if (effect instanceof CanvasEffect)
  19162. return effect.id;
  19163. return effect;
  19164. });
  19165. }
  19166. if (!inFilter.name && !inFilter.origin && !inFilter.target && !inFilter.sceneId && !inFilter.effects && !inFilter.origin)
  19167. return false;
  19168. return foundry.utils.mergeObject(
  19169. {
  19170. effects: false,
  19171. name: false,
  19172. source: false,
  19173. target: false,
  19174. sceneId: false,
  19175. origin: false
  19176. },
  19177. inFilter
  19178. );
  19179. }
  19180. /**
  19181. * Actually plays the effect on the canvas
  19182. *
  19183. * @param data
  19184. * @param setFlags
  19185. * @returns {Promise<{duration: Promise<number>, promise: Promise<void>}>}
  19186. * @private
  19187. */
  19188. static async _playEffect(data, setFlags = true) {
  19189. const effect = CanvasEffect.make(data);
  19190. if (data.persist && setFlags && effect.context && effect.owner && !data.temporary && !data.remote) {
  19191. flagManager.addFlags(effect.context.uuid, effect.data);
  19192. }
  19193. if (!effect.shouldPlay)
  19194. return;
  19195. const playData = effect.play();
  19196. SequenceManager.VisibleEffects.add(effect.id, effect);
  19197. if (effect.data.name) {
  19198. effect._ticker.add(() => {
  19199. if (effect.isDestroyed)
  19200. return;
  19201. PositionContainer.set(effect.data.name, {
  19202. start: effect.sourcePosition,
  19203. end: effect.targetPosition
  19204. });
  19205. });
  19206. }
  19207. if (data.temporary && effect.owner) {
  19208. let lastSourcePosition = {};
  19209. let lastTargetPosition = {};
  19210. effect._ticker.add(() => {
  19211. if (effect.source && !effect.isSourceDestroyed) {
  19212. const sourceData = effect.getSourceData();
  19213. if (JSON.stringify(sourceData) !== lastSourcePosition) {
  19214. sequencerSocket.executeForOthers(
  19215. SOCKET_HANDLERS.UPDATE_POSITION,
  19216. data.source,
  19217. sourceData
  19218. );
  19219. lastSourcePosition = JSON.stringify(sourceData);
  19220. }
  19221. }
  19222. if (effect.target && !effect.isTargetDestroyed) {
  19223. const targetData = effect.getTargetData();
  19224. if (JSON.stringify(targetData) !== lastTargetPosition) {
  19225. sequencerSocket.executeForOthers(
  19226. SOCKET_HANDLERS.UPDATE_POSITION,
  19227. data.target,
  19228. targetData
  19229. );
  19230. lastTargetPosition = JSON.stringify(targetData);
  19231. }
  19232. }
  19233. });
  19234. }
  19235. if (!data.persist) {
  19236. playData.promise.then(() => this._removeEffect(effect));
  19237. }
  19238. return playData;
  19239. }
  19240. /**
  19241. * Updates a single effect with the given data
  19242. *
  19243. * @param inEffectId
  19244. * @param inUpdates
  19245. * @returns {promise|boolean}
  19246. * @private
  19247. */
  19248. static _updateEffect(inEffectId, inUpdates) {
  19249. const effect = SequenceManager.VisibleEffects.get(inEffectId);
  19250. if (!effect)
  19251. return false;
  19252. return effect._update(inUpdates);
  19253. }
  19254. /**
  19255. * Updates a single effect with new animations
  19256. *
  19257. * @param inEffectId
  19258. * @param inAnimations
  19259. * @param inLoopingAnimations
  19260. * @returns {promise|boolean}
  19261. * @private
  19262. */
  19263. static _addEffectAnimations(inEffectId, inAnimations, inLoopingAnimations) {
  19264. const effect = SequenceManager.VisibleEffects.get(inEffectId);
  19265. if (!effect)
  19266. return false;
  19267. return effect._addAnimations(inAnimations, inLoopingAnimations);
  19268. }
  19269. /**
  19270. * Sets up persisting effects when the scene is first loaded
  19271. *
  19272. * @returns {promise}
  19273. */
  19274. static async setUpPersists() {
  19275. await this.tearDownPersists();
  19276. const allObjects = get_all_documents_from_scene();
  19277. allObjects.push(canvas.scene);
  19278. const docEffectsMap = allObjects.reduce((acc, doc) => {
  19279. let tokenEffects = flagManager.getFlags(doc);
  19280. if (doc instanceof TokenDocument && doc?.actorLink) {
  19281. const actorEffects = flagManager.getFlags(doc?.actor);
  19282. actorEffects.forEach((e) => {
  19283. e[1]._id = randomID();
  19284. e[1].source = doc.uuid;
  19285. e[1].sceneId = doc.parent.id;
  19286. });
  19287. tokenEffects = tokenEffects.concat(actorEffects);
  19288. }
  19289. if (tokenEffects.length) {
  19290. acc[doc.uuid] = tokenEffects;
  19291. }
  19292. return acc;
  19293. }, {});
  19294. const promises = Object.entries(docEffectsMap).map(([uuid, effects]) => {
  19295. return this._playEffectMap(effects, fromUuidSync(uuid));
  19296. }).flat();
  19297. return Promise.all(promises).then(() => {
  19298. Hooks.callAll("sequencerEffectManagerReady");
  19299. });
  19300. }
  19301. /**
  19302. * Tears down persisting effects when the scene is unloaded
  19303. */
  19304. static tearDownPersists() {
  19305. return Promise.allSettled(
  19306. this.effects.map((effect) => {
  19307. SequenceManager.VisibleEffects.delete(effect.id);
  19308. return effect.destroy();
  19309. })
  19310. );
  19311. }
  19312. /**
  19313. * Patches an object's creation data before it's created so that the effect plays on it correctly
  19314. *
  19315. * @param inDocument
  19316. * @param data
  19317. * @param options
  19318. * @returns {*}
  19319. */
  19320. static async patchCreationData(inDocument, data, options) {
  19321. const effects = flagManager.getFlags(inDocument);
  19322. if (!effects?.length)
  19323. return;
  19324. const updates = {};
  19325. let documentUuid;
  19326. if (!inDocument._id) {
  19327. const documentId = randomID();
  19328. documentUuid = inDocument.uuid + documentId;
  19329. updates["_id"] = documentId;
  19330. options.keepId = true;
  19331. } else {
  19332. documentUuid = inDocument.uuid;
  19333. }
  19334. updates[CONSTANTS.EFFECTS_FLAG] = this.patchEffectDataForDocument(
  19335. documentUuid,
  19336. effects
  19337. );
  19338. return inDocument.updateSource(updates);
  19339. }
  19340. static patchEffectDataForDocument(inDocumentUuid, effects) {
  19341. return effects.map((effect) => {
  19342. effect[0] = randomID();
  19343. const effectData = effect[1];
  19344. effectData._id = effect[0];
  19345. if (is_UUID(effectData.source)) {
  19346. if (effectData.masks.includes(effectData.source)) {
  19347. const index = effectData.masks.indexOf(effectData.source);
  19348. effectData.masks[index] = inDocumentUuid;
  19349. }
  19350. effectData.source = inDocumentUuid;
  19351. }
  19352. effectData.sceneId = inDocumentUuid.split(".")[1];
  19353. return effect;
  19354. });
  19355. }
  19356. /**
  19357. * Plays the effects of a given document on creation
  19358. *
  19359. * @param inDocument
  19360. * @returns {*}
  19361. */
  19362. static async documentCreated(inDocument) {
  19363. let effects = flagManager.getFlags(inDocument);
  19364. if (inDocument instanceof TokenDocument && inDocument?.actorLink) {
  19365. let actorEffects = flagManager.getFlags(inDocument.actor);
  19366. if (actorEffects.length) {
  19367. actorEffects = this.patchEffectDataForDocument(
  19368. inDocument.uuid,
  19369. actorEffects
  19370. );
  19371. }
  19372. effects = effects.concat(actorEffects);
  19373. }
  19374. if (!effects?.length)
  19375. return;
  19376. return this._playEffectMap(effects, inDocument);
  19377. }
  19378. /**
  19379. * Plays multiple effects at the same time
  19380. *
  19381. * @param inEffects
  19382. * @param inDocument
  19383. * @returns {Promise<{duration: Promise<number>, promise: Promise<void>}[]>}
  19384. * @private
  19385. */
  19386. static _playEffectMap(inEffects, inDocument) {
  19387. if (inEffects instanceof Map)
  19388. inEffects = Array.from(inEffects);
  19389. return Promise.all(
  19390. inEffects.map(async (effect) => {
  19391. if (!CanvasEffect.checkValid(effect[1])) {
  19392. if (game.user.isGM) {
  19393. custom_warning(
  19394. `Removed effect from ${inDocument.uuid} as it no longer had a valid source or target`
  19395. );
  19396. }
  19397. return flagManager.removeFlags(inDocument.uuid, effect);
  19398. }
  19399. return this._playEffect(effect[1], false).then((result) => {
  19400. if (!result) {
  19401. debug("Error playing effect");
  19402. }
  19403. }).catch((err) => {
  19404. debug("Error playing effect:", err);
  19405. });
  19406. })
  19407. );
  19408. }
  19409. /**
  19410. * Ends one or many effects at the same time, returning a promise that resolves once every effect has fully ended
  19411. *
  19412. * @param inEffectIds
  19413. * @returns {Promise}
  19414. * @private
  19415. */
  19416. static async _endManyEffects(inEffectIds = false) {
  19417. const actorEffectsToEnd = this.effects.filter((effect) => {
  19418. return effect.context?.actorLink && inEffectIds.includes(effect.data?.persistOptions?.id);
  19419. });
  19420. const effectsByActorUuid = Object.values(
  19421. group_by(actorEffectsToEnd, "context.actor.uuid")
  19422. );
  19423. const regularEffectsToEnd = this.effects.filter((effect) => {
  19424. return inEffectIds.includes(effect.id) || !effect.context?.actorLink && inEffectIds.includes(effect.data?.persistOptions?.id);
  19425. });
  19426. const effectsByContextUuid = Object.values(
  19427. group_by(regularEffectsToEnd, "context.uuid")
  19428. );
  19429. effectsByContextUuid.forEach((effects) => {
  19430. effects = effects.filter(
  19431. (effect) => effect.data.persist && !effect.data.temporary
  19432. );
  19433. if (!effects.length)
  19434. return;
  19435. const effectData = effects.map((effect) => effect.data);
  19436. flagManager.removeFlags(
  19437. effects[0].context.uuid,
  19438. effectData,
  19439. !inEffectIds
  19440. );
  19441. });
  19442. effectsByActorUuid.forEach((effects) => {
  19443. effects = effects.filter(
  19444. (effect) => effect.data.persist && !effect.data.temporary
  19445. );
  19446. if (!effects.length)
  19447. return;
  19448. const effectContext = effects[0].context;
  19449. const effectData = effects.map((effect) => effect.data);
  19450. if (!(effectContext instanceof TokenDocument && effectContext.actorLink && effectContext.actor.prototypeToken.actorLink)) {
  19451. return;
  19452. }
  19453. const persistentEffectData = effectData.filter(
  19454. (data) => data?.persistOptions?.persistTokenPrototype
  19455. );
  19456. if (!persistentEffectData.length)
  19457. return;
  19458. const actorEffects = flagManager.getFlags(effectContext.actor);
  19459. const applicableActorEffects = actorEffects.filter((effect) => {
  19460. return effect[1]?.persistOptions?.persistTokenPrototype && persistentEffectData.some(
  19461. (persistentEffect) => persistentEffect.persistOptions.id === effect[1]?.persistOptions?.id
  19462. );
  19463. }).map((e) => e[0]);
  19464. flagManager.removeFlags(
  19465. effectContext.actor.uuid,
  19466. applicableActorEffects,
  19467. !inEffectIds
  19468. );
  19469. });
  19470. const effectsToEnd = effectsByContextUuid.concat(effectsByActorUuid).deepFlatten();
  19471. return Promise.allSettled(
  19472. effectsToEnd.map((effect) => this._removeEffect(effect))
  19473. );
  19474. }
  19475. static _effectContextFilter(inUUID, effectData) {
  19476. return effectData?.source === inUUID || effectData?.target === inUUID || (effectData?.tiedDocuments ?? []).indexOf(inUUID) > -1;
  19477. }
  19478. /**
  19479. * Handles the deletion of objects that effects are attached to
  19480. *
  19481. * @param inUUID
  19482. * @returns {Promise}
  19483. */
  19484. static objectDeleted(inUUID) {
  19485. const documentsToCheck = game.scenes.filter((scene) => scene.id !== game.user.viewedScene).map((scene) => [scene, ...get_all_documents_from_scene(scene.id)]).deepFlatten();
  19486. const documentEffectsToEnd = documentsToCheck.map((obj) => {
  19487. const objEffects = flagManager.getFlags(obj);
  19488. const effectsToEnd = objEffects.filter(
  19489. ([effectId, effectData]) => this._effectContextFilter(inUUID, effectData)
  19490. );
  19491. return {
  19492. document: obj,
  19493. effects: effectsToEnd.map((effect) => effect[0])
  19494. };
  19495. }).filter((obj) => obj.effects.length);
  19496. const visibleEffectsToEnd = this.effects.filter((effect) => this._effectContextFilter(inUUID, effect.data)).map((e) => e.id);
  19497. return Promise.allSettled([
  19498. this._endManyEffects(visibleEffectsToEnd),
  19499. ...documentEffectsToEnd.map((obj) => {
  19500. return flagManager.removeFlags(obj.document.uuid, obj.effects);
  19501. })
  19502. ]);
  19503. }
  19504. /**
  19505. * Removes the effect from the manager and ends it, returning a promise that resolves once the effect has fully _ended
  19506. *
  19507. * @param effect
  19508. * @returns {Promise}
  19509. * @private
  19510. */
  19511. static _removeEffect(effect) {
  19512. SequenceManager.VisibleEffects.delete(effect.id);
  19513. TemporaryPositionsContainer.delete(effect.data.source);
  19514. TemporaryPositionsContainer.delete(effect.data.target);
  19515. return effect.endEffect();
  19516. }
  19517. static async _playPrototypeTokenEffects(data, push) {
  19518. if (!is_UUID(data.source))
  19519. return;
  19520. const object = from_uuid_fast(data.source);
  19521. if (!(object instanceof TokenDocument))
  19522. return;
  19523. const tokenEffectsToPlay = game.scenes.map(
  19524. (scene) => scene.tokens.filter((token) => {
  19525. return token.actorLink && token.actor === object.actor && token !== object;
  19526. })
  19527. ).deepFlatten();
  19528. for (const tokenDocument of tokenEffectsToPlay) {
  19529. const duplicatedData = foundry.utils.deepClone(data);
  19530. duplicatedData._id = randomID();
  19531. duplicatedData.sceneId = tokenDocument.uuid.split(".")[1];
  19532. duplicatedData.masks = duplicatedData.masks.map((uuid) => uuid.replace(duplicatedData.source, tokenDocument.uuid)).filter((uuid) => uuid.includes(duplicatedData.sceneId));
  19533. duplicatedData.source = tokenDocument.uuid;
  19534. if (CanvasEffect.checkValid(duplicatedData)) {
  19535. if (push)
  19536. sequencerSocket.executeForOthers(
  19537. SOCKET_HANDLERS.PLAY_EFFECT,
  19538. duplicatedData
  19539. );
  19540. if (duplicatedData.sceneId === game.user.viewedScene) {
  19541. await this._playEffect(duplicatedData, false);
  19542. }
  19543. }
  19544. }
  19545. }
  19546. }
  19547. class BaseEffectsLayer extends InteractionLayer {
  19548. static get layerOptions() {
  19549. return foundry.utils.mergeObject(super.layerOptions, {
  19550. elevation: 1e8,
  19551. name: "sequencerEffects"
  19552. });
  19553. }
  19554. }
  19555. class SequencerInterfaceLayer extends InteractionLayer {
  19556. constructor(...args) {
  19557. super(...args);
  19558. }
  19559. static get layerOptions() {
  19560. return foundry.utils.mergeObject(super.layerOptions, {
  19561. elevation: 1e8,
  19562. name: "sequencerInterfaceEffects"
  19563. });
  19564. }
  19565. deactivate() {
  19566. super.deactivate();
  19567. if (!this.active)
  19568. return;
  19569. this._clearChildren();
  19570. this.active = false;
  19571. InteractionManager.tearDown();
  19572. }
  19573. _setup() {
  19574. if (!this.UIContainer || this.UIContainer._destroyed) {
  19575. this.UIContainer = new PIXI.Container();
  19576. this.UIContainer.sortableChildren = true;
  19577. this.UIContainer.parentName = "sequencerUIContainer";
  19578. this.UIContainer.zIndex = 1e13;
  19579. this.addChild(this.UIContainer);
  19580. this.linePoint = this.UIContainer.addChild(new PIXI.Graphics());
  19581. this.line = this.UIContainer.addChild(new PIXI.Graphics());
  19582. this.lineHead = this.UIContainer.addChild(new PIXI.Graphics());
  19583. this.suggestionPoint = this.UIContainer.addChild(new PIXI.Graphics());
  19584. this.effectHoverBoxes = this.UIContainer.addChild(new PIXI.Graphics());
  19585. this.effectSelectionBorder = this.UIContainer.addChild(
  19586. new PIXI.Graphics()
  19587. );
  19588. this.effectSourcePosition = this.UIContainer.addChild(
  19589. new PIXI.Graphics()
  19590. );
  19591. this.effectTargetPosition = this.UIContainer.addChild(
  19592. new PIXI.Graphics()
  19593. );
  19594. this.suggestionPoint.filters = [new PIXI.filters.AlphaFilter(0.75)];
  19595. this.effectSourcePosition.filters = [new PIXI.filters.AlphaFilter(0.75)];
  19596. this.effectTargetPosition.filters = [new PIXI.filters.AlphaFilter(0.75)];
  19597. this.effectSelectionBorder.zIndex = 1;
  19598. this.effectSourcePosition.interactive = true;
  19599. this.effectSourcePosition.on("mousedown", () => {
  19600. SelectionManager.sourcePointSelected();
  19601. });
  19602. this.effectTargetPosition.interactive = true;
  19603. this.effectTargetPosition.on("mousedown", () => {
  19604. SelectionManager.targetPointSelected();
  19605. });
  19606. }
  19607. }
  19608. async _draw(...args) {
  19609. }
  19610. render(...args) {
  19611. super.render(...args);
  19612. this._setup();
  19613. this._clearChildren();
  19614. this._drawHoveredEffectElements();
  19615. if (!this.active)
  19616. return;
  19617. this._drawLine();
  19618. this._drawPoints();
  19619. this._drawSelectedEffectElements();
  19620. this._drawSuggestionPoint();
  19621. }
  19622. _clearChildren() {
  19623. if (!this.UIContainer)
  19624. return;
  19625. this.UIContainer.children.forEach((child) => {
  19626. child.clear();
  19627. });
  19628. }
  19629. _drawLine() {
  19630. if (!EffectPlayer.startPos || !EffectPlayer.endPos || game?.activeTool !== "play-effect")
  19631. return;
  19632. this.line.lineStyle(3, CONSTANTS.COLOR.PRIMARY, 1);
  19633. this.line.moveTo(EffectPlayer.startPos.x, EffectPlayer.startPos.y);
  19634. this.line.lineTo(EffectPlayer.endPos.x, EffectPlayer.endPos.y);
  19635. }
  19636. _drawPoints() {
  19637. if (game?.activeTool !== "play-effect")
  19638. return;
  19639. const startPos = EffectPlayer.startPos || EffectPlayer.cursorPos;
  19640. this.linePoint.beginFill(CONSTANTS.COLOR.PRIMARY);
  19641. this.linePoint.drawCircle(startPos.x, startPos.y, 5);
  19642. if (EffectPlayer.sourceAttachFound) {
  19643. this._drawCrossAtLocation(this.linePoint, startPos);
  19644. }
  19645. if (!EffectPlayer.endPos)
  19646. return;
  19647. const angle = new Ray(startPos, EffectPlayer.endPos).angle;
  19648. this.lineHead.beginFill(CONSTANTS.COLOR.PRIMARY);
  19649. this.lineHead.moveTo(0, -5);
  19650. this.lineHead.lineTo(-15, 30);
  19651. this.lineHead.lineTo(15, 30);
  19652. this.lineHead.endFill();
  19653. this.lineHead.rotation = angle + Math.PI / 2;
  19654. this.lineHead.position.set(EffectPlayer.endPos.x, EffectPlayer.endPos.y);
  19655. if (EffectPlayer.targetAttachFound) {
  19656. this.linePoint.beginFill(CONSTANTS.COLOR.SECONDARY);
  19657. this._drawCrossAtLocation(this.linePoint, EffectPlayer.endPos);
  19658. }
  19659. }
  19660. _drawHoveredEffectElements() {
  19661. const effects = new Set(SelectionManager.hoveredEffects);
  19662. if (SelectionManager.hoveredEffectUI)
  19663. effects.add(SelectionManager.hoveredEffectUI);
  19664. for (const effect of effects) {
  19665. if (!effect || effect === SelectionManager.selectedEffect || effect.data.screenSpace || effect._isEnding)
  19666. continue;
  19667. this._drawBoxAroundEffect(this.effectHoverBoxes, effect);
  19668. }
  19669. }
  19670. _drawSelectedEffectElements() {
  19671. if (!SelectionManager.selectedEffect)
  19672. return;
  19673. this._drawBoxAroundEffect(
  19674. this.effectSelectionBorder,
  19675. SelectionManager.selectedEffect,
  19676. true
  19677. );
  19678. this._drawEffectStartEndPoints(SelectionManager.selectedEffect);
  19679. }
  19680. _drawBoxAroundEffect(graphic, effect, selected = false) {
  19681. if (!effect || effect._destroyed || !effect.spriteContainer || !effect.ready)
  19682. return;
  19683. graphic.lineStyle(3, selected ? CONSTANTS.COLOR.PRIMARY : 16777215, 0.9);
  19684. let boundingBox = effect.sprite.getLocalBounds();
  19685. let dimensions = {
  19686. x: effect.position.x + boundingBox.x * effect.sprite.scale.x,
  19687. y: effect.position.y + boundingBox.y * effect.sprite.scale.y,
  19688. width: boundingBox.width * effect.sprite.scale.x,
  19689. height: boundingBox.height * effect.sprite.scale.y
  19690. };
  19691. if (effect.data.shapes.length) {
  19692. for (const shape of Object.values(effect.shapes)) {
  19693. boundingBox = shape.getLocalBounds();
  19694. dimensions = {
  19695. x: Math.min(
  19696. dimensions.x,
  19697. effect.position.x + boundingBox.x * shape.scale.x
  19698. ),
  19699. y: Math.min(
  19700. dimensions.y,
  19701. effect.position.y + boundingBox.y * shape.scale.y
  19702. ),
  19703. width: Math.max(dimensions.width, boundingBox.width * shape.scale.x),
  19704. height: Math.max(
  19705. dimensions.height,
  19706. boundingBox.height * shape.scale.y
  19707. )
  19708. };
  19709. }
  19710. }
  19711. const rotation2 = Math.normalizeRadians(
  19712. effect.rotationContainer.rotation + effect.spriteContainer.rotation + effect.sprite.rotation
  19713. );
  19714. this._drawRectangle(graphic, effect.position, rotation2, dimensions);
  19715. }
  19716. _drawRectangle(graphic, position, rotation2, dimensions) {
  19717. graphic.moveTo(
  19718. ...rotate_coordinate(
  19719. position,
  19720. {
  19721. x: dimensions.x,
  19722. y: dimensions.y
  19723. },
  19724. -rotation2
  19725. )
  19726. );
  19727. graphic.lineTo(
  19728. ...rotate_coordinate(
  19729. position,
  19730. {
  19731. x: dimensions.x + dimensions.width,
  19732. y: dimensions.y
  19733. },
  19734. -rotation2
  19735. )
  19736. );
  19737. graphic.lineTo(
  19738. ...rotate_coordinate(
  19739. position,
  19740. {
  19741. x: dimensions.x + dimensions.width,
  19742. y: dimensions.y + dimensions.height
  19743. },
  19744. -rotation2
  19745. )
  19746. );
  19747. graphic.lineTo(
  19748. ...rotate_coordinate(
  19749. position,
  19750. {
  19751. x: dimensions.x,
  19752. y: dimensions.y + dimensions.height
  19753. },
  19754. -rotation2
  19755. )
  19756. );
  19757. graphic.lineTo(
  19758. ...rotate_coordinate(
  19759. position,
  19760. {
  19761. x: dimensions.x,
  19762. y: dimensions.y
  19763. },
  19764. -rotation2
  19765. )
  19766. );
  19767. graphic.lineTo(
  19768. ...rotate_coordinate(
  19769. position,
  19770. {
  19771. x: dimensions.x + dimensions.width,
  19772. y: dimensions.y
  19773. },
  19774. -rotation2
  19775. )
  19776. );
  19777. }
  19778. /**
  19779. * Draws the start/end point circles
  19780. * @private
  19781. */
  19782. _drawEffectStartEndPoints(effect) {
  19783. if (!effect || effect._destroyed || !effect.spriteContainer)
  19784. return;
  19785. if (!effect.data.stretchTo || !effect.sourcePosition || !effect.targetPosition)
  19786. return;
  19787. this.effectSourcePosition.beginFill(CONSTANTS.COLOR.PRIMARY);
  19788. this.effectSourcePosition.drawCircle(
  19789. effect.sourcePosition.x,
  19790. effect.sourcePosition.y,
  19791. canvas.grid.size * 0.25
  19792. );
  19793. if (typeof effect.data.source === "string") {
  19794. this._drawCrossAtLocation(
  19795. this.effectSourcePosition,
  19796. effect.sourcePosition
  19797. );
  19798. }
  19799. this.effectTargetPosition.beginFill(CONSTANTS.COLOR.SECONDARY);
  19800. this.effectTargetPosition.drawCircle(
  19801. effect.targetPosition.x,
  19802. effect.targetPosition.y,
  19803. canvas.grid.size * 0.25
  19804. );
  19805. this.effectTargetPosition.alpha = 0.75;
  19806. if (typeof effect.data.target === "string") {
  19807. this._drawCrossAtLocation(
  19808. this.effectTargetPosition,
  19809. effect.targetPosition
  19810. );
  19811. }
  19812. }
  19813. _drawSuggestionPoint() {
  19814. if (!SelectionManager.suggestedProperties || !SelectionManager.selectedEffect)
  19815. return;
  19816. const effect = SelectionManager.selectedEffect;
  19817. const suggestion = SelectionManager.suggestedProperties;
  19818. this.suggestionPoint.position.set(0, 0);
  19819. this.suggestionPoint.rotation = 0;
  19820. if (effect.data.stretchTo) {
  19821. this.suggestionPoint.beginFill(suggestion.color);
  19822. this.suggestionPoint.drawCircle(
  19823. suggestion.position.x,
  19824. suggestion.position.y,
  19825. canvas.grid.size * 0.25
  19826. );
  19827. if (suggestion.showCursor) {
  19828. this._drawCrossAtLocation(this.suggestionPoint, suggestion.position);
  19829. }
  19830. return;
  19831. }
  19832. const boundingBox = effect.spriteContainer.getLocalBounds();
  19833. const dimensions = {
  19834. x: boundingBox.x * effect.scale.x,
  19835. y: boundingBox.y * effect.scale.y,
  19836. width: boundingBox.width * effect.scale.x,
  19837. height: boundingBox.height * effect.scale.y
  19838. };
  19839. this.suggestionPoint.lineStyle(3, CONSTANTS.COLOR.PRIMARY, 0.9);
  19840. this.suggestionPoint.position.set(
  19841. suggestion.position.x,
  19842. suggestion.position.y
  19843. );
  19844. this._drawRectangle(
  19845. this.suggestionPoint,
  19846. suggestion.position,
  19847. effect.rotation,
  19848. dimensions,
  19849. true
  19850. );
  19851. if (suggestion.showCursor) {
  19852. this.suggestionPoint.beginFill(CONSTANTS.COLOR.SECONDARY);
  19853. this._drawCrossAtLocation(this.suggestionPoint);
  19854. }
  19855. if (suggestion.showPoint) {
  19856. this.suggestionPoint.drawCircle(0, 0, canvas.grid.size * 0.2);
  19857. }
  19858. }
  19859. _drawCrossAtLocation(inElement, inPosition = { x: 0, y: 0 }) {
  19860. inElement.drawRect(
  19861. inPosition.x + canvas.grid.size * -0.05,
  19862. inPosition.y + canvas.grid.size * -0.5,
  19863. canvas.grid.size * 0.1,
  19864. canvas.grid.size
  19865. );
  19866. inElement.drawRect(
  19867. inPosition.x + canvas.grid.size * -0.5,
  19868. inPosition.y + canvas.grid.size * -0.05,
  19869. canvas.grid.size,
  19870. canvas.grid.size * 0.1
  19871. );
  19872. }
  19873. }
  19874. class UIEffectsLayer extends InteractionLayer {
  19875. static get layerOptions() {
  19876. return foundry.utils.mergeObject(super.layerOptions, {
  19877. zIndex: 999999999999999,
  19878. name: "sequencerEffectsAboveEverything"
  19879. });
  19880. }
  19881. updateTransform() {
  19882. if (this.sortableChildren && this.sortDirty) {
  19883. this.sortChildren();
  19884. }
  19885. this._boundsID++;
  19886. this.transform.updateTransform(PIXI.Transform.IDENTITY);
  19887. this.worldAlpha = this.alpha;
  19888. for (let child of this.children) {
  19889. if (child.visible) {
  19890. child.updateTransform();
  19891. }
  19892. }
  19893. }
  19894. }
  19895. let layer = false;
  19896. class SequencerAboveUILayer {
  19897. constructor(name, zIndex = 0.1) {
  19898. this.canvas = document.createElement("canvas");
  19899. this.canvas.id = name;
  19900. this.canvas.style.cssText = `
  19901. position:absolute;
  19902. touch-action: none;
  19903. pointer-events: none;
  19904. width:100%;
  19905. height:100%;
  19906. z-index:${zIndex};
  19907. padding: 0;
  19908. margin: 0;
  19909. `;
  19910. document.body.appendChild(this.canvas);
  19911. this.app = new PIXI.Application({
  19912. width: window.innerWidth,
  19913. height: window.innerHeight,
  19914. view: this.canvas,
  19915. antialias: true,
  19916. backgroundAlpha: 0,
  19917. sharedTicker: true
  19918. });
  19919. this.app.resizeTo = window;
  19920. this.app.stage.renderable = false;
  19921. }
  19922. static setup() {
  19923. if (!game.settings.get("sequencer", "enable-above-ui-screenspace"))
  19924. return;
  19925. layer = new this("sequencerUILayerAbove", 1e4);
  19926. }
  19927. static getLayer() {
  19928. return layer ? layer.app.stage : canvas.uiEffectsLayer;
  19929. }
  19930. static addChild(...args) {
  19931. const result = this.getLayer().addChild(...args);
  19932. layer.app.stage.renderable = layer.app.stage.children.length > 0;
  19933. return result;
  19934. }
  19935. static sortChildren() {
  19936. return this.getLayer().sortChildren();
  19937. }
  19938. static removeContainerByEffect(inEffect) {
  19939. const child = this.getLayer().children.find((child2) => child2 === inEffect);
  19940. if (!child)
  19941. return;
  19942. this.getLayer().removeChild(child);
  19943. layer.app.stage.renderable = layer.app.stage.children.length > 0;
  19944. }
  19945. updateTransform() {
  19946. if (this.app.stage.sortableChildren && this.app.stage.sortDirty) {
  19947. this.app.stage.sortChildren();
  19948. }
  19949. this.app.stage._boundsID++;
  19950. this.app.stage.transform.updateTransform(PIXI.Transform.IDENTITY);
  19951. this.app.stage.worldAlpha = this.app.stage.alpha;
  19952. for (let child of this.app.stage.children) {
  19953. if (child.visible) {
  19954. child.updateTransform();
  19955. }
  19956. }
  19957. }
  19958. }
  19959. class VisionSamplerShader extends BaseSamplerShader {
  19960. /** @override */
  19961. static classPluginName = null;
  19962. /** @inheritdoc */
  19963. static vertexShader = `
  19964. precision ${PIXI.settings.PRECISION_VERTEX} float;
  19965. attribute vec2 aVertexPosition;
  19966. attribute vec2 aTextureCoord;
  19967. uniform mat3 projectionMatrix;
  19968. uniform vec2 screenDimensions;
  19969. varying vec2 vUvsMask;
  19970. varying vec2 vUvs;
  19971. void main() {
  19972. vUvs = aTextureCoord;
  19973. vUvsMask = aVertexPosition / screenDimensions;
  19974. gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
  19975. }
  19976. `;
  19977. /** @inheritdoc */
  19978. static fragmentShader = `
  19979. precision ${PIXI.settings.PRECISION_FRAGMENT} float;
  19980. varying vec2 vUvs;
  19981. varying vec2 vUvsMask;
  19982. uniform vec4 tintAlpha;
  19983. uniform sampler2D sampler;
  19984. uniform sampler2D maskSampler;
  19985. uniform bool enableVisionMasking;
  19986. void main() {
  19987. float mask = enableVisionMasking ? texture2D(maskSampler, vUvsMask).r : 1.0;
  19988. gl_FragColor = texture2D(sampler, vUvs) * tintAlpha * mask;
  19989. }
  19990. `;
  19991. /** @inheritdoc */
  19992. static defaultUniforms = {
  19993. tintAlpha: [1, 1, 1, 1],
  19994. sampler: 0,
  19995. maskSampler: 0,
  19996. screenDimensions: [1, 1],
  19997. enableVisionMasking: false
  19998. };
  19999. /** @override */
  20000. _preRender(mesh) {
  20001. super._preRender(mesh);
  20002. this.uniforms.maskSampler = canvas.masks.vision.renderTexture;
  20003. this.uniforms.screenDimensions = canvas.screenDimensions;
  20004. this.uniforms.enableVisionMasking = canvas.effects.visibility.visible;
  20005. }
  20006. }
  20007. const hooksManager = {
  20008. _hooks: /* @__PURE__ */ new Map(),
  20009. _hooksRegistered: /* @__PURE__ */ new Set(),
  20010. addHook(effectUuid, hookName, callable, callNow = false) {
  20011. if (!this._hooksRegistered.has(hookName)) {
  20012. debug("registering hook for: " + hookName);
  20013. this._hooksRegistered.add(hookName);
  20014. Hooks.on(hookName, (...args) => {
  20015. this._hookCalled(hookName, ...args);
  20016. });
  20017. }
  20018. const key = hookName + "-" + effectUuid;
  20019. if (!this._hooks.has(key)) {
  20020. this._hooks.set(key, []);
  20021. }
  20022. this._hooks.get(key).push(callable);
  20023. if (callNow) {
  20024. setTimeout(() => {
  20025. callable();
  20026. }, 20);
  20027. }
  20028. },
  20029. _hookCalled(hookName, ...args) {
  20030. Array.from(this._hooks).filter((entry) => entry[0].startsWith(hookName + "-")).map((hooks) => hooks[1]).deepFlatten().forEach((callback) => callback(...args));
  20031. },
  20032. removeHooks(effectUuid) {
  20033. Array.from(this._hooks).filter((entry) => entry[0].endsWith("-" + effectUuid)).forEach((entry) => this._hooks.delete(entry[0]));
  20034. }
  20035. };
  20036. class CanvasEffect extends PIXI.Container {
  20037. #elevation = 0;
  20038. #sort = 0;
  20039. constructor(inData) {
  20040. super();
  20041. this.sortableChildren = true;
  20042. this.actualCreationTime = +new Date();
  20043. this.data = inData;
  20044. this._resolve = null;
  20045. this._durationResolve = null;
  20046. this.ready = false;
  20047. this._ended = false;
  20048. this._isEnding = false;
  20049. this._cachedSourceData = {};
  20050. this._cachedTargetData = {};
  20051. this.uuid = false;
  20052. }
  20053. static get protectedValues() {
  20054. return [
  20055. "_id",
  20056. "sequenceId",
  20057. "creationTimestamp",
  20058. "creatorUserId",
  20059. "moduleName",
  20060. "index",
  20061. "repetition",
  20062. "moves",
  20063. "fadeIn",
  20064. "fadeOut",
  20065. "scaleIn",
  20066. "scaleOut",
  20067. "rotateIn",
  20068. "rotateOut",
  20069. "fadeInAudio",
  20070. "fadeOutAudio",
  20071. "animations",
  20072. "nameOffsetMap",
  20073. "persist"
  20074. ];
  20075. }
  20076. /** @type {number} */
  20077. get elevation() {
  20078. return this.#elevation;
  20079. }
  20080. set elevation(value) {
  20081. this.#elevation = value;
  20082. }
  20083. /** @type {number} */
  20084. get sort() {
  20085. return this.#sort;
  20086. }
  20087. set sort(value) {
  20088. this.#sort = value;
  20089. }
  20090. get context() {
  20091. return this.data.attachTo?.active && this.sourceDocument ? this.sourceDocument : game.scenes.get(this.data.sceneId);
  20092. }
  20093. get isIsometricActive() {
  20094. const sceneIsIsometric = getProperty(
  20095. game.scenes.get(this.data.sceneId),
  20096. CONSTANTS.INTEGRATIONS.ISOMETRIC.SCENE_ENABLED
  20097. );
  20098. return CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE && sceneIsIsometric;
  20099. }
  20100. /**
  20101. * The ID of the effect
  20102. *
  20103. * @returns {string}
  20104. */
  20105. get id() {
  20106. return this.data._id;
  20107. }
  20108. /**
  20109. * Whether this effect is destroyed or is in the process of being destroyed
  20110. */
  20111. get isDestroyed() {
  20112. return this.source && this.isSourceDestroyed || this.target && this.isTargetDestroyed;
  20113. }
  20114. /**
  20115. * Whether the source of this effect is temporary
  20116. *
  20117. * @returns {boolean}
  20118. */
  20119. get isSourceTemporary() {
  20120. return this.data.attachTo?.active && this.sourceDocument && !is_UUID(this.sourceDocument?.uuid);
  20121. }
  20122. /**
  20123. * Whether the source of this effect has been destroyed
  20124. *
  20125. * @returns {boolean}
  20126. */
  20127. get isSourceDestroyed() {
  20128. return this.source && (this.source?.destroyed || this.sourceDocument?._destroyed);
  20129. }
  20130. /**
  20131. * Whether the target of this effect is temporary
  20132. *
  20133. * @returns {boolean}
  20134. */
  20135. get isTargetTemporary() {
  20136. return (this.data.stretchTo?.attachTo || this.data.rotateTowards?.attachTo) && this.targetDocument && !is_UUID(this.targetDocument.uuid);
  20137. }
  20138. /**
  20139. * Whether the target of this effect has been destroyed
  20140. *
  20141. * @returns {boolean}
  20142. */
  20143. get isTargetDestroyed() {
  20144. return this.target && (this.target?.destroyed || this.targetDocument?._destroyed);
  20145. }
  20146. /**
  20147. * The source object (or source location) of the effect
  20148. *
  20149. * @returns {boolean|object}
  20150. */
  20151. get source() {
  20152. if (!this._source && this.data.source) {
  20153. this._source = this._getObjectByID(this.data.source);
  20154. this._source = this._source?._object ?? this._source;
  20155. }
  20156. return this._source;
  20157. }
  20158. /**
  20159. * Retrieves the source document
  20160. *
  20161. * @returns {Document|PlaceableObject}
  20162. */
  20163. get sourceDocument() {
  20164. return this.source?.document ?? this.source;
  20165. }
  20166. /**
  20167. * Retrieves the PIXI object for the source object
  20168. *
  20169. * @returns {*|PIXI.Sprite|TileHUD<Application.Options>}
  20170. */
  20171. get sourceMesh() {
  20172. return this.source?.mesh ?? this.source?.template;
  20173. }
  20174. /**
  20175. * The source position with the relevant offsets calculated
  20176. *
  20177. * @returns {{x: number, y: number}}
  20178. */
  20179. get sourcePosition() {
  20180. let position = this.getSourceData().position;
  20181. let offset2 = this._getOffset(this.data.source, true);
  20182. return {
  20183. x: position.x - offset2.x,
  20184. y: position.y - offset2.y
  20185. };
  20186. }
  20187. /**
  20188. * The target object (or target location) of the effect
  20189. *
  20190. * @returns {boolean|object}
  20191. */
  20192. get target() {
  20193. if (!this._target && this.data.target) {
  20194. this._target = this._getObjectByID(this.data.target);
  20195. this._target = this._target?._object ?? this._target;
  20196. }
  20197. return this._target;
  20198. }
  20199. /**
  20200. * Retrieves the document of the target
  20201. *
  20202. * @returns {Document|PlaceableObject}
  20203. */
  20204. get targetDocument() {
  20205. return this.target?.document ?? this.target;
  20206. }
  20207. /**
  20208. * Retrieves the PIXI object for the target object
  20209. *
  20210. * @returns {*|PIXI.Sprite|TileHUD<Application.Options>}
  20211. */
  20212. get targetMesh() {
  20213. return this.target?.mesh ?? this.target?.template;
  20214. }
  20215. /**
  20216. * The target position with the relevant offsets calculated
  20217. *
  20218. * @returns {{x: number, y: number}}
  20219. */
  20220. get targetPosition() {
  20221. const position = this.getTargetData().position;
  20222. const offset2 = this._getOffset(this.data.target);
  20223. return {
  20224. x: position.x - offset2.x,
  20225. y: position.y - offset2.y
  20226. };
  20227. }
  20228. /**
  20229. * Returns this effect's world position
  20230. *
  20231. * @returns {{x: number, y: number}}
  20232. */
  20233. get worldPosition() {
  20234. const t = canvas.stage.worldTransform;
  20235. return {
  20236. x: (this.sprite.worldTransform.tx - t.tx) / canvas.stage.scale.x,
  20237. y: (this.sprite.worldTransform.ty - t.ty) / canvas.stage.scale.y
  20238. };
  20239. }
  20240. /**
  20241. * Whether the current user is the owner of this effect
  20242. *
  20243. * @returns {boolean}
  20244. */
  20245. get owner() {
  20246. return this.data.creatorUserId === game.user.id;
  20247. }
  20248. /**
  20249. * Whether the current user can update this effect
  20250. *
  20251. * @returns {boolean}
  20252. */
  20253. get userCanUpdate() {
  20254. return game.user.isGM || this.owner || this.data.attachTo?.active && this.sourceDocument.canUserModify(game.user, "update");
  20255. }
  20256. /**
  20257. * Whether the current user can delete this effect
  20258. *
  20259. * @returns {boolean}
  20260. */
  20261. get userCanDelete() {
  20262. return this.userCanUpdate || user_can_do("permissions-effect-delete");
  20263. }
  20264. /**
  20265. * Whether this effect is on the current scene
  20266. *
  20267. * @returns {boolean}
  20268. */
  20269. get onCurrentScene() {
  20270. return this.data.sceneId === game.user.viewedScene;
  20271. }
  20272. /**
  20273. * Whether this effect should be shown as faded or not - effects created by users for other users should be shown
  20274. * for all
  20275. *
  20276. * @returns {boolean}
  20277. */
  20278. get shouldShowFadedVersion() {
  20279. return game.settings.get(CONSTANTS.MODULE_NAME, "user-effect-opacity") !== 0 && 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);
  20280. }
  20281. /**
  20282. * Getter for the current playing video of the effect
  20283. *
  20284. * @returns {null|*}
  20285. */
  20286. get video() {
  20287. return this._video;
  20288. }
  20289. /**
  20290. * Setter for the current playing video of the effect
  20291. */
  20292. set video(inVideo) {
  20293. if (!inVideo)
  20294. return;
  20295. inVideo.playbackRate = this.data.playbackRate ? this.data.playbackRate : 1;
  20296. inVideo.muted = !this.data.volume;
  20297. inVideo.volume = (this.data.volume ?? 0) * game.settings.get("core", "globalInterfaceVolume");
  20298. if (!this._video) {
  20299. this._video = inVideo;
  20300. return;
  20301. }
  20302. const isLooping = this._video?.loop;
  20303. const currentTime = this._video.currentTime;
  20304. this._video = inVideo;
  20305. this._video.currentTime = this.playNaturally ? 0 : Math.min(currentTime, this._video.duration);
  20306. this._video.loop = isLooping;
  20307. this._texture.update();
  20308. }
  20309. async playMedia() {
  20310. if (this.animatedSprite) {
  20311. await this.sprite.play();
  20312. } else if (this.video) {
  20313. try {
  20314. await this.video.play();
  20315. } catch (err) {
  20316. }
  20317. }
  20318. this._setupTimestampHook(this.mediaCurrentTime * 1e3);
  20319. }
  20320. async pauseMedia() {
  20321. if (this.animatedSprite) {
  20322. return this.sprite.stop();
  20323. } else if (this.video) {
  20324. return this.video.pause();
  20325. }
  20326. }
  20327. get mediaLooping() {
  20328. if (this.animatedSprite) {
  20329. return this.sprite.loop;
  20330. }
  20331. return this.video?.loop ?? false;
  20332. }
  20333. set mediaLooping(looping) {
  20334. if (this.animatedSprite) {
  20335. this.sprite.loop = looping;
  20336. return;
  20337. }
  20338. if (this.video) {
  20339. this.video.loop = looping;
  20340. }
  20341. }
  20342. get mediaIsPlaying() {
  20343. if (this.animatedSprite) {
  20344. return this.sprite.playing;
  20345. }
  20346. return this.video;
  20347. }
  20348. get mediaCurrentTime() {
  20349. if (this.animatedSprite) {
  20350. return this.sprite.currentFrame / this.sprite.totalFrames * (this.sprite.totalFrames / 24);
  20351. }
  20352. return this.video?.currentTime ?? null;
  20353. }
  20354. set mediaPlaybackRate(inPlaybackRate) {
  20355. if (this.animatedSprite) {
  20356. this.sprite.animationSpeed = 0.4 * inPlaybackRate;
  20357. } else if (this.video) {
  20358. this.video.playbackRate = inPlaybackRate;
  20359. }
  20360. }
  20361. set mediaCurrentTime(newTime) {
  20362. if (this.animatedSprite) {
  20363. const newFrame = Math.floor(newTime * this.sprite.totalFrames);
  20364. const clampedFrame = Math.max(
  20365. 0,
  20366. Math.min(newFrame, this.sprite.totalFrames)
  20367. );
  20368. if (this.mediaIsPlaying) {
  20369. this.sprite.gotoAndPlay(clampedFrame);
  20370. } else {
  20371. this.sprite.gotoAndStop(clampedFrame);
  20372. }
  20373. } else if (this.video) {
  20374. this.video.currentTime = newTime;
  20375. }
  20376. }
  20377. get mediaDuration() {
  20378. if (this.animatedSprite) {
  20379. return this.sprite.totalFrames / this.sprite.animationSpeed / PIXI.Ticker.shared.FPS;
  20380. } else if (this.video) {
  20381. return this.video?.duration;
  20382. }
  20383. return 1;
  20384. }
  20385. get hasAnimatedMedia() {
  20386. return !!(this.video || this.animatedSprite);
  20387. }
  20388. /**
  20389. * The template of the effect, determining the effect's internal grid size, and start/end padding
  20390. *
  20391. * @returns {object}
  20392. */
  20393. get template() {
  20394. return foundry.utils.mergeObject(
  20395. {
  20396. gridSize: 100,
  20397. startPoint: 0,
  20398. endPoint: 0
  20399. },
  20400. this._template ?? {}
  20401. );
  20402. }
  20403. /**
  20404. * 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.
  20405. *
  20406. * @returns {number}
  20407. */
  20408. get gridSizeDifference() {
  20409. return canvas.grid.size / this.template.gridSize;
  20410. }
  20411. /**
  20412. * Whether the effect should be flipped on any given axis
  20413. *
  20414. * @returns {number}
  20415. */
  20416. get flipX() {
  20417. return this.data.flipX ? -1 : 1;
  20418. }
  20419. get flipY() {
  20420. return this.data.flipY ? -1 : 1;
  20421. }
  20422. /**
  20423. * Whether this effect should play at all, depending on a multitude of factors
  20424. *
  20425. * @returns {boolean}
  20426. */
  20427. get shouldPlay() {
  20428. 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));
  20429. }
  20430. get shouldPlayVisible() {
  20431. let playVisible = this.shouldPlay && game.settings.get("sequencer", "effectsEnabled") && game.user.viewedScene === this.data.sceneId;
  20432. if (isNewerVersion(game.version, "10.289") && game.settings.get("core", "photosensitiveMode")) {
  20433. playVisible = false;
  20434. throttled_custom_warning(
  20435. this.data.moduleName,
  20436. "Photosensitive Mode is turned on, so Sequencer's visual effects aren't being rendered"
  20437. );
  20438. }
  20439. return playVisible;
  20440. }
  20441. /**
  20442. * Whether this effect should play naturally, or be constrained to a subsection of the video
  20443. *
  20444. * @returns {boolean}
  20445. */
  20446. get playNaturally() {
  20447. return (!this.data.time || this._startTime === 0 && this._endTime === this.mediaDuration) && this._animationTimes.loopStart === void 0 && this._animationTimes.loopEnd === void 0;
  20448. }
  20449. static make(inData) {
  20450. return !inData.persist ? new CanvasEffect(inData) : new PersistentCanvasEffect(inData);
  20451. }
  20452. static checkValid(effectData) {
  20453. let sourceExists = true;
  20454. let targetExists = true;
  20455. if (effectData.source && is_UUID(effectData.source)) {
  20456. sourceExists = from_uuid_fast(effectData.source);
  20457. }
  20458. if (effectData.target && is_UUID(effectData.target)) {
  20459. targetExists = from_uuid_fast(effectData.target);
  20460. }
  20461. if (effectData.source && is_UUID(effectData.source) && effectData.target && is_UUID(effectData.target)) {
  20462. const sourceScene = effectData.source.split(".")[1];
  20463. const targetScene = effectData.target.split(".")[1];
  20464. if (sourceScene !== targetScene || sourceScene !== effectData.sceneId)
  20465. return false;
  20466. }
  20467. return sourceExists && targetExists;
  20468. }
  20469. /**
  20470. * Validates that the update contains the appropriate data
  20471. *
  20472. * @param inUpdates
  20473. */
  20474. static validateUpdate(inUpdates) {
  20475. const updateKeys = Object.keys(inUpdates);
  20476. const protectedValues = updateKeys.filter(
  20477. (key) => CanvasEffect.protectedValues.includes(key)
  20478. );
  20479. if (protectedValues.length) {
  20480. throw custom_error(
  20481. "Sequencer",
  20482. `CanvasEffect | update | You cannot update the following keys of an effect's data: ${protectedValues.join(
  20483. "\n - "
  20484. )}`
  20485. );
  20486. }
  20487. if (updateKeys.includes("source")) {
  20488. if (!(is_UUID(inUpdates.source) || is_object_canvas_data(inUpdates.source))) {
  20489. throw custom_error(
  20490. "Sequencer",
  20491. `CanvasEffect | update | source must be of type document UUID or object with X and Y coordinates`
  20492. );
  20493. }
  20494. }
  20495. if (updateKeys.includes("target")) {
  20496. if (!(is_UUID(inUpdates.target) || is_object_canvas_data(inUpdates.target))) {
  20497. throw custom_error(
  20498. "Sequencer",
  20499. `CanvasEffect | update | target must be of type document UUID or object with X and Y coordinates`
  20500. );
  20501. }
  20502. }
  20503. }
  20504. getHook(type, uuid) {
  20505. if (!is_UUID(uuid))
  20506. return false;
  20507. const parts = uuid.split(".");
  20508. return type + parts[parts.length - 2];
  20509. }
  20510. /**
  20511. * Gets the source hook name
  20512. *
  20513. * @param {string} type
  20514. * @returns {string|boolean}
  20515. */
  20516. getSourceHook(type = "") {
  20517. return this.getHook(type, this.data.source);
  20518. }
  20519. /**
  20520. * The source object's current position, or its current position
  20521. *
  20522. * @returns {boolean|object}
  20523. */
  20524. getSourceData() {
  20525. if (this.data.temporary && !this.owner) {
  20526. return SequencerEffectManager.getPositionForUUID(this.data.source);
  20527. }
  20528. const position = this.source instanceof PlaceableObject && !this.isSourceTemporary ? get_object_position(this.source) : this.source?.worldPosition || this.source?.center || this.source;
  20529. const { width: width2, height } = get_object_dimensions(this.source);
  20530. if (this.isIsometricActive && this.source instanceof PlaceableObject) {
  20531. position.x += (this.sourceDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size;
  20532. position.y -= (this.sourceDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size;
  20533. if (this.data.isometric?.overlay || this.target instanceof PlaceableObject) {
  20534. position.x += (this.source?.height ?? height) / 2;
  20535. position.y -= (this.source?.height ?? height) / 2;
  20536. }
  20537. }
  20538. if (position !== void 0) {
  20539. this._cachedSourceData.position = position;
  20540. }
  20541. if (width2 !== void 0 && height !== void 0) {
  20542. this._cachedSourceData.width = width2;
  20543. this._cachedSourceData.height = height;
  20544. }
  20545. let rotation2 = 0;
  20546. if (this.source instanceof MeasuredTemplate && this.sourceDocument?.t !== "rect") {
  20547. rotation2 = Math.normalizeRadians(
  20548. Math.toRadians(this.sourceDocument?.direction)
  20549. );
  20550. } else if (!(this.source instanceof MeasuredTemplate)) {
  20551. rotation2 = this.sourceDocument?.rotation ? Math.normalizeRadians(Math.toRadians(this.sourceDocument?.rotation)) : 0;
  20552. }
  20553. if (rotation2 !== void 0) {
  20554. this._cachedSourceData.rotation = rotation2;
  20555. }
  20556. const alpha = this.sourceDocument instanceof TokenDocument || this.sourceDocument instanceof TileDocument ? this.sourceDocument?.alpha ?? 1 : 1;
  20557. if (alpha !== void 0) {
  20558. this._cachedSourceData.alpha = alpha;
  20559. }
  20560. return {
  20561. ...this._cachedSourceData
  20562. };
  20563. }
  20564. /**
  20565. * Gets the target hook name
  20566. *
  20567. * @param {string} type
  20568. * @returns {string|boolean}
  20569. */
  20570. getTargetHook(type = "") {
  20571. return this.getHook(type, this.data.target);
  20572. }
  20573. /**
  20574. * The target object's current position, or its current position
  20575. *
  20576. * @returns {boolean|object}
  20577. */
  20578. getTargetData() {
  20579. if (this.data.temporary && !this.owner) {
  20580. return SequencerEffectManager.getPositionForUUID(this.data.target) ?? this.getSourceData();
  20581. }
  20582. const position = this.target instanceof PlaceableObject && !this.isTargetTemporary ? get_object_position(this.target, { measure: true }) : this.target?.worldPosition || this.target?.center || this.target;
  20583. const { width: width2, height } = get_object_dimensions(this.target);
  20584. if (this.isIsometricActive && this.target instanceof PlaceableObject) {
  20585. const targetHeight = this.target?.height ?? height;
  20586. position.x += (this.targetDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size + targetHeight;
  20587. position.y -= (this.targetDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size + targetHeight;
  20588. }
  20589. if (width2 !== void 0 && height !== void 0) {
  20590. this._cachedTargetData.width = width2;
  20591. this._cachedTargetData.height = height;
  20592. }
  20593. if (position !== void 0) {
  20594. this._cachedTargetData.position = position;
  20595. }
  20596. let rotation2 = 0;
  20597. if (this.target instanceof MeasuredTemplate && this.targetDocument?.t !== "rect") {
  20598. rotation2 = Math.normalizeRadians(
  20599. Math.toRadians(this.targetDocument?.direction)
  20600. );
  20601. } else if (!(this.target instanceof MeasuredTemplate)) {
  20602. rotation2 = this.targetDocument?.rotation ? Math.normalizeRadians(Math.toRadians(this.targetDocument?.rotation)) : 0;
  20603. }
  20604. if (rotation2 !== void 0) {
  20605. this._cachedTargetData.rotation = rotation2;
  20606. }
  20607. const alpha = this.targetDocument instanceof TokenDocument || this.targetDocument instanceof TileDocument ? this.targetDocument?.alpha ?? 1 : 1;
  20608. if (alpha !== void 0) {
  20609. this._cachedTargetData.alpha = alpha;
  20610. }
  20611. return {
  20612. ...this._cachedTargetData
  20613. };
  20614. }
  20615. /**
  20616. * Calculates the offset for a given offset property and name mapping
  20617. *
  20618. * @param {string} offsetMapName
  20619. * @param {boolean} source
  20620. * @returns {{x: number, y: number}|*}
  20621. * @private
  20622. */
  20623. _getOffset(offsetMapName, source = false) {
  20624. const key = source ? "source" : "target";
  20625. const offset2 = {
  20626. x: 0,
  20627. y: 0
  20628. };
  20629. let twister = this._twister;
  20630. let nameOffsetMap = this._nameOffsetMap?.[this.data.name];
  20631. if (nameOffsetMap) {
  20632. twister = nameOffsetMap.twister;
  20633. }
  20634. if (this.data.missed && (!source || !this.data.target)) {
  20635. let missedOffset = this._offsetCache[key]?.missedOffset || calculate_missed_position(this.source, this.target, twister);
  20636. this._offsetCache[key].missedOffset = missedOffset;
  20637. offset2.x -= missedOffset.x;
  20638. offset2.y -= missedOffset.y;
  20639. }
  20640. const obj = source ? this.source : this.target;
  20641. const multiplier = source ? this.data.randomOffset?.source : this.data.randomOffset?.target;
  20642. if (obj && multiplier) {
  20643. let randomOffset = this._offsetCache[key]?.randomOffset || get_random_offset(obj, multiplier, twister);
  20644. this._offsetCache[key].randomOffset = randomOffset;
  20645. offset2.x -= randomOffset.x;
  20646. offset2.y -= randomOffset.y;
  20647. }
  20648. let extraOffset = this.data?.offset?.[key];
  20649. if (extraOffset) {
  20650. let newOffset = {
  20651. x: extraOffset.x,
  20652. y: extraOffset.y
  20653. };
  20654. if (extraOffset.gridUnits) {
  20655. newOffset.x *= canvas.grid.size;
  20656. newOffset.y *= canvas.grid.size;
  20657. }
  20658. if (extraOffset.local) {
  20659. newOffset = rotateAroundPoint(
  20660. 0,
  20661. 0,
  20662. newOffset.x,
  20663. newOffset.y,
  20664. -this.rotationContainer.angle
  20665. );
  20666. }
  20667. offset2.x -= newOffset.x;
  20668. offset2.y -= newOffset.y;
  20669. }
  20670. let offsetMap = this._nameOffsetMap?.[offsetMapName];
  20671. if (!this._offsetCache[key]["nameCache"][offsetMapName]) {
  20672. this._offsetCache[key]["nameCache"][offsetMapName] = {};
  20673. }
  20674. if (offsetMap) {
  20675. if (offsetMap.missed) {
  20676. const missedOffset = this._offsetCache[key]["nameCache"][offsetMapName]?.missedOffset || calculate_missed_position(
  20677. offsetMap.sourceObj,
  20678. offsetMap.targetObj,
  20679. offsetMap.twister
  20680. );
  20681. this._offsetCache[key]["nameCache"][offsetMapName].missedOffset = missedOffset;
  20682. offset2.x -= missedOffset.x;
  20683. offset2.y -= missedOffset.y;
  20684. }
  20685. const obj2 = offsetMap.targetObj || offsetMap.sourceObj;
  20686. const multiplier2 = offsetMap.randomOffset?.source || offsetMap.randomOffset?.target;
  20687. if (obj2 && multiplier2) {
  20688. let randomOffset = this._offsetCache[key]["nameCache"][offsetMapName]?.randomOffset || get_random_offset(obj2, multiplier2, offsetMap.twister);
  20689. this._offsetCache[key]["nameCache"][offsetMapName].randomOffset = randomOffset;
  20690. offset2.x -= randomOffset.x;
  20691. offset2.y -= randomOffset.y;
  20692. }
  20693. if (offsetMap.offset) {
  20694. offset2.x += offsetMap.offset.x;
  20695. offset2.y += offsetMap.offset.y;
  20696. }
  20697. }
  20698. return offset2;
  20699. }
  20700. /**
  20701. * Initializes the name offset map by establishing targets
  20702. *
  20703. * @param inOffsetMap
  20704. * @returns {{setup}|*}
  20705. * @private
  20706. */
  20707. _setupOffsetMap(inOffsetMap) {
  20708. if (!inOffsetMap.setup) {
  20709. inOffsetMap.setup = true;
  20710. inOffsetMap.sourceObj = inOffsetMap.source ? this._validateObject(inOffsetMap.source) : false;
  20711. inOffsetMap.targetObj = inOffsetMap.target ? this._validateObject(inOffsetMap.target) : false;
  20712. const repetition = this.data.repetition % inOffsetMap.repetitions;
  20713. const seed = get_hash(`${inOffsetMap.seed}-${repetition}`);
  20714. inOffsetMap.twister = new MersenneTwister(seed);
  20715. }
  20716. return inOffsetMap;
  20717. }
  20718. /**
  20719. * Plays the effect, returning two promises; one that resolves once the duration has been established, and another
  20720. * when the effect has finished playing
  20721. *
  20722. * @returns {Object}
  20723. */
  20724. play() {
  20725. const durationPromise = new Promise((resolve, reject2) => {
  20726. this._durationResolve = resolve;
  20727. });
  20728. const finishPromise = new Promise(async (resolve, reject2) => {
  20729. this._resolve = resolve;
  20730. Hooks.callAll("createSequencerEffect", this);
  20731. debug(`Playing effect:`, this.data);
  20732. this._initialize();
  20733. });
  20734. return {
  20735. duration: durationPromise,
  20736. promise: finishPromise
  20737. };
  20738. }
  20739. /**
  20740. * Ends the effect
  20741. */
  20742. endEffect() {
  20743. if (this._ended)
  20744. return;
  20745. Hooks.callAll("endedSequencerEffect", this);
  20746. this.destroy();
  20747. }
  20748. destroy(...args) {
  20749. this._destroyDependencies();
  20750. return super.destroy(...args);
  20751. }
  20752. /**
  20753. * Updates this effect with the given parameters
  20754. * @param inUpdates
  20755. * @returns {Promise}
  20756. */
  20757. async update(inUpdates) {
  20758. if (!this.userCanUpdate)
  20759. throw custom_error(
  20760. "Sequencer",
  20761. "CanvasEffect | Update | You do not have permission to update this effect"
  20762. );
  20763. CanvasEffect.validateUpdate(inUpdates);
  20764. const newData = foundry.utils.deepClone(this.data);
  20765. const updateKeys = Object.keys(inUpdates);
  20766. updateKeys.forEach((key) => {
  20767. setProperty(newData, key, inUpdates[key]);
  20768. });
  20769. if (Object.keys(foundry.utils.diffObject(newData, this.data)).length === 0) {
  20770. debug(
  20771. `Skipped updating effect with ID ${this.id} - no changes needed`
  20772. );
  20773. return;
  20774. }
  20775. if (this.data.persist) {
  20776. const originalSourceUUID = is_UUID(this.data.source) && this.data.attachTo ? this.data.source : "Scene." + this.data.sceneId;
  20777. const newSourceUUID = is_UUID(newData.source) && newData.attachTo ? newData.source : "Scene." + newData.sceneId;
  20778. if (originalSourceUUID !== newSourceUUID) {
  20779. flagManager.removeFlags(originalSourceUUID, newData);
  20780. }
  20781. flagManager.addFlags(newSourceUUID, newData);
  20782. }
  20783. debug(`Updated effect with ID ${this.id}`);
  20784. return sequencerSocket.executeForEveryone(
  20785. SOCKET_HANDLERS.UPDATE_EFFECT,
  20786. this.id,
  20787. newData
  20788. );
  20789. }
  20790. async addAnimatedProperties({ animations = [], loopingAnimation = [] } = {}) {
  20791. const animationsToAdd = [];
  20792. if (!Array.isArray(animations)) {
  20793. throw custom_error(
  20794. this.data.moduleName,
  20795. `animations must be an array of arrays`
  20796. );
  20797. }
  20798. for (const animationData of animations) {
  20799. if (!Array.isArray(animationData)) {
  20800. throw custom_error(
  20801. this.data.moduleName,
  20802. `each entry in animations must be an array, each with target, property name, and animation options`
  20803. );
  20804. }
  20805. const result = validateAnimation(...animationData);
  20806. if (typeof result === "string") {
  20807. throw custom_error(this.data.moduleName, result);
  20808. }
  20809. result.creationTimestamp = +new Date();
  20810. animationsToAdd.push(result);
  20811. }
  20812. if (!Array.isArray(loopingAnimation)) {
  20813. throw custom_error(
  20814. this.data.moduleName,
  20815. `loopingAnimation must be an array of arrays`
  20816. );
  20817. }
  20818. for (const animationData of loopingAnimation) {
  20819. if (!Array.isArray(animationData)) {
  20820. throw custom_error(
  20821. this.data.moduleName,
  20822. `each entry in loopingAnimation must be an array, each with target, property name, and animation options`
  20823. );
  20824. }
  20825. const result = validateLoopingAnimation(...animationData);
  20826. if (typeof result === "string") {
  20827. throw custom_error(this.data.moduleName, result);
  20828. }
  20829. result.creationTimestamp = +new Date();
  20830. animationsToAdd.push(result);
  20831. }
  20832. if (this.data.persist) {
  20833. const originalSourceUUID = is_UUID(this.data.source) && this.data.attachTo ? this.data.source : "Scene." + this.data.sceneId;
  20834. const newData = foundry.utils.deepClone(this.data);
  20835. newData.animations = (newData.animations ?? []).concat(
  20836. foundry.utils.deepClone(animationsToAdd)
  20837. );
  20838. flagManager.addFlags(originalSourceUUID, newData);
  20839. }
  20840. return sequencerSocket.executeForEveryone(
  20841. SOCKET_HANDLERS.ADD_EFFECT_ANIMATIONS,
  20842. this.id,
  20843. animationsToAdd
  20844. );
  20845. }
  20846. async _addAnimations(inAnimations) {
  20847. this._playAnimations(inAnimations);
  20848. this.data.animations = (this.data.animations ?? []).concat(inAnimations);
  20849. }
  20850. /**
  20851. * Updates the effect
  20852. *
  20853. * @param inUpdates
  20854. * @returns {Promise}
  20855. * @private
  20856. */
  20857. _update(inUpdates) {
  20858. this.data = inUpdates;
  20859. Hooks.callAll("updateSequencerEffect", this);
  20860. this._destroyDependencies();
  20861. return this._reinitialize();
  20862. }
  20863. /**
  20864. * Determines whether a position is within the bounds of this effect
  20865. *
  20866. * @param inPosition
  20867. * @returns {boolean}
  20868. */
  20869. isPositionWithinBounds(inPosition) {
  20870. if (!this.spriteContainer)
  20871. return false;
  20872. return is_position_within_bounds(
  20873. inPosition,
  20874. this.spriteContainer,
  20875. this.parent
  20876. );
  20877. }
  20878. /**
  20879. * Initializes the effect and places it on the canvas
  20880. *
  20881. * @param {boolean} play
  20882. * @returns {Promise}
  20883. * @private
  20884. */
  20885. async _initialize(play = true) {
  20886. this.ready = false;
  20887. this._initializeVariables();
  20888. await this._contextLostCallback();
  20889. await this._loadTexture();
  20890. this._addToContainer();
  20891. this._createSprite();
  20892. this._calculateDuration();
  20893. this._createShapes();
  20894. await this._setupMasks();
  20895. await this._transformSprite();
  20896. this._playCustomAnimations();
  20897. this._playPresetAnimations();
  20898. this._setEndTimeout();
  20899. this._timeoutVisibility();
  20900. if (play)
  20901. await this.playMedia();
  20902. this.ready = true;
  20903. }
  20904. /**
  20905. * Reinitializes the effect after it has been updated
  20906. *
  20907. * @param play
  20908. * @returns {Promise}
  20909. * @private
  20910. */
  20911. async _reinitialize(play = true) {
  20912. this.renderable = false;
  20913. if (!this.shouldPlay) {
  20914. return Sequencer.EffectManager._removeEffect(this);
  20915. }
  20916. this.actualCreationTime = +new Date();
  20917. return this._initialize(play);
  20918. }
  20919. /**
  20920. * Initializes variables core to the function of the effect
  20921. * This is run as a part of the construction of the effect
  20922. *
  20923. * @private
  20924. */
  20925. _initializeVariables() {
  20926. this.rotationContainer = this.addChild(new PIXI.Container());
  20927. this.rotationContainer.id = this.id + "-rotationContainer";
  20928. this.isometricContainer = this.rotationContainer.addChild(
  20929. new PIXI.Container()
  20930. );
  20931. this.isometricContainer.id = this.id + "-isometricContainer";
  20932. this.spriteContainer = this.isometricContainer.addChild(
  20933. new PIXI.Container()
  20934. );
  20935. this.spriteContainer.id = this.id + "-spriteContainer";
  20936. this._template = this.data.template;
  20937. this._ended = null;
  20938. this._maskContainer = null;
  20939. this._maskSprite = null;
  20940. this._file = null;
  20941. this._loopOffset = 0;
  20942. this.effectFilters = {};
  20943. this._animationDuration = 0;
  20944. this._animationTimes = {};
  20945. this._twister = new MersenneTwister(this.data.creationTimestamp);
  20946. this._video = null;
  20947. this._distanceCache = null;
  20948. this._isRangeFind = false;
  20949. this._customAngle = 0;
  20950. this._currentFilePath = this.data.file;
  20951. this._relatedSprites = {};
  20952. this._hooks = [];
  20953. if (this._resetTimeout) {
  20954. clearTimeout(this._resetTimeout);
  20955. }
  20956. this._resetTimeout = null;
  20957. this._source = false;
  20958. this._target = false;
  20959. this._offsetCache = {
  20960. source: { nameCache: {} },
  20961. target: { nameCache: {} }
  20962. };
  20963. this._nameOffsetMap = Object.fromEntries(
  20964. Object.entries(
  20965. foundry.utils.deepClone(this.data.nameOffsetMap ?? {})
  20966. ).map((entry) => {
  20967. return [entry[0], this._setupOffsetMap(entry[1])];
  20968. })
  20969. );
  20970. this.uuid = !is_UUID(this.context.uuid) ? this.id : this.context.uuid + ".data.flags.sequencer.effects." + this.id;
  20971. const maxPerformance = game.settings.get("core", "performanceMode") === 3;
  20972. const maxFPS = game.settings.get("core", "maxFPS");
  20973. this._ticker = new PIXI.Ticker();
  20974. this._ticker.maxFPS = maxPerformance && maxFPS === 60 ? 60 : maxFPS;
  20975. this._ticker.start();
  20976. }
  20977. /**
  20978. * Destroys all dependencies to this element, such as tickers, animations, textures, and child elements
  20979. *
  20980. * @private
  20981. */
  20982. _destroyDependencies() {
  20983. if (this._ended)
  20984. return;
  20985. this._ended = true;
  20986. this.mask = null;
  20987. hooksManager.removeHooks(this.uuid);
  20988. try {
  20989. this._ticker.stop();
  20990. this._ticker.destroy();
  20991. } catch (err) {
  20992. }
  20993. this._ticker = null;
  20994. Object.values(this._relatedSprites).forEach(
  20995. (sprite) => sprite.destroy({ children: true })
  20996. );
  20997. SequencerAnimationEngine.endAnimations(this.id);
  20998. if (this._maskContainer)
  20999. this._maskContainer.destroy({ children: true });
  21000. if (this._maskSprite) {
  21001. try {
  21002. this._maskSprite.texture.destroy(true);
  21003. this._maskSprite.destroy();
  21004. } catch (err) {
  21005. }
  21006. }
  21007. if (this._file instanceof SequencerFileBase) {
  21008. this._file.destroy();
  21009. }
  21010. if (this.video) {
  21011. try {
  21012. this.video.removeAttribute("src");
  21013. this.video.pause();
  21014. this.video.load();
  21015. } catch (err) {
  21016. }
  21017. }
  21018. try {
  21019. if (this.data.screenSpace) {
  21020. SequencerAboveUILayer.removeContainerByEffect(this);
  21021. }
  21022. } catch (err) {
  21023. }
  21024. this.removeChildren().forEach((child) => child.destroy({ children: true }));
  21025. }
  21026. /**
  21027. * Plays preset animations
  21028. *
  21029. * @private
  21030. */
  21031. _playPresetAnimations() {
  21032. this._moveTowards();
  21033. this._fadeIn();
  21034. this._fadeInAudio();
  21035. this._scaleIn();
  21036. this._rotateIn();
  21037. this._fadeOut();
  21038. this._fadeOutAudio();
  21039. this._scaleOut();
  21040. this._rotateOut();
  21041. }
  21042. /**
  21043. * Gets an object based on an identifier, checking if it exists within the named offset map, whether it's a
  21044. * coordinate object, or if it's an UUID that needs to be fetched from the scene
  21045. *
  21046. * @param inIdentifier
  21047. * @returns {*}
  21048. * @private
  21049. */
  21050. _getObjectByID(inIdentifier) {
  21051. let source = inIdentifier;
  21052. let offsetMap = this._nameOffsetMap?.[inIdentifier];
  21053. if (offsetMap) {
  21054. source = offsetMap?.targetObj || offsetMap?.sourceObj || source;
  21055. } else {
  21056. source = this._validateObject(source);
  21057. }
  21058. return source;
  21059. }
  21060. /**
  21061. * Validates the given parameter, whether it's a UUID or a coordinate object, and returns the proper one
  21062. *
  21063. * @param inObject
  21064. * @returns {*}
  21065. * @private
  21066. */
  21067. _validateObject(inObject) {
  21068. if (is_UUID(inObject) || !is_object_canvas_data(inObject)) {
  21069. inObject = get_object_from_scene(inObject, this.data.sceneId);
  21070. inObject = inObject?._object ?? inObject;
  21071. }
  21072. return inObject;
  21073. }
  21074. /**
  21075. * Adds this effect to the appropriate container on the right layer
  21076. *
  21077. * @private
  21078. */
  21079. _addToContainer() {
  21080. let layer2;
  21081. if (this.data.screenSpaceAboveUI) {
  21082. layer2 = SequencerAboveUILayer;
  21083. } else if (this.data.screenSpace) {
  21084. layer2 = canvas.sequencerEffectsUILayer;
  21085. } else if (this.data.aboveLighting) {
  21086. layer2 = canvas.interface;
  21087. } else {
  21088. layer2 = canvas.primary;
  21089. }
  21090. layer2.addChild(this);
  21091. layer2.sortChildren();
  21092. }
  21093. /**
  21094. * Loads the texture for this effect, handling cases where it's a simple path or a database path
  21095. *
  21096. * @private
  21097. */
  21098. async _loadTexture() {
  21099. if (this.data.file === "") {
  21100. return;
  21101. }
  21102. if (this.data.customRange) {
  21103. this._file = SequencerFileBase.make(
  21104. this.data.file,
  21105. Object.values(this.template),
  21106. "temporary.range.file"
  21107. );
  21108. } else {
  21109. if (!Sequencer.Database.entryExists(this.data.file)) {
  21110. let texture = await SequencerFileCache.loadFile(this.data.file);
  21111. this.video = this.data.file.toLowerCase().endsWith(".webm") ? texture?.baseTexture?.resource?.source ?? false : false;
  21112. this._texture = texture;
  21113. this._file = texture;
  21114. this._currentFilePath = this.data.file;
  21115. return;
  21116. }
  21117. this._file = Sequencer.Database.getEntry(this.data.file).clone();
  21118. }
  21119. this._file.fileIndex = this.data.forcedIndex;
  21120. this._file.twister = this._twister;
  21121. this._isRangeFind = this._file?.rangeFind;
  21122. this.spriteSheet = false;
  21123. if (this.data.stretchTo) {
  21124. let ray = new Ray(this.sourcePosition, this.targetPosition);
  21125. this._rotateTowards(ray);
  21126. ray = new Ray(this.sourcePosition, this.targetPosition);
  21127. let { filePath, texture, sheet } = await this._getTextureForDistance(
  21128. ray.distance
  21129. );
  21130. this._currentFilePath = filePath;
  21131. this._texture = texture;
  21132. this.spriteSheet = sheet;
  21133. } else if (!this._isRangeFind || this._isRangeFind && !this.data.stretchTo) {
  21134. const { filePath, texture, sheet } = await this._file.getTexture();
  21135. this._currentFilePath = filePath;
  21136. this._texture = texture;
  21137. this.spriteSheet = sheet;
  21138. }
  21139. if (this._isRangeFind && (this.data.stretchTo?.attachTo?.active || this.data.attachTo?.active)) {
  21140. let sprite;
  21141. let spriteType = this.data.tilingTexture ? PIXI.TilingSprite : SpriteMesh;
  21142. if (this.data.xray) {
  21143. sprite = new spriteType(this._texture);
  21144. } else {
  21145. sprite = new spriteType(this._texture, VisionSamplerShader);
  21146. }
  21147. this._relatedSprites[this._currentFilePath] = sprite;
  21148. new Promise(async (resolve) => {
  21149. for (let filePath of this._file.getAllFiles()) {
  21150. if (filePath === this._currentFilePath)
  21151. continue;
  21152. let texture = await this._file._getTexture(filePath);
  21153. let sprite2;
  21154. let spriteType2 = this.data.tilingTexture ? PIXI.TilingSprite : SpriteMesh;
  21155. if (this.data.xray) {
  21156. sprite2 = new spriteType2(texture);
  21157. } else {
  21158. sprite2 = new spriteType2(texture, VisionSamplerShader);
  21159. }
  21160. sprite2.renderable = false;
  21161. this._relatedSprites[filePath] = sprite2;
  21162. }
  21163. resolve();
  21164. });
  21165. }
  21166. this._template = this._file.template ?? this._template;
  21167. this.video = this._currentFilePath.toLowerCase().endsWith(".webm") ? this._texture?.baseTexture?.resource?.source : false;
  21168. }
  21169. /**
  21170. * Calculates the duration of this effect, based on animation durations, the video source duration, end/start times, etc
  21171. *
  21172. * @private
  21173. */
  21174. _calculateDuration() {
  21175. this._animationDuration = this.data.duration || this.mediaDuration * 1e3;
  21176. if (this.data.moveSpeed && this.data.moves) {
  21177. let distance = distance_between(
  21178. this.sourcePosition,
  21179. this.targetPosition
  21180. );
  21181. let durationFromSpeed = distance / this.data.moveSpeed * 1e3;
  21182. this._animationDuration = Math.max(durationFromSpeed, this.data.duration);
  21183. } else if (!this.data.duration && !this.hasAnimatedMedia) {
  21184. let fadeDuration = (this.data.fadeIn?.duration ?? 0) + (this.data.fadeOut?.duration ?? 0);
  21185. let scaleDuration = (this.data.scaleIn?.duration ?? 0) + (this.data.scaleOut?.duration ?? 0);
  21186. let rotateDuration = (this.data.rotateIn?.duration ?? 0) + (this.data.rotateOut?.duration ?? 0);
  21187. let moveDuration = 0;
  21188. if (this.data.moves) {
  21189. let distance = distance_between(
  21190. this.sourcePosition,
  21191. this.targetPosition
  21192. );
  21193. moveDuration = (this.data.moveSpeed ? distance / this.data.moveSpeed * 1e3 : 1e3) + this.data.moves.delay;
  21194. }
  21195. let animationDurations = this.data.animations ? Math.max(
  21196. ...this.data.animations.map((animation2) => {
  21197. if (animation2.looping) {
  21198. if (animation2.loops === 0)
  21199. return 0;
  21200. return (animation2?.duration ?? 0) * (animation2?.loops ?? 0) + (animation2?.delay ?? 0);
  21201. } else {
  21202. return (animation2?.duration ?? 0) + (animation2?.delay ?? 0);
  21203. }
  21204. })
  21205. ) : 0;
  21206. this._animationDuration = Math.max(
  21207. fadeDuration,
  21208. scaleDuration,
  21209. rotateDuration,
  21210. moveDuration,
  21211. animationDurations
  21212. );
  21213. this._animationDuration = this._animationDuration || 1e3;
  21214. }
  21215. this._startTime = 0;
  21216. if (this.data.time?.start && this.mediaCurrentTime !== null) {
  21217. let currentTime = !this.data.time.start.isPerc ? this.data.time.start.value ?? 0 : this._animationDuration * this.data.time.start.value;
  21218. this.mediaCurrentTime = currentTime / 1e3;
  21219. this._startTime = this.mediaCurrentTime;
  21220. }
  21221. if (this.data.time?.end) {
  21222. this._animationDuration = !this.data.time.end.isPerc ? this.data.time.isRange ? this.data.time.end.value - this.data.time.start.value : this._animationDuration - this.data.time.end.value : this._animationDuration * this.data.time.end.value;
  21223. }
  21224. this._endTime = this._animationDuration / 1e3;
  21225. if (this._file?.markers && this._startTime === 0 && this._endTime === this.mediaDuration) {
  21226. this._animationTimes.loopStart = this._file.markers.loop.start / 1e3;
  21227. this._animationTimes.loopEnd = this._file.markers.loop.end / 1e3;
  21228. this._animationTimes.forcedEnd = this._file.markers.forcedEnd / 1e3;
  21229. }
  21230. this._animationDuration /= this.data.playbackRate ?? 1;
  21231. this._durationResolve(this._animationDuration);
  21232. this.mediaLooping = this._animationDuration / 1e3 > this.mediaDuration && !this.data.noLoop;
  21233. this.mediaPlaybackRate = this.data.playbackRate ? this.data.playbackRate : 1;
  21234. }
  21235. /**
  21236. * If this effect is animatable, hold off on rendering it for a bit so that the animations have time to initialize to
  21237. * prevent it from spawning and then jumping to the right place
  21238. *
  21239. * @private
  21240. */
  21241. _timeoutVisibility() {
  21242. setTimeout(
  21243. () => {
  21244. this._setupHooks();
  21245. },
  21246. this.data.animations ? 50 : 0
  21247. );
  21248. }
  21249. /**
  21250. * If this effect is attached to an object, check whether the object has been destroyed, if so, end the effect
  21251. *
  21252. * @private
  21253. */
  21254. _contextLostCallback() {
  21255. if (this.isSourceTemporary) {
  21256. this._ticker.add(() => {
  21257. if (this.isSourceDestroyed) {
  21258. this._ticker.stop();
  21259. this._source = this.sourcePosition;
  21260. SequencerEffectManager.endEffects({ effects: this });
  21261. }
  21262. });
  21263. }
  21264. if (this.isTargetTemporary) {
  21265. this._ticker.add(() => {
  21266. if (this.isTargetDestroyed) {
  21267. this._ticker.stop();
  21268. this._target = this.targetPosition;
  21269. SequencerEffectManager.endEffects({ effects: this });
  21270. }
  21271. });
  21272. }
  21273. }
  21274. /**
  21275. * Creates the sprite, and the relevant containers that manage the position and offsets of the overall visual look of the sprite
  21276. *
  21277. * @private
  21278. */
  21279. _createSprite() {
  21280. this.renderable = false;
  21281. const args = [this.spriteSheet ? this.spriteSheet : null];
  21282. if (!this.data.xray && !this.spriteSheet && !this.data.screenSpace && !this.data.screenSpaceAboveUI) {
  21283. args.push(VisionSamplerShader);
  21284. }
  21285. const spriteType = this.spriteSheet ? PIXI.AnimatedSprite : SpriteMesh;
  21286. const sprite = new spriteType(...args);
  21287. this.sprite = this.spriteContainer.addChild(sprite);
  21288. this.sprite.id = this.id + "-sprite";
  21289. Object.values(this._relatedSprites).forEach((sprite2) => {
  21290. this.sprite.addChild(sprite2);
  21291. });
  21292. this.animatedSprite = false;
  21293. if (this.spriteSheet) {
  21294. this.animatedSprite = true;
  21295. this.sprite.animationSpeed = 0.4;
  21296. this.sprite.loop = false;
  21297. }
  21298. let textSprite;
  21299. if (this.data.text) {
  21300. const text2 = this.data.text.text;
  21301. const fontSettings = foundry.utils.deepClone(this.data.text);
  21302. fontSettings.fontSize = (fontSettings?.fontSize ?? 26) * (150 / canvas.grid.size);
  21303. textSprite = new PIXI.Text(text2, fontSettings);
  21304. textSprite.resolution = 5;
  21305. textSprite.zIndex = 1;
  21306. textSprite.anchor.set(
  21307. this.data.text?.anchor?.x ?? 0.5,
  21308. this.data.text?.anchor?.y ?? 0.5
  21309. );
  21310. }
  21311. this.sprite.filters = [];
  21312. if (this.data.filters) {
  21313. for (let index = 0; index < this.data.filters.length; index++) {
  21314. const filterData = this.data.filters[index];
  21315. const filter2 = new filters[filterData.className](filterData.data);
  21316. filter2.id = this.id + "-" + filterData.className + "-" + index.toString();
  21317. this.sprite.filters.push(filter2);
  21318. const filterKeyName = filterData.name || filterData.className;
  21319. this.effectFilters[filterKeyName] = filter2;
  21320. }
  21321. }
  21322. this.alphaFilter = new PIXI.filters.AlphaFilter(this.data.opacity);
  21323. this.alphaFilter.id = this.id + "-alphaFilter";
  21324. this.sprite.filters.push(this.alphaFilter);
  21325. let spriteOffsetX = this.data.spriteOffset?.x ?? 0;
  21326. let spriteOffsetY = this.data.spriteOffset?.y ?? 0;
  21327. if (this.data.spriteOffset?.gridUnits) {
  21328. spriteOffsetX *= canvas.grid.size;
  21329. spriteOffsetY *= canvas.grid.size;
  21330. }
  21331. this.sprite.position.set(spriteOffsetX, spriteOffsetY);
  21332. this.sprite.anchor.set(
  21333. this.data.spriteAnchor?.x ?? 0.5,
  21334. this.data.spriteAnchor?.y ?? 0.5
  21335. );
  21336. this.sprite.rotation = Math.normalizeRadians(
  21337. Math.toRadians(this.data.spriteRotation ?? 0)
  21338. );
  21339. this._customAngle = this.data.angle ?? 0;
  21340. if (this.data.randomRotation) {
  21341. this._customAngle += random_float_between(-360, 360, this._twister);
  21342. }
  21343. this.spriteContainer.rotation = -Math.normalizeRadians(
  21344. Math.toRadians(this._customAngle)
  21345. );
  21346. if (CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE) {
  21347. this.isometricContainer.rotation = Math.PI / 4;
  21348. }
  21349. if (this.data.tint) {
  21350. this.sprite.tint = this.data.tint;
  21351. }
  21352. if (textSprite) {
  21353. if (this.data.tint) {
  21354. textSprite.tint = this.data.tint;
  21355. }
  21356. this.sprite.addChild(textSprite);
  21357. }
  21358. if (this.shouldShowFadedVersion) {
  21359. this.filters = [
  21360. new PIXI.filters.ColorMatrixFilter({ saturation: -1 }),
  21361. new PIXI.filters.AlphaFilter(
  21362. game.settings.get(CONSTANTS.MODULE_NAME, "user-effect-opacity") / 100
  21363. )
  21364. ];
  21365. }
  21366. this.updateElevation();
  21367. }
  21368. _createShapes() {
  21369. const nonMaskShapes = (this.data?.shapes ?? []).filter(
  21370. (shape) => !shape.isMask
  21371. );
  21372. this.shapes = {};
  21373. for (const shape of nonMaskShapes) {
  21374. const graphic = createShape(shape);
  21375. graphic.filters = this.sprite.filters;
  21376. this.spriteContainer.addChild(graphic);
  21377. this.shapes[shape?.name ?? "shape-" + randomID()] = graphic;
  21378. }
  21379. }
  21380. updateElevation() {
  21381. const targetElevation = Math.max(
  21382. get_object_elevation(this.source ?? {}),
  21383. get_object_elevation(this.target ?? {})
  21384. ) + 1;
  21385. let effectElevation = this.data.elevation?.elevation ?? 0;
  21386. if (!this.data.elevation?.absolute) {
  21387. effectElevation += targetElevation;
  21388. }
  21389. const isIsometric = getProperty(
  21390. game.scenes.get(this.data.sceneId),
  21391. CONSTANTS.INTEGRATIONS.ISOMETRIC.SCENE_ENABLED
  21392. );
  21393. if (CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE && isIsometric) {
  21394. const sourceSort = this.source ? this.sourceMesh.sort + (this.data.isometric?.overlay ? 1 : -1) : 0;
  21395. const targetSort = this.target ? this.targetMesh.sort + (this.data.isometric?.overlay ? 1 : -1) : 0;
  21396. this.sort = Math.max(sourceSort, targetSort);
  21397. } else {
  21398. this.sort = !is_real_number(this.data.zIndex) ? this.data.index + this.parent.children.length : 1e5 + this.data.zIndex;
  21399. }
  21400. this.elevation = effectElevation;
  21401. this.parent.sortChildren();
  21402. }
  21403. updateTransform() {
  21404. super.updateTransform();
  21405. if (this.data.screenSpace || this.data.screenSpaceAboveUI) {
  21406. const [screenWidth, screenHeight] = canvas.screenDimensions;
  21407. this.position.set(
  21408. (this.data.screenSpacePosition?.x ?? 0) + screenWidth * (this.data.screenSpaceAnchor?.x ?? this.data.anchor?.x ?? 0.5),
  21409. (this.data.screenSpacePosition?.y ?? 0) + screenHeight * (this.data.screenSpaceAnchor?.y ?? this.data.anchor?.y ?? 0.5)
  21410. );
  21411. if (this.data.screenSpaceScale) {
  21412. const scaleData = this.data.screenSpaceScale ?? { x: 1, y: 1 };
  21413. let scaleX = scaleData.x;
  21414. let scaleY = scaleData.y;
  21415. if (scaleData.fitX) {
  21416. scaleX = scaleX * (screenWidth / this.sprite.width);
  21417. }
  21418. if (scaleData.fitY) {
  21419. scaleY = scaleY * (screenHeight / this.sprite.height);
  21420. }
  21421. scaleX = scaleData.ratioX ? scaleY : scaleX;
  21422. scaleY = scaleData.ratioY ? scaleX : scaleY;
  21423. this.scale.set(scaleX, scaleY);
  21424. }
  21425. }
  21426. }
  21427. async _setupMasks() {
  21428. const maskShapes = this.data.shapes.filter((shape) => shape.isMask);
  21429. if (!this.data?.masks?.length && !maskShapes.length)
  21430. return;
  21431. this.masksReady = false;
  21432. this._maskContainer = new PIXI.Container();
  21433. this._maskSprite = new PIXI.Sprite();
  21434. this.parent.addChild(this._maskSprite);
  21435. if (this.sprite) {
  21436. this.sprite.mask = this._maskSprite;
  21437. }
  21438. const blurFilter = new filters.Blur({ strength: 2 });
  21439. this._maskContainer.filters = [blurFilter];
  21440. for (const uuid of this.data.masks) {
  21441. const documentType = uuid.split(".")[2];
  21442. const documentObj = await fromUuid(uuid);
  21443. if (!documentObj || documentObj.parent !== this.sourceDocument.parent)
  21444. continue;
  21445. const placeableObject = documentObj.object;
  21446. const objMaskSprite = documentType === "Token" || documentType === "Tile" ? new PIXI.Sprite() : new PIXI.Graphics();
  21447. objMaskSprite.uuid = uuid;
  21448. objMaskSprite.placeableObject = placeableObject;
  21449. objMaskSprite.documentType = documentType;
  21450. const clipFilter = new filters.Clip();
  21451. const blurFilter2 = new filters.Blur({ strength: 1 });
  21452. objMaskSprite.filters = [blurFilter2, clipFilter];
  21453. const spriteContainer = new PIXI.Container();
  21454. spriteContainer.addChild(objMaskSprite);
  21455. spriteContainer.maskSprite = objMaskSprite;
  21456. spriteContainer.exclude = false;
  21457. this._maskContainer.addChild(spriteContainer);
  21458. hooksManager.addHook(this.uuid, "delete" + documentType, (doc) => {
  21459. if (doc !== documentObj)
  21460. return;
  21461. const mask = this._maskContainer.children.find(
  21462. (mask2) => mask2.maskSprite.uuid === doc.uuid
  21463. );
  21464. if (!mask)
  21465. return;
  21466. mask.destroy();
  21467. this._updateMaskSprite();
  21468. if (!this._maskContainer.children.length) {
  21469. if (this.sprite) {
  21470. this.sprite.mask = null;
  21471. }
  21472. }
  21473. });
  21474. hooksManager.addHook(this.uuid, "update" + documentType, (doc) => {
  21475. if (doc !== documentObj)
  21476. return;
  21477. const mask = this._maskContainer.children.find(
  21478. (mask2) => mask2.maskSprite.uuid === doc.uuid
  21479. );
  21480. if (!mask)
  21481. return;
  21482. const changed = this._handleUpdatingMask(mask);
  21483. if (changed)
  21484. this._updateMaskSprite();
  21485. });
  21486. }
  21487. let anyMaskChanged = false;
  21488. for (const maskShape of maskShapes) {
  21489. const graphic = createShape(maskShape);
  21490. const clipFilter = new filters.Clip();
  21491. const blurFilter2 = new filters.Blur({ strength: 1 });
  21492. graphic.filters = [blurFilter2, clipFilter];
  21493. const spriteContainer = new PIXI.Container();
  21494. spriteContainer.addChild(graphic);
  21495. spriteContainer.maskSprite = graphic;
  21496. spriteContainer.exclude = true;
  21497. this._maskContainer.addChild(spriteContainer);
  21498. anyMaskChanged = true;
  21499. }
  21500. if (!this._maskContainer.children.length) {
  21501. if (this.sprite) {
  21502. this.sprite.mask = null;
  21503. }
  21504. return false;
  21505. }
  21506. this._ticker.add(() => {
  21507. for (let container of this._maskContainer.children) {
  21508. if (!container.exclude) {
  21509. anyMaskChanged = anyMaskChanged || this._handleUpdatingMask(container);
  21510. }
  21511. }
  21512. if (anyMaskChanged) {
  21513. this._updateMaskSprite();
  21514. }
  21515. });
  21516. this.masksReady = true;
  21517. }
  21518. _handleUpdatingMask(container) {
  21519. const mask = container.maskSprite;
  21520. let objectSprite;
  21521. let objectWidth = 0;
  21522. let objectHeight = 0;
  21523. let additionalData = {
  21524. walledmask: getProperty(
  21525. mask.placeableObject,
  21526. "flags.walledtemplates.enabled"
  21527. )
  21528. };
  21529. try {
  21530. if (mask.documentType === "Token") {
  21531. objectSprite = mask.placeableObject.mesh;
  21532. objectWidth = objectSprite.width / 2;
  21533. objectHeight = objectSprite.height / 2;
  21534. additionalData["img"] = mask.placeableObject.document.texture.src;
  21535. additionalData["elevation"] = mask.placeableObject.document.elevation;
  21536. } else if (mask.documentType === "Tile") {
  21537. objectSprite = mask.placeableObject.mesh;
  21538. objectWidth = objectSprite.width / 2;
  21539. objectHeight = objectSprite.height / 2;
  21540. additionalData["img"] = mask.placeableObject.document.texture.src;
  21541. } else if (mask.documentType === "Drawing") {
  21542. objectSprite = mask.placeableObject.shape;
  21543. } else if (mask.documentType === "MeasuredTemplate") {
  21544. objectSprite = mask.placeableObject.template;
  21545. additionalData["direction"] = mask.placeableObject.direction;
  21546. additionalData["distance"] = mask.placeableObject.distance;
  21547. additionalData["angle"] = mask.placeableObject.angle;
  21548. additionalData["width"] = mask.placeableObject.width;
  21549. }
  21550. } catch (err) {
  21551. return false;
  21552. }
  21553. let position = {
  21554. x: objectSprite.parent.x + objectSprite.x - objectWidth,
  21555. y: objectSprite.parent.y + objectSprite.y - objectHeight
  21556. };
  21557. const angle = objectSprite.angle;
  21558. const data = duplicate(additionalData);
  21559. const noChange = container.position.x === position.x && container.position.y === position.y && mask.scale.x === objectSprite.scale.x && mask.scale.y === objectSprite.scale.y && mask.texture === objectSprite.texture && mask.angle === angle && foundry.utils.isEmpty(foundry.utils.diffObject(mask.oldData, data));
  21560. if (noChange)
  21561. return false;
  21562. if (mask.documentType === "Drawing") {
  21563. mask.clear();
  21564. mask.beginFill(16777215, 1);
  21565. const drawingType = mask.placeableObject.type;
  21566. if (drawingType === CONST.DRAWING_TYPES.RECTANGLE) {
  21567. mask.drawRect(
  21568. objectSprite.width / -2,
  21569. objectSprite.height / -2,
  21570. objectSprite.width,
  21571. objectSprite.height
  21572. );
  21573. } else if (drawingType === CONST.DRAWING_TYPES.ELLIPSE) {
  21574. mask.drawEllipse(
  21575. objectSprite.width / -2,
  21576. objectSprite.height / -2,
  21577. objectSprite.width / 2,
  21578. objectSprite.height / 2
  21579. );
  21580. } else {
  21581. const vertices = mask.placeableObject.points;
  21582. if (drawingType === CONST.DRAWING_TYPES.FREEHAND) {
  21583. let factor = mask.placeableObject.bezierFactor ?? 0.5;
  21584. let last = vertices[vertices.length - 1];
  21585. let isClosed = vertices[0].equals(last);
  21586. mask.moveTo(...vertices[0]);
  21587. if (vertices.length < 2)
  21588. return;
  21589. else if (vertices.length === 2) {
  21590. mask.lineTo(...vertices[1]);
  21591. return;
  21592. }
  21593. let [previous, point] = vertices.slice(0, 2);
  21594. let cp0 = getBezierControlPoints(
  21595. factor,
  21596. last,
  21597. previous,
  21598. point
  21599. ).next_cp0;
  21600. let cp1, next_cp0, next;
  21601. for (let i = 1; i < vertices.length; i++) {
  21602. next = vertices[i + 1];
  21603. if (next) {
  21604. let bp = getBezierControlPoints(
  21605. factor,
  21606. previous,
  21607. point,
  21608. next
  21609. );
  21610. cp1 = bp.cp1;
  21611. next_cp0 = bp.next_cp0;
  21612. }
  21613. if (i === 1 && !isClosed) {
  21614. mask.quadraticCurveTo(cp1.x, cp1.y, point[0], point[1]);
  21615. } else if (i === vertices.length - 1 && !isClosed) {
  21616. mask.quadraticCurveTo(cp0.x, cp0.y, point[0], point[1]);
  21617. } else {
  21618. mask.bezierCurveTo(
  21619. cp0.x,
  21620. cp0.y,
  21621. cp1.x,
  21622. cp1.y,
  21623. point[0],
  21624. point[1]
  21625. );
  21626. }
  21627. previous = point;
  21628. point = next;
  21629. cp0 = next_cp0;
  21630. }
  21631. } else {
  21632. mask.moveTo(...vertices[0]);
  21633. for (let i = 1; i < vertices.length; i++) {
  21634. mask.lineTo(...vertices[i]);
  21635. }
  21636. }
  21637. }
  21638. mask.endFill();
  21639. } else if (mask.documentType === "MeasuredTemplate") {
  21640. mask.clear();
  21641. mask.beginFill(16777215, 1);
  21642. const shape = mask.placeableObject.shape.clone();
  21643. mask.drawShape(shape);
  21644. }
  21645. mask.texture = objectSprite.texture;
  21646. container.position.set(position.x, position.y);
  21647. mask.scale.set(objectSprite.scale.x, objectSprite.scale.y);
  21648. mask.angle = angle;
  21649. mask.oldData = additionalData;
  21650. if (mask instanceof PIXI.Sprite) {
  21651. mask.anchor.set(0.5, 0.5);
  21652. mask.position.set(mask.width / 2, mask.height / 2);
  21653. if (CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE) {
  21654. const elevation = game.settings.get(
  21655. CONSTANTS.INTEGRATIONS.ISOMETRIC.MODULE_NAME,
  21656. "token_elevation"
  21657. ) && mask.placeableObject.document?.elevation ? canvas.scene.grid.distance / mask.placeableObject.document.elevation * mask.placeableObject.document.height : Infinity;
  21658. const isometricType = getProperty(
  21659. mask.placeableObject.document,
  21660. CONSTANTS.INTEGRATIONS.ISOMETRIC.PROJECTION_FLAG
  21661. );
  21662. switch (isometricType) {
  21663. case CONSTANTS.INTEGRATIONS.ISOMETRIC.PROJECTION_TYPES.TOPDOWN:
  21664. break;
  21665. case CONSTANTS.INTEGRATIONS.ISOMETRIC.PROJECTION_TYPES.DIAMETRIC:
  21666. mask.anchor.set(0.5, 0.75 + 0.505 / elevation);
  21667. break;
  21668. default:
  21669. mask.anchor.set(0.5, 0.69 + 0.63 / elevation);
  21670. break;
  21671. }
  21672. }
  21673. }
  21674. return true;
  21675. }
  21676. _updateMaskSprite() {
  21677. if (!this.masksReady)
  21678. return;
  21679. const smallestX = this._maskContainer.children.reduce((acc, container) => {
  21680. let x;
  21681. const bounds = container.getBounds();
  21682. if (container.exclude) {
  21683. x = this.x - bounds.width / 2 + container.maskSprite.offset.x;
  21684. } else {
  21685. x = bounds.x;
  21686. }
  21687. return acc >= x ? x : acc;
  21688. }, Infinity);
  21689. const smallestY = this._maskContainer.children.reduce((acc, container) => {
  21690. let y;
  21691. const bounds = container.getBounds(true);
  21692. if (container.exclude) {
  21693. y = this.y - bounds.height / 2 + container.maskSprite.offset.y;
  21694. } else {
  21695. y = bounds.y;
  21696. }
  21697. return acc >= y ? y : acc;
  21698. }, Infinity);
  21699. this._maskSprite.position.set(smallestX, smallestY);
  21700. this._maskSprite.texture.destroy(true);
  21701. this._maskSprite.texture = canvas.app.renderer.generateTexture(
  21702. this._maskContainer
  21703. );
  21704. }
  21705. /**
  21706. * Sets up the hooks relating to this effect's source and target
  21707. *
  21708. * @private
  21709. */
  21710. _setupHooks() {
  21711. const attachedToSource = this.data.attachTo?.active && is_UUID(this.data.source);
  21712. const attachedToTarget = (this.data.stretchTo?.attachTo || this.data.rotateTowards?.attachTo) && is_UUID(this.data.target);
  21713. const baseRenderable = this.shouldPlayVisible;
  21714. let renderable = baseRenderable;
  21715. let alpha = null;
  21716. if (attachedToSource) {
  21717. hooksManager.addHook(this.uuid, this.getSourceHook("delete"), (doc) => {
  21718. const uuid = doc.uuid;
  21719. if (doc !== this.sourceDocument)
  21720. return;
  21721. this._source = this._cachedSourceData.position;
  21722. SequencerEffectManager.objectDeleted(uuid);
  21723. });
  21724. if (this.data.attachTo?.bindVisibility) {
  21725. hooksManager.addHook(
  21726. this.uuid,
  21727. "sightRefresh",
  21728. () => {
  21729. const sourceVisible = this.source && (this.sourceMesh?.visible ?? true);
  21730. const sourceHidden = this.sourceDocument && (this.sourceDocument?.hidden ?? false);
  21731. const targetVisible = this.target && (!attachedToTarget || (this.targetMesh?.visible ?? true));
  21732. this.renderable = baseRenderable && (sourceVisible || targetVisible);
  21733. this.alpha = sourceVisible && sourceHidden ? 0.5 : 1;
  21734. renderable = baseRenderable && this.renderable;
  21735. },
  21736. true
  21737. );
  21738. }
  21739. if (this.data.attachTo?.bindAlpha || this.data.attachTo?.bindElevation) {
  21740. hooksManager.addHook(this.uuid, this.getSourceHook("update"), (doc) => {
  21741. if (doc !== this.sourceDocument)
  21742. return;
  21743. if (this.data.attachTo?.bindAlpha) {
  21744. this.spriteContainer.alpha = this._cachedSourceData.alpha;
  21745. }
  21746. if (this.data.attachTo?.bindElevation) {
  21747. this.updateElevation();
  21748. }
  21749. });
  21750. }
  21751. if (this.data.attachTo?.bindAlpha) {
  21752. alpha = this._cachedSourceData.alpha;
  21753. }
  21754. }
  21755. if (attachedToTarget) {
  21756. hooksManager.addHook(this.uuid, this.getTargetHook("delete"), (doc) => {
  21757. if (doc !== this.target)
  21758. return;
  21759. this._target = this._cachedTargetData.position;
  21760. const uuid = doc.uuid;
  21761. SequencerEffectManager.objectDeleted(uuid);
  21762. });
  21763. hooksManager.addHook(this.uuid, this.getTargetHook("update"), (doc) => {
  21764. if (doc !== this.target)
  21765. return;
  21766. this.updateElevation();
  21767. });
  21768. }
  21769. for (let uuid of this.data?.tiedDocuments ?? []) {
  21770. const tiedDocument = from_uuid_fast(uuid);
  21771. if (tiedDocument) {
  21772. hooksManager.addHook(
  21773. this.uuid,
  21774. this.getHook("delete", tiedDocument.uuid),
  21775. (doc) => {
  21776. if (tiedDocument !== doc)
  21777. return;
  21778. SequencerEffectManager.objectDeleted(doc.uuid);
  21779. }
  21780. );
  21781. }
  21782. }
  21783. setTimeout(() => {
  21784. this.renderable = renderable;
  21785. this.spriteContainer.alpha = alpha ?? 1;
  21786. }, 25);
  21787. }
  21788. /**
  21789. * Calculates the padding and scale to stretch an effect across the given distance
  21790. *
  21791. * If the file is a SequencerFileBase instance, it will also pick the appropriate file for the right distance
  21792. *
  21793. * @param distance
  21794. * @returns {Object}
  21795. * @private
  21796. */
  21797. async _getTextureForDistance(distance) {
  21798. if (!this._distanceCache || this._distanceCache?.distance !== distance) {
  21799. let scaleX = 1;
  21800. let scaleY = 1;
  21801. let texture;
  21802. let filePath;
  21803. let spriteAnchor = this.data.anchor?.x ?? 1;
  21804. if (this._file instanceof SequencerFileBase) {
  21805. const scaledDistance = distance / (this.data.scale.x ?? 1);
  21806. const result = await this._file.getTexture(scaledDistance);
  21807. filePath = result.filePath;
  21808. texture = result.texture;
  21809. spriteAnchor = result.spriteAnchor ?? this.data.anchor?.x ?? 0;
  21810. scaleX = result.spriteScale;
  21811. if (this.data.stretchTo?.onlyX) {
  21812. const widthWithPadding = texture.width - (this.template.startPoint + this.template.endPoint);
  21813. scaleY = widthWithPadding / texture.width;
  21814. } else {
  21815. scaleY = result.spriteScale;
  21816. }
  21817. } else if (this._file instanceof PIXI.Texture) {
  21818. filePath = this.data.file;
  21819. texture = this._file;
  21820. spriteAnchor = this.template.startPoint / texture.width;
  21821. const widthWithPadding = texture.width - (this.template.startPoint + this.template.endPoint);
  21822. let spriteScale = distance / widthWithPadding;
  21823. scaleX = spriteScale;
  21824. if (this.data.stretchTo?.onlyX) {
  21825. scaleY = widthWithPadding / texture.width;
  21826. } else {
  21827. scaleY = spriteScale;
  21828. }
  21829. }
  21830. this._distanceCache = {
  21831. filePath,
  21832. texture,
  21833. spriteAnchor,
  21834. scaleX,
  21835. scaleY,
  21836. distance
  21837. };
  21838. }
  21839. return this._distanceCache;
  21840. }
  21841. /**
  21842. * Applies the distance scaling to the sprite based on the previous method
  21843. *
  21844. * @returns {Promise<void>}
  21845. * @private
  21846. */
  21847. async _applyDistanceScaling() {
  21848. const ray = new Ray(this.sourcePosition, this.targetPosition);
  21849. this._rotateTowards(ray);
  21850. let { filePath, texture, spriteAnchor, scaleX, scaleY, distance } = await this._getTextureForDistance(ray.distance);
  21851. if (this._currentFilePath !== filePath || !this._relatedSprites[filePath]) {
  21852. this._texture = texture;
  21853. this.video = filePath.toLowerCase().endsWith(".webm") ? texture?.baseTexture?.resource?.source ?? false : false;
  21854. Object.values(this._relatedSprites).forEach((subsprite) => {
  21855. subsprite.renderable = false;
  21856. });
  21857. this._currentFilePath = filePath;
  21858. if (this._relatedSprites[filePath]) {
  21859. this._relatedSprites[filePath].renderable = true;
  21860. } else {
  21861. let sprite;
  21862. let spriteType = this.data.tilingTexture ? PIXI.TilingSprite : SpriteMesh;
  21863. if (this.data.xray) {
  21864. sprite = new spriteType(texture);
  21865. } else {
  21866. sprite = new spriteType(texture, VisionSamplerShader);
  21867. }
  21868. this._relatedSprites[filePath] = sprite;
  21869. if (this.data.tint) {
  21870. sprite.tint = this.data.tint;
  21871. }
  21872. this.sprite.addChild(sprite);
  21873. }
  21874. }
  21875. try {
  21876. this._relatedSprites[filePath].texture?.baseTexture?.resource?.source.play().then(() => {
  21877. this._relatedSprites[filePath].texture.update();
  21878. });
  21879. } catch (err) {
  21880. }
  21881. if (this._relatedSprites[filePath]) {
  21882. if (this.data.attachTo?.active) {
  21883. this._applyAttachmentOffset();
  21884. }
  21885. const sprite = this._relatedSprites[filePath];
  21886. if (!sprite.parent) {
  21887. this.sprite.addChild(sprite);
  21888. }
  21889. if (this.data.tilingTexture) {
  21890. const scaleX2 = (this.data.scale.x ?? 1) * this.gridSizeDifference;
  21891. const scaleY2 = (this.data.scale.y ?? 1) * this.gridSizeDifference;
  21892. sprite.width = distance / scaleX2;
  21893. sprite.height = texture.height;
  21894. sprite.scale.set(scaleX2 * this.flipX, scaleY2 * this.flipY);
  21895. sprite.tileScale.x = this.data.tilingTexture.scale.x;
  21896. sprite.tileScale.y = this.data.tilingTexture.scale.y;
  21897. sprite.tilePosition = this.data.tilingTexture.position;
  21898. } else {
  21899. sprite.scale.set(
  21900. scaleX * (this.data.scale.x ?? 1) * this.flipX,
  21901. scaleY * (this.data.scale.y ?? 1) * this.flipY
  21902. );
  21903. }
  21904. sprite.anchor.set(
  21905. this.flipX === 1 ? spriteAnchor : 1 - spriteAnchor,
  21906. this.data.anchor?.y ?? 0.5
  21907. );
  21908. }
  21909. }
  21910. /**
  21911. * Rotates the effect towards the target
  21912. *
  21913. * @param ray
  21914. * @private
  21915. */
  21916. _rotateTowards(ray) {
  21917. if (!ray) {
  21918. const sourcePosition = this.flipX === 1 ? this.sourcePosition : this.targetPosition;
  21919. const targetPosition = this.flipX === 1 ? this.targetPosition : this.sourcePosition;
  21920. ray = new Ray(sourcePosition, targetPosition);
  21921. }
  21922. this.rotationContainer.rotation = Math.normalizeRadians(
  21923. ray.angle + Math.toRadians(this.data.rotateTowards?.rotationOffset ?? 0)
  21924. );
  21925. this._tweakRotationForIsometric();
  21926. }
  21927. _tweakRotationForIsometric() {
  21928. if (!CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE)
  21929. return;
  21930. if (this.data.stretchTo) {
  21931. let skew = Math.normalizeRadians(
  21932. this.rotationContainer.rotation - Math.PI / 4
  21933. );
  21934. if (Math.abs(skew) >= Math.PI / 2 - 0.5 && Math.abs(skew) <= Math.PI / 2 + 0.5) {
  21935. skew -= Math.PI / 2;
  21936. }
  21937. this.isometricContainer.skew.set(Math.normalizeRadians(skew), 0);
  21938. this.isometricContainer.rotation = 0;
  21939. } else if (this.data?.isometric?.overlay) {
  21940. this.rotationContainer.rotation = 0;
  21941. let skew = Math.PI / 4 + this.rotationContainer.rotation;
  21942. this.isometricContainer.skew.set(
  21943. Math.normalizeRadians(skew - Math.PI / 4),
  21944. 0
  21945. );
  21946. this.isometricContainer.scale.set(
  21947. 1,
  21948. window.scale ?? CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION
  21949. );
  21950. } else {
  21951. this.isometricContainer.rotation = 0;
  21952. }
  21953. }
  21954. /**
  21955. * Transforms the sprite, rotating it, stretching it, scaling it, sizing it according its data
  21956. *
  21957. * @private
  21958. */
  21959. async _transformSprite() {
  21960. if (this.data.stretchTo) {
  21961. if (this.data.stretchTo?.attachTo) {
  21962. this._transformStretchToAttachedSprite();
  21963. }
  21964. await this._applyDistanceScaling();
  21965. } else {
  21966. if (!this.sprite?.texture?.valid && this._texture?.valid) {
  21967. this.sprite.texture = this._texture;
  21968. }
  21969. }
  21970. if (this.video && (this._startTime || this._loopOffset > 0) && this.video?.currentTime !== void 0) {
  21971. await wait$1(20);
  21972. this.sprite.texture.update();
  21973. }
  21974. if (!this.data.stretchTo) {
  21975. this._transformNoStretchSprite();
  21976. }
  21977. if (this.data.attachTo?.active && !this.data.stretchTo?.attachTo) {
  21978. await this._transformAttachedNoStretchSprite();
  21979. } else {
  21980. if (!this.data.screenSpace) {
  21981. this.position.set(this.sourcePosition.x, this.sourcePosition.y);
  21982. }
  21983. }
  21984. if (this.data.rotateTowards) {
  21985. this._rotateTowards();
  21986. if (this.data.rotateTowards?.attachTo) {
  21987. this._transformRotateTowardsAttachedSprite();
  21988. }
  21989. }
  21990. this._tweakRotationForIsometric();
  21991. if (!this.data.anchor && this.data.rotateTowards) {
  21992. const startPointRatio = this.template.startPoint / this._texture.width / 2;
  21993. this.spriteContainer.pivot.set(
  21994. this.sprite.width * (-0.5 + startPointRatio),
  21995. 0
  21996. );
  21997. } else {
  21998. this.spriteContainer.pivot.set(
  21999. interpolate(
  22000. this.sprite.width * -0.5,
  22001. this.sprite.width * 0.5,
  22002. this.data.anchor?.x ?? 0.5
  22003. ),
  22004. interpolate(
  22005. this.sprite.height * -0.5,
  22006. this.sprite.height * 0.5,
  22007. this.data.anchor?.y ?? 0.5
  22008. )
  22009. );
  22010. }
  22011. }
  22012. _transformStretchToAttachedSprite() {
  22013. this._ticker.add(async () => {
  22014. try {
  22015. await this._applyDistanceScaling();
  22016. } catch (err) {
  22017. }
  22018. });
  22019. }
  22020. _transformNoStretchSprite() {
  22021. if (this.data.tilingTexture) {
  22022. this.sprite.tileScale = {
  22023. x: this.data.tilingTexture.scale.x * this.gridSizeDifference,
  22024. y: this.data.tilingTexture.scale.y * this.gridSizeDifference
  22025. };
  22026. this.sprite.tilePosition = this.data.tilingTexture.position;
  22027. }
  22028. const baseScaleX = (this.data.scale?.x ?? 1) * (this.data.spriteScale?.x ?? 1) * this.flipX;
  22029. const baseScaleY = (this.data.scale?.y ?? 1) * (this.data.spriteScale?.y ?? 1) * this.flipY;
  22030. const heightWidthRatio = this.sprite.height / this.sprite.width;
  22031. const widthHeightRatio = this.sprite.width / this.sprite.height;
  22032. const ratioToUse = heightWidthRatio > widthHeightRatio;
  22033. if (this.data.scaleToObject) {
  22034. let { width: width2, height } = this.target ? this.getTargetData() : this.getSourceData();
  22035. const target = this.targetDocument || this.sourceDocument;
  22036. if (target instanceof TokenDocument) {
  22037. width2 *= this.data.scaleToObject?.considerTokenScale ? target.texture.scaleX : 1;
  22038. height *= this.data.scaleToObject?.considerTokenScale ? target.texture.scaleY : 1;
  22039. }
  22040. if (this.data.scaleToObject?.uniform) {
  22041. let newWidth = Math.max(width2, height);
  22042. height = Math.max(width2, height);
  22043. width2 = newWidth;
  22044. } else {
  22045. width2 = width2 * (ratioToUse ? widthHeightRatio : 1);
  22046. height = height * (!ratioToUse ? heightWidthRatio : 1);
  22047. }
  22048. this.sprite.width = width2 * (this.data.scaleToObject?.scale ?? 1) * baseScaleX;
  22049. this.sprite.height = height * (this.data.scaleToObject?.scale ?? 1) * baseScaleY;
  22050. } else if (this.data.size) {
  22051. let { height, width: width2 } = this.data.size;
  22052. if (this.data.size.width === "auto" || this.data.size.height === "auto") {
  22053. height = this.sprite.height;
  22054. width2 = this.sprite.width;
  22055. if (this.data.size.width === "auto") {
  22056. height = this.data.size.height;
  22057. if (this.data.size.gridUnits) {
  22058. height *= canvas.grid.size;
  22059. }
  22060. width2 = height * widthHeightRatio;
  22061. } else if (this.data.size.height === "auto") {
  22062. width2 = this.data.size.width;
  22063. if (this.data.size.gridUnits) {
  22064. width2 *= canvas.grid.size;
  22065. }
  22066. height = width2 * heightWidthRatio;
  22067. }
  22068. } else if (this.data.size.gridUnits) {
  22069. height *= canvas.grid.size;
  22070. width2 *= canvas.grid.size;
  22071. }
  22072. this.sprite.width = width2 * baseScaleX;
  22073. this.sprite.height = height * baseScaleY;
  22074. } else {
  22075. this.sprite.scale.set(
  22076. baseScaleX * this.gridSizeDifference,
  22077. baseScaleY * this.gridSizeDifference
  22078. );
  22079. }
  22080. }
  22081. async _transformAttachedNoStretchSprite() {
  22082. const applyRotation = this.data.attachTo?.followRotation && !(this.sourceDocument instanceof TokenDocument && this.sourceDocument.lockRotation) && (this.sourceDocument?.rotation !== void 0 || this.sourceDocument?.direction !== void 0) && !this.data.rotateTowards && !this.data.stretchTo;
  22083. this._ticker.add(() => {
  22084. if (this.isDestroyed)
  22085. return;
  22086. if (applyRotation) {
  22087. this.rotationContainer.rotation = this.getSourceData().rotation;
  22088. }
  22089. this._tweakRotationForIsometric();
  22090. try {
  22091. this._applyAttachmentOffset();
  22092. } catch (err) {
  22093. debug_error(err);
  22094. }
  22095. });
  22096. }
  22097. _applyAttachmentOffset() {
  22098. let offset2 = { x: 0, y: 0 };
  22099. if (this.data.attachTo?.align && this.data.attachTo?.align !== "center") {
  22100. offset2 = align({
  22101. context: this.source,
  22102. spriteWidth: this.sprite.width,
  22103. spriteHeight: this.sprite.height,
  22104. align: this.data.attachTo?.align,
  22105. edge: this.data.attachTo?.edge
  22106. });
  22107. }
  22108. this.position.set(
  22109. this.sourcePosition.x - offset2.x,
  22110. this.sourcePosition.y - offset2.y
  22111. );
  22112. }
  22113. _transformRotateTowardsAttachedSprite() {
  22114. this._ticker.add(async () => {
  22115. if (this.isDestroyed)
  22116. return;
  22117. try {
  22118. this._rotateTowards();
  22119. } catch (err) {
  22120. debug_error(err);
  22121. }
  22122. });
  22123. }
  22124. /**
  22125. * Provided an animation targeting the rotation of the sprite's primary container, this method will counter-rotate
  22126. * the sprite in an equal fashion so that the sprite's rotation remains static relative to this animation
  22127. *
  22128. * @param animation
  22129. * @returns {*[]}
  22130. * @private
  22131. */
  22132. _counterAnimateRotation(animation2) {
  22133. if (animation2.target === this.spriteContainer && this.data.zeroSpriteRotation) {
  22134. delete animation2.target;
  22135. let counterAnimation = foundry.utils.deepClone(animation2);
  22136. animation2.target = this.spriteContainer;
  22137. counterAnimation.target = this.sprite;
  22138. if (counterAnimation.values) {
  22139. counterAnimation.values = counterAnimation.values.map(
  22140. (value) => value * -1
  22141. );
  22142. } else {
  22143. counterAnimation.from *= -1;
  22144. counterAnimation.to *= -1;
  22145. }
  22146. if (!Array.isArray(animation2)) {
  22147. animation2 = [animation2, counterAnimation];
  22148. } else {
  22149. animation2.push(counterAnimation);
  22150. }
  22151. }
  22152. return animation2;
  22153. }
  22154. /**
  22155. * Plays the custom animations of this effect
  22156. *
  22157. * @returns {number}
  22158. * @private
  22159. */
  22160. _playCustomAnimations() {
  22161. if (!this.data.animations)
  22162. return 0;
  22163. this._playAnimations(
  22164. foundry.utils.deepClone(this.data.animations) ?? [],
  22165. this.actualCreationTime - this.data.creationTimestamp
  22166. );
  22167. }
  22168. _playAnimations(animations, timeDifference = 0) {
  22169. let animationsToSend = [];
  22170. const oneShotAnimations = animations.filter(
  22171. (animation2) => !animation2.looping && !animation2.fromEnd
  22172. );
  22173. for (let animation2 of oneShotAnimations) {
  22174. animation2.target = foundry.utils.getProperty(this, animation2.target);
  22175. if (!animation2.target)
  22176. continue;
  22177. if (animation2.propertyName.indexOf("rotation") > -1) {
  22178. animation2.from = animation2.from * (Math.PI / 180);
  22179. animation2.to = animation2.to * (Math.PI / 180);
  22180. }
  22181. if (["position.x", "position.y", "height", "width"].includes(
  22182. animation2.propertyName
  22183. ) && animation2.gridUnits) {
  22184. animation2.from *= canvas.grid.size;
  22185. animation2.to *= canvas.grid.size;
  22186. }
  22187. if (["hue"].includes(animation2.propertyName)) {
  22188. animation2.getPropertyName = "values." + animation2.propertyName;
  22189. }
  22190. animationsToSend = animationsToSend.concat(
  22191. this._counterAnimateRotation(animation2)
  22192. );
  22193. }
  22194. const loopingAnimations = animations.filter(
  22195. (animation2) => animation2.looping
  22196. );
  22197. for (let animation2 of loopingAnimations) {
  22198. animation2.target = foundry.utils.getProperty(this, animation2.target);
  22199. if (!animation2.target)
  22200. continue;
  22201. if (animation2.propertyName.indexOf("rotation") > -1) {
  22202. animation2.values = animation2.values.map((angle) => {
  22203. return angle * (Math.PI / 180);
  22204. });
  22205. }
  22206. if (["position.x", "position.y", "height", "width"].includes(
  22207. animation2.propertyName
  22208. ) && animation2.gridUnits) {
  22209. animation2.values = animation2.values.map((value) => {
  22210. return value * canvas.grid.size;
  22211. });
  22212. }
  22213. if (["hue"].includes(animation2.propertyName)) {
  22214. animation2.getPropertyName = "values." + animation2.propertyName;
  22215. }
  22216. animationsToSend = animationsToSend.concat(
  22217. this._counterAnimateRotation(animation2)
  22218. );
  22219. }
  22220. if (!(this instanceof PersistentCanvasEffect)) {
  22221. animationsToSend = animationsToSend.concat(
  22222. this._getFromEndCustomAnimations()
  22223. );
  22224. }
  22225. setTimeout(() => {
  22226. SequencerAnimationEngine.addAnimation(
  22227. this.id,
  22228. animationsToSend,
  22229. timeDifference
  22230. );
  22231. }, 20);
  22232. }
  22233. _getFromEndCustomAnimations(immediate = false) {
  22234. let fromEndAnimations = [];
  22235. const animations = foundry.utils.deepClone(this.data.animations) ?? [];
  22236. const oneShotEndingAnimations = animations.filter(
  22237. (animation2) => !animation2.looping && animation2.fromEnd
  22238. );
  22239. for (let animation2 of oneShotEndingAnimations) {
  22240. animation2.target = foundry.utils.getProperty(this, animation2.target);
  22241. if (!animation2.target)
  22242. continue;
  22243. animation2.delay = is_real_number(immediate) ? Math.max(immediate - animation2.duration + animation2.delay, 0) : Math.max(
  22244. this._animationDuration - animation2.duration + animation2.delay,
  22245. 0
  22246. );
  22247. if (animation2.propertyName.indexOf("rotation") > -1) {
  22248. animation2.from = animation2.from * (Math.PI / 180);
  22249. animation2.to = animation2.to * (Math.PI / 180);
  22250. }
  22251. if (["position.x", "position.y", "height", "width"].includes(
  22252. animation2.propertyName
  22253. ) && animation2.gridUnits) {
  22254. animation2.from *= canvas.grid.size;
  22255. animation2.to *= canvas.grid.size;
  22256. }
  22257. fromEndAnimations = fromEndAnimations.concat(
  22258. this._counterAnimateRotation(animation2)
  22259. );
  22260. }
  22261. return fromEndAnimations;
  22262. }
  22263. /**
  22264. * Fades in the effect at the start of the effect
  22265. *
  22266. * @returns {number|*}
  22267. * @private
  22268. */
  22269. _fadeIn() {
  22270. if (!this.data.fadeIn || !this.sprite)
  22271. return 0;
  22272. let fadeIn = this.data.fadeIn;
  22273. if (this.actualCreationTime - (this.data.creationTimestamp + fadeIn.duration + fadeIn.delay) > 0) {
  22274. return;
  22275. }
  22276. this.alphaFilter.alpha = 0;
  22277. SequencerAnimationEngine.addAnimation(this.id, {
  22278. target: this.alphaFilter,
  22279. propertyName: "alpha",
  22280. to: this.data.opacity,
  22281. duration: fadeIn.duration,
  22282. ease: fadeIn.ease,
  22283. delay: fadeIn.delay,
  22284. absolute: true
  22285. });
  22286. return fadeIn.duration + fadeIn.delay;
  22287. }
  22288. /**
  22289. * Fades in the effect's audio at the start of the effect
  22290. *
  22291. * @returns {number|*}
  22292. * @private
  22293. */
  22294. _fadeInAudio() {
  22295. if (!this.data.fadeInAudio || !this.sprite || !this.video)
  22296. return 0;
  22297. let fadeInAudio = this.data.fadeInAudio;
  22298. if (this.actualCreationTime - (this.data.creationTimestamp + fadeInAudio.duration + fadeInAudio.delay) > 0)
  22299. return;
  22300. this.video.volume = 0;
  22301. SequencerAnimationEngine.addAnimation(this.id, {
  22302. target: this,
  22303. propertyName: "video.volume",
  22304. to: (this.data.volume ?? 0) * game.settings.get("core", "globalInterfaceVolume"),
  22305. duration: fadeInAudio.duration,
  22306. ease: fadeInAudio.ease,
  22307. delay: fadeInAudio.delay,
  22308. absolute: true
  22309. });
  22310. return fadeInAudio.duration + fadeInAudio.delay;
  22311. }
  22312. /**
  22313. * Fades out the effect at the end of the effect's duration
  22314. *
  22315. * @returns {number|*}
  22316. * @private
  22317. */
  22318. _fadeOut(immediate = false) {
  22319. if (!this.data.fadeOut || !this.sprite)
  22320. return 0;
  22321. let fadeOut = this.data.fadeOut;
  22322. fadeOut.delay = is_real_number(immediate) ? Math.max(immediate - fadeOut.duration + fadeOut.delay, 0) : Math.max(this._animationDuration - fadeOut.duration + fadeOut.delay, 0);
  22323. SequencerAnimationEngine.addAnimation(this.id, {
  22324. target: this.alphaFilter,
  22325. propertyName: "alpha",
  22326. to: 0,
  22327. duration: fadeOut.duration,
  22328. ease: fadeOut.ease,
  22329. delay: fadeOut.delay,
  22330. absolute: true
  22331. });
  22332. return fadeOut.duration + fadeOut.delay;
  22333. }
  22334. /**
  22335. * Fades out the effect at the end of the effect's duration
  22336. *
  22337. * @returns {number|*}
  22338. * @private
  22339. */
  22340. _fadeOutAudio(immediate = false) {
  22341. if (!this.data.fadeOutAudio || !this.sprite || !this.video)
  22342. return 0;
  22343. let fadeOutAudio = this.data.fadeOutAudio;
  22344. fadeOutAudio.delay = is_real_number(immediate) ? Math.max(immediate - fadeOutAudio.duration + fadeOutAudio.delay, 0) : Math.max(
  22345. this._animationDuration - fadeOutAudio.duration + fadeOutAudio.delay,
  22346. 0
  22347. );
  22348. SequencerAnimationEngine.addAnimation(this.id, {
  22349. target: this,
  22350. propertyName: "video.volume",
  22351. to: 0,
  22352. duration: fadeOutAudio.duration,
  22353. ease: fadeOutAudio.ease,
  22354. delay: fadeOutAudio.delay,
  22355. absolute: true
  22356. });
  22357. return fadeOutAudio.duration + fadeOutAudio.delay;
  22358. }
  22359. /**
  22360. * Determines the scale to animate from or to
  22361. * @param property
  22362. * @returns {{x: number, y: number}}
  22363. * @private
  22364. */
  22365. _determineScale(property) {
  22366. let scale2 = {
  22367. x: this.sprite.scale.x,
  22368. y: this.sprite.scale.y
  22369. };
  22370. if (is_real_number(property.value)) {
  22371. scale2.x *= property.value * this.gridSizeDifference * this.flipX;
  22372. scale2.y *= property.value * this.gridSizeDifference * this.flipY;
  22373. } else {
  22374. scale2.x *= property.value.x * this.gridSizeDifference * this.flipX;
  22375. scale2.y *= property.value.y * this.gridSizeDifference * this.flipY;
  22376. }
  22377. return scale2;
  22378. }
  22379. /**
  22380. * Scales the effect in at the start of the effect
  22381. *
  22382. * @returns {number|*}
  22383. * @private
  22384. */
  22385. _scaleIn() {
  22386. if (!this.data.scaleIn || !this.sprite)
  22387. return 0;
  22388. let scaleIn = this.data.scaleIn;
  22389. let fromScale = this._determineScale(scaleIn);
  22390. if (this.actualCreationTime - (this.data.creationTimestamp + scaleIn.duration + scaleIn.delay) > 0)
  22391. return;
  22392. let toScale = {
  22393. x: this.sprite.scale.x,
  22394. y: this.sprite.scale.y
  22395. };
  22396. this.sprite.scale.set(fromScale.x, fromScale.y);
  22397. SequencerAnimationEngine.addAnimation(this.id, [
  22398. {
  22399. target: this.sprite,
  22400. propertyName: "scale.x",
  22401. from: fromScale.x,
  22402. to: toScale.x,
  22403. duration: scaleIn.duration,
  22404. ease: scaleIn.ease,
  22405. delay: scaleIn.delay,
  22406. absolute: true
  22407. },
  22408. {
  22409. target: this.sprite,
  22410. propertyName: "scale.y",
  22411. from: fromScale.y,
  22412. to: toScale.y,
  22413. duration: scaleIn.duration,
  22414. ease: scaleIn.ease,
  22415. delay: scaleIn.delay,
  22416. absolute: true
  22417. }
  22418. ]);
  22419. return scaleIn.duration + scaleIn.delay;
  22420. }
  22421. /**
  22422. * Scales the effect out at the end of the effect's duration
  22423. *
  22424. * @returns {number|*}
  22425. * @private
  22426. */
  22427. _scaleOut(immediate = false) {
  22428. if (!this.data.scaleOut || !this.sprite)
  22429. return 0;
  22430. let scaleOut = this.data.scaleOut;
  22431. let scale2 = this._determineScale(scaleOut);
  22432. scaleOut.delay = is_real_number(immediate) ? Math.max(immediate - scaleOut.duration + scaleOut.delay, 0) : Math.max(
  22433. this._animationDuration - scaleOut.duration + scaleOut.delay,
  22434. 0
  22435. );
  22436. SequencerAnimationEngine.addAnimation(this.id, [
  22437. {
  22438. target: this.sprite,
  22439. propertyName: "scale.x",
  22440. to: scale2.x,
  22441. duration: scaleOut.duration,
  22442. ease: scaleOut.ease,
  22443. delay: scaleOut.delay,
  22444. absolute: true
  22445. },
  22446. {
  22447. target: this.sprite,
  22448. propertyName: "scale.y",
  22449. to: scale2.y,
  22450. duration: scaleOut.duration,
  22451. ease: scaleOut.ease,
  22452. delay: scaleOut.delay,
  22453. absolute: true
  22454. }
  22455. ]);
  22456. return scaleOut.duration + scaleOut.delay;
  22457. }
  22458. /**
  22459. * Rotates the effect in at the start of the effect
  22460. *
  22461. * @returns {number|*}
  22462. * @private
  22463. */
  22464. _rotateIn() {
  22465. if (!this.data.rotateIn || !this.sprite)
  22466. return 0;
  22467. let rotateIn = this.data.rotateIn;
  22468. if (this.actualCreationTime - (this.data.creationTimestamp + rotateIn.duration + rotateIn.delay) > 0)
  22469. return;
  22470. let original_radians = this.spriteContainer.rotation;
  22471. this.spriteContainer.rotation = rotateIn.value * (Math.PI / 180);
  22472. SequencerAnimationEngine.addAnimation(
  22473. this.id,
  22474. this._counterAnimateRotation({
  22475. target: this.spriteContainer,
  22476. propertyName: "rotation",
  22477. to: original_radians,
  22478. duration: rotateIn.duration,
  22479. ease: rotateIn.ease,
  22480. delay: rotateIn.delay,
  22481. absolute: true
  22482. })
  22483. );
  22484. return rotateIn.duration + rotateIn.delay;
  22485. }
  22486. /**
  22487. * Rotates the effect out at the end of the effect's duration
  22488. *
  22489. * @returns {number|*}
  22490. * @private
  22491. */
  22492. _rotateOut(immediate = false) {
  22493. if (!this.data.rotateOut || !this.sprite)
  22494. return 0;
  22495. let rotateOut = this.data.rotateOut;
  22496. rotateOut.delay = is_real_number(immediate) ? Math.max(immediate - rotateOut.duration + rotateOut.delay, 0) : Math.max(
  22497. this._animationDuration - rotateOut.duration + rotateOut.delay,
  22498. 0
  22499. );
  22500. SequencerAnimationEngine.addAnimation(
  22501. this.id,
  22502. this._counterAnimateRotation({
  22503. target: this.spriteContainer,
  22504. propertyName: "rotation",
  22505. to: rotateOut.value * (Math.PI / 180),
  22506. duration: rotateOut.duration,
  22507. ease: rotateOut.ease,
  22508. delay: rotateOut.delay,
  22509. absolute: true
  22510. })
  22511. );
  22512. return rotateOut.duration + rotateOut.delay;
  22513. }
  22514. /**
  22515. * Causes the effect to move towards the given location
  22516. *
  22517. * @returns {number|*}
  22518. * @private
  22519. */
  22520. _moveTowards() {
  22521. if (!this.data.moves || !this.sprite)
  22522. return 0;
  22523. let moves2 = this.data.moves;
  22524. let movementDuration = this._animationDuration;
  22525. if (this.data.moveSpeed) {
  22526. const distance = distance_between(
  22527. this.sourcePosition,
  22528. this.targetPosition
  22529. );
  22530. movementDuration = distance / this.data.moveSpeed * 1e3;
  22531. }
  22532. if (this.data.moves.rotate)
  22533. this._rotateTowards();
  22534. const duration = movementDuration - moves2.delay;
  22535. if (this.actualCreationTime - (this.data.creationTimestamp + duration + moves2.delay) > 0)
  22536. return;
  22537. SequencerAnimationEngine.addAnimation(this.id, [
  22538. {
  22539. target: this,
  22540. propertyName: "position.x",
  22541. to: this.targetPosition.x,
  22542. duration,
  22543. ease: moves2.ease,
  22544. delay: moves2.delay
  22545. },
  22546. {
  22547. target: this,
  22548. propertyName: "position.y",
  22549. to: this.targetPosition.y,
  22550. duration,
  22551. ease: moves2.ease,
  22552. delay: moves2.delay
  22553. }
  22554. ]);
  22555. return duration + moves2.delay;
  22556. }
  22557. /**
  22558. * If this effect is temporary, this sets the timeout for when the effect should resolve and get removed;
  22559. *
  22560. * @private
  22561. */
  22562. _setEndTimeout() {
  22563. setTimeout(() => {
  22564. this._resolve(this.data);
  22565. this.endEffect();
  22566. }, this._animationDuration);
  22567. }
  22568. _setupTimestampHook(offset2) {
  22569. if (!this._file?.originalMetadata?.timestamps || this._ended)
  22570. return;
  22571. const timestamps = this._file.getTimestamps();
  22572. const timestampArray = Array.isArray(timestamps) ? timestamps : [timestamps];
  22573. for (const timestamp of timestampArray) {
  22574. if (!is_real_number(timestamp))
  22575. continue;
  22576. let realTimestamp = timestamp - offset2;
  22577. if (realTimestamp < 0) {
  22578. realTimestamp += this._endTime;
  22579. }
  22580. setTimeout(() => {
  22581. if (this._ended)
  22582. return;
  22583. Hooks.callAll("sequencerEffectTimestamp", this, this._file);
  22584. if (this.mediaLooping) {
  22585. const offsets = (this._endTime - this.mediaCurrentTime) * -1e3;
  22586. this._setupTimestampHook(offsets);
  22587. }
  22588. }, realTimestamp);
  22589. }
  22590. }
  22591. }
  22592. class PersistentCanvasEffect extends CanvasEffect {
  22593. /**
  22594. * @OVERRIDE
  22595. * @returns {Promise<void>}
  22596. * @private
  22597. */
  22598. async _initialize() {
  22599. await super._initialize(false);
  22600. await this._startEffect();
  22601. }
  22602. /**
  22603. * @OVERRIDE
  22604. * @returns {Promise<void>}
  22605. * @private
  22606. */
  22607. async _reinitialize() {
  22608. await super._reinitialize(false);
  22609. }
  22610. /** @OVERRIDE */
  22611. _playPresetAnimations() {
  22612. this._moveTowards();
  22613. this._fadeIn();
  22614. this._scaleIn();
  22615. this._rotateIn();
  22616. }
  22617. /**
  22618. * Starts the loop of this effect, calculating the difference between the effect's creation time, and the actual
  22619. * creation time on the client
  22620. *
  22621. * @returns {Promise<void>}
  22622. * @private
  22623. */
  22624. async _startEffect() {
  22625. if (!this.hasAnimatedMedia)
  22626. return;
  22627. let creationTimeDifference = this.actualCreationTime - this.data.creationTimestamp;
  22628. if (!this.data.noLoop) {
  22629. return this._startLoop(creationTimeDifference);
  22630. }
  22631. if (creationTimeDifference < this._animationDuration) {
  22632. this.mediaCurrentTime = creationTimeDifference / 1e3;
  22633. if (this._endTime !== this.mediaDuration) {
  22634. setTimeout(() => {
  22635. this.mediaCurrentTime = this._endTime;
  22636. this.sprite.texture.update();
  22637. }, this._endTime * 1e3 - creationTimeDifference);
  22638. }
  22639. await this.playMedia();
  22640. return;
  22641. }
  22642. await this.pauseMedia();
  22643. this.mediaCurrentTime = this._endTime;
  22644. if (this.sprite.texture && this.video) {
  22645. const oldRenderable = this.renderable;
  22646. this.renderable = false;
  22647. setTimeout(() => {
  22648. this.renderable = oldRenderable;
  22649. this.sprite.texture.update();
  22650. }, 350);
  22651. }
  22652. }
  22653. /**
  22654. * Kicks off the loop, or just sets the video to loop
  22655. *
  22656. * @param creationTimeDifference
  22657. * @returns {Promise<void>}
  22658. * @private
  22659. */
  22660. async _startLoop(creationTimeDifference) {
  22661. this.mediaLooping = this.playNaturally;
  22662. if (!this._animationTimes.loopStart) {
  22663. this._loopOffset = creationTimeDifference % this._animationDuration / 1e3;
  22664. } else if (creationTimeDifference / 1e3 > this._animationTimes.loopStart) {
  22665. const loopDuration = this._animationTimes.loopEnd - this._animationTimes.loopStart;
  22666. this._loopOffset = creationTimeDifference % (loopDuration * 1e3) / 1e3;
  22667. }
  22668. return this._resetLoop();
  22669. }
  22670. /**
  22671. * Continuously reset the video to the right time so that the start and end time can be preserved
  22672. *
  22673. * @returns {Promise<void>}
  22674. * @private
  22675. */
  22676. async _resetLoop(firstLoop = true) {
  22677. if (this._ended)
  22678. return;
  22679. let loopWaitTime = 0;
  22680. if (this._animationTimes.loopStart) {
  22681. if (this._isEnding)
  22682. return;
  22683. this.mediaCurrentTime = (firstLoop ? 0 : this._animationTimes.loopStart) + (this._loopOffset > 0 ? this._loopOffset : 0);
  22684. loopWaitTime = (this._animationTimes.loopEnd - this.mediaCurrentTime) * 1e3;
  22685. } else {
  22686. this.mediaCurrentTime = this._startTime + this._loopOffset;
  22687. loopWaitTime = this._animationDuration - this._loopOffset * 1e3;
  22688. }
  22689. await this.playMedia();
  22690. if (this.mediaLooping) {
  22691. return;
  22692. }
  22693. this._resetTimeout = setTimeout(() => {
  22694. if (this._ended)
  22695. return;
  22696. this._loopOffset = 0;
  22697. this._resetLoop(false);
  22698. }, loopWaitTime);
  22699. }
  22700. /** @OVERRIDE */
  22701. _timeoutVisibility() {
  22702. let creationTimeDifference = this.actualCreationTime - this.data.creationTimestamp;
  22703. let timeout = creationTimeDifference === 0 && !this.data.animations ? 0 : 50;
  22704. setTimeout(() => {
  22705. this._setupHooks();
  22706. }, timeout);
  22707. }
  22708. /** @OVERRIDE */
  22709. _setEndTimeout() {
  22710. let creationTimeDifference = this.actualCreationTime - this.data.creationTimestamp;
  22711. if (!this.data.noLoop || creationTimeDifference >= this._animationDuration || !(this.hasAnimatedMedia || this.data.text))
  22712. return;
  22713. setTimeout(() => {
  22714. this.pauseMedia();
  22715. }, this._animationDuration);
  22716. }
  22717. /** @OVERRIDE */
  22718. async endEffect() {
  22719. if (this._isEnding)
  22720. return;
  22721. this._isEnding = true;
  22722. let fullWaitDuration = 0;
  22723. let extraEndDuration = this.data.extraEndDuration ?? 0;
  22724. if (this._animationTimes?.forcedEnd) {
  22725. this.mediaCurrentTime = this._animationTimes.forcedEnd;
  22726. fullWaitDuration = (this.mediaDuration - (this._animationTimes?.forcedEnd ?? 0)) * 1e3;
  22727. } else if (this._animationTimes?.loopEnd) {
  22728. fullWaitDuration = (this.mediaDuration - this.mediaCurrentTime) * 1e3;
  22729. this.mediaLooping = false;
  22730. extraEndDuration = Math.max(extraEndDuration, fullWaitDuration);
  22731. }
  22732. const fromEndCustomAnimations = this._getFromEndCustomAnimations(extraEndDuration);
  22733. const durations = [
  22734. this._fadeOut(extraEndDuration),
  22735. this._fadeOutAudio(extraEndDuration),
  22736. this._scaleOut(extraEndDuration),
  22737. this._rotateOut(extraEndDuration),
  22738. this.data.extraEndDuration,
  22739. fullWaitDuration,
  22740. ...fromEndCustomAnimations.map(
  22741. (animation2) => animation2.duration + animation2.delay
  22742. )
  22743. ].filter(Boolean);
  22744. SequencerAnimationEngine.addAnimation(this.id, fromEndCustomAnimations);
  22745. const waitDuration = Math.max(...durations, 0);
  22746. this._resolve(waitDuration);
  22747. return new Promise(
  22748. (resolve) => setTimeout(() => {
  22749. super.endEffect();
  22750. resolve(this.data);
  22751. }, waitDuration)
  22752. );
  22753. }
  22754. }
  22755. function createShape(shape) {
  22756. const graphic = new PIXI.Graphics();
  22757. graphic.beginFill(
  22758. shape?.fillColor ?? 16777215,
  22759. shape?.fillColor !== void 0 ? shape?.fillAlpha ?? 1 : 0
  22760. );
  22761. graphic.lineStyle(
  22762. shape.lineSize ?? (shape?.lineColor !== void 0 ? 1 : 0),
  22763. shape?.lineColor ?? 16777215
  22764. );
  22765. const offsetX = (shape.offset?.x ?? 0) * (shape.offset?.gridUnits ? canvas.grid.size : 1);
  22766. const offsetY = (shape.offset?.y ?? 0) * (shape.offset?.gridUnits ? canvas.grid.size : 1);
  22767. const sizeMultiplier = shape.gridUnits ? canvas.grid.size : 1;
  22768. graphic.offset = {
  22769. x: offsetX,
  22770. y: offsetY
  22771. };
  22772. switch (shape.type) {
  22773. case CONSTANTS.SHAPES.CIRC:
  22774. graphic.drawCircle(
  22775. graphic.offset.x,
  22776. graphic.offset.y,
  22777. shape.radius * sizeMultiplier
  22778. );
  22779. break;
  22780. case CONSTANTS.SHAPES.RECT:
  22781. graphic.drawRect(
  22782. graphic.offset.x,
  22783. graphic.offset.y,
  22784. shape.width * sizeMultiplier,
  22785. shape.height * sizeMultiplier
  22786. );
  22787. break;
  22788. case CONSTANTS.SHAPES.ELIP:
  22789. graphic.drawEllipse(
  22790. graphic.offset.x,
  22791. graphic.offset.y,
  22792. shape.width * sizeMultiplier,
  22793. shape.height * sizeMultiplier
  22794. );
  22795. break;
  22796. case CONSTANTS.SHAPES.RREC:
  22797. graphic.drawRoundedRect(
  22798. graphic.offset.x,
  22799. graphic.offset.y,
  22800. shape.width * sizeMultiplier,
  22801. shape.height * sizeMultiplier,
  22802. shape.radius * sizeMultiplier
  22803. );
  22804. break;
  22805. case CONSTANTS.SHAPES.POLY:
  22806. graphic.drawPolygon(
  22807. shape.points.map((point, index) => {
  22808. return new PIXI.Point(
  22809. point[0] * sizeMultiplier + graphic.offset.x,
  22810. point[1] * sizeMultiplier + graphic.offset.y
  22811. );
  22812. })
  22813. );
  22814. break;
  22815. }
  22816. graphic.alpha = shape.alpha ?? 1;
  22817. graphic.endFill();
  22818. return graphic;
  22819. }
  22820. function calculate_missed_position(source, target, twister) {
  22821. const sourcePosition = get_object_position(source);
  22822. if (!target) {
  22823. const sourceDimensions = get_object_dimensions(source, true);
  22824. const angle2 = twister.random() * Math.PI * 2;
  22825. let x2 = Math.cos(angle2) * sourceDimensions.width;
  22826. let y2 = Math.sin(angle2) * sourceDimensions.height;
  22827. return {
  22828. x: random_float_between(x2 * 1.5, x2 * 2.5, twister),
  22829. y: random_float_between(y2 * 1.5, y2 * 2.5, twister)
  22830. };
  22831. }
  22832. const targetDimensions = get_object_dimensions(target, true);
  22833. const targetPosition = get_object_position(target);
  22834. const ray = new Ray(targetPosition, sourcePosition);
  22835. let startRadians = ray.angle + Math.PI / 2;
  22836. let endRadians = ray.angle - Math.PI / 2;
  22837. let distance = ray.distance / canvas.grid.size - 1;
  22838. let angle;
  22839. if (distance < 1) {
  22840. angle = twister.random() > 0.5 ? startRadians : endRadians;
  22841. } else {
  22842. distance = Math.max(Math.abs(distance - 15), 6);
  22843. endRadians -= Math.PI / distance;
  22844. startRadians += Math.PI / distance;
  22845. angle = interpolate(startRadians, endRadians, twister.random());
  22846. }
  22847. let x = Math.cos(angle) * targetDimensions.width;
  22848. let y = Math.sin(angle) * targetDimensions.height;
  22849. return {
  22850. x: random_float_between(x * 1.5, x * 2.5, twister),
  22851. y: random_float_between(y * 1.5, y * 2.5, twister)
  22852. };
  22853. }
  22854. function get_object_position(obj, { measure = false, exact = false } = {}) {
  22855. if (obj instanceof CanvasEffect) {
  22856. return obj.worldPosition;
  22857. }
  22858. obj = obj?._object ?? obj.object ?? obj;
  22859. let pos = {};
  22860. if (obj instanceof MeasuredTemplate) {
  22861. if (measure) {
  22862. if (obj.document.t === "cone" || obj.document.t === "ray") {
  22863. pos.x = obj.ray.B.x;
  22864. pos.y = obj.ray.B.y;
  22865. }
  22866. }
  22867. if (obj.document.t === "rect") {
  22868. pos.x = obj.x;
  22869. pos.y = obj.y;
  22870. if (!exact) {
  22871. pos.x += Math.abs(obj.shape.width / 2) + obj.shape.x;
  22872. pos.y += Math.abs(obj.shape.height / 2) + obj.shape.y;
  22873. }
  22874. }
  22875. } else if (obj instanceof Tile) {
  22876. pos = {
  22877. x: obj.document.x,
  22878. y: obj.document.y
  22879. };
  22880. if (!exact) {
  22881. pos.x += Math.abs(obj.document.width / 2);
  22882. pos.y += Math.abs(obj.document.height / 2);
  22883. }
  22884. } else if (obj instanceof Token) {
  22885. pos = {
  22886. x: obj.mesh.x,
  22887. y: obj.mesh.y
  22888. };
  22889. if (exact) {
  22890. const halfSize = get_object_dimensions(obj, true);
  22891. pos.x -= halfSize.width;
  22892. pos.y -= halfSize.height;
  22893. }
  22894. }
  22895. pos = {
  22896. x: pos.x ?? obj?.x ?? obj?.position?.x ?? obj?.position?._x ?? obj?.document?.x ?? obj?.document?.position?.x ?? 0,
  22897. y: pos.y ?? obj?.y ?? obj?.position?.y ?? obj?.position?._y ?? obj?.document?.y ?? obj?.document?.position?.y ?? 0
  22898. };
  22899. if (!is_real_number(pos.x) || !is_real_number(pos.y)) {
  22900. return false;
  22901. }
  22902. return pos;
  22903. }
  22904. function get_random_offset(target, randomOffset, twister = false) {
  22905. let { width: width2, height } = get_object_dimensions(target, true);
  22906. width2 *= randomOffset;
  22907. height *= randomOffset;
  22908. return {
  22909. x: random_float_between(width2 * -1, width2, twister),
  22910. y: random_float_between(height * -1, height, twister)
  22911. };
  22912. }
  22913. function get_object_dimensions(inObj, half = false) {
  22914. inObj = inObj?.object ?? inObj?._object ?? inObj;
  22915. let width2 = inObj?.hitArea?.width ?? inObj?.w ?? inObj?.shape?.width ?? (inObj?.shape?.radius ? inObj?.shape?.radius * 2 : void 0) ?? inObj?.width ?? canvas.grid.size;
  22916. let height = inObj?.hitArea?.height ?? inObj?.h ?? inObj?.shape?.height ?? (inObj?.shape?.radius ? inObj?.shape?.radius * 2 : void 0) ?? inObj?.height ?? canvas.grid.size;
  22917. return {
  22918. width: width2 / (half ? 2 : 1),
  22919. height: height / (half ? 2 : 1)
  22920. };
  22921. }
  22922. const alignments = {
  22923. "top-left": { x: 0.5, y: 0.5 },
  22924. top: { x: 0, y: 0.5 },
  22925. "top-right": { x: -0.5, y: 0.5 },
  22926. left: { x: 0.5, y: 0 },
  22927. center: { x: 0, y: 0 },
  22928. right: { x: -0.5, y: 0 },
  22929. "bottom-left": { x: 0.5, y: -0.5 },
  22930. bottom: { x: 0, y: -0.5 },
  22931. "bottom-right": { x: -0.5, y: -0.5 }
  22932. };
  22933. function align({
  22934. context,
  22935. spriteWidth,
  22936. spriteHeight,
  22937. align: align2,
  22938. edge
  22939. } = {}) {
  22940. let { width: width2, height } = get_object_dimensions(context);
  22941. const alignRatio = alignments[align2];
  22942. const offset2 = {
  22943. x: interpolate(width2 * -0.5, width2 * 0.5, alignRatio.x + 0.5),
  22944. y: interpolate(height * -0.5, height * 0.5, alignRatio.y + 0.5)
  22945. };
  22946. return {
  22947. x: offset2.x + (edge && edge !== "on" ? spriteWidth * alignRatio.x * (edge === "outer" ? 1 : -1) : 0),
  22948. y: offset2.y + (edge && edge !== "on" ? spriteHeight * alignRatio.y * (edge === "outer" ? 1 : -1) : 0)
  22949. };
  22950. }
  22951. function is_object_canvas_data(inObj) {
  22952. if (typeof inObj !== "object")
  22953. return false;
  22954. const keys = Object.keys(inObj);
  22955. keys.sort();
  22956. return keys.includes("x") && keys.includes("y") || keys.includes("height") && keys.includes("width") && keys.includes("x") && keys.includes("y");
  22957. }
  22958. function get_object_canvas_data(inObject, measure = false) {
  22959. inObject = inObject?.object ?? inObject;
  22960. return {
  22961. ...get_object_position(inObject, { measure }),
  22962. ...get_object_dimensions(inObject?.mesh ?? inObject?.tile ?? inObject),
  22963. elevation: get_object_elevation(inObject),
  22964. uuid: inObject?.document?.uuid ?? inObject?.uuid,
  22965. cachedLocation: true
  22966. };
  22967. }
  22968. function get_object_elevation(inObject) {
  22969. return inObject?.document?.elevation ?? inObject?.elevation ?? 0;
  22970. }
  22971. function get_mouse_position(snapToGrid = false, gridSnap = 2) {
  22972. const pos = canvas.app.renderer.plugins.interaction.mouse.getLocalPosition(
  22973. canvas.app.stage
  22974. );
  22975. return !snapToGrid ? new PIXI.Point(pos.x, pos.y) : canvas.grid.getSnappedPosition(pos.x, pos.y, gridSnap);
  22976. }
  22977. function distance_between(p1, p2) {
  22978. return new Ray(p1, p2).distance;
  22979. }
  22980. function is_position_within_bounds(inPosition, inElement, relativeTo) {
  22981. const localPosition = inElement.toLocal(inPosition, relativeTo);
  22982. return inElement.getLocalBounds().contains(localPosition.x, localPosition.y);
  22983. }
  22984. function rotate_coordinate(p1, p2, radians) {
  22985. let cos = Math.cos(radians);
  22986. let sin = Math.sin(radians);
  22987. let nx = cos * (p2.x - p1.x) + sin * (p2.y - p1.y) + p1.x;
  22988. let ny = cos * (p2.y - p1.y) - sin * (p2.x - p1.x) + p1.y;
  22989. return [nx, ny];
  22990. }
  22991. function get_closest_token(inPosition, { minimumDistance = false } = {}) {
  22992. let tokens = Array.from(canvas.scene.tokens);
  22993. if (minimumDistance) {
  22994. tokens = tokens.filter(
  22995. (token) => distance_between(get_object_position(token), inPosition) <= minimumDistance
  22996. );
  22997. }
  22998. tokens.sort((a, b) => {
  22999. return distance_between(get_object_position(a), inPosition) - distance_between(get_object_position(b), inPosition);
  23000. });
  23001. return tokens?.[0] ?? false;
  23002. }
  23003. function getBezierControlPoints(factor, previous, point, next) {
  23004. let vector = { x: next[0] - previous[0], y: next[1] - previous[1] }, preDistance = Math.hypot(previous[0] - point[0], previous[1] - point[1]), postDistance = Math.hypot(next[0] - point[0], next[1] - point[1]), distance = preDistance + postDistance;
  23005. let cp0d = distance === 0 ? 0 : factor * (preDistance / distance), cp1d = distance === 0 ? 0 : factor * (postDistance / distance);
  23006. return {
  23007. cp1: {
  23008. x: point[0] - vector.x * cp0d,
  23009. y: point[1] - vector.y * cp0d
  23010. },
  23011. next_cp0: {
  23012. x: point[0] + vector.x * cp1d,
  23013. y: point[1] + vector.y * cp1d
  23014. }
  23015. };
  23016. }
  23017. function rotateAroundPoint(cx, cy, x, y, angle) {
  23018. let radians = Math.PI / 180 * angle, cos = Math.cos(radians), sin = Math.sin(radians), nx = cos * (x - cx) + sin * (y - cy) + cx, ny = cos * (y - cy) - sin * (x - cx) + cy;
  23019. return { x: nx, y: ny };
  23020. }
  23021. function validateAnimation(inTarget, inPropertyName, inOptions) {
  23022. if (typeof inPropertyName !== "string") {
  23023. return `inPropertyName must be of type string`;
  23024. }
  23025. if (typeof inTarget !== "string") {
  23026. return `inTarget must be of type string`;
  23027. }
  23028. if (!is_real_number(inOptions.from)) {
  23029. return `inOptions.from must be of type number`;
  23030. }
  23031. if (!is_real_number(inOptions.to)) {
  23032. return `inOptions.to must be of type number`;
  23033. }
  23034. if (!is_real_number(inOptions.duration)) {
  23035. return `inOptions.duration must be of type number`;
  23036. }
  23037. if (inOptions?.delay !== void 0 && !is_real_number(inOptions.delay)) {
  23038. return `inOptions.delay must be of type number`;
  23039. }
  23040. if (inOptions?.ease !== void 0 && typeof inOptions.ease !== "string") {
  23041. return `inOptions.ease must be of type string`;
  23042. }
  23043. if (inOptions?.fromEnd !== void 0 && typeof inOptions.fromEnd !== "boolean") {
  23044. return `inOptions.fromEnd must be of type boolean`;
  23045. }
  23046. if (inOptions?.gridUnits !== void 0) {
  23047. if (typeof inOptions.gridUnits !== "boolean") {
  23048. return `inOptions.gridUnits must be of type boolean`;
  23049. }
  23050. if (inOptions.gridUnits && ![
  23051. "position.x",
  23052. "position.y",
  23053. "scale.x",
  23054. "scale.y",
  23055. "height",
  23056. "width"
  23057. ].includes(inPropertyName)) {
  23058. return `if inOptions.gridUnits is true, inPropertyName must be position.x, position.y, scale.x, scale.y, width, or height`;
  23059. }
  23060. }
  23061. return {
  23062. target: inTarget,
  23063. propertyName: inPropertyName,
  23064. from: inOptions?.from,
  23065. to: inOptions?.to,
  23066. duration: inOptions?.duration ?? 0,
  23067. delay: inOptions?.delay ?? 0,
  23068. ease: inOptions?.ease ?? "linear",
  23069. looping: false,
  23070. fromEnd: inOptions?.fromEnd ?? false,
  23071. gridUnits: inOptions?.gridUnits ?? false
  23072. };
  23073. }
  23074. function validateLoopingAnimation(inTarget, inPropertyName, inOptions) {
  23075. if (typeof inPropertyName !== "string") {
  23076. return `inPropertyName must be of type string`;
  23077. }
  23078. if (typeof inTarget !== "string") {
  23079. return `inTarget must be of type string`;
  23080. }
  23081. if (!inOptions?.values) {
  23082. if (!inOptions?.from === void 0 || !inOptions?.to === void 0) {
  23083. return `if inOptions.values is not set, you must provide inOptions.from and inOptions.to`;
  23084. }
  23085. if (!is_real_number(inOptions.from)) {
  23086. return `inOptions.from must be of type number`;
  23087. }
  23088. if (!is_real_number(inOptions.to)) {
  23089. return `inOptions.to must be of type number`;
  23090. }
  23091. inOptions.values = [inOptions?.from, inOptions?.to];
  23092. delete inOptions.from;
  23093. delete inOptions.to;
  23094. } else {
  23095. if (!Array.isArray(inOptions.values)) {
  23096. return `inOptions.values must be of type array`;
  23097. }
  23098. inOptions.values.forEach((value) => {
  23099. if (!is_real_number(value)) {
  23100. return `values in inOptions.keys must be of type number`;
  23101. }
  23102. });
  23103. }
  23104. if (!is_real_number(inOptions.duration)) {
  23105. return `inOptions.duration must be of type number`;
  23106. }
  23107. if (inOptions?.delay !== void 0 && !is_real_number(inOptions.delay)) {
  23108. return `inOptions.delay must be of type number`;
  23109. }
  23110. if (inOptions?.ease !== void 0 && typeof inOptions.ease !== "string") {
  23111. return `inOptions.ease must be of type string`;
  23112. }
  23113. if (inOptions?.loops !== void 0 && !is_real_number(inOptions.loops)) {
  23114. return `inOptions.loops must be of type number`;
  23115. }
  23116. if (inOptions?.pingPong !== void 0 && typeof inOptions.pingPong !== "boolean") {
  23117. return `inOptions.pingPong must be of type boolean`;
  23118. }
  23119. if (inOptions?.gridUnits !== void 0) {
  23120. if (typeof inOptions.gridUnits !== "boolean") {
  23121. return `inOptions.gridUnits must be of type boolean`;
  23122. }
  23123. if (inOptions.gridUnits && ![
  23124. "position.x",
  23125. "position.y",
  23126. "scale.x",
  23127. "scale.y",
  23128. "height",
  23129. "width"
  23130. ].includes(inPropertyName)) {
  23131. return `if inOptions.gridUnits is true, inPropertyName must be position.x, position.y, scale.x, scale.y, width, or height`;
  23132. }
  23133. }
  23134. return {
  23135. target: inTarget,
  23136. propertyName: inPropertyName,
  23137. values: inOptions?.values,
  23138. duration: inOptions?.duration ?? 0,
  23139. delay: inOptions?.delay ?? 0,
  23140. ease: inOptions?.ease ?? "linear",
  23141. looping: true,
  23142. loops: inOptions?.loops,
  23143. indefinite: inOptions?.loops === void 0 || !is_real_number(inOptions?.loops),
  23144. pingPong: inOptions?.pingPong ?? false,
  23145. gridUnits: inOptions?.gridUnits ?? false
  23146. };
  23147. }
  23148. const PlayerSettings = {
  23149. file: {
  23150. label: "SEQUENCER.Player.Option.File",
  23151. store: writable$1(""),
  23152. default: ""
  23153. },
  23154. scale: {
  23155. label: "SEQUENCER.Player.Option.Scale",
  23156. store: writable$1(1),
  23157. default: 1
  23158. },
  23159. users: {
  23160. label: "SEQUENCER.Player.Option.ForUsers",
  23161. store: writable$1([]),
  23162. default: []
  23163. },
  23164. belowTokens: {
  23165. label: "SEQUENCER.Player.Option.BelowTokens",
  23166. store: writable$1(false),
  23167. default: false
  23168. },
  23169. snapToGrid: {
  23170. label: "SEQUENCER.Player.Option.SnapToGrid",
  23171. store: writable$1(false),
  23172. default: false
  23173. },
  23174. rotation: {
  23175. label: "SEQUENCER.Player.Option.Rotation",
  23176. store: writable$1(0),
  23177. default: 0
  23178. },
  23179. randomRotation: {
  23180. label: "SEQUENCER.Player.Option.Randomize",
  23181. store: writable$1(false),
  23182. default: false
  23183. },
  23184. fadeIn: {
  23185. label: "SEQUENCER.Player.Option.FadeIn",
  23186. store: writable$1(0),
  23187. default: 0
  23188. },
  23189. fadeOut: {
  23190. label: "SEQUENCER.Player.Option.FadeOut",
  23191. store: writable$1(0),
  23192. default: 0
  23193. },
  23194. scaleIn: {
  23195. label: "SEQUENCER.Player.Option.ScaleIn",
  23196. store: writable$1(0),
  23197. default: 0
  23198. },
  23199. scaleOut: {
  23200. label: "SEQUENCER.Player.Option.ScaleOut",
  23201. store: writable$1(0),
  23202. default: 0
  23203. },
  23204. mirrorX: {
  23205. label: "SEQUENCER.Player.Option.MirrorX",
  23206. store: writable$1(false),
  23207. default: false
  23208. },
  23209. mirrorY: {
  23210. label: "SEQUENCER.Player.Option.MirrorY",
  23211. store: writable$1(false),
  23212. default: false
  23213. },
  23214. randomMirrorX: {
  23215. label: "SEQUENCER.Player.Option.Randomize",
  23216. store: writable$1(false),
  23217. default: false
  23218. },
  23219. randomMirrorY: {
  23220. label: "SEQUENCER.Player.Option.Randomize",
  23221. store: writable$1(false),
  23222. default: false
  23223. },
  23224. offsetX: {
  23225. label: "SEQUENCER.Player.Option.OffsetX",
  23226. store: writable$1(0),
  23227. default: 0
  23228. },
  23229. offsetY: {
  23230. label: "SEQUENCER.Player.Option.OffsetY",
  23231. store: writable$1(0),
  23232. default: 0
  23233. },
  23234. offsetGridUnits: {
  23235. label: "SEQUENCER.Player.Option.GridUnits",
  23236. store: writable$1(false),
  23237. default: false
  23238. },
  23239. randomOffsetAmount: {
  23240. label: "SEQUENCER.Player.Option.RandomOffset",
  23241. store: writable$1(0),
  23242. default: 0
  23243. },
  23244. randomOffset: {
  23245. label: "SEQUENCER.Player.Option.Randomize",
  23246. store: writable$1(false),
  23247. default: false
  23248. },
  23249. preload: {
  23250. label: "SEQUENCER.Player.Option.Preload",
  23251. store: writable$1(false),
  23252. default: false
  23253. },
  23254. moveTowards: {
  23255. label: "SEQUENCER.Player.Option.DragBehavior",
  23256. label_off: "SEQUENCER.Player.Option.DragStretch",
  23257. label_on: "SEQUENCER.Player.Option.DragMove",
  23258. store: writable$1(false),
  23259. default: false
  23260. },
  23261. moveSpeed: {
  23262. label: "SEQUENCER.Player.Option.MoveSpeed",
  23263. store: writable$1(0),
  23264. default: 0
  23265. },
  23266. attachTo: {
  23267. label: "SEQUENCER.Player.Option.AttachTo",
  23268. store: writable$1(false),
  23269. default: false,
  23270. callback: (e) => {
  23271. EffectPlayer.sourceAttach = e.target.checked;
  23272. }
  23273. },
  23274. stretchToAttach: {
  23275. label: "SEQUENCER.Player.Option.StretchToAttach",
  23276. store: writable$1(false),
  23277. default: false,
  23278. callback: (e) => {
  23279. EffectPlayer.targetAttach = e.target.checked;
  23280. }
  23281. },
  23282. persist: {
  23283. label: "SEQUENCER.Player.Option.Persist",
  23284. store: writable$1(false),
  23285. default: false
  23286. },
  23287. repeat: {
  23288. label: "SEQUENCER.Player.Option.Repeat",
  23289. store: writable$1(false),
  23290. default: false
  23291. },
  23292. repetitions: {
  23293. label: "SEQUENCER.Player.Option.Repetitions",
  23294. store: writable$1(1),
  23295. default: 1
  23296. },
  23297. repeatDelayMin: {
  23298. label: "SEQUENCER.Player.Option.DelayMin",
  23299. store: writable$1(200),
  23300. default: 200
  23301. },
  23302. repeatDelayMax: {
  23303. label: "SEQUENCER.Player.Option.DelayMax",
  23304. store: writable$1(400),
  23305. default: 400
  23306. }
  23307. };
  23308. PlayerSettings.export = () => {
  23309. return Object.fromEntries(
  23310. Object.entries(PlayerSettings).map((entry) => {
  23311. return [entry[0], get_store_value(entry[1].store)];
  23312. })
  23313. );
  23314. };
  23315. PlayerSettings.import = (settings) => {
  23316. Object.entries(PlayerSettings).forEach((entry) => {
  23317. if (settings?.[entry[0]] !== void 0) {
  23318. entry[1].store.set(settings?.[entry[0]]);
  23319. } else if (entry[1]?.default !== void 0) {
  23320. entry[1].store.set(entry[1].default);
  23321. }
  23322. });
  23323. };
  23324. PlayerSettings.getPresets = () => {
  23325. return Object.keys(game.settings.get(CONSTANTS.MODULE_NAME, "effectPresets"));
  23326. };
  23327. PlayerSettings.loadPreset = (name) => {
  23328. const effectPresets = game.settings.get(
  23329. CONSTANTS.MODULE_NAME,
  23330. "effectPresets"
  23331. );
  23332. return PlayerSettings.import(effectPresets[name]);
  23333. };
  23334. PlayerSettings.savePreset = async (name) => {
  23335. const newName = await promptNewPresetName(name);
  23336. if (!newName)
  23337. return;
  23338. const effectPresets = game.settings.get(
  23339. CONSTANTS.MODULE_NAME,
  23340. "effectPresets"
  23341. );
  23342. effectPresets[newName] = PlayerSettings.export();
  23343. return game.settings.set(
  23344. CONSTANTS.MODULE_NAME,
  23345. "effectPresets",
  23346. effectPresets
  23347. );
  23348. };
  23349. PlayerSettings.copyPreset = async (name) => {
  23350. const newName = await promptNewPresetName(name, true);
  23351. if (!newName)
  23352. return;
  23353. const effectPresets = game.settings.get(
  23354. CONSTANTS.MODULE_NAME,
  23355. "effectPresets"
  23356. );
  23357. effectPresets[newName] = PlayerSettings.export();
  23358. return game.settings.set(
  23359. CONSTANTS.MODULE_NAME,
  23360. "effectPresets",
  23361. effectPresets
  23362. );
  23363. };
  23364. PlayerSettings.deletePreset = (name) => {
  23365. const effectPresets = game.settings.get(
  23366. CONSTANTS.MODULE_NAME,
  23367. "effectPresets"
  23368. );
  23369. delete effectPresets[name];
  23370. return game.settings.set(
  23371. CONSTANTS.MODULE_NAME,
  23372. "effectPresets",
  23373. effectPresets
  23374. );
  23375. };
  23376. async function promptNewPresetName(inName, copy = false) {
  23377. const effectPresets = game.settings.get(
  23378. CONSTANTS.MODULE_NAME,
  23379. "effectPresets"
  23380. );
  23381. let title = copy ? game.i18n.localize("SEQUENCER.Player.CopyPresetTitle") : game.i18n.localize("SEQUENCER.Player.CreateNewPresetTitle");
  23382. let presetName = await new Promise((resolve) => {
  23383. new Dialog({
  23384. title,
  23385. content: `<p><input type="text" placeholder="${game.i18n.localize(
  23386. "SEQUENCER.Player.CreateNewPresetInputLabel"
  23387. )}" id="newPresetName" style="width:100%;"></p>`,
  23388. buttons: {
  23389. okay: {
  23390. icon: '<i class="fas fa-check"></i>',
  23391. label: game.i18n.localize("SEQUENCER.OK"),
  23392. callback: async (html) => {
  23393. let name = html.find("#newPresetName").val();
  23394. if (name === "" || !name) {
  23395. name = false;
  23396. }
  23397. resolve(name);
  23398. }
  23399. },
  23400. cancel: {
  23401. icon: '<i class="fas fa-times"></i>',
  23402. label: game.i18n.localize("SEQUENCER.Cancel"),
  23403. callback: () => {
  23404. resolve(false);
  23405. }
  23406. }
  23407. },
  23408. close: () => {
  23409. },
  23410. render: (html) => {
  23411. html.find("#newPresetName").val(inName).focus();
  23412. }
  23413. }).render(true);
  23414. });
  23415. if (presetName) {
  23416. if (presetName.toLowerCase() === "default") {
  23417. Dialog.prompt({
  23418. title: game.i18n.localize("SEQUENCER.Player.DefaultErrorTitle"),
  23419. content: `<p>${game.i18n.localize(
  23420. "SEQUENCER.Player.DefaultErrorContent"
  23421. )}</p>`,
  23422. label: game.i18n.localize("SEQUENCER.OK"),
  23423. callback: () => {
  23424. }
  23425. });
  23426. return false;
  23427. }
  23428. if (effectPresets[presetName]) {
  23429. const overwrite = await Dialog.confirm({
  23430. title: game.i18n.localize("SEQUENCER.Player.OverwritePresetTitle"),
  23431. content: `<p>${game.i18n.format(
  23432. "SEQUENCER.Player.OverwritePresetContent",
  23433. { preset_name: presetName }
  23434. )}</p>`
  23435. });
  23436. if (!overwrite) {
  23437. presetName = await promptPresetName(presetName);
  23438. }
  23439. }
  23440. }
  23441. return presetName;
  23442. }
  23443. PlayerSettings.migrateOldPresets = () => {
  23444. if (!game.user.isGM)
  23445. return;
  23446. const effectPresets = game.settings.get(
  23447. CONSTANTS.MODULE_NAME,
  23448. "effectPresets"
  23449. );
  23450. const presetsToUpdate = Object.values(effectPresets).filter((preset) => {
  23451. return !preset?.version;
  23452. });
  23453. if (!presetsToUpdate.length)
  23454. return;
  23455. const newEffectPresets = Object.fromEntries(
  23456. Object.entries(effectPresets).map(([name, preset]) => {
  23457. if (preset?.version)
  23458. return [name, preset];
  23459. preset.version = game.modules.get(CONSTANTS.MODULE_NAME).version;
  23460. if (preset.repetitions > 1) {
  23461. preset.repeat = true;
  23462. }
  23463. if (preset.randomMirrorY) {
  23464. preset.mirrorY = true;
  23465. }
  23466. if (preset.randomOffset) {
  23467. preset.randomOffsetAmount = 1;
  23468. preset.offsetGridUnits = true;
  23469. }
  23470. return [name, preset];
  23471. })
  23472. );
  23473. return game.settings.set(
  23474. CONSTANTS.MODULE_NAME,
  23475. "effectPresets",
  23476. newEffectPresets
  23477. );
  23478. };
  23479. const InteractionManager = {
  23480. startDragPosition: false,
  23481. state: {
  23482. LeftMouseDown: false,
  23483. RightMouseDown: false,
  23484. Dragging: false
  23485. },
  23486. get isLayerActive() {
  23487. return canvas.sequencerInterfaceLayer.active;
  23488. },
  23489. initialize() {
  23490. window.addEventListener("mousedown", (event) => {
  23491. if (!canvas.ready)
  23492. return;
  23493. if (!this.isLayerActive)
  23494. return;
  23495. const hover = document.elementFromPoint(event.clientX, event.clientY);
  23496. if (!hover || hover.id !== "board")
  23497. return;
  23498. const button = event.button;
  23499. if (!(button === 0 || button === 2))
  23500. return;
  23501. if (button === 0) {
  23502. this.state.LeftMouseDown = true;
  23503. this._propagateEvent("mouseLeftDown");
  23504. }
  23505. if (button === 2) {
  23506. this.state.RightMouseDown = true;
  23507. this._propagateEvent("mouseRightDown");
  23508. }
  23509. });
  23510. window.addEventListener("mouseup", (event) => {
  23511. if (!canvas.ready)
  23512. return;
  23513. if (!this.isLayerActive)
  23514. return;
  23515. const hover = document.elementFromPoint(event.clientX, event.clientY);
  23516. if (!hover || hover.id !== "board")
  23517. return;
  23518. if (document.activeElement.tagName !== "BODY")
  23519. return;
  23520. const button = event.button;
  23521. if (!(button === 0 || button === 2))
  23522. return;
  23523. if (button === 0) {
  23524. this.state.LeftMouseDown = false;
  23525. this._propagateEvent("mouseLeftUp");
  23526. this.state.Dragging = false;
  23527. this.startDragPosition = false;
  23528. }
  23529. if (button === 2) {
  23530. this.state.RightMouseDown = false;
  23531. this._propagateEvent("mouseRightUp");
  23532. this.state.Dragging = false;
  23533. this.startDragPosition = false;
  23534. }
  23535. });
  23536. window.addEventListener("mousemove", (event) => {
  23537. if (!canvas.ready)
  23538. return;
  23539. const hover = document.elementFromPoint(event.clientX, event.clientY);
  23540. if (!hover || hover.id !== "board")
  23541. return;
  23542. if (!this.isLayerActive)
  23543. return;
  23544. this._propagateEvent("mouseMove");
  23545. if (this.state.LeftMouseDown && !this.startDragPosition) {
  23546. this.startDragPosition = get_mouse_position();
  23547. }
  23548. if (this.state.LeftMouseDown && !this.state.Dragging) {
  23549. const distance = distance_between(
  23550. this.startDragPosition,
  23551. get_mouse_position()
  23552. );
  23553. this.state.Dragging = distance > 20;
  23554. }
  23555. });
  23556. EffectPlayer.initialize();
  23557. SelectionManager.initialize();
  23558. },
  23559. tearDown() {
  23560. EffectPlayer.tearDown();
  23561. SelectionManager.tearDown();
  23562. },
  23563. _propagateEvent(eventName) {
  23564. if (EffectPlayer.isActive && EffectPlayer[eventName]) {
  23565. EffectPlayer[eventName]();
  23566. }
  23567. if (SelectionManager.isActive && SelectionManager[eventName]) {
  23568. SelectionManager[eventName]();
  23569. }
  23570. }
  23571. };
  23572. const EffectPlayer = {
  23573. sequenceBuffer: [],
  23574. playMany: false,
  23575. playManySequenced: false,
  23576. cursorPos: false,
  23577. startPos: false,
  23578. endPos: false,
  23579. get snapLocationToGrid() {
  23580. return get_store_value(PlayerSettings.snapToGrid.store);
  23581. },
  23582. get sourceAttach() {
  23583. return get_store_value(PlayerSettings.attachTo.store);
  23584. },
  23585. get targetAttach() {
  23586. return get_store_value(PlayerSettings.stretchToAttach.store);
  23587. },
  23588. sourceAttachFound: false,
  23589. targetAttachFound: false,
  23590. get isActive() {
  23591. return InteractionManager.isLayerActive && game?.activeTool === "play-effect";
  23592. },
  23593. /**
  23594. * Opens the Sequencer Effects UI with the player tab open
  23595. */
  23596. show() {
  23597. return EffectsUIApp.show({ tab: "player" });
  23598. },
  23599. initialize() {
  23600. this.layer = canvas.sequencerInterfaceLayer;
  23601. },
  23602. tearDown() {
  23603. this._reset();
  23604. },
  23605. /**
  23606. * Mouse events
  23607. */
  23608. mouseLeftDown() {
  23609. this._evaluateStartPosition();
  23610. },
  23611. mouseLeftUp() {
  23612. if (!this.startPos)
  23613. return;
  23614. this._playEffect();
  23615. this.startPos = false;
  23616. this.endPos = false;
  23617. this.sourceAttachFound = false;
  23618. this.targetAttachFound = false;
  23619. },
  23620. mouseRightUp() {
  23621. this._reset();
  23622. },
  23623. mouseMove() {
  23624. this._evaluateCursorPosition();
  23625. if (InteractionManager.state.Dragging) {
  23626. this._evaluateEndPosition();
  23627. }
  23628. },
  23629. /**
  23630. * Hotkeys
  23631. */
  23632. playManyUp() {
  23633. this._playEffects();
  23634. this._reset();
  23635. },
  23636. /**
  23637. * Private methods
  23638. */
  23639. _evaluatePosition(attach = false) {
  23640. let position = get_mouse_position(this.snapLocationToGrid);
  23641. const attachToObject = attach ? get_closest_token(position, {
  23642. minimumDistance: canvas.grid.size
  23643. }) : false;
  23644. let attachFound = false;
  23645. if (attachToObject) {
  23646. attachFound = true;
  23647. position = get_object_position(attachToObject);
  23648. }
  23649. return [position, attachFound];
  23650. },
  23651. _evaluateCursorPosition() {
  23652. const attach = InteractionManager.state.Dragging ? this.targetAttach : this.sourceAttach;
  23653. [this.cursorPos] = this._evaluatePosition(attach);
  23654. },
  23655. _evaluateStartPosition() {
  23656. if (this.startPos)
  23657. return;
  23658. [this.startPos, this.sourceAttachFound] = this._evaluatePosition(
  23659. this.sourceAttach
  23660. );
  23661. },
  23662. _evaluateEndPosition() {
  23663. [this.endPos, this.targetAttachFound] = this._evaluatePosition(
  23664. this.targetAttach
  23665. );
  23666. },
  23667. _reset() {
  23668. if (!this.layer)
  23669. return;
  23670. this.startPos = false;
  23671. this.endPos = false;
  23672. this.sourceAttachFound = false;
  23673. this.targetAttachFound = false;
  23674. this.sequenceBuffer = [];
  23675. this._evaluateCursorPosition();
  23676. },
  23677. async _playEffect() {
  23678. const settings = foundry.utils.mergeObject(PlayerSettings.export(), {
  23679. ...InteractionManager.state,
  23680. startPos: this.startPos,
  23681. endPos: this.endPos
  23682. });
  23683. if (!settings.users.length || settings.users?.[0] === "all")
  23684. settings.users = [];
  23685. if (settings.file === "")
  23686. return;
  23687. if (!(Sequencer.Database.entryExists(settings.file) || await SequencerFileCache.srcExists(settings.file))) {
  23688. throw custom_error(
  23689. "Sequencer",
  23690. `Sequencer Player | Could not find file or database entry: ${settings.file}`
  23691. );
  23692. }
  23693. if (settings.preload) {
  23694. await Sequencer.Preloader.preloadForClients(settings.file);
  23695. }
  23696. const sequence = this.sequenceBuffer.length > 0 && this.playManySequenced ? this.sequenceBuffer[this.sequenceBuffer.length - 1] : new Sequence();
  23697. 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);
  23698. if (settings.repeat) {
  23699. effect.repeats(
  23700. settings.repetitions,
  23701. settings.repeatDelayMin,
  23702. settings.repeatDelayMax
  23703. );
  23704. }
  23705. if (settings.fadeIn > 0)
  23706. effect.fadeIn(settings.fadeIn);
  23707. if (settings.fadeOut > 0)
  23708. effect.fadeOut(settings.fadeOut);
  23709. const offsetData = settings.randomOffset ? {
  23710. offset: { x: settings.offsetX, y: settings.offsetY },
  23711. gridUnits: settings.offsetGridUnits
  23712. } : {};
  23713. const offsetSource = !settings.Dragging ? offsetData : {};
  23714. const attachToObject = settings.attachTo ? get_closest_token(settings.startPos, {
  23715. minimumDistance: canvas.grid.size
  23716. }) : false;
  23717. if (attachToObject) {
  23718. effect.attachTo(attachToObject, {
  23719. randomOffset: settings.randomOffset && !settings.Dragging ? settings.randomOffsetAmount : false,
  23720. ...offsetSource
  23721. });
  23722. } else {
  23723. effect.atLocation(settings.startPos, {
  23724. randomOffset: settings.randomOffset && !settings.Dragging ? settings.randomOffsetAmount : false,
  23725. ...offsetSource
  23726. });
  23727. }
  23728. if (settings.persist && settings.name && settings.name !== "" && settings.name !== "default" && settings.name !== "new") {
  23729. effect.name("Preset: " + settings.name);
  23730. }
  23731. if (settings.Dragging) {
  23732. if (settings.moveTowards) {
  23733. effect.moveTowards(settings.endPos, {
  23734. randomOffset: settings.randomOffset ? settings.randomOffsetAmount : false,
  23735. ...offsetData
  23736. });
  23737. if (settings.moveSpeed) {
  23738. effect.moveSpeed(settings.moveSpeed);
  23739. }
  23740. } else {
  23741. let target = settings.stretchToAttach ? get_closest_token(settings.endPos, {
  23742. minimumDistance: canvas.grid.size
  23743. }) : settings.endPos;
  23744. effect.stretchTo(target, {
  23745. attachTo: settings.stretchToAttach,
  23746. randomOffset: settings.randomOffset ? settings.randomOffsetAmount : false,
  23747. ...offsetData
  23748. });
  23749. }
  23750. }
  23751. if (!settings.Dragging || settings.Dragging && settings.moveTowards) {
  23752. effect.scale(settings.scale);
  23753. if (settings.scaleIn > 0)
  23754. effect.scaleIn(0, settings.scaleIn, { ease: "easeInOutSine" });
  23755. if (settings.scaleOut > 0)
  23756. effect.scaleOut(0, settings.scaleOut, { ease: "easeInOutSine" });
  23757. effect.randomRotation(settings.randomRotation);
  23758. }
  23759. if (this.playManySequenced) {
  23760. effect.waitUntilFinished();
  23761. }
  23762. if (!this.playManySequenced || this.sequenceBuffer.length === 0) {
  23763. this.sequenceBuffer.push(sequence);
  23764. }
  23765. if (!this.playMany && !this.playManySequenced)
  23766. this._playEffects();
  23767. },
  23768. _playEffects() {
  23769. this.sequenceBuffer.forEach((sequence) => sequence.play());
  23770. this.sequenceBuffer = [];
  23771. }
  23772. };
  23773. const SelectionManager = {
  23774. selectedEffect: false,
  23775. hoveredEffects: /* @__PURE__ */ new Set(),
  23776. suggestedProperties: false,
  23777. sourceOrTarget: false,
  23778. dragOffset: false,
  23779. hoveredEffectUI: false,
  23780. get snapToGrid() {
  23781. return get_store_value(PlayerSettings.snapToGrid.store);
  23782. },
  23783. set snapToGrid(bool) {
  23784. PlayerSettings.snapToGrid.store.set(bool);
  23785. },
  23786. set attachToTarget(bool) {
  23787. PlayerSettings.stretchToAttach.store.set(bool);
  23788. },
  23789. get attachToTarget() {
  23790. return get_store_value(PlayerSettings.stretchToAttach.store);
  23791. },
  23792. get isActive() {
  23793. return InteractionManager.isLayerActive && game?.activeTool === "select-effect";
  23794. },
  23795. get effects() {
  23796. return SequencerEffectManager.effects.filter(
  23797. (effect) => effect.userCanDelete
  23798. );
  23799. },
  23800. initialize() {
  23801. this.layer = canvas.sequencerInterfaceLayer;
  23802. },
  23803. tearDown() {
  23804. this._reset();
  23805. this.hoveredEffects = /* @__PURE__ */ new Set();
  23806. },
  23807. sourcePointSelected() {
  23808. this.sourceOrTarget = "source";
  23809. },
  23810. targetPointSelected() {
  23811. this.sourceOrTarget = "target";
  23812. },
  23813. /**
  23814. * Mouse Events
  23815. */
  23816. mouseLeftDown() {
  23817. if (!this.selectedEffect) {
  23818. return this._selectEffects();
  23819. }
  23820. if (!this.hoveredEffects.size) {
  23821. this._reset();
  23822. }
  23823. },
  23824. mouseRightDown() {
  23825. },
  23826. mouseLeftUp() {
  23827. if (!InteractionManager.state.Dragging) {
  23828. return this._selectEffects();
  23829. }
  23830. if (!InteractionManager.state.Dragging || !this.selectedEffect || !this.suggestedProperties)
  23831. return;
  23832. this._updateEffect();
  23833. },
  23834. mouseRightUp() {
  23835. InteractionManager.state.LeftMouseDown = false;
  23836. this.suggestedProperties = false;
  23837. this.sourceOrTarget = false;
  23838. this.dragOffset = false;
  23839. },
  23840. mouseMove() {
  23841. this._evaluateHoveredEffects();
  23842. if (InteractionManager.state.LeftMouseDown && !InteractionManager.state.RightMouseDown) {
  23843. this._evaluateEffectPositionUpdate();
  23844. }
  23845. },
  23846. /**
  23847. * Hotkeys
  23848. */
  23849. async delete() {
  23850. if (!this.selectedEffect)
  23851. return;
  23852. await SequencerEffectManager.endEffects({ effects: this.selectedEffect });
  23853. this.selectedEffect = false;
  23854. },
  23855. attachToTargetDown() {
  23856. if (InteractionManager.state.LeftMouseDown && !InteractionManager.state.RightMouseDown) {
  23857. this._evaluateEffectPositionUpdate();
  23858. }
  23859. },
  23860. /**
  23861. * Private methods
  23862. */
  23863. _selectEffects() {
  23864. this._reset();
  23865. if (!this.hoveredEffects.size)
  23866. return;
  23867. const firstElement = Array.from(this.hoveredEffects)[0];
  23868. this.selectedEffect = !firstElement.selected ? firstElement : false;
  23869. },
  23870. _evaluateHoveredEffects() {
  23871. const position = get_mouse_position();
  23872. this.hoveredEffects = this.effects.filter(
  23873. (effect) => effect.isPositionWithinBounds(position)
  23874. );
  23875. this.hoveredEffects.sort((a, b) => {
  23876. return a.data.layer !== b.data.zIndex ? a.data.zIndex - b.data.zIndex : a.data.layer - b.data.zIndex;
  23877. });
  23878. this.hoveredEffects = new Set(this.hoveredEffects);
  23879. },
  23880. _evaluateEffectPositionUpdate() {
  23881. if (!this.selectedEffect)
  23882. return;
  23883. if (this.selectedEffect.data.stretchTo && !this.sourceOrTarget) {
  23884. return;
  23885. }
  23886. let showCursor = false;
  23887. let showPoint = this.snapToGrid;
  23888. let position = get_mouse_position(this.snapToGrid);
  23889. if (!this.selectedEffect.data.stretchTo && !this.dragOffset) {
  23890. this.dragOffset = {
  23891. x: position.x - this.selectedEffect.position.x,
  23892. y: position.y - this.selectedEffect.position.y
  23893. };
  23894. }
  23895. if (this.attachToTarget) {
  23896. const obj = get_closest_token(position, {
  23897. minimumDistance: canvas.grid.size
  23898. });
  23899. if (obj) {
  23900. position = get_object_position(obj);
  23901. showCursor = true;
  23902. showPoint = false;
  23903. }
  23904. }
  23905. if (this.dragOffset && !showCursor && !this.snapToGrid) {
  23906. position.x -= this.dragOffset.x;
  23907. position.y -= this.dragOffset.y;
  23908. }
  23909. const color = (this.sourceOrTarget || "source") === "source" ? CONSTANTS.COLOR.PRIMARY : CONSTANTS.COLOR.SECONDARY;
  23910. this.suggestedProperties = {
  23911. position,
  23912. showCursor,
  23913. showPoint,
  23914. color
  23915. };
  23916. },
  23917. _updateEffect() {
  23918. if (!this.selectedEffect)
  23919. return;
  23920. const updates = {
  23921. attachTo: this.selectedEffect.data.attachTo,
  23922. stretchTo: this.selectedEffect.data.stretchTo
  23923. };
  23924. const obj = this.attachToTarget ? get_closest_token(this.suggestedProperties.position, {
  23925. minimumDistance: canvas.grid.size,
  23926. type: TokenDocument
  23927. }) : false;
  23928. let objUuid = obj ? get_object_identifier(obj) : false;
  23929. if (this.sourceOrTarget === "source") {
  23930. if (!updates.attachTo) {
  23931. updates.attachTo = {
  23932. active: false,
  23933. align: "center",
  23934. rotation: true,
  23935. bindVisibility: true,
  23936. bindAlpha: true
  23937. };
  23938. }
  23939. updates.attachTo = foundry.utils.mergeObject(updates.attachTo || {}, {
  23940. active: this.attachToTarget && !!objUuid
  23941. });
  23942. if (this.attachToTarget && objUuid) {
  23943. updates.source = objUuid;
  23944. } else {
  23945. updates["source"] = this.suggestedProperties.position;
  23946. }
  23947. } else if (this.sourceOrTarget === "target") {
  23948. updates.stretchTo.attachTo = this.attachToTarget && !!objUuid;
  23949. if (this.attachToTarget && objUuid) {
  23950. updates.target = objUuid;
  23951. } else {
  23952. updates["target"] = this.suggestedProperties.position;
  23953. }
  23954. } else {
  23955. updates["source"] = this.suggestedProperties.position;
  23956. }
  23957. this.selectedEffect.update(updates);
  23958. this.suggestedProperties = false;
  23959. this.sourceOrTarget = false;
  23960. this.dragOffset = false;
  23961. },
  23962. _reset() {
  23963. this.selectedEffect = false;
  23964. this.suggestedProperties = false;
  23965. this.sourceOrTarget = false;
  23966. this.dragOffset = false;
  23967. }
  23968. };
  23969. const EffectEntry_svelte_svelte_type_style_lang = "";
  23970. function create_fragment$c(ctx) {
  23971. let div1;
  23972. let button;
  23973. let t0;
  23974. let div0;
  23975. let t1_value = (
  23976. /*getEffectName*/
  23977. ctx[1](
  23978. /*effect*/
  23979. ctx[0]
  23980. ) + ""
  23981. );
  23982. let t1;
  23983. let mounted;
  23984. let dispose;
  23985. return {
  23986. c() {
  23987. div1 = element("div");
  23988. button = element("button");
  23989. button.innerHTML = `<i class="fas fa-times"></i>`;
  23990. t0 = space();
  23991. div0 = element("div");
  23992. t1 = text$1(t1_value);
  23993. attr(button, "class", "btn_end svelte-ese-1fyjvdk");
  23994. attr(button, "type", "button");
  23995. attr(div0, "class", "effect-text hover-text svelte-ese-1fyjvdk");
  23996. attr(div1, "class", "effect hover-highlight svelte-ese-1fyjvdk");
  23997. },
  23998. m(target, anchor) {
  23999. insert(target, div1, anchor);
  24000. append(div1, button);
  24001. append(div1, t0);
  24002. append(div1, div0);
  24003. append(div0, t1);
  24004. if (!mounted) {
  24005. dispose = [
  24006. listen(
  24007. button,
  24008. "click",
  24009. /*endEffect*/
  24010. ctx[4]
  24011. ),
  24012. listen(
  24013. div1,
  24014. "mouseleave",
  24015. /*mouseLeave*/
  24016. ctx[3]
  24017. ),
  24018. listen(
  24019. div1,
  24020. "mouseover",
  24021. /*mouseOver*/
  24022. ctx[2]
  24023. )
  24024. ];
  24025. mounted = true;
  24026. }
  24027. },
  24028. p(ctx2, [dirty]) {
  24029. if (dirty & /*effect*/
  24030. 1 && t1_value !== (t1_value = /*getEffectName*/
  24031. ctx2[1](
  24032. /*effect*/
  24033. ctx2[0]
  24034. ) + ""))
  24035. set_data(t1, t1_value);
  24036. },
  24037. i: noop,
  24038. o: noop,
  24039. d(detaching) {
  24040. if (detaching)
  24041. detach(div1);
  24042. mounted = false;
  24043. run_all(dispose);
  24044. }
  24045. };
  24046. }
  24047. function instance$c($$self, $$props, $$invalidate) {
  24048. let { effect } = $$props;
  24049. function getEffectName(effect2) {
  24050. let effectName = "Unknown effect";
  24051. if (effect2.data.file) {
  24052. effectName = effect2.data.file.split("\\").pop().split("/").pop();
  24053. } else if (effect2.data.text) {
  24054. effectName = "Text: " + effect2.data.text.text;
  24055. } else {
  24056. effectName = "Shape: " + effect2.data.shapes[0].type;
  24057. }
  24058. effectName = effect2.data.name ? `${effect2.data.name} (${effectName})` : effectName;
  24059. if (effect2.data.creatorUserId !== game.userId) {
  24060. let user_name = game.users.get(effect2.data.creatorUserId)?.name;
  24061. let formattedUsername = user_name ? localize("SEQUENCER.ManagerPlayersEffect", { user_name }) : localize("SEQUENCER.ManagerUnknownEffect");
  24062. effectName += ` (${formattedUsername})`;
  24063. }
  24064. return effectName;
  24065. }
  24066. function mouseOver() {
  24067. SelectionManager.hoveredEffectUI = effect;
  24068. }
  24069. function mouseLeave() {
  24070. SelectionManager.hoveredEffectUI = false;
  24071. }
  24072. function endEffect() {
  24073. SequencerEffectManager.endEffects({ effects: [effect] });
  24074. }
  24075. $$self.$$set = ($$props2) => {
  24076. if ("effect" in $$props2)
  24077. $$invalidate(0, effect = $$props2.effect);
  24078. };
  24079. return [effect, getEffectName, mouseOver, mouseLeave, endEffect];
  24080. }
  24081. class EffectEntry extends SvelteComponent {
  24082. constructor(options) {
  24083. super();
  24084. init(this, options, instance$c, create_fragment$c, safe_not_equal, { effect: 0 });
  24085. }
  24086. }
  24087. const SoundEntry_svelte_svelte_type_style_lang = "";
  24088. function create_fragment$b(ctx) {
  24089. let div1;
  24090. let button;
  24091. let t0;
  24092. let div0;
  24093. let t1_value = (
  24094. /*sound*/
  24095. ctx[0].src.split("/").slice(-1) + ""
  24096. );
  24097. let t1;
  24098. let mounted;
  24099. let dispose;
  24100. return {
  24101. c() {
  24102. div1 = element("div");
  24103. button = element("button");
  24104. button.innerHTML = `<i class="fas fa-times"></i>`;
  24105. t0 = space();
  24106. div0 = element("div");
  24107. t1 = text$1(t1_value);
  24108. attr(button, "class", "btn_end svelte-ese-1fyjvdk");
  24109. attr(button, "type", "button");
  24110. attr(div0, "class", "effect-text hover-text svelte-ese-1fyjvdk");
  24111. attr(div1, "class", "effect hover-highlight svelte-ese-1fyjvdk");
  24112. },
  24113. m(target, anchor) {
  24114. insert(target, div1, anchor);
  24115. append(div1, button);
  24116. append(div1, t0);
  24117. append(div1, div0);
  24118. append(div0, t1);
  24119. if (!mounted) {
  24120. dispose = listen(
  24121. button,
  24122. "click",
  24123. /*endSound*/
  24124. ctx[1]
  24125. );
  24126. mounted = true;
  24127. }
  24128. },
  24129. p(ctx2, [dirty]) {
  24130. if (dirty & /*sound*/
  24131. 1 && t1_value !== (t1_value = /*sound*/
  24132. ctx2[0].src.split("/").slice(-1) + ""))
  24133. set_data(t1, t1_value);
  24134. },
  24135. i: noop,
  24136. o: noop,
  24137. d(detaching) {
  24138. if (detaching)
  24139. detach(div1);
  24140. mounted = false;
  24141. dispose();
  24142. }
  24143. };
  24144. }
  24145. function instance$b($$self, $$props, $$invalidate) {
  24146. let { id } = $$props;
  24147. let { sound } = $$props;
  24148. function endSound() {
  24149. SequencerAudioHelper$1.stop([id], true);
  24150. }
  24151. $$self.$$set = ($$props2) => {
  24152. if ("id" in $$props2)
  24153. $$invalidate(2, id = $$props2.id);
  24154. if ("sound" in $$props2)
  24155. $$invalidate(0, sound = $$props2.sound);
  24156. };
  24157. return [sound, endSound, id];
  24158. }
  24159. class SoundEntry extends SvelteComponent {
  24160. constructor(options) {
  24161. super();
  24162. init(this, options, instance$b, create_fragment$b, safe_not_equal, { id: 2, sound: 0 });
  24163. }
  24164. }
  24165. const Manager_svelte_svelte_type_style_lang = "";
  24166. function get_each_context$3(ctx, list, i) {
  24167. const child_ctx = ctx.slice();
  24168. child_ctx[10] = list[i][0];
  24169. child_ctx[11] = list[i][1];
  24170. return child_ctx;
  24171. }
  24172. function get_each_context_1$1(ctx, list, i) {
  24173. const child_ctx = ctx.slice();
  24174. child_ctx[14] = list[i];
  24175. return child_ctx;
  24176. }
  24177. function get_each_context_2$1(ctx, list, i) {
  24178. const child_ctx = ctx.slice();
  24179. child_ctx[14] = list[i];
  24180. return child_ctx;
  24181. }
  24182. function create_if_block_5(ctx) {
  24183. let div;
  24184. let h2;
  24185. return {
  24186. c() {
  24187. div = element("div");
  24188. h2 = element("h2");
  24189. h2.textContent = `${localize("SEQUENCER.Manager.NothingPlaying")}`;
  24190. attr(div, "class", "no-effects");
  24191. },
  24192. m(target, anchor) {
  24193. insert(target, div, anchor);
  24194. append(div, h2);
  24195. },
  24196. p: noop,
  24197. d(detaching) {
  24198. if (detaching)
  24199. detach(div);
  24200. }
  24201. };
  24202. }
  24203. function create_if_block_1(ctx) {
  24204. let button;
  24205. let t1;
  24206. let div;
  24207. let t2;
  24208. let t3;
  24209. let current;
  24210. let mounted;
  24211. let dispose;
  24212. let if_block0 = (
  24213. /*persistentEffects*/
  24214. ctx[2].length && create_if_block_4(ctx)
  24215. );
  24216. let if_block1 = (
  24217. /*temporaryEffects*/
  24218. ctx[1].length && /*persistentEffects*/
  24219. ctx[2].length && create_if_block_3()
  24220. );
  24221. let if_block2 = (
  24222. /*temporaryEffects*/
  24223. ctx[1].length && create_if_block_2(ctx)
  24224. );
  24225. return {
  24226. c() {
  24227. button = element("button");
  24228. button.textContent = `${localize("SEQUENCER.Manager.EndAllEffects")}`;
  24229. t1 = space();
  24230. div = element("div");
  24231. if (if_block0)
  24232. if_block0.c();
  24233. t2 = space();
  24234. if (if_block1)
  24235. if_block1.c();
  24236. t3 = space();
  24237. if (if_block2)
  24238. if_block2.c();
  24239. attr(button, "class", "w-100 end-all-effects mb-2 svelte-ese-nc5j73");
  24240. attr(button, "type", "button");
  24241. attr(div, "class", "effects svelte-ese-nc5j73");
  24242. },
  24243. m(target, anchor) {
  24244. insert(target, button, anchor);
  24245. insert(target, t1, anchor);
  24246. insert(target, div, anchor);
  24247. if (if_block0)
  24248. if_block0.m(div, null);
  24249. append(div, t2);
  24250. if (if_block1)
  24251. if_block1.m(div, null);
  24252. append(div, t3);
  24253. if (if_block2)
  24254. if_block2.m(div, null);
  24255. current = true;
  24256. if (!mounted) {
  24257. dispose = listen(
  24258. button,
  24259. "click",
  24260. /*endAllEffects*/
  24261. ctx[6]
  24262. );
  24263. mounted = true;
  24264. }
  24265. },
  24266. p(ctx2, dirty) {
  24267. if (
  24268. /*persistentEffects*/
  24269. ctx2[2].length
  24270. ) {
  24271. if (if_block0) {
  24272. if_block0.p(ctx2, dirty);
  24273. if (dirty & /*persistentEffects*/
  24274. 4) {
  24275. transition_in(if_block0, 1);
  24276. }
  24277. } else {
  24278. if_block0 = create_if_block_4(ctx2);
  24279. if_block0.c();
  24280. transition_in(if_block0, 1);
  24281. if_block0.m(div, t2);
  24282. }
  24283. } else if (if_block0) {
  24284. group_outros();
  24285. transition_out(if_block0, 1, 1, () => {
  24286. if_block0 = null;
  24287. });
  24288. check_outros();
  24289. }
  24290. if (
  24291. /*temporaryEffects*/
  24292. ctx2[1].length && /*persistentEffects*/
  24293. ctx2[2].length
  24294. ) {
  24295. if (if_block1)
  24296. ;
  24297. else {
  24298. if_block1 = create_if_block_3();
  24299. if_block1.c();
  24300. if_block1.m(div, t3);
  24301. }
  24302. } else if (if_block1) {
  24303. if_block1.d(1);
  24304. if_block1 = null;
  24305. }
  24306. if (
  24307. /*temporaryEffects*/
  24308. ctx2[1].length
  24309. ) {
  24310. if (if_block2) {
  24311. if_block2.p(ctx2, dirty);
  24312. if (dirty & /*temporaryEffects*/
  24313. 2) {
  24314. transition_in(if_block2, 1);
  24315. }
  24316. } else {
  24317. if_block2 = create_if_block_2(ctx2);
  24318. if_block2.c();
  24319. transition_in(if_block2, 1);
  24320. if_block2.m(div, null);
  24321. }
  24322. } else if (if_block2) {
  24323. group_outros();
  24324. transition_out(if_block2, 1, 1, () => {
  24325. if_block2 = null;
  24326. });
  24327. check_outros();
  24328. }
  24329. },
  24330. i(local) {
  24331. if (current)
  24332. return;
  24333. transition_in(if_block0);
  24334. transition_in(if_block2);
  24335. current = true;
  24336. },
  24337. o(local) {
  24338. transition_out(if_block0);
  24339. transition_out(if_block2);
  24340. current = false;
  24341. },
  24342. d(detaching) {
  24343. if (detaching)
  24344. detach(button);
  24345. if (detaching)
  24346. detach(t1);
  24347. if (detaching)
  24348. detach(div);
  24349. if (if_block0)
  24350. if_block0.d();
  24351. if (if_block1)
  24352. if_block1.d();
  24353. if (if_block2)
  24354. if_block2.d();
  24355. mounted = false;
  24356. dispose();
  24357. }
  24358. };
  24359. }
  24360. function create_if_block_4(ctx) {
  24361. let h2;
  24362. let t1;
  24363. let div;
  24364. let each_blocks = [];
  24365. let each_1_lookup = /* @__PURE__ */ new Map();
  24366. let current;
  24367. let each_value_2 = (
  24368. /*persistentEffects*/
  24369. ctx[2]
  24370. );
  24371. const get_key = (ctx2) => (
  24372. /*effect*/
  24373. ctx2[14].id
  24374. );
  24375. for (let i = 0; i < each_value_2.length; i += 1) {
  24376. let child_ctx = get_each_context_2$1(ctx, each_value_2, i);
  24377. let key = get_key(child_ctx);
  24378. each_1_lookup.set(key, each_blocks[i] = create_each_block_2$1(key, child_ctx));
  24379. }
  24380. return {
  24381. c() {
  24382. h2 = element("h2");
  24383. h2.textContent = `${localize("SEQUENCER.Manager.PersistentEffects")}`;
  24384. t1 = space();
  24385. div = element("div");
  24386. for (let i = 0; i < each_blocks.length; i += 1) {
  24387. each_blocks[i].c();
  24388. }
  24389. },
  24390. m(target, anchor) {
  24391. insert(target, h2, anchor);
  24392. insert(target, t1, anchor);
  24393. insert(target, div, anchor);
  24394. for (let i = 0; i < each_blocks.length; i += 1) {
  24395. each_blocks[i].m(div, null);
  24396. }
  24397. current = true;
  24398. },
  24399. p(ctx2, dirty) {
  24400. if (dirty & /*persistentEffects*/
  24401. 4) {
  24402. each_value_2 = /*persistentEffects*/
  24403. ctx2[2];
  24404. group_outros();
  24405. 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);
  24406. check_outros();
  24407. }
  24408. },
  24409. i(local) {
  24410. if (current)
  24411. return;
  24412. for (let i = 0; i < each_value_2.length; i += 1) {
  24413. transition_in(each_blocks[i]);
  24414. }
  24415. current = true;
  24416. },
  24417. o(local) {
  24418. for (let i = 0; i < each_blocks.length; i += 1) {
  24419. transition_out(each_blocks[i]);
  24420. }
  24421. current = false;
  24422. },
  24423. d(detaching) {
  24424. if (detaching)
  24425. detach(h2);
  24426. if (detaching)
  24427. detach(t1);
  24428. if (detaching)
  24429. detach(div);
  24430. for (let i = 0; i < each_blocks.length; i += 1) {
  24431. each_blocks[i].d();
  24432. }
  24433. }
  24434. };
  24435. }
  24436. function create_each_block_2$1(key_1, ctx) {
  24437. let first;
  24438. let effectentry;
  24439. let current;
  24440. effectentry = new EffectEntry({ props: { effect: (
  24441. /*effect*/
  24442. ctx[14]
  24443. ) } });
  24444. return {
  24445. key: key_1,
  24446. first: null,
  24447. c() {
  24448. first = empty();
  24449. create_component(effectentry.$$.fragment);
  24450. this.first = first;
  24451. },
  24452. m(target, anchor) {
  24453. insert(target, first, anchor);
  24454. mount_component(effectentry, target, anchor);
  24455. current = true;
  24456. },
  24457. p(new_ctx, dirty) {
  24458. ctx = new_ctx;
  24459. const effectentry_changes = {};
  24460. if (dirty & /*persistentEffects*/
  24461. 4)
  24462. effectentry_changes.effect = /*effect*/
  24463. ctx[14];
  24464. effectentry.$set(effectentry_changes);
  24465. },
  24466. i(local) {
  24467. if (current)
  24468. return;
  24469. transition_in(effectentry.$$.fragment, local);
  24470. current = true;
  24471. },
  24472. o(local) {
  24473. transition_out(effectentry.$$.fragment, local);
  24474. current = false;
  24475. },
  24476. d(detaching) {
  24477. if (detaching)
  24478. detach(first);
  24479. destroy_component(effectentry, detaching);
  24480. }
  24481. };
  24482. }
  24483. function create_if_block_3(ctx) {
  24484. let hr;
  24485. return {
  24486. c() {
  24487. hr = element("hr");
  24488. },
  24489. m(target, anchor) {
  24490. insert(target, hr, anchor);
  24491. },
  24492. d(detaching) {
  24493. if (detaching)
  24494. detach(hr);
  24495. }
  24496. };
  24497. }
  24498. function create_if_block_2(ctx) {
  24499. let h2;
  24500. let t1;
  24501. let div;
  24502. let each_blocks = [];
  24503. let each_1_lookup = /* @__PURE__ */ new Map();
  24504. let current;
  24505. let each_value_1 = (
  24506. /*temporaryEffects*/
  24507. ctx[1]
  24508. );
  24509. const get_key = (ctx2) => (
  24510. /*effect*/
  24511. ctx2[14].id
  24512. );
  24513. for (let i = 0; i < each_value_1.length; i += 1) {
  24514. let child_ctx = get_each_context_1$1(ctx, each_value_1, i);
  24515. let key = get_key(child_ctx);
  24516. each_1_lookup.set(key, each_blocks[i] = create_each_block_1$1(key, child_ctx));
  24517. }
  24518. return {
  24519. c() {
  24520. h2 = element("h2");
  24521. h2.textContent = `${localize("SEQUENCER.Manager.TemporaryEffects")}`;
  24522. t1 = space();
  24523. div = element("div");
  24524. for (let i = 0; i < each_blocks.length; i += 1) {
  24525. each_blocks[i].c();
  24526. }
  24527. },
  24528. m(target, anchor) {
  24529. insert(target, h2, anchor);
  24530. insert(target, t1, anchor);
  24531. insert(target, div, anchor);
  24532. for (let i = 0; i < each_blocks.length; i += 1) {
  24533. each_blocks[i].m(div, null);
  24534. }
  24535. current = true;
  24536. },
  24537. p(ctx2, dirty) {
  24538. if (dirty & /*temporaryEffects*/
  24539. 2) {
  24540. each_value_1 = /*temporaryEffects*/
  24541. ctx2[1];
  24542. group_outros();
  24543. 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);
  24544. check_outros();
  24545. }
  24546. },
  24547. i(local) {
  24548. if (current)
  24549. return;
  24550. for (let i = 0; i < each_value_1.length; i += 1) {
  24551. transition_in(each_blocks[i]);
  24552. }
  24553. current = true;
  24554. },
  24555. o(local) {
  24556. for (let i = 0; i < each_blocks.length; i += 1) {
  24557. transition_out(each_blocks[i]);
  24558. }
  24559. current = false;
  24560. },
  24561. d(detaching) {
  24562. if (detaching)
  24563. detach(h2);
  24564. if (detaching)
  24565. detach(t1);
  24566. if (detaching)
  24567. detach(div);
  24568. for (let i = 0; i < each_blocks.length; i += 1) {
  24569. each_blocks[i].d();
  24570. }
  24571. }
  24572. };
  24573. }
  24574. function create_each_block_1$1(key_1, ctx) {
  24575. let first;
  24576. let effectentry;
  24577. let current;
  24578. effectentry = new EffectEntry({ props: { effect: (
  24579. /*effect*/
  24580. ctx[14]
  24581. ) } });
  24582. return {
  24583. key: key_1,
  24584. first: null,
  24585. c() {
  24586. first = empty();
  24587. create_component(effectentry.$$.fragment);
  24588. this.first = first;
  24589. },
  24590. m(target, anchor) {
  24591. insert(target, first, anchor);
  24592. mount_component(effectentry, target, anchor);
  24593. current = true;
  24594. },
  24595. p(new_ctx, dirty) {
  24596. ctx = new_ctx;
  24597. const effectentry_changes = {};
  24598. if (dirty & /*temporaryEffects*/
  24599. 2)
  24600. effectentry_changes.effect = /*effect*/
  24601. ctx[14];
  24602. effectentry.$set(effectentry_changes);
  24603. },
  24604. i(local) {
  24605. if (current)
  24606. return;
  24607. transition_in(effectentry.$$.fragment, local);
  24608. current = true;
  24609. },
  24610. o(local) {
  24611. transition_out(effectentry.$$.fragment, local);
  24612. current = false;
  24613. },
  24614. d(detaching) {
  24615. if (detaching)
  24616. detach(first);
  24617. destroy_component(effectentry, detaching);
  24618. }
  24619. };
  24620. }
  24621. function create_if_block$2(ctx) {
  24622. let button;
  24623. let t1;
  24624. let div1;
  24625. let h2;
  24626. let t3;
  24627. let div0;
  24628. let each_blocks = [];
  24629. let each_1_lookup = /* @__PURE__ */ new Map();
  24630. let current;
  24631. let mounted;
  24632. let dispose;
  24633. let each_value = (
  24634. /*sounds*/
  24635. ctx[3]
  24636. );
  24637. const get_key = (ctx2) => (
  24638. /*id*/
  24639. ctx2[10]
  24640. );
  24641. for (let i = 0; i < each_value.length; i += 1) {
  24642. let child_ctx = get_each_context$3(ctx, each_value, i);
  24643. let key = get_key(child_ctx);
  24644. each_1_lookup.set(key, each_blocks[i] = create_each_block$3(key, child_ctx));
  24645. }
  24646. return {
  24647. c() {
  24648. button = element("button");
  24649. button.textContent = `${localize("SEQUENCER.Manager.EndAllSounds")}`;
  24650. t1 = space();
  24651. div1 = element("div");
  24652. h2 = element("h2");
  24653. h2.textContent = `${localize("SEQUENCER.Manager.Sounds")}`;
  24654. t3 = space();
  24655. div0 = element("div");
  24656. for (let i = 0; i < each_blocks.length; i += 1) {
  24657. each_blocks[i].c();
  24658. }
  24659. attr(button, "class", "w-100 end-all-effects mb-2 svelte-ese-nc5j73");
  24660. attr(button, "type", "button");
  24661. },
  24662. m(target, anchor) {
  24663. insert(target, button, anchor);
  24664. insert(target, t1, anchor);
  24665. insert(target, div1, anchor);
  24666. append(div1, h2);
  24667. append(div1, t3);
  24668. append(div1, div0);
  24669. for (let i = 0; i < each_blocks.length; i += 1) {
  24670. each_blocks[i].m(div0, null);
  24671. }
  24672. current = true;
  24673. if (!mounted) {
  24674. dispose = listen(
  24675. button,
  24676. "click",
  24677. /*endAllSounds*/
  24678. ctx[7]
  24679. );
  24680. mounted = true;
  24681. }
  24682. },
  24683. p(ctx2, dirty) {
  24684. if (dirty & /*sounds*/
  24685. 8) {
  24686. each_value = /*sounds*/
  24687. ctx2[3];
  24688. group_outros();
  24689. 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);
  24690. check_outros();
  24691. }
  24692. },
  24693. i(local) {
  24694. if (current)
  24695. return;
  24696. for (let i = 0; i < each_value.length; i += 1) {
  24697. transition_in(each_blocks[i]);
  24698. }
  24699. current = true;
  24700. },
  24701. o(local) {
  24702. for (let i = 0; i < each_blocks.length; i += 1) {
  24703. transition_out(each_blocks[i]);
  24704. }
  24705. current = false;
  24706. },
  24707. d(detaching) {
  24708. if (detaching)
  24709. detach(button);
  24710. if (detaching)
  24711. detach(t1);
  24712. if (detaching)
  24713. detach(div1);
  24714. for (let i = 0; i < each_blocks.length; i += 1) {
  24715. each_blocks[i].d();
  24716. }
  24717. mounted = false;
  24718. dispose();
  24719. }
  24720. };
  24721. }
  24722. function create_each_block$3(key_1, ctx) {
  24723. let first;
  24724. let soundentry;
  24725. let current;
  24726. soundentry = new SoundEntry({
  24727. props: {
  24728. id: (
  24729. /*id*/
  24730. ctx[10]
  24731. ),
  24732. sound: (
  24733. /*sound*/
  24734. ctx[11]
  24735. )
  24736. }
  24737. });
  24738. return {
  24739. key: key_1,
  24740. first: null,
  24741. c() {
  24742. first = empty();
  24743. create_component(soundentry.$$.fragment);
  24744. this.first = first;
  24745. },
  24746. m(target, anchor) {
  24747. insert(target, first, anchor);
  24748. mount_component(soundentry, target, anchor);
  24749. current = true;
  24750. },
  24751. p(new_ctx, dirty) {
  24752. ctx = new_ctx;
  24753. const soundentry_changes = {};
  24754. if (dirty & /*sounds*/
  24755. 8)
  24756. soundentry_changes.id = /*id*/
  24757. ctx[10];
  24758. if (dirty & /*sounds*/
  24759. 8)
  24760. soundentry_changes.sound = /*sound*/
  24761. ctx[11];
  24762. soundentry.$set(soundentry_changes);
  24763. },
  24764. i(local) {
  24765. if (current)
  24766. return;
  24767. transition_in(soundentry.$$.fragment, local);
  24768. current = true;
  24769. },
  24770. o(local) {
  24771. transition_out(soundentry.$$.fragment, local);
  24772. current = false;
  24773. },
  24774. d(detaching) {
  24775. if (detaching)
  24776. detach(first);
  24777. destroy_component(soundentry, detaching);
  24778. }
  24779. };
  24780. }
  24781. function create_fragment$a(ctx) {
  24782. let div;
  24783. let t0;
  24784. let t1;
  24785. let current;
  24786. let if_block0 = !/*effects*/
  24787. ctx[0].length && !/*sounds*/
  24788. ctx[3].length && create_if_block_5();
  24789. let if_block1 = (
  24790. /*effects*/
  24791. ctx[0].length && create_if_block_1(ctx)
  24792. );
  24793. let if_block2 = (
  24794. /*sounds*/
  24795. ctx[3].length && create_if_block$2(ctx)
  24796. );
  24797. return {
  24798. c() {
  24799. div = element("div");
  24800. if (if_block0)
  24801. if_block0.c();
  24802. t0 = space();
  24803. if (if_block1)
  24804. if_block1.c();
  24805. t1 = space();
  24806. if (if_block2)
  24807. if_block2.c();
  24808. attr(div, "class", "effects-container svelte-ese-nc5j73");
  24809. },
  24810. m(target, anchor) {
  24811. insert(target, div, anchor);
  24812. if (if_block0)
  24813. if_block0.m(div, null);
  24814. append(div, t0);
  24815. if (if_block1)
  24816. if_block1.m(div, null);
  24817. append(div, t1);
  24818. if (if_block2)
  24819. if_block2.m(div, null);
  24820. current = true;
  24821. },
  24822. p(ctx2, [dirty]) {
  24823. if (!/*effects*/
  24824. ctx2[0].length && !/*sounds*/
  24825. ctx2[3].length) {
  24826. if (if_block0) {
  24827. if_block0.p(ctx2, dirty);
  24828. } else {
  24829. if_block0 = create_if_block_5();
  24830. if_block0.c();
  24831. if_block0.m(div, t0);
  24832. }
  24833. } else if (if_block0) {
  24834. if_block0.d(1);
  24835. if_block0 = null;
  24836. }
  24837. if (
  24838. /*effects*/
  24839. ctx2[0].length
  24840. ) {
  24841. if (if_block1) {
  24842. if_block1.p(ctx2, dirty);
  24843. if (dirty & /*effects*/
  24844. 1) {
  24845. transition_in(if_block1, 1);
  24846. }
  24847. } else {
  24848. if_block1 = create_if_block_1(ctx2);
  24849. if_block1.c();
  24850. transition_in(if_block1, 1);
  24851. if_block1.m(div, t1);
  24852. }
  24853. } else if (if_block1) {
  24854. group_outros();
  24855. transition_out(if_block1, 1, 1, () => {
  24856. if_block1 = null;
  24857. });
  24858. check_outros();
  24859. }
  24860. if (
  24861. /*sounds*/
  24862. ctx2[3].length
  24863. ) {
  24864. if (if_block2) {
  24865. if_block2.p(ctx2, dirty);
  24866. if (dirty & /*sounds*/
  24867. 8) {
  24868. transition_in(if_block2, 1);
  24869. }
  24870. } else {
  24871. if_block2 = create_if_block$2(ctx2);
  24872. if_block2.c();
  24873. transition_in(if_block2, 1);
  24874. if_block2.m(div, null);
  24875. }
  24876. } else if (if_block2) {
  24877. group_outros();
  24878. transition_out(if_block2, 1, 1, () => {
  24879. if_block2 = null;
  24880. });
  24881. check_outros();
  24882. }
  24883. },
  24884. i(local) {
  24885. if (current)
  24886. return;
  24887. transition_in(if_block1);
  24888. transition_in(if_block2);
  24889. current = true;
  24890. },
  24891. o(local) {
  24892. transition_out(if_block1);
  24893. transition_out(if_block2);
  24894. current = false;
  24895. },
  24896. d(detaching) {
  24897. if (detaching)
  24898. detach(div);
  24899. if (if_block0)
  24900. if_block0.d();
  24901. if (if_block1)
  24902. if_block1.d();
  24903. if (if_block2)
  24904. if_block2.d();
  24905. }
  24906. };
  24907. }
  24908. function instance$a($$self, $$props, $$invalidate) {
  24909. let effects;
  24910. let sounds;
  24911. let persistentEffects;
  24912. let temporaryEffects;
  24913. let $RunningSounds;
  24914. let $VisibleEffects;
  24915. const VisibleEffects = SequenceManager.VisibleEffects;
  24916. component_subscribe($$self, VisibleEffects, (value) => $$invalidate(9, $VisibleEffects = value));
  24917. const RunningSounds = SequenceManager.RunningSounds;
  24918. component_subscribe($$self, RunningSounds, (value) => $$invalidate(8, $RunningSounds = value));
  24919. function endAllEffects() {
  24920. Sequencer.EffectManager.endEffects({
  24921. effects: effects.filter((effect) => effect.userCanDelete && !effect.data.private)
  24922. });
  24923. }
  24924. function endAllSounds() {
  24925. SequencerAudioHelper.stop(RunningSounds.keys());
  24926. }
  24927. $$self.$$.update = () => {
  24928. if ($$self.$$.dirty & /*$VisibleEffects*/
  24929. 512) {
  24930. $$invalidate(0, effects = Object.values($VisibleEffects));
  24931. }
  24932. if ($$self.$$.dirty & /*$RunningSounds*/
  24933. 256) {
  24934. $$invalidate(3, sounds = Object.entries($RunningSounds));
  24935. }
  24936. if ($$self.$$.dirty & /*effects*/
  24937. 1) {
  24938. $$invalidate(2, persistentEffects = effects.filter((effect) => effect.data.persist));
  24939. }
  24940. if ($$self.$$.dirty & /*effects*/
  24941. 1) {
  24942. $$invalidate(1, temporaryEffects = effects.filter((effect) => !effect.data.persist));
  24943. }
  24944. };
  24945. return [
  24946. effects,
  24947. temporaryEffects,
  24948. persistentEffects,
  24949. sounds,
  24950. VisibleEffects,
  24951. RunningSounds,
  24952. endAllEffects,
  24953. endAllSounds,
  24954. $RunningSounds,
  24955. $VisibleEffects
  24956. ];
  24957. }
  24958. class Manager extends SvelteComponent {
  24959. constructor(options) {
  24960. super();
  24961. init(this, options, instance$a, create_fragment$a, safe_not_equal, {});
  24962. }
  24963. }
  24964. const SliderInput_svelte_svelte_type_style_lang = "";
  24965. function create_fragment$9(ctx) {
  24966. let div;
  24967. let label;
  24968. let t0_value = localize(
  24969. /*setting*/
  24970. ctx[0].label
  24971. ) + "";
  24972. let t0;
  24973. let t1;
  24974. let input0;
  24975. let t2;
  24976. let input1;
  24977. let applyStyles_action;
  24978. let mounted;
  24979. let dispose;
  24980. return {
  24981. c() {
  24982. div = element("div");
  24983. label = element("label");
  24984. t0 = text$1(t0_value);
  24985. t1 = space();
  24986. input0 = element("input");
  24987. t2 = space();
  24988. input1 = element("input");
  24989. attr(label, "class", "svelte-ese-x3192w");
  24990. input0.disabled = /*$isLocked*/
  24991. ctx[6];
  24992. attr(
  24993. input0,
  24994. "max",
  24995. /*max*/
  24996. ctx[2]
  24997. );
  24998. attr(
  24999. input0,
  25000. "min",
  25001. /*min*/
  25002. ctx[1]
  25003. );
  25004. attr(
  25005. input0,
  25006. "step",
  25007. /*step*/
  25008. ctx[4]
  25009. );
  25010. attr(input0, "type", "range");
  25011. attr(input0, "class", "svelte-ese-x3192w");
  25012. input1.disabled = /*$isLocked*/
  25013. ctx[6];
  25014. attr(
  25015. input1,
  25016. "max",
  25017. /*maxInput*/
  25018. ctx[3]
  25019. );
  25020. attr(
  25021. input1,
  25022. "min",
  25023. /*min*/
  25024. ctx[1]
  25025. );
  25026. input1.required = true;
  25027. attr(
  25028. input1,
  25029. "step",
  25030. /*step*/
  25031. ctx[4]
  25032. );
  25033. attr(input1, "type", "number");
  25034. attr(input1, "class", "svelte-ese-x3192w");
  25035. set_style(div, "display", "flex");
  25036. set_style(div, "align-items", "center");
  25037. },
  25038. m(target, anchor) {
  25039. insert(target, div, anchor);
  25040. append(div, label);
  25041. append(label, t0);
  25042. append(div, t1);
  25043. append(div, input0);
  25044. set_input_value(
  25045. input0,
  25046. /*$store*/
  25047. ctx[7]
  25048. );
  25049. append(div, t2);
  25050. append(div, input1);
  25051. set_input_value(
  25052. input1,
  25053. /*$store*/
  25054. ctx[7]
  25055. );
  25056. if (!mounted) {
  25057. dispose = [
  25058. listen(
  25059. input0,
  25060. "change",
  25061. /*input0_change_input_handler*/
  25062. ctx[11]
  25063. ),
  25064. listen(
  25065. input0,
  25066. "input",
  25067. /*input0_change_input_handler*/
  25068. ctx[11]
  25069. ),
  25070. listen(
  25071. input1,
  25072. "input",
  25073. /*input1_input_handler*/
  25074. ctx[12]
  25075. ),
  25076. action_destroyer(applyStyles_action = applyStyles.call(
  25077. null,
  25078. div,
  25079. /*styles*/
  25080. ctx[5]
  25081. ))
  25082. ];
  25083. mounted = true;
  25084. }
  25085. },
  25086. p(ctx2, [dirty]) {
  25087. if (dirty & /*setting*/
  25088. 1 && t0_value !== (t0_value = localize(
  25089. /*setting*/
  25090. ctx2[0].label
  25091. ) + ""))
  25092. set_data(t0, t0_value);
  25093. if (dirty & /*$isLocked*/
  25094. 64) {
  25095. input0.disabled = /*$isLocked*/
  25096. ctx2[6];
  25097. }
  25098. if (dirty & /*max*/
  25099. 4) {
  25100. attr(
  25101. input0,
  25102. "max",
  25103. /*max*/
  25104. ctx2[2]
  25105. );
  25106. }
  25107. if (dirty & /*min*/
  25108. 2) {
  25109. attr(
  25110. input0,
  25111. "min",
  25112. /*min*/
  25113. ctx2[1]
  25114. );
  25115. }
  25116. if (dirty & /*step*/
  25117. 16) {
  25118. attr(
  25119. input0,
  25120. "step",
  25121. /*step*/
  25122. ctx2[4]
  25123. );
  25124. }
  25125. if (dirty & /*$store*/
  25126. 128) {
  25127. set_input_value(
  25128. input0,
  25129. /*$store*/
  25130. ctx2[7]
  25131. );
  25132. }
  25133. if (dirty & /*$isLocked*/
  25134. 64) {
  25135. input1.disabled = /*$isLocked*/
  25136. ctx2[6];
  25137. }
  25138. if (dirty & /*maxInput*/
  25139. 8) {
  25140. attr(
  25141. input1,
  25142. "max",
  25143. /*maxInput*/
  25144. ctx2[3]
  25145. );
  25146. }
  25147. if (dirty & /*min*/
  25148. 2) {
  25149. attr(
  25150. input1,
  25151. "min",
  25152. /*min*/
  25153. ctx2[1]
  25154. );
  25155. }
  25156. if (dirty & /*step*/
  25157. 16) {
  25158. attr(
  25159. input1,
  25160. "step",
  25161. /*step*/
  25162. ctx2[4]
  25163. );
  25164. }
  25165. if (dirty & /*$store*/
  25166. 128 && to_number(input1.value) !== /*$store*/
  25167. ctx2[7]) {
  25168. set_input_value(
  25169. input1,
  25170. /*$store*/
  25171. ctx2[7]
  25172. );
  25173. }
  25174. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
  25175. 32)
  25176. applyStyles_action.update.call(
  25177. null,
  25178. /*styles*/
  25179. ctx2[5]
  25180. );
  25181. },
  25182. i: noop,
  25183. o: noop,
  25184. d(detaching) {
  25185. if (detaching)
  25186. detach(div);
  25187. mounted = false;
  25188. run_all(dispose);
  25189. }
  25190. };
  25191. }
  25192. function instance$9($$self, $$props, $$invalidate) {
  25193. let $isLocked;
  25194. let $store;
  25195. let { setting } = $$props;
  25196. let { lock = false } = $$props;
  25197. let { min = 0.1 } = $$props;
  25198. let { max = 2 } = $$props;
  25199. let { maxInput = Infinity } = $$props;
  25200. let { step = 0.01 } = $$props;
  25201. let { styles = {} } = $$props;
  25202. const store = setting.store;
  25203. component_subscribe($$self, store, (value) => $$invalidate(7, $store = value));
  25204. const isLocked = lock ? lock.store : writable$1(false);
  25205. component_subscribe($$self, isLocked, (value) => $$invalidate(6, $isLocked = value));
  25206. function input0_change_input_handler() {
  25207. $store = to_number(this.value);
  25208. store.set($store);
  25209. }
  25210. function input1_input_handler() {
  25211. $store = to_number(this.value);
  25212. store.set($store);
  25213. }
  25214. $$self.$$set = ($$props2) => {
  25215. if ("setting" in $$props2)
  25216. $$invalidate(0, setting = $$props2.setting);
  25217. if ("lock" in $$props2)
  25218. $$invalidate(10, lock = $$props2.lock);
  25219. if ("min" in $$props2)
  25220. $$invalidate(1, min = $$props2.min);
  25221. if ("max" in $$props2)
  25222. $$invalidate(2, max = $$props2.max);
  25223. if ("maxInput" in $$props2)
  25224. $$invalidate(3, maxInput = $$props2.maxInput);
  25225. if ("step" in $$props2)
  25226. $$invalidate(4, step = $$props2.step);
  25227. if ("styles" in $$props2)
  25228. $$invalidate(5, styles = $$props2.styles);
  25229. };
  25230. return [
  25231. setting,
  25232. min,
  25233. max,
  25234. maxInput,
  25235. step,
  25236. styles,
  25237. $isLocked,
  25238. $store,
  25239. store,
  25240. isLocked,
  25241. lock,
  25242. input0_change_input_handler,
  25243. input1_input_handler
  25244. ];
  25245. }
  25246. class SliderInput extends SvelteComponent {
  25247. constructor(options) {
  25248. super();
  25249. init(this, options, instance$9, create_fragment$9, safe_not_equal, {
  25250. setting: 0,
  25251. lock: 10,
  25252. min: 1,
  25253. max: 2,
  25254. maxInput: 3,
  25255. step: 4,
  25256. styles: 5
  25257. });
  25258. }
  25259. }
  25260. function create_fragment$8(ctx) {
  25261. let div;
  25262. let input;
  25263. let input_disabled_value;
  25264. let t0;
  25265. let label_1;
  25266. let t1_value = localize(
  25267. /*setting*/
  25268. ctx[0].label
  25269. ) + "";
  25270. let t1;
  25271. let applyStyles_action;
  25272. let mounted;
  25273. let dispose;
  25274. return {
  25275. c() {
  25276. div = element("div");
  25277. input = element("input");
  25278. t0 = space();
  25279. label_1 = element("label");
  25280. t1 = text$1(t1_value);
  25281. attr(
  25282. input,
  25283. "id",
  25284. /*id*/
  25285. ctx[5]
  25286. );
  25287. input.disabled = input_disabled_value = /*$isLocked*/
  25288. ctx[3] !== /*inverse*/
  25289. ctx[1];
  25290. set_style(input, "height", "15px");
  25291. attr(input, "type", "checkbox");
  25292. attr(
  25293. label_1,
  25294. "for",
  25295. /*id*/
  25296. ctx[5]
  25297. );
  25298. set_style(div, "display", "flex");
  25299. set_style(div, "align-items", "center");
  25300. },
  25301. m(target, anchor) {
  25302. insert(target, div, anchor);
  25303. append(div, input);
  25304. input.checked = /*$store*/
  25305. ctx[4];
  25306. append(div, t0);
  25307. append(div, label_1);
  25308. append(label_1, t1);
  25309. if (!mounted) {
  25310. dispose = [
  25311. listen(
  25312. input,
  25313. "change",
  25314. /*input_change_handler*/
  25315. ctx[10]
  25316. ),
  25317. action_destroyer(applyStyles_action = applyStyles.call(
  25318. null,
  25319. div,
  25320. /*styles*/
  25321. ctx[2]
  25322. ))
  25323. ];
  25324. mounted = true;
  25325. }
  25326. },
  25327. p(ctx2, [dirty]) {
  25328. if (dirty & /*$isLocked, inverse*/
  25329. 10 && input_disabled_value !== (input_disabled_value = /*$isLocked*/
  25330. ctx2[3] !== /*inverse*/
  25331. ctx2[1])) {
  25332. input.disabled = input_disabled_value;
  25333. }
  25334. if (dirty & /*$store*/
  25335. 16) {
  25336. input.checked = /*$store*/
  25337. ctx2[4];
  25338. }
  25339. if (dirty & /*setting*/
  25340. 1 && t1_value !== (t1_value = localize(
  25341. /*setting*/
  25342. ctx2[0].label
  25343. ) + ""))
  25344. set_data(t1, t1_value);
  25345. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
  25346. 4)
  25347. applyStyles_action.update.call(
  25348. null,
  25349. /*styles*/
  25350. ctx2[2]
  25351. );
  25352. },
  25353. i: noop,
  25354. o: noop,
  25355. d(detaching) {
  25356. if (detaching)
  25357. detach(div);
  25358. mounted = false;
  25359. run_all(dispose);
  25360. }
  25361. };
  25362. }
  25363. function instance$8($$self, $$props, $$invalidate) {
  25364. let $store;
  25365. let $isLocked;
  25366. let { setting } = $$props;
  25367. let { label = false } = $$props;
  25368. let { lock = false } = $$props;
  25369. let { inverse = false } = $$props;
  25370. let { styles = {} } = $$props;
  25371. const id = "sequencer-input-" + randomID();
  25372. const store = setting.store;
  25373. component_subscribe($$self, store, (value) => $$invalidate(4, $store = value));
  25374. const isLocked = lock ? lock.store : writable$1(inverse);
  25375. component_subscribe($$self, isLocked, (value) => $$invalidate(3, $isLocked = value));
  25376. function input_change_handler() {
  25377. $store = this.checked;
  25378. store.set($store);
  25379. }
  25380. $$self.$$set = ($$props2) => {
  25381. if ("setting" in $$props2)
  25382. $$invalidate(0, setting = $$props2.setting);
  25383. if ("label" in $$props2)
  25384. $$invalidate(8, label = $$props2.label);
  25385. if ("lock" in $$props2)
  25386. $$invalidate(9, lock = $$props2.lock);
  25387. if ("inverse" in $$props2)
  25388. $$invalidate(1, inverse = $$props2.inverse);
  25389. if ("styles" in $$props2)
  25390. $$invalidate(2, styles = $$props2.styles);
  25391. };
  25392. $$self.$$.update = () => {
  25393. if ($$self.$$.dirty & /*$isLocked, inverse*/
  25394. 10) {
  25395. {
  25396. if ($isLocked !== inverse) {
  25397. set_store_value(store, $store = false, $store);
  25398. }
  25399. }
  25400. }
  25401. };
  25402. return [
  25403. setting,
  25404. inverse,
  25405. styles,
  25406. $isLocked,
  25407. $store,
  25408. id,
  25409. store,
  25410. isLocked,
  25411. label,
  25412. lock,
  25413. input_change_handler
  25414. ];
  25415. }
  25416. class Checkbox extends SvelteComponent {
  25417. constructor(options) {
  25418. super();
  25419. init(this, options, instance$8, create_fragment$8, safe_not_equal, {
  25420. setting: 0,
  25421. label: 8,
  25422. lock: 9,
  25423. inverse: 1,
  25424. styles: 2
  25425. });
  25426. }
  25427. }
  25428. const NumberInput_svelte_svelte_type_style_lang = "";
  25429. function create_if_block$1(ctx) {
  25430. let label;
  25431. let t_value = localize(
  25432. /*setting*/
  25433. ctx[0].label
  25434. ) + "";
  25435. let t;
  25436. return {
  25437. c() {
  25438. label = element("label");
  25439. t = text$1(t_value);
  25440. attr(
  25441. label,
  25442. "for",
  25443. /*id*/
  25444. ctx[5]
  25445. );
  25446. attr(label, "class", "svelte-ese-ywsxq0");
  25447. },
  25448. m(target, anchor) {
  25449. insert(target, label, anchor);
  25450. append(label, t);
  25451. },
  25452. p(ctx2, dirty) {
  25453. if (dirty & /*setting*/
  25454. 1 && t_value !== (t_value = localize(
  25455. /*setting*/
  25456. ctx2[0].label
  25457. ) + ""))
  25458. set_data(t, t_value);
  25459. },
  25460. d(detaching) {
  25461. if (detaching)
  25462. detach(label);
  25463. }
  25464. };
  25465. }
  25466. function create_fragment$7(ctx) {
  25467. let div;
  25468. let show_if = localize(
  25469. /*setting*/
  25470. ctx[0].label
  25471. );
  25472. let t;
  25473. let input;
  25474. let input_disabled_value;
  25475. let applyStyles_action;
  25476. let mounted;
  25477. let dispose;
  25478. let if_block = show_if && create_if_block$1(ctx);
  25479. return {
  25480. c() {
  25481. div = element("div");
  25482. if (if_block)
  25483. if_block.c();
  25484. t = space();
  25485. input = element("input");
  25486. attr(
  25487. input,
  25488. "id",
  25489. /*id*/
  25490. ctx[5]
  25491. );
  25492. attr(input, "type", "number");
  25493. input.disabled = input_disabled_value = /*$isLocked*/
  25494. ctx[3] !== /*inverse*/
  25495. ctx[1];
  25496. attr(input, "class", "svelte-ese-ywsxq0");
  25497. set_style(div, "display", "flex");
  25498. set_style(div, "align-items", "center");
  25499. },
  25500. m(target, anchor) {
  25501. insert(target, div, anchor);
  25502. if (if_block)
  25503. if_block.m(div, null);
  25504. append(div, t);
  25505. append(div, input);
  25506. set_input_value(
  25507. input,
  25508. /*$store*/
  25509. ctx[4]
  25510. );
  25511. if (!mounted) {
  25512. dispose = [
  25513. listen(
  25514. input,
  25515. "input",
  25516. /*input_input_handler*/
  25517. ctx[9]
  25518. ),
  25519. listen(
  25520. input,
  25521. "change",
  25522. /*change_handler*/
  25523. ctx[10]
  25524. ),
  25525. action_destroyer(applyStyles_action = applyStyles.call(
  25526. null,
  25527. div,
  25528. /*styles*/
  25529. ctx[2]
  25530. ))
  25531. ];
  25532. mounted = true;
  25533. }
  25534. },
  25535. p(ctx2, [dirty]) {
  25536. if (dirty & /*setting*/
  25537. 1)
  25538. show_if = localize(
  25539. /*setting*/
  25540. ctx2[0].label
  25541. );
  25542. if (show_if) {
  25543. if (if_block) {
  25544. if_block.p(ctx2, dirty);
  25545. } else {
  25546. if_block = create_if_block$1(ctx2);
  25547. if_block.c();
  25548. if_block.m(div, t);
  25549. }
  25550. } else if (if_block) {
  25551. if_block.d(1);
  25552. if_block = null;
  25553. }
  25554. if (dirty & /*$isLocked, inverse*/
  25555. 10 && input_disabled_value !== (input_disabled_value = /*$isLocked*/
  25556. ctx2[3] !== /*inverse*/
  25557. ctx2[1])) {
  25558. input.disabled = input_disabled_value;
  25559. }
  25560. if (dirty & /*$store*/
  25561. 16 && to_number(input.value) !== /*$store*/
  25562. ctx2[4]) {
  25563. set_input_value(
  25564. input,
  25565. /*$store*/
  25566. ctx2[4]
  25567. );
  25568. }
  25569. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
  25570. 4)
  25571. applyStyles_action.update.call(
  25572. null,
  25573. /*styles*/
  25574. ctx2[2]
  25575. );
  25576. },
  25577. i: noop,
  25578. o: noop,
  25579. d(detaching) {
  25580. if (detaching)
  25581. detach(div);
  25582. if (if_block)
  25583. if_block.d();
  25584. mounted = false;
  25585. run_all(dispose);
  25586. }
  25587. };
  25588. }
  25589. function instance$7($$self, $$props, $$invalidate) {
  25590. let $isLocked;
  25591. let $store;
  25592. let { setting } = $$props;
  25593. let { lock = false } = $$props;
  25594. let { inverse = false } = $$props;
  25595. let { styles = {} } = $$props;
  25596. const id = "sequencer-input-" + randomID();
  25597. const store = setting.store;
  25598. component_subscribe($$self, store, (value) => $$invalidate(4, $store = value));
  25599. const isLocked = lock ? lock.store : writable$1(inverse);
  25600. component_subscribe($$self, isLocked, (value) => $$invalidate(3, $isLocked = value));
  25601. function input_input_handler() {
  25602. $store = to_number(this.value);
  25603. store.set($store);
  25604. }
  25605. const change_handler = () => {
  25606. if ($store === null)
  25607. set_store_value(store, $store = 0, $store);
  25608. };
  25609. $$self.$$set = ($$props2) => {
  25610. if ("setting" in $$props2)
  25611. $$invalidate(0, setting = $$props2.setting);
  25612. if ("lock" in $$props2)
  25613. $$invalidate(8, lock = $$props2.lock);
  25614. if ("inverse" in $$props2)
  25615. $$invalidate(1, inverse = $$props2.inverse);
  25616. if ("styles" in $$props2)
  25617. $$invalidate(2, styles = $$props2.styles);
  25618. };
  25619. return [
  25620. setting,
  25621. inverse,
  25622. styles,
  25623. $isLocked,
  25624. $store,
  25625. id,
  25626. store,
  25627. isLocked,
  25628. lock,
  25629. input_input_handler,
  25630. change_handler
  25631. ];
  25632. }
  25633. class NumberInput extends SvelteComponent {
  25634. constructor(options) {
  25635. super();
  25636. init(this, options, instance$7, create_fragment$7, safe_not_equal, {
  25637. setting: 0,
  25638. lock: 8,
  25639. inverse: 1,
  25640. styles: 2
  25641. });
  25642. }
  25643. }
  25644. const SwitchToggle_svelte_svelte_type_style_lang = "";
  25645. function create_fragment$6(ctx) {
  25646. let div2;
  25647. let div0;
  25648. let span0;
  25649. let t0_value = localize(
  25650. /*setting*/
  25651. ctx[0].label_off
  25652. ) + "";
  25653. let t0;
  25654. let t1;
  25655. let div1;
  25656. let span1;
  25657. let t2_value = localize(
  25658. /*setting*/
  25659. ctx[0].label_on
  25660. ) + "";
  25661. let t2;
  25662. let applyStyles_action;
  25663. let mounted;
  25664. let dispose;
  25665. return {
  25666. c() {
  25667. div2 = element("div");
  25668. div0 = element("div");
  25669. span0 = element("span");
  25670. t0 = text$1(t0_value);
  25671. t1 = space();
  25672. div1 = element("div");
  25673. span1 = element("span");
  25674. t2 = text$1(t2_value);
  25675. attr(div0, "class", "first svelte-ese-o0yoxs");
  25676. toggle_class(div0, "active", !/*$store*/
  25677. ctx[2]);
  25678. attr(div1, "class", "second svelte-ese-o0yoxs");
  25679. toggle_class(
  25680. div1,
  25681. "active",
  25682. /*$store*/
  25683. ctx[2]
  25684. );
  25685. set_style(div2, "display", "flex");
  25686. set_style(div2, "align-items", "center");
  25687. attr(div2, "class", "svelte-ese-o0yoxs");
  25688. },
  25689. m(target, anchor) {
  25690. insert(target, div2, anchor);
  25691. append(div2, div0);
  25692. append(div0, span0);
  25693. append(span0, t0);
  25694. append(div2, t1);
  25695. append(div2, div1);
  25696. append(div1, span1);
  25697. append(span1, t2);
  25698. if (!mounted) {
  25699. dispose = [
  25700. listen(
  25701. div0,
  25702. "click",
  25703. /*click_handler*/
  25704. ctx[5]
  25705. ),
  25706. listen(
  25707. div1,
  25708. "click",
  25709. /*click_handler_1*/
  25710. ctx[6]
  25711. ),
  25712. action_destroyer(applyStyles_action = applyStyles.call(
  25713. null,
  25714. div2,
  25715. /*finalStyles*/
  25716. ctx[1]
  25717. ))
  25718. ];
  25719. mounted = true;
  25720. }
  25721. },
  25722. p(ctx2, [dirty]) {
  25723. if (dirty & /*setting*/
  25724. 1 && t0_value !== (t0_value = localize(
  25725. /*setting*/
  25726. ctx2[0].label_off
  25727. ) + ""))
  25728. set_data(t0, t0_value);
  25729. if (dirty & /*$store*/
  25730. 4) {
  25731. toggle_class(div0, "active", !/*$store*/
  25732. ctx2[2]);
  25733. }
  25734. if (dirty & /*setting*/
  25735. 1 && t2_value !== (t2_value = localize(
  25736. /*setting*/
  25737. ctx2[0].label_on
  25738. ) + ""))
  25739. set_data(t2, t2_value);
  25740. if (dirty & /*$store*/
  25741. 4) {
  25742. toggle_class(
  25743. div1,
  25744. "active",
  25745. /*$store*/
  25746. ctx2[2]
  25747. );
  25748. }
  25749. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*finalStyles*/
  25750. 2)
  25751. applyStyles_action.update.call(
  25752. null,
  25753. /*finalStyles*/
  25754. ctx2[1]
  25755. );
  25756. },
  25757. i: noop,
  25758. o: noop,
  25759. d(detaching) {
  25760. if (detaching)
  25761. detach(div2);
  25762. mounted = false;
  25763. run_all(dispose);
  25764. }
  25765. };
  25766. }
  25767. const width = 5.75;
  25768. function instance$6($$self, $$props, $$invalidate) {
  25769. let finalStyles;
  25770. let $store;
  25771. let { setting } = $$props;
  25772. let { styles = {} } = $$props;
  25773. const store = setting.store;
  25774. component_subscribe($$self, store, (value) => $$invalidate(2, $store = value));
  25775. const click_handler = () => {
  25776. set_store_value(store, $store = false, $store);
  25777. };
  25778. const click_handler_1 = () => {
  25779. set_store_value(store, $store = true, $store);
  25780. };
  25781. $$self.$$set = ($$props2) => {
  25782. if ("setting" in $$props2)
  25783. $$invalidate(0, setting = $$props2.setting);
  25784. if ("styles" in $$props2)
  25785. $$invalidate(4, styles = $$props2.styles);
  25786. };
  25787. $$self.$$.update = () => {
  25788. if ($$self.$$.dirty & /*styles*/
  25789. 16) {
  25790. $$invalidate(1, finalStyles = {
  25791. ...styles,
  25792. "--switch-width": `${width}em`,
  25793. "--half-switch-width": `${width - 1.5}em`,
  25794. "--half-switch-width-on": `${width - 1.7}em`
  25795. });
  25796. }
  25797. };
  25798. return [setting, finalStyles, $store, store, styles, click_handler, click_handler_1];
  25799. }
  25800. class SwitchToggle extends SvelteComponent {
  25801. constructor(options) {
  25802. super();
  25803. init(this, options, instance$6, create_fragment$6, safe_not_equal, { setting: 0, styles: 4 });
  25804. }
  25805. }
  25806. const Player_svelte_svelte_type_style_lang = "";
  25807. function get_each_context$2(ctx, list, i) {
  25808. const child_ctx = ctx.slice();
  25809. child_ctx[20] = list[i];
  25810. return child_ctx;
  25811. }
  25812. function get_each_context_1(ctx, list, i) {
  25813. const child_ctx = ctx.slice();
  25814. child_ctx[23] = list[i];
  25815. return child_ctx;
  25816. }
  25817. function get_each_context_2(ctx, list, i) {
  25818. const child_ctx = ctx.slice();
  25819. child_ctx[26] = list[i];
  25820. return child_ctx;
  25821. }
  25822. function create_each_block_2(ctx) {
  25823. let option;
  25824. let t_value = (
  25825. /*suggestion*/
  25826. ctx[26] + ""
  25827. );
  25828. let t;
  25829. let option_value_value;
  25830. return {
  25831. c() {
  25832. option = element("option");
  25833. t = text$1(t_value);
  25834. option.__value = option_value_value = /*suggestion*/
  25835. ctx[26];
  25836. option.value = option.__value;
  25837. },
  25838. m(target, anchor) {
  25839. insert(target, option, anchor);
  25840. append(option, t);
  25841. },
  25842. p(ctx2, dirty) {
  25843. if (dirty & /*suggestions*/
  25844. 2 && t_value !== (t_value = /*suggestion*/
  25845. ctx2[26] + ""))
  25846. set_data(t, t_value);
  25847. if (dirty & /*suggestions*/
  25848. 2 && option_value_value !== (option_value_value = /*suggestion*/
  25849. ctx2[26])) {
  25850. option.__value = option_value_value;
  25851. option.value = option.__value;
  25852. }
  25853. },
  25854. d(detaching) {
  25855. if (detaching)
  25856. detach(option);
  25857. }
  25858. };
  25859. }
  25860. function create_each_block_1(key_1, ctx) {
  25861. let option;
  25862. let t_value = (
  25863. /*user*/
  25864. ctx[23].name + ""
  25865. );
  25866. let t;
  25867. return {
  25868. key: key_1,
  25869. first: null,
  25870. c() {
  25871. option = element("option");
  25872. t = text$1(t_value);
  25873. option.__value = /*user*/
  25874. ctx[23].id;
  25875. option.value = option.__value;
  25876. this.first = option;
  25877. },
  25878. m(target, anchor) {
  25879. insert(target, option, anchor);
  25880. append(option, t);
  25881. },
  25882. p(new_ctx, dirty) {
  25883. ctx = new_ctx;
  25884. },
  25885. d(detaching) {
  25886. if (detaching)
  25887. detach(option);
  25888. }
  25889. };
  25890. }
  25891. function create_each_block$2(ctx) {
  25892. let option;
  25893. let t_value = (
  25894. /*preset*/
  25895. ctx[20] + ""
  25896. );
  25897. let t;
  25898. return {
  25899. c() {
  25900. option = element("option");
  25901. t = text$1(t_value);
  25902. option.__value = /*preset*/
  25903. ctx[20];
  25904. option.value = option.__value;
  25905. },
  25906. m(target, anchor) {
  25907. insert(target, option, anchor);
  25908. append(option, t);
  25909. },
  25910. p: noop,
  25911. d(detaching) {
  25912. if (detaching)
  25913. detach(option);
  25914. }
  25915. };
  25916. }
  25917. function create_fragment$5(ctx) {
  25918. let div16;
  25919. let fieldset0;
  25920. let legend0;
  25921. let t1;
  25922. let button0;
  25923. let t3;
  25924. let div0;
  25925. let input;
  25926. let t4;
  25927. let button1;
  25928. let t5;
  25929. let datalist;
  25930. let t6;
  25931. let div1;
  25932. let label;
  25933. let t8;
  25934. let select0;
  25935. let option0;
  25936. let each_blocks_1 = [];
  25937. let each1_lookup = /* @__PURE__ */ new Map();
  25938. let t10;
  25939. let div4;
  25940. let div2;
  25941. let t12;
  25942. let div3;
  25943. let select1;
  25944. let option1;
  25945. let t14;
  25946. let button2;
  25947. let t15;
  25948. let button3;
  25949. let i2;
  25950. let button3_disabled_value;
  25951. let t16;
  25952. let button4;
  25953. let i3;
  25954. let button4_disabled_value;
  25955. let t17;
  25956. let fieldset1;
  25957. let legend1;
  25958. let t19;
  25959. let div11;
  25960. let sliderinput0;
  25961. let t20;
  25962. let numberinput0;
  25963. let t21;
  25964. let numberinput1;
  25965. let t22;
  25966. let div5;
  25967. let t23;
  25968. let sliderinput1;
  25969. let t24;
  25970. let div6;
  25971. let t25;
  25972. let checkbox0;
  25973. let t26;
  25974. let div7;
  25975. let t27;
  25976. let div8;
  25977. let t28;
  25978. let checkbox1;
  25979. let t29;
  25980. let checkbox2;
  25981. let t30;
  25982. let checkbox3;
  25983. let t31;
  25984. let checkbox4;
  25985. let t32;
  25986. let div9;
  25987. let t33;
  25988. let numberinput2;
  25989. let t34;
  25990. let numberinput3;
  25991. let t35;
  25992. let checkbox5;
  25993. let t36;
  25994. let numberinput4;
  25995. let t37;
  25996. let checkbox6;
  25997. let t38;
  25998. let div10;
  25999. let t39;
  26000. let fieldset2;
  26001. let legend2;
  26002. let t41;
  26003. let div15;
  26004. let numberinput5;
  26005. let t42;
  26006. let numberinput6;
  26007. let t43;
  26008. let div12;
  26009. let t44;
  26010. let checkbox7;
  26011. let t45;
  26012. let numberinput7;
  26013. let t46;
  26014. let numberinput8;
  26015. let t47;
  26016. let numberinput9;
  26017. let t48;
  26018. let div13;
  26019. let t49;
  26020. let switchtoggle;
  26021. let t50;
  26022. let numberinput10;
  26023. let t51;
  26024. let div14;
  26025. let t52;
  26026. let checkbox8;
  26027. let t53;
  26028. let checkbox9;
  26029. let t54;
  26030. let checkbox10;
  26031. let t55;
  26032. let checkbox11;
  26033. let t56;
  26034. let checkbox12;
  26035. let current;
  26036. let mounted;
  26037. let dispose;
  26038. let each_value_2 = (
  26039. /*suggestions*/
  26040. ctx[1]
  26041. );
  26042. let each_blocks_2 = [];
  26043. for (let i = 0; i < each_value_2.length; i += 1) {
  26044. each_blocks_2[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i));
  26045. }
  26046. let each_value_1 = (
  26047. /*users*/
  26048. ctx[5]
  26049. );
  26050. const get_key = (ctx2) => (
  26051. /*user*/
  26052. ctx2[23].id
  26053. );
  26054. for (let i = 0; i < each_value_1.length; i += 1) {
  26055. let child_ctx = get_each_context_1(ctx, each_value_1, i);
  26056. let key = get_key(child_ctx);
  26057. each1_lookup.set(key, each_blocks_1[i] = create_each_block_1(key, child_ctx));
  26058. }
  26059. let each_value = (
  26060. /*presets*/
  26061. ctx[8]
  26062. );
  26063. let each_blocks = [];
  26064. for (let i = 0; i < each_value.length; i += 1) {
  26065. each_blocks[i] = create_each_block$2(get_each_context$2(ctx, each_value, i));
  26066. }
  26067. sliderinput0 = new SliderInput({
  26068. props: {
  26069. setting: PlayerSettings.scale,
  26070. styles: { "grid-column": `1 / 5` }
  26071. }
  26072. });
  26073. numberinput0 = new NumberInput({
  26074. props: {
  26075. setting: PlayerSettings.scaleIn,
  26076. styles: { "grid-column": `1 / 3` }
  26077. }
  26078. });
  26079. numberinput1 = new NumberInput({
  26080. props: {
  26081. setting: PlayerSettings.scaleOut,
  26082. styles: { "grid-column": `3 / 5` }
  26083. }
  26084. });
  26085. sliderinput1 = new SliderInput({
  26086. props: {
  26087. setting: PlayerSettings.rotation,
  26088. lock: PlayerSettings.randomRotation,
  26089. min: "-180",
  26090. max: "180",
  26091. styles: { "grid-column": `1 / 5` }
  26092. }
  26093. });
  26094. checkbox0 = new Checkbox({
  26095. props: {
  26096. setting: PlayerSettings.randomRotation,
  26097. styles: { "grid-column": `2 / 4` }
  26098. }
  26099. });
  26100. checkbox1 = new Checkbox({
  26101. props: {
  26102. setting: PlayerSettings.mirrorX,
  26103. styles: { "grid-column": `1 / 3` }
  26104. }
  26105. });
  26106. checkbox2 = new Checkbox({
  26107. props: {
  26108. setting: PlayerSettings.mirrorY,
  26109. styles: { "grid-column": `3 / 5` }
  26110. }
  26111. });
  26112. checkbox3 = new Checkbox({
  26113. props: {
  26114. setting: PlayerSettings.randomMirrorX,
  26115. styles: { "grid-column": `1 / 3` },
  26116. lock: PlayerSettings.mirrorX,
  26117. inverse: true
  26118. }
  26119. });
  26120. checkbox4 = new Checkbox({
  26121. props: {
  26122. setting: PlayerSettings.randomMirrorY,
  26123. styles: { "grid-column": `3 / 5` },
  26124. lock: PlayerSettings.mirrorY,
  26125. inverse: true
  26126. }
  26127. });
  26128. numberinput2 = new NumberInput({
  26129. props: {
  26130. setting: PlayerSettings.offsetX,
  26131. lock: PlayerSettings.randomOffset,
  26132. styles: { "grid-column": `1 / 3` }
  26133. }
  26134. });
  26135. numberinput3 = new NumberInput({
  26136. props: {
  26137. setting: PlayerSettings.offsetY,
  26138. lock: PlayerSettings.randomOffset,
  26139. styles: { "grid-column": `3 / 5` }
  26140. }
  26141. });
  26142. checkbox5 = new Checkbox({
  26143. props: {
  26144. setting: PlayerSettings.randomOffset,
  26145. styles: { "grid-column": `1 / 3` }
  26146. }
  26147. });
  26148. numberinput4 = new NumberInput({
  26149. props: {
  26150. setting: PlayerSettings.randomOffsetAmount,
  26151. lock: PlayerSettings.randomOffset,
  26152. inverse: true,
  26153. styles: { "grid-column": `3 / 5` }
  26154. }
  26155. });
  26156. checkbox6 = new Checkbox({
  26157. props: {
  26158. setting: PlayerSettings.offsetGridUnits,
  26159. styles: { "grid-column": `2 / 5` }
  26160. }
  26161. });
  26162. numberinput5 = new NumberInput({
  26163. props: {
  26164. setting: PlayerSettings.fadeIn,
  26165. styles: { "grid-column": `1 / 3` }
  26166. }
  26167. });
  26168. numberinput6 = new NumberInput({
  26169. props: {
  26170. setting: PlayerSettings.fadeOut,
  26171. styles: { "grid-column": `3 / 5` }
  26172. }
  26173. });
  26174. checkbox7 = new Checkbox({
  26175. props: {
  26176. setting: PlayerSettings.repeat,
  26177. styles: { "grid-column": `1 / 3` }
  26178. }
  26179. });
  26180. numberinput7 = new NumberInput({
  26181. props: {
  26182. setting: PlayerSettings.repetitions,
  26183. lock: PlayerSettings.repeat,
  26184. inverse: true,
  26185. styles: { "grid-column": `3 / 5` }
  26186. }
  26187. });
  26188. numberinput8 = new NumberInput({
  26189. props: {
  26190. setting: PlayerSettings.repeatDelayMin,
  26191. lock: PlayerSettings.repeat,
  26192. inverse: true,
  26193. styles: { "grid-column": `3 / 4` }
  26194. }
  26195. });
  26196. numberinput9 = new NumberInput({
  26197. props: {
  26198. setting: PlayerSettings.repeatDelayMax,
  26199. lock: PlayerSettings.repeat,
  26200. inverse: true,
  26201. styles: { "grid-column": `4 / 5` }
  26202. }
  26203. });
  26204. switchtoggle = new SwitchToggle({
  26205. props: {
  26206. setting: PlayerSettings.moveTowards,
  26207. styles: { "grid-column": `1 / 3` }
  26208. }
  26209. });
  26210. numberinput10 = new NumberInput({
  26211. props: {
  26212. setting: PlayerSettings.moveSpeed,
  26213. lock: PlayerSettings.moveTowards,
  26214. inverse: true,
  26215. styles: { "grid-column": `3 / 5` }
  26216. }
  26217. });
  26218. checkbox8 = new Checkbox({
  26219. props: {
  26220. setting: PlayerSettings.attachTo,
  26221. styles: { "grid-column": `1 / 5` }
  26222. }
  26223. });
  26224. checkbox9 = new Checkbox({
  26225. props: {
  26226. setting: PlayerSettings.stretchToAttach,
  26227. styles: { "grid-column": `1 / 5` }
  26228. }
  26229. });
  26230. checkbox10 = new Checkbox({
  26231. props: {
  26232. setting: PlayerSettings.snapToGrid,
  26233. styles: { "grid-column": `1 / 5` }
  26234. }
  26235. });
  26236. checkbox11 = new Checkbox({
  26237. props: {
  26238. setting: PlayerSettings.persist,
  26239. styles: { "grid-column": `1 / 5` }
  26240. }
  26241. });
  26242. checkbox12 = new Checkbox({
  26243. props: {
  26244. setting: PlayerSettings.belowTokens,
  26245. styles: { "grid-column": `1 / 5` }
  26246. }
  26247. });
  26248. return {
  26249. c() {
  26250. div16 = element("div");
  26251. fieldset0 = element("fieldset");
  26252. legend0 = element("legend");
  26253. legend0.textContent = "Effect";
  26254. t1 = space();
  26255. button0 = element("button");
  26256. button0.textContent = `${localize("SEQUENCER.Player.SwitchToLayer")}`;
  26257. t3 = space();
  26258. div0 = element("div");
  26259. input = element("input");
  26260. t4 = space();
  26261. button1 = element("button");
  26262. button1.innerHTML = `<i class="fas fa-file-import svelte-ese-1ipnpu1"></i>`;
  26263. t5 = space();
  26264. datalist = element("datalist");
  26265. for (let i = 0; i < each_blocks_2.length; i += 1) {
  26266. each_blocks_2[i].c();
  26267. }
  26268. t6 = space();
  26269. div1 = element("div");
  26270. label = element("label");
  26271. label.textContent = "Play for users:";
  26272. t8 = space();
  26273. select0 = element("select");
  26274. option0 = element("option");
  26275. option0.textContent = `${localize("SEQUENCER.Player.AllUsers")}`;
  26276. for (let i = 0; i < each_blocks_1.length; i += 1) {
  26277. each_blocks_1[i].c();
  26278. }
  26279. t10 = space();
  26280. div4 = element("div");
  26281. div2 = element("div");
  26282. div2.textContent = `${localize("SEQUENCER.Player.Presets")}`;
  26283. t12 = space();
  26284. div3 = element("div");
  26285. select1 = element("select");
  26286. option1 = element("option");
  26287. option1.textContent = `${localize("SEQUENCER.Player.PresetsDefault")}`;
  26288. for (let i = 0; i < each_blocks.length; i += 1) {
  26289. each_blocks[i].c();
  26290. }
  26291. t14 = space();
  26292. button2 = element("button");
  26293. button2.innerHTML = `<i class="fas fa-download svelte-ese-1ipnpu1"></i>`;
  26294. t15 = space();
  26295. button3 = element("button");
  26296. i2 = element("i");
  26297. t16 = space();
  26298. button4 = element("button");
  26299. i3 = element("i");
  26300. t17 = space();
  26301. fieldset1 = element("fieldset");
  26302. legend1 = element("legend");
  26303. legend1.textContent = "Transform";
  26304. t19 = space();
  26305. div11 = element("div");
  26306. create_component(sliderinput0.$$.fragment);
  26307. t20 = space();
  26308. create_component(numberinput0.$$.fragment);
  26309. t21 = space();
  26310. create_component(numberinput1.$$.fragment);
  26311. t22 = space();
  26312. div5 = element("div");
  26313. t23 = space();
  26314. create_component(sliderinput1.$$.fragment);
  26315. t24 = space();
  26316. div6 = element("div");
  26317. t25 = space();
  26318. create_component(checkbox0.$$.fragment);
  26319. t26 = space();
  26320. div7 = element("div");
  26321. t27 = space();
  26322. div8 = element("div");
  26323. t28 = space();
  26324. create_component(checkbox1.$$.fragment);
  26325. t29 = space();
  26326. create_component(checkbox2.$$.fragment);
  26327. t30 = space();
  26328. create_component(checkbox3.$$.fragment);
  26329. t31 = space();
  26330. create_component(checkbox4.$$.fragment);
  26331. t32 = space();
  26332. div9 = element("div");
  26333. t33 = space();
  26334. create_component(numberinput2.$$.fragment);
  26335. t34 = space();
  26336. create_component(numberinput3.$$.fragment);
  26337. t35 = space();
  26338. create_component(checkbox5.$$.fragment);
  26339. t36 = space();
  26340. create_component(numberinput4.$$.fragment);
  26341. t37 = space();
  26342. create_component(checkbox6.$$.fragment);
  26343. t38 = space();
  26344. div10 = element("div");
  26345. t39 = space();
  26346. fieldset2 = element("fieldset");
  26347. legend2 = element("legend");
  26348. legend2.textContent = "Behavior";
  26349. t41 = space();
  26350. div15 = element("div");
  26351. create_component(numberinput5.$$.fragment);
  26352. t42 = space();
  26353. create_component(numberinput6.$$.fragment);
  26354. t43 = space();
  26355. div12 = element("div");
  26356. t44 = space();
  26357. create_component(checkbox7.$$.fragment);
  26358. t45 = space();
  26359. create_component(numberinput7.$$.fragment);
  26360. t46 = space();
  26361. create_component(numberinput8.$$.fragment);
  26362. t47 = space();
  26363. create_component(numberinput9.$$.fragment);
  26364. t48 = space();
  26365. div13 = element("div");
  26366. t49 = space();
  26367. create_component(switchtoggle.$$.fragment);
  26368. t50 = space();
  26369. create_component(numberinput10.$$.fragment);
  26370. t51 = space();
  26371. div14 = element("div");
  26372. t52 = space();
  26373. create_component(checkbox8.$$.fragment);
  26374. t53 = space();
  26375. create_component(checkbox9.$$.fragment);
  26376. t54 = space();
  26377. create_component(checkbox10.$$.fragment);
  26378. t55 = space();
  26379. create_component(checkbox11.$$.fragment);
  26380. t56 = space();
  26381. create_component(checkbox12.$$.fragment);
  26382. attr(button0, "class", "activate-layer svelte-ese-1ipnpu1");
  26383. attr(button0, "type", "button");
  26384. attr(input, "class", "file-input flex3");
  26385. attr(input, "list", "dblist");
  26386. attr(input, "placeholder", localize("SEQUENCER.Player.PathInput"));
  26387. attr(input, "type", "text");
  26388. attr(button1, "class", "custom-file-picker small-button svelte-ese-1ipnpu1");
  26389. attr(button1, "type", "button");
  26390. attr(datalist, "id", "dblist");
  26391. attr(div0, "class", "file-settings svelte-ese-1ipnpu1");
  26392. attr(label, "for", "user-select");
  26393. option0.selected = true;
  26394. option0.__value = "all";
  26395. option0.value = option0.__value;
  26396. attr(select0, "class", "user-select");
  26397. attr(select0, "id", "user-select");
  26398. select0.multiple = true;
  26399. if (
  26400. /*$userStore*/
  26401. ctx[3] === void 0
  26402. )
  26403. add_render_callback(() => (
  26404. /*select0_change_handler*/
  26405. ctx[11].call(select0)
  26406. ));
  26407. attr(div1, "class", "user-settings flexcol svelte-ese-1ipnpu1");
  26408. attr(div2, "class", "row w-100");
  26409. option1.__value = "default";
  26410. option1.value = option1.__value;
  26411. attr(select1, "class", "preset-select svelte-ese-1ipnpu1");
  26412. if (
  26413. /*selectedPreset*/
  26414. ctx[2] === void 0
  26415. )
  26416. add_render_callback(() => (
  26417. /*select1_change_handler*/
  26418. ctx[12].call(select1)
  26419. ));
  26420. attr(button2, "class", "save-preset small-button svelte-ese-1ipnpu1");
  26421. attr(button2, "data-tooltip", "Save Preset");
  26422. attr(button2, "type", "button");
  26423. attr(i2, "class", "fas fa-copy svelte-ese-1ipnpu1");
  26424. attr(button3, "class", "copy-preset small-button svelte-ese-1ipnpu1");
  26425. attr(button3, "data-tooltip", "Copy Preset");
  26426. button3.disabled = button3_disabled_value = /*selectedPreset*/
  26427. ctx[2] === "default";
  26428. attr(button3, "type", "button");
  26429. attr(i3, "class", "fas fa-times svelte-ese-1ipnpu1");
  26430. attr(button4, "class", "delete-preset small-button svelte-ese-1ipnpu1");
  26431. attr(button4, "data-tooltip", "Delete Preset");
  26432. button4.disabled = button4_disabled_value = /*selectedPreset*/
  26433. ctx[2] === "default";
  26434. attr(button4, "type", "button");
  26435. attr(div3, "class", "preset-container svelte-ese-1ipnpu1");
  26436. attr(div4, "class", "flexcol");
  26437. attr(div5, "class", "divider");
  26438. attr(div8, "class", "divider");
  26439. attr(div9, "class", "divider");
  26440. attr(div11, "class", "effect-transform-container");
  26441. attr(div12, "class", "divider");
  26442. attr(div13, "class", "divider");
  26443. attr(div14, "class", "divider");
  26444. attr(div15, "class", "effect-transform-container");
  26445. attr(div16, "class", "effect-player-container");
  26446. },
  26447. m(target, anchor) {
  26448. insert(target, div16, anchor);
  26449. append(div16, fieldset0);
  26450. append(fieldset0, legend0);
  26451. append(fieldset0, t1);
  26452. append(fieldset0, button0);
  26453. append(fieldset0, t3);
  26454. append(fieldset0, div0);
  26455. append(div0, input);
  26456. set_input_value(
  26457. input,
  26458. /*$fileStore*/
  26459. ctx[0]
  26460. );
  26461. append(div0, t4);
  26462. append(div0, button1);
  26463. append(div0, t5);
  26464. append(div0, datalist);
  26465. for (let i = 0; i < each_blocks_2.length; i += 1) {
  26466. each_blocks_2[i].m(datalist, null);
  26467. }
  26468. append(fieldset0, t6);
  26469. append(fieldset0, div1);
  26470. append(div1, label);
  26471. append(div1, t8);
  26472. append(div1, select0);
  26473. append(select0, option0);
  26474. for (let i = 0; i < each_blocks_1.length; i += 1) {
  26475. each_blocks_1[i].m(select0, null);
  26476. }
  26477. select_options(
  26478. select0,
  26479. /*$userStore*/
  26480. ctx[3]
  26481. );
  26482. append(fieldset0, t10);
  26483. append(fieldset0, div4);
  26484. append(div4, div2);
  26485. append(div4, t12);
  26486. append(div4, div3);
  26487. append(div3, select1);
  26488. append(select1, option1);
  26489. for (let i = 0; i < each_blocks.length; i += 1) {
  26490. each_blocks[i].m(select1, null);
  26491. }
  26492. select_option(
  26493. select1,
  26494. /*selectedPreset*/
  26495. ctx[2]
  26496. );
  26497. append(div3, t14);
  26498. append(div3, button2);
  26499. append(div3, t15);
  26500. append(div3, button3);
  26501. append(button3, i2);
  26502. append(div3, t16);
  26503. append(div3, button4);
  26504. append(button4, i3);
  26505. append(div16, t17);
  26506. append(div16, fieldset1);
  26507. append(fieldset1, legend1);
  26508. append(fieldset1, t19);
  26509. append(fieldset1, div11);
  26510. mount_component(sliderinput0, div11, null);
  26511. append(div11, t20);
  26512. mount_component(numberinput0, div11, null);
  26513. append(div11, t21);
  26514. mount_component(numberinput1, div11, null);
  26515. append(div11, t22);
  26516. append(div11, div5);
  26517. append(div11, t23);
  26518. mount_component(sliderinput1, div11, null);
  26519. append(div11, t24);
  26520. append(div11, div6);
  26521. append(div11, t25);
  26522. mount_component(checkbox0, div11, null);
  26523. append(div11, t26);
  26524. append(div11, div7);
  26525. append(div11, t27);
  26526. append(div11, div8);
  26527. append(div11, t28);
  26528. mount_component(checkbox1, div11, null);
  26529. append(div11, t29);
  26530. mount_component(checkbox2, div11, null);
  26531. append(div11, t30);
  26532. mount_component(checkbox3, div11, null);
  26533. append(div11, t31);
  26534. mount_component(checkbox4, div11, null);
  26535. append(div11, t32);
  26536. append(div11, div9);
  26537. append(div11, t33);
  26538. mount_component(numberinput2, div11, null);
  26539. append(div11, t34);
  26540. mount_component(numberinput3, div11, null);
  26541. append(div11, t35);
  26542. mount_component(checkbox5, div11, null);
  26543. append(div11, t36);
  26544. mount_component(numberinput4, div11, null);
  26545. append(div11, t37);
  26546. mount_component(checkbox6, div11, null);
  26547. append(div11, t38);
  26548. append(div11, div10);
  26549. append(div16, t39);
  26550. append(div16, fieldset2);
  26551. append(fieldset2, legend2);
  26552. append(fieldset2, t41);
  26553. append(fieldset2, div15);
  26554. mount_component(numberinput5, div15, null);
  26555. append(div15, t42);
  26556. mount_component(numberinput6, div15, null);
  26557. append(div15, t43);
  26558. append(div15, div12);
  26559. append(div15, t44);
  26560. mount_component(checkbox7, div15, null);
  26561. append(div15, t45);
  26562. mount_component(numberinput7, div15, null);
  26563. append(div15, t46);
  26564. mount_component(numberinput8, div15, null);
  26565. append(div15, t47);
  26566. mount_component(numberinput9, div15, null);
  26567. append(div15, t48);
  26568. append(div15, div13);
  26569. append(div15, t49);
  26570. mount_component(switchtoggle, div15, null);
  26571. append(div15, t50);
  26572. mount_component(numberinput10, div15, null);
  26573. append(div15, t51);
  26574. append(div15, div14);
  26575. append(div15, t52);
  26576. mount_component(checkbox8, div15, null);
  26577. append(div15, t53);
  26578. mount_component(checkbox9, div15, null);
  26579. append(div15, t54);
  26580. mount_component(checkbox10, div15, null);
  26581. append(div15, t55);
  26582. mount_component(checkbox11, div15, null);
  26583. append(div15, t56);
  26584. mount_component(checkbox12, div15, null);
  26585. current = true;
  26586. if (!mounted) {
  26587. dispose = [
  26588. listen(
  26589. button0,
  26590. "click",
  26591. /*click_handler*/
  26592. ctx[9]
  26593. ),
  26594. listen(
  26595. input,
  26596. "input",
  26597. /*input_input_handler*/
  26598. ctx[10]
  26599. ),
  26600. listen(
  26601. button1,
  26602. "click",
  26603. /*handleClick*/
  26604. ctx[7]
  26605. ),
  26606. listen(
  26607. select0,
  26608. "change",
  26609. /*select0_change_handler*/
  26610. ctx[11]
  26611. ),
  26612. listen(
  26613. select1,
  26614. "change",
  26615. /*select1_change_handler*/
  26616. ctx[12]
  26617. ),
  26618. listen(
  26619. select1,
  26620. "change",
  26621. /*change_handler*/
  26622. ctx[13]
  26623. ),
  26624. listen(
  26625. button2,
  26626. "click",
  26627. /*click_handler_1*/
  26628. ctx[14]
  26629. ),
  26630. listen(
  26631. button3,
  26632. "click",
  26633. /*click_handler_2*/
  26634. ctx[15]
  26635. ),
  26636. listen(
  26637. button4,
  26638. "click",
  26639. /*click_handler_3*/
  26640. ctx[16]
  26641. )
  26642. ];
  26643. mounted = true;
  26644. }
  26645. },
  26646. p(ctx2, [dirty]) {
  26647. if (dirty & /*$fileStore*/
  26648. 1 && input.value !== /*$fileStore*/
  26649. ctx2[0]) {
  26650. set_input_value(
  26651. input,
  26652. /*$fileStore*/
  26653. ctx2[0]
  26654. );
  26655. }
  26656. if (dirty & /*suggestions*/
  26657. 2) {
  26658. each_value_2 = /*suggestions*/
  26659. ctx2[1];
  26660. let i;
  26661. for (i = 0; i < each_value_2.length; i += 1) {
  26662. const child_ctx = get_each_context_2(ctx2, each_value_2, i);
  26663. if (each_blocks_2[i]) {
  26664. each_blocks_2[i].p(child_ctx, dirty);
  26665. } else {
  26666. each_blocks_2[i] = create_each_block_2(child_ctx);
  26667. each_blocks_2[i].c();
  26668. each_blocks_2[i].m(datalist, null);
  26669. }
  26670. }
  26671. for (; i < each_blocks_2.length; i += 1) {
  26672. each_blocks_2[i].d(1);
  26673. }
  26674. each_blocks_2.length = each_value_2.length;
  26675. }
  26676. if (dirty & /*users*/
  26677. 32) {
  26678. each_value_1 = /*users*/
  26679. ctx2[5];
  26680. 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);
  26681. }
  26682. if (dirty & /*$userStore, users*/
  26683. 40) {
  26684. select_options(
  26685. select0,
  26686. /*$userStore*/
  26687. ctx2[3]
  26688. );
  26689. }
  26690. if (dirty & /*presets*/
  26691. 256) {
  26692. each_value = /*presets*/
  26693. ctx2[8];
  26694. let i;
  26695. for (i = 0; i < each_value.length; i += 1) {
  26696. const child_ctx = get_each_context$2(ctx2, each_value, i);
  26697. if (each_blocks[i]) {
  26698. each_blocks[i].p(child_ctx, dirty);
  26699. } else {
  26700. each_blocks[i] = create_each_block$2(child_ctx);
  26701. each_blocks[i].c();
  26702. each_blocks[i].m(select1, null);
  26703. }
  26704. }
  26705. for (; i < each_blocks.length; i += 1) {
  26706. each_blocks[i].d(1);
  26707. }
  26708. each_blocks.length = each_value.length;
  26709. }
  26710. if (dirty & /*selectedPreset, presets*/
  26711. 260) {
  26712. select_option(
  26713. select1,
  26714. /*selectedPreset*/
  26715. ctx2[2]
  26716. );
  26717. }
  26718. if (!current || dirty & /*selectedPreset, presets*/
  26719. 260 && button3_disabled_value !== (button3_disabled_value = /*selectedPreset*/
  26720. ctx2[2] === "default")) {
  26721. button3.disabled = button3_disabled_value;
  26722. }
  26723. if (!current || dirty & /*selectedPreset, presets*/
  26724. 260 && button4_disabled_value !== (button4_disabled_value = /*selectedPreset*/
  26725. ctx2[2] === "default")) {
  26726. button4.disabled = button4_disabled_value;
  26727. }
  26728. },
  26729. i(local) {
  26730. if (current)
  26731. return;
  26732. transition_in(sliderinput0.$$.fragment, local);
  26733. transition_in(numberinput0.$$.fragment, local);
  26734. transition_in(numberinput1.$$.fragment, local);
  26735. transition_in(sliderinput1.$$.fragment, local);
  26736. transition_in(checkbox0.$$.fragment, local);
  26737. transition_in(checkbox1.$$.fragment, local);
  26738. transition_in(checkbox2.$$.fragment, local);
  26739. transition_in(checkbox3.$$.fragment, local);
  26740. transition_in(checkbox4.$$.fragment, local);
  26741. transition_in(numberinput2.$$.fragment, local);
  26742. transition_in(numberinput3.$$.fragment, local);
  26743. transition_in(checkbox5.$$.fragment, local);
  26744. transition_in(numberinput4.$$.fragment, local);
  26745. transition_in(checkbox6.$$.fragment, local);
  26746. transition_in(numberinput5.$$.fragment, local);
  26747. transition_in(numberinput6.$$.fragment, local);
  26748. transition_in(checkbox7.$$.fragment, local);
  26749. transition_in(numberinput7.$$.fragment, local);
  26750. transition_in(numberinput8.$$.fragment, local);
  26751. transition_in(numberinput9.$$.fragment, local);
  26752. transition_in(switchtoggle.$$.fragment, local);
  26753. transition_in(numberinput10.$$.fragment, local);
  26754. transition_in(checkbox8.$$.fragment, local);
  26755. transition_in(checkbox9.$$.fragment, local);
  26756. transition_in(checkbox10.$$.fragment, local);
  26757. transition_in(checkbox11.$$.fragment, local);
  26758. transition_in(checkbox12.$$.fragment, local);
  26759. current = true;
  26760. },
  26761. o(local) {
  26762. transition_out(sliderinput0.$$.fragment, local);
  26763. transition_out(numberinput0.$$.fragment, local);
  26764. transition_out(numberinput1.$$.fragment, local);
  26765. transition_out(sliderinput1.$$.fragment, local);
  26766. transition_out(checkbox0.$$.fragment, local);
  26767. transition_out(checkbox1.$$.fragment, local);
  26768. transition_out(checkbox2.$$.fragment, local);
  26769. transition_out(checkbox3.$$.fragment, local);
  26770. transition_out(checkbox4.$$.fragment, local);
  26771. transition_out(numberinput2.$$.fragment, local);
  26772. transition_out(numberinput3.$$.fragment, local);
  26773. transition_out(checkbox5.$$.fragment, local);
  26774. transition_out(numberinput4.$$.fragment, local);
  26775. transition_out(checkbox6.$$.fragment, local);
  26776. transition_out(numberinput5.$$.fragment, local);
  26777. transition_out(numberinput6.$$.fragment, local);
  26778. transition_out(checkbox7.$$.fragment, local);
  26779. transition_out(numberinput7.$$.fragment, local);
  26780. transition_out(numberinput8.$$.fragment, local);
  26781. transition_out(numberinput9.$$.fragment, local);
  26782. transition_out(switchtoggle.$$.fragment, local);
  26783. transition_out(numberinput10.$$.fragment, local);
  26784. transition_out(checkbox8.$$.fragment, local);
  26785. transition_out(checkbox9.$$.fragment, local);
  26786. transition_out(checkbox10.$$.fragment, local);
  26787. transition_out(checkbox11.$$.fragment, local);
  26788. transition_out(checkbox12.$$.fragment, local);
  26789. current = false;
  26790. },
  26791. d(detaching) {
  26792. if (detaching)
  26793. detach(div16);
  26794. destroy_each(each_blocks_2, detaching);
  26795. for (let i = 0; i < each_blocks_1.length; i += 1) {
  26796. each_blocks_1[i].d();
  26797. }
  26798. destroy_each(each_blocks, detaching);
  26799. destroy_component(sliderinput0);
  26800. destroy_component(numberinput0);
  26801. destroy_component(numberinput1);
  26802. destroy_component(sliderinput1);
  26803. destroy_component(checkbox0);
  26804. destroy_component(checkbox1);
  26805. destroy_component(checkbox2);
  26806. destroy_component(checkbox3);
  26807. destroy_component(checkbox4);
  26808. destroy_component(numberinput2);
  26809. destroy_component(numberinput3);
  26810. destroy_component(checkbox5);
  26811. destroy_component(numberinput4);
  26812. destroy_component(checkbox6);
  26813. destroy_component(numberinput5);
  26814. destroy_component(numberinput6);
  26815. destroy_component(checkbox7);
  26816. destroy_component(numberinput7);
  26817. destroy_component(numberinput8);
  26818. destroy_component(numberinput9);
  26819. destroy_component(switchtoggle);
  26820. destroy_component(numberinput10);
  26821. destroy_component(checkbox8);
  26822. destroy_component(checkbox9);
  26823. destroy_component(checkbox10);
  26824. destroy_component(checkbox11);
  26825. destroy_component(checkbox12);
  26826. mounted = false;
  26827. run_all(dispose);
  26828. }
  26829. };
  26830. }
  26831. let lastInput = "";
  26832. async function activateLayer() {
  26833. ui.controls.initialize({
  26834. control: "sequencer",
  26835. tool: "play-effect"
  26836. });
  26837. canvas.sequencerInterfaceLayer.activate({ tool: "play-effect" });
  26838. }
  26839. function instance$5($$self, $$props, $$invalidate) {
  26840. let $fileStore;
  26841. let $userStore;
  26842. const fileStore = PlayerSettings.file.store;
  26843. component_subscribe($$self, fileStore, (value) => $$invalidate(0, $fileStore = value));
  26844. const users2 = game.users.filter((user) => user.active);
  26845. const userStore = PlayerSettings.users.store;
  26846. component_subscribe($$self, userStore, (value) => $$invalidate(3, $userStore = value));
  26847. let lastResults = [];
  26848. let suggestions = [];
  26849. const searchDebounce = foundry.utils.debounce(
  26850. () => {
  26851. const fileInput = get_store_value(fileStore).toLowerCase();
  26852. if (!fileInput) {
  26853. $$invalidate(1, suggestions = SequencerDatabase.publicModules);
  26854. return;
  26855. }
  26856. if (lastInput === fileInput)
  26857. return;
  26858. let results = SequencerDatabase.searchFor(fileInput);
  26859. if (lastResults.equals(results))
  26860. return;
  26861. lastResults = foundry.utils.duplicate(results);
  26862. if (results.length === 1 && results[0].startsWith(fileInput))
  26863. return;
  26864. if (results.length > 100) {
  26865. results = results.slice(0, 100);
  26866. }
  26867. $$invalidate(1, suggestions = foundry.utils.duplicate(results));
  26868. },
  26869. 200
  26870. );
  26871. let filePicker = false;
  26872. function handleClick() {
  26873. if (!filePicker) {
  26874. const file = get_store_value(fileStore);
  26875. const current = SequencerDatabase.getEntry(file, { softFail: true }) ? file : "";
  26876. filePicker = new FilePicker({
  26877. type: "imageVideo",
  26878. current,
  26879. callback: (path) => {
  26880. fileStore.set(path);
  26881. filePicker = false;
  26882. }
  26883. });
  26884. }
  26885. filePicker.render(true, { focus: true });
  26886. }
  26887. const presets = PlayerSettings.getPresets();
  26888. let selectedPreset = "default";
  26889. const click_handler = () => activateLayer();
  26890. function input_input_handler() {
  26891. $fileStore = this.value;
  26892. fileStore.set($fileStore);
  26893. }
  26894. function select0_change_handler() {
  26895. $userStore = select_multiple_value(this);
  26896. userStore.set($userStore);
  26897. $$invalidate(5, users2);
  26898. }
  26899. function select1_change_handler() {
  26900. selectedPreset = select_value(this);
  26901. $$invalidate(2, selectedPreset);
  26902. $$invalidate(8, presets);
  26903. }
  26904. const change_handler = () => PlayerSettings.loadPreset(selectedPreset);
  26905. const click_handler_1 = () => PlayerSettings.savePreset(selectedPreset);
  26906. const click_handler_2 = () => PlayerSettings.copyPreset(selectedPreset);
  26907. const click_handler_3 = () => PlayerSettings.deletePreset(selectedPreset);
  26908. $$self.$$.update = () => {
  26909. if ($$self.$$.dirty & /*$fileStore*/
  26910. 1) {
  26911. searchDebounce($fileStore);
  26912. }
  26913. };
  26914. return [
  26915. $fileStore,
  26916. suggestions,
  26917. selectedPreset,
  26918. $userStore,
  26919. fileStore,
  26920. users2,
  26921. userStore,
  26922. handleClick,
  26923. presets,
  26924. click_handler,
  26925. input_input_handler,
  26926. select0_change_handler,
  26927. select1_change_handler,
  26928. change_handler,
  26929. click_handler_1,
  26930. click_handler_2,
  26931. click_handler_3
  26932. ];
  26933. }
  26934. class Player extends SvelteComponent {
  26935. constructor(options) {
  26936. super();
  26937. init(this, options, instance$5, create_fragment$5, safe_not_equal, {});
  26938. }
  26939. }
  26940. const SequenceStatus_svelte_svelte_type_style_lang = "";
  26941. function create_fragment$4(ctx) {
  26942. let i0;
  26943. let t;
  26944. let i1;
  26945. return {
  26946. c() {
  26947. i0 = element("i");
  26948. t = space();
  26949. i1 = element("i");
  26950. attr(i0, "class", "fa-solid background-circle svelte-ese-1wkm0y3");
  26951. toggle_class(
  26952. i0,
  26953. "invisible",
  26954. /*$status*/
  26955. ctx[1] === CONSTANTS.STATUS.READY
  26956. );
  26957. toggle_class(
  26958. i0,
  26959. "fa-arrow-right",
  26960. /*$status*/
  26961. ctx[1] === CONSTANTS.STATUS.RUNNING || /*$status*/
  26962. ctx[1] === CONSTANTS.STATUS.READY
  26963. );
  26964. toggle_class(
  26965. i0,
  26966. "fa-check",
  26967. /*$status*/
  26968. ctx[1] === CONSTANTS.STATUS.COMPLETE
  26969. );
  26970. toggle_class(
  26971. i0,
  26972. "fa-arrow-down",
  26973. /*$status*/
  26974. ctx[1] === CONSTANTS.STATUS.SKIPPED
  26975. );
  26976. toggle_class(
  26977. i0,
  26978. "fa-times",
  26979. /*$status*/
  26980. ctx[1] === CONSTANTS.STATUS.ABORTED
  26981. );
  26982. attr(i1, "class", "fa-solid ");
  26983. },
  26984. m(target, anchor) {
  26985. insert(target, i0, anchor);
  26986. insert(target, t, anchor);
  26987. insert(target, i1, anchor);
  26988. },
  26989. p(ctx2, [dirty]) {
  26990. if (dirty & /*$status, CONSTANTS*/
  26991. 2) {
  26992. toggle_class(
  26993. i0,
  26994. "invisible",
  26995. /*$status*/
  26996. ctx2[1] === CONSTANTS.STATUS.READY
  26997. );
  26998. }
  26999. if (dirty & /*$status, CONSTANTS*/
  27000. 2) {
  27001. toggle_class(
  27002. i0,
  27003. "fa-arrow-right",
  27004. /*$status*/
  27005. ctx2[1] === CONSTANTS.STATUS.RUNNING || /*$status*/
  27006. ctx2[1] === CONSTANTS.STATUS.READY
  27007. );
  27008. }
  27009. if (dirty & /*$status, CONSTANTS*/
  27010. 2) {
  27011. toggle_class(
  27012. i0,
  27013. "fa-check",
  27014. /*$status*/
  27015. ctx2[1] === CONSTANTS.STATUS.COMPLETE
  27016. );
  27017. }
  27018. if (dirty & /*$status, CONSTANTS*/
  27019. 2) {
  27020. toggle_class(
  27021. i0,
  27022. "fa-arrow-down",
  27023. /*$status*/
  27024. ctx2[1] === CONSTANTS.STATUS.SKIPPED
  27025. );
  27026. }
  27027. if (dirty & /*$status, CONSTANTS*/
  27028. 2) {
  27029. toggle_class(
  27030. i0,
  27031. "fa-times",
  27032. /*$status*/
  27033. ctx2[1] === CONSTANTS.STATUS.ABORTED
  27034. );
  27035. }
  27036. },
  27037. i: noop,
  27038. o: noop,
  27039. d(detaching) {
  27040. if (detaching)
  27041. detach(i0);
  27042. if (detaching)
  27043. detach(t);
  27044. if (detaching)
  27045. detach(i1);
  27046. }
  27047. };
  27048. }
  27049. function instance$4($$self, $$props, $$invalidate) {
  27050. let $status, $$unsubscribe_status = noop, $$subscribe_status = () => ($$unsubscribe_status(), $$unsubscribe_status = subscribe(status, ($$value) => $$invalidate(1, $status = $$value)), status);
  27051. $$self.$$.on_destroy.push(() => $$unsubscribe_status());
  27052. let { status } = $$props;
  27053. $$subscribe_status();
  27054. $$self.$$set = ($$props2) => {
  27055. if ("status" in $$props2)
  27056. $$subscribe_status($$invalidate(0, status = $$props2.status));
  27057. };
  27058. return [status, $status];
  27059. }
  27060. class SequenceStatus extends SvelteComponent {
  27061. constructor(options) {
  27062. super();
  27063. init(this, options, instance$4, create_fragment$4, safe_not_equal, { status: 0 });
  27064. }
  27065. }
  27066. const SequenceSection_svelte_svelte_type_style_lang = "";
  27067. function create_fragment$3(ctx) {
  27068. let div;
  27069. let span0;
  27070. let sequencestatus;
  27071. let t0;
  27072. let span1;
  27073. let t2;
  27074. let span2;
  27075. let a;
  27076. let current;
  27077. let mounted;
  27078. let dispose;
  27079. sequencestatus = new SequenceStatus({ props: { status: (
  27080. /*status*/
  27081. ctx[2]
  27082. ) } });
  27083. return {
  27084. c() {
  27085. div = element("div");
  27086. span0 = element("span");
  27087. create_component(sequencestatus.$$.fragment);
  27088. t0 = space();
  27089. span1 = element("span");
  27090. span1.textContent = `${/*sectionName*/
  27091. ctx[3]}`;
  27092. t2 = space();
  27093. span2 = element("span");
  27094. a = element("a");
  27095. a.innerHTML = `<i class="fas fa-stop"></i>`;
  27096. attr(span1, "class", "section-name svelte-ese-1ee9h3");
  27097. attr(a, "class", "svelte-ese-1ee9h3");
  27098. toggle_class(
  27099. a,
  27100. "section-done",
  27101. /*$status*/
  27102. ctx[1] > CONSTANTS.STATUS.READY
  27103. );
  27104. attr(span2, "class", "sequence-actions svelte-ese-1ee9h3");
  27105. attr(span2, "data-tooltip", localize("SEQUENCER.Sequences.AbortSection"));
  27106. attr(div, "class", "svelte-ese-1ee9h3");
  27107. },
  27108. m(target, anchor) {
  27109. insert(target, div, anchor);
  27110. append(div, span0);
  27111. mount_component(sequencestatus, span0, null);
  27112. append(div, t0);
  27113. append(div, span1);
  27114. append(div, t2);
  27115. append(div, span2);
  27116. append(span2, a);
  27117. current = true;
  27118. if (!mounted) {
  27119. dispose = listen(
  27120. a,
  27121. "click",
  27122. /*click_handler*/
  27123. ctx[4]
  27124. );
  27125. mounted = true;
  27126. }
  27127. },
  27128. p(ctx2, [dirty]) {
  27129. if (!current || dirty & /*$status, CONSTANTS*/
  27130. 2) {
  27131. toggle_class(
  27132. a,
  27133. "section-done",
  27134. /*$status*/
  27135. ctx2[1] > CONSTANTS.STATUS.READY
  27136. );
  27137. }
  27138. },
  27139. i(local) {
  27140. if (current)
  27141. return;
  27142. transition_in(sequencestatus.$$.fragment, local);
  27143. current = true;
  27144. },
  27145. o(local) {
  27146. transition_out(sequencestatus.$$.fragment, local);
  27147. current = false;
  27148. },
  27149. d(detaching) {
  27150. if (detaching)
  27151. detach(div);
  27152. destroy_component(sequencestatus);
  27153. mounted = false;
  27154. dispose();
  27155. }
  27156. };
  27157. }
  27158. function instance$3($$self, $$props, $$invalidate) {
  27159. let $status;
  27160. let { section } = $$props;
  27161. const status = section.sectionStatus;
  27162. component_subscribe($$self, status, (value) => $$invalidate(1, $status = value));
  27163. const sectionName = (section?.constructor?.niceName ? section?.constructor?.niceName : section.constructor.name) + (section?._name ? " - " + section._name : "");
  27164. const click_handler = () => {
  27165. section._abortSection();
  27166. };
  27167. $$self.$$set = ($$props2) => {
  27168. if ("section" in $$props2)
  27169. $$invalidate(0, section = $$props2.section);
  27170. };
  27171. return [section, $status, status, sectionName, click_handler];
  27172. }
  27173. class SequenceSection extends SvelteComponent {
  27174. constructor(options) {
  27175. super();
  27176. init(this, options, instance$3, create_fragment$3, safe_not_equal, { section: 0 });
  27177. }
  27178. }
  27179. const Sequence_svelte_svelte_type_style_lang = "";
  27180. function get_each_context$1(ctx, list, i) {
  27181. const child_ctx = ctx.slice();
  27182. child_ctx[6] = list[i];
  27183. return child_ctx;
  27184. }
  27185. function create_each_block$1(ctx) {
  27186. let sequencesection;
  27187. let current;
  27188. sequencesection = new SequenceSection({ props: { section: (
  27189. /*section*/
  27190. ctx[6]
  27191. ) } });
  27192. return {
  27193. c() {
  27194. create_component(sequencesection.$$.fragment);
  27195. },
  27196. m(target, anchor) {
  27197. mount_component(sequencesection, target, anchor);
  27198. current = true;
  27199. },
  27200. p(ctx2, dirty) {
  27201. const sequencesection_changes = {};
  27202. if (dirty & /*sequence*/
  27203. 1)
  27204. sequencesection_changes.section = /*section*/
  27205. ctx2[6];
  27206. sequencesection.$set(sequencesection_changes);
  27207. },
  27208. i(local) {
  27209. if (current)
  27210. return;
  27211. transition_in(sequencesection.$$.fragment, local);
  27212. current = true;
  27213. },
  27214. o(local) {
  27215. transition_out(sequencesection.$$.fragment, local);
  27216. current = false;
  27217. },
  27218. d(detaching) {
  27219. destroy_component(sequencesection, detaching);
  27220. }
  27221. };
  27222. }
  27223. function create_fragment$2(ctx) {
  27224. let div;
  27225. let span0;
  27226. let sequencestatus;
  27227. let t0;
  27228. let span1;
  27229. let t1;
  27230. let t2;
  27231. let t3_value = (
  27232. /*sequence*/
  27233. ctx[0].moduleName ? ` (${/*sequence*/
  27234. ctx[0].moduleName})` : ""
  27235. );
  27236. let t3;
  27237. let t4;
  27238. let span2;
  27239. let a0;
  27240. let i0;
  27241. let t5;
  27242. let a1;
  27243. let i1;
  27244. let t6;
  27245. let each_1_anchor;
  27246. let current;
  27247. let mounted;
  27248. let dispose;
  27249. sequencestatus = new SequenceStatus({ props: { status: (
  27250. /*status*/
  27251. ctx[3]
  27252. ) } });
  27253. let each_value = (
  27254. /*sequence*/
  27255. ctx[0].sections
  27256. );
  27257. let each_blocks = [];
  27258. for (let i = 0; i < each_value.length; i += 1) {
  27259. each_blocks[i] = create_each_block$1(get_each_context$1(ctx, each_value, i));
  27260. }
  27261. const out = (i) => transition_out(each_blocks[i], 1, 1, () => {
  27262. each_blocks[i] = null;
  27263. });
  27264. return {
  27265. c() {
  27266. div = element("div");
  27267. span0 = element("span");
  27268. create_component(sequencestatus.$$.fragment);
  27269. t0 = space();
  27270. span1 = element("span");
  27271. t1 = text$1("Sequence ");
  27272. t2 = text$1(
  27273. /*index*/
  27274. ctx[1]
  27275. );
  27276. t3 = text$1(t3_value);
  27277. t4 = space();
  27278. span2 = element("span");
  27279. a0 = element("a");
  27280. i0 = element("i");
  27281. t5 = space();
  27282. a1 = element("a");
  27283. i1 = element("i");
  27284. t6 = space();
  27285. for (let i = 0; i < each_blocks.length; i += 1) {
  27286. each_blocks[i].c();
  27287. }
  27288. each_1_anchor = empty();
  27289. attr(span1, "class", "sequence-name svelte-ese-1dcwqos");
  27290. attr(i0, "class", "fas fa-trash-can");
  27291. attr(a0, "class", "clear-sequence svelte-ese-1dcwqos");
  27292. attr(a0, "data-tooltip", localize("SEQUENCER.Sequences.Clear"));
  27293. toggle_class(
  27294. a0,
  27295. "sequence-done-show",
  27296. /*$status*/
  27297. ctx[2] > CONSTANTS.STATUS.RUNNING
  27298. );
  27299. attr(i1, "class", "fas fa-stop");
  27300. attr(a1, "data-tooltip", localize("SEQUENCER.Sequences.AbortSequence"));
  27301. attr(a1, "class", "svelte-ese-1dcwqos");
  27302. toggle_class(
  27303. a1,
  27304. "sequence-done-hide",
  27305. /*$status*/
  27306. ctx[2] > CONSTANTS.STATUS.RUNNING
  27307. );
  27308. attr(span2, "class", "sequence-actions svelte-ese-1dcwqos");
  27309. attr(div, "class", "sequence-name-container svelte-ese-1dcwqos");
  27310. },
  27311. m(target, anchor) {
  27312. insert(target, div, anchor);
  27313. append(div, span0);
  27314. mount_component(sequencestatus, span0, null);
  27315. append(div, t0);
  27316. append(div, span1);
  27317. append(span1, t1);
  27318. append(span1, t2);
  27319. append(span1, t3);
  27320. append(div, t4);
  27321. append(div, span2);
  27322. append(span2, a0);
  27323. append(a0, i0);
  27324. append(span2, t5);
  27325. append(span2, a1);
  27326. append(a1, i1);
  27327. insert(target, t6, anchor);
  27328. for (let i = 0; i < each_blocks.length; i += 1) {
  27329. each_blocks[i].m(target, anchor);
  27330. }
  27331. insert(target, each_1_anchor, anchor);
  27332. current = true;
  27333. if (!mounted) {
  27334. dispose = [
  27335. listen(
  27336. a0,
  27337. "click",
  27338. /*click_handler*/
  27339. ctx[4]
  27340. ),
  27341. listen(
  27342. a1,
  27343. "click",
  27344. /*click_handler_1*/
  27345. ctx[5]
  27346. )
  27347. ];
  27348. mounted = true;
  27349. }
  27350. },
  27351. p(ctx2, [dirty]) {
  27352. if (!current || dirty & /*index*/
  27353. 2)
  27354. set_data(
  27355. t2,
  27356. /*index*/
  27357. ctx2[1]
  27358. );
  27359. if ((!current || dirty & /*sequence*/
  27360. 1) && t3_value !== (t3_value = /*sequence*/
  27361. ctx2[0].moduleName ? ` (${/*sequence*/
  27362. ctx2[0].moduleName})` : ""))
  27363. set_data(t3, t3_value);
  27364. if (!current || dirty & /*$status, CONSTANTS*/
  27365. 4) {
  27366. toggle_class(
  27367. a0,
  27368. "sequence-done-show",
  27369. /*$status*/
  27370. ctx2[2] > CONSTANTS.STATUS.RUNNING
  27371. );
  27372. }
  27373. if (!current || dirty & /*$status, CONSTANTS*/
  27374. 4) {
  27375. toggle_class(
  27376. a1,
  27377. "sequence-done-hide",
  27378. /*$status*/
  27379. ctx2[2] > CONSTANTS.STATUS.RUNNING
  27380. );
  27381. }
  27382. if (dirty & /*sequence*/
  27383. 1) {
  27384. each_value = /*sequence*/
  27385. ctx2[0].sections;
  27386. let i;
  27387. for (i = 0; i < each_value.length; i += 1) {
  27388. const child_ctx = get_each_context$1(ctx2, each_value, i);
  27389. if (each_blocks[i]) {
  27390. each_blocks[i].p(child_ctx, dirty);
  27391. transition_in(each_blocks[i], 1);
  27392. } else {
  27393. each_blocks[i] = create_each_block$1(child_ctx);
  27394. each_blocks[i].c();
  27395. transition_in(each_blocks[i], 1);
  27396. each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor);
  27397. }
  27398. }
  27399. group_outros();
  27400. for (i = each_value.length; i < each_blocks.length; i += 1) {
  27401. out(i);
  27402. }
  27403. check_outros();
  27404. }
  27405. },
  27406. i(local) {
  27407. if (current)
  27408. return;
  27409. transition_in(sequencestatus.$$.fragment, local);
  27410. for (let i = 0; i < each_value.length; i += 1) {
  27411. transition_in(each_blocks[i]);
  27412. }
  27413. current = true;
  27414. },
  27415. o(local) {
  27416. transition_out(sequencestatus.$$.fragment, local);
  27417. each_blocks = each_blocks.filter(Boolean);
  27418. for (let i = 0; i < each_blocks.length; i += 1) {
  27419. transition_out(each_blocks[i]);
  27420. }
  27421. current = false;
  27422. },
  27423. d(detaching) {
  27424. if (detaching)
  27425. detach(div);
  27426. destroy_component(sequencestatus);
  27427. if (detaching)
  27428. detach(t6);
  27429. destroy_each(each_blocks, detaching);
  27430. if (detaching)
  27431. detach(each_1_anchor);
  27432. mounted = false;
  27433. run_all(dispose);
  27434. }
  27435. };
  27436. }
  27437. function instance$2($$self, $$props, $$invalidate) {
  27438. let $status;
  27439. let { sequence } = $$props;
  27440. let { index } = $$props;
  27441. const status = sequence.status;
  27442. component_subscribe($$self, status, (value) => $$invalidate(2, $status = value));
  27443. const click_handler = () => {
  27444. SequenceManager.RunningSequences.delete(sequence.id);
  27445. };
  27446. const click_handler_1 = () => {
  27447. sequence._abort();
  27448. };
  27449. $$self.$$set = ($$props2) => {
  27450. if ("sequence" in $$props2)
  27451. $$invalidate(0, sequence = $$props2.sequence);
  27452. if ("index" in $$props2)
  27453. $$invalidate(1, index = $$props2.index);
  27454. };
  27455. return [sequence, index, $status, status, click_handler, click_handler_1];
  27456. }
  27457. let Sequence$2 = class Sequence2 extends SvelteComponent {
  27458. constructor(options) {
  27459. super();
  27460. init(this, options, instance$2, create_fragment$2, safe_not_equal, { sequence: 0, index: 1 });
  27461. }
  27462. };
  27463. const Sequences_svelte_svelte_type_style_lang = "";
  27464. function get_each_context(ctx, list, i) {
  27465. const child_ctx = ctx.slice();
  27466. child_ctx[7] = list[i][0];
  27467. child_ctx[8] = list[i][1];
  27468. child_ctx[10] = i;
  27469. return child_ctx;
  27470. }
  27471. function create_else_block(ctx) {
  27472. let div0;
  27473. let button0;
  27474. let t1;
  27475. let button1;
  27476. let t3;
  27477. let div1;
  27478. let each_blocks = [];
  27479. let each_1_lookup = /* @__PURE__ */ new Map();
  27480. let current;
  27481. let mounted;
  27482. let dispose;
  27483. let each_value = (
  27484. /*runningSequences*/
  27485. ctx[0]
  27486. );
  27487. const get_key = (ctx2) => (
  27488. /*id*/
  27489. ctx2[7]
  27490. );
  27491. for (let i = 0; i < each_value.length; i += 1) {
  27492. let child_ctx = get_each_context(ctx, each_value, i);
  27493. let key = get_key(child_ctx);
  27494. each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx));
  27495. }
  27496. return {
  27497. c() {
  27498. div0 = element("div");
  27499. button0 = element("button");
  27500. button0.textContent = `${localize("SEQUENCER.Sequences.ClearFinished")}`;
  27501. t1 = space();
  27502. button1 = element("button");
  27503. button1.textContent = `${localize("SEQUENCER.Sequences.StopAll")}`;
  27504. t3 = space();
  27505. div1 = element("div");
  27506. for (let i = 0; i < each_blocks.length; i += 1) {
  27507. each_blocks[i].c();
  27508. }
  27509. attr(button0, "type", "button");
  27510. attr(button0, "class", "svelte-ese-1ismzan");
  27511. attr(button1, "type", "button");
  27512. attr(button1, "class", "svelte-ese-1ismzan");
  27513. attr(div0, "class", "sequence-button-header svelte-ese-1ismzan");
  27514. attr(div1, "class", "running-sequences svelte-ese-1ismzan");
  27515. },
  27516. m(target, anchor) {
  27517. insert(target, div0, anchor);
  27518. append(div0, button0);
  27519. append(div0, t1);
  27520. append(div0, button1);
  27521. insert(target, t3, anchor);
  27522. insert(target, div1, anchor);
  27523. for (let i = 0; i < each_blocks.length; i += 1) {
  27524. each_blocks[i].m(div1, null);
  27525. }
  27526. current = true;
  27527. if (!mounted) {
  27528. dispose = [
  27529. listen(
  27530. button0,
  27531. "click",
  27532. /*click_handler*/
  27533. ctx[3]
  27534. ),
  27535. listen(
  27536. button1,
  27537. "click",
  27538. /*click_handler_1*/
  27539. ctx[4]
  27540. )
  27541. ];
  27542. mounted = true;
  27543. }
  27544. },
  27545. p(ctx2, dirty) {
  27546. if (dirty & /*runningSequences*/
  27547. 1) {
  27548. each_value = /*runningSequences*/
  27549. ctx2[0];
  27550. group_outros();
  27551. 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);
  27552. check_outros();
  27553. }
  27554. },
  27555. i(local) {
  27556. if (current)
  27557. return;
  27558. for (let i = 0; i < each_value.length; i += 1) {
  27559. transition_in(each_blocks[i]);
  27560. }
  27561. current = true;
  27562. },
  27563. o(local) {
  27564. for (let i = 0; i < each_blocks.length; i += 1) {
  27565. transition_out(each_blocks[i]);
  27566. }
  27567. current = false;
  27568. },
  27569. d(detaching) {
  27570. if (detaching)
  27571. detach(div0);
  27572. if (detaching)
  27573. detach(t3);
  27574. if (detaching)
  27575. detach(div1);
  27576. for (let i = 0; i < each_blocks.length; i += 1) {
  27577. each_blocks[i].d();
  27578. }
  27579. mounted = false;
  27580. run_all(dispose);
  27581. }
  27582. };
  27583. }
  27584. function create_if_block(ctx) {
  27585. let div;
  27586. let h2;
  27587. return {
  27588. c() {
  27589. div = element("div");
  27590. h2 = element("h2");
  27591. h2.textContent = `${localize("SEQUENCER.Sequences.NoSequences")}`;
  27592. attr(div, "class", "no-sequences");
  27593. },
  27594. m(target, anchor) {
  27595. insert(target, div, anchor);
  27596. append(div, h2);
  27597. },
  27598. p: noop,
  27599. i: noop,
  27600. o: noop,
  27601. d(detaching) {
  27602. if (detaching)
  27603. detach(div);
  27604. }
  27605. };
  27606. }
  27607. function create_each_block(key_1, ctx) {
  27608. let first;
  27609. let sequence;
  27610. let current;
  27611. sequence = new Sequence$2({
  27612. props: {
  27613. sequence: (
  27614. /*sequence*/
  27615. ctx[8]
  27616. ),
  27617. index: (
  27618. /*index*/
  27619. ctx[10] + 1
  27620. )
  27621. }
  27622. });
  27623. return {
  27624. key: key_1,
  27625. first: null,
  27626. c() {
  27627. first = empty();
  27628. create_component(sequence.$$.fragment);
  27629. this.first = first;
  27630. },
  27631. m(target, anchor) {
  27632. insert(target, first, anchor);
  27633. mount_component(sequence, target, anchor);
  27634. current = true;
  27635. },
  27636. p(new_ctx, dirty) {
  27637. ctx = new_ctx;
  27638. const sequence_changes = {};
  27639. if (dirty & /*runningSequences*/
  27640. 1)
  27641. sequence_changes.sequence = /*sequence*/
  27642. ctx[8];
  27643. if (dirty & /*runningSequences*/
  27644. 1)
  27645. sequence_changes.index = /*index*/
  27646. ctx[10] + 1;
  27647. sequence.$set(sequence_changes);
  27648. },
  27649. i(local) {
  27650. if (current)
  27651. return;
  27652. transition_in(sequence.$$.fragment, local);
  27653. current = true;
  27654. },
  27655. o(local) {
  27656. transition_out(sequence.$$.fragment, local);
  27657. current = false;
  27658. },
  27659. d(detaching) {
  27660. if (detaching)
  27661. detach(first);
  27662. destroy_component(sequence, detaching);
  27663. }
  27664. };
  27665. }
  27666. function create_fragment$1(ctx) {
  27667. let div;
  27668. let current_block_type_index;
  27669. let if_block;
  27670. let current;
  27671. const if_block_creators = [create_if_block, create_else_block];
  27672. const if_blocks = [];
  27673. function select_block_type(ctx2, dirty) {
  27674. if (!/*runningSequences*/
  27675. ctx2[0].length)
  27676. return 0;
  27677. return 1;
  27678. }
  27679. current_block_type_index = select_block_type(ctx);
  27680. if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
  27681. return {
  27682. c() {
  27683. div = element("div");
  27684. if_block.c();
  27685. attr(div, "class", "sequence-container svelte-ese-1ismzan");
  27686. },
  27687. m(target, anchor) {
  27688. insert(target, div, anchor);
  27689. if_blocks[current_block_type_index].m(div, null);
  27690. current = true;
  27691. },
  27692. p(ctx2, [dirty]) {
  27693. let previous_block_index = current_block_type_index;
  27694. current_block_type_index = select_block_type(ctx2);
  27695. if (current_block_type_index === previous_block_index) {
  27696. if_blocks[current_block_type_index].p(ctx2, dirty);
  27697. } else {
  27698. group_outros();
  27699. transition_out(if_blocks[previous_block_index], 1, 1, () => {
  27700. if_blocks[previous_block_index] = null;
  27701. });
  27702. check_outros();
  27703. if_block = if_blocks[current_block_type_index];
  27704. if (!if_block) {
  27705. if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
  27706. if_block.c();
  27707. } else {
  27708. if_block.p(ctx2, dirty);
  27709. }
  27710. transition_in(if_block, 1);
  27711. if_block.m(div, null);
  27712. }
  27713. },
  27714. i(local) {
  27715. if (current)
  27716. return;
  27717. transition_in(if_block);
  27718. current = true;
  27719. },
  27720. o(local) {
  27721. transition_out(if_block);
  27722. current = false;
  27723. },
  27724. d(detaching) {
  27725. if (detaching)
  27726. detach(div);
  27727. if_blocks[current_block_type_index].d();
  27728. }
  27729. };
  27730. }
  27731. function instance$1($$self, $$props, $$invalidate) {
  27732. let runningSequences;
  27733. let $RunningSequences;
  27734. const RunningSequences = SequenceManager.RunningSequences;
  27735. component_subscribe($$self, RunningSequences, (value) => $$invalidate(2, $RunningSequences = value));
  27736. onDestroy(() => {
  27737. SequenceManager.RunningSequences.clearFinishedSequences();
  27738. });
  27739. const click_handler = () => {
  27740. SequenceManager.RunningSequences.clearFinishedSequences();
  27741. };
  27742. const click_handler_1 = () => {
  27743. SequenceManager.RunningSequences.stopAll();
  27744. };
  27745. $$self.$$.update = () => {
  27746. if ($$self.$$.dirty & /*$RunningSequences*/
  27747. 4) {
  27748. Object.values($RunningSequences);
  27749. }
  27750. if ($$self.$$.dirty & /*$RunningSequences*/
  27751. 4) {
  27752. $$invalidate(0, runningSequences = Object.entries($RunningSequences));
  27753. }
  27754. };
  27755. return [
  27756. runningSequences,
  27757. RunningSequences,
  27758. $RunningSequences,
  27759. click_handler,
  27760. click_handler_1
  27761. ];
  27762. }
  27763. class Sequences extends SvelteComponent {
  27764. constructor(options) {
  27765. super();
  27766. init(this, options, instance$1, create_fragment$1, safe_not_equal, {});
  27767. }
  27768. }
  27769. function create_default_slot(ctx) {
  27770. let tabs_1;
  27771. let updating_activeTab;
  27772. let t;
  27773. let switch_instance;
  27774. let switch_instance_anchor;
  27775. let current;
  27776. function tabs_1_activeTab_binding(value) {
  27777. ctx[4](value);
  27778. }
  27779. let tabs_1_props = { tabs: (
  27780. /*tabs*/
  27781. ctx[3]
  27782. ) };
  27783. if (
  27784. /*activeTab*/
  27785. ctx[1] !== void 0
  27786. ) {
  27787. tabs_1_props.activeTab = /*activeTab*/
  27788. ctx[1];
  27789. }
  27790. tabs_1 = new Tabs({ props: tabs_1_props });
  27791. binding_callbacks.push(() => bind(tabs_1, "activeTab", tabs_1_activeTab_binding));
  27792. var switch_value = (
  27793. /*component*/
  27794. ctx[2]
  27795. );
  27796. function switch_props(ctx2) {
  27797. return {};
  27798. }
  27799. if (switch_value) {
  27800. switch_instance = construct_svelte_component(switch_value, switch_props());
  27801. }
  27802. return {
  27803. c() {
  27804. create_component(tabs_1.$$.fragment);
  27805. t = space();
  27806. if (switch_instance)
  27807. create_component(switch_instance.$$.fragment);
  27808. switch_instance_anchor = empty();
  27809. },
  27810. m(target, anchor) {
  27811. mount_component(tabs_1, target, anchor);
  27812. insert(target, t, anchor);
  27813. if (switch_instance)
  27814. mount_component(switch_instance, target, anchor);
  27815. insert(target, switch_instance_anchor, anchor);
  27816. current = true;
  27817. },
  27818. p(ctx2, dirty) {
  27819. const tabs_1_changes = {};
  27820. if (!updating_activeTab && dirty & /*activeTab*/
  27821. 2) {
  27822. updating_activeTab = true;
  27823. tabs_1_changes.activeTab = /*activeTab*/
  27824. ctx2[1];
  27825. add_flush_callback(() => updating_activeTab = false);
  27826. }
  27827. tabs_1.$set(tabs_1_changes);
  27828. if (switch_value !== (switch_value = /*component*/
  27829. ctx2[2])) {
  27830. if (switch_instance) {
  27831. group_outros();
  27832. const old_component = switch_instance;
  27833. transition_out(old_component.$$.fragment, 1, 0, () => {
  27834. destroy_component(old_component, 1);
  27835. });
  27836. check_outros();
  27837. }
  27838. if (switch_value) {
  27839. switch_instance = construct_svelte_component(switch_value, switch_props());
  27840. create_component(switch_instance.$$.fragment);
  27841. transition_in(switch_instance.$$.fragment, 1);
  27842. mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
  27843. } else {
  27844. switch_instance = null;
  27845. }
  27846. }
  27847. },
  27848. i(local) {
  27849. if (current)
  27850. return;
  27851. transition_in(tabs_1.$$.fragment, local);
  27852. if (switch_instance)
  27853. transition_in(switch_instance.$$.fragment, local);
  27854. current = true;
  27855. },
  27856. o(local) {
  27857. transition_out(tabs_1.$$.fragment, local);
  27858. if (switch_instance)
  27859. transition_out(switch_instance.$$.fragment, local);
  27860. current = false;
  27861. },
  27862. d(detaching) {
  27863. destroy_component(tabs_1, detaching);
  27864. if (detaching)
  27865. detach(t);
  27866. if (detaching)
  27867. detach(switch_instance_anchor);
  27868. if (switch_instance)
  27869. destroy_component(switch_instance, detaching);
  27870. }
  27871. };
  27872. }
  27873. function create_fragment(ctx) {
  27874. let applicationshell;
  27875. let updating_elementRoot;
  27876. let current;
  27877. function applicationshell_elementRoot_binding(value) {
  27878. ctx[5](value);
  27879. }
  27880. let applicationshell_props = {
  27881. $$slots: { default: [create_default_slot] },
  27882. $$scope: { ctx }
  27883. };
  27884. if (
  27885. /*elementRoot*/
  27886. ctx[0] !== void 0
  27887. ) {
  27888. applicationshell_props.elementRoot = /*elementRoot*/
  27889. ctx[0];
  27890. }
  27891. applicationshell = new ApplicationShell({ props: applicationshell_props });
  27892. binding_callbacks.push(() => bind(applicationshell, "elementRoot", applicationshell_elementRoot_binding));
  27893. return {
  27894. c() {
  27895. create_component(applicationshell.$$.fragment);
  27896. },
  27897. m(target, anchor) {
  27898. mount_component(applicationshell, target, anchor);
  27899. current = true;
  27900. },
  27901. p(ctx2, [dirty]) {
  27902. const applicationshell_changes = {};
  27903. if (dirty & /*$$scope, component, activeTab*/
  27904. 134) {
  27905. applicationshell_changes.$$scope = { dirty, ctx: ctx2 };
  27906. }
  27907. if (!updating_elementRoot && dirty & /*elementRoot*/
  27908. 1) {
  27909. updating_elementRoot = true;
  27910. applicationshell_changes.elementRoot = /*elementRoot*/
  27911. ctx2[0];
  27912. add_flush_callback(() => updating_elementRoot = false);
  27913. }
  27914. applicationshell.$set(applicationshell_changes);
  27915. },
  27916. i(local) {
  27917. if (current)
  27918. return;
  27919. transition_in(applicationshell.$$.fragment, local);
  27920. current = true;
  27921. },
  27922. o(local) {
  27923. transition_out(applicationshell.$$.fragment, local);
  27924. current = false;
  27925. },
  27926. d(detaching) {
  27927. destroy_component(applicationshell, detaching);
  27928. }
  27929. };
  27930. }
  27931. function instance($$self, $$props, $$invalidate) {
  27932. let component;
  27933. const { application } = getContext("#external");
  27934. let { elementRoot } = $$props;
  27935. let tabs = [
  27936. {
  27937. value: "player",
  27938. label: localize("SEQUENCER.Player.Title"),
  27939. icon: "fas fa-play-circle",
  27940. component: Player
  27941. },
  27942. {
  27943. value: "manager",
  27944. label: localize("SEQUENCER.Manager.Title"),
  27945. icon: "fas fa-film",
  27946. component: Manager
  27947. },
  27948. {
  27949. value: "sequences",
  27950. label: localize("SEQUENCER.Sequences.Title"),
  27951. icon: "fas fa-play",
  27952. component: Sequences
  27953. },
  27954. {
  27955. value: "howto",
  27956. label: localize("SEQUENCER.HowTo.Title"),
  27957. icon: "fas fa-chalkboard-teacher",
  27958. component: HowTo
  27959. }
  27960. ];
  27961. let activeTab = application.options.tab ?? "manager";
  27962. function tabs_1_activeTab_binding(value) {
  27963. activeTab = value;
  27964. $$invalidate(1, activeTab);
  27965. }
  27966. function applicationshell_elementRoot_binding(value) {
  27967. elementRoot = value;
  27968. $$invalidate(0, elementRoot);
  27969. }
  27970. $$self.$$set = ($$props2) => {
  27971. if ("elementRoot" in $$props2)
  27972. $$invalidate(0, elementRoot = $$props2.elementRoot);
  27973. };
  27974. $$self.$$.update = () => {
  27975. if ($$self.$$.dirty & /*activeTab*/
  27976. 2) {
  27977. $$invalidate(2, component = tabs.find((tab) => tab.value === activeTab).component);
  27978. }
  27979. };
  27980. return [
  27981. elementRoot,
  27982. activeTab,
  27983. component,
  27984. tabs,
  27985. tabs_1_activeTab_binding,
  27986. applicationshell_elementRoot_binding
  27987. ];
  27988. }
  27989. class Effects_ui_shell extends SvelteComponent {
  27990. constructor(options) {
  27991. super();
  27992. init(this, options, instance, create_fragment, safe_not_equal, { elementRoot: 0 });
  27993. }
  27994. get elementRoot() {
  27995. return this.$$.ctx[0];
  27996. }
  27997. set elementRoot(elementRoot) {
  27998. this.$$set({ elementRoot });
  27999. flush();
  28000. }
  28001. }
  28002. class EffectsUIApp extends SvelteApplication {
  28003. static get defaultOptions() {
  28004. return foundry.utils.mergeObject(super.defaultOptions, {
  28005. title: game.i18n.localize("SEQUENCER.ManagerUI"),
  28006. classes: ["dialog"],
  28007. width: "auto",
  28008. height: "auto",
  28009. top: 65,
  28010. left: 120,
  28011. resizable: false,
  28012. svelte: {
  28013. class: Effects_ui_shell,
  28014. target: document.body
  28015. }
  28016. });
  28017. }
  28018. static getActiveApp() {
  28019. return Object.values(ui.windows).find((app) => {
  28020. return app instanceof this && app._state > Application.RENDER_STATES.CLOSED;
  28021. });
  28022. }
  28023. static async show(options = {}) {
  28024. const existingApp = this.getActiveApp();
  28025. if (existingApp)
  28026. return existingApp.render(false, { focus: true });
  28027. return new Promise((resolve) => {
  28028. options.resolve = resolve;
  28029. new this(options).render(true, { focus: true });
  28030. });
  28031. }
  28032. }
  28033. function registerSettings() {
  28034. game.settings.register(CONSTANTS.MODULE_NAME, "enable-pixi-fix", {
  28035. name: "SEQUENCER.Setting.EnablePixiFix.Title",
  28036. hint: "SEQUENCER.Setting.EnablePixiFix.Label",
  28037. scope: "client",
  28038. config: true,
  28039. default: true,
  28040. requiresReload: true,
  28041. type: Boolean
  28042. });
  28043. game.settings.register(CONSTANTS.MODULE_NAME, "enable-global-pixi-fix", {
  28044. name: "SEQUENCER.Setting.EnableGlobalPixiFix.Title",
  28045. hint: "SEQUENCER.Setting.EnableGlobalPixiFix.Label",
  28046. scope: "client",
  28047. config: true,
  28048. default: false,
  28049. requiresReload: true,
  28050. type: Boolean
  28051. });
  28052. game.settings.register(CONSTANTS.MODULE_NAME, "enable-above-ui-screenspace", {
  28053. name: "SEQUENCER.Setting.EnableAboveUIScreenspace.Title",
  28054. hint: "SEQUENCER.Setting.EnableAboveUIScreenspace.Label",
  28055. scope: "client",
  28056. config: true,
  28057. default: true,
  28058. requiresReload: true,
  28059. type: Boolean
  28060. });
  28061. game.settings.register(CONSTANTS.MODULE_NAME, "debug", {
  28062. name: "SEQUENCER.Setting.Debug.Title",
  28063. hint: "SEQUENCER.Setting.Debug.Label",
  28064. scope: "client",
  28065. config: true,
  28066. default: false,
  28067. type: Boolean
  28068. });
  28069. game.settings.register(CONSTANTS.MODULE_NAME, "showSidebarTools", {
  28070. name: "SEQUENCER.Setting.ShowTools.Title",
  28071. hint: "SEQUENCER.Setting.ShowTools.Label",
  28072. scope: "client",
  28073. config: true,
  28074. default: true,
  28075. requiresReload: true,
  28076. type: Boolean
  28077. });
  28078. game.settings.register(CONSTANTS.MODULE_NAME, "showTokenSidebarTools", {
  28079. name: "SEQUENCER.Setting.ShowTokenTools.Title",
  28080. hint: "SEQUENCER.Setting.ShowTokenTools.Label",
  28081. scope: "client",
  28082. config: true,
  28083. default: true,
  28084. requiresReload: true,
  28085. type: Boolean
  28086. });
  28087. game.settings.register(CONSTANTS.MODULE_NAME, "effectsEnabled", {
  28088. name: "SEQUENCER.Setting.EnableEffects.Title",
  28089. hint: "SEQUENCER.Setting.EnableEffects.Label",
  28090. scope: "client",
  28091. config: true,
  28092. default: true,
  28093. requiresReload: true,
  28094. type: Boolean
  28095. });
  28096. game.settings.register(CONSTANTS.MODULE_NAME, "soundsEnabled", {
  28097. name: "SEQUENCER.Setting.EnableSounds.Title",
  28098. hint: "SEQUENCER.Setting.EnableSounds.Label",
  28099. scope: "client",
  28100. config: true,
  28101. default: true,
  28102. requiresReload: true,
  28103. type: Boolean
  28104. });
  28105. game.settings.register(CONSTANTS.MODULE_NAME, "user-effect-opacity", {
  28106. name: "SEQUENCER.Setting.ExternalEffectOpacity.Title",
  28107. hint: "SEQUENCER.Setting.ExternalEffectOpacity.Label",
  28108. scope: "client",
  28109. config: true,
  28110. default: 50,
  28111. type: Number,
  28112. range: {
  28113. min: 0,
  28114. max: 100,
  28115. step: 1
  28116. }
  28117. });
  28118. game.settings.register(CONSTANTS.MODULE_NAME, "db-list-view", {
  28119. scope: "client",
  28120. config: false,
  28121. default: false,
  28122. type: Boolean
  28123. });
  28124. const permissionLevels = [
  28125. game.i18n.localize("SEQUENCER.Permission.Player"),
  28126. game.i18n.localize("SEQUENCER.Permission.Trusted"),
  28127. game.i18n.localize("SEQUENCER.Permission.Assistant"),
  28128. game.i18n.localize("SEQUENCER.Permission.GM")
  28129. ];
  28130. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-effect-create", {
  28131. name: "SEQUENCER.Setting.Permission.EffectCreate.Title",
  28132. hint: "SEQUENCER.Setting.Permission.EffectCreate.Label",
  28133. scope: "world",
  28134. config: true,
  28135. default: 0,
  28136. type: Number,
  28137. choices: permissionLevels,
  28138. requiresReload: true
  28139. });
  28140. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-effect-delete", {
  28141. name: "SEQUENCER.Setting.Permission.EffectDelete.Title",
  28142. hint: "SEQUENCER.Setting.Permission.EffectDelete.Label",
  28143. scope: "world",
  28144. config: true,
  28145. default: 2,
  28146. type: Number,
  28147. choices: permissionLevels,
  28148. requiresReload: true
  28149. });
  28150. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-sound-create", {
  28151. name: "SEQUENCER.Setting.Permission.SoundCreate.Title",
  28152. hint: "SEQUENCER.Setting.Permission.SoundCreate.Label",
  28153. scope: "world",
  28154. config: true,
  28155. default: 0,
  28156. type: Number,
  28157. choices: permissionLevels,
  28158. requiresReload: true
  28159. });
  28160. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-preload", {
  28161. name: "SEQUENCER.Setting.Permission.PreloadClients.Title",
  28162. hint: "SEQUENCER.Setting.Permission.PreloadClients.Label",
  28163. scope: "world",
  28164. config: true,
  28165. default: 1,
  28166. type: Number,
  28167. choices: permissionLevels,
  28168. requiresReload: true
  28169. });
  28170. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-sidebar-tools", {
  28171. name: "SEQUENCER.Setting.Permission.UseSidebarTools.Title",
  28172. hint: "SEQUENCER.Setting.Permission.UseSidebarTools.Label",
  28173. scope: "world",
  28174. config: true,
  28175. default: 0,
  28176. type: Number,
  28177. choices: permissionLevels,
  28178. requiresReload: true
  28179. });
  28180. game.settings.register(CONSTANTS.MODULE_NAME, "effectPresets", {
  28181. scope: "client",
  28182. default: {},
  28183. type: Object
  28184. });
  28185. Hooks.on("getSceneControlButtons", (controls) => {
  28186. if (!game.settings.get(CONSTANTS.MODULE_NAME, "showSidebarTools"))
  28187. return;
  28188. const selectTool = {
  28189. icon: "fas fa-expand",
  28190. name: "select-effect",
  28191. title: "SEQUENCER.SidebarButtons.Select",
  28192. visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools")
  28193. };
  28194. const playTool = {
  28195. icon: "fas fa-play",
  28196. name: "play-effect",
  28197. title: "SEQUENCER.SidebarButtons.Play",
  28198. visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
  28199. onClick: () => {
  28200. EffectsUIApp.show({ inFocus: true, tab: "player" });
  28201. }
  28202. };
  28203. const viewer = {
  28204. icon: "fas fa-film",
  28205. name: "effectviewer",
  28206. title: "SEQUENCER.SidebarButtons.Manager",
  28207. button: true,
  28208. visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
  28209. onClick: () => {
  28210. EffectsUIApp.show({ inFocus: true, tab: "manager" });
  28211. }
  28212. };
  28213. const database = {
  28214. icon: "fas fa-database",
  28215. name: "effectdatabase",
  28216. title: "SEQUENCER.SidebarButtons.Database",
  28217. button: true,
  28218. visible: user_can_do("permissions-sidebar-tools"),
  28219. onClick: () => {
  28220. DatabaseViewerApp.show();
  28221. }
  28222. };
  28223. controls.push({
  28224. name: CONSTANTS.MODULE_NAME,
  28225. title: "Sequencer Layer",
  28226. icon: "fas fa-list-ol",
  28227. layer: "sequencerInterfaceLayer",
  28228. visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
  28229. activeTool: "select-effect",
  28230. tools: [selectTool, playTool, database, viewer]
  28231. });
  28232. if (!game.settings.get(CONSTANTS.MODULE_NAME, "showTokenSidebarTools"))
  28233. return;
  28234. const bar = controls.find((c) => c.name === "token");
  28235. bar.tools.push(database);
  28236. bar.tools.push(viewer);
  28237. });
  28238. console.log("Sequencer | Registered settings");
  28239. }
  28240. async function migrateSettings() {
  28241. const oldPixiFixSetting = game.settings.storage.get("client").getItem("sequencer.disable-pixi-fix");
  28242. if (oldPixiFixSetting) {
  28243. const value = oldPixiFixSetting === "true";
  28244. game.settings.storage.get("client").removeItem("sequencer.disable-pixi-fix");
  28245. await game.settings.set(CONSTANTS.MODULE_NAME, "enable-pixi-fix", !value);
  28246. }
  28247. const oldScreenspaceSetting = game.settings.storage.get("client").getItem("sequencer.disable-above-ui-screenspace");
  28248. if (oldScreenspaceSetting) {
  28249. const value = oldScreenspaceSetting === "true";
  28250. game.settings.storage.get("client").removeItem("sequencer.disable-above-ui-screenspace");
  28251. await game.settings.set(
  28252. CONSTANTS.MODULE_NAME,
  28253. "enable-above-ui-screenspace",
  28254. !value
  28255. );
  28256. }
  28257. }
  28258. function registerLayers() {
  28259. CONFIG.Canvas.layers = foundry.utils.mergeObject(Canvas.layers, {
  28260. sequencerEffects: {
  28261. layerClass: BaseEffectsLayer,
  28262. group: "primary"
  28263. },
  28264. sequencerInterfaceLayer: {
  28265. layerClass: SequencerInterfaceLayer,
  28266. group: "interface"
  28267. },
  28268. sequencerEffectsUILayer: {
  28269. layerClass: UIEffectsLayer,
  28270. group: "interface"
  28271. }
  28272. });
  28273. if (!Object.is(Canvas.layers, CONFIG.Canvas.layers)) {
  28274. const layers = Canvas.layers;
  28275. Object.defineProperty(Canvas, "layers", {
  28276. get: function() {
  28277. return foundry.utils.mergeObject(layers, CONFIG.Canvas.layers);
  28278. }
  28279. });
  28280. }
  28281. console.log("Sequencer | Registered Layers");
  28282. }
  28283. const hotkeys = {
  28284. get _ready() {
  28285. return canvas.ready && canvas.sequencerInterfaceLayer.active;
  28286. },
  28287. playTool: {
  28288. playManySequencedDown: () => {
  28289. if (!hotkeys._ready)
  28290. return;
  28291. EffectPlayer.playManySequenced = true;
  28292. },
  28293. playManySequencedUp: () => {
  28294. if (!hotkeys._ready)
  28295. return;
  28296. EffectPlayer.playManySequenced = false;
  28297. if (!EffectPlayer.isActive)
  28298. return;
  28299. EffectPlayer.playManyUp();
  28300. },
  28301. playManyDown: () => {
  28302. if (!hotkeys._ready)
  28303. return;
  28304. EffectPlayer.playMany = true;
  28305. },
  28306. playManyUp: () => {
  28307. if (!hotkeys._ready)
  28308. return;
  28309. EffectPlayer.playMany = false;
  28310. if (!EffectPlayer.isActive)
  28311. return;
  28312. EffectPlayer.playManyUp();
  28313. },
  28314. attachToDown: () => {
  28315. if (!hotkeys._ready)
  28316. return;
  28317. PlayerSettings.attachTo.store.set(true);
  28318. PlayerSettings.stretchToAttach.store.set(true);
  28319. },
  28320. attachToUp: () => {
  28321. if (!hotkeys._ready)
  28322. return;
  28323. PlayerSettings.attachTo.store.set(false);
  28324. PlayerSettings.stretchToAttach.store.set(false);
  28325. }
  28326. },
  28327. selectTool: {
  28328. snapToGridDown: () => {
  28329. if (!hotkeys._ready)
  28330. return;
  28331. SelectionManager.snapToGrid = true;
  28332. },
  28333. snapToGridUp: () => {
  28334. if (!hotkeys._ready)
  28335. return;
  28336. SelectionManager.snapToGrid = false;
  28337. },
  28338. attachToTargetDown: () => {
  28339. if (!hotkeys._ready)
  28340. return;
  28341. if (!SelectionManager.isActive)
  28342. return;
  28343. PlayerSettings.attachTo.store.set(true);
  28344. PlayerSettings.stretchToAttach.store.set(true);
  28345. },
  28346. attachToTargetUp: () => {
  28347. if (!hotkeys._ready)
  28348. return;
  28349. PlayerSettings.attachTo.store.set(false);
  28350. PlayerSettings.stretchToAttach.store.set(false);
  28351. },
  28352. deleteDown: () => {
  28353. if (!hotkeys._ready)
  28354. return;
  28355. SelectionManager.delete();
  28356. }
  28357. }
  28358. };
  28359. function registerHotkeys() {
  28360. game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-control", {
  28361. name: "SEQUENCER.Hotkeys.PlayTool.Control",
  28362. editable: [{ key: "ControlLeft" }],
  28363. onDown: hotkeys.playTool.playManySequencedDown,
  28364. onUp: hotkeys.playTool.playManySequencedUp
  28365. });
  28366. game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-shift", {
  28367. name: "SEQUENCER.Hotkeys.PlayTool.Shift",
  28368. editable: [{ key: "ShiftLeft" }],
  28369. onDown: hotkeys.playTool.playManyDown,
  28370. onUp: hotkeys.playTool.playManyUp
  28371. });
  28372. game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-alt", {
  28373. name: "SEQUENCER.Hotkeys.PlayTool.Alt",
  28374. editable: [{ key: "AltLeft" }],
  28375. onDown: hotkeys.playTool.attachToDown,
  28376. onUp: hotkeys.playTool.attachToDown
  28377. });
  28378. game.keybindings.register(
  28379. CONSTANTS.MODULE_NAME,
  28380. "select-tool-hotkey-control",
  28381. {
  28382. name: "SEQUENCER.Hotkeys.SelectTool.Control",
  28383. editable: [{ key: "ControlLeft" }],
  28384. onDown: hotkeys.selectTool.snapToGridDown,
  28385. onUp: hotkeys.selectTool.snapToGridUp
  28386. }
  28387. );
  28388. game.keybindings.register(CONSTANTS.MODULE_NAME, "select-tool-hotkey-alt", {
  28389. name: "SEQUENCER.Hotkeys.SelectTool.Alt",
  28390. editable: [{ key: "AltLeft" }],
  28391. onDown: hotkeys.selectTool.attachToTargetDown,
  28392. onUp: hotkeys.selectTool.attachToTargetUp
  28393. });
  28394. game.keybindings.register(
  28395. CONSTANTS.MODULE_NAME,
  28396. "select-tool-hotkey-delete",
  28397. {
  28398. name: "SEQUENCER.Hotkeys.SelectTool.Delete",
  28399. editable: [{ key: "Delete" }],
  28400. onDown: hotkeys.selectTool.deleteDown
  28401. }
  28402. );
  28403. }
  28404. async function registerTypes(register) {
  28405. fetch("modules/sequencer/typings/types.d.ts").then((response) => response.text()).then((content) => register("sequencer/types.d.ts", content));
  28406. }
  28407. const presetMap = /* @__PURE__ */ new Map();
  28408. class SequencerPresets {
  28409. /**
  28410. * Adds a preset that can then be used in sequences
  28411. *
  28412. * @param {string} inName
  28413. * @param {Function} inFunction
  28414. * @param {boolean} [overwrite=false] overwrite
  28415. * @returns {Map<string, Function>}
  28416. */
  28417. static add(inName, inFunction, overwrite = false) {
  28418. if (typeof inName !== "string") {
  28419. throw custom_error(
  28420. "Sequencer",
  28421. `SequencerPresets | inName must be of type string`
  28422. );
  28423. }
  28424. if (!is_function$1(inFunction)) {
  28425. throw custom_error(
  28426. "Sequencer",
  28427. `SequencerPresets | inFunction must be of type function`
  28428. );
  28429. }
  28430. if (presetMap.get(inName) && !overwrite) {
  28431. throw custom_error(
  28432. "Sequencer",
  28433. `SequencerPresets | Preset "${inName}" already exists`
  28434. );
  28435. }
  28436. presetMap.set(inName, inFunction);
  28437. console.log(`Sequencer | Presets | Added "${inName}" preset`);
  28438. return presetMap;
  28439. }
  28440. /**
  28441. * Retrieves all presets
  28442. *
  28443. * @returns {Map<string, Function>}
  28444. */
  28445. static getAll() {
  28446. return presetMap;
  28447. }
  28448. /**
  28449. * Retrieves preset based on its name
  28450. *
  28451. * @param {string} name
  28452. * @returns {Function}
  28453. */
  28454. static get(name) {
  28455. return presetMap.get(name);
  28456. }
  28457. }
  28458. class Section {
  28459. constructor(inSequence) {
  28460. this.sequence = inSequence;
  28461. this._applyTraits();
  28462. this._sectionStatus = writable$1(CONSTANTS.STATUS.READY);
  28463. this._playIf = true;
  28464. this._waitUntilFinished = false;
  28465. this._async = false;
  28466. this._waitUntilFinishedDelay = [0, 0];
  28467. this._repetitions = 1;
  28468. this._currentRepetition = 0;
  28469. this._repeatDelayMin = 0;
  28470. this._repeatDelayMax = 0;
  28471. this._repeatDelay = 0;
  28472. this._delayMin = 0;
  28473. this._delayMax = 0;
  28474. this._basicDelay = 0;
  28475. this._duration = false;
  28476. }
  28477. static niceName = "Section";
  28478. /**
  28479. * @protected
  28480. */
  28481. get _shouldAsync() {
  28482. return this._async || this._waitAnyway;
  28483. }
  28484. /**
  28485. * @protected
  28486. */
  28487. get shouldWaitUntilFinished() {
  28488. return this._waitUntilFinished || this._waitAnyway;
  28489. }
  28490. /**
  28491. * @protected
  28492. */
  28493. get _waitAnyway() {
  28494. return (this._async || this._waitUntilFinished) && this._isLastRepetition || this._isLastRepetition && this._isLastSection;
  28495. }
  28496. /**
  28497. * @protected
  28498. */
  28499. get _isLastSection() {
  28500. return this.sequence.sections.length - 1 === this.sequence.sections.indexOf(this);
  28501. }
  28502. /** ------------------------------------------------------------------------------------------------------------------------------ *
  28503. * Methods below this point should NOT be overridden by child instances of the class, they are integral to the sequence functioning
  28504. * ------------------------------------------------------------------------------------------------------------------------------- */
  28505. /**
  28506. * @protected
  28507. */
  28508. get _isLastRepetition() {
  28509. return this._repetitions === 1 || this._repetitions === this._currentRepetition + 1;
  28510. }
  28511. /**
  28512. * @protected
  28513. */
  28514. get _currentWaitTime() {
  28515. let waitUntilFinishedDelay = this._waitAnyway ? random_int_between(...this._waitUntilFinishedDelay) : 0;
  28516. return waitUntilFinishedDelay + this._repeatDelay;
  28517. }
  28518. /**
  28519. * Method overwritten by inheriting classes, which is called just before the "run" method is called (see below)
  28520. *
  28521. * @returns {Promise<void>}
  28522. * @protected
  28523. */
  28524. async preRun() {
  28525. }
  28526. /**
  28527. * Method overwritten by inheriting classes, which is called when this section is executed by the Sequence
  28528. *
  28529. * @returns {Promise<void>}
  28530. * @protected
  28531. */
  28532. async run() {
  28533. }
  28534. /**
  28535. * Method overwritten by inheriting classes, which is called when this section is serialized by the Sequence
  28536. *
  28537. * @returns {object}
  28538. * @private
  28539. */
  28540. async _serialize() {
  28541. return {
  28542. async: this._async,
  28543. delay: [this._delayMin, this._delayMax],
  28544. waitUntilFinished: this._waitUntilFinished,
  28545. waitUntilFinishedDelay: this._waitUntilFinishedDelay,
  28546. repetitions: this._repetitions,
  28547. repetitionsDelay: [this._repeatDelayMin, this._repeatDelayMax]
  28548. };
  28549. }
  28550. _deserialize(data) {
  28551. this._async = data.async;
  28552. this._waitUntilFinished = data.waitUntilFinished;
  28553. this._waitUntilFinishedDelay = data.waitUntilFinishedDelay;
  28554. this._repetitions = data.repetitions;
  28555. this._repeatDelayMin = data.repetitionsDelay[0];
  28556. this._repeatDelayMax = data.repetitionsDelay[1];
  28557. return this;
  28558. }
  28559. /**
  28560. * Method overwritten by inheriting classes, which stores data or prepares data before the Sequence executes it (see EffectsSection)
  28561. *
  28562. * @protected
  28563. */
  28564. async _initialize() {
  28565. }
  28566. /**
  28567. * Method overwritten by inheriting classes. Inheriting classes uses the following to apply traits to themselves:
  28568. * - Object.assign(this.constructor.prototype, trait)
  28569. *
  28570. * @protected
  28571. */
  28572. _applyTraits() {
  28573. }
  28574. /**
  28575. * Causes the section to be repeated n amount of times, with an optional delay. If given inRepeatDelayMin
  28576. * and inRepeatDelayMax, a random repetition delay will be picked for every repetition
  28577. *
  28578. * @param {number} inRepetitions
  28579. * @param {number} inRepeatDelayMin
  28580. * @param {number} inRepeatDelayMax
  28581. * @returns {Section} this
  28582. */
  28583. repeats(inRepetitions, inRepeatDelayMin = 0, inRepeatDelayMax) {
  28584. if (!is_real_number(inRepetitions))
  28585. throw this.sequence._customError(
  28586. this,
  28587. "repeats",
  28588. "inRepetitions must be of type number"
  28589. );
  28590. if (!is_real_number(inRepeatDelayMin))
  28591. throw this.sequence._customError(
  28592. this,
  28593. "repeats",
  28594. "repeatDelayMin must be of type number"
  28595. );
  28596. if (inRepeatDelayMax && !is_real_number(inRepeatDelayMax)) {
  28597. throw this.sequence._customError(
  28598. this,
  28599. "repeats",
  28600. "repeatDelayMax must be of type number"
  28601. );
  28602. }
  28603. this._repetitions = inRepetitions;
  28604. this._repeatDelayMin = Math.min(
  28605. inRepeatDelayMin,
  28606. inRepeatDelayMax ?? inRepeatDelayMin
  28607. );
  28608. this._repeatDelayMax = Math.max(
  28609. inRepeatDelayMin,
  28610. inRepeatDelayMax ?? inRepeatDelayMin
  28611. );
  28612. return this;
  28613. }
  28614. /**
  28615. * Causes the effect or sound to not play, and skip all delays, repetitions, waits, etc. If you pass a function,
  28616. * the function should return something false-y if you do not want the effect or sound to play.
  28617. *
  28618. * @param {boolean|function} inCondition
  28619. * @returns {Section} this
  28620. */
  28621. playIf(inCondition) {
  28622. this._playIf = inCondition;
  28623. return this;
  28624. }
  28625. /**
  28626. * Causes the section to finish running before starting the next section.
  28627. *
  28628. * @param {number|boolean} [minDelay=0] minDelay
  28629. * @param {number/null} [maxDelay=null] maxDelay
  28630. * @returns {Section} this
  28631. */
  28632. waitUntilFinished(minDelay = 0, maxDelay = null) {
  28633. if (minDelay === false)
  28634. return this;
  28635. if (!is_real_number(minDelay))
  28636. throw this.sequence._customError(
  28637. this,
  28638. "waitUntilFinished",
  28639. "minDelay must be of type number"
  28640. );
  28641. if (maxDelay !== null && !is_real_number(maxDelay))
  28642. throw this.sequence._customError(
  28643. this,
  28644. "waitUntilFinished",
  28645. "maxDelay must be of type number"
  28646. );
  28647. this._waitUntilFinished = true;
  28648. this._waitUntilFinishedDelay = [
  28649. Math.min(minDelay, maxDelay ?? minDelay),
  28650. Math.max(minDelay, maxDelay ?? minDelay)
  28651. ];
  28652. return this;
  28653. }
  28654. /**
  28655. * Causes each effect or sound to finish playing before the next one starts playing. This differs from
  28656. * .waitUntilFinished() in the sense that this is for each repetition, whilst .waitUntilFinished() is
  28657. * for the entire section.
  28658. *
  28659. * @returns {Section} this
  28660. */
  28661. async() {
  28662. this._async = true;
  28663. return this;
  28664. }
  28665. /**
  28666. * Delays the effect or sound from being played for a set amount of milliseconds. If given a second number, a
  28667. * random delay between the two numbers will be generated.
  28668. *
  28669. * @param {number} [msMin=1] minMs
  28670. * @param {number} [msMax=1] maxMs
  28671. * @returns {Section} this
  28672. */
  28673. delay(msMin, msMax) {
  28674. if (!is_real_number(msMin))
  28675. throw this.sequence._customError(
  28676. this,
  28677. "delay",
  28678. "msMin must be of type number"
  28679. );
  28680. if (msMax && !is_real_number(msMax))
  28681. throw this.sequence._customError(
  28682. this,
  28683. "delay",
  28684. "msMax must be of type number"
  28685. );
  28686. this._delayMin = Math.min(msMin, msMax ?? msMin);
  28687. this._delayMax = Math.max(msMin, msMax ?? msMin);
  28688. return this;
  28689. }
  28690. /**
  28691. * Overrides the duration of this section
  28692. *
  28693. * @param {number} inDuration
  28694. * @returns {Section} this
  28695. */
  28696. duration(inDuration) {
  28697. if (!is_real_number(inDuration))
  28698. throw this.sequence._customError(
  28699. this,
  28700. "duration",
  28701. "inDuration must be of type number"
  28702. );
  28703. this._duration = inDuration;
  28704. return this;
  28705. }
  28706. /**
  28707. * Applies a preset to the current section
  28708. *
  28709. * @param {string} presetName
  28710. * @param {*} args
  28711. * @returns {Section|FunctionSection|EffectSection|AnimationSection|SoundSection}
  28712. */
  28713. preset(presetName, ...args) {
  28714. if (typeof presetName !== "string") {
  28715. throw this.sequence._customError(
  28716. this,
  28717. "name",
  28718. `inName must be of type string`
  28719. );
  28720. }
  28721. const preset = SequencerPresets.get(presetName);
  28722. if (!preset) {
  28723. custom_warning(
  28724. "Sequencer",
  28725. `preset | Could not find preset with name "${presetName}"`
  28726. );
  28727. return this;
  28728. }
  28729. return preset(this, ...args);
  28730. }
  28731. /**
  28732. * @protected
  28733. */
  28734. async _shouldPlay() {
  28735. return is_function$1(this._playIf) ? await this._playIf() : this._playIf;
  28736. }
  28737. /**
  28738. * @protected
  28739. */
  28740. _validateLocation(inLocation) {
  28741. inLocation = validate_document(inLocation);
  28742. if (typeof inLocation === "string") {
  28743. inLocation = get_object_from_scene(inLocation) ?? inLocation;
  28744. }
  28745. if (typeof inLocation === "string") {
  28746. inLocation = safe_str(inLocation);
  28747. }
  28748. return inLocation;
  28749. }
  28750. /**
  28751. * @protected
  28752. */
  28753. async _execute() {
  28754. if (!await this._shouldPlay()) {
  28755. this.sectionStatus = CONSTANTS.STATUS.SKIPPED;
  28756. return;
  28757. }
  28758. this._basicDelay = random_float_between(this._delayMin, this._delayMax);
  28759. return new Promise(async (resolve) => {
  28760. setTimeout(async () => {
  28761. this.sectionStatus = CONSTANTS.STATUS.RUNNING;
  28762. for (let i = 0; i < this._repetitions; i++) {
  28763. if (get_store_value(this.sectionStatus) === CONSTANTS.STATUS.ABORTED) {
  28764. resolve();
  28765. return;
  28766. }
  28767. this._currentRepetition = i;
  28768. this._repeatDelay = i !== this._repetitions - 1 ? random_float_between(
  28769. this._repeatDelayMin,
  28770. this._repeatDelayMax
  28771. ) : 0;
  28772. await this.preRun();
  28773. if (this._shouldAsync) {
  28774. await this.run();
  28775. } else {
  28776. this.run();
  28777. }
  28778. if (this._repetitions > 1 && i !== this._repetitions - 1) {
  28779. await this._delayBetweenRepetitions();
  28780. }
  28781. }
  28782. resolve();
  28783. }, this._basicDelay);
  28784. }).then(() => {
  28785. this.sectionStatus = CONSTANTS.STATUS.COMPLETE;
  28786. });
  28787. }
  28788. set sectionStatus(inStatus) {
  28789. this._sectionStatus.update((currentStatus) => {
  28790. if (currentStatus === CONSTANTS.STATUS.READY || currentStatus === CONSTANTS.STATUS.RUNNING && inStatus !== CONSTANTS.STATUS.ABORTED) {
  28791. return inStatus;
  28792. }
  28793. return currentStatus;
  28794. });
  28795. }
  28796. get sectionStatus() {
  28797. return this._sectionStatus;
  28798. }
  28799. _abortSection() {
  28800. this.sectionStatus = CONSTANTS.STATUS.ABORTED;
  28801. }
  28802. /**
  28803. * @protected
  28804. */
  28805. async _delayBetweenRepetitions() {
  28806. let self = this;
  28807. return new Promise((resolve) => {
  28808. setTimeout(resolve, self._repeatDelay);
  28809. });
  28810. }
  28811. }
  28812. class FunctionSection extends Section {
  28813. constructor(inSequence, inFunc) {
  28814. super(inSequence);
  28815. if (!is_function$1(inFunc))
  28816. this._customError(
  28817. "create",
  28818. "The given function needs to be an actual function"
  28819. );
  28820. this._func = inFunc;
  28821. this._waitUntilFinished = inFunc.constructor.name === "AsyncFunction";
  28822. }
  28823. static niceName = "Function";
  28824. /**
  28825. * @returns {Promise<void>}
  28826. */
  28827. async run() {
  28828. debug("Running function");
  28829. await this._func();
  28830. }
  28831. /**
  28832. * @returns {Promise}
  28833. * @private
  28834. */
  28835. async _execute() {
  28836. await this.run();
  28837. }
  28838. }
  28839. const animation = {
  28840. /**
  28841. * Base properties
  28842. */
  28843. _animations: null,
  28844. /**
  28845. * Animates a property on the target of the animation.
  28846. *
  28847. * @param {string} inTarget
  28848. * @param {string} inPropertyName
  28849. * @param {object} inOptions
  28850. * @param {Number} inOptions.from - a single number from which to animate
  28851. * @param {Number} inOptions.to - a single number to which to animate
  28852. * @param {Number} inOptions.duration - how long in ms the animation should take
  28853. * @param {Number} inOptions.delay - inserts a delay in ms before the animation starts
  28854. * @param {String} inOptions.ease - what type of easing the animation should use
  28855. * @param {Boolean} inOptions.gridUnits - if animating width or height, this will set it to work in the scene's grid units
  28856. * @param {Boolean} inOptions.fromEnd - makes this animation play from the end, like fadeOut, scaleOut, etc
  28857. *
  28858. * @returns this
  28859. */
  28860. animateProperty(inTarget, inPropertyName, inOptions = {}) {
  28861. if (!this._animations)
  28862. this._animations = [];
  28863. const result = validateAnimation(
  28864. inTarget,
  28865. inPropertyName,
  28866. inOptions
  28867. );
  28868. if (typeof result === "string") {
  28869. throw this.sequence._customError(this, "animateProperty", result);
  28870. }
  28871. this._animations.push(result);
  28872. return this;
  28873. },
  28874. /**
  28875. * Loops a property between a set of values on the target
  28876. *
  28877. * @param {string} inTarget
  28878. * @param {string} inPropertyName
  28879. * @param {object} inOptions
  28880. * @param {Number} inOptions.from - a single number from which to loop
  28881. * @param {Number} inOptions.to - a single number to which to loop
  28882. * @param {Number} inOptions.values - an array of values to loop between
  28883. * @param {Number} inOptions.duration - how long in ms the loop should take
  28884. * @param {Number} inOptions.loops - how many loops in total this animation should go through - if none are specified, the loop is indefinite
  28885. * @param {Number} inOptions.delay - inserts a delay in ms before the animation starts
  28886. * @param {String} inOptions.ease - what type of easing the animation should use
  28887. * @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
  28888. * @param {Boolean} inOptions.gridUnits - if animating width or height, this will set it to work in the scene's grid units
  28889. *
  28890. * @returns this
  28891. */
  28892. loopProperty(inTarget, inPropertyName, inOptions = {}) {
  28893. if (!this._animations)
  28894. this._animations = [];
  28895. const result = validateLoopingAnimation(
  28896. inTarget,
  28897. inPropertyName,
  28898. inOptions
  28899. );
  28900. if (typeof result === "string") {
  28901. throw this.sequence._customError(this, "loopProperty", result);
  28902. }
  28903. this._animations.push(result);
  28904. return this;
  28905. }
  28906. };
  28907. const audio = {
  28908. /**
  28909. * Base properties
  28910. */
  28911. _volume: null,
  28912. _fadeInAudio: null,
  28913. _fadeOutAudio: null,
  28914. /**
  28915. * Sets the volume of the sound.
  28916. *
  28917. * @param {number} inVolume
  28918. * @returns this
  28919. */
  28920. volume(inVolume) {
  28921. if (!is_real_number(inVolume))
  28922. throw this.sequence._customError(
  28923. this,
  28924. "volume",
  28925. "inVolume must be of type number"
  28926. );
  28927. this._volume = Math.max(0, Math.min(1, inVolume));
  28928. return this;
  28929. },
  28930. /**
  28931. * Causes the animated section to fade in its audio (if any) when played
  28932. *
  28933. * @param {number} duration How long the fade should be
  28934. * @param {object} [options] Additional options, such as easing and delay
  28935. * @returns this
  28936. */
  28937. fadeInAudio(duration, options = {}) {
  28938. if (typeof options !== "object")
  28939. throw this.sequence._customError(
  28940. this,
  28941. "fadeInAudio",
  28942. "options must be of type object"
  28943. );
  28944. options = foundry.utils.mergeObject(
  28945. {
  28946. ease: "linear",
  28947. delay: 0
  28948. },
  28949. options
  28950. );
  28951. if (!is_real_number(duration))
  28952. throw this.sequence._customError(
  28953. this,
  28954. "fadeInAudio",
  28955. "duration must be of type number"
  28956. );
  28957. if (typeof options.ease !== "string")
  28958. throw this.sequence._customError(
  28959. this,
  28960. "fadeInAudio",
  28961. "options.ease must be of type string"
  28962. );
  28963. if (!is_real_number(options.delay))
  28964. throw this.sequence._customError(
  28965. this,
  28966. "fadeInAudio",
  28967. "options.delay must be of type number"
  28968. );
  28969. this._fadeInAudio = {
  28970. duration,
  28971. ease: options.ease,
  28972. delay: options.delay
  28973. };
  28974. return this;
  28975. },
  28976. /**
  28977. * Causes the audio to fade out at the end of the animated section's duration
  28978. *
  28979. * @param {number} duration How long the fade should be
  28980. * @param {object} [options] Additional options, such as easing and delay
  28981. * @returns this
  28982. */
  28983. fadeOutAudio(duration, options = {}) {
  28984. if (typeof options !== "object")
  28985. throw this.sequence._customError(
  28986. this,
  28987. "fadeOutAudio",
  28988. "options must be of type object"
  28989. );
  28990. options = foundry.utils.mergeObject(
  28991. {
  28992. ease: "linear",
  28993. delay: 0
  28994. },
  28995. options
  28996. );
  28997. if (!is_real_number(duration))
  28998. throw this.sequence._customError(
  28999. this,
  29000. "fadeOutAudio",
  29001. "duration must be of type number"
  29002. );
  29003. if (typeof options.ease !== "string")
  29004. throw this.sequence._customError(
  29005. this,
  29006. "fadeOutAudio",
  29007. "ease must be of type string"
  29008. );
  29009. if (!is_real_number(options.delay))
  29010. throw this.sequence._customError(
  29011. this,
  29012. "fadeOutAudio",
  29013. "delay must be of type number"
  29014. );
  29015. this._fadeOutAudio = {
  29016. duration,
  29017. ease: options.ease,
  29018. delay: options.delay
  29019. };
  29020. return this;
  29021. }
  29022. };
  29023. const files = {
  29024. /**
  29025. * Base properties
  29026. */
  29027. _file: "",
  29028. _fileOptions: false,
  29029. _baseFolder: "",
  29030. _mustache: null,
  29031. /**
  29032. * Declares which file to be played. This may also be an array of paths, which will be randomly picked from each
  29033. * time the section is played.
  29034. *
  29035. * @param {string|array} inFile
  29036. * @returns this
  29037. */
  29038. file(inFile) {
  29039. this._file = inFile;
  29040. return this;
  29041. },
  29042. /**
  29043. * Defines the base folder that will prepend to the file path. This is mainly just useful to make the file
  29044. * path easier to manage.
  29045. *
  29046. * @param {string} inBaseFolder
  29047. * @returns this
  29048. */
  29049. baseFolder(inBaseFolder) {
  29050. if (typeof inBaseFolder !== "string")
  29051. throw this.sequence._customError(
  29052. this,
  29053. "baseFolder",
  29054. "inBaseFolder must be of type string"
  29055. );
  29056. this._baseFolder = inBaseFolder + (inBaseFolder.endsWith("/") ? "" : "/");
  29057. return this;
  29058. },
  29059. /**
  29060. * Sets the Mustache of the filepath. This is applied after the randomization of the filepath, if available.
  29061. *
  29062. * @param {object} inMustache
  29063. * @returns this
  29064. */
  29065. setMustache(inMustache) {
  29066. if (typeof inMustache !== "object")
  29067. throw this.sequence._customError(
  29068. this,
  29069. "setMustache",
  29070. "inMustache must be of type object"
  29071. );
  29072. this._mustache = inMustache;
  29073. return this;
  29074. },
  29075. async _determineFile(inFile) {
  29076. if (!Array.isArray(inFile) && typeof inFile === "object") {
  29077. return this._validateCustomRange(inFile);
  29078. }
  29079. if (Array.isArray(inFile))
  29080. inFile = random_array_element(inFile, { recurse: true });
  29081. inFile = this._applyMustache(inFile);
  29082. if (Sequencer.Database.entryExists(inFile)) {
  29083. return this._determineDatabaseFile(inFile);
  29084. }
  29085. const determinedFile = await this._processFile(inFile);
  29086. return { file: determinedFile, forcedIndex: false, customRange: false };
  29087. },
  29088. async _processFile(inFile) {
  29089. inFile = this._applyMustache(inFile);
  29090. inFile = this._applyBaseFolder(inFile);
  29091. inFile = await this._applyWildcard(inFile);
  29092. if (Array.isArray(inFile))
  29093. inFile = random_array_element(inFile, { recurse: true });
  29094. return inFile;
  29095. },
  29096. async _validateCustomRange(inFile) {
  29097. const finalFiles = {};
  29098. const validRanges = Object.keys(SequencerFileRangeFind.ftToDistanceMap);
  29099. for (const [range, rangeFile] of Object.entries(inFile)) {
  29100. if (!validRanges.includes(range)) {
  29101. throw this.sequence._customError(
  29102. this,
  29103. "file",
  29104. `a file-distance key map must only contain the following keys: ${validRanges.join(
  29105. ", "
  29106. )}`
  29107. );
  29108. }
  29109. finalFiles[range] = await this._processFile(rangeFile);
  29110. }
  29111. return { file: finalFiles, forcedIndex: false, customRange: true };
  29112. },
  29113. _determineDatabaseFile(inFile) {
  29114. const entries = Sequencer.Database.getEntry(inFile);
  29115. const entry = Array.isArray(entries) ? random_array_element(entries) : entries;
  29116. const match = inFile.match(/(\d)+$/);
  29117. return {
  29118. file: entry,
  29119. forcedIndex: match ? Number(match[1]) : false,
  29120. customRange: false
  29121. };
  29122. },
  29123. _applyBaseFolder(inFile) {
  29124. if (Array.isArray(inFile))
  29125. return inFile.map((file) => this._applyBaseFolder(file));
  29126. return inFile.startsWith(this._baseFolder) ? inFile : this._baseFolder + inFile;
  29127. },
  29128. _applyMustache(inFile) {
  29129. if (!this._mustache)
  29130. return inFile;
  29131. let template = Handlebars.compile(inFile);
  29132. return template(this._mustache);
  29133. },
  29134. async _applyWildcard(inFile) {
  29135. if (!inFile.includes("*"))
  29136. return inFile;
  29137. if (Array.isArray(inFile))
  29138. return inFile.map(async (file) => await this._applyWildcard(file));
  29139. inFile = this._applyBaseFolder(inFile);
  29140. return getFiles(inFile, {
  29141. applyWildCard: true,
  29142. softFail: this.sequence.softFail
  29143. });
  29144. }
  29145. };
  29146. const moves = {
  29147. /**
  29148. * Base properties
  29149. */
  29150. _moveTowards: null,
  29151. _moveSpeed: null,
  29152. /**
  29153. * Sets the location to move the target object to
  29154. *
  29155. * @param {object|string} inTarget
  29156. * @param {object} options
  29157. * @returns this
  29158. */
  29159. moveTowards(inTarget, options = {}) {
  29160. options = foundry.utils.mergeObject(
  29161. {
  29162. ease: "linear",
  29163. delay: 0,
  29164. rotate: true,
  29165. cacheLocation: false
  29166. },
  29167. options
  29168. );
  29169. if (typeof options.ease !== "string")
  29170. throw this.sequence._customError(
  29171. this,
  29172. "moveTowards",
  29173. "options.ease must be of type string"
  29174. );
  29175. if (!is_real_number(options.delay))
  29176. throw this.sequence._customError(
  29177. this,
  29178. "moveTowards",
  29179. "options.delay must be of type number"
  29180. );
  29181. if (typeof options.rotate !== "boolean")
  29182. throw this.sequence._customError(
  29183. this,
  29184. "moveTowards",
  29185. "options.rotate must be of type boolean"
  29186. );
  29187. if (typeof options.cacheLocation !== "boolean")
  29188. throw this.sequence._customError(
  29189. this,
  29190. "moveTowards",
  29191. "options.cacheLocation must be of type boolean"
  29192. );
  29193. options.target = this._validateLocation(inTarget);
  29194. if (!options.target)
  29195. throw this.sequence._customError(
  29196. this,
  29197. "moveTowards",
  29198. "could not find position of given object"
  29199. );
  29200. options.target = options.cacheLocation ? get_object_position(options.cacheLocation, { measure: true }) : options.target;
  29201. this._moveTowards = options;
  29202. return this;
  29203. },
  29204. /**
  29205. * Sets the speed (pixels per frame) to move the target object
  29206. *
  29207. * @param {number} inSpeed
  29208. * @returns this
  29209. */
  29210. moveSpeed(inSpeed) {
  29211. if (!is_real_number(inSpeed))
  29212. throw this.sequence._customError(
  29213. this,
  29214. "moveSpeed",
  29215. "inSpeed must be of type number"
  29216. );
  29217. this._moveSpeed = inSpeed;
  29218. return this;
  29219. }
  29220. };
  29221. const opacity = {
  29222. /**
  29223. * Base properties
  29224. */
  29225. _opacity: null,
  29226. _fadeIn: null,
  29227. _fadeOut: null,
  29228. /**
  29229. * Sets the opacity of the effect. If used with ._fadeIn() and/or ._fadeOut(), this defines what the effect will fade to/from
  29230. *
  29231. * @param {number} inOpacity
  29232. * @returns this
  29233. */
  29234. opacity(inOpacity) {
  29235. if (!is_real_number(inOpacity))
  29236. throw this.sequence._customError(
  29237. this,
  29238. "opacity",
  29239. "inOpacity must be of type number"
  29240. );
  29241. this._opacity = inOpacity;
  29242. return this;
  29243. },
  29244. /**
  29245. * Causes the effect to fade in when played
  29246. *
  29247. * @param {number} duration How long the fade should be
  29248. * @param {object} [options] Additional options, such as easing and delay
  29249. * @returns this
  29250. */
  29251. fadeIn(duration, options = {}) {
  29252. if (typeof options !== "object")
  29253. throw this.sequence._customError(
  29254. this,
  29255. "fadeIn",
  29256. "options must be of type object"
  29257. );
  29258. options = foundry.utils.mergeObject(
  29259. {
  29260. ease: "linear",
  29261. delay: 0
  29262. },
  29263. options
  29264. );
  29265. if (!is_real_number(duration))
  29266. throw this.sequence._customError(
  29267. this,
  29268. "fadeIn",
  29269. "duration must be of type number"
  29270. );
  29271. if (typeof options.ease !== "string")
  29272. throw this.sequence._customError(
  29273. this,
  29274. "fadeIn",
  29275. "options.ease must be of type string"
  29276. );
  29277. if (!is_real_number(options.delay))
  29278. throw this.sequence._customError(
  29279. this,
  29280. "fadeIn",
  29281. "options.delay must be of type number"
  29282. );
  29283. this._fadeIn = {
  29284. duration,
  29285. ease: options.ease,
  29286. delay: options.delay
  29287. };
  29288. return this;
  29289. },
  29290. /**
  29291. * Causes the effect to fade out at the end of the effect's duration
  29292. *
  29293. * @param {number} duration How long the fade should be
  29294. * @param {object} [options] Additional options, such as easing and delay
  29295. * @returns this
  29296. */
  29297. fadeOut(duration, options = {}) {
  29298. if (typeof options !== "object")
  29299. throw this.sequence._customError(
  29300. this,
  29301. "fadeOut",
  29302. "options must be of type object"
  29303. );
  29304. options = foundry.utils.mergeObject(
  29305. {
  29306. ease: "linear",
  29307. delay: 0
  29308. },
  29309. options
  29310. );
  29311. if (!is_real_number(duration))
  29312. throw this.sequence._customError(
  29313. this,
  29314. "fadeOut",
  29315. "duration must be of type number"
  29316. );
  29317. if (typeof options.ease !== "string")
  29318. throw this.sequence._customError(
  29319. this,
  29320. "fadeOut",
  29321. "ease must be of type string"
  29322. );
  29323. if (!is_real_number(options.delay))
  29324. throw this.sequence._customError(
  29325. this,
  29326. "fadeOut",
  29327. "delay must be of type number"
  29328. );
  29329. this._fadeOut = {
  29330. duration,
  29331. ease: options.ease,
  29332. delay: options.delay
  29333. };
  29334. return this;
  29335. }
  29336. };
  29337. const rotation = {
  29338. /**
  29339. * Base properties
  29340. */
  29341. _angle: null,
  29342. _rotateIn: null,
  29343. _rotateOut: null,
  29344. _randomRotation: null,
  29345. _rotateTowards: null,
  29346. /**
  29347. * The object gets a random rotation, which means it should not be used with .stretchTo()
  29348. *
  29349. * @param {boolean} [inBool=true] inBool
  29350. * @returns this
  29351. */
  29352. randomRotation(inBool = true) {
  29353. if (typeof inBool !== "boolean")
  29354. throw this.sequence._customError(
  29355. this,
  29356. "randomRotation",
  29357. "inBool must be of type boolean"
  29358. );
  29359. this._randomRotation = inBool;
  29360. return this;
  29361. },
  29362. /**
  29363. * Sets the rotation of the object, which is added on top of the calculated rotation after .rotateTowards() or .randomRotation()
  29364. *
  29365. * @param {number} inRotation
  29366. * @returns this
  29367. */
  29368. rotate(inRotation) {
  29369. if (!is_real_number(inRotation))
  29370. throw this.sequence._customError(
  29371. this,
  29372. "opacity",
  29373. "inRotation must be of type number"
  29374. );
  29375. this._angle = inRotation;
  29376. return this;
  29377. },
  29378. /**
  29379. * Causes the object to rotate when it starts playing
  29380. *
  29381. * @param {number} degrees
  29382. * @param {number} duration
  29383. * @param {object} [options] options
  29384. * @returns this
  29385. */
  29386. rotateIn(degrees, duration, options = {}) {
  29387. if (typeof options !== "object")
  29388. throw this.sequence._customError(
  29389. this,
  29390. "rotateIn",
  29391. "options must be of type object"
  29392. );
  29393. options = foundry.utils.mergeObject(
  29394. {
  29395. ease: "linear",
  29396. delay: 0
  29397. },
  29398. options
  29399. );
  29400. if (!is_real_number(degrees))
  29401. throw this.sequence._customError(
  29402. this,
  29403. "rotateOut",
  29404. "degrees must be of type number"
  29405. );
  29406. if (!is_real_number(duration))
  29407. throw this.sequence._customError(
  29408. this,
  29409. "rotateOut",
  29410. "duration must be of type number"
  29411. );
  29412. if (typeof options.ease !== "string")
  29413. throw this.sequence._customError(
  29414. this,
  29415. "rotateIn",
  29416. "options.ease must be of type string"
  29417. );
  29418. if (!is_real_number(options.delay))
  29419. throw this.sequence._customError(
  29420. this,
  29421. "rotateIn",
  29422. "options.delay must be of type number"
  29423. );
  29424. this._rotateIn = {
  29425. value: degrees,
  29426. duration,
  29427. ease: options.ease,
  29428. delay: options.delay
  29429. };
  29430. return this;
  29431. },
  29432. /**
  29433. * Causes the object to rotate at the end of the effect's duration
  29434. *
  29435. * @param {number} degrees
  29436. * @param {number} duration
  29437. * @param {object} [options] options
  29438. * @returns this
  29439. */
  29440. rotateOut(degrees, duration, options = {}) {
  29441. if (typeof options !== "object")
  29442. throw this.sequence._customError(
  29443. this,
  29444. "rotateOut",
  29445. "options must be of type object"
  29446. );
  29447. options = foundry.utils.mergeObject(
  29448. {
  29449. ease: "linear",
  29450. delay: 0
  29451. },
  29452. options
  29453. );
  29454. if (!is_real_number(degrees))
  29455. throw this.sequence._customError(
  29456. this,
  29457. "rotateOut",
  29458. "degrees must be of type number"
  29459. );
  29460. if (!is_real_number(duration))
  29461. throw this.sequence._customError(
  29462. this,
  29463. "rotateOut",
  29464. "duration must be of type number"
  29465. );
  29466. if (typeof options.ease !== "string")
  29467. throw this.sequence._customError(
  29468. this,
  29469. "rotateOut",
  29470. "options.ease must be of type string"
  29471. );
  29472. if (!is_real_number(options.delay))
  29473. throw this.sequence._customError(
  29474. this,
  29475. "rotateOut",
  29476. "options.delay must be of type number"
  29477. );
  29478. this._rotateOut = {
  29479. value: degrees,
  29480. duration,
  29481. ease: options.ease,
  29482. delay: options.delay
  29483. };
  29484. return this;
  29485. }
  29486. };
  29487. const scale = {
  29488. _scaleMin: null,
  29489. _scaleMax: null,
  29490. _scaleIn: null,
  29491. _scaleOut: null,
  29492. /**
  29493. * A method that can take the following:
  29494. * - A number to set the scale uniformly
  29495. * - An object with x and y for non-uniform scaling
  29496. * - Two numbers which the Sequencer will randomly pick a uniform scale between
  29497. *
  29498. * @param {number|object} inScaleMin
  29499. * @param {number} [inScaleMax] inScaleMax
  29500. * @returns this
  29501. */
  29502. scale(inScaleMin, inScaleMax) {
  29503. if (!is_real_number(inScaleMin) && typeof inScaleMin !== "object")
  29504. throw this.sequence._customError(
  29505. this,
  29506. "scale",
  29507. "inScale must be of type number or object"
  29508. );
  29509. if (is_real_number(inScaleMin)) {
  29510. if (inScaleMax && !is_real_number(inScaleMax)) {
  29511. throw this.sequence._customError(
  29512. this,
  29513. "scale",
  29514. "if inScaleMin is a number, inScaleMax must also be of type number"
  29515. );
  29516. }
  29517. }
  29518. this._scaleMin = inScaleMin;
  29519. this._scaleMax = inScaleMax ?? false;
  29520. return this;
  29521. },
  29522. /**
  29523. * Causes the effect to scale when it starts playing
  29524. *
  29525. * @param {number|object} scale
  29526. * @param {number} duration
  29527. * @param {object} [options] options
  29528. * @returns this
  29529. */
  29530. scaleIn(scale2, duration, options = {}) {
  29531. if (typeof options !== "object")
  29532. throw this.sequence._customError(
  29533. this,
  29534. "scaleIn",
  29535. "options must be of type object"
  29536. );
  29537. options = foundry.utils.mergeObject(
  29538. {
  29539. ease: "linear",
  29540. delay: 0
  29541. },
  29542. options
  29543. );
  29544. if (!is_real_number(duration))
  29545. throw this.sequence._customError(
  29546. this,
  29547. "scaleIn",
  29548. "duration must be of type number"
  29549. );
  29550. if (!is_real_number(scale2) && typeof scale2 !== "object")
  29551. throw this.sequence._customError(
  29552. this,
  29553. "scaleIn",
  29554. "scale must be of type number or object"
  29555. );
  29556. if (typeof options.ease !== "string")
  29557. throw this.sequence._customError(
  29558. this,
  29559. "scaleIn",
  29560. "options.ease must be of type string"
  29561. );
  29562. if (!is_real_number(options.delay))
  29563. throw this.sequence._customError(
  29564. this,
  29565. "scaleIn",
  29566. "options.delay must be of type number"
  29567. );
  29568. this._scaleIn = {
  29569. value: scale2,
  29570. duration,
  29571. ease: options.ease,
  29572. delay: options.delay
  29573. };
  29574. return this;
  29575. },
  29576. /**
  29577. * Causes the effect to scale at the end of the effect's duration
  29578. *
  29579. * @param {number|object} scale
  29580. * @param {number} duration
  29581. * @param {object} [options] options
  29582. * @returns this
  29583. */
  29584. scaleOut(scale2, duration, options = {}) {
  29585. if (typeof options !== "object")
  29586. throw this.sequence._customError(
  29587. this,
  29588. "scaleOut",
  29589. "options must be of type object"
  29590. );
  29591. options = foundry.utils.mergeObject(
  29592. {
  29593. ease: "linear",
  29594. delay: 0
  29595. },
  29596. options
  29597. );
  29598. if (!is_real_number(duration))
  29599. throw this.sequence._customError(
  29600. this,
  29601. "scaleOut",
  29602. "duration must be of type number"
  29603. );
  29604. if (!is_real_number(scale2) && typeof scale2 !== "object")
  29605. throw this.sequence._customError(
  29606. this,
  29607. "scaleOut",
  29608. "scale must be of type number or object"
  29609. );
  29610. if (typeof options.ease !== "string")
  29611. throw this.sequence._customError(
  29612. this,
  29613. "scaleOut",
  29614. "options.ease must be of type string"
  29615. );
  29616. if (!is_real_number(options.delay))
  29617. throw this.sequence._customError(
  29618. this,
  29619. "scaleOut",
  29620. "options.delay must be of type number"
  29621. );
  29622. this._scaleOut = {
  29623. value: scale2,
  29624. duration,
  29625. ease: options.ease,
  29626. delay: options.delay
  29627. };
  29628. return this;
  29629. }
  29630. };
  29631. const time = {
  29632. _hasTime: true,
  29633. _isRange: false,
  29634. _startTime: null,
  29635. _startPerc: null,
  29636. _endTime: null,
  29637. _endPerc: null,
  29638. /**
  29639. * Sets the start and end time of the section, playing only that range
  29640. *
  29641. * @param {number} inMsStart
  29642. * @param {number} inMsEnd
  29643. * @returns this
  29644. */
  29645. timeRange(inMsStart, inMsEnd) {
  29646. if (!is_real_number(inMsStart))
  29647. throw this.sequence._customError(
  29648. this,
  29649. "timeRange",
  29650. "inMsStart must be of type number"
  29651. );
  29652. if (!is_real_number(inMsEnd))
  29653. throw this.sequence._customError(
  29654. this,
  29655. "timeRange",
  29656. "inMsEnd must be of type number"
  29657. );
  29658. this._startTime = inMsStart;
  29659. this._endTime = inMsEnd;
  29660. this._isRange = true;
  29661. return this;
  29662. },
  29663. /**
  29664. * Sets the start time of the section.
  29665. *
  29666. * @param {number} inMs
  29667. * @returns this
  29668. */
  29669. startTime(inMs) {
  29670. if (!is_real_number(inMs))
  29671. throw this.sequence._customError(
  29672. this,
  29673. "startTime",
  29674. "inMs must be of type number"
  29675. );
  29676. this._startTime = inMs;
  29677. this._startPerc = false;
  29678. this._isRange = false;
  29679. return this;
  29680. },
  29681. /**
  29682. * Sets the start time of the section based on a percentage from its total duration.
  29683. *
  29684. * @param {number} inPercentage
  29685. * @returns this
  29686. */
  29687. startTimePerc(inPercentage) {
  29688. if (!is_real_number(inPercentage))
  29689. throw this.sequence._customError(
  29690. this,
  29691. "startTimePerc",
  29692. "inPercentage must be of type number"
  29693. );
  29694. this._startTime = inPercentage;
  29695. this._startPerc = true;
  29696. this._isRange = false;
  29697. return this;
  29698. },
  29699. /**
  29700. * Sets the ending time of the section (from the end).
  29701. *
  29702. * @param {number} inMs
  29703. * @returns this
  29704. */
  29705. endTime(inMs) {
  29706. if (!is_real_number(inMs))
  29707. throw this.sequence._customError(
  29708. this,
  29709. "endTime",
  29710. "inMs must be of type number"
  29711. );
  29712. this._endTime = inMs;
  29713. this._endPerc = false;
  29714. this._isRange = false;
  29715. return this;
  29716. },
  29717. /**
  29718. * Sets the ending time of the section based on a percentage from the total duration.
  29719. *
  29720. * @param {number} inPercentage
  29721. * @returns this
  29722. */
  29723. endTimePerc(inPercentage) {
  29724. if (!is_real_number(inPercentage))
  29725. throw this.sequence._customError(
  29726. this,
  29727. "endTimePerc",
  29728. "inPercentage must be of type number"
  29729. );
  29730. this._endTime = inPercentage;
  29731. this._endPerc = true;
  29732. this._isRange = false;
  29733. return this;
  29734. }
  29735. };
  29736. const users = {
  29737. _users: null,
  29738. _addUser(inUser) {
  29739. if (!this._users)
  29740. this._users = [];
  29741. if (typeof inUser !== "string")
  29742. throw this.sequence._customError(
  29743. this,
  29744. "_addUser",
  29745. "inUser must be of type string"
  29746. );
  29747. if (!game.users.has(inUser)) {
  29748. if (game.users.getName(inUser)) {
  29749. inUser = game.users.getName(inUser).id;
  29750. } else {
  29751. throw this.sequence._customError(
  29752. this,
  29753. "_addUser",
  29754. `user with id or name "${inUser}" does not exist!`
  29755. );
  29756. }
  29757. }
  29758. if (!this._users.includes(inUser))
  29759. this._users.push(inUser);
  29760. },
  29761. _deleteUser(inUser) {
  29762. if (!this._users)
  29763. this._users = [];
  29764. if (this._users.includes(inUser)) {
  29765. let index = this._users.indexOf(inUser);
  29766. this._users.splice(index, 1);
  29767. }
  29768. },
  29769. /**
  29770. * Causes section to be executed only locally, and not push to other connected clients.
  29771. *
  29772. * @param {boolean} inLocally
  29773. * @returns this
  29774. */
  29775. locally(inLocally = true) {
  29776. if (inLocally)
  29777. this._addUser(game.userId);
  29778. else
  29779. this._deleteUser(game.userId);
  29780. return this;
  29781. },
  29782. /**
  29783. * Causes the section to be executed for only a set of users.
  29784. *
  29785. * @param {string|User|array<string|User>} inUsers
  29786. * @returns this
  29787. */
  29788. forUsers(inUsers) {
  29789. if (!Array.isArray(inUsers)) {
  29790. if (typeof inUsers !== "string")
  29791. throw this.sequence._customError(
  29792. this,
  29793. "forUsers",
  29794. "inUser must be of type string"
  29795. );
  29796. inUsers = [inUsers];
  29797. }
  29798. inUsers.forEach((u) => this._addUser(u));
  29799. return this;
  29800. }
  29801. };
  29802. const filter = {
  29803. _filters: null,
  29804. _addFilter(inFilterName, inData, inName = false) {
  29805. if (!this._filters)
  29806. this._filters = [];
  29807. this._filters.push({
  29808. className: inFilterName,
  29809. name: inName,
  29810. data: inData
  29811. });
  29812. },
  29813. _testFilter(inFilterName, inData) {
  29814. let filter2 = new filters[inFilterName](inData);
  29815. if (!filter2.isValid)
  29816. throw this.sequence._customError(
  29817. this,
  29818. "filter",
  29819. `Could not create ${inFilterName} filter - data is malformed!`
  29820. );
  29821. },
  29822. filter(inFilterName, inData = {}, inName = "") {
  29823. if (typeof inFilterName !== "string")
  29824. throw this.sequence._customError(
  29825. this,
  29826. "filter",
  29827. `inFilterName must be of type string`
  29828. );
  29829. if (!Object.keys(filters).includes(inFilterName))
  29830. throw this.sequence._customError(
  29831. this,
  29832. "filter",
  29833. `"${inFilterName}" does not exist`
  29834. );
  29835. this._testFilter(inFilterName, inData);
  29836. this._addFilter(inFilterName, inData, inName);
  29837. return this;
  29838. }
  29839. };
  29840. const tint = {
  29841. _tint: null,
  29842. /**
  29843. * Tints the target of this section by the color given to the
  29844. *
  29845. * @param {number|string} inColor
  29846. * @returns this
  29847. */
  29848. tint(inColor) {
  29849. if (!is_real_number(inColor) && typeof inColor !== "string")
  29850. throw this.sequence._customError(
  29851. this,
  29852. "tint",
  29853. `inColor must be of type string (hexadecimal) or number (decimal)!`
  29854. );
  29855. this._tint = parseColor(inColor);
  29856. return this;
  29857. }
  29858. };
  29859. const location = {
  29860. /**
  29861. * Base properties
  29862. */
  29863. _source: null,
  29864. /**
  29865. * A smart method that can take a reference to an object, or a direct on the canvas to play the effect at,
  29866. * or a string reference (see .name())
  29867. *
  29868. * @param {Object|String} inLocation
  29869. * @param {Object} inOptions
  29870. * @returns {EffectSection}
  29871. */
  29872. atLocation(inLocation, inOptions = {}) {
  29873. if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
  29874. throw this.sequence._customError(
  29875. this,
  29876. "atLocation",
  29877. `inLocation is invalid, and must be of type of object, string, placeable object, or document`
  29878. );
  29879. }
  29880. if (typeof inOptions !== "object")
  29881. throw this.sequence._customError(
  29882. this,
  29883. "atLocation",
  29884. `inOptions must be of type object`
  29885. );
  29886. inOptions = foundry.utils.mergeObject(
  29887. {
  29888. cacheLocation: false,
  29889. offset: false,
  29890. randomOffset: false,
  29891. gridUnits: false,
  29892. local: false
  29893. },
  29894. inOptions
  29895. );
  29896. inLocation = this._validateLocation(inLocation);
  29897. if (inLocation === void 0)
  29898. throw this.sequence._customError(
  29899. this,
  29900. "atLocation",
  29901. "could not find position of given object"
  29902. );
  29903. if (typeof inOptions.cacheLocation !== "boolean")
  29904. throw this.sequence._customError(
  29905. this,
  29906. "atLocation",
  29907. "inOptions.cacheLocation must be of type boolean"
  29908. );
  29909. if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
  29910. throw this.sequence._customError(
  29911. this,
  29912. "atLocation",
  29913. "inOptions.randomOffset must be of type boolean or number"
  29914. );
  29915. this._temporaryEffect = this._temporaryEffect || (inLocation instanceof foundry.abstract.Document ? !is_UUID(inLocation?.uuid) : false);
  29916. if (inOptions.offset) {
  29917. const offsetData = this._validateOffset(
  29918. "atLocation",
  29919. inOptions.offset,
  29920. inOptions
  29921. );
  29922. this._offset = {
  29923. source: offsetData,
  29924. target: this._offset?.target ?? false
  29925. };
  29926. }
  29927. this._randomOffset = {
  29928. source: inOptions.randomOffset,
  29929. target: this._randomOffset?.target ?? false
  29930. };
  29931. this._source = inOptions.cacheLocation && typeof inLocation !== "string" ? get_object_canvas_data(inLocation) : inLocation;
  29932. return this;
  29933. }
  29934. };
  29935. const offset = {
  29936. _offset: null,
  29937. _randomOffset: null,
  29938. _validateOffset(functionName, inOffset, inOptions = {}) {
  29939. inOffset = foundry.utils.mergeObject(
  29940. {
  29941. x: 0,
  29942. y: 0
  29943. },
  29944. inOffset
  29945. );
  29946. inOptions = foundry.utils.mergeObject(
  29947. {
  29948. gridUnits: false,
  29949. local: false
  29950. },
  29951. inOptions
  29952. );
  29953. if (typeof inOptions.gridUnits !== "boolean")
  29954. throw this.sequence._customError(
  29955. this,
  29956. functionName,
  29957. "inOptions.gridUnits must be of type boolean"
  29958. );
  29959. if (typeof inOptions.local !== "boolean")
  29960. throw this.sequence._customError(
  29961. this,
  29962. functionName,
  29963. "inOptions.local must be of type boolean"
  29964. );
  29965. if (!is_real_number(inOffset.x))
  29966. throw this.sequence._customError(
  29967. this,
  29968. functionName,
  29969. `inOffset.x must be of type number!`
  29970. );
  29971. if (!is_real_number(inOffset.y))
  29972. throw this.sequence._customError(
  29973. this,
  29974. functionName,
  29975. `inOffset.y must be of type number!`
  29976. );
  29977. return {
  29978. ...inOffset,
  29979. ...inOptions
  29980. };
  29981. }
  29982. };
  29983. const text = {
  29984. _text: null,
  29985. /**
  29986. * Creates a text element, attached to the sprite. The options for the text are available here:
  29987. * https://pixijs.io/pixi-text-style/
  29988. *
  29989. * @param {String} inText
  29990. * @param {Object} inOptions
  29991. * @returns {EffectSection}
  29992. */
  29993. text(inText, inOptions = {}) {
  29994. if (typeof inText !== "string")
  29995. throw this.sequence._customError(
  29996. this,
  29997. "text",
  29998. "inText must be of type string"
  29999. );
  30000. this._text = foundry.utils.mergeObject(
  30001. {
  30002. text: inText
  30003. },
  30004. inOptions
  30005. );
  30006. return this;
  30007. }
  30008. };
  30009. const traits = {
  30010. animation,
  30011. audio,
  30012. files,
  30013. moves,
  30014. opacity,
  30015. rotation,
  30016. scale,
  30017. time,
  30018. users,
  30019. filter,
  30020. tint,
  30021. location,
  30022. offset,
  30023. text
  30024. };
  30025. class EffectSection extends Section {
  30026. constructor(inSequence, inFile = "") {
  30027. super(inSequence);
  30028. this._deserializedData = null;
  30029. this._file = inFile;
  30030. this._text = null;
  30031. this._source = null;
  30032. this._stretchTo = null;
  30033. this._attachTo = null;
  30034. this._from = null;
  30035. this._origin = null;
  30036. this._anchor = null;
  30037. this._spriteAnchor = null;
  30038. this._randomOffset = null;
  30039. this._missed = null;
  30040. this._private = null;
  30041. this._randomMirrorX = null;
  30042. this._randomMirrorY = null;
  30043. this._mirrorX = null;
  30044. this._mirrorY = null;
  30045. this._playbackRate = null;
  30046. this._template = null;
  30047. this._overrides = [];
  30048. this._name = null;
  30049. this._zIndex = null;
  30050. this._offset = null;
  30051. this._spriteOffset = null;
  30052. this._size = null;
  30053. this._persist = null;
  30054. this._persistOptions = null;
  30055. this._zeroSpriteRotation = null;
  30056. this._extraEndDuration = null;
  30057. this._noLoop = null;
  30058. this._tilingTexture = null;
  30059. this._snapToGrid = null;
  30060. this._scaleToObject = null;
  30061. this._screenSpace = null;
  30062. this._screenSpaceAboveUI = null;
  30063. this._screenSpaceAnchor = null;
  30064. this._screenSpacePosition = null;
  30065. this._screenSpaceScale = null;
  30066. this._elevation = null;
  30067. this._masks = [];
  30068. this._tiedDocuments = [];
  30069. this._selfMask = false;
  30070. this._temporaryEffect = false;
  30071. this._spriteRotation = 0;
  30072. this._isRangedEffect = null;
  30073. this._offsetLegacy = null;
  30074. this._randomOffsetLegacy = null;
  30075. this._aboveLighting = null;
  30076. this._spriteScaleMin = 1;
  30077. this._spriteScaleMax = null;
  30078. this._isometric = null;
  30079. this._shapes = [];
  30080. this._xray = null;
  30081. this._playEffect = true;
  30082. }
  30083. static niceName = "Effect";
  30084. /**
  30085. * @private
  30086. */
  30087. get _target() {
  30088. return this._stretchTo || this._rotateTowards || this._moveTowards || false;
  30089. }
  30090. static debounceWarning() {
  30091. custom_warning(
  30092. "Sequencer",
  30093. "Effect | This user does not have permissions to play effects. This can be configured in Sequencer's module settings."
  30094. );
  30095. }
  30096. /**
  30097. * Causes the effect's position to be stored and can then be used with .atLocation(), .stretchTowards(),
  30098. * and .rotateTowards() to refer to previous effects' locations
  30099. *
  30100. * @param {String} inName
  30101. * @returns {EffectSection}
  30102. */
  30103. name(inName) {
  30104. if (typeof inName !== "string")
  30105. throw this.sequence._customError(
  30106. this,
  30107. "name",
  30108. "inName must be of type string"
  30109. );
  30110. this._name = safe_str(inName);
  30111. return this;
  30112. }
  30113. /**
  30114. * Causes the effect to persist indefinitely on the canvas until _ended via SequencerEffectManager.endAllEffects() or
  30115. * name the effect with .name() and then end it through SequencerEffectManager.endEffect()
  30116. *
  30117. * @param {Boolean} [inBool=true] inBool
  30118. * @param {Object} [inOptions={}] inOptions
  30119. * @returns {EffectSection}
  30120. */
  30121. persist(inBool = true, inOptions = {}) {
  30122. if (typeof inBool !== "boolean")
  30123. throw this.sequence._customError(
  30124. this,
  30125. "persist",
  30126. "inBool must be of type boolean"
  30127. );
  30128. if (typeof inOptions !== "object")
  30129. throw this.sequence._customError(
  30130. this,
  30131. "persist",
  30132. `inOptions must be of type object`
  30133. );
  30134. inOptions = foundry.utils.mergeObject(
  30135. {
  30136. id: randomID(),
  30137. persistTokenPrototype: false
  30138. },
  30139. inOptions
  30140. );
  30141. if (typeof inOptions.persistTokenPrototype !== "boolean")
  30142. throw this.sequence._customError(
  30143. this,
  30144. "persist",
  30145. "inOptions.persistTokenPrototype must be of type boolean"
  30146. );
  30147. this._persist = inBool;
  30148. this._persistOptions = inOptions;
  30149. return this;
  30150. }
  30151. /**
  30152. * Sets the effect's playback rate. A playback rate of 2.0 would make it play 2x as fast, 0.5 would make
  30153. * it play half as fast.
  30154. *
  30155. * @param {Number} inNumber
  30156. * @returns {EffectSection}
  30157. */
  30158. playbackRate(inNumber = 1) {
  30159. if (!is_real_number(inNumber))
  30160. throw this.sequence._customError(
  30161. this,
  30162. "playbackRate",
  30163. "inNumber must be of type number"
  30164. );
  30165. this._playbackRate = inNumber;
  30166. return this;
  30167. }
  30168. /**
  30169. * Causes the effect to target a location close to the .stretchTowards() location, but not on it.
  30170. *
  30171. * @param {Boolean} [inBool=true] inBool
  30172. * @returns {EffectSection}
  30173. */
  30174. missed(inBool = true) {
  30175. if (typeof inBool !== "boolean")
  30176. throw this.sequence._customError(
  30177. this,
  30178. "missed",
  30179. "inBool must be of type boolean"
  30180. );
  30181. this._missed = inBool;
  30182. return this;
  30183. }
  30184. /**
  30185. * Adds a function that will run at the end of the effect serialization step, but before it is played. Allows direct
  30186. * modifications of effect's data. For example, it could be manipulated to change which file will be used based
  30187. * on the distance to the target.
  30188. *
  30189. * @param {Function} inFunc
  30190. * @returns {EffectSection}
  30191. */
  30192. addOverride(inFunc) {
  30193. if (!is_function$1(inFunc))
  30194. throw this.sequence._customError(
  30195. this,
  30196. "addOverride",
  30197. "The given function needs to be an actual function."
  30198. );
  30199. this._overrides.push(inFunc);
  30200. return this;
  30201. }
  30202. /**
  30203. * A smart method that can take a reference to an object, or a direct on the canvas to attach an effect to,
  30204. * or a string reference (see .name())
  30205. *
  30206. * @param {Object|String} inObject
  30207. * @param {Object} inOptions
  30208. * @returns {EffectSection}
  30209. */
  30210. attachTo(inObject, inOptions = {}) {
  30211. if (!(typeof inObject === "object" || typeof inObject === "string")) {
  30212. throw this.sequence._customError(
  30213. this,
  30214. "attachTo",
  30215. `inObject is invalid, and must be of type of object, string, placeable object, or document`
  30216. );
  30217. }
  30218. if (typeof inOptions !== "object")
  30219. throw this.sequence._customError(
  30220. this,
  30221. "attachTo",
  30222. `inOptions must be of type object`
  30223. );
  30224. inOptions = foundry.utils.mergeObject(
  30225. {
  30226. align: "center",
  30227. edge: "on",
  30228. bindVisibility: true,
  30229. bindAlpha: true,
  30230. bindElevation: true,
  30231. followRotation: true,
  30232. offset: false,
  30233. randomOffset: false,
  30234. gridUnits: false,
  30235. local: false
  30236. },
  30237. inOptions
  30238. );
  30239. const validatedObject = this._validateLocation(inObject);
  30240. if (validatedObject === void 0)
  30241. throw this.sequence._customError(
  30242. this,
  30243. "attachTo",
  30244. "could not find given object"
  30245. );
  30246. let isValidObject = true;
  30247. if (typeof inObject === "string") {
  30248. isValidObject = validatedObject instanceof Token || validatedObject instanceof TokenDocument || validatedObject instanceof Tile || validatedObject instanceof TileDocument || validatedObject instanceof Drawing || validatedObject instanceof DrawingDocument || validatedObject instanceof MeasuredTemplate || validatedObject instanceof MeasuredTemplateDocument || validatedObject instanceof CanvasEffect;
  30249. if (!isValidObject) {
  30250. this.sequence._showWarning(
  30251. this,
  30252. "attachTo",
  30253. "Only Tokens, Tiles, Drawings, and MeasuredTemplates may have attached effects - will play effect on target's location"
  30254. );
  30255. }
  30256. }
  30257. const aligns = Object.keys(alignments);
  30258. if (typeof inOptions.align !== "string" || !aligns.includes(inOptions.align)) {
  30259. throw this.sequence._customError(
  30260. this,
  30261. "attachTo",
  30262. `inOptions.align must be of type string, one of: ${aligns.join(", ")}`
  30263. );
  30264. }
  30265. if (typeof inOptions.edge !== "string" || !(inOptions.edge === "on" || inOptions.edge === "inner" || inOptions.edge === "outer")) {
  30266. throw this.sequence._customError(
  30267. this,
  30268. "attachTo",
  30269. `inOptions.edge must of type string with the value of either "on", "inner", or "outer"`
  30270. );
  30271. }
  30272. if (typeof inOptions.bindVisibility !== "boolean")
  30273. throw this.sequence._customError(
  30274. this,
  30275. "attachTo",
  30276. `inOptions.bindVisibility must be of type boolean`
  30277. );
  30278. if (typeof inOptions.followRotation !== "boolean")
  30279. throw this.sequence._customError(
  30280. this,
  30281. "attachTo",
  30282. `inOptions.followRotation must be of type boolean`
  30283. );
  30284. if (typeof inOptions.bindAlpha !== "boolean")
  30285. throw this.sequence._customError(
  30286. this,
  30287. "attachTo",
  30288. "inOptions.bindAlpha must be of type boolean"
  30289. );
  30290. if (typeof inOptions.bindElevation !== "boolean")
  30291. throw this.sequence._customError(
  30292. this,
  30293. "attachTo",
  30294. "inOptions.bindElevation must be of type boolean"
  30295. );
  30296. if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
  30297. throw this.sequence._customError(
  30298. this,
  30299. "attachTo",
  30300. "inOptions.randomOffset must be of type boolean or number"
  30301. );
  30302. this._source = validatedObject;
  30303. this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document || validatedObject instanceof MeasuredTemplate ? !is_UUID(validatedObject?.uuid) : false);
  30304. if (inOptions.offset) {
  30305. const offsetData = this._validateOffset(
  30306. "attachTo",
  30307. inOptions.offset,
  30308. inOptions
  30309. );
  30310. this._offset = {
  30311. source: offsetData,
  30312. target: this._offset?.target ?? false
  30313. };
  30314. }
  30315. this._randomOffset = {
  30316. source: inOptions.randomOffset,
  30317. target: this._randomOffset?.target ?? false
  30318. };
  30319. this._attachTo = {
  30320. active: isValidObject,
  30321. align: inOptions.align,
  30322. edge: inOptions.edge,
  30323. bindVisibility: inOptions.bindVisibility,
  30324. bindAlpha: inOptions.bindAlpha,
  30325. bindElevation: inOptions.bindElevation,
  30326. followRotation: inOptions.followRotation
  30327. };
  30328. return this;
  30329. }
  30330. /**
  30331. * 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())
  30332. * This effectively calculates the proper X scale for the effect to reach the target
  30333. *
  30334. * @param {Object|String} inLocation
  30335. * @param {Object} inOptions
  30336. * @returns {EffectSection}
  30337. */
  30338. stretchTo(inLocation, inOptions = {}) {
  30339. if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
  30340. throw this.sequence._customError(
  30341. this,
  30342. "stretchTo",
  30343. `inLocation is invalid, and must be of type of object, string, placeable object, or document`
  30344. );
  30345. }
  30346. if (typeof inOptions !== "object")
  30347. throw this.sequence._customError(
  30348. this,
  30349. "stretchTo",
  30350. `inOptions must be of type object`
  30351. );
  30352. inOptions = foundry.utils.mergeObject(
  30353. {
  30354. cacheLocation: false,
  30355. attachTo: false,
  30356. onlyX: false,
  30357. tiling: false,
  30358. offset: false,
  30359. randomOffset: false,
  30360. gridUnits: false,
  30361. local: false
  30362. },
  30363. inOptions
  30364. );
  30365. const validatedObject = this._validateLocation(inLocation);
  30366. if (validatedObject === void 0)
  30367. throw this.sequence._customError(
  30368. this,
  30369. "stretchTo",
  30370. "could not find position of given object"
  30371. );
  30372. if (typeof inOptions.cacheLocation !== "boolean")
  30373. throw this.sequence._customError(
  30374. this,
  30375. "stretchTo",
  30376. "inOptions.cacheLocation must be of type boolean"
  30377. );
  30378. if (typeof inOptions.attachTo !== "boolean")
  30379. throw this.sequence._customError(
  30380. this,
  30381. "stretchTo",
  30382. "inOptions.attachTo must be of type boolean"
  30383. );
  30384. if (typeof inOptions.onlyX !== "boolean")
  30385. throw this.sequence._customError(
  30386. this,
  30387. "stretchTo",
  30388. "inOptions.onlyX must be of type boolean"
  30389. );
  30390. if (typeof inOptions.tiling !== "boolean")
  30391. throw this.sequence._customError(
  30392. this,
  30393. "stretchTo",
  30394. "inOptions.tiling must be of type boolean"
  30395. );
  30396. if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
  30397. throw this.sequence._customError(
  30398. this,
  30399. "stretchTo",
  30400. "inOptions.randomOffset must be of type boolean or number"
  30401. );
  30402. if (inOptions.cacheLocation && inOptions.attachTo) {
  30403. throw this.sequence._customError(
  30404. this,
  30405. "stretchTo",
  30406. "cacheLocation and attachTo cannot both be true - pick one or the other"
  30407. );
  30408. }
  30409. if (inOptions.tiling)
  30410. this.tilingTexture();
  30411. this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document ? !is_UUID(validatedObject?.uuid) : false);
  30412. if (inOptions.offset) {
  30413. const offsetData = this._validateOffset(
  30414. "stretchTo",
  30415. inOptions.offset,
  30416. inOptions
  30417. );
  30418. this._offset = {
  30419. source: this._offset?.source ?? false,
  30420. target: offsetData
  30421. };
  30422. }
  30423. this._randomOffset = {
  30424. source: this._randomOffset?.source ?? false,
  30425. target: inOptions.randomOffset
  30426. };
  30427. this._stretchTo = {
  30428. target: inOptions.cacheLocation ? get_object_canvas_data(validatedObject, { measure: true }) : validatedObject,
  30429. attachTo: inOptions.attachTo,
  30430. onlyX: inOptions.onlyX
  30431. };
  30432. return this;
  30433. }
  30434. /**
  30435. * Sets the location to rotate the object to
  30436. *
  30437. * @param {object|string} inLocation
  30438. * @param {object} inOptions
  30439. * @returns this
  30440. */
  30441. rotateTowards(inLocation, inOptions = {}) {
  30442. if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
  30443. throw this.sequence._customError(
  30444. this,
  30445. "inLocation",
  30446. `inLocation is invalid, and must be of type of object, string, placeable object, or document`
  30447. );
  30448. }
  30449. inOptions = foundry.utils.mergeObject(
  30450. {
  30451. rotationOffset: 0,
  30452. cacheLocation: false,
  30453. attachTo: false,
  30454. offset: false,
  30455. randomOffset: false,
  30456. local: false,
  30457. gridUnits: false
  30458. },
  30459. inOptions
  30460. );
  30461. if (!is_real_number(inOptions.rotationOffset))
  30462. throw this.sequence._customError(
  30463. this,
  30464. "rotateTowards",
  30465. "inOptions.rotationOffset must be of type number"
  30466. );
  30467. if (typeof inOptions.attachTo !== "boolean")
  30468. throw this.sequence._customError(
  30469. this,
  30470. "rotateTowards",
  30471. "inOptions.attachTo must be of type boolean"
  30472. );
  30473. if (typeof inOptions.cacheLocation !== "boolean")
  30474. throw this.sequence._customError(
  30475. this,
  30476. "rotateTowards",
  30477. "inOptions.cacheLocation must be of type boolean"
  30478. );
  30479. const validatedObject = this._validateLocation(inLocation);
  30480. if (!validatedObject)
  30481. throw this.sequence._customError(
  30482. this,
  30483. "rotateTowards",
  30484. "could not find position of given object"
  30485. );
  30486. this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document ? !is_UUID(validatedObject?.uuid) : false);
  30487. if (inOptions.offset) {
  30488. const offsetData = this._validateOffset(
  30489. "attachTo",
  30490. inOptions.offset,
  30491. inOptions
  30492. );
  30493. this._offset = {
  30494. source: offsetData,
  30495. target: this._offset?.target ?? false
  30496. };
  30497. }
  30498. this._randomOffset = {
  30499. source: inOptions.randomOffset,
  30500. target: this._randomOffset?.target ?? false
  30501. };
  30502. this._rotateTowards = {
  30503. target: inOptions.cacheLocation ? get_object_canvas_data(validatedObject, { measure: true }) : validatedObject,
  30504. rotationOffset: inOptions.rotationOffset,
  30505. cacheLocation: inOptions.cacheLocation,
  30506. attachTo: inOptions.attachTo
  30507. };
  30508. return this;
  30509. }
  30510. /**
  30511. * 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.
  30512. *
  30513. * @param {Object} inObject
  30514. * @param {Object} inOptions
  30515. * @returns {EffectSection}
  30516. */
  30517. from(inObject, inOptions = {}) {
  30518. if (!(inObject instanceof Token || inObject instanceof Tile || inObject instanceof TokenDocument || inObject instanceof TileDocument)) {
  30519. throw this.sequence._customError(
  30520. this,
  30521. "from",
  30522. "inObject must be of type Token, Tile, TokenDocument, or TileDocument"
  30523. );
  30524. }
  30525. if (typeof inOptions !== "object")
  30526. throw this.sequence._customError(
  30527. this,
  30528. "from",
  30529. `inOptions must be of type object`
  30530. );
  30531. inObject = inObject.document ?? inObject;
  30532. if (!inObject?.texture?.src)
  30533. throw this.sequence._customError(
  30534. this,
  30535. "from",
  30536. "could not find the image for the given object"
  30537. );
  30538. inOptions = foundry.utils.mergeObject(
  30539. {
  30540. cacheLocation: false,
  30541. offset: false,
  30542. randomOffset: false,
  30543. local: false,
  30544. gridUnits: false
  30545. },
  30546. inOptions
  30547. );
  30548. if (typeof inOptions.cacheLocation !== "boolean")
  30549. throw this.sequence._customError(
  30550. this,
  30551. "from",
  30552. "inOptions.cacheLocation must be of type boolean"
  30553. );
  30554. if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
  30555. throw this.sequence._customError(
  30556. this,
  30557. "from",
  30558. "inOptions.randomOffset must be of type boolean or number"
  30559. );
  30560. this._temporaryEffect = this._temporaryEffect || (inObject instanceof foundry.abstract.Document ? !is_UUID(inObject?.uuid) : false);
  30561. if (inOptions.offset) {
  30562. const offsetData = this._validateOffset(
  30563. "attachTo",
  30564. inOptions.offset,
  30565. inOptions
  30566. );
  30567. this._offset = {
  30568. source: offsetData,
  30569. target: this._offset?.target ?? false
  30570. };
  30571. }
  30572. this._randomOffset = {
  30573. source: inOptions.randomOffset,
  30574. target: this._randomOffset?.target ?? false
  30575. };
  30576. this._from = {
  30577. object: inObject,
  30578. options: inOptions
  30579. };
  30580. return this;
  30581. }
  30582. shape(inType, inOptions = {}) {
  30583. if (typeof inType !== "string")
  30584. throw this.sequence._customError(
  30585. this,
  30586. "shape",
  30587. "type must be of type string"
  30588. );
  30589. if (!Object.values(CONSTANTS.SHAPES).includes(inType)) {
  30590. throw this.sequence._customError(
  30591. this,
  30592. "shape",
  30593. "type must be one of: " + Object.values(CONSTANTS.SHAPES).join(", ")
  30594. );
  30595. }
  30596. if (inType === CONSTANTS.SHAPES.POLY) {
  30597. if (!Array.isArray(inOptions.points)) {
  30598. throw this.sequence._customError(
  30599. this,
  30600. "shape",
  30601. "if creating polygon, inOptions.points must be of type array"
  30602. );
  30603. }
  30604. inOptions.points = inOptions.points.map((point) => {
  30605. if (Array.isArray(point)) {
  30606. if (!is_real_number(point[0]) || !is_real_number(point[1])) {
  30607. throw this.sequence._customError(
  30608. this,
  30609. "shape",
  30610. "inOptions.points must be an array, containing an array of two numbers or objects with x and y number properties"
  30611. );
  30612. }
  30613. return point;
  30614. }
  30615. if (typeof point === "object") {
  30616. if (!is_real_number(point?.x) || !is_real_number(point?.y)) {
  30617. throw this.sequence._customError(
  30618. this,
  30619. "shape",
  30620. "inOptions.points must be an array, containing an array of two numbers or objects with x and y number properties"
  30621. );
  30622. }
  30623. return [point.x, point.y];
  30624. }
  30625. });
  30626. } else if (inType === CONSTANTS.SHAPES.CIRC) {
  30627. if (typeof inOptions.radius !== "number") {
  30628. throw this.sequence._customError(
  30629. this,
  30630. "shape",
  30631. "if creating circle, inOptions.radius must be of type number"
  30632. );
  30633. }
  30634. } else if (inType === CONSTANTS.SHAPES.RECT || inType === CONSTANTS.SHAPES.RREC || inType === CONSTANTS.SHAPES.ELIP) {
  30635. if (inOptions.width ^ inOptions.height) {
  30636. inOptions.width = inOptions.width ?? inOptions.height;
  30637. inOptions.height = inOptions.height ?? inOptions.width;
  30638. }
  30639. if (typeof inOptions.width !== "number") {
  30640. throw this.sequence._customError(
  30641. this,
  30642. "shape",
  30643. `if creating rectangle, rounded rectangle, or an ellipse, inOptions.width must be of type number`
  30644. );
  30645. }
  30646. if (typeof inOptions.height !== "number") {
  30647. throw this.sequence._customError(
  30648. this,
  30649. "shape",
  30650. "if creating rectangle, rounded rectangle, or an ellipse, inOptions.height must be of type number"
  30651. );
  30652. }
  30653. if (inType === CONSTANTS.SHAPES.RREC && typeof inOptions.radius !== "number") {
  30654. throw this.sequence._customError(
  30655. this,
  30656. "shape",
  30657. "if creating rounded border rectangle, inOptions.radius must be of type number"
  30658. );
  30659. }
  30660. }
  30661. if (inOptions.gridUnits !== void 0 && typeof inOptions.gridUnits !== "boolean") {
  30662. throw this.sequence._customError(
  30663. this,
  30664. "shape",
  30665. "inOptions.gridUnits must be of type boolean"
  30666. );
  30667. }
  30668. if (inOptions.name && typeof inOptions.name !== "string") {
  30669. throw this.sequence._customError(
  30670. this,
  30671. "shape",
  30672. "inOptions.name must be of type string"
  30673. );
  30674. }
  30675. if (inOptions.fillColor && !is_real_number(inOptions.fillColor) && typeof inOptions.fillColor !== "string") {
  30676. throw this.sequence._customError(
  30677. this,
  30678. "shape",
  30679. "inOptions.fillColor must be of type string (hexadecimal) or number (decimal)"
  30680. );
  30681. } else {
  30682. inOptions.fillColor = parseColor(inOptions.fillColor).decimal;
  30683. }
  30684. if (inOptions.fillAlpha && !is_real_number(inOptions.fillAlpha)) {
  30685. throw this.sequence._customError(
  30686. this,
  30687. "shape",
  30688. "inOptions.fillAlpha must be of type number"
  30689. );
  30690. }
  30691. if (inOptions.alpha && !is_real_number(inOptions.alpha)) {
  30692. throw this.sequence._customError(
  30693. this,
  30694. "shape",
  30695. "inOptions.alpha must be of type number"
  30696. );
  30697. }
  30698. if (inOptions.lineSize && !is_real_number(inOptions.lineSize)) {
  30699. throw this.sequence._customError(
  30700. this,
  30701. "shape",
  30702. "inOptions.lineSize must be of type number"
  30703. );
  30704. }
  30705. if (inOptions.lineColor && !is_real_number(inOptions.lineColor) && typeof inOptions.lineColor !== "string") {
  30706. throw this.sequence._customError(
  30707. this,
  30708. "shape",
  30709. "inOptions.lineColor must be of type string (hexadecimal) or number (decimal)"
  30710. );
  30711. } else {
  30712. inOptions.lineColor = parseColor(inOptions.lineColor).decimal;
  30713. }
  30714. if (inOptions.offset) {
  30715. inOptions.offset = this._validateOffset(
  30716. "shape",
  30717. inOptions.offset,
  30718. inOptions.offset
  30719. );
  30720. }
  30721. if (inOptions.isMask !== void 0 && typeof inOptions.isMask !== "boolean") {
  30722. throw this.sequence._customError(
  30723. this,
  30724. "shape",
  30725. "inOptions.isMask must be of type boolean"
  30726. );
  30727. }
  30728. this._shapes.push({
  30729. ...inOptions,
  30730. type: inType
  30731. });
  30732. return this;
  30733. }
  30734. /**
  30735. * Causes the effect to be offset relative to its location based on a given vector
  30736. *
  30737. * @param {Object} inOffset
  30738. * @param {Object} inOptions
  30739. * @returns {EffectSection}
  30740. */
  30741. offset(inOffset, inOptions = {}) {
  30742. this.sequence._showWarning(
  30743. this,
  30744. "offset",
  30745. "This method is becoming deprecated, please use the secondary offset option in atLocation, attachTo, stretchTo instead.",
  30746. true
  30747. );
  30748. if (inOffset === void 0)
  30749. throw this.sequence._customError(
  30750. this,
  30751. "offset",
  30752. "inOffset must not be undefined"
  30753. );
  30754. if (typeof inOptions !== "object")
  30755. throw this.sequence._customError(
  30756. this,
  30757. "offset",
  30758. "options must be of type object"
  30759. );
  30760. this._offsetLegacy = this._validateOffset("offset", inOffset, inOptions);
  30761. return this;
  30762. }
  30763. /**
  30764. * Causes the effect's sprite to be offset relative to its location based on a given vector
  30765. *
  30766. * @param {Object} inOffset
  30767. * @param {Object} inOptions
  30768. * @returns {EffectSection}
  30769. */
  30770. spriteOffset(inOffset, inOptions = {}) {
  30771. if (inOffset === void 0)
  30772. throw this.sequence._customError(
  30773. this,
  30774. "spriteOffset",
  30775. "inOffset must not be undefined"
  30776. );
  30777. if (typeof inOptions !== "object")
  30778. throw this.sequence._customError(
  30779. this,
  30780. "spriteOffset",
  30781. "options must be of type object"
  30782. );
  30783. this._spriteOffset = this._validateOffset(
  30784. "spriteOffset",
  30785. inOffset,
  30786. inOptions
  30787. );
  30788. return this;
  30789. }
  30790. /**
  30791. * Causes the final effect location to be snapped to the grid
  30792. *
  30793. * @param {Boolean} inBool
  30794. * @returns {EffectSection}
  30795. */
  30796. snapToGrid(inBool = true) {
  30797. if (typeof inBool !== "boolean")
  30798. throw this.sequence._customError(
  30799. this,
  30800. "snapToGrid",
  30801. "inBool must be of type boolean"
  30802. );
  30803. this._snapToGrid = inBool;
  30804. return this;
  30805. }
  30806. /**
  30807. * Causes the effect to be scaled to the target object's width
  30808. *
  30809. * @param {Number} inScale
  30810. * @param {Object} inOptions
  30811. * @returns {EffectSection}
  30812. */
  30813. scaleToObject(inScale = 1, inOptions = {}) {
  30814. if (!is_real_number(inScale))
  30815. throw this.sequence._customError(
  30816. this,
  30817. "scaleToObject",
  30818. `inScale must be of type number!`
  30819. );
  30820. if (typeof inOptions !== "object")
  30821. throw this.sequence._customError(
  30822. this,
  30823. "scaleToObject",
  30824. "inOptions must be of type object"
  30825. );
  30826. inOptions = foundry.utils.mergeObject(
  30827. {
  30828. scale: inScale,
  30829. considerTokenScale: false,
  30830. uniform: false
  30831. },
  30832. inOptions
  30833. );
  30834. if (typeof inOptions.uniform !== "boolean")
  30835. throw this.sequence._customError(
  30836. this,
  30837. "scaleToObject",
  30838. "inBool must be of type boolean"
  30839. );
  30840. this._scaleToObject = inOptions;
  30841. return this;
  30842. }
  30843. /**
  30844. * Sets the width and the height of the effect in pixels, this size is set before any scaling
  30845. *
  30846. * @param {Number|Object<{width: {Number}, height: {Number}}>} inSize
  30847. * @param {Object} inOptions
  30848. * @returns {EffectSection}
  30849. */
  30850. size(inSize, inOptions = {}) {
  30851. if (!is_real_number(inSize) && typeof inSize !== "object")
  30852. throw this.sequence._customError(
  30853. this,
  30854. "size",
  30855. "inSize must be of type number or object"
  30856. );
  30857. if (typeof inOptions !== "object")
  30858. throw this.sequence._customError(
  30859. this,
  30860. "size",
  30861. "inOptions must be of type object"
  30862. );
  30863. if (is_real_number(inSize)) {
  30864. inSize = {
  30865. width: inSize,
  30866. height: inSize
  30867. };
  30868. }
  30869. if (inSize.width === void 0 ^ inSize.height === void 0) {
  30870. if (inSize.width) {
  30871. if (!is_real_number(inSize.width))
  30872. throw this.sequence._customError(
  30873. this,
  30874. "size",
  30875. "inSize.width must be of type number or string 'auto'"
  30876. );
  30877. inSize["height"] = "auto";
  30878. } else {
  30879. if (!is_real_number(inSize.height))
  30880. throw this.sequence._customError(
  30881. this,
  30882. "size",
  30883. "inSize.height must be of type number or string 'auto'"
  30884. );
  30885. inSize["width"] = "auto";
  30886. }
  30887. }
  30888. inOptions = foundry.utils.mergeObject(
  30889. {
  30890. gridUnits: false
  30891. },
  30892. inOptions
  30893. );
  30894. if (!is_real_number(inSize.width) && inSize.width !== "auto")
  30895. throw this.sequence._customError(
  30896. this,
  30897. "size",
  30898. "inSize.width must be of type number or string 'auto'"
  30899. );
  30900. if (!is_real_number(inSize.height) && inSize.height !== "auto")
  30901. throw this.sequence._customError(
  30902. this,
  30903. "size",
  30904. "inSize.height must be of type number or string 'auto'"
  30905. );
  30906. if (typeof inOptions.gridUnits !== "boolean")
  30907. throw this.sequence._customError(
  30908. this,
  30909. "size",
  30910. "inOptions.gridUnits must be of type boolean"
  30911. );
  30912. this._size = {
  30913. width: inSize.width ?? canvas.grid.size,
  30914. height: inSize.height ?? canvas.grid.size,
  30915. ...inOptions
  30916. };
  30917. return this;
  30918. }
  30919. /**
  30920. * This scales the sprite of the effect, and this method can take the following:
  30921. * - A number to set the scale uniformly
  30922. * - An object with x and y for non-uniform scaling
  30923. * - Two numbers which the Sequencer will randomly pick a uniform scale between
  30924. *
  30925. * @param {number|object} inScaleMin
  30926. * @param {number} [inScaleMax] inScaleMax
  30927. * @returns this
  30928. */
  30929. spriteScale(inScaleMin, inScaleMax) {
  30930. if (!is_real_number(inScaleMin) && typeof inScaleMin !== "object")
  30931. throw this.sequence._customError(
  30932. this,
  30933. "spriteScale",
  30934. "inScale must be of type number or object"
  30935. );
  30936. if (is_real_number(inScaleMin)) {
  30937. if (inScaleMax && !is_real_number(inScaleMax)) {
  30938. throw this.sequence._customError(
  30939. this,
  30940. "spriteScale",
  30941. "if inScaleMin is a number, inScaleMax must also be of type number"
  30942. );
  30943. }
  30944. }
  30945. this._spriteScaleMin = inScaleMin;
  30946. this._spriteScaleMax = inScaleMax ?? false;
  30947. return this;
  30948. }
  30949. /**
  30950. * 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
  30951. * relative to the canvas's grid size. Start and end point defines padding at the left and right of the effect
  30952. *
  30953. * @param {Number} gridSize
  30954. * @param {Number} startPoint
  30955. * @param {Number} endPoint
  30956. * @returns {EffectSection}
  30957. */
  30958. template({ gridSize, startPoint, endPoint } = {}) {
  30959. if (gridSize && !is_real_number(gridSize))
  30960. throw this.sequence._customError(
  30961. this,
  30962. "template",
  30963. "gridSize must be of type number"
  30964. );
  30965. if (startPoint && !is_real_number(startPoint))
  30966. throw this.sequence._customError(
  30967. this,
  30968. "template",
  30969. "startPoint must be of type number"
  30970. );
  30971. if (endPoint && !is_real_number(endPoint))
  30972. throw this.sequence._customError(
  30973. this,
  30974. "template",
  30975. "endPoint must be of type number"
  30976. );
  30977. if (!gridSize && !startPoint && !endPoint)
  30978. throw this.sequence._customError(
  30979. this,
  30980. "template",
  30981. "You need to define at least one parameter!"
  30982. );
  30983. if (!this._template)
  30984. this._template = {};
  30985. if (gridSize)
  30986. this._template["gridSize"] = gridSize;
  30987. if (startPoint)
  30988. this._template["startPoint"] = startPoint;
  30989. if (endPoint)
  30990. this._template["endPoint"] = endPoint;
  30991. return this;
  30992. }
  30993. /**
  30994. * This makes the texture of the effect tile, effectively repeat itself within the sprite's dimensions
  30995. *
  30996. * @param {Object|Number} scale
  30997. * @param {Object} position
  30998. * @returns {EffectSection}
  30999. */
  31000. tilingTexture(scale2 = { x: 1, y: 1 }, position = { x: 0, y: 0 }) {
  31001. if (is_real_number(scale2)) {
  31002. scale2 = { x: scale2, y: scale2 };
  31003. }
  31004. scale2 = { x: scale2?.x ?? 1, y: scale2?.y ?? 1 };
  31005. if (!is_real_number(scale2.x))
  31006. throw this.sequence._customError(
  31007. this,
  31008. "tilingTexture",
  31009. `scale.x must be of type number!`
  31010. );
  31011. if (!is_real_number(scale2.y))
  31012. throw this.sequence._customError(
  31013. this,
  31014. "tilingTexture",
  31015. `scale.y must be of type number!`
  31016. );
  31017. position = { x: position?.x ?? 0, y: position?.y ?? 0 };
  31018. if (!is_real_number(position.x))
  31019. throw this.sequence._customError(
  31020. this,
  31021. "tilingTexture",
  31022. `position.x must be of type number!`
  31023. );
  31024. if (!is_real_number(position.y))
  31025. throw this.sequence._customError(
  31026. this,
  31027. "tilingTexture",
  31028. `position.y must be of type number!`
  31029. );
  31030. this._tilingTexture = {
  31031. scale: scale2,
  31032. position
  31033. };
  31034. return this;
  31035. }
  31036. /**
  31037. * Anchors the sprite's container according to the given x and y coordinates, or uniformly based on a single number
  31038. *
  31039. * @param {Number|Object} inAnchor
  31040. * @returns {EffectSection}
  31041. */
  31042. anchor(inAnchor) {
  31043. if (is_real_number(inAnchor)) {
  31044. inAnchor = {
  31045. x: inAnchor,
  31046. y: inAnchor
  31047. };
  31048. }
  31049. inAnchor = {
  31050. x: inAnchor?.x ?? 0.5,
  31051. y: inAnchor?.y ?? 0.5
  31052. };
  31053. if (!is_real_number(inAnchor.x))
  31054. throw this.sequence._customError(
  31055. this,
  31056. "anchor",
  31057. `inAnchor.x must be of type number!`
  31058. );
  31059. if (!is_real_number(inAnchor.y))
  31060. throw this.sequence._customError(
  31061. this,
  31062. "anchor",
  31063. `inAnchor.y must be of type number!`
  31064. );
  31065. this._anchor = inAnchor;
  31066. return this;
  31067. }
  31068. /**
  31069. * Anchors the sprite according to the given x and y coordinates, or uniformly based on a single number
  31070. *
  31071. * @param {Number|Object} inAnchor
  31072. * @returns {EffectSection}
  31073. */
  31074. spriteAnchor(inAnchor) {
  31075. if (is_real_number(inAnchor)) {
  31076. inAnchor = {
  31077. x: inAnchor,
  31078. y: inAnchor
  31079. };
  31080. }
  31081. inAnchor = {
  31082. x: inAnchor?.x ?? 0.5,
  31083. y: inAnchor?.y ?? 0.5
  31084. };
  31085. if (!is_real_number(inAnchor.x))
  31086. throw this.sequence._customError(
  31087. this,
  31088. "anchor",
  31089. `inAnchor.x must be of type number!`
  31090. );
  31091. if (!is_real_number(inAnchor.y))
  31092. throw this.sequence._customError(
  31093. this,
  31094. "anchor",
  31095. `inAnchor.y must be of type number!`
  31096. );
  31097. this._spriteAnchor = inAnchor;
  31098. return this;
  31099. }
  31100. /**
  31101. * Centers the sprite, effectively giving it an anchor of {x: 0.5, y: 0.5}
  31102. *
  31103. * Note: If this is used, it will override the anchor set by Aim Towards, which sets the sprite's anchor to the
  31104. * outermost edge of the location the sprite is played at
  31105. *
  31106. * @returns {EffectSection}
  31107. */
  31108. center() {
  31109. this.anchor(0.5);
  31110. return this;
  31111. }
  31112. /**
  31113. * The sprite gets a random offset on its target location, usually within the object's bounds. The optional parameter
  31114. * scales how much offset should be added. Defaults to 1.0, which covers the entire target position, 0.5 would cover half.
  31115. *
  31116. * @param {Number} inOffsetScale
  31117. * @returns {EffectSection}
  31118. */
  31119. randomOffset(inOffsetScale = 1) {
  31120. this.sequence._showWarning(
  31121. this,
  31122. "randomOffset",
  31123. "This method has been deprecated, please use randomOffset as a second parameter on atLocation, stretchTo, etc.",
  31124. true
  31125. );
  31126. if (!is_real_number(inOffsetScale))
  31127. throw this.sequence._customError(
  31128. this,
  31129. "randomOffset",
  31130. "inBool must be of type number"
  31131. );
  31132. this._randomOffsetLegacy = inOffsetScale;
  31133. return this;
  31134. }
  31135. /**
  31136. * The sprite gets a randomized flipped X scale. If the scale on that axis was 1, it can
  31137. * become 1 or -1, effectively mirroring the sprite on its horizontal axis
  31138. *
  31139. * @param {Boolean} inBool
  31140. * @returns {EffectSection}
  31141. */
  31142. randomizeMirrorX(inBool = true) {
  31143. if (typeof inBool !== "boolean")
  31144. throw this.sequence._customError(
  31145. this,
  31146. "randomizeMirrorX",
  31147. "inBool must be of type boolean"
  31148. );
  31149. this._randomMirrorX = inBool;
  31150. return this;
  31151. }
  31152. /**
  31153. * The sprite gets a randomized flipped Y scale. If the scale on that axis was 1, it can
  31154. * become 1 or -1, effectively mirroring the sprite on its vertical axis
  31155. *
  31156. * @param {Boolean} inBool
  31157. * @returns {EffectSection}
  31158. */
  31159. randomizeMirrorY(inBool = true) {
  31160. if (typeof inBool !== "boolean")
  31161. throw this.sequence._customError(
  31162. this,
  31163. "randomizeMirrorY",
  31164. "inBool must be of type boolean"
  31165. );
  31166. this._randomMirrorY = inBool;
  31167. return this;
  31168. }
  31169. /**
  31170. * The sprite gets a flipped X scale. If the scale on that axis was 1, it will become 1 or -1, effectively
  31171. * mirroring the sprite on its horizontal axis
  31172. *
  31173. * @param {Boolean} inBool
  31174. * @returns {EffectSection}
  31175. */
  31176. mirrorX(inBool = true) {
  31177. if (typeof inBool !== "boolean")
  31178. throw this.sequence._customError(
  31179. this,
  31180. "mirrorX",
  31181. "inBool must be of type boolean"
  31182. );
  31183. this._mirrorX = inBool;
  31184. return this;
  31185. }
  31186. /**
  31187. * The sprite gets a flipped Y scale. If the scale on that axis was 1, it will become 1 or -1, effectively
  31188. * mirroring the sprite on its vertical axis
  31189. *
  31190. * @param {Boolean} inBool
  31191. * @returns {EffectSection}
  31192. */
  31193. mirrorY(inBool = true) {
  31194. if (typeof inBool !== "boolean")
  31195. throw this.sequence._customError(
  31196. this,
  31197. "mirrorY",
  31198. "inBool must be of type boolean"
  31199. );
  31200. this._mirrorY = inBool;
  31201. return this;
  31202. }
  31203. /**
  31204. * Causes the effect to play beneath most tokens
  31205. *
  31206. * @param {Boolean} inBool
  31207. * @returns {EffectSection}
  31208. */
  31209. belowTokens(inBool = true) {
  31210. if (typeof inBool !== "boolean")
  31211. throw this.sequence._customError(
  31212. this,
  31213. "belowTokens",
  31214. "inBool must be of type boolean"
  31215. );
  31216. if (!inBool)
  31217. return this;
  31218. return this.elevation(0, { absolute: true });
  31219. }
  31220. /**
  31221. * Causes the effect to play beneath most tiles
  31222. *
  31223. * @param {Boolean} inBool
  31224. * @returns {EffectSection}
  31225. */
  31226. belowTiles(inBool = true) {
  31227. if (typeof inBool !== "boolean")
  31228. throw this.sequence._customError(
  31229. this,
  31230. "belowTokens",
  31231. "inBool must be of type boolean"
  31232. );
  31233. if (!inBool)
  31234. return this;
  31235. return this.elevation(-1, { absolute: true });
  31236. }
  31237. /**
  31238. * Causes the effect to be played on top of the vision mask
  31239. *
  31240. * @param {Boolean} inBool
  31241. * @returns {EffectSection}
  31242. */
  31243. aboveLighting(inBool = true) {
  31244. if (typeof inBool !== "boolean")
  31245. throw this.sequence._customError(
  31246. this,
  31247. "aboveLighting",
  31248. "inBool must be of type boolean"
  31249. );
  31250. this._aboveLighting = inBool;
  31251. return this;
  31252. }
  31253. /**
  31254. * Changes the effect's elevation
  31255. *
  31256. * @param {Number} inElevation
  31257. * @param {Object} inOptions
  31258. * @returns {EffectSection}
  31259. */
  31260. elevation(inElevation, inOptions = {}) {
  31261. if (typeof inElevation !== "number")
  31262. throw this.sequence._customError(
  31263. this,
  31264. "elevation",
  31265. "inElevation must be of type number"
  31266. );
  31267. if (typeof inOptions !== "object")
  31268. throw this.sequence._customError(
  31269. this,
  31270. "elevation",
  31271. `inOptions must be of type object`
  31272. );
  31273. inOptions = foundry.utils.mergeObject(
  31274. {
  31275. elevation: 1,
  31276. absolute: false
  31277. },
  31278. inOptions
  31279. );
  31280. if (typeof inOptions.absolute !== "boolean")
  31281. throw this.sequence._customError(
  31282. this,
  31283. "elevation",
  31284. "inOptions.absolute must be of type boolean"
  31285. );
  31286. this._elevation = {
  31287. elevation: inElevation,
  31288. absolute: inOptions.absolute
  31289. };
  31290. return this;
  31291. }
  31292. /**
  31293. * Sets the zIndex of the effect, potentially displaying it on top of other effects the same elevation
  31294. *
  31295. * @param {Number} inZIndex
  31296. * @returns {EffectSection}
  31297. */
  31298. zIndex(inZIndex) {
  31299. if (!is_real_number(inZIndex))
  31300. throw this.sequence._customError(
  31301. this,
  31302. "zIndex",
  31303. "inZIndex must be of type number"
  31304. );
  31305. this._zIndex = inZIndex;
  31306. return this;
  31307. }
  31308. /**
  31309. * 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.
  31310. *
  31311. * @param {Number} inExtraDuration
  31312. * @returns {EffectSection}
  31313. */
  31314. extraEndDuration(inExtraDuration) {
  31315. if (!is_real_number(inExtraDuration))
  31316. throw this.sequence._customError(
  31317. this,
  31318. "extraEndDuration",
  31319. "inExtraDuration must be of type number"
  31320. );
  31321. this._extraEndDuration = inExtraDuration;
  31322. return this;
  31323. }
  31324. /**
  31325. * Rotates the sprite
  31326. *
  31327. * @param {Number} inAngle
  31328. * @returns {EffectSection}
  31329. */
  31330. spriteRotation(inAngle) {
  31331. if (!is_real_number(inAngle))
  31332. throw this.sequence._customError(
  31333. this,
  31334. "spriteRotation",
  31335. "inAngle must be of type number"
  31336. );
  31337. this._spriteRotation = inAngle;
  31338. return this;
  31339. }
  31340. /**
  31341. * Causes the effect to not rotate should its container rotate
  31342. *
  31343. * @param {Boolean} [inBool=true] inBool
  31344. * @returns {EffectSection}
  31345. */
  31346. zeroSpriteRotation(inBool = true) {
  31347. if (typeof inBool !== "boolean")
  31348. throw this.sequence._customError(
  31349. this,
  31350. "zeroSpriteRotation",
  31351. "inBool must be of type boolean"
  31352. );
  31353. this._zeroSpriteRotation = inBool;
  31354. return this;
  31355. }
  31356. /**
  31357. * If the effect would loop due to its duration or persistence, this causes it not to
  31358. *
  31359. * @param {Boolean} [inBool=true] inBool
  31360. * @returns {EffectSection}
  31361. */
  31362. noLoop(inBool = true) {
  31363. if (typeof inBool !== "boolean")
  31364. throw this.sequence._customError(
  31365. this,
  31366. "noLoop",
  31367. "inBool must be of type boolean"
  31368. );
  31369. this._noLoop = inBool;
  31370. return this;
  31371. }
  31372. /**
  31373. * Causes the effect to not show up in the Effect Manager UI - DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING
  31374. *
  31375. * @param inBool
  31376. * @returns {EffectSection}
  31377. */
  31378. private(inBool = true) {
  31379. if (typeof inBool !== "boolean")
  31380. throw this.sequence._customError(
  31381. this,
  31382. "private",
  31383. "inBool must be of type boolean"
  31384. );
  31385. this._private = inBool;
  31386. return this;
  31387. }
  31388. /**
  31389. * Causes the effect to be played in screen space instead of world space (where tokens are)
  31390. *
  31391. * @param {Boolean} [inBool=true] inBool
  31392. * @returns {EffectSection}
  31393. */
  31394. screenSpace(inBool = true) {
  31395. if (typeof inBool !== "boolean")
  31396. throw this.sequence._customError(
  31397. this,
  31398. "screenSpace",
  31399. "inBool must be of type boolean"
  31400. );
  31401. this._screenSpace = inBool;
  31402. this._screenSpaceAnchor = this._screenSpaceAnchor ?? { x: 0.5, y: 0.5 };
  31403. return this;
  31404. }
  31405. /**
  31406. * Causes the effect to be played above all of the UI elements
  31407. *
  31408. * @param {Boolean} [inBool=true] inBool
  31409. * @returns {EffectSection}
  31410. */
  31411. screenSpaceAboveUI(inBool = true) {
  31412. if (typeof inBool !== "boolean")
  31413. throw this.sequence._customError(
  31414. this,
  31415. "screenSpaceAboveUI",
  31416. "inBool must be of type boolean"
  31417. );
  31418. this._screenSpaceAboveUI = inBool;
  31419. return this;
  31420. }
  31421. /**
  31422. * Positions the effect in a screen space position, offset from its .screenSpaceAnchor()
  31423. *
  31424. * @param {Object} inPosition
  31425. * @returns {EffectSection}
  31426. */
  31427. screenSpacePosition(inPosition) {
  31428. inPosition = {
  31429. x: inPosition?.x ?? 0,
  31430. y: inPosition?.y ?? 0
  31431. };
  31432. if (!is_real_number(inPosition.x))
  31433. throw this.sequence._customError(
  31434. this,
  31435. "screenSpacePosition",
  31436. `inPosition.x must be of type number!`
  31437. );
  31438. if (!is_real_number(inPosition.y))
  31439. throw this.sequence._customError(
  31440. this,
  31441. "screenSpacePosition",
  31442. `inPosition.y must be of type number!`
  31443. );
  31444. this._screenSpacePosition = inPosition;
  31445. return this;
  31446. }
  31447. /**
  31448. * Anchors the sprite according to the given x and y coordinates, or uniformly based on a single number in screen space
  31449. *
  31450. * @param {Number|Object} inAnchor
  31451. * @returns {EffectSection}
  31452. */
  31453. screenSpaceAnchor(inAnchor) {
  31454. if (is_real_number(inAnchor)) {
  31455. inAnchor = {
  31456. x: inAnchor,
  31457. y: inAnchor
  31458. };
  31459. }
  31460. inAnchor = {
  31461. x: inAnchor?.x ?? 0.5,
  31462. y: inAnchor?.y ?? 0.5
  31463. };
  31464. if (!is_real_number(inAnchor.x))
  31465. throw this.sequence._customError(
  31466. this,
  31467. "screenSpaceAnchor",
  31468. `inAnchor.x must be of type number!`
  31469. );
  31470. if (!is_real_number(inAnchor.y))
  31471. throw this.sequence._customError(
  31472. this,
  31473. "screenSpaceAnchor",
  31474. `inAnchor.y must be of type number!`
  31475. );
  31476. this._screenSpaceAnchor = inAnchor;
  31477. return this;
  31478. }
  31479. /**
  31480. * Sets up various properties relating to scale of the effect on the screen
  31481. *
  31482. * @param {Object} inOptions
  31483. * @returns {EffectSection}
  31484. */
  31485. screenSpaceScale(inOptions) {
  31486. if (typeof inOptions !== "object")
  31487. throw this.sequence._customError(
  31488. this,
  31489. "screenSpaceScale",
  31490. `inOptions must be of type object`
  31491. );
  31492. inOptions = foundry.utils.mergeObject(
  31493. {
  31494. x: 1,
  31495. y: 1,
  31496. fitX: false,
  31497. fitY: false,
  31498. ratioX: false,
  31499. ratioY: false
  31500. },
  31501. inOptions
  31502. );
  31503. if (!is_real_number(inOptions.x))
  31504. throw this.sequence._customError(
  31505. this,
  31506. "screenSpaceScale",
  31507. `inOptions.x must be of type number!`
  31508. );
  31509. if (!is_real_number(inOptions.y))
  31510. throw this.sequence._customError(
  31511. this,
  31512. "screenSpaceScale",
  31513. `inOptions.y must be of type number!`
  31514. );
  31515. if (typeof inOptions.fitX !== "boolean")
  31516. throw this.sequence._customError(
  31517. this,
  31518. "screenSpaceScale",
  31519. "inOptions.fitX must be of type boolean"
  31520. );
  31521. if (typeof inOptions.fitY !== "boolean")
  31522. throw this.sequence._customError(
  31523. this,
  31524. "screenSpaceScale",
  31525. "inOptions.fitY must be of type boolean"
  31526. );
  31527. if (typeof inOptions.ratioX !== "boolean")
  31528. throw this.sequence._customError(
  31529. this,
  31530. "screenSpaceScale",
  31531. "inOptions.ratioX must be of type boolean"
  31532. );
  31533. if (typeof inOptions.ratioY !== "boolean")
  31534. throw this.sequence._customError(
  31535. this,
  31536. "screenSpaceScale",
  31537. "inOptions.ratioY must be of type boolean"
  31538. );
  31539. if (inOptions.ratioX && inOptions.ratioY)
  31540. throw this.sequence._customError(
  31541. this,
  31542. "screenSpaceScale",
  31543. "both ratioX and ratioY cannot be true, one axis must fit or be set directly"
  31544. );
  31545. this._screenSpaceScale = inOptions;
  31546. return this;
  31547. }
  31548. /**
  31549. * This is for adding extra information to an effect, like the origin of the effect in the form of the item's uuid.
  31550. * The method accepts a string or a Document that has an UUID.
  31551. *
  31552. * @param {string|document} inOrigin
  31553. * @returns {Section}
  31554. */
  31555. origin(inOrigin) {
  31556. inOrigin = validate_document(inOrigin);
  31557. if (inOrigin instanceof foundry.abstract.Document) {
  31558. inOrigin = inOrigin?.uuid;
  31559. if (!inOrigin)
  31560. throw this.sequence._customError(
  31561. this,
  31562. "origin",
  31563. "could not find the UUID for the given Document"
  31564. );
  31565. }
  31566. if (typeof inOrigin !== "string")
  31567. throw this.sequence._customError(
  31568. this,
  31569. "origin",
  31570. "inOrigin must be of type string"
  31571. );
  31572. this._origin = inOrigin;
  31573. return this;
  31574. }
  31575. /**
  31576. * Ties the effect to any number of documents in Foundry - if those get deleted, the effect is ended.
  31577. *
  31578. * @param {String|PlaceableObject|foundry.abstract.Document|Array<String|PlaceableObject|foundry.abstract.Document>} inDocuments
  31579. * @returns {EffectSection}
  31580. */
  31581. tieToDocuments(inDocuments) {
  31582. if (!Array.isArray(inDocuments)) {
  31583. inDocuments = [inDocuments];
  31584. }
  31585. for (let doc of inDocuments) {
  31586. if (typeof doc !== "string" && !(doc instanceof PlaceableObject) && !(doc instanceof foundry.abstract.Document)) {
  31587. throw this.sequence._customError(
  31588. this,
  31589. "tieToDocument",
  31590. "inOrigin must be of type string, PlaceableObject, or Document, or an array thereof"
  31591. );
  31592. }
  31593. if (typeof doc === "string") {
  31594. const obj = from_uuid_fast(doc);
  31595. if (!obj)
  31596. throw this.sequence._customError(
  31597. this,
  31598. "tieToDocument",
  31599. `could not find document with UUID "${doc}"`
  31600. );
  31601. } else {
  31602. doc = validate_document(doc);
  31603. if (doc instanceof foundry.abstract.Document) {
  31604. doc = doc?.uuid;
  31605. if (!doc)
  31606. throw this.sequence._customError(
  31607. this,
  31608. "tieToDocument",
  31609. "could not find the UUID for the given object"
  31610. );
  31611. }
  31612. }
  31613. this._tiedDocuments.push(doc);
  31614. }
  31615. return this;
  31616. }
  31617. /**
  31618. * Masks the effect to the given object or objects. If no object is given, the effect will be masked to the source
  31619. * of the effect.
  31620. *
  31621. * @param {Token/TokenDocument/Tile/TileDocument/Drawing/DrawingDocument/MeasuredTemplate/MeasuredTemplateDocument/Array} inObject
  31622. * @returns {Section}
  31623. */
  31624. mask(inObject) {
  31625. if (!inObject) {
  31626. this._selfMask = true;
  31627. return this;
  31628. }
  31629. if (Array.isArray(inObject)) {
  31630. for (let obj of inObject) {
  31631. this.mask(obj);
  31632. }
  31633. return this;
  31634. }
  31635. const validatedObject = this._validateLocation(inObject);
  31636. const isValidObject = validatedObject instanceof TokenDocument || validatedObject instanceof TileDocument || validatedObject instanceof DrawingDocument || validatedObject instanceof MeasuredTemplateDocument;
  31637. if (!isValidObject) {
  31638. throw this.sequence._customError(
  31639. this,
  31640. "mask",
  31641. "A foundry object was provided, but only Tokens, Tiles, Drawings, and MeasuredTemplates may be used to create effect masks"
  31642. );
  31643. }
  31644. this._masks.push(get_object_identifier(validatedObject));
  31645. return this;
  31646. }
  31647. /**
  31648. * Causes the effect to be visible through walls
  31649. *
  31650. * @param inBool
  31651. * @returns {EffectSection}
  31652. */
  31653. xray(inBool = true) {
  31654. if (typeof inBool !== "boolean")
  31655. throw this.sequence._customError(
  31656. this,
  31657. "xray",
  31658. "inBool must be of type boolean"
  31659. );
  31660. this._xray = inBool;
  31661. return this;
  31662. }
  31663. /**
  31664. * Configures the isometric configuration of this effect
  31665. *
  31666. * @param inOptions
  31667. * @returns {EffectSection}
  31668. */
  31669. isometric(inOptions = {}) {
  31670. if (typeof inOptions !== "object")
  31671. throw this.sequence._customError(
  31672. this,
  31673. "isometric",
  31674. `inOptions must be of type object`
  31675. );
  31676. inOptions = foundry.utils.mergeObject(
  31677. {
  31678. overlay: false
  31679. },
  31680. inOptions
  31681. );
  31682. if (typeof inOptions?.overlay !== "boolean")
  31683. throw this.sequence._customError(
  31684. this,
  31685. "isometric",
  31686. "inOptions.overlay must be of type boolean"
  31687. );
  31688. this._isometric = inOptions;
  31689. return this;
  31690. }
  31691. /**
  31692. * @private
  31693. */
  31694. _expressWarnings() {
  31695. if (this._stretchTo && this._anchor?.x) {
  31696. this.sequence._showWarning(
  31697. this,
  31698. "stretchTo",
  31699. "you have called .stretchTo() and .anchor() - stretchTo will manually set the X axis of the anchor and may not behave like you expect.",
  31700. true
  31701. );
  31702. }
  31703. if (this._stretchTo && this._scaleToObject) {
  31704. throw this.sequence._customError(
  31705. this,
  31706. "stretchTo",
  31707. "You're trying to stretch towards an object, while scaling to fit another??? Make up your mind!"
  31708. );
  31709. }
  31710. if (this._stretchTo && this._randomRotation) {
  31711. throw this.sequence._customError(
  31712. this,
  31713. "stretchTo",
  31714. "You're trying to stretch towards an object, while trying to randomly rotate the effect? What?"
  31715. );
  31716. }
  31717. if (this._stretchTo && this._moveTowards) {
  31718. throw this.sequence._customError(
  31719. this,
  31720. "stretchTo",
  31721. "You're trying to stretch towards an object, while moving towards it? You're insane."
  31722. );
  31723. }
  31724. if (this._attachTo && this._stretchTo?.attachTo && (this._startTime || this._endTime) && this._isRangedEffect) {
  31725. throw this.sequence._customError(
  31726. this,
  31727. "stretchTo",
  31728. "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."
  31729. );
  31730. }
  31731. const source = this._getSourceObject();
  31732. const target = this._getTargetObject();
  31733. if (!this._screenSpace && this._persistOptions?.persistTokenPrototype && this._masks.filter((uuid) => uuid !== source).length > 0) {
  31734. this.sequence._showWarning(
  31735. this,
  31736. "persist",
  31737. "You have applied persistTokenPrototype with multiple masks from objects in the scene - these will not be persisted across scenes!",
  31738. true
  31739. );
  31740. }
  31741. if (!source && !target && !this._screenSpace) {
  31742. throw this.sequence._customError(
  31743. this,
  31744. "play",
  31745. "Could not determine where to play the effect!"
  31746. );
  31747. }
  31748. }
  31749. /**
  31750. * @OVERRIDE
  31751. */
  31752. async preRun() {
  31753. if (this._from) {
  31754. this._file = this._file || this._from.object?.texture?.src;
  31755. if (this._source === null) {
  31756. this._source = this._validateLocation(this._from.object);
  31757. }
  31758. if (this._size === null) {
  31759. const size = get_object_dimensions(this._from.object);
  31760. this._size = {
  31761. width: size?.width ?? canvas.grid.size,
  31762. height: size?.height ?? canvas.grid.size,
  31763. gridUnits: false
  31764. };
  31765. }
  31766. if (this._mirrorX === null && (this._from.object.mirrorX || this._from.object?.tile && this._from.object?.tile.scale.x < 0)) {
  31767. this._mirrorX = true;
  31768. }
  31769. if (this._mirrorY === null && (this._from.object.mirrorY || this._from.object?.tile && this._from.object?.tile.scale.y < 0)) {
  31770. this._mirrorY = true;
  31771. }
  31772. if (this._angle === null && this._from.object?.rotation) {
  31773. this._angle = -this._from.object.rotation;
  31774. }
  31775. this._randomOffset = {
  31776. source: this._randomOffset?.source ?? this._from.options.randomOffset,
  31777. target: this._randomOffset?.target ?? false
  31778. };
  31779. }
  31780. }
  31781. /**
  31782. * @OVERRIDE
  31783. * @returns {Promise<void>}
  31784. */
  31785. async run() {
  31786. if (!user_can_do("permissions-effect-create") || !this._playEffect) {
  31787. if (!user_can_do("permissions-effect-create")) {
  31788. debounce(EffectSection.debounceWarning, 1e3);
  31789. }
  31790. return new Promise((resolve) => {
  31791. resolve();
  31792. });
  31793. }
  31794. if (!this._deserializedData)
  31795. this._expressWarnings();
  31796. const data = await this._sanitizeEffectData();
  31797. if (Hooks.call("preCreateSequencerEffect", data) === false)
  31798. return;
  31799. let push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
  31800. let canvasEffectData = await Sequencer.EffectManager.play(data, push);
  31801. let totalDuration = this._currentWaitTime;
  31802. if (this._persist) {
  31803. totalDuration += await canvasEffectData.promise;
  31804. } else {
  31805. totalDuration += await canvasEffectData.duration;
  31806. }
  31807. await new Promise((resolve) => setTimeout(resolve, totalDuration));
  31808. }
  31809. /**
  31810. * @private
  31811. */
  31812. _applyTraits() {
  31813. Object.assign(this.constructor.prototype, traits.files);
  31814. Object.assign(this.constructor.prototype, traits.audio);
  31815. Object.assign(this.constructor.prototype, traits.moves);
  31816. Object.assign(this.constructor.prototype, traits.opacity);
  31817. Object.assign(this.constructor.prototype, traits.rotation);
  31818. Object.assign(this.constructor.prototype, traits.scale);
  31819. Object.assign(this.constructor.prototype, traits.time);
  31820. Object.assign(this.constructor.prototype, traits.users);
  31821. Object.assign(this.constructor.prototype, traits.animation);
  31822. Object.assign(this.constructor.prototype, traits.filter);
  31823. Object.assign(this.constructor.prototype, traits.tint);
  31824. Object.assign(this.constructor.prototype, traits.location);
  31825. Object.assign(this.constructor.prototype, traits.offset);
  31826. Object.assign(this.constructor.prototype, traits.text);
  31827. }
  31828. /**
  31829. * @private
  31830. */
  31831. async _initialize() {
  31832. if (this._name) {
  31833. if (!this.sequence.nameOffsetMap) {
  31834. this.sequence.nameOffsetMap = {};
  31835. }
  31836. if (!this.sequence.nameOffsetMap[this._name]) {
  31837. const source = this._getSourceObject();
  31838. const target = this._getTargetObject();
  31839. if (this._offsetLegacy && !this._offset) {
  31840. this._offset = {
  31841. source: !target ? this._offsetLegacy : false,
  31842. target: !!target ? this._offsetLegacy : false
  31843. };
  31844. }
  31845. if (this._randomOffsetLegacy && !this._randomOffset) {
  31846. this._randomOffset = {
  31847. source: !target ? this._randomOffsetLegacy : false,
  31848. target: !!target ? this._randomOffsetLegacy : false
  31849. };
  31850. }
  31851. this.sequence.nameOffsetMap[this._name] = {
  31852. seed: `${this._name}-${randomID()}`,
  31853. source,
  31854. target,
  31855. randomOffset: this._randomOffset,
  31856. missed: this._missed,
  31857. offset: this._offset,
  31858. repetitions: this._repetitions,
  31859. twister: {}
  31860. };
  31861. }
  31862. }
  31863. if (!this._file && !this._from && !this._text && !this._shapes.length && this.sequence.softFail) {
  31864. this._playEffect = false;
  31865. return;
  31866. }
  31867. let fileData = this._file ? await this._determineFile(this._file) : {
  31868. file: this._file,
  31869. forcedIndex: false,
  31870. customRange: false
  31871. };
  31872. this._isRangedEffect = fileData?.file?.rangeFind;
  31873. if (fileData.customRange || fileData.file?.dbPath)
  31874. return;
  31875. let exists = false;
  31876. try {
  31877. exists = await SequencerFileCache.srcExists(fileData.file);
  31878. } catch (err) {
  31879. }
  31880. if (!exists) {
  31881. if (this.sequence.softFail) {
  31882. this._playEffect = false;
  31883. return;
  31884. }
  31885. throw this.sequence._customError(
  31886. this,
  31887. "Play",
  31888. `Could not find file:<br>${fileData.file}`
  31889. );
  31890. }
  31891. }
  31892. /**
  31893. * @private
  31894. */
  31895. _getSourceObject() {
  31896. if (!this._source || typeof this._source !== "object")
  31897. return this._source;
  31898. if (this._source?.cachedLocation) {
  31899. return get_object_canvas_data(this._source);
  31900. }
  31901. return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
  31902. }
  31903. /**
  31904. * @private
  31905. */
  31906. _getTargetObject() {
  31907. if (!this._target?.target)
  31908. return this._target;
  31909. if (typeof this._target.target !== "object")
  31910. return this._target.target;
  31911. if (this._target?.target?.cachedLocation) {
  31912. return get_object_canvas_data(this._target.target, true);
  31913. }
  31914. return get_object_identifier(this._target.target) ?? get_object_canvas_data(this._target.target, true);
  31915. }
  31916. /**
  31917. * @private
  31918. */
  31919. async _sanitizeEffectData() {
  31920. if (this._deserializedData) {
  31921. this._deserializedData.creationTimestamp = +new Date();
  31922. this._deserializedData.remote = true;
  31923. return this._deserializedData;
  31924. }
  31925. const { file, forcedIndex, customRange } = this._file && this._playEffect ? await this._determineFile(this._file) : {
  31926. file: this._file,
  31927. forcedIndex: false,
  31928. customRange: false
  31929. };
  31930. const source = this._getSourceObject();
  31931. const target = this._getTargetObject();
  31932. if (this._offsetLegacy) {
  31933. this._offset = {
  31934. source: !target && this._offset?.source ? this._offsetLegacy : this._offset?.source,
  31935. target: !!target && this._offset?.target ? this._offsetLegacy : this._offset?.target
  31936. };
  31937. }
  31938. if (this._randomOffsetLegacy) {
  31939. this._randomOffset = {
  31940. source: !target && this._randomOffset?.source ? this._randomOffsetLegacy : this._randomOffset?.source,
  31941. target: !!target && this._randomOffset?.target ? this._randomOffsetLegacy : this._randomOffset?.target
  31942. };
  31943. }
  31944. if (this._selfMask) {
  31945. this._masks.push(
  31946. get_object_identifier(this._source) ?? get_object_canvas_data(this._source)
  31947. );
  31948. }
  31949. let sceneId = game.user.viewedScene;
  31950. if (is_UUID(source)) {
  31951. const potentialSceneId = source.split(".")[1];
  31952. if (game.scenes.get(potentialSceneId)) {
  31953. sceneId = potentialSceneId;
  31954. }
  31955. } else if (is_UUID(target)) {
  31956. const potentialSceneId = target.split(".")[1];
  31957. if (game.scenes.get(potentialSceneId)) {
  31958. sceneId = potentialSceneId;
  31959. }
  31960. }
  31961. let data = foundry.utils.duplicate({
  31962. /**
  31963. * Core properties
  31964. */
  31965. _id: randomID(),
  31966. flagVersion: flagManager.latestFlagVersion,
  31967. sequenceId: this.sequence.id,
  31968. creationTimestamp: +new Date(),
  31969. sceneId,
  31970. creatorUserId: game.userId,
  31971. moduleName: this.sequence.moduleName,
  31972. users: this._users ? Array.from(this._users) : false,
  31973. name: this._name,
  31974. origin: this._origin,
  31975. index: this.sequence.effectIndex,
  31976. repetition: this._currentRepetition,
  31977. private: this._private,
  31978. temporary: this._temporaryEffect,
  31979. tiedDocuments: Array.from(new Set(this._tiedDocuments)),
  31980. /**
  31981. * Source/target properties
  31982. */
  31983. source,
  31984. target,
  31985. rotateTowards: this._rotateTowards,
  31986. stretchTo: this._stretchTo ? {
  31987. attachTo: this._stretchTo.attachTo,
  31988. onlyX: this._stretchTo.onlyX
  31989. } : false,
  31990. moveTowards: this._moveTowards ? {
  31991. ease: this._moveTowards.ease,
  31992. rotate: this._moveTowards.rotate
  31993. } : false,
  31994. attachTo: this._attachTo,
  31995. missed: this._missed,
  31996. /**
  31997. * Sprite properties
  31998. */
  31999. file: file?.dbPath ?? file,
  32000. customRange,
  32001. forcedIndex,
  32002. text: this._text,
  32003. tilingTexture: this._tilingTexture,
  32004. masks: Array.from(new Set(this._masks)),
  32005. shapes: this._shapes,
  32006. volume: this._volume,
  32007. isometric: this._isometric,
  32008. // Transforms
  32009. scale: this._getCalculatedScale("scale"),
  32010. spriteScale: this._getCalculatedScale("spriteScale"),
  32011. angle: this._angle,
  32012. size: this._size,
  32013. offset: this._offset,
  32014. anchor: this._anchor,
  32015. spriteOffset: this._spriteOffset,
  32016. spriteAnchor: this._spriteAnchor,
  32017. template: this._template,
  32018. zeroSpriteRotation: this._zeroSpriteRotation,
  32019. randomOffset: this._randomOffset,
  32020. randomRotation: this._randomRotation,
  32021. scaleToObject: this._scaleToObject,
  32022. elevation: this._elevation,
  32023. aboveLighting: this._aboveLighting,
  32024. xray: this._xray,
  32025. // Appearance
  32026. zIndex: this._zIndex,
  32027. opacity: is_real_number(this._opacity) ? this._opacity : 1,
  32028. filters: this._filters,
  32029. noLoop: this._noLoop,
  32030. spriteRotation: this._spriteRotation,
  32031. tint: this._tint?.decimal,
  32032. flipX: this._mirrorX || this._randomMirrorX && Math.random() < 0.5,
  32033. flipY: this._mirrorY || this._randomMirrorY && Math.random() < 0.5,
  32034. /**
  32035. * Time properties
  32036. */
  32037. duration: this._duration,
  32038. persist: this._persist,
  32039. persistOptions: this._persistOptions,
  32040. playbackRate: this._playbackRate,
  32041. extraEndDuration: this._extraEndDuration,
  32042. time: this._startTime || this._endTime ? {
  32043. start: is_real_number(this._startTime) ? {
  32044. value: this._startTime,
  32045. isPerc: this._startPerc
  32046. } : false,
  32047. end: is_real_number(this._endTime) ? {
  32048. value: this._endTime,
  32049. isPerc: this._endPerc
  32050. } : false,
  32051. isRange: this._isRange
  32052. } : false,
  32053. /**
  32054. * Animation properties
  32055. */
  32056. moves: this._moveTowards,
  32057. moveSpeed: this._moveSpeed,
  32058. fadeIn: this._fadeIn,
  32059. fadeOut: this._fadeOut,
  32060. scaleIn: this._scaleIn,
  32061. scaleOut: this._scaleOut,
  32062. rotateIn: this._rotateIn,
  32063. rotateOut: this._rotateOut,
  32064. fadeInAudio: this._fadeInAudio,
  32065. fadeOutAudio: this._fadeOutAudio,
  32066. animations: this._animations,
  32067. /**
  32068. * Screenspace properties
  32069. */
  32070. screenSpace: this._screenSpace,
  32071. screenSpaceAboveUI: this._screenSpaceAboveUI,
  32072. screenSpaceAnchor: this._screenSpaceAnchor,
  32073. screenSpacePosition: this._screenSpacePosition,
  32074. screenSpaceScale: this._screenSpaceScale,
  32075. nameOffsetMap: this.sequence.nameOffsetMap
  32076. });
  32077. for (let override of this._overrides) {
  32078. data = await override(this, data);
  32079. }
  32080. if ((typeof data.file !== "string" || data.file === "") && !data.text && !data.shapes && !data.customRange) {
  32081. throw this.sequence._customError(
  32082. this,
  32083. "file",
  32084. "an effect must have a file, text, or have a shape!"
  32085. );
  32086. }
  32087. return data;
  32088. }
  32089. async _serialize() {
  32090. const data = await super._serialize();
  32091. await this.preRun();
  32092. const sectionData = await this._sanitizeEffectData();
  32093. return {
  32094. ...data,
  32095. type: "effect",
  32096. sectionData
  32097. };
  32098. }
  32099. async _deserialize(data) {
  32100. this._deserializedData = data.sectionData;
  32101. return super._deserialize(data);
  32102. }
  32103. /**
  32104. * @private
  32105. */
  32106. _getCalculatedScale(type) {
  32107. const min = this["_" + type + "Min"];
  32108. const max = this["_" + type + "Max"];
  32109. let scale2 = min;
  32110. if (is_real_number(min)) {
  32111. if (max && is_real_number(max)) {
  32112. scale2 = random_float_between(min, max);
  32113. }
  32114. scale2 = {
  32115. x: scale2,
  32116. y: scale2
  32117. };
  32118. }
  32119. return {
  32120. x: scale2?.x ?? 1,
  32121. y: scale2?.y ?? 1
  32122. };
  32123. }
  32124. }
  32125. class SoundSection extends Section {
  32126. constructor(inSequence, inFile = "") {
  32127. super(inSequence);
  32128. this._file = inFile;
  32129. this._volume = 0.8;
  32130. this._overrides = [];
  32131. }
  32132. static niceName = "Sound";
  32133. /**
  32134. * Adds a function that will run at the end of the sound serialization step, but before it is played. Allows direct
  32135. * modifications of sound's data.
  32136. *
  32137. * @param {function} inFunc
  32138. * @returns {SoundSection}
  32139. */
  32140. addOverride(inFunc) {
  32141. if (!is_function$1(inFunc))
  32142. throw this.sequence._customError(
  32143. this,
  32144. "addOverride",
  32145. "The given function needs to be an actual function."
  32146. );
  32147. this._overrides.push(inFunc);
  32148. return this;
  32149. }
  32150. /**
  32151. * @private
  32152. */
  32153. _applyTraits() {
  32154. Object.assign(this.constructor.prototype, traits.files);
  32155. Object.assign(this.constructor.prototype, traits.audio);
  32156. Object.assign(this.constructor.prototype, traits.time);
  32157. Object.assign(this.constructor.prototype, traits.users);
  32158. }
  32159. /**
  32160. * @OVERRIDE
  32161. * @returns {Promise}
  32162. */
  32163. async run() {
  32164. const playData = await this._sanitizeSoundData();
  32165. if (!playData.play && this.sequence.softFail) {
  32166. return new Promise((reject2) => {
  32167. reject2();
  32168. });
  32169. }
  32170. if (!playData?.play) {
  32171. this.sequence._customError(
  32172. this,
  32173. "Play",
  32174. `File not found: ${playData.src}`
  32175. );
  32176. return new Promise((reject2) => reject2());
  32177. }
  32178. if (Hooks.call("preCreateSequencerSound", playData) === false)
  32179. return;
  32180. let push = !(playData?.users?.length === 1 && playData?.users?.includes(game.userId)) && !this.sequence.localOnly;
  32181. SequencerAudioHelper$1.play(playData, push);
  32182. await new Promise(
  32183. (resolve) => setTimeout(resolve, this._currentWaitTime + playData.duration)
  32184. );
  32185. }
  32186. /**
  32187. * @returns {Promise}
  32188. * @private
  32189. */
  32190. async _sanitizeSoundData() {
  32191. if (this._deserializedData) {
  32192. return this._deserializedData;
  32193. }
  32194. if (!this._file) {
  32195. return {
  32196. play: false,
  32197. src: false
  32198. };
  32199. }
  32200. let { file, forcedIndex } = await this._determineFile(this._file);
  32201. if (!file) {
  32202. return {
  32203. play: false,
  32204. src: false
  32205. };
  32206. }
  32207. if (file instanceof SequencerFileBase) {
  32208. file.forcedIndex = forcedIndex;
  32209. if (file.timeRange) {
  32210. [this._startTime, this._endTime] = file.timeRange;
  32211. this._isRange = true;
  32212. }
  32213. file = file.getFile();
  32214. }
  32215. let soundFile = await AudioHelper.preloadSound(file);
  32216. if (!soundFile || soundFile.failed) {
  32217. return {
  32218. play: false,
  32219. src: this._file
  32220. };
  32221. }
  32222. let duration = soundFile.duration * 1e3;
  32223. let startTime = (this._startTime ? !this._startPerc ? this._startTime : this._startTime * duration : 0) / 1e3;
  32224. if (this._endTime) {
  32225. duration = !this._endPerc ? Number(
  32226. this._isRange ? this._endTime - this._startTime : duration - this._endTime
  32227. ) : this._endTime * duration;
  32228. }
  32229. let data = {
  32230. id: randomID(),
  32231. play: true,
  32232. src: file,
  32233. loop: this._duration > duration,
  32234. volume: this._volume,
  32235. fadeIn: this._fadeInAudio,
  32236. fadeOut: this._fadeOutAudio,
  32237. startTime,
  32238. duration: this._duration || duration,
  32239. sceneId: game.user.viewedScene,
  32240. users: this._users ? Array.from(this._users) : null
  32241. };
  32242. for (let override of this._overrides) {
  32243. data = await override(this, data);
  32244. }
  32245. if (typeof data.src !== "string" || data.src === "") {
  32246. throw this.sequence._customError(
  32247. this,
  32248. "file",
  32249. "a sound must have a src of type string!"
  32250. );
  32251. }
  32252. return data;
  32253. }
  32254. async _serialize() {
  32255. const data = await super._serialize();
  32256. const sectionData = await this._sanitizeSoundData();
  32257. return {
  32258. ...data,
  32259. type: "sound",
  32260. sectionData
  32261. };
  32262. }
  32263. async _deserialize(data) {
  32264. this._deserializedData = data.sectionData;
  32265. return super._deserialize(data);
  32266. }
  32267. }
  32268. class AnimationSection extends Section {
  32269. constructor(inSequence, inTarget) {
  32270. super(inSequence);
  32271. this._teleportTo = false;
  32272. this._originObject = false;
  32273. this._moveSpeed = 23;
  32274. this._offset = { x: 0, y: 0 };
  32275. this._closestSquare = false;
  32276. this._snapToGrid = false;
  32277. this._hide = void 0;
  32278. if (inTarget)
  32279. this.on(inTarget);
  32280. }
  32281. static niceName = "Animation";
  32282. /**
  32283. * Sets the target object to be animated
  32284. *
  32285. * @param {object|string} inTarget
  32286. * @returns {AnimationSection}
  32287. */
  32288. on(inTarget) {
  32289. inTarget = this._validateLocation(inTarget);
  32290. if (!inTarget)
  32291. throw this.sequence._customError(
  32292. this,
  32293. "on",
  32294. "could not find position of given object"
  32295. );
  32296. this._originObject = this._validateLocation(inTarget);
  32297. return this;
  32298. }
  32299. /**
  32300. * Sets the location to teleport the target object to
  32301. *
  32302. * @param {object|string} inTarget
  32303. * @param {object} options
  32304. * @returns {AnimationSection}
  32305. */
  32306. teleportTo(inTarget, options = {}) {
  32307. options = foundry.utils.mergeObject(
  32308. {
  32309. delay: 0,
  32310. relativeToCenter: false
  32311. },
  32312. options
  32313. );
  32314. if (!is_real_number(options.delay))
  32315. throw this.sequence._customError(
  32316. this,
  32317. "teleportTo",
  32318. "options.delay must be of type number"
  32319. );
  32320. inTarget = this._validateLocation(inTarget);
  32321. if (!inTarget)
  32322. throw this.sequence._customError(
  32323. this,
  32324. "teleportTo",
  32325. "could not find position of given object"
  32326. );
  32327. options.target = this._validateLocation(inTarget);
  32328. this._teleportTo = options;
  32329. return this;
  32330. }
  32331. /**
  32332. * Sets the location to rotate the object to
  32333. *
  32334. * @param {object|string} inLocation
  32335. * @param {object} options
  32336. * @returns this
  32337. */
  32338. rotateTowards(inLocation, options = {}) {
  32339. options = foundry.utils.mergeObject(
  32340. {
  32341. duration: 0,
  32342. ease: "linear",
  32343. delay: 0,
  32344. rotationOffset: 0,
  32345. towardsCenter: true,
  32346. cacheLocation: false
  32347. },
  32348. options
  32349. );
  32350. if (!is_real_number(options.duration))
  32351. throw this.sequence._customError(
  32352. this,
  32353. "rotateTowards",
  32354. "options.duration must be of type number"
  32355. );
  32356. if (typeof options.ease !== "string")
  32357. throw this.sequence._customError(
  32358. this,
  32359. "rotateTowards",
  32360. "options.ease must be of type string"
  32361. );
  32362. if (!is_real_number(options.delay))
  32363. throw this.sequence._customError(
  32364. this,
  32365. "rotateTowards",
  32366. "options.delay must be of type number"
  32367. );
  32368. if (!is_real_number(options.rotationOffset))
  32369. throw this.sequence._customError(
  32370. this,
  32371. "rotateTowards",
  32372. "options.rotationOffset must be of type number"
  32373. );
  32374. if (typeof options.towardsCenter !== "boolean")
  32375. throw this.sequence._customError(
  32376. this,
  32377. "rotateTowards",
  32378. "options.towardsCenter must be of type boolean"
  32379. );
  32380. if (typeof options.cacheLocation !== "boolean")
  32381. throw this.sequence._customError(
  32382. this,
  32383. "rotateTowards",
  32384. "options.cacheLocation must be of type boolean"
  32385. );
  32386. options.target = this._validateLocation(inLocation);
  32387. if (!options.target)
  32388. throw this.sequence._customError(
  32389. this,
  32390. "rotateTowards",
  32391. "could not find position of given object"
  32392. );
  32393. options.target = options.cacheLocation ? get_object_position(options.target, { measure: true }) : options.target;
  32394. this._rotateTowards = options;
  32395. return this;
  32396. }
  32397. /**
  32398. * Causes the movement or teleportation to be offset in the X and/or Y axis
  32399. *
  32400. * @param {object} inOffset
  32401. * @returns {AnimationSection}
  32402. */
  32403. offset(inOffset) {
  32404. inOffset = foundry.utils.mergeObject({ x: 0, y: 0 }, inOffset);
  32405. this._offset = this._validateLocation(inOffset);
  32406. return this;
  32407. }
  32408. /**
  32409. * Causes the movement or teleportation to pick the closest non-intersecting square, if the target is a token or tile
  32410. *
  32411. * @param {boolean} inBool
  32412. * @returns {AnimationSection}
  32413. */
  32414. closestSquare(inBool = true) {
  32415. if (typeof inBool !== "boolean")
  32416. throw this.sequence._customError(
  32417. this,
  32418. "closestSquare",
  32419. "inBool must be of type boolean"
  32420. );
  32421. this._closestSquare = inBool;
  32422. return this;
  32423. }
  32424. /**
  32425. * Causes the final location to be snapped to the grid
  32426. *
  32427. * @param {boolean} inBool
  32428. * @returns {AnimationSection}
  32429. */
  32430. snapToGrid(inBool = true) {
  32431. if (typeof inBool !== "boolean")
  32432. throw this.sequence._customError(
  32433. this,
  32434. "snapToGrid",
  32435. "inBool must be of type boolean"
  32436. );
  32437. this._snapToGrid = inBool;
  32438. return this;
  32439. }
  32440. /**
  32441. * Causes the object to become hidden
  32442. *
  32443. * @param {boolean} inBool
  32444. * @returns {AnimationSection}
  32445. */
  32446. hide(inBool = true) {
  32447. if (typeof inBool !== "boolean")
  32448. throw this.sequence._customError(
  32449. this,
  32450. "hide",
  32451. "inBool must be of type boolean"
  32452. );
  32453. this._hide = inBool;
  32454. return this;
  32455. }
  32456. /**
  32457. * Causes the object to become visible
  32458. *
  32459. * @param {boolean} inBool
  32460. * @returns {AnimationSection}
  32461. */
  32462. show(inBool = true) {
  32463. if (typeof inBool !== "boolean")
  32464. throw this.sequence._customError(
  32465. this,
  32466. "show",
  32467. "inBool must be of type boolean"
  32468. );
  32469. this._hide = !inBool;
  32470. return this;
  32471. }
  32472. /**
  32473. * @private
  32474. */
  32475. async run() {
  32476. return this._runAnimate();
  32477. }
  32478. /**
  32479. * @private
  32480. */
  32481. _applyTraits() {
  32482. Object.assign(this.constructor.prototype, traits.moves);
  32483. Object.assign(this.constructor.prototype, traits.opacity);
  32484. Object.assign(this.constructor.prototype, traits.rotation);
  32485. Object.assign(this.constructor.prototype, traits.audio);
  32486. Object.assign(this.constructor.prototype, traits.tint);
  32487. }
  32488. /**
  32489. * @private
  32490. */
  32491. async _updateObject(obj, updates, animate = false, animation2 = {}) {
  32492. const uuid = obj?.uuid ?? obj?.document?.uuid;
  32493. await sequencerSocket.executeAsGM(
  32494. SOCKET_HANDLERS.UPDATE_DOCUMENT,
  32495. uuid,
  32496. updates,
  32497. { animate, animation: animation2 }
  32498. );
  32499. }
  32500. /**
  32501. * @private
  32502. */
  32503. async _execute() {
  32504. if (!await this._shouldPlay())
  32505. return;
  32506. let self = this;
  32507. this._basicDelay = random_float_between(this._delayMin, this._delayMax);
  32508. return new Promise(async (resolve) => {
  32509. setTimeout(async () => {
  32510. if (this._shouldAsync) {
  32511. await self.run();
  32512. } else {
  32513. self.run();
  32514. }
  32515. resolve();
  32516. }, this._basicDelay);
  32517. });
  32518. }
  32519. /**
  32520. * @private
  32521. */
  32522. _getClosestSquare(origin, target) {
  32523. let originLoc = get_object_position(origin, { exact: true });
  32524. let targetLoc = get_object_position(target, { exact: true });
  32525. let originDimensions = get_object_dimensions(origin);
  32526. let targetDimensions = get_object_dimensions(target);
  32527. let originBottom = Math.max(
  32528. originDimensions.width - canvas.grid.size,
  32529. canvas.grid.size
  32530. );
  32531. let originRight = Math.max(
  32532. originDimensions.height - canvas.grid.size,
  32533. canvas.grid.size
  32534. );
  32535. let ray = new Ray(originLoc, targetLoc);
  32536. let dx = ray.dx;
  32537. let dy = ray.dy;
  32538. if (dx > 0 && Math.abs(dx) > originRight) {
  32539. dx -= originDimensions.width;
  32540. } else if (dx < 0 && Math.abs(dx) > targetDimensions.width) {
  32541. dx += targetDimensions.height;
  32542. } else {
  32543. dx = 0;
  32544. }
  32545. if (dy > 0 && Math.abs(dy) > originBottom) {
  32546. dy -= originDimensions.height;
  32547. } else if (dy < 0 && Math.abs(dy) > targetDimensions.height) {
  32548. dy += targetDimensions.height;
  32549. } else {
  32550. dy = 0;
  32551. }
  32552. return {
  32553. x: originLoc.x + dx,
  32554. y: originLoc.y + dy
  32555. };
  32556. }
  32557. /**
  32558. * @private
  32559. */
  32560. _snapLocationToGrid(inLocation) {
  32561. return canvas.grid.getSnappedPosition(inLocation.x, inLocation.y);
  32562. }
  32563. /**
  32564. * This needs a rewrite, jeesus.
  32565. */
  32566. async _runAnimate() {
  32567. let animData = {
  32568. attributes: [],
  32569. maxFPS: 1e3 / game.settings.get("core", "maxFPS"),
  32570. lastTimespan: performance.now(),
  32571. totalDt: 0
  32572. };
  32573. let overallDuration = this._duration ? this._duration : 0;
  32574. if (this._rotateTowards) {
  32575. let offset2 = (this._angle ? this._angle : 0) + this._rotateTowards.rotationOffset;
  32576. let targetLoc = this._moveTowards?.target || this._teleportTo?.target || this._originObject;
  32577. targetLoc = this._closestSquare ? this._getClosestSquare(this._originObject, targetLoc) : get_object_position(targetLoc, { exact: true });
  32578. targetLoc.x += this._offset.x;
  32579. targetLoc.y += this._offset.y;
  32580. if (this._snapToGrid) {
  32581. targetLoc = this._snapLocationToGrid(targetLoc);
  32582. }
  32583. if (this._originObject instanceof TokenDocument) {
  32584. setTimeout(async () => {
  32585. let target = this._rotateTowards.target?.object ?? this._rotateTowards.target;
  32586. if (this._rotateTowards.towardsCenter) {
  32587. target = target?.center ?? target;
  32588. }
  32589. let ray = new Ray(targetLoc, target);
  32590. let angle = ray.angle * 180 / Math.PI - 90;
  32591. angle += offset2;
  32592. await this._updateObject(
  32593. this._originObject,
  32594. { rotation: angle },
  32595. this._rotateTowards.duration > 0,
  32596. {
  32597. duration: this._rotateTowards.duration,
  32598. easing: this._rotateTowards.ease
  32599. }
  32600. );
  32601. }, this._rotateTowards.delay ?? 0);
  32602. } else {
  32603. animData.attributes.push({
  32604. name: "rotationTowards",
  32605. offset: offset2,
  32606. origin: this._originObject,
  32607. originLocation: targetLoc,
  32608. target: this._rotateTowards.target?.object ?? this._rotateTowards.target,
  32609. towardsCenter: this._rotateTowards.towardsCenter,
  32610. from: false,
  32611. to: false,
  32612. progress: 0,
  32613. done: false,
  32614. duration: this._rotateTowards.duration,
  32615. durationDone: 0,
  32616. delay: this._rotateTowards.delay,
  32617. ease: this._rotateTowards.ease
  32618. });
  32619. }
  32620. let rotateDuration = this._rotateTowards.duration + this._rotateTowards.delay;
  32621. overallDuration = overallDuration > rotateDuration ? overallDuration : rotateDuration;
  32622. }
  32623. if (this._fadeIn) {
  32624. let to = is_real_number(this._opacity) ? this._opacity : 1;
  32625. if (this._originObject instanceof TokenDocument) {
  32626. setTimeout(async () => {
  32627. await this._updateObject(this._originObject, { alpha: to }, true, {
  32628. duration: this._fadeIn.duration,
  32629. easing: this._fadeIn.ease
  32630. });
  32631. }, this._fadeIn.delay ?? 0);
  32632. } else {
  32633. animData.attributes.push({
  32634. name: "alpha",
  32635. from: 0,
  32636. to,
  32637. progress: 0,
  32638. done: false,
  32639. duration: this._fadeIn.duration,
  32640. durationDone: 0,
  32641. delay: this._fadeIn.delay,
  32642. ease: this._fadeIn.ease
  32643. });
  32644. }
  32645. let fadeDuration = this._fadeIn.duration + this._fadeIn.delay;
  32646. overallDuration = overallDuration > fadeDuration ? overallDuration : fadeDuration;
  32647. }
  32648. if (this._fadeInAudio && this._originObject?.video?.volume !== void 0) {
  32649. let to = is_real_number(this._volume) ? this._volume : 1;
  32650. animData.attributes.push({
  32651. name: "video.volume",
  32652. from: 0,
  32653. to,
  32654. progress: 0,
  32655. done: false,
  32656. duration: this._fadeInAudio.duration,
  32657. durationDone: 0,
  32658. delay: this._fadeInAudio.delay,
  32659. ease: this._fadeInAudio.ease
  32660. });
  32661. let fadeDuration = this._fadeInAudio.duration + this._fadeInAudio.delay;
  32662. overallDuration = overallDuration > fadeDuration ? overallDuration : fadeDuration;
  32663. }
  32664. if (this._rotateIn) {
  32665. let from = this._angle ? this._angle : this._originObject.rotation;
  32666. let to = this._rotateIn.value;
  32667. if (Math.abs(from - to) > 180) {
  32668. if (to < 0) {
  32669. to += 360;
  32670. } else if (from > to) {
  32671. from -= 360;
  32672. }
  32673. }
  32674. if (this._originObject instanceof TokenDocument) {
  32675. setTimeout(async () => {
  32676. if (this._originObject.rotation !== from) {
  32677. await this._updateObject(
  32678. this._originObject,
  32679. { rotation: from },
  32680. false
  32681. );
  32682. }
  32683. await this._updateObject(this._originObject, { rotation: to }, true, {
  32684. duration: this._rotateIn.duration,
  32685. easing: this._rotateIn.ease
  32686. });
  32687. }, this._rotateIn.delay ?? 0);
  32688. } else {
  32689. animData.attributes.push({
  32690. name: "rotation",
  32691. from,
  32692. to,
  32693. progress: 0,
  32694. done: false,
  32695. duration: this._rotateIn.duration,
  32696. durationDone: 0,
  32697. delay: this._rotateIn.delay,
  32698. ease: this._rotateIn.ease
  32699. });
  32700. }
  32701. let rotateDuration = this._rotateIn.duration + this._rotateIn.delay;
  32702. overallDuration = overallDuration > rotateDuration ? overallDuration : rotateDuration;
  32703. }
  32704. if (this._moveTowards) {
  32705. let originLocation = get_object_position(this._originObject, {
  32706. exact: true
  32707. });
  32708. let targetLocation = this._closestSquare ? this._getClosestSquare(this._originObject, this._moveTowards.target) : get_object_position(this._moveTowards.target, {
  32709. exact: true
  32710. });
  32711. targetLocation.x += this._offset.x;
  32712. targetLocation.y += this._offset.y;
  32713. targetLocation.elevation = targetLocation?.elevation ?? this._originObject?.elevation;
  32714. if (this._moveTowards.relativeToCenter) {
  32715. const dimensions = get_object_dimensions(this._originObject);
  32716. targetLocation.x -= dimensions.width / 2;
  32717. targetLocation.y -= dimensions.height / 2;
  32718. if (this._snapToGrid) {
  32719. targetLocation.x -= 0.01;
  32720. targetLocation.y -= 0.01;
  32721. }
  32722. }
  32723. if (this._snapToGrid) {
  32724. targetLocation = this._snapLocationToGrid(targetLocation);
  32725. }
  32726. let originalDx = targetLocation.x - originLocation.x;
  32727. let originalDy = targetLocation.y - originLocation.y;
  32728. let originalDistance = Math.sqrt(
  32729. originalDx * originalDx + originalDy * originalDy
  32730. );
  32731. let duration = this._duration ? this._duration : originalDistance / this._moveSpeed * animData.maxFPS;
  32732. let moveDuration = duration + this._moveTowards.delay;
  32733. overallDuration = overallDuration > moveDuration ? overallDuration : moveDuration;
  32734. if (this._originObject instanceof TokenDocument) {
  32735. setTimeout(async () => {
  32736. await this._updateObject(this._originObject, targetLocation, true, {
  32737. movementSpeed: this._moveSpeed,
  32738. duration,
  32739. easing: this._moveTowards.ease
  32740. });
  32741. }, this._moveTowards.delay ?? 0);
  32742. } else {
  32743. if (!(duration === 0 || originalDistance === 0)) {
  32744. animData.attributes.push({
  32745. name: "position",
  32746. origin: originLocation,
  32747. target: targetLocation,
  32748. originalDistance,
  32749. currentDistance: 0,
  32750. progress: 0,
  32751. speed: 0,
  32752. duration,
  32753. done: false,
  32754. ease: this._moveTowards.ease,
  32755. delay: this._moveTowards.delay
  32756. });
  32757. }
  32758. }
  32759. }
  32760. if (this._fadeOut) {
  32761. let from = is_real_number(this._opacity) ? this._opacity : this._originObject.alpha ?? 1;
  32762. if (this._originObject instanceof TokenDocument) {
  32763. setTimeout(async () => {
  32764. await this._updateObject(this._originObject, { alpha: 0 }, true, {
  32765. duration: this._fadeOut.duration,
  32766. easing: this._fadeOut.ease
  32767. });
  32768. }, this._fadeOut.delay ?? 0);
  32769. } else {
  32770. animData.attributes.push({
  32771. name: "alpha",
  32772. from,
  32773. to: 0,
  32774. progress: 0,
  32775. done: false,
  32776. duration: this._fadeOut.duration,
  32777. durationDone: 0,
  32778. delay: overallDuration - this._fadeOut.duration,
  32779. ease: this._fadeOut.ease
  32780. });
  32781. }
  32782. let fadeOutDuration = this._fadeOut.duration + this._fadeOut.delay;
  32783. overallDuration = overallDuration > fadeOutDuration ? overallDuration : fadeOutDuration;
  32784. }
  32785. if (this._fadeOutAudio && this._originObject?.video?.volume !== void 0) {
  32786. let from = is_real_number(this._volume) ? this._volume : this._originObject.video.volume;
  32787. animData.attributes.push({
  32788. name: "video.volume",
  32789. from,
  32790. to: 0,
  32791. progress: 0,
  32792. done: false,
  32793. duration: this._fadeOutAudio.duration,
  32794. durationDone: 0,
  32795. delay: overallDuration - this._fadeOutAudio.duration,
  32796. ease: this._fadeOutAudio.ease
  32797. });
  32798. let fadeOutAudioDuration = this._fadeOutAudio.duration + this._fadeOutAudio.delay;
  32799. overallDuration = overallDuration > fadeOutAudioDuration ? overallDuration : fadeOutAudioDuration;
  32800. }
  32801. if (this._rotateOut) {
  32802. let from = this._rotateOut.value;
  32803. let to = this._angle ? this._angle : this._originObject.rotation;
  32804. if (this._rotateIn)
  32805. from += this._rotateIn.value;
  32806. if (Math.abs(from - to) > 180) {
  32807. if (to < 0) {
  32808. to += 360;
  32809. } else if (from > to) {
  32810. from -= 360;
  32811. }
  32812. }
  32813. if (this._originObject instanceof TokenDocument) {
  32814. setTimeout(async () => {
  32815. if (this._originObject.rotation !== from) {
  32816. await this._updateObject(
  32817. this._originObject,
  32818. { rotation: from },
  32819. false
  32820. );
  32821. }
  32822. await this._updateObject(this._originObject, { rotation: to }, true, {
  32823. duration: this._rotateOut.duration,
  32824. easing: this._rotateOut.ease
  32825. });
  32826. }, this._rotateOut.delay ?? 0);
  32827. } else {
  32828. animData.attributes.push({
  32829. name: "rotation",
  32830. from,
  32831. to,
  32832. progress: 0,
  32833. done: false,
  32834. duration: this._rotateOut.duration,
  32835. durationDone: 0,
  32836. delay: overallDuration - this._rotateOut.duration,
  32837. ease: this._rotateOut.ease
  32838. });
  32839. }
  32840. let rotateOutDuration = this._rotateOut.duration + this._rotateOut.delay;
  32841. overallDuration = overallDuration > rotateOutDuration ? overallDuration : rotateOutDuration;
  32842. }
  32843. if (this._teleportTo) {
  32844. setTimeout(async () => {
  32845. let targetLocation = this._closestSquare ? this._getClosestSquare(this._originObject, this._teleportTo.target) : get_object_position(this._teleportTo.target, {
  32846. exact: true
  32847. });
  32848. targetLocation.x += this._offset.x;
  32849. targetLocation.y += this._offset.y;
  32850. targetLocation.elevation = targetLocation?.elevation ?? this._originObject?.elevation;
  32851. const dimensions = get_object_dimensions(this._originObject);
  32852. if (this._teleportTo.relativeToCenter) {
  32853. targetLocation.x -= dimensions.width / 2;
  32854. targetLocation.y -= dimensions.height / 2;
  32855. if (this._snapToGrid) {
  32856. targetLocation.x -= 0.01;
  32857. targetLocation.y -= 0.01;
  32858. }
  32859. }
  32860. if (this._snapToGrid) {
  32861. targetLocation.x -= dimensions.width / 2;
  32862. targetLocation.y -= dimensions.height / 2;
  32863. targetLocation = this._snapLocationToGrid(targetLocation);
  32864. }
  32865. await this._updateObject(this._originObject, targetLocation, false);
  32866. }, this._teleportTo.delay);
  32867. if (overallDuration <= this._teleportTo.delay) {
  32868. this._waitUntilFinished = true;
  32869. }
  32870. overallDuration = overallDuration > this._teleportTo.delay ? overallDuration : this._teleportTo.delay;
  32871. }
  32872. let updateAttributes = {};
  32873. if (is_real_number(this._angle) && !this._rotateIn && !this._rotateOut) {
  32874. updateAttributes["rotation"] = this._angle;
  32875. }
  32876. if (is_real_number(this._opacity) && !this._fadeIn && !this._fadeOut) {
  32877. updateAttributes["alpha"] = this._opacity;
  32878. }
  32879. if (is_real_number(this._volume) && !this._fadeInAudio && !this._fadeOutAudio && this._originObject?.video?.volume !== void 0) {
  32880. updateAttributes["video.volume"] = this._volume;
  32881. }
  32882. if (this._tint) {
  32883. updateAttributes["tint"] = this._tint.hexadecimal;
  32884. }
  32885. if (this._hide !== void 0) {
  32886. updateAttributes["hidden"] = this._hide;
  32887. }
  32888. if (Object.keys(updateAttributes).length) {
  32889. await wait$1(1);
  32890. await this._updateObject(this._originObject, updateAttributes);
  32891. await wait$1(1);
  32892. }
  32893. return new Promise(async (resolve) => {
  32894. this._animate(animData, resolve);
  32895. setTimeout(
  32896. resolve,
  32897. Math.max(0, overallDuration + this._currentWaitTime + animData.maxFPS)
  32898. );
  32899. });
  32900. }
  32901. /**
  32902. * @private
  32903. */
  32904. async _animate(animData, resolve, timespan) {
  32905. if (timespan) {
  32906. let animatedAttributes = {};
  32907. let dt = timespan - animData.lastTimespan;
  32908. if (dt >= animData.maxFPS) {
  32909. animData.totalDt += dt;
  32910. for (let attribute of animData.attributes) {
  32911. if (attribute.done)
  32912. continue;
  32913. if (animData.totalDt < attribute.delay)
  32914. continue;
  32915. if (attribute.name === "position") {
  32916. attribute.speed = attribute.originalDistance / (attribute.duration / dt);
  32917. attribute.currentDistance += attribute.speed;
  32918. attribute.progress = attribute.currentDistance / attribute.originalDistance;
  32919. let x = interpolate(
  32920. attribute.origin.x,
  32921. attribute.target.x,
  32922. attribute.progress,
  32923. attribute.ease
  32924. );
  32925. let y = interpolate(
  32926. attribute.origin.y,
  32927. attribute.target.y,
  32928. attribute.progress,
  32929. attribute.ease
  32930. );
  32931. if (attribute.currentDistance >= attribute.originalDistance) {
  32932. x = attribute.target.x;
  32933. y = attribute.target.y;
  32934. attribute.done = true;
  32935. }
  32936. animatedAttributes["x"] = x;
  32937. animatedAttributes["y"] = y;
  32938. } else {
  32939. if (attribute.name === "rotationTowards" && !attribute.from && !attribute.to) {
  32940. let target = attribute.target;
  32941. if (this._rotateTowards.towardsCenter)
  32942. target = target?.center ?? target;
  32943. let ray = new Ray(attribute.originLocation, target);
  32944. let angle = ray.angle * 180 / Math.PI - 90;
  32945. angle += attribute.offset;
  32946. attribute.from = attribute.origin.rotation;
  32947. attribute.to = angle;
  32948. if (Math.abs(attribute.from - attribute.to) > 180) {
  32949. if (attribute.to < 0) {
  32950. attribute.to += 360;
  32951. } else if (attribute.from > attribute.to) {
  32952. attribute.from -= 360;
  32953. }
  32954. }
  32955. attribute.name = "rotation";
  32956. }
  32957. attribute.durationDone += dt;
  32958. attribute.progress = attribute.durationDone / attribute.duration;
  32959. let val = interpolate(
  32960. attribute.from,
  32961. attribute.to,
  32962. attribute.progress,
  32963. attribute.ease
  32964. );
  32965. if (attribute.progress >= 1) {
  32966. val = attribute.to;
  32967. attribute.done = true;
  32968. }
  32969. animatedAttributes[attribute.name] = val;
  32970. }
  32971. }
  32972. if (Object.keys(animatedAttributes).length > 0) {
  32973. await this._updateObject(this._originObject, animatedAttributes);
  32974. }
  32975. animData.attributes = animData.attributes.filter((a) => !a.done);
  32976. if (animData.attributes.length === 0)
  32977. return;
  32978. animData.lastTimespan = timespan;
  32979. }
  32980. }
  32981. let self = this;
  32982. requestAnimationFrame(function(timespan2) {
  32983. self._animate(animData, resolve, timespan2);
  32984. });
  32985. }
  32986. }
  32987. class ScrollingTextSection extends Section {
  32988. constructor(inSequence, target, text2, textOptions) {
  32989. super(inSequence);
  32990. this._deserializedData = null;
  32991. this._source = null;
  32992. this._text = "";
  32993. this._duration = 2e3;
  32994. this._distance = null;
  32995. this._jitter = 0;
  32996. this._anchor = null;
  32997. this._direction = null;
  32998. this._seed = get_hash(randomID());
  32999. if (target) {
  33000. this.atLocation(target);
  33001. }
  33002. if (text2) {
  33003. this.text(text2, textOptions);
  33004. }
  33005. }
  33006. static niceName = "Scrolling Text";
  33007. /**
  33008. * @private
  33009. */
  33010. _applyTraits() {
  33011. Object.assign(this.constructor.prototype, traits.users);
  33012. Object.assign(this.constructor.prototype, traits.location);
  33013. Object.assign(this.constructor.prototype, traits.offset);
  33014. Object.assign(this.constructor.prototype, traits.text);
  33015. }
  33016. direction(inDirection) {
  33017. if (!(typeof inDirection === "string" || is_real_number(inDirection))) {
  33018. throw this.sequence._customError(
  33019. this,
  33020. "direction",
  33021. "inDirection must be of type string (CONST.TEXT_ANCHOR_POINTS) or number"
  33022. );
  33023. }
  33024. if (typeof inDirection === "string" && !CONST.TEXT_ANCHOR_POINTS[inDirection]) {
  33025. throw this.sequence._customError(
  33026. this,
  33027. "direction",
  33028. `${inDirection} does not exist in CONST.TEXT_ANCHOR_POINTS!`
  33029. );
  33030. } else if (typeof inDirection === "string") {
  33031. this._direction = CONST.TEXT_ANCHOR_POINTS[inDirection];
  33032. } else {
  33033. this._direction = inDirection;
  33034. }
  33035. return this;
  33036. }
  33037. anchor(inAnchor) {
  33038. if (!(typeof inAnchor === "string" || is_real_number(inAnchor))) {
  33039. throw this.sequence._customError(
  33040. this,
  33041. "direction",
  33042. "inAnchor must be of type string (CONST.TEXT_ANCHOR_POINTS) or number"
  33043. );
  33044. }
  33045. if (typeof inAnchor === "string" && !CONST.TEXT_ANCHOR_POINTS[inAnchor] || is_real_number(inAnchor) && !Object.values(CONST.TEXT_ANCHOR_POINTS).includes(inAnchor)) {
  33046. throw this.sequence._customError(
  33047. this,
  33048. "direction",
  33049. `${inAnchor} does not exist in CONST.TEXT_ANCHOR_POINTS!`
  33050. );
  33051. } else if (typeof inAnchor === "string") {
  33052. this._anchor = CONST.TEXT_ANCHOR_POINTS[inAnchor];
  33053. } else {
  33054. this._anchor = inAnchor;
  33055. }
  33056. return this;
  33057. }
  33058. jitter(inJitter) {
  33059. if (!(is_real_number(inJitter) && inJitter >= 0 && inJitter <= 1)) {
  33060. throw this.sequence._customError(
  33061. this,
  33062. "jitter",
  33063. "inJitter must be of type number between 0 and 1"
  33064. );
  33065. }
  33066. this._jitter = inJitter;
  33067. return this;
  33068. }
  33069. async run() {
  33070. const data = await this._sanitizeTextData();
  33071. if (Hooks.call("preCreateScrollingText", data) === false)
  33072. return;
  33073. const push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
  33074. const duration = SequencerFoundryReplicator.playScrollingText(data, push);
  33075. await new Promise(
  33076. (resolve) => setTimeout(resolve, this._currentWaitTime + duration)
  33077. );
  33078. }
  33079. _getSourceObject() {
  33080. if (!this._source || typeof this._source !== "object")
  33081. return this._source;
  33082. return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
  33083. }
  33084. async _sanitizeTextData() {
  33085. if (this._deserializedData) {
  33086. return this._deserializedData;
  33087. }
  33088. return {
  33089. sceneId: game.user.viewedScene,
  33090. seed: this._seed,
  33091. sequenceId: this.sequence.id,
  33092. creatorUserId: game.userId,
  33093. users: this._users ? Array.from(this._users) : false,
  33094. moduleName: this.sequence.moduleName,
  33095. source: this._getSourceObject(),
  33096. offset: this._offset?.source ?? false,
  33097. randomOffset: this._randomOffset?.source ?? false,
  33098. content: this._text?.text ?? "",
  33099. options: {
  33100. anchor: this._anchor,
  33101. direction: this._direction,
  33102. duration: this._duration,
  33103. distance: this._distance,
  33104. jitter: this._jitter,
  33105. ...this._text
  33106. }
  33107. };
  33108. }
  33109. async _serialize() {
  33110. const data = await super._serialize();
  33111. const sectionData = await this._sanitizeTextData();
  33112. return {
  33113. ...data,
  33114. type: "scrollingText",
  33115. sectionData
  33116. };
  33117. }
  33118. async _deserialize(data) {
  33119. this._deserializedData = data.sectionData;
  33120. return super._deserialize(data);
  33121. }
  33122. }
  33123. class CanvasPanSection extends Section {
  33124. constructor(inSequence, target, duration, scale2) {
  33125. super(inSequence);
  33126. this._deserializedData = null;
  33127. this._source = null;
  33128. this._duration = duration ?? 250;
  33129. this._speed = null;
  33130. this._scale = scale2 ?? 1;
  33131. this._lockView = null;
  33132. this._seed = get_hash(randomID());
  33133. if (target) {
  33134. this.atLocation(target);
  33135. }
  33136. }
  33137. static niceName = "Canvas Pan";
  33138. /**
  33139. * @private
  33140. */
  33141. _applyTraits() {
  33142. Object.assign(this.constructor.prototype, traits.users);
  33143. Object.assign(this.constructor.prototype, traits.location);
  33144. Object.assign(this.constructor.prototype, traits.offset);
  33145. }
  33146. /**
  33147. * Sets the speed (pixels per frame) of the canvas pan
  33148. *
  33149. * @param {number} inSpeed
  33150. * @returns this
  33151. */
  33152. speed(inSpeed) {
  33153. if (!is_real_number(inSpeed))
  33154. throw this.sequence._customError(
  33155. this,
  33156. "speed",
  33157. "inSpeed must be of type number"
  33158. );
  33159. if (inSpeed < 0) {
  33160. throw this.sequence._customError(
  33161. this,
  33162. "speed",
  33163. "inSpeed must be greater or equal to 0"
  33164. );
  33165. }
  33166. this._speed = inSpeed;
  33167. return this;
  33168. }
  33169. /**
  33170. * Sets the zoom level of the canvas pan
  33171. *
  33172. * @param {number} inScale
  33173. * @returns this
  33174. */
  33175. scale(inScale) {
  33176. if (!is_real_number(inScale))
  33177. throw this.sequence._customError(
  33178. this,
  33179. "scale",
  33180. "inScale must be of type number"
  33181. );
  33182. if (inScale <= 0) {
  33183. throw this.sequence._customError(
  33184. this,
  33185. "scale",
  33186. "inScale must be greater than 0"
  33187. );
  33188. }
  33189. this._scale = inScale;
  33190. return this;
  33191. }
  33192. /**
  33193. * Locks the canvas at the given location for the given duration
  33194. *
  33195. * @param {number} inDuration
  33196. * @returns this
  33197. */
  33198. lockView(inDuration) {
  33199. if (!is_real_number(inDuration))
  33200. throw this.sequence._customError(
  33201. this,
  33202. "lockView",
  33203. "inDuration must be of type number"
  33204. );
  33205. if (inDuration < 0) {
  33206. throw this.sequence._customError(
  33207. this,
  33208. "lockView",
  33209. "inDuration must be greater or equal to 0"
  33210. );
  33211. }
  33212. this._lockView = inDuration;
  33213. return this;
  33214. }
  33215. async run() {
  33216. const data = await this._sanitizeData();
  33217. if (Hooks.call("preCanvasPan", data) === false)
  33218. return;
  33219. const push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
  33220. const duration = SequencerFoundryReplicator.panCanvas(data, push);
  33221. await new Promise(
  33222. (resolve) => setTimeout(resolve, this._currentWaitTime + duration)
  33223. );
  33224. }
  33225. _getSourceObject() {
  33226. if (!this._source || typeof this._source !== "object")
  33227. return this._source;
  33228. return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
  33229. }
  33230. async _sanitizeData() {
  33231. if (this._deserializedData) {
  33232. return this._deserializedData;
  33233. }
  33234. return {
  33235. sceneId: game.user.viewedScene,
  33236. seed: this._seed,
  33237. sequenceId: this.sequence.id,
  33238. creatorUserId: game.userId,
  33239. users: this._users ? Array.from(this._users) : false,
  33240. moduleName: this.sequence.moduleName,
  33241. source: this._getSourceObject(),
  33242. offset: this._offset?.source ?? false,
  33243. randomOffset: this._randomOffset?.source ?? false,
  33244. duration: this._duration,
  33245. speed: this._speed,
  33246. scale: this._scale,
  33247. lockView: this._lockView
  33248. };
  33249. }
  33250. async _serialize() {
  33251. const data = await super._serialize();
  33252. const sectionData = await this._sanitizeData();
  33253. return {
  33254. ...data,
  33255. type: "canvasPan",
  33256. sectionData
  33257. };
  33258. }
  33259. async _deserialize(data) {
  33260. this._deserializedData = data.sectionData;
  33261. return super._deserialize(data);
  33262. }
  33263. }
  33264. class WaitSection extends Section {
  33265. constructor(inSequence, msMin, msMax) {
  33266. super(inSequence);
  33267. this._waitUntilFinished = true;
  33268. this._waitDuration = random_int_between(msMin, Math.max(msMin, msMax));
  33269. }
  33270. static niceName = "Wait";
  33271. /**
  33272. * @returns {Promise<void>}
  33273. */
  33274. async run() {
  33275. debug("Running wait");
  33276. await new Promise(async (resolve) => {
  33277. setTimeout(resolve, this._waitDuration);
  33278. });
  33279. }
  33280. /**
  33281. * @returns {Promise}
  33282. * @private
  33283. */
  33284. async _execute() {
  33285. await this.run();
  33286. }
  33287. async _serialize() {
  33288. const data = await super._serialize();
  33289. return {
  33290. ...data,
  33291. type: "wait",
  33292. sectionData: {
  33293. waitDuration: this._waitDuration
  33294. }
  33295. };
  33296. }
  33297. async _deserialize(data) {
  33298. this._waitDuration = data.sectionData.waitDuration;
  33299. return super._deserialize(data);
  33300. }
  33301. }
  33302. let Sequence$1 = class Sequence3 {
  33303. constructor(options = {
  33304. moduleName: "Sequencer",
  33305. softFail: false
  33306. }, softFail = false) {
  33307. this.id = randomID();
  33308. this.moduleName = typeof options === "string" ? options : options?.moduleName ?? "Sequencer";
  33309. this.softFail = options?.softFail ?? softFail;
  33310. this.sections = [];
  33311. this.nameOffsetMap = false;
  33312. this.effectIndex = 0;
  33313. this.sectionToCreate = void 0;
  33314. this.localOnly = false;
  33315. this._status = writable$1(CONSTANTS.STATUS.READY);
  33316. return sequence_proxy_wrap(this);
  33317. }
  33318. /**
  33319. * Plays all of this sequence's sections
  33320. *
  33321. * @returns {Promise}
  33322. */
  33323. async play({ remote = false } = {}) {
  33324. if (remote) {
  33325. this.localOnly = true;
  33326. const data = await this.toJSON();
  33327. sequencerSocket.executeForOthers(
  33328. SOCKET_HANDLERS.RUN_SEQUENCE_LOCALLY,
  33329. data
  33330. );
  33331. return new Sequence3().fromJSON(data).play();
  33332. }
  33333. Hooks.callAll("createSequencerSequence", this);
  33334. debug("Initializing sections");
  33335. for (let section of this.sections) {
  33336. await section._initialize();
  33337. }
  33338. SequenceManager.RunningSequences.add(this.id, this);
  33339. this.effectIndex = 0;
  33340. debug("Playing sections");
  33341. this.status = CONSTANTS.STATUS.RUNNING;
  33342. const promises = [];
  33343. for (let section of this.sections) {
  33344. if (section instanceof EffectSection)
  33345. this.effectIndex++;
  33346. if (section.shouldWaitUntilFinished) {
  33347. promises.push(await section._execute());
  33348. } else {
  33349. promises.push(section._execute());
  33350. }
  33351. if (get_store_value(this.status) === CONSTANTS.STATUS.ABORTED) {
  33352. continue;
  33353. }
  33354. if (!section._isLastSection) {
  33355. await new Promise((resolve) => setTimeout(resolve, 1));
  33356. }
  33357. }
  33358. return Promise.allSettled(promises).then(() => {
  33359. Hooks.callAll("endedSequencerSequence");
  33360. debug("Finished playing sections");
  33361. this.status = CONSTANTS.STATUS.COMPLETE;
  33362. });
  33363. }
  33364. /**
  33365. * Creates a section that will run a function.
  33366. *
  33367. * @param {function} inFunc
  33368. * @returns {Sequence} this
  33369. */
  33370. thenDo(inFunc) {
  33371. const func2 = section_proxy_wrap(new FunctionSection(this, inFunc));
  33372. this.sections.push(func2);
  33373. return func2;
  33374. }
  33375. /**
  33376. * Creates a section that will run a macro based on a name or a direct reference to a macro.
  33377. *
  33378. * @param {string|Macro} inMacro
  33379. * @param {*} args
  33380. * @returns {Sequence} this
  33381. */
  33382. macro(inMacro, ...args) {
  33383. let macro;
  33384. let compendium = false;
  33385. if (typeof inMacro === "string") {
  33386. if (inMacro.startsWith("Compendium")) {
  33387. let packArray = inMacro.split(".");
  33388. let pack = game.packs.get(`${packArray[1]}.${packArray[2]}`);
  33389. if (!pack) {
  33390. if (this.softFail) {
  33391. return this;
  33392. }
  33393. throw custom_error(
  33394. this.moduleName,
  33395. `macro - Compendium '${packArray[1]}.${packArray[2]}' was not found`
  33396. );
  33397. }
  33398. macro = packArray;
  33399. compendium = pack;
  33400. } else {
  33401. macro = game.macros.getName(inMacro);
  33402. if (!macro) {
  33403. if (this.softFail) {
  33404. return this;
  33405. }
  33406. throw custom_error(
  33407. this.moduleName,
  33408. `macro - Macro '${inMacro}' was not found`
  33409. );
  33410. }
  33411. }
  33412. } else if (inMacro instanceof Macro) {
  33413. macro = inMacro;
  33414. } else {
  33415. throw custom_error(
  33416. this.moduleName,
  33417. `macro - inMacro must be of instance string or Macro`
  33418. );
  33419. }
  33420. if (args && args.length && !game.modules.get("advanced-macros")?.active) {
  33421. custom_warning(
  33422. this.moduleName,
  33423. `macro - Supplying macros with arguments require the advanced-macros module to be active`,
  33424. true
  33425. );
  33426. }
  33427. const func2 = section_proxy_wrap(
  33428. new FunctionSection(
  33429. this,
  33430. async () => {
  33431. if (compendium) {
  33432. const macroData = (await compendium.getDocuments()).find((i) => i.name === macro[3])?.toObject();
  33433. if (!macroData) {
  33434. if (this.softFail) {
  33435. return;
  33436. }
  33437. throw custom_error(
  33438. this.moduleName,
  33439. `macro - Macro '${macro[3]}' was not found in compendium '${macro[1]}.${macro[2]}'`
  33440. );
  33441. }
  33442. macro = new Macro(macroData);
  33443. macro.ownership.default = CONST.DOCUMENT_PERMISSION_LEVELS.OWNER;
  33444. }
  33445. const version = game.modules.get("advanced-macros")?.version;
  33446. const bugAdvancedMacros = game.modules.get("advanced-macros")?.active && isNewerVersion(
  33447. version.startsWith("v") ? version.slice(1) : version,
  33448. "1.18.2"
  33449. ) && !isNewerVersion(
  33450. version.startsWith("v") ? version.slice(1) : version,
  33451. "1.19.1"
  33452. );
  33453. if (bugAdvancedMacros) {
  33454. await macro.execute([...args]);
  33455. } else {
  33456. await macro.execute(...args);
  33457. }
  33458. },
  33459. true
  33460. )
  33461. );
  33462. this.sections.push(func2);
  33463. return this;
  33464. }
  33465. /**
  33466. * Creates an effect section. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Effect section.
  33467. *
  33468. * @param {string} [inFile] inFile
  33469. * @returns {Section}
  33470. */
  33471. effect(inFile = "") {
  33472. const effect = section_proxy_wrap(new EffectSection(this, inFile));
  33473. this.sections.push(effect);
  33474. return effect;
  33475. }
  33476. /**
  33477. * Creates a sound section. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Sound section.
  33478. *
  33479. * @param {string} [inFile] inFile
  33480. * @returns {Section}
  33481. */
  33482. sound(inFile = "") {
  33483. const sound = section_proxy_wrap(new SoundSection(this, inFile));
  33484. this.sections.push(sound);
  33485. return sound;
  33486. }
  33487. /**
  33488. * Creates an animation. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Animation section.
  33489. *
  33490. * @param {Token|Tile} [inTarget=false] inTarget
  33491. * @returns {AnimationSection}
  33492. */
  33493. animation(inTarget) {
  33494. const animation2 = section_proxy_wrap(
  33495. new AnimationSection(this, inTarget)
  33496. );
  33497. this.sections.push(animation2);
  33498. return animation2;
  33499. }
  33500. /**
  33501. * Creates a scrolling text. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Scrolling Text section.
  33502. *
  33503. * @param {Object|String|Boolean} [inTarget=false] inTarget
  33504. * @param {String|Boolean} [inText=false] inText
  33505. * @param {Object} [inTextOptions={}] inTextOptions
  33506. * @returns {AnimationSection}
  33507. */
  33508. scrollingText(inTarget = false, inText = false, inTextOptions = {}) {
  33509. const scrolling = section_proxy_wrap(
  33510. new ScrollingTextSection(this, inTarget, inText, inTextOptions)
  33511. );
  33512. this.sections.push(scrolling);
  33513. return scrolling;
  33514. }
  33515. /**
  33516. * Pans the canvas text. Until you call any other sections you'll be working on the Canvas Pan section.
  33517. *
  33518. * @param {Object|String|Boolean} [inTarget=false] inTarget
  33519. * @param {Boolean|Number} inDuration
  33520. * @param {Boolean|Number} inSpeed
  33521. * @returns {AnimationSection}
  33522. */
  33523. canvasPan(inTarget = false, inDuration = null, inSpeed = null) {
  33524. const panning = section_proxy_wrap(
  33525. new CanvasPanSection(this, inTarget)
  33526. );
  33527. this.sections.push(panning);
  33528. return panning;
  33529. }
  33530. /**
  33531. * Causes the sequence to wait after the last section for as many milliseconds as you pass to this method. If given
  33532. * a second number, a random wait time between the two given numbers will be generated.
  33533. *
  33534. * @param {number} [msMin=1] minMs
  33535. * @param {number} [msMax=1] maxMs
  33536. * @returns {Sequence} this
  33537. */
  33538. wait(msMin = 1, msMax = 1) {
  33539. if (msMin < 1)
  33540. throw custom_error(
  33541. this.moduleName,
  33542. `wait - Wait ms cannot be less than 1`
  33543. );
  33544. if (msMax < 1)
  33545. throw custom_error(
  33546. this.moduleName,
  33547. `wait - Max wait ms cannot be less than 1`
  33548. );
  33549. const section = section_proxy_wrap(new WaitSection(this, msMin, msMax));
  33550. this.sections.push(section);
  33551. return this;
  33552. }
  33553. /**
  33554. * Applies a preset to the sequence
  33555. *
  33556. * @param {string} presetName
  33557. * @param {*} args
  33558. * @returns {Sequence|FunctionSection|EffectSection|AnimationSection|SoundSection}
  33559. */
  33560. preset(presetName, ...args) {
  33561. if (typeof presetName !== "string") {
  33562. throw this._customError(this, "name", `inName must be of type string`);
  33563. }
  33564. const preset = SequencerPresets.get(presetName);
  33565. if (!preset) {
  33566. custom_warning(
  33567. "Sequencer",
  33568. `preset | Could not find preset with name "${presetName}"`
  33569. );
  33570. return this;
  33571. }
  33572. return preset(this, ...args);
  33573. }
  33574. /**
  33575. * Adds the sections from a given Sequence to this Sequence
  33576. *
  33577. * @param {Sequence|FunctionSection|EffectSection|AnimationSection|SoundSection} inSequence
  33578. * @returns {Sequence} this
  33579. */
  33580. addSequence(inSequence) {
  33581. if (inSequence instanceof Section)
  33582. inSequence = inSequence.sequence;
  33583. if (!(inSequence instanceof Sequence3)) {
  33584. throw custom_error(
  33585. this.moduleName,
  33586. `addSequence - could not find the sequence from the given parameter`
  33587. );
  33588. }
  33589. const newSections = inSequence.sections.map((section) => {
  33590. const newSection = Object.assign(
  33591. Object.create(Object.getPrototypeOf(section)),
  33592. section
  33593. );
  33594. newSection.sequence = this;
  33595. return newSection;
  33596. });
  33597. this.sections = this.sections.concat(newSections);
  33598. return this;
  33599. }
  33600. async toJSON() {
  33601. const data = {
  33602. options: { moduleName: this.moduleName, softFail: this.softFail },
  33603. sections: []
  33604. };
  33605. for (const section of this.sections) {
  33606. const sectionData = await section._serialize();
  33607. if (!sectionData.type) {
  33608. throw new Error(
  33609. `Sequencer | toJson | ${section.constructor.name} does not support serialization!`
  33610. );
  33611. }
  33612. data.sections.push(sectionData);
  33613. }
  33614. return data;
  33615. }
  33616. fromJSON(data) {
  33617. this.moduleName = data.options.moduleName;
  33618. this.softFail = data.options.softFail;
  33619. this.localOnly = true;
  33620. for (const section of data.sections) {
  33621. this[section.type]()._deserialize(section);
  33622. }
  33623. return this;
  33624. }
  33625. _createCustomSection(...args) {
  33626. const func2 = section_proxy_wrap(
  33627. new this.sectionToCreate(this, ...args)
  33628. );
  33629. this.sectionToCreate = void 0;
  33630. this.sections.push(func2);
  33631. return func2;
  33632. }
  33633. _showWarning(self, func2, warning, notify) {
  33634. custom_warning(
  33635. this.moduleName,
  33636. `${self.constructor.name.replace("Section", "")} | ${func2} - ${warning}`,
  33637. notify
  33638. );
  33639. }
  33640. _customError(self, func2, error) {
  33641. return custom_error(
  33642. this.moduleName,
  33643. `${self.constructor.name.replace("Section", "")} | ${func2} - ${error}`
  33644. );
  33645. }
  33646. set status(inStatus) {
  33647. this._status.update((currentStatus) => {
  33648. if (currentStatus === CONSTANTS.STATUS.READY || currentStatus === CONSTANTS.STATUS.RUNNING) {
  33649. return inStatus;
  33650. }
  33651. return currentStatus;
  33652. });
  33653. }
  33654. get status() {
  33655. return this._status;
  33656. }
  33657. _abort() {
  33658. this.status = CONSTANTS.STATUS.ABORTED;
  33659. for (const section of this.sections) {
  33660. section._abortSection();
  33661. }
  33662. }
  33663. };
  33664. const SequencerPreloader = {
  33665. _usersToRespond: /* @__PURE__ */ new Set(),
  33666. _clientsDone: [],
  33667. _resolve: () => {
  33668. },
  33669. /**
  33670. * Caches provided file(s) locally, vastly improving loading speed of those files.
  33671. *
  33672. * @param {Array|String} inSrcs
  33673. * @param {Boolean} showProgressBar
  33674. * @returns {Promise<void>}
  33675. */
  33676. preload(inSrcs, showProgressBar = false) {
  33677. if (Array.isArray(inSrcs)) {
  33678. inSrcs.forEach((str) => {
  33679. if (typeof str !== "string") {
  33680. throw custom_error(
  33681. "Sequencer",
  33682. "preload | each entry in inSrcs must be of type string"
  33683. );
  33684. }
  33685. });
  33686. } else if (typeof inSrcs !== "string") {
  33687. throw custom_error(
  33688. "Sequencer",
  33689. "preload | inSrcs must be of type string or array of strings"
  33690. );
  33691. }
  33692. if (typeof showProgressBar !== "boolean") {
  33693. throw custom_error(
  33694. "Sequencer",
  33695. "preload | showProgressBar must be of type of boolean"
  33696. );
  33697. }
  33698. const srcs = this._cleanSrcs(inSrcs);
  33699. if (srcs.length === 0)
  33700. return;
  33701. return this._preloadLocal(srcs, showProgressBar);
  33702. },
  33703. /**
  33704. * Causes each connected client (including the caller) to fetch and cache the provided file(s) locally, vastly
  33705. * improving loading speed of those files.
  33706. *
  33707. * @param {Array|String} inSrcs
  33708. * @param {Boolean} showProgressBar
  33709. * @returns {Promise<void>}
  33710. */
  33711. preloadForClients(inSrcs, showProgressBar = false) {
  33712. if (Array.isArray(inSrcs)) {
  33713. inSrcs.forEach((str) => {
  33714. if (typeof str !== "string") {
  33715. throw custom_error(
  33716. "Sequencer",
  33717. "preloadForClients | each entry in inSrcs must be of type string"
  33718. );
  33719. }
  33720. });
  33721. } else if (typeof inSrcs !== "string") {
  33722. throw custom_error(
  33723. "Sequencer",
  33724. "preloadForClients | inSrcs must be of type string or array of strings"
  33725. );
  33726. }
  33727. if (typeof showProgressBar !== "boolean") {
  33728. throw custom_error(
  33729. "Sequencer",
  33730. "preloadForClients | showProgressBar must be of type of boolean"
  33731. );
  33732. }
  33733. const srcs = this._cleanSrcs(inSrcs);
  33734. if (srcs.length === 0)
  33735. return;
  33736. if (!user_can_do("permissions-preload")) {
  33737. custom_warning(
  33738. "Sequencer",
  33739. "preloadForClients - You do not have permission to force other clients to preload. Preloading locally instead."
  33740. );
  33741. return this._preloadLocal(srcs, showProgressBar);
  33742. }
  33743. const promise2 = new Promise((resolve) => {
  33744. this._resolve = resolve;
  33745. });
  33746. this._usersToRespond = new Set(
  33747. game.users.filter((user) => user.active).map((user) => user.id)
  33748. );
  33749. sequencerSocket.executeForEveryone(
  33750. SOCKET_HANDLERS.PRELOAD,
  33751. game.user.id,
  33752. srcs,
  33753. showProgressBar
  33754. );
  33755. return promise2;
  33756. },
  33757. async respond(inSenderId, inSrcs, showProgressBar) {
  33758. const numFilesFailedToLoad = await this._preloadLocal(
  33759. inSrcs,
  33760. showProgressBar
  33761. );
  33762. return sequencerSocket.executeAsUser(
  33763. SOCKET_HANDLERS.PRELOAD_RESPONSE,
  33764. inSenderId,
  33765. game.user.id,
  33766. numFilesFailedToLoad
  33767. );
  33768. },
  33769. handleResponse(inUserId, numFilesFailedToLoad) {
  33770. this._usersToRespond.delete(inUserId);
  33771. this._clientsDone.push({
  33772. userId: inUserId,
  33773. numFilesFailedToLoad
  33774. });
  33775. if (this._usersToRespond.size > 0)
  33776. return;
  33777. this._clientsDone.forEach((user) => {
  33778. if (user.numFilesFailedToLoad > 0) {
  33779. debug(
  33780. `${game.users.get(user.userId).name} preloaded files, failed to preload ${user.numFilesFailedToLoad} files`
  33781. );
  33782. } else {
  33783. debug(
  33784. `${game.users.get(user.userId).name} preloaded files successfully`
  33785. );
  33786. }
  33787. });
  33788. debug(`All clients responded to file preloads`);
  33789. this._resolve();
  33790. this._usersToRespond = /* @__PURE__ */ new Set();
  33791. this._clientsDone = [];
  33792. this._resolve = () => {
  33793. };
  33794. },
  33795. /**
  33796. * Filters and cleans up file paths given to the preload methods
  33797. *
  33798. * @private
  33799. */
  33800. _cleanSrcs(inSrcs) {
  33801. if (!Array.isArray(inSrcs)) {
  33802. inSrcs = [inSrcs];
  33803. }
  33804. if (inSrcs.length === 0) {
  33805. custom_warning("Sequencer", "You need to provide files to preload");
  33806. return [];
  33807. }
  33808. inSrcs = make_array_unique(
  33809. inSrcs.filter(Boolean).map((src) => {
  33810. if (Sequencer.Database.entryExists(src)) {
  33811. return Sequencer.Database.getAllFileEntries(src);
  33812. }
  33813. return src;
  33814. })
  33815. ).deepFlatten();
  33816. if (inSrcs.length >= 750) {
  33817. custom_warning(
  33818. "Sequencer",
  33819. "You are preloading over 750 files, you are most likely preloading more files than the system can cache.",
  33820. true
  33821. );
  33822. }
  33823. return inSrcs;
  33824. },
  33825. /**
  33826. * The method that actually preloads files locally, with an optional progress bar
  33827. *
  33828. * @private
  33829. */
  33830. _preloadLocal(inSrcs, showProgressBar) {
  33831. let startTime = performance.now();
  33832. let numFilesToLoad = inSrcs.length;
  33833. debug(`Preloading ${numFilesToLoad} files...`);
  33834. if (showProgressBar)
  33835. LoadingBar.init(
  33836. `Sequencer - Preloading ${numFilesToLoad} files`,
  33837. numFilesToLoad
  33838. );
  33839. return new Promise(async (resolve) => {
  33840. let numFilesFailedToLoad = 0;
  33841. for (let src of inSrcs) {
  33842. const blob = await SequencerFileCache.loadFile(src, true);
  33843. if (showProgressBar)
  33844. LoadingBar.incrementProgress();
  33845. if (!blob) {
  33846. numFilesFailedToLoad++;
  33847. }
  33848. }
  33849. const timeTaken = (performance.now() - startTime) / 1e3;
  33850. let failedToLoad = ` (${numFilesFailedToLoad} failed to load)`;
  33851. debug(
  33852. `Preloading ${numFilesToLoad} files took ${timeTaken}s` + failedToLoad
  33853. );
  33854. resolve(numFilesFailedToLoad);
  33855. });
  33856. }
  33857. };
  33858. class SequencerSectionManager {
  33859. constructor() {
  33860. this.externalSections = {};
  33861. }
  33862. /**
  33863. * Registers a class by a name that will then be available through the Sequencer
  33864. *
  33865. * @param {String} inModuleName
  33866. * @param {String} inMethodName
  33867. * @param {Class} inClass
  33868. * @param {Boolean} overwrite
  33869. * @returns {Boolean} Whether the registration succeeded
  33870. */
  33871. registerSection(inModuleName, inMethodName, inClass, overwrite = false) {
  33872. if (!(inClass.prototype instanceof Section)) {
  33873. throw custom_error(
  33874. inModuleName,
  33875. `inClass must be instance of Sequencer.BaseSection`
  33876. );
  33877. }
  33878. let coreMethods = Object.getOwnPropertyNames(Sequence.prototype).filter(
  33879. (method) => {
  33880. return !method.startsWith("_") && method !== "constructor";
  33881. }
  33882. );
  33883. if (coreMethods.includes(inMethodName)) {
  33884. throw custom_error(
  33885. inModuleName,
  33886. `${inMethodName} is an existing protected method of the Sequence class - please register with another method name!`
  33887. );
  33888. }
  33889. if (this.externalSections[inMethodName] && !overwrite) {
  33890. throw custom_error(
  33891. inModuleName,
  33892. `${inMethodName} is already a registered Section with the class ${this.externalSections[inMethodName].constructor.name}`
  33893. );
  33894. }
  33895. coreMethods = coreMethods.concat(Object.keys(this.externalSections));
  33896. const clashingMethods = Object.getOwnPropertyNames(
  33897. inClass.prototype
  33898. ).filter((method) => coreMethods.includes(method));
  33899. if (clashingMethods.length) {
  33900. let errors = clashingMethods.join(", ");
  33901. throw custom_error(
  33902. inModuleName,
  33903. `${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.`
  33904. );
  33905. }
  33906. debug(
  33907. `SectionManager | Successfully registered ${inMethodName} with Sequencer!`
  33908. );
  33909. this.externalSections[inMethodName] = inClass;
  33910. return true;
  33911. }
  33912. }
  33913. let libWrapper = void 0;
  33914. const TGT_SPLIT_RE = new RegExp(
  33915. `([^.[]+|\\[('([^'\\\\]|\\\\.)+?'|"([^"\\\\]|\\\\.)+?")\\])`,
  33916. "g"
  33917. );
  33918. const TGT_CLEANUP_RE = new RegExp(`(^\\['|'\\]$|^\\["|"\\]$)`, "g");
  33919. Hooks.once("init", () => {
  33920. if (globalThis.libWrapper && !(globalThis.libWrapper.is_fallback ?? true)) {
  33921. libWrapper = globalThis.libWrapper;
  33922. return;
  33923. }
  33924. libWrapper = class {
  33925. static get is_fallback() {
  33926. return true;
  33927. }
  33928. static get WRAPPER() {
  33929. return "WRAPPER";
  33930. }
  33931. static get MIXED() {
  33932. return "MIXED";
  33933. }
  33934. static get OVERRIDE() {
  33935. return "OVERRIDE";
  33936. }
  33937. static register(package_id, target, fn, type = "MIXED", { chain = void 0, bind: bind2 = [] } = {}) {
  33938. const is_setter = target.endsWith("#set");
  33939. target = !is_setter ? target : target.slice(0, -4);
  33940. const split = target.match(TGT_SPLIT_RE).map((x) => x.replace(/\\(.)/g, "$1").replace(TGT_CLEANUP_RE, ""));
  33941. const root_nm = split.splice(0, 1)[0];
  33942. let obj, fn_name;
  33943. if (split.length == 0) {
  33944. obj = globalThis;
  33945. fn_name = root_nm;
  33946. } else {
  33947. const _eval = eval;
  33948. fn_name = split.pop();
  33949. obj = split.reduce(
  33950. (x, y) => x[y],
  33951. globalThis[root_nm] ?? _eval(root_nm)
  33952. );
  33953. }
  33954. let iObj = obj;
  33955. let descriptor = null;
  33956. while (iObj) {
  33957. descriptor = Object.getOwnPropertyDescriptor(iObj, fn_name);
  33958. if (descriptor)
  33959. break;
  33960. iObj = Object.getPrototypeOf(iObj);
  33961. }
  33962. if (!descriptor || descriptor?.configurable === false)
  33963. throw new Error(
  33964. `libWrapper Shim: '${target}' does not exist, could not be found, or has a non-configurable descriptor.`
  33965. );
  33966. let original = null;
  33967. const wrapper = chain ?? (type.toUpperCase?.() != "OVERRIDE" && type != 3) ? function(...args) {
  33968. return fn.call(this, original.bind(this), ...bind2, ...args);
  33969. } : function(...args) {
  33970. return fn.call(this, ...bind2, ...args);
  33971. };
  33972. if (!is_setter) {
  33973. if (descriptor.value) {
  33974. original = descriptor.value;
  33975. descriptor.value = wrapper;
  33976. } else {
  33977. original = descriptor.get;
  33978. descriptor.get = wrapper;
  33979. }
  33980. } else {
  33981. if (!descriptor.set)
  33982. throw new Error(
  33983. `libWrapper Shim: '${target}' does not have a setter`
  33984. );
  33985. original = descriptor.set;
  33986. descriptor.set = wrapper;
  33987. }
  33988. descriptor.configurable = true;
  33989. Object.defineProperty(obj, fn_name, descriptor);
  33990. }
  33991. };
  33992. });
  33993. function registerLibwrappers() {
  33994. const override = isNewerVersion(game.version, "11") ? "PIXI.BaseImageResource.prototype.upload" : "PIXI.resources.BaseImageResource.prototype.upload";
  33995. libWrapper.register(CONSTANTS.MODULE_NAME, override, PIXIUPLOAD);
  33996. }
  33997. function PIXIUPLOAD(wrapped, ...args) {
  33998. let baseTexture = args[1];
  33999. if (baseTexture.sequencer_patched || !game.settings.get(CONSTANTS.MODULE_NAME, "enable-global-pixi-fix")) {
  34000. return wrapped(...args);
  34001. }
  34002. let source = args[3];
  34003. source = source || this.source;
  34004. const isVideo = !!source.videoWidth;
  34005. if (isVideo) {
  34006. baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
  34007. baseTexture.sequencer_patched = true;
  34008. }
  34009. return wrapped(...args);
  34010. }
  34011. async function runMigrations() {
  34012. const sortedMigrations = Object.entries(migrations).sort((a, b) => {
  34013. return isNewerVersion(b[0], a[0]) ? -1 : 1;
  34014. });
  34015. for (const [version, migration] of sortedMigrations) {
  34016. try {
  34017. await migration(version);
  34018. } catch (err) {
  34019. custom_warning(
  34020. "Sequencer",
  34021. `Something went wrong when migrating to version ${version}. Please check the console for the error!`,
  34022. true
  34023. );
  34024. console.error(err);
  34025. }
  34026. }
  34027. }
  34028. function getSequencerEffectTokens(version, tokenFilter = false) {
  34029. return Array.from(game.scenes).map((scene) => [
  34030. scene,
  34031. Array.from(scene.tokens).filter((token, index) => {
  34032. if (tokenFilter) {
  34033. return tokenFilter(token, index);
  34034. }
  34035. const effects = getProperty(token, CONSTANTS.EFFECTS_FLAG) ?? [];
  34036. const effectsOutOfDate = effects.filter(
  34037. (e) => isNewerVersion(version, e[1].flagVersion)
  34038. );
  34039. return effectsOutOfDate.length;
  34040. })
  34041. ]).filter(([_, tokens]) => tokens.length);
  34042. }
  34043. function getSequencerEffectActors(version, actorFilter = false) {
  34044. return Array.from(game.actors).filter((actor, index) => {
  34045. if (actorFilter) {
  34046. return actorFilter(actor, index);
  34047. }
  34048. const effects = getProperty(actor, CONSTANTS.EFFECTS_FLAG) ?? [];
  34049. const effectsOutOfDate = effects.filter(
  34050. (e) => isNewerVersion(version, e[1].flagVersion)
  34051. );
  34052. return effectsOutOfDate.length;
  34053. });
  34054. }
  34055. const migrations = {
  34056. "3.0.0": async (version) => {
  34057. const actorsToUpdate = getSequencerEffectActors(version, (actor) => {
  34058. const effects = getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
  34059. const effectsOutOfDate = effects.filter(
  34060. (e) => isNewerVersion(version, e[1].flagVersion)
  34061. );
  34062. return effectsOutOfDate.length;
  34063. });
  34064. const actorUpdateArray = actorsToUpdate.map((actor) => {
  34065. const effectsToMoveFromTokenPrototype = getProperty(
  34066. actor.prototypeToken,
  34067. CONSTANTS.EFFECTS_FLAG
  34068. ).filter(([_, effect]) => {
  34069. return effect.persistOptions?.persistTokenPrototype;
  34070. }).map(([id, effect]) => {
  34071. effect.flagVersion = version;
  34072. return [id, effect];
  34073. });
  34074. const effectsToKeepOnTokenPrototype = getProperty(
  34075. actor.prototypeToken,
  34076. CONSTANTS.EFFECTS_FLAG
  34077. ).filter(([_, effect]) => {
  34078. return !effect.persistOptions?.persistTokenPrototype;
  34079. }).map(([id, effect]) => {
  34080. effect.flagVersion = version;
  34081. return [id, effect];
  34082. });
  34083. return {
  34084. _id: actor.id,
  34085. [CONSTANTS.EFFECTS_FLAG]: effectsToMoveFromTokenPrototype,
  34086. ["prototypeToken." + CONSTANTS.EFFECTS_FLAG]: effectsToKeepOnTokenPrototype
  34087. };
  34088. });
  34089. const tokensOnScenes = getSequencerEffectTokens(version, (t) => {
  34090. let actor;
  34091. try {
  34092. actor = t.actor;
  34093. } catch (err) {
  34094. return false;
  34095. }
  34096. const effects = getProperty(t, CONSTANTS.EFFECTS_FLAG) ?? [];
  34097. const prototypeTokenEffects = getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
  34098. const effectsOutOfDate = effects.filter(
  34099. (e) => isNewerVersion(version, e[1].flagVersion)
  34100. );
  34101. const prototypeEffectsOutOfDate = prototypeTokenEffects.filter(
  34102. (e) => isNewerVersion(version, e[1].flagVersion)
  34103. );
  34104. return t.actorLink && (effectsOutOfDate.length || prototypeEffectsOutOfDate.length);
  34105. });
  34106. for (const [scene, tokens] of tokensOnScenes) {
  34107. const updates = [];
  34108. for (const token of tokens) {
  34109. const effectsToKeepOnToken = getProperty(token, CONSTANTS.EFFECTS_FLAG).filter(([_, effect]) => {
  34110. return !effect.persistOptions?.persistTokenPrototype;
  34111. }).map(([id, effect]) => {
  34112. effect.flagVersion = version;
  34113. return [id, effect];
  34114. });
  34115. updates.push({
  34116. _id: token.id,
  34117. [CONSTANTS.EFFECTS_FLAG]: effectsToKeepOnToken
  34118. });
  34119. }
  34120. if (updates.length) {
  34121. console.log(
  34122. `Sequencer | Updated ${updates.length} tokens' effects on scene ${scene.id} to version ${version}`
  34123. );
  34124. await scene.updateEmbeddedDocuments("Token", updates);
  34125. }
  34126. }
  34127. if (actorUpdateArray.length) {
  34128. console.log(
  34129. `Sequencer | Updated ${actorUpdateArray.length} actors' effects to version ${version}`
  34130. );
  34131. await Actor.updateDocuments(actorUpdateArray);
  34132. }
  34133. }
  34134. };
  34135. Hooks.once("init", async function() {
  34136. if (!game.modules.get("socketlib")?.active)
  34137. return;
  34138. CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE = false;
  34139. initialize_module();
  34140. });
  34141. Hooks.once("socketlib.ready", () => {
  34142. registerSocket();
  34143. });
  34144. Hooks.once("ready", async function() {
  34145. if (!game.modules.get("socketlib")?.active) {
  34146. ui.notifications.error(
  34147. "Sequencer requires the SocketLib module to be active and will not work without it!",
  34148. { console: true }
  34149. );
  34150. return;
  34151. }
  34152. for (const [name, func2] of Object.entries(easeFunctions)) {
  34153. if (!CanvasAnimation[name]) {
  34154. CanvasAnimation[name] = func2;
  34155. }
  34156. }
  34157. await runMigrations();
  34158. PlayerSettings.migrateOldPresets();
  34159. SequencerFoundryReplicator.registerHooks();
  34160. InteractionManager.initialize();
  34161. setTimeout(() => {
  34162. console.log("Sequencer | Ready to go!");
  34163. Hooks.callAll("sequencer.ready");
  34164. Hooks.callAll("sequencerReady");
  34165. migrateSettings();
  34166. SequencerEffectManager.setUpPersists();
  34167. Hooks.on("canvasReady", () => {
  34168. SequencerEffectManager.setUpPersists();
  34169. });
  34170. }, 50);
  34171. });
  34172. function initialize_module() {
  34173. window.Sequence = Sequence$1;
  34174. window.Sequencer = {
  34175. Player: EffectPlayer,
  34176. Presets: SequencerPresets,
  34177. Database: SequencerDatabase,
  34178. DatabaseViewer: DatabaseViewerApp,
  34179. Preloader: SequencerPreloader,
  34180. EffectManager: SequencerEffectManager,
  34181. SectionManager: new SequencerSectionManager(),
  34182. registerEase,
  34183. BaseSection: Section,
  34184. CONSTANTS: {
  34185. EASE
  34186. },
  34187. Helpers: {
  34188. wait: wait$1,
  34189. clamp,
  34190. interpolate,
  34191. random_float_between,
  34192. random_int_between,
  34193. shuffle_array,
  34194. random_array_element,
  34195. random_object_element,
  34196. make_array_unique
  34197. }
  34198. };
  34199. registerSettings();
  34200. registerLayers();
  34201. registerHotkeys();
  34202. registerLibwrappers();
  34203. SequencerAboveUILayer.setup();
  34204. Hooks.on(
  34205. "preCreateToken",
  34206. (...args) => Sequencer.EffectManager.patchCreationData(...args)
  34207. );
  34208. Hooks.on(
  34209. "preCreateDrawing",
  34210. (...args) => Sequencer.EffectManager.patchCreationData(...args)
  34211. );
  34212. Hooks.on(
  34213. "preCreateTile",
  34214. (...args) => Sequencer.EffectManager.patchCreationData(...args)
  34215. );
  34216. Hooks.on(
  34217. "preCreateMeasuredTemplate",
  34218. (...args) => Sequencer.EffectManager.patchCreationData(...args)
  34219. );
  34220. Hooks.on(
  34221. "createToken",
  34222. (...args) => Sequencer.EffectManager.documentCreated(...args)
  34223. );
  34224. Hooks.on(
  34225. "createDrawing",
  34226. (...args) => Sequencer.EffectManager.documentCreated(...args)
  34227. );
  34228. Hooks.on(
  34229. "createTile",
  34230. (...args) => Sequencer.EffectManager.documentCreated(...args)
  34231. );
  34232. Hooks.on(
  34233. "createMeasuredTemplate",
  34234. (...args) => Sequencer.EffectManager.documentCreated(...args)
  34235. );
  34236. }
  34237. Hooks.once("monaco-editor.ready", registerTypes);
  34238. //# sourceMappingURL=module.js.map