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.

34481 lines
1023 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
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 flip_negate(num_1, num_2) {
  266. if (num_1 > 0) {
  267. num_1 -= num_2;
  268. } else {
  269. num_1 += num_2;
  270. }
  271. return num_1;
  272. }
  273. function shuffle_array(inArray, twister = false) {
  274. let shuffled = [...inArray];
  275. const randomMethod = twister?.random ?? Math.random;
  276. for (let i = shuffled.length - 1; i > 0; i--) {
  277. let j = Math.floor(randomMethod() * (i + 1));
  278. let temp = shuffled[i];
  279. shuffled[i] = shuffled[j];
  280. shuffled[j] = temp;
  281. }
  282. return shuffled;
  283. }
  284. function is_function$1(inFunc) {
  285. return inFunc && ({}.toString.call(inFunc) === "[object Function]" || {}.toString.call(inFunc) === "[object AsyncFunction]");
  286. }
  287. function random_array_element(inArray, { recurse = false, twister = false, index = false } = {}) {
  288. const chosenIndex = random_int_between(0, inArray.length, twister);
  289. let choice = inArray[chosenIndex];
  290. if (recurse && Array.isArray(choice)) {
  291. return random_array_element(choice, { recurse, twister, index });
  292. }
  293. if (index) {
  294. return chosenIndex;
  295. }
  296. return choice;
  297. }
  298. function random_object_element(inObject, { recurse = false, twister = false } = {}) {
  299. let keys = Object.keys(inObject).filter((k) => !k.startsWith("_"));
  300. let choice = inObject[random_array_element(keys, { twister })];
  301. if (typeof choice === "object" && recurse) {
  302. return random_object_element(choice, { recurse: true });
  303. }
  304. return choice;
  305. }
  306. function is_real_number(inNumber) {
  307. return !isNaN(inNumber) && typeof inNumber === "number" && isFinite(inNumber);
  308. }
  309. function deep_get(obj, path) {
  310. if (!Array.isArray(path))
  311. path = path.split(".");
  312. try {
  313. let i;
  314. for (i = 0; i < path.length - 1; i++) {
  315. obj = obj[path[i]];
  316. }
  317. return obj[path[i]];
  318. } catch (err) {
  319. }
  320. }
  321. function deep_set(obj, path, value) {
  322. if (!Array.isArray(path))
  323. path = path.split(".");
  324. try {
  325. let i;
  326. for (i = 0; i < path.length - 1; i++) {
  327. obj = obj[path[i]];
  328. }
  329. if (is_function$1(obj[path[i]])) {
  330. obj[path[i]](value);
  331. } else {
  332. obj[path[i]] = value;
  333. }
  334. } catch (err) {
  335. }
  336. }
  337. function flatten_object(obj) {
  338. let toReturn = [];
  339. for (let i in obj) {
  340. if (i.startsWith("_"))
  341. continue;
  342. if (!obj.hasOwnProperty(i))
  343. continue;
  344. if (typeof obj[i] == "object") {
  345. let flatObject = flatten_object(obj[i]);
  346. for (let x in flatObject) {
  347. if (!flatObject.hasOwnProperty(x))
  348. continue;
  349. toReturn[i + "." + x] = flatObject[x];
  350. }
  351. } else {
  352. toReturn[i] = obj[i];
  353. }
  354. }
  355. return toReturn;
  356. }
  357. function wait$1(ms) {
  358. return new Promise((resolve) => setTimeout(resolve, ms));
  359. }
  360. function clamp(num, min, max) {
  361. const _max = Math.max(min, max);
  362. const _min = Math.min(min, max);
  363. return Math.max(_min, Math.min(_max, num));
  364. }
  365. function is_UUID(inId) {
  366. return typeof inId === "string" && (inId.startsWith("Scene") || inId.startsWith("Actor") || inId.startsWith("Item")) && (inId.match(/\./g) || []).length && !inId.endsWith(".");
  367. }
  368. function get_object_from_scene(inObjectId, inSceneId = game.user.viewedScene) {
  369. let tryUUID = is_UUID(inObjectId);
  370. if (tryUUID) {
  371. const obj = fromUuidSync(inObjectId);
  372. if (obj)
  373. return obj;
  374. tryUUID = false;
  375. }
  376. return get_all_documents_from_scene(inSceneId).find((obj) => {
  377. return get_object_identifier(obj, tryUUID) === inObjectId;
  378. });
  379. }
  380. function get_all_documents_from_scene(inSceneId = false) {
  381. const scene = inSceneId ? game.scenes.get(inSceneId) : game.scenes.get(game.user?.viewedScene);
  382. if (!scene)
  383. return [];
  384. return [
  385. ...canvas.templates?.preview?.children ?? [],
  386. ...Array.from(scene?.tokens ?? []),
  387. ...Array.from(scene?.lights ?? []),
  388. ...Array.from(scene?.sounds ?? []),
  389. ...Array.from(scene?.templates ?? []),
  390. ...Array.from(scene?.tiles ?? []),
  391. ...Array.from(scene?.walls ?? []),
  392. ...Array.from(scene?.drawings ?? [])
  393. ].deepFlatten().filter(Boolean);
  394. }
  395. function validate_document(inObject) {
  396. const document2 = inObject?.document ?? inObject;
  397. return is_UUID(document2?.uuid) ? document2 : inObject;
  398. }
  399. function get_object_identifier(inObject, tryUUID = true) {
  400. const uuid = tryUUID && is_UUID(inObject?.uuid) ? inObject?.uuid : void 0;
  401. return uuid ?? inObject?.id ?? inObject?.document?.name ?? inObject?.name ?? (inObject?.tag !== "" ? inObject?.tag : void 0) ?? (inObject?.label !== "" ? inObject?.label : void 0);
  402. }
  403. function make_array_unique(inArray) {
  404. return Array.from(new Set(inArray));
  405. }
  406. function debug(msg, args = "") {
  407. if (game.settings.get(CONSTANTS.MODULE_NAME, "debug"))
  408. console.log(`DEBUG | Sequencer | ${msg}`, args);
  409. }
  410. function debug_error(msg, args) {
  411. if (game.settings.get(CONSTANTS.MODULE_NAME, "debug"))
  412. console.error(`DEBUG | Sequencer | ${msg}`, args);
  413. }
  414. function custom_warning(inClassName, warning, notify = false) {
  415. inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
  416. warning = `${inClassName} | ${warning}`;
  417. if (notify)
  418. ui.notifications.warn(warning, { console: false });
  419. console.warn(warning.replace("<br>", "\n"));
  420. }
  421. const throttledWarnings = {};
  422. function throttled_custom_warning(inClassName, warning, delay = 1e4, notify = false) {
  423. inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
  424. warning = `${inClassName} | ${warning}`;
  425. if (throttledWarnings[warning])
  426. return;
  427. throttledWarnings[warning] = true;
  428. if (notify)
  429. ui.notifications.warn(warning, { console: false });
  430. console.warn(warning.replace("<br>", "\n"));
  431. setTimeout(() => {
  432. delete throttledWarnings[warning];
  433. }, delay);
  434. }
  435. function custom_error(inClassName, error, notify = true) {
  436. inClassName = inClassName !== "Sequencer" ? "Sequencer | Module: " + inClassName : inClassName;
  437. error = `${inClassName} | ${error}`;
  438. if (notify)
  439. ui.notifications.error(error, { console: false });
  440. return new Error(error.replace("<br>", "\n"));
  441. }
  442. function user_can_do(inSetting) {
  443. return game.user.role > game.settings.get(CONSTANTS.MODULE_NAME, inSetting);
  444. }
  445. function group_by(xs, key) {
  446. return xs.reduce(function(acc, obj) {
  447. let property = getProperty(obj, key);
  448. acc[property] = acc[property] || [];
  449. acc[property].push(obj);
  450. return acc;
  451. }, {});
  452. }
  453. function objHasProperty(obj, prop) {
  454. return obj.constructor.prototype.hasOwnProperty(prop);
  455. }
  456. function sequence_proxy_wrap(inSequence) {
  457. return new Proxy(inSequence, {
  458. get: function(target, prop) {
  459. if (!objHasProperty(target, prop)) {
  460. if (Sequencer.SectionManager.externalSections[prop] === void 0) {
  461. const section = target.sections[target.sections.length - 1];
  462. if (section && objHasProperty(section, prop)) {
  463. const targetProperty = Reflect.get(section, prop);
  464. return is_function$1(targetProperty) ? targetProperty.bind(section) : targetProperty;
  465. }
  466. return Reflect.get(target, prop);
  467. }
  468. target.sectionToCreate = Sequencer.SectionManager.externalSections[prop];
  469. return Reflect.get(target, "_createCustomSection");
  470. }
  471. return Reflect.get(target, prop);
  472. }
  473. });
  474. }
  475. function section_proxy_wrap(inClass) {
  476. return new Proxy(inClass, {
  477. get: function(target, prop) {
  478. if (!objHasProperty(target, prop) && objHasProperty(target.sequence, prop)) {
  479. const targetProperty = Reflect.get(target.sequence, prop);
  480. return is_function$1(targetProperty) ? targetProperty.bind(target.sequence) : targetProperty;
  481. }
  482. return Reflect.get(target, prop);
  483. }
  484. });
  485. }
  486. function str_to_search_regex_str(str) {
  487. return str.trim().replace(/[^A-Za-z0-9 .*_-]/g, "").replace(/\*+/g, ".*?");
  488. }
  489. function safe_str(str) {
  490. return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
  491. }
  492. function get_hash(input) {
  493. let hash2 = 0;
  494. const len = input.length;
  495. for (let i = 0; i < len; i++) {
  496. hash2 = (hash2 << 5) - hash2 + input.charCodeAt(i);
  497. hash2 |= 0;
  498. }
  499. return hash2;
  500. }
  501. function parseColor(inColor) {
  502. return {
  503. hexadecimal: is_real_number(inColor) ? inColor.toString(16) : inColor,
  504. decimal: typeof inColor === "string" && inColor.startsWith("#") ? parseInt(inColor.slice(1), 16) : inColor
  505. };
  506. }
  507. function getCanvasMouse() {
  508. return game.release.generation === 11 ? canvas.app.renderer.plugins.interaction.pointer : canvas.app.renderer.plugins.interaction.mouse;
  509. }
  510. function noop() {
  511. }
  512. const identity = (x) => x;
  513. function assign(tar, src) {
  514. for (const k in src)
  515. tar[k] = src[k];
  516. return tar;
  517. }
  518. function run(fn) {
  519. return fn();
  520. }
  521. function blank_object() {
  522. return /* @__PURE__ */ Object.create(null);
  523. }
  524. function run_all(fns) {
  525. fns.forEach(run);
  526. }
  527. function is_function(thing) {
  528. return typeof thing === "function";
  529. }
  530. function safe_not_equal(a, b) {
  531. return a != a ? b == b : a !== b || (a && typeof a === "object" || typeof a === "function");
  532. }
  533. let src_url_equal_anchor;
  534. function src_url_equal(element_src, url) {
  535. if (!src_url_equal_anchor) {
  536. src_url_equal_anchor = document.createElement("a");
  537. }
  538. src_url_equal_anchor.href = url;
  539. return element_src === src_url_equal_anchor.href;
  540. }
  541. function is_empty(obj) {
  542. return Object.keys(obj).length === 0;
  543. }
  544. function subscribe(store, ...callbacks) {
  545. if (store == null) {
  546. return noop;
  547. }
  548. const unsub = store.subscribe(...callbacks);
  549. return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
  550. }
  551. function get_store_value(store) {
  552. let value;
  553. subscribe(store, (_) => value = _)();
  554. return value;
  555. }
  556. function component_subscribe(component, store, callback) {
  557. component.$$.on_destroy.push(subscribe(store, callback));
  558. }
  559. function create_slot(definition, ctx, $$scope, fn) {
  560. if (definition) {
  561. const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);
  562. return definition[0](slot_ctx);
  563. }
  564. }
  565. function get_slot_context(definition, ctx, $$scope, fn) {
  566. return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx;
  567. }
  568. function get_slot_changes(definition, $$scope, dirty, fn) {
  569. if (definition[2] && fn) {
  570. const lets = definition[2](fn(dirty));
  571. if ($$scope.dirty === void 0) {
  572. return lets;
  573. }
  574. if (typeof lets === "object") {
  575. const merged = [];
  576. const len = Math.max($$scope.dirty.length, lets.length);
  577. for (let i = 0; i < len; i += 1) {
  578. merged[i] = $$scope.dirty[i] | lets[i];
  579. }
  580. return merged;
  581. }
  582. return $$scope.dirty | lets;
  583. }
  584. return $$scope.dirty;
  585. }
  586. function update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn) {
  587. if (slot_changes) {
  588. const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
  589. slot.p(slot_context, slot_changes);
  590. }
  591. }
  592. function get_all_dirty_from_scope($$scope) {
  593. if ($$scope.ctx.length > 32) {
  594. const dirty = [];
  595. const length = $$scope.ctx.length / 32;
  596. for (let i = 0; i < length; i++) {
  597. dirty[i] = -1;
  598. }
  599. return dirty;
  600. }
  601. return -1;
  602. }
  603. function exclude_internal_props(props) {
  604. const result = {};
  605. for (const k in props)
  606. if (k[0] !== "$")
  607. result[k] = props[k];
  608. return result;
  609. }
  610. function compute_slots(slots) {
  611. const result = {};
  612. for (const key in slots) {
  613. result[key] = true;
  614. }
  615. return result;
  616. }
  617. function set_store_value(store, ret, value) {
  618. store.set(value);
  619. return ret;
  620. }
  621. function action_destroyer(action_result) {
  622. return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
  623. }
  624. const is_client = typeof window !== "undefined";
  625. let now = is_client ? () => window.performance.now() : () => Date.now();
  626. let raf = is_client ? (cb) => requestAnimationFrame(cb) : noop;
  627. const tasks = /* @__PURE__ */ new Set();
  628. function run_tasks(now2) {
  629. tasks.forEach((task) => {
  630. if (!task.c(now2)) {
  631. tasks.delete(task);
  632. task.f();
  633. }
  634. });
  635. if (tasks.size !== 0)
  636. raf(run_tasks);
  637. }
  638. function loop(callback) {
  639. let task;
  640. if (tasks.size === 0)
  641. raf(run_tasks);
  642. return {
  643. promise: new Promise((fulfill) => {
  644. tasks.add(task = { c: callback, f: fulfill });
  645. }),
  646. abort() {
  647. tasks.delete(task);
  648. }
  649. };
  650. }
  651. function append(target, node) {
  652. target.appendChild(node);
  653. }
  654. function get_root_for_style(node) {
  655. if (!node)
  656. return document;
  657. const root = node.getRootNode ? node.getRootNode() : node.ownerDocument;
  658. if (root && root.host) {
  659. return root;
  660. }
  661. return node.ownerDocument;
  662. }
  663. function append_empty_stylesheet(node) {
  664. const style_element = element("style");
  665. append_stylesheet(get_root_for_style(node), style_element);
  666. return style_element.sheet;
  667. }
  668. function append_stylesheet(node, style) {
  669. append(node.head || node, style);
  670. return style.sheet;
  671. }
  672. function insert(target, node, anchor) {
  673. target.insertBefore(node, anchor || null);
  674. }
  675. function detach(node) {
  676. if (node.parentNode) {
  677. node.parentNode.removeChild(node);
  678. }
  679. }
  680. function destroy_each(iterations, detaching) {
  681. for (let i = 0; i < iterations.length; i += 1) {
  682. if (iterations[i])
  683. iterations[i].d(detaching);
  684. }
  685. }
  686. function element(name) {
  687. return document.createElement(name);
  688. }
  689. function svg_element(name) {
  690. return document.createElementNS("http://www.w3.org/2000/svg", name);
  691. }
  692. function text$1(data) {
  693. return document.createTextNode(data);
  694. }
  695. function space() {
  696. return text$1(" ");
  697. }
  698. function empty() {
  699. return text$1("");
  700. }
  701. function listen(node, event, handler, options) {
  702. node.addEventListener(event, handler, options);
  703. return () => node.removeEventListener(event, handler, options);
  704. }
  705. function prevent_default(fn) {
  706. return function(event) {
  707. event.preventDefault();
  708. return fn.call(this, event);
  709. };
  710. }
  711. function stop_propagation(fn) {
  712. return function(event) {
  713. event.stopPropagation();
  714. return fn.call(this, event);
  715. };
  716. }
  717. function attr(node, attribute, value) {
  718. if (value == null)
  719. node.removeAttribute(attribute);
  720. else if (node.getAttribute(attribute) !== value)
  721. node.setAttribute(attribute, value);
  722. }
  723. function to_number(value) {
  724. return value === "" ? null : +value;
  725. }
  726. function children(element2) {
  727. return Array.from(element2.childNodes);
  728. }
  729. function set_data(text2, data) {
  730. data = "" + data;
  731. if (text2.wholeText !== data)
  732. text2.data = data;
  733. }
  734. function set_input_value(input, value) {
  735. input.value = value == null ? "" : value;
  736. }
  737. function set_style(node, key, value, important) {
  738. if (value === null) {
  739. node.style.removeProperty(key);
  740. } else {
  741. node.style.setProperty(key, value, important ? "important" : "");
  742. }
  743. }
  744. function select_option(select, value) {
  745. for (let i = 0; i < select.options.length; i += 1) {
  746. const option = select.options[i];
  747. if (option.__value === value) {
  748. option.selected = true;
  749. return;
  750. }
  751. }
  752. select.selectedIndex = -1;
  753. }
  754. function select_options(select, value) {
  755. for (let i = 0; i < select.options.length; i += 1) {
  756. const option = select.options[i];
  757. option.selected = ~value.indexOf(option.__value);
  758. }
  759. }
  760. function select_value(select) {
  761. const selected_option = select.querySelector(":checked") || select.options[0];
  762. return selected_option && selected_option.__value;
  763. }
  764. function select_multiple_value(select) {
  765. return [].map.call(select.querySelectorAll(":checked"), (option) => option.__value);
  766. }
  767. function toggle_class(element2, name, toggle) {
  768. element2.classList[toggle ? "add" : "remove"](name);
  769. }
  770. function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
  771. const e = document.createEvent("CustomEvent");
  772. e.initCustomEvent(type, bubbles, cancelable, detail);
  773. return e;
  774. }
  775. class HtmlTag {
  776. constructor(is_svg = false) {
  777. this.is_svg = false;
  778. this.is_svg = is_svg;
  779. this.e = this.n = null;
  780. }
  781. c(html) {
  782. this.h(html);
  783. }
  784. m(html, target, anchor = null) {
  785. if (!this.e) {
  786. if (this.is_svg)
  787. this.e = svg_element(target.nodeName);
  788. else
  789. this.e = element(target.nodeName);
  790. this.t = target;
  791. this.c(html);
  792. }
  793. this.i(anchor);
  794. }
  795. h(html) {
  796. this.e.innerHTML = html;
  797. this.n = Array.from(this.e.childNodes);
  798. }
  799. i(anchor) {
  800. for (let i = 0; i < this.n.length; i += 1) {
  801. insert(this.t, this.n[i], anchor);
  802. }
  803. }
  804. p(html) {
  805. this.d();
  806. this.h(html);
  807. this.i(this.a);
  808. }
  809. d() {
  810. this.n.forEach(detach);
  811. }
  812. }
  813. function construct_svelte_component(component, props) {
  814. return new component(props);
  815. }
  816. const managed_styles = /* @__PURE__ */ new Map();
  817. let active = 0;
  818. function hash(str) {
  819. let hash2 = 5381;
  820. let i = str.length;
  821. while (i--)
  822. hash2 = (hash2 << 5) - hash2 ^ str.charCodeAt(i);
  823. return hash2 >>> 0;
  824. }
  825. function create_style_information(doc, node) {
  826. const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
  827. managed_styles.set(doc, info);
  828. return info;
  829. }
  830. function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {
  831. const step = 16.666 / duration;
  832. let keyframes = "{\n";
  833. for (let p = 0; p <= 1; p += step) {
  834. const t = a + (b - a) * ease(p);
  835. keyframes += p * 100 + `%{${fn(t, 1 - t)}}
  836. `;
  837. }
  838. const rule = keyframes + `100% {${fn(b, 1 - b)}}
  839. }`;
  840. const name = `__svelte_${hash(rule)}_${uid}`;
  841. const doc = get_root_for_style(node);
  842. const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
  843. if (!rules[name]) {
  844. rules[name] = true;
  845. stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
  846. }
  847. const animation2 = node.style.animation || "";
  848. node.style.animation = `${animation2 ? `${animation2}, ` : ""}${name} ${duration}ms linear ${delay}ms 1 both`;
  849. active += 1;
  850. return name;
  851. }
  852. function delete_rule(node, name) {
  853. const previous = (node.style.animation || "").split(", ");
  854. const next = previous.filter(
  855. name ? (anim) => anim.indexOf(name) < 0 : (anim) => anim.indexOf("__svelte") === -1
  856. // remove all Svelte animations
  857. );
  858. const deleted = previous.length - next.length;
  859. if (deleted) {
  860. node.style.animation = next.join(", ");
  861. active -= deleted;
  862. if (!active)
  863. clear_rules();
  864. }
  865. }
  866. function clear_rules() {
  867. raf(() => {
  868. if (active)
  869. return;
  870. managed_styles.forEach((info) => {
  871. const { ownerNode } = info.stylesheet;
  872. if (ownerNode)
  873. detach(ownerNode);
  874. });
  875. managed_styles.clear();
  876. });
  877. }
  878. let current_component;
  879. function set_current_component(component) {
  880. current_component = component;
  881. }
  882. function get_current_component() {
  883. if (!current_component)
  884. throw new Error("Function called outside component initialization");
  885. return current_component;
  886. }
  887. function onMount(fn) {
  888. get_current_component().$$.on_mount.push(fn);
  889. }
  890. function afterUpdate(fn) {
  891. get_current_component().$$.after_update.push(fn);
  892. }
  893. function onDestroy(fn) {
  894. get_current_component().$$.on_destroy.push(fn);
  895. }
  896. function createEventDispatcher() {
  897. const component = get_current_component();
  898. return (type, detail, { cancelable = false } = {}) => {
  899. const callbacks = component.$$.callbacks[type];
  900. if (callbacks) {
  901. const event = custom_event(type, detail, { cancelable });
  902. callbacks.slice().forEach((fn) => {
  903. fn.call(component, event);
  904. });
  905. return !event.defaultPrevented;
  906. }
  907. return true;
  908. };
  909. }
  910. function setContext(key, context) {
  911. get_current_component().$$.context.set(key, context);
  912. return context;
  913. }
  914. function getContext(key) {
  915. return get_current_component().$$.context.get(key);
  916. }
  917. const dirty_components = [];
  918. const binding_callbacks = [];
  919. const render_callbacks = [];
  920. const flush_callbacks = [];
  921. const resolved_promise = Promise.resolve();
  922. let update_scheduled = false;
  923. function schedule_update() {
  924. if (!update_scheduled) {
  925. update_scheduled = true;
  926. resolved_promise.then(flush);
  927. }
  928. }
  929. function add_render_callback(fn) {
  930. render_callbacks.push(fn);
  931. }
  932. function add_flush_callback(fn) {
  933. flush_callbacks.push(fn);
  934. }
  935. const seen_callbacks = /* @__PURE__ */ new Set();
  936. let flushidx = 0;
  937. function flush() {
  938. if (flushidx !== 0) {
  939. return;
  940. }
  941. const saved_component = current_component;
  942. do {
  943. try {
  944. while (flushidx < dirty_components.length) {
  945. const component = dirty_components[flushidx];
  946. flushidx++;
  947. set_current_component(component);
  948. update(component.$$);
  949. }
  950. } catch (e) {
  951. dirty_components.length = 0;
  952. flushidx = 0;
  953. throw e;
  954. }
  955. set_current_component(null);
  956. dirty_components.length = 0;
  957. flushidx = 0;
  958. while (binding_callbacks.length)
  959. binding_callbacks.pop()();
  960. for (let i = 0; i < render_callbacks.length; i += 1) {
  961. const callback = render_callbacks[i];
  962. if (!seen_callbacks.has(callback)) {
  963. seen_callbacks.add(callback);
  964. callback();
  965. }
  966. }
  967. render_callbacks.length = 0;
  968. } while (dirty_components.length);
  969. while (flush_callbacks.length) {
  970. flush_callbacks.pop()();
  971. }
  972. update_scheduled = false;
  973. seen_callbacks.clear();
  974. set_current_component(saved_component);
  975. }
  976. function update($$) {
  977. if ($$.fragment !== null) {
  978. $$.update();
  979. run_all($$.before_update);
  980. const dirty = $$.dirty;
  981. $$.dirty = [-1];
  982. $$.fragment && $$.fragment.p($$.ctx, dirty);
  983. $$.after_update.forEach(add_render_callback);
  984. }
  985. }
  986. let promise;
  987. function wait() {
  988. if (!promise) {
  989. promise = Promise.resolve();
  990. promise.then(() => {
  991. promise = null;
  992. });
  993. }
  994. return promise;
  995. }
  996. function dispatch(node, direction, kind) {
  997. node.dispatchEvent(custom_event(`${direction ? "intro" : "outro"}${kind}`));
  998. }
  999. const outroing = /* @__PURE__ */ new Set();
  1000. let outros;
  1001. function group_outros() {
  1002. outros = {
  1003. r: 0,
  1004. c: [],
  1005. p: outros
  1006. // parent group
  1007. };
  1008. }
  1009. function check_outros() {
  1010. if (!outros.r) {
  1011. run_all(outros.c);
  1012. }
  1013. outros = outros.p;
  1014. }
  1015. function transition_in(block, local) {
  1016. if (block && block.i) {
  1017. outroing.delete(block);
  1018. block.i(local);
  1019. }
  1020. }
  1021. function transition_out(block, local, detach2, callback) {
  1022. if (block && block.o) {
  1023. if (outroing.has(block))
  1024. return;
  1025. outroing.add(block);
  1026. outros.c.push(() => {
  1027. outroing.delete(block);
  1028. if (callback) {
  1029. if (detach2)
  1030. block.d(1);
  1031. callback();
  1032. }
  1033. });
  1034. block.o(local);
  1035. } else if (callback) {
  1036. callback();
  1037. }
  1038. }
  1039. const null_transition = { duration: 0 };
  1040. function create_in_transition(node, fn, params) {
  1041. const options = { direction: "in" };
  1042. let config = fn(node, params, options);
  1043. let running = false;
  1044. let animation_name;
  1045. let task;
  1046. let uid = 0;
  1047. function cleanup() {
  1048. if (animation_name)
  1049. delete_rule(node, animation_name);
  1050. }
  1051. function go() {
  1052. const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;
  1053. if (css)
  1054. animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++);
  1055. tick(0, 1);
  1056. const start_time = now() + delay;
  1057. const end_time = start_time + duration;
  1058. if (task)
  1059. task.abort();
  1060. running = true;
  1061. add_render_callback(() => dispatch(node, true, "start"));
  1062. task = loop((now2) => {
  1063. if (running) {
  1064. if (now2 >= end_time) {
  1065. tick(1, 0);
  1066. dispatch(node, true, "end");
  1067. cleanup();
  1068. return running = false;
  1069. }
  1070. if (now2 >= start_time) {
  1071. const t = easing((now2 - start_time) / duration);
  1072. tick(t, 1 - t);
  1073. }
  1074. }
  1075. return running;
  1076. });
  1077. }
  1078. let started = false;
  1079. return {
  1080. start() {
  1081. if (started)
  1082. return;
  1083. started = true;
  1084. delete_rule(node);
  1085. if (is_function(config)) {
  1086. config = config(options);
  1087. wait().then(go);
  1088. } else {
  1089. go();
  1090. }
  1091. },
  1092. invalidate() {
  1093. started = false;
  1094. },
  1095. end() {
  1096. if (running) {
  1097. cleanup();
  1098. running = false;
  1099. }
  1100. }
  1101. };
  1102. }
  1103. function create_out_transition(node, fn, params) {
  1104. const options = { direction: "out" };
  1105. let config = fn(node, params, options);
  1106. let running = true;
  1107. let animation_name;
  1108. const group = outros;
  1109. group.r += 1;
  1110. function go() {
  1111. const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;
  1112. if (css)
  1113. animation_name = create_rule(node, 1, 0, duration, delay, easing, css);
  1114. const start_time = now() + delay;
  1115. const end_time = start_time + duration;
  1116. add_render_callback(() => dispatch(node, false, "start"));
  1117. loop((now2) => {
  1118. if (running) {
  1119. if (now2 >= end_time) {
  1120. tick(0, 1);
  1121. dispatch(node, false, "end");
  1122. if (!--group.r) {
  1123. run_all(group.c);
  1124. }
  1125. return false;
  1126. }
  1127. if (now2 >= start_time) {
  1128. const t = easing((now2 - start_time) / duration);
  1129. tick(1 - t, t);
  1130. }
  1131. }
  1132. return running;
  1133. });
  1134. }
  1135. if (is_function(config)) {
  1136. wait().then(() => {
  1137. config = config(options);
  1138. go();
  1139. });
  1140. } else {
  1141. go();
  1142. }
  1143. return {
  1144. end(reset) {
  1145. if (reset && config.tick) {
  1146. config.tick(1, 0);
  1147. }
  1148. if (running) {
  1149. if (animation_name)
  1150. delete_rule(node, animation_name);
  1151. running = false;
  1152. }
  1153. }
  1154. };
  1155. }
  1156. function destroy_block(block, lookup) {
  1157. block.d(1);
  1158. lookup.delete(block.key);
  1159. }
  1160. function outro_and_destroy_block(block, lookup) {
  1161. transition_out(block, 1, 1, () => {
  1162. lookup.delete(block.key);
  1163. });
  1164. }
  1165. function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block2, next, get_context) {
  1166. let o = old_blocks.length;
  1167. let n = list.length;
  1168. let i = o;
  1169. const old_indexes = {};
  1170. while (i--)
  1171. old_indexes[old_blocks[i].key] = i;
  1172. const new_blocks = [];
  1173. const new_lookup = /* @__PURE__ */ new Map();
  1174. const deltas = /* @__PURE__ */ new Map();
  1175. i = n;
  1176. while (i--) {
  1177. const child_ctx = get_context(ctx, list, i);
  1178. const key = get_key(child_ctx);
  1179. let block = lookup.get(key);
  1180. if (!block) {
  1181. block = create_each_block2(key, child_ctx);
  1182. block.c();
  1183. } else if (dynamic) {
  1184. block.p(child_ctx, dirty);
  1185. }
  1186. new_lookup.set(key, new_blocks[i] = block);
  1187. if (key in old_indexes)
  1188. deltas.set(key, Math.abs(i - old_indexes[key]));
  1189. }
  1190. const will_move = /* @__PURE__ */ new Set();
  1191. const did_move = /* @__PURE__ */ new Set();
  1192. function insert2(block) {
  1193. transition_in(block, 1);
  1194. block.m(node, next);
  1195. lookup.set(block.key, block);
  1196. next = block.first;
  1197. n--;
  1198. }
  1199. while (o && n) {
  1200. const new_block = new_blocks[n - 1];
  1201. const old_block = old_blocks[o - 1];
  1202. const new_key = new_block.key;
  1203. const old_key = old_block.key;
  1204. if (new_block === old_block) {
  1205. next = new_block.first;
  1206. o--;
  1207. n--;
  1208. } else if (!new_lookup.has(old_key)) {
  1209. destroy(old_block, lookup);
  1210. o--;
  1211. } else if (!lookup.has(new_key) || will_move.has(new_key)) {
  1212. insert2(new_block);
  1213. } else if (did_move.has(old_key)) {
  1214. o--;
  1215. } else if (deltas.get(new_key) > deltas.get(old_key)) {
  1216. did_move.add(new_key);
  1217. insert2(new_block);
  1218. } else {
  1219. will_move.add(old_key);
  1220. o--;
  1221. }
  1222. }
  1223. while (o--) {
  1224. const old_block = old_blocks[o];
  1225. if (!new_lookup.has(old_block.key))
  1226. destroy(old_block, lookup);
  1227. }
  1228. while (n)
  1229. insert2(new_blocks[n - 1]);
  1230. return new_blocks;
  1231. }
  1232. function get_spread_update(levels, updates) {
  1233. const update2 = {};
  1234. const to_null_out = {};
  1235. const accounted_for = { $$scope: 1 };
  1236. let i = levels.length;
  1237. while (i--) {
  1238. const o = levels[i];
  1239. const n = updates[i];
  1240. if (n) {
  1241. for (const key in o) {
  1242. if (!(key in n))
  1243. to_null_out[key] = 1;
  1244. }
  1245. for (const key in n) {
  1246. if (!accounted_for[key]) {
  1247. update2[key] = n[key];
  1248. accounted_for[key] = 1;
  1249. }
  1250. }
  1251. levels[i] = n;
  1252. } else {
  1253. for (const key in o) {
  1254. accounted_for[key] = 1;
  1255. }
  1256. }
  1257. }
  1258. for (const key in to_null_out) {
  1259. if (!(key in update2))
  1260. update2[key] = void 0;
  1261. }
  1262. return update2;
  1263. }
  1264. function get_spread_object(spread_props) {
  1265. return typeof spread_props === "object" && spread_props !== null ? spread_props : {};
  1266. }
  1267. function bind(component, name, callback) {
  1268. const index = component.$$.props[name];
  1269. if (index !== void 0) {
  1270. component.$$.bound[index] = callback;
  1271. callback(component.$$.ctx[index]);
  1272. }
  1273. }
  1274. function create_component(block) {
  1275. block && block.c();
  1276. }
  1277. function mount_component(component, target, anchor, customElement) {
  1278. const { fragment, after_update } = component.$$;
  1279. fragment && fragment.m(target, anchor);
  1280. if (!customElement) {
  1281. add_render_callback(() => {
  1282. const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
  1283. if (component.$$.on_destroy) {
  1284. component.$$.on_destroy.push(...new_on_destroy);
  1285. } else {
  1286. run_all(new_on_destroy);
  1287. }
  1288. component.$$.on_mount = [];
  1289. });
  1290. }
  1291. after_update.forEach(add_render_callback);
  1292. }
  1293. function destroy_component(component, detaching) {
  1294. const $$ = component.$$;
  1295. if ($$.fragment !== null) {
  1296. run_all($$.on_destroy);
  1297. $$.fragment && $$.fragment.d(detaching);
  1298. $$.on_destroy = $$.fragment = null;
  1299. $$.ctx = [];
  1300. }
  1301. }
  1302. function make_dirty(component, i) {
  1303. if (component.$$.dirty[0] === -1) {
  1304. dirty_components.push(component);
  1305. schedule_update();
  1306. component.$$.dirty.fill(0);
  1307. }
  1308. component.$$.dirty[i / 31 | 0] |= 1 << i % 31;
  1309. }
  1310. function init(component, options, instance2, create_fragment2, not_equal, props, append_styles, dirty = [-1]) {
  1311. const parent_component = current_component;
  1312. set_current_component(component);
  1313. const $$ = component.$$ = {
  1314. fragment: null,
  1315. ctx: [],
  1316. // state
  1317. props,
  1318. update: noop,
  1319. not_equal,
  1320. bound: blank_object(),
  1321. // lifecycle
  1322. on_mount: [],
  1323. on_destroy: [],
  1324. on_disconnect: [],
  1325. before_update: [],
  1326. after_update: [],
  1327. context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
  1328. // everything else
  1329. callbacks: blank_object(),
  1330. dirty,
  1331. skip_bound: false,
  1332. root: options.target || parent_component.$$.root
  1333. };
  1334. append_styles && append_styles($$.root);
  1335. let ready = false;
  1336. $$.ctx = instance2 ? instance2(component, options.props || {}, (i, ret, ...rest) => {
  1337. const value = rest.length ? rest[0] : ret;
  1338. if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
  1339. if (!$$.skip_bound && $$.bound[i])
  1340. $$.bound[i](value);
  1341. if (ready)
  1342. make_dirty(component, i);
  1343. }
  1344. return ret;
  1345. }) : [];
  1346. $$.update();
  1347. ready = true;
  1348. run_all($$.before_update);
  1349. $$.fragment = create_fragment2 ? create_fragment2($$.ctx) : false;
  1350. if (options.target) {
  1351. if (options.hydrate) {
  1352. const nodes = children(options.target);
  1353. $$.fragment && $$.fragment.l(nodes);
  1354. nodes.forEach(detach);
  1355. } else {
  1356. $$.fragment && $$.fragment.c();
  1357. }
  1358. if (options.intro)
  1359. transition_in(component.$$.fragment);
  1360. mount_component(component, options.target, options.anchor, options.customElement);
  1361. flush();
  1362. }
  1363. set_current_component(parent_component);
  1364. }
  1365. class SvelteComponent {
  1366. $destroy() {
  1367. destroy_component(this, 1);
  1368. this.$destroy = noop;
  1369. }
  1370. $on(type, callback) {
  1371. if (!is_function(callback)) {
  1372. return noop;
  1373. }
  1374. const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []);
  1375. callbacks.push(callback);
  1376. return () => {
  1377. const index = callbacks.indexOf(callback);
  1378. if (index !== -1)
  1379. callbacks.splice(index, 1);
  1380. };
  1381. }
  1382. $set($$props) {
  1383. if (this.$$set && !is_empty($$props)) {
  1384. this.$$.skip_bound = true;
  1385. this.$$set($$props);
  1386. this.$$.skip_bound = false;
  1387. }
  1388. }
  1389. }
  1390. const s_TAG_OBJECT = "[object Object]";
  1391. function deepMerge(target = {}, ...sourceObj) {
  1392. if (Object.prototype.toString.call(target) !== s_TAG_OBJECT) {
  1393. throw new TypeError(`deepMerge error: 'target' is not an 'object'.`);
  1394. }
  1395. for (let cntr = 0; cntr < sourceObj.length; cntr++) {
  1396. if (Object.prototype.toString.call(sourceObj[cntr]) !== s_TAG_OBJECT) {
  1397. throw new TypeError(`deepMerge error: 'sourceObj[${cntr}]' is not an 'object'.`);
  1398. }
  1399. }
  1400. return _deepMerge(target, ...sourceObj);
  1401. }
  1402. function isIterable(value) {
  1403. if (value === null || value === void 0 || typeof value !== "object") {
  1404. return false;
  1405. }
  1406. return typeof value[Symbol.iterator] === "function";
  1407. }
  1408. function isObject(value) {
  1409. return value !== null && typeof value === "object";
  1410. }
  1411. function isPlainObject(value) {
  1412. if (Object.prototype.toString.call(value) !== s_TAG_OBJECT) {
  1413. return false;
  1414. }
  1415. const prototype = Object.getPrototypeOf(value);
  1416. return prototype === null || prototype === Object.prototype;
  1417. }
  1418. function safeAccess(data, accessor, defaultValue = void 0) {
  1419. if (typeof data !== "object") {
  1420. return defaultValue;
  1421. }
  1422. if (typeof accessor !== "string") {
  1423. return defaultValue;
  1424. }
  1425. const access = accessor.split(".");
  1426. for (let cntr = 0; cntr < access.length; cntr++) {
  1427. if (typeof data[access[cntr]] === "undefined" || data[access[cntr]] === null) {
  1428. return defaultValue;
  1429. }
  1430. data = data[access[cntr]];
  1431. }
  1432. return data;
  1433. }
  1434. function safeSet(data, accessor, value, operation = "set", createMissing = true) {
  1435. if (typeof data !== "object") {
  1436. throw new TypeError(`safeSet Error: 'data' is not an 'object'.`);
  1437. }
  1438. if (typeof accessor !== "string") {
  1439. throw new TypeError(`safeSet Error: 'accessor' is not a 'string'.`);
  1440. }
  1441. const access = accessor.split(".");
  1442. for (let cntr = 0; cntr < access.length; cntr++) {
  1443. if (Array.isArray(data)) {
  1444. const number = +access[cntr];
  1445. if (!Number.isInteger(number) || number < 0) {
  1446. return false;
  1447. }
  1448. }
  1449. if (cntr === access.length - 1) {
  1450. switch (operation) {
  1451. case "add":
  1452. data[access[cntr]] += value;
  1453. break;
  1454. case "div":
  1455. data[access[cntr]] /= value;
  1456. break;
  1457. case "mult":
  1458. data[access[cntr]] *= value;
  1459. break;
  1460. case "set":
  1461. data[access[cntr]] = value;
  1462. break;
  1463. case "set-undefined":
  1464. if (typeof data[access[cntr]] === "undefined") {
  1465. data[access[cntr]] = value;
  1466. }
  1467. break;
  1468. case "sub":
  1469. data[access[cntr]] -= value;
  1470. break;
  1471. }
  1472. } else {
  1473. if (createMissing && typeof data[access[cntr]] === "undefined") {
  1474. data[access[cntr]] = {};
  1475. }
  1476. if (data[access[cntr]] === null || typeof data[access[cntr]] !== "object") {
  1477. return false;
  1478. }
  1479. data = data[access[cntr]];
  1480. }
  1481. }
  1482. return true;
  1483. }
  1484. function _deepMerge(target = {}, ...sourceObj) {
  1485. for (let cntr = 0; cntr < sourceObj.length; cntr++) {
  1486. const obj = sourceObj[cntr];
  1487. for (const prop in obj) {
  1488. if (Object.prototype.hasOwnProperty.call(obj, prop)) {
  1489. if (prop.startsWith("-=")) {
  1490. delete target[prop.slice(2)];
  1491. continue;
  1492. }
  1493. target[prop] = Object.prototype.hasOwnProperty.call(target, prop) && target[prop]?.constructor === Object && obj[prop]?.constructor === Object ? _deepMerge({}, target[prop], obj[prop]) : obj[prop];
  1494. }
  1495. }
  1496. }
  1497. return target;
  1498. }
  1499. class A11yHelper {
  1500. /**
  1501. * Apply focus to the HTMLElement targets in a given A11yFocusSource data object. An iterable list `options.focusEl`
  1502. * can contain HTMLElements or selector strings. If multiple focus targets are provided in a list then the first
  1503. * valid target found will be focused. If focus target is a string then a lookup via `document.querySelector` is
  1504. * performed. In this case you should provide a unique selector for the desired focus target.
  1505. *
  1506. * Note: The body of this method is postponed to the next clock tick to allow any changes in the DOM to occur that
  1507. * might alter focus targets before applying.
  1508. *
  1509. * @param {A11yFocusSource|{ focusSource: A11yFocusSource }} options - The focus options instance to apply.
  1510. */
  1511. static applyFocusSource(options) {
  1512. if (!isObject(options)) {
  1513. return;
  1514. }
  1515. const focusOpts = isObject(options?.focusSource) ? options.focusSource : options;
  1516. setTimeout(() => {
  1517. const debug2 = typeof focusOpts.debug === "boolean" ? focusOpts.debug : false;
  1518. if (isIterable(focusOpts.focusEl)) {
  1519. if (debug2) {
  1520. console.debug(`A11yHelper.applyFocusSource debug - Attempting to apply focus target: `, focusOpts.focusEl);
  1521. }
  1522. for (const target of focusOpts.focusEl) {
  1523. if (target instanceof HTMLElement && target.isConnected) {
  1524. target.focus();
  1525. if (debug2) {
  1526. console.debug(`A11yHelper.applyFocusSource debug - Applied focus to target: `, target);
  1527. }
  1528. break;
  1529. } else if (typeof target === "string") {
  1530. const element2 = document.querySelector(target);
  1531. if (element2 instanceof HTMLElement && element2.isConnected) {
  1532. element2.focus();
  1533. if (debug2) {
  1534. console.debug(`A11yHelper.applyFocusSource debug - Applied focus to target: `, element2);
  1535. }
  1536. break;
  1537. } else if (debug2) {
  1538. console.debug(`A11yHelper.applyFocusSource debug - Could not query selector: `, target);
  1539. }
  1540. }
  1541. }
  1542. } else if (debug2) {
  1543. console.debug(`A11yHelper.applyFocusSource debug - No focus targets defined.`);
  1544. }
  1545. }, 0);
  1546. }
  1547. /**
  1548. * Returns first focusable element within a specified element.
  1549. *
  1550. * @param {HTMLElement|Document} [element=document] - Optional element to start query.
  1551. *
  1552. * @param {object} [options] - Optional parameters.
  1553. *
  1554. * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
  1555. *
  1556. * @param {Set<HTMLElement>} [options.ignoreElements] - Set of elements to ignore.
  1557. *
  1558. * @returns {HTMLElement} First focusable child element
  1559. */
  1560. static getFirstFocusableElement(element2 = document, options) {
  1561. const focusableElements = this.getFocusableElements(element2, options);
  1562. return focusableElements.length > 0 ? focusableElements[0] : void 0;
  1563. }
  1564. /**
  1565. * Returns all focusable elements within a specified element.
  1566. *
  1567. * @param {HTMLElement|Document} [element=document] Optional element to start query.
  1568. *
  1569. * @param {object} [options] - Optional parameters.
  1570. *
  1571. * @param {boolean} [options.anchorHref=true] - When true anchors must have an HREF.
  1572. *
  1573. * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
  1574. *
  1575. * @param {Set<HTMLElement>} [options.ignoreElements] - Set of elements to ignore.
  1576. *
  1577. * @param {string} [options.selectors] - Custom list of focusable selectors for `querySelectorAll`.
  1578. *
  1579. * @returns {Array<HTMLElement>} Child keyboard focusable
  1580. */
  1581. static getFocusableElements(element2 = document, { anchorHref = true, ignoreClasses, ignoreElements, selectors } = {}) {
  1582. if (!(element2 instanceof HTMLElement) && !(element2 instanceof Document)) {
  1583. throw new TypeError(`'element' is not a HTMLElement or Document instance.`);
  1584. }
  1585. if (typeof anchorHref !== "boolean") {
  1586. throw new TypeError(`'anchorHref' is not a boolean.`);
  1587. }
  1588. if (ignoreClasses !== void 0 && !isIterable(ignoreClasses)) {
  1589. throw new TypeError(`'ignoreClasses' is not an iterable list.`);
  1590. }
  1591. if (ignoreElements !== void 0 && !(ignoreElements instanceof Set)) {
  1592. throw new TypeError(`'ignoreElements' is not a Set.`);
  1593. }
  1594. if (selectors !== void 0 && typeof selectors !== "string") {
  1595. throw new TypeError(`'selectors' is not a string.`);
  1596. }
  1597. const selectorQuery = selectors ?? this.#getFocusableSelectors(anchorHref);
  1598. const allElements = [...element2.querySelectorAll(selectorQuery)];
  1599. if (ignoreElements && ignoreClasses) {
  1600. return allElements.filter((el) => {
  1601. let hasIgnoreClass = false;
  1602. for (const ignoreClass of ignoreClasses) {
  1603. if (el.classList.contains(ignoreClass)) {
  1604. hasIgnoreClass = true;
  1605. break;
  1606. }
  1607. }
  1608. return !hasIgnoreClass && !ignoreElements.has(el) && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1609. });
  1610. } else if (ignoreClasses) {
  1611. return allElements.filter((el) => {
  1612. let hasIgnoreClass = false;
  1613. for (const ignoreClass of ignoreClasses) {
  1614. if (el.classList.contains(ignoreClass)) {
  1615. hasIgnoreClass = true;
  1616. break;
  1617. }
  1618. }
  1619. return !hasIgnoreClass && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1620. });
  1621. } else if (ignoreElements) {
  1622. return allElements.filter((el) => {
  1623. return !ignoreElements.has(el) && el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1624. });
  1625. } else {
  1626. return allElements.filter((el) => {
  1627. return el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1628. });
  1629. }
  1630. }
  1631. /**
  1632. * Returns the default focusable selectors query.
  1633. *
  1634. * @param {boolean} [anchorHref=true] - When true anchors must have an HREF.
  1635. *
  1636. * @returns {string} Focusable selectors for `querySelectorAll`.
  1637. */
  1638. static #getFocusableSelectors(anchorHref = true) {
  1639. 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"])`;
  1640. }
  1641. /**
  1642. * Gets a A11yFocusSource object from the given DOM event allowing for optional X / Y screen space overrides.
  1643. * Browsers (Firefox / Chrome) forwards a mouse event for the context menu keyboard button. Provides detection of
  1644. * when the context menu event is from the keyboard. Firefox as of (1/23) does not provide the correct screen space
  1645. * coordinates, so for keyboard context menu presses coordinates are generated from the centroid point of the
  1646. * element.
  1647. *
  1648. * A default fallback element or selector string may be provided to provide the focus target. If the event comes from
  1649. * the keyboard however the source focused element is inserted as the target with the fallback value appended to the
  1650. * list of focus targets. When A11yFocusSource is applied by {@link A11yHelper.applyFocusSource} the target focus
  1651. * list is iterated through until a connected target is found and focus applied.
  1652. *
  1653. * @param {object} options - Options
  1654. *
  1655. * @param {KeyboardEvent|MouseEvent} [options.event] - The source DOM event.
  1656. *
  1657. * @param {boolean} [options.debug] - When true {@link A11yHelper.applyFocusSource} logs focus target data.
  1658. *
  1659. * @param {HTMLElement|string} [options.focusEl] - A specific HTMLElement or selector string as the focus target.
  1660. *
  1661. * @param {number} [options.x] - Used when an event isn't provided; integer of event source in screen space.
  1662. *
  1663. * @param {number} [options.y] - Used when an event isn't provided; integer of event source in screen space.
  1664. *
  1665. * @returns {A11yFocusSource} A A11yFocusSource object.
  1666. *
  1667. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1426671
  1668. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=314314
  1669. *
  1670. * TODO: Evaluate / test against touch input devices.
  1671. */
  1672. static getFocusSource({ event, x, y, focusEl, debug: debug2 = false }) {
  1673. if (focusEl !== void 0 && !(focusEl instanceof HTMLElement) && typeof focusEl !== "string") {
  1674. throw new TypeError(
  1675. `A11yHelper.getFocusSource error: 'focusEl' is not a HTMLElement or string.`
  1676. );
  1677. }
  1678. if (debug2 !== void 0 && typeof debug2 !== "boolean") {
  1679. throw new TypeError(`A11yHelper.getFocusSource error: 'debug' is not a boolean.`);
  1680. }
  1681. if (event === void 0) {
  1682. if (typeof x !== "number") {
  1683. throw new TypeError(`A11yHelper.getFocusSource error: 'event' not defined and 'x' is not a number.`);
  1684. }
  1685. if (typeof y !== "number") {
  1686. throw new TypeError(`A11yHelper.getFocusSource error: 'event' not defined and 'y' is not a number.`);
  1687. }
  1688. return {
  1689. debug: debug2,
  1690. focusEl: focusEl !== void 0 ? [focusEl] : void 0,
  1691. x,
  1692. y
  1693. };
  1694. }
  1695. if (!(event instanceof KeyboardEvent) && !(event instanceof MouseEvent)) {
  1696. throw new TypeError(`A11yHelper.getFocusSource error: 'event' is not a KeyboardEvent or MouseEvent.`);
  1697. }
  1698. if (x !== void 0 && !Number.isInteger(x)) {
  1699. throw new TypeError(`A11yHelper.getFocusSource error: 'x' is not a number.`);
  1700. }
  1701. if (y !== void 0 && !Number.isInteger(y)) {
  1702. throw new TypeError(`A11yHelper.getFocusSource error: 'y' is not a number.`);
  1703. }
  1704. const targetEl = event.target;
  1705. if (!(targetEl instanceof HTMLElement)) {
  1706. throw new TypeError(`A11yHelper.getFocusSource error: 'event.target' is not an HTMLElement.`);
  1707. }
  1708. const result = { debug: debug2 };
  1709. if (event instanceof MouseEvent) {
  1710. if (event?.button !== 2 && event.type === "contextmenu") {
  1711. const rect = targetEl.getBoundingClientRect();
  1712. result.x = x ?? rect.left + rect.width / 2;
  1713. result.y = y ?? rect.top + rect.height / 2;
  1714. result.focusEl = focusEl !== void 0 ? [targetEl, focusEl] : [targetEl];
  1715. result.source = "keyboard";
  1716. } else {
  1717. result.x = x ?? event.pageX;
  1718. result.y = y ?? event.pageY;
  1719. result.focusEl = focusEl !== void 0 ? [focusEl] : void 0;
  1720. }
  1721. } else {
  1722. const rect = targetEl.getBoundingClientRect();
  1723. result.x = x ?? rect.left + rect.width / 2;
  1724. result.y = y ?? rect.top + rect.height / 2;
  1725. result.focusEl = focusEl !== void 0 ? [targetEl, focusEl] : [targetEl];
  1726. result.source = "keyboard";
  1727. }
  1728. return result;
  1729. }
  1730. /**
  1731. * Returns first focusable element within a specified element.
  1732. *
  1733. * @param {HTMLElement|Document} [element=document] - Optional element to start query.
  1734. *
  1735. * @param {object} [options] - Optional parameters.
  1736. *
  1737. * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
  1738. *
  1739. * @param {Set<HTMLElement>} [options.ignoreElements] - Set of elements to ignore.
  1740. *
  1741. * @returns {HTMLElement} First focusable child element
  1742. */
  1743. static getLastFocusableElement(element2 = document, options) {
  1744. const focusableElements = this.getFocusableElements(element2, options);
  1745. return focusableElements.length > 0 ? focusableElements[focusableElements.length - 1] : void 0;
  1746. }
  1747. /**
  1748. * Tests if the given element is focusable.
  1749. *
  1750. * @param {HTMLElement} [el] - Element to test.
  1751. *
  1752. * @param {object} [options] - Optional parameters.
  1753. *
  1754. * @param {boolean} [options.anchorHref=true] - When true anchors must have an HREF.
  1755. *
  1756. * @param {Iterable<string>} [options.ignoreClasses] - Iterable list of classes to ignore elements.
  1757. *
  1758. * @returns {boolean} Element is focusable.
  1759. */
  1760. static isFocusable(el, { anchorHref = true, ignoreClasses } = {}) {
  1761. if (el === void 0 || el === null || !(el instanceof HTMLElement) || el?.hidden || !el?.isConnected) {
  1762. return false;
  1763. }
  1764. if (typeof anchorHref !== "boolean") {
  1765. throw new TypeError(`'anchorHref' is not a boolean.`);
  1766. }
  1767. if (ignoreClasses !== void 0 && !isIterable(ignoreClasses)) {
  1768. throw new TypeError(`'ignoreClasses' is not an iterable list.`);
  1769. }
  1770. const contenteditableAttr = el.getAttribute("contenteditable");
  1771. const contenteditableFocusable = typeof contenteditableAttr === "string" && (contenteditableAttr === "" || contenteditableAttr === "true");
  1772. const tabindexAttr = el.getAttribute("tabindex");
  1773. const tabindexFocusable = typeof tabindexAttr === "string" && tabindexAttr !== "-1";
  1774. const isAnchor = el instanceof HTMLAnchorElement;
  1775. 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) {
  1776. if (isAnchor && anchorHref && typeof el.getAttribute("href") !== "string") {
  1777. return false;
  1778. }
  1779. return el.style.display !== "none" && el.style.visibility !== "hidden" && !el.hasAttribute("disabled") && !el.hasAttribute("inert") && el.getAttribute("aria-hidden") !== "true";
  1780. }
  1781. return false;
  1782. }
  1783. /**
  1784. * Convenience method to check if the given data is a valid focus source.
  1785. *
  1786. * @param {HTMLElement|string} data - Either an HTMLElement or selector string.
  1787. *
  1788. * @returns {boolean} Is valid focus source.
  1789. */
  1790. static isFocusSource(data) {
  1791. return data instanceof HTMLElement || typeof data === "string";
  1792. }
  1793. }
  1794. class StyleManager {
  1795. /** @type {CSSStyleRule} */
  1796. #cssRule;
  1797. /** @type {string} */
  1798. #docKey;
  1799. /** @type {string} */
  1800. #selector;
  1801. /** @type {HTMLStyleElement} */
  1802. #styleElement;
  1803. /** @type {number} */
  1804. #version;
  1805. /**
  1806. *
  1807. * @param {object} opts - Options.
  1808. *
  1809. * @param {string} opts.docKey - Required key providing a link to a specific style sheet element.
  1810. *
  1811. * @param {string} [opts.selector=:root] - Selector element.
  1812. *
  1813. * @param {Document} [opts.document] - Target document to load styles into.
  1814. *
  1815. * @param {number} [opts.version] - An integer representing the version / level of styles being managed.
  1816. *
  1817. */
  1818. constructor({ docKey, selector = ":root", document: document2 = globalThis.document, version } = {}) {
  1819. if (typeof docKey !== "string") {
  1820. throw new TypeError(`StyleManager error: 'docKey' is not a string.`);
  1821. }
  1822. if (typeof selector !== "string") {
  1823. throw new TypeError(`StyleManager error: 'selector' is not a string.`);
  1824. }
  1825. if (version !== void 0 && !Number.isSafeInteger(version) && version < 1) {
  1826. throw new TypeError(`StyleManager error: 'version' is defined and is not a positive integer >= 1.`);
  1827. }
  1828. this.#selector = selector;
  1829. this.#docKey = docKey;
  1830. this.#version = version;
  1831. if (document2[this.#docKey] === void 0) {
  1832. this.#styleElement = document2.createElement("style");
  1833. document2.head.append(this.#styleElement);
  1834. this.#styleElement._STYLE_MANAGER_VERSION = version;
  1835. this.#styleElement.sheet.insertRule(`${selector} {}`, 0);
  1836. this.#cssRule = this.#styleElement.sheet.cssRules[0];
  1837. document2[docKey] = this.#styleElement;
  1838. } else {
  1839. this.#styleElement = document2[docKey];
  1840. this.#cssRule = this.#styleElement.sheet.cssRules[0];
  1841. if (version) {
  1842. const existingVersion = this.#styleElement._STYLE_MANAGER_VERSION ?? 0;
  1843. if (version > existingVersion) {
  1844. this.#cssRule.style.cssText = "";
  1845. }
  1846. }
  1847. }
  1848. }
  1849. /**
  1850. * @returns {string} Provides an accessor to get the `cssText` for the style sheet.
  1851. */
  1852. get cssText() {
  1853. return this.#cssRule.style.cssText;
  1854. }
  1855. /**
  1856. * @returns {number} Returns the version of this instance.
  1857. */
  1858. get version() {
  1859. return this.#version;
  1860. }
  1861. /**
  1862. * Provides a copy constructor to duplicate an existing StyleManager instance into a new document.
  1863. *
  1864. * Note: This is used to support the `PopOut` module.
  1865. *
  1866. * @param {Document} [document] Target browser document to clone into.
  1867. *
  1868. * @returns {StyleManager} New style manager instance.
  1869. */
  1870. clone(document2 = globalThis.document) {
  1871. const newStyleManager = new StyleManager({
  1872. selector: this.#selector,
  1873. docKey: this.#docKey,
  1874. document: document2,
  1875. version: this.#version
  1876. });
  1877. newStyleManager.#cssRule.style.cssText = this.#cssRule.style.cssText;
  1878. return newStyleManager;
  1879. }
  1880. get() {
  1881. const cssText = this.#cssRule.style.cssText;
  1882. const result = {};
  1883. if (cssText !== "") {
  1884. for (const entry of cssText.split(";")) {
  1885. if (entry !== "") {
  1886. const values = entry.split(":");
  1887. result[values[0].trim()] = values[1];
  1888. }
  1889. }
  1890. }
  1891. return result;
  1892. }
  1893. /**
  1894. * Gets a particular CSS variable.
  1895. *
  1896. * @param {string} key - CSS variable property key.
  1897. *
  1898. * @returns {string} Returns CSS variable value.
  1899. */
  1900. getProperty(key) {
  1901. if (typeof key !== "string") {
  1902. throw new TypeError(`StyleManager error: 'key' is not a string.`);
  1903. }
  1904. return this.#cssRule.style.getPropertyValue(key);
  1905. }
  1906. /**
  1907. * Set rules by property / value; useful for CSS variables.
  1908. *
  1909. * @param {Object<string, string>} rules - An object with property / value string pairs to load.
  1910. *
  1911. * @param {boolean} [overwrite=true] - When true overwrites any existing values.
  1912. */
  1913. setProperties(rules, overwrite = true) {
  1914. if (!isObject(rules)) {
  1915. throw new TypeError(`StyleManager error: 'rules' is not an object.`);
  1916. }
  1917. if (typeof overwrite !== "boolean") {
  1918. throw new TypeError(`StyleManager error: 'overwrite' is not a boolean.`);
  1919. }
  1920. if (overwrite) {
  1921. for (const [key, value] of Object.entries(rules)) {
  1922. this.#cssRule.style.setProperty(key, value);
  1923. }
  1924. } else {
  1925. for (const [key, value] of Object.entries(rules)) {
  1926. if (this.#cssRule.style.getPropertyValue(key) === "") {
  1927. this.#cssRule.style.setProperty(key, value);
  1928. }
  1929. }
  1930. }
  1931. }
  1932. /**
  1933. * Sets a particular property.
  1934. *
  1935. * @param {string} key - CSS variable property key.
  1936. *
  1937. * @param {string} value - CSS variable value.
  1938. *
  1939. * @param {boolean} [overwrite=true] - Overwrite any existing value.
  1940. */
  1941. setProperty(key, value, overwrite = true) {
  1942. if (typeof key !== "string") {
  1943. throw new TypeError(`StyleManager error: 'key' is not a string.`);
  1944. }
  1945. if (typeof value !== "string") {
  1946. throw new TypeError(`StyleManager error: 'value' is not a string.`);
  1947. }
  1948. if (typeof overwrite !== "boolean") {
  1949. throw new TypeError(`StyleManager error: 'overwrite' is not a boolean.`);
  1950. }
  1951. if (overwrite) {
  1952. this.#cssRule.style.setProperty(key, value);
  1953. } else {
  1954. if (this.#cssRule.style.getPropertyValue(key) === "") {
  1955. this.#cssRule.style.setProperty(key, value);
  1956. }
  1957. }
  1958. }
  1959. /**
  1960. * Removes the property keys specified. If `keys` is an iterable list then all property keys in the list are removed.
  1961. *
  1962. * @param {Iterable<string>} keys - The property keys to remove.
  1963. */
  1964. removeProperties(keys) {
  1965. if (!isIterable(keys)) {
  1966. throw new TypeError(`StyleManager error: 'keys' is not an iterable list.`);
  1967. }
  1968. for (const key of keys) {
  1969. if (typeof key === "string") {
  1970. this.#cssRule.style.removeProperty(key);
  1971. }
  1972. }
  1973. }
  1974. /**
  1975. * Removes a particular CSS variable.
  1976. *
  1977. * @param {string} key - CSS variable property key.
  1978. *
  1979. * @returns {string} CSS variable value when removed.
  1980. */
  1981. removeProperty(key) {
  1982. if (typeof key !== "string") {
  1983. throw new TypeError(`StyleManager error: 'key' is not a string.`);
  1984. }
  1985. return this.#cssRule.style.removeProperty(key);
  1986. }
  1987. }
  1988. const s_REGEX = /(\d+)\s*px/;
  1989. function styleParsePixels(value) {
  1990. if (typeof value !== "string") {
  1991. return void 0;
  1992. }
  1993. const isPixels = s_REGEX.test(value);
  1994. const number = parseInt(value);
  1995. return isPixels && Number.isFinite(number) ? number : void 0;
  1996. }
  1997. const applicationShellContract = ["elementRoot"];
  1998. Object.freeze(applicationShellContract);
  1999. function isApplicationShell(component) {
  2000. if (component === null || component === void 0) {
  2001. return false;
  2002. }
  2003. let compHasContract = true;
  2004. let protoHasContract = true;
  2005. for (const accessor of applicationShellContract) {
  2006. const descriptor = Object.getOwnPropertyDescriptor(component, accessor);
  2007. if (descriptor === void 0 || descriptor.get === void 0 || descriptor.set === void 0) {
  2008. compHasContract = false;
  2009. }
  2010. }
  2011. const prototype = Object.getPrototypeOf(component);
  2012. for (const accessor of applicationShellContract) {
  2013. const descriptor = Object.getOwnPropertyDescriptor(prototype, accessor);
  2014. if (descriptor === void 0 || descriptor.get === void 0 || descriptor.set === void 0) {
  2015. protoHasContract = false;
  2016. }
  2017. }
  2018. return compHasContract || protoHasContract;
  2019. }
  2020. function isHMRProxy(comp) {
  2021. const instanceName = comp?.constructor?.name;
  2022. if (typeof instanceName === "string" && (instanceName.startsWith("Proxy<") || instanceName === "ProxyComponent")) {
  2023. return true;
  2024. }
  2025. const prototypeName = comp?.prototype?.constructor?.name;
  2026. return typeof prototypeName === "string" && (prototypeName.startsWith("Proxy<") || prototypeName === "ProxyComponent");
  2027. }
  2028. function isSvelteComponent(comp) {
  2029. if (comp === null || comp === void 0 || typeof comp !== "function") {
  2030. return false;
  2031. }
  2032. const prototypeName = comp?.prototype?.constructor?.name;
  2033. if (typeof prototypeName === "string" && (prototypeName.startsWith("Proxy<") || prototypeName === "ProxyComponent")) {
  2034. return true;
  2035. }
  2036. return typeof window !== void 0 ? typeof comp.prototype.$destroy === "function" && typeof comp.prototype.$on === "function" : (
  2037. // client-side
  2038. typeof comp.render === "function"
  2039. );
  2040. }
  2041. async function outroAndDestroy(instance2) {
  2042. return new Promise((resolve) => {
  2043. if (instance2.$$.fragment && instance2.$$.fragment.o) {
  2044. group_outros();
  2045. transition_out(instance2.$$.fragment, 0, 0, () => {
  2046. instance2.$destroy();
  2047. resolve();
  2048. });
  2049. check_outros();
  2050. } else {
  2051. instance2.$destroy();
  2052. resolve();
  2053. }
  2054. });
  2055. }
  2056. function parseSvelteConfig(config, thisArg = void 0) {
  2057. if (typeof config !== "object") {
  2058. throw new TypeError(`parseSvelteConfig - 'config' is not an object:
  2059. ${JSON.stringify(config)}.`);
  2060. }
  2061. if (!isSvelteComponent(config.class)) {
  2062. throw new TypeError(
  2063. `parseSvelteConfig - 'class' is not a Svelte component constructor for config:
  2064. ${JSON.stringify(config)}.`
  2065. );
  2066. }
  2067. if (config.hydrate !== void 0 && typeof config.hydrate !== "boolean") {
  2068. throw new TypeError(
  2069. `parseSvelteConfig - 'hydrate' is not a boolean for config:
  2070. ${JSON.stringify(config)}.`
  2071. );
  2072. }
  2073. if (config.intro !== void 0 && typeof config.intro !== "boolean") {
  2074. throw new TypeError(
  2075. `parseSvelteConfig - 'intro' is not a boolean for config:
  2076. ${JSON.stringify(config)}.`
  2077. );
  2078. }
  2079. if (config.target !== void 0 && typeof config.target !== "string" && !(config.target instanceof HTMLElement) && !(config.target instanceof ShadowRoot) && !(config.target instanceof DocumentFragment)) {
  2080. throw new TypeError(
  2081. `parseSvelteConfig - 'target' is not a string, HTMLElement, ShadowRoot, or DocumentFragment for config:
  2082. ${JSON.stringify(config)}.`
  2083. );
  2084. }
  2085. if (config.anchor !== void 0 && typeof config.anchor !== "string" && !(config.anchor instanceof HTMLElement) && !(config.anchor instanceof ShadowRoot) && !(config.anchor instanceof DocumentFragment)) {
  2086. throw new TypeError(
  2087. `parseSvelteConfig - 'anchor' is not a string, HTMLElement, ShadowRoot, or DocumentFragment for config:
  2088. ${JSON.stringify(config)}.`
  2089. );
  2090. }
  2091. if (config.context !== void 0 && typeof config.context !== "function" && !(config.context instanceof Map) && typeof config.context !== "object") {
  2092. throw new TypeError(
  2093. `parseSvelteConfig - 'context' is not a Map, function or object for config:
  2094. ${JSON.stringify(config)}.`
  2095. );
  2096. }
  2097. if (config.selectorTarget !== void 0 && typeof config.selectorTarget !== "string") {
  2098. throw new TypeError(
  2099. `parseSvelteConfig - 'selectorTarget' is not a string for config:
  2100. ${JSON.stringify(config)}.`
  2101. );
  2102. }
  2103. if (config.options !== void 0 && typeof config.options !== "object") {
  2104. throw new TypeError(
  2105. `parseSvelteConfig - 'options' is not an object for config:
  2106. ${JSON.stringify(config)}.`
  2107. );
  2108. }
  2109. if (config.options !== void 0) {
  2110. if (config.options.injectApp !== void 0 && typeof config.options.injectApp !== "boolean") {
  2111. throw new TypeError(
  2112. `parseSvelteConfig - 'options.injectApp' is not a boolean for config:
  2113. ${JSON.stringify(config)}.`
  2114. );
  2115. }
  2116. if (config.options.injectEventbus !== void 0 && typeof config.options.injectEventbus !== "boolean") {
  2117. throw new TypeError(
  2118. `parseSvelteConfig - 'options.injectEventbus' is not a boolean for config:
  2119. ${JSON.stringify(config)}.`
  2120. );
  2121. }
  2122. if (config.options.selectorElement !== void 0 && typeof config.options.selectorElement !== "string") {
  2123. throw new TypeError(
  2124. `parseSvelteConfig - 'selectorElement' is not a string for config:
  2125. ${JSON.stringify(config)}.`
  2126. );
  2127. }
  2128. }
  2129. const svelteConfig = { ...config };
  2130. delete svelteConfig.options;
  2131. let externalContext = {};
  2132. if (typeof svelteConfig.context === "function") {
  2133. const contextFunc = svelteConfig.context;
  2134. delete svelteConfig.context;
  2135. const result = contextFunc.call(thisArg);
  2136. if (isObject(result)) {
  2137. externalContext = { ...result };
  2138. } else {
  2139. throw new Error(`parseSvelteConfig - 'context' is a function that did not return an object for config:
  2140. ${JSON.stringify(config)}`);
  2141. }
  2142. } else if (svelteConfig.context instanceof Map) {
  2143. externalContext = Object.fromEntries(svelteConfig.context);
  2144. delete svelteConfig.context;
  2145. } else if (isObject(svelteConfig.context)) {
  2146. externalContext = svelteConfig.context;
  2147. delete svelteConfig.context;
  2148. }
  2149. svelteConfig.props = s_PROCESS_PROPS(svelteConfig.props, thisArg, config);
  2150. if (Array.isArray(svelteConfig.children)) {
  2151. const children2 = [];
  2152. for (let cntr = 0; cntr < svelteConfig.children.length; cntr++) {
  2153. const child = svelteConfig.children[cntr];
  2154. if (!isSvelteComponent(child.class)) {
  2155. throw new Error(`parseSvelteConfig - 'class' is not a Svelte component for child[${cntr}] for config:
  2156. ${JSON.stringify(config)}`);
  2157. }
  2158. child.props = s_PROCESS_PROPS(child.props, thisArg, config);
  2159. children2.push(child);
  2160. }
  2161. if (children2.length > 0) {
  2162. externalContext.children = children2;
  2163. }
  2164. delete svelteConfig.children;
  2165. } else if (isObject(svelteConfig.children)) {
  2166. if (!isSvelteComponent(svelteConfig.children.class)) {
  2167. throw new Error(`parseSvelteConfig - 'class' is not a Svelte component for children object for config:
  2168. ${JSON.stringify(config)}`);
  2169. }
  2170. svelteConfig.children.props = s_PROCESS_PROPS(svelteConfig.children.props, thisArg, config);
  2171. externalContext.children = [svelteConfig.children];
  2172. delete svelteConfig.children;
  2173. }
  2174. if (!(svelteConfig.context instanceof Map)) {
  2175. svelteConfig.context = /* @__PURE__ */ new Map();
  2176. }
  2177. svelteConfig.context.set("#external", externalContext);
  2178. return svelteConfig;
  2179. }
  2180. function s_PROCESS_PROPS(props, thisArg, config) {
  2181. if (typeof props === "function") {
  2182. const result = props.call(thisArg);
  2183. if (isObject(result)) {
  2184. return result;
  2185. } else {
  2186. throw new Error(`parseSvelteConfig - 'props' is a function that did not return an object for config:
  2187. ${JSON.stringify(config)}`);
  2188. }
  2189. } else if (isObject(props)) {
  2190. return props;
  2191. } else if (props !== void 0) {
  2192. throw new Error(
  2193. `parseSvelteConfig - 'props' is not a function or an object for config:
  2194. ${JSON.stringify(config)}`
  2195. );
  2196. }
  2197. return {};
  2198. }
  2199. function hasGetter(object, accessor) {
  2200. if (object === null || object === void 0) {
  2201. return false;
  2202. }
  2203. const iDescriptor = Object.getOwnPropertyDescriptor(object, accessor);
  2204. if (iDescriptor !== void 0 && iDescriptor.get !== void 0) {
  2205. return true;
  2206. }
  2207. for (let o = Object.getPrototypeOf(object); o; o = Object.getPrototypeOf(o)) {
  2208. const descriptor = Object.getOwnPropertyDescriptor(o, accessor);
  2209. if (descriptor !== void 0 && descriptor.get !== void 0) {
  2210. return true;
  2211. }
  2212. }
  2213. return false;
  2214. }
  2215. const subscriber_queue = [];
  2216. function readable(value, start) {
  2217. return {
  2218. subscribe: writable$1(value, start).subscribe
  2219. };
  2220. }
  2221. function writable$1(value, start = noop) {
  2222. let stop;
  2223. const subscribers = /* @__PURE__ */ new Set();
  2224. function set(new_value) {
  2225. if (safe_not_equal(value, new_value)) {
  2226. value = new_value;
  2227. if (stop) {
  2228. const run_queue = !subscriber_queue.length;
  2229. for (const subscriber of subscribers) {
  2230. subscriber[1]();
  2231. subscriber_queue.push(subscriber, value);
  2232. }
  2233. if (run_queue) {
  2234. for (let i = 0; i < subscriber_queue.length; i += 2) {
  2235. subscriber_queue[i][0](subscriber_queue[i + 1]);
  2236. }
  2237. subscriber_queue.length = 0;
  2238. }
  2239. }
  2240. }
  2241. }
  2242. function update2(fn) {
  2243. set(fn(value));
  2244. }
  2245. function subscribe2(run2, invalidate = noop) {
  2246. const subscriber = [run2, invalidate];
  2247. subscribers.add(subscriber);
  2248. if (subscribers.size === 1) {
  2249. stop = start(set) || noop;
  2250. }
  2251. run2(value);
  2252. return () => {
  2253. subscribers.delete(subscriber);
  2254. if (subscribers.size === 0) {
  2255. stop();
  2256. stop = null;
  2257. }
  2258. };
  2259. }
  2260. return { set, update: update2, subscribe: subscribe2 };
  2261. }
  2262. function derived(stores, fn, initial_value) {
  2263. const single = !Array.isArray(stores);
  2264. const stores_array = single ? [stores] : stores;
  2265. const auto = fn.length < 2;
  2266. return readable(initial_value, (set) => {
  2267. let inited = false;
  2268. const values = [];
  2269. let pending = 0;
  2270. let cleanup = noop;
  2271. const sync = () => {
  2272. if (pending) {
  2273. return;
  2274. }
  2275. cleanup();
  2276. const result = fn(single ? values[0] : values, set);
  2277. if (auto) {
  2278. set(result);
  2279. } else {
  2280. cleanup = is_function(result) ? result : noop;
  2281. }
  2282. };
  2283. const unsubscribers = stores_array.map((store, i) => subscribe(store, (value) => {
  2284. values[i] = value;
  2285. pending &= ~(1 << i);
  2286. if (inited) {
  2287. sync();
  2288. }
  2289. }, () => {
  2290. pending |= 1 << i;
  2291. }));
  2292. inited = true;
  2293. sync();
  2294. return function stop() {
  2295. run_all(unsubscribers);
  2296. cleanup();
  2297. };
  2298. });
  2299. }
  2300. function isSimpleDeriver(deriver) {
  2301. return deriver.length < 2;
  2302. }
  2303. function generator(storage2) {
  2304. function readable2(key, value, start) {
  2305. return {
  2306. subscribe: writable2(key, value, start).subscribe
  2307. };
  2308. }
  2309. function writable2(key, value, start = noop) {
  2310. function wrap_start(ogSet) {
  2311. return start(function wrap_set(new_value) {
  2312. if (storage2) {
  2313. storage2.setItem(key, JSON.stringify(new_value));
  2314. }
  2315. return ogSet(new_value);
  2316. });
  2317. }
  2318. if (storage2) {
  2319. const storageValue = storage2.getItem(key);
  2320. try {
  2321. if (storageValue) {
  2322. value = JSON.parse(storageValue);
  2323. }
  2324. } catch (err) {
  2325. }
  2326. storage2.setItem(key, JSON.stringify(value));
  2327. }
  2328. const ogStore = writable$1(value, start ? wrap_start : void 0);
  2329. function set(new_value) {
  2330. if (storage2) {
  2331. storage2.setItem(key, JSON.stringify(new_value));
  2332. }
  2333. ogStore.set(new_value);
  2334. }
  2335. function update2(fn) {
  2336. set(fn(get_store_value(ogStore)));
  2337. }
  2338. function subscribe2(run2, invalidate = noop) {
  2339. return ogStore.subscribe(run2, invalidate);
  2340. }
  2341. return { set, update: update2, subscribe: subscribe2 };
  2342. }
  2343. function derived2(key, stores, fn, initial_value) {
  2344. const single = !Array.isArray(stores);
  2345. const stores_array = single ? [stores] : stores;
  2346. if (storage2 && storage2.getItem(key)) {
  2347. try {
  2348. initial_value = JSON.parse(storage2.getItem(key));
  2349. } catch (err) {
  2350. }
  2351. }
  2352. return readable2(key, initial_value, (set) => {
  2353. let inited = false;
  2354. const values = [];
  2355. let pending = 0;
  2356. let cleanup = noop;
  2357. const sync = () => {
  2358. if (pending) {
  2359. return;
  2360. }
  2361. cleanup();
  2362. const input = single ? values[0] : values;
  2363. if (isSimpleDeriver(fn)) {
  2364. set(fn(input));
  2365. } else {
  2366. const result = fn(input, set);
  2367. cleanup = is_function(result) ? result : noop;
  2368. }
  2369. };
  2370. const unsubscribers = stores_array.map((store, i) => store.subscribe((value) => {
  2371. values[i] = value;
  2372. pending &= ~(1 << i);
  2373. if (inited) {
  2374. sync();
  2375. }
  2376. }, () => {
  2377. pending |= 1 << i;
  2378. }));
  2379. inited = true;
  2380. sync();
  2381. return function stop() {
  2382. run_all(unsubscribers);
  2383. cleanup();
  2384. };
  2385. });
  2386. }
  2387. return {
  2388. readable: readable2,
  2389. writable: writable2,
  2390. derived: derived2,
  2391. get: get_store_value
  2392. };
  2393. }
  2394. var storage = typeof window !== "undefined" ? window.sessionStorage : void 0;
  2395. var g = generator(storage);
  2396. var writable = g.writable;
  2397. class TJSSessionStorage {
  2398. /**
  2399. * @type {Map<string, import('svelte/store').Writable>}
  2400. */
  2401. #stores = /* @__PURE__ */ new Map();
  2402. /**
  2403. * Creates a new store for the given key.
  2404. *
  2405. * @param {string} key - Key to lookup in stores map.
  2406. *
  2407. * @param {boolean} [defaultValue] - A default value to set for the store.
  2408. *
  2409. * @returns {import('svelte/store').Writable} The new store.
  2410. */
  2411. static #createStore(key, defaultValue = void 0) {
  2412. try {
  2413. const value = sessionStorage.getItem(key);
  2414. if (value !== null) {
  2415. defaultValue = value === "undefined" ? void 0 : JSON.parse(value);
  2416. }
  2417. } catch (err) {
  2418. }
  2419. return writable(key, defaultValue);
  2420. }
  2421. /**
  2422. * Gets a store from the `stores` Map or creates a new store for the key and a given default value.
  2423. *
  2424. * @param {string} key - Key to lookup in stores map.
  2425. *
  2426. * @param {boolean} [defaultValue] - A default value to set for the store.
  2427. *
  2428. * @returns {import('svelte/store').Writable} The store for the given key.
  2429. */
  2430. #getStore(key, defaultValue = void 0) {
  2431. let store = this.#stores.get(key);
  2432. if (store === void 0) {
  2433. store = TJSSessionStorage.#createStore(key, defaultValue);
  2434. this.#stores.set(key, store);
  2435. }
  2436. return store;
  2437. }
  2438. /**
  2439. * Get value from the sessionStorage.
  2440. *
  2441. * @param {string} key - Key to lookup in sessionStorage.
  2442. *
  2443. * @param {*} [defaultValue] - A default value to return if key not present in session storage.
  2444. *
  2445. * @returns {*} Value from session storage or if not defined any default value provided.
  2446. */
  2447. getItem(key, defaultValue) {
  2448. let value = defaultValue;
  2449. const storageValue = sessionStorage.getItem(key);
  2450. if (storageValue !== null) {
  2451. try {
  2452. value = storageValue === "undefined" ? void 0 : JSON.parse(storageValue);
  2453. } catch (err) {
  2454. value = defaultValue;
  2455. }
  2456. } else if (defaultValue !== void 0) {
  2457. try {
  2458. const newValue = JSON.stringify(defaultValue);
  2459. sessionStorage.setItem(key, newValue === "undefined" ? void 0 : newValue);
  2460. } catch (err) {
  2461. }
  2462. }
  2463. return value;
  2464. }
  2465. /**
  2466. * Returns the backing Svelte store for the given key; potentially sets a default value if the key
  2467. * is not already set.
  2468. *
  2469. * @param {string} key - Key to lookup in sessionStorage.
  2470. *
  2471. * @param {*} [defaultValue] - A default value to return if key not present in session storage.
  2472. *
  2473. * @returns {import('svelte/store').Writable} The Svelte store for this key.
  2474. */
  2475. getStore(key, defaultValue) {
  2476. return this.#getStore(key, defaultValue);
  2477. }
  2478. /**
  2479. * Sets the value for the given key in sessionStorage.
  2480. *
  2481. * @param {string} key - Key to lookup in sessionStorage.
  2482. *
  2483. * @param {*} value - A value to set for this key.
  2484. */
  2485. setItem(key, value) {
  2486. const store = this.#getStore(key);
  2487. store.set(value);
  2488. }
  2489. /**
  2490. * Convenience method to swap a boolean value stored in session storage.
  2491. *
  2492. * @param {string} key - Key to lookup in sessionStorage.
  2493. *
  2494. * @param {boolean} [defaultValue] - A default value to return if key not present in session storage.
  2495. *
  2496. * @returns {boolean} The boolean swap for the given key.
  2497. */
  2498. swapItemBoolean(key, defaultValue) {
  2499. const store = this.#getStore(key, defaultValue);
  2500. let currentValue = false;
  2501. try {
  2502. currentValue = !!JSON.parse(sessionStorage.getItem(key));
  2503. } catch (err) {
  2504. }
  2505. const newValue = typeof currentValue === "boolean" ? !currentValue : false;
  2506. store.set(newValue);
  2507. return newValue;
  2508. }
  2509. }
  2510. function isUpdatableStore(store) {
  2511. if (store === null || store === void 0) {
  2512. return false;
  2513. }
  2514. switch (typeof store) {
  2515. case "function":
  2516. case "object":
  2517. return typeof store.subscribe === "function" && typeof store.update === "function";
  2518. }
  2519. return false;
  2520. }
  2521. function subscribeIgnoreFirst(store, update2) {
  2522. let firedFirst = false;
  2523. return store.subscribe((value) => {
  2524. if (!firedFirst) {
  2525. firedFirst = true;
  2526. } else {
  2527. update2(value);
  2528. }
  2529. });
  2530. }
  2531. function writableDerived(origins, derive, reflect, initial) {
  2532. var childDerivedSetter, originValues, blockNextDerive = false;
  2533. var reflectOldValues = reflect.length >= 2;
  2534. var wrappedDerive = (got, set) => {
  2535. childDerivedSetter = set;
  2536. if (reflectOldValues) {
  2537. originValues = got;
  2538. }
  2539. if (!blockNextDerive) {
  2540. let returned = derive(got, set);
  2541. if (derive.length < 2) {
  2542. set(returned);
  2543. } else {
  2544. return returned;
  2545. }
  2546. }
  2547. blockNextDerive = false;
  2548. };
  2549. var childDerived = derived(origins, wrappedDerive, initial);
  2550. var singleOrigin = !Array.isArray(origins);
  2551. function doReflect(reflecting) {
  2552. var setWith = reflect(reflecting, originValues);
  2553. if (singleOrigin) {
  2554. blockNextDerive = true;
  2555. origins.set(setWith);
  2556. } else {
  2557. setWith.forEach((value, i) => {
  2558. blockNextDerive = true;
  2559. origins[i].set(value);
  2560. });
  2561. }
  2562. blockNextDerive = false;
  2563. }
  2564. var tryingSet = false;
  2565. function update2(fn) {
  2566. var isUpdated, mutatedBySubscriptions, oldValue, newValue;
  2567. if (tryingSet) {
  2568. newValue = fn(get_store_value(childDerived));
  2569. childDerivedSetter(newValue);
  2570. return;
  2571. }
  2572. var unsubscribe = childDerived.subscribe((value) => {
  2573. if (!tryingSet) {
  2574. oldValue = value;
  2575. } else if (!isUpdated) {
  2576. isUpdated = true;
  2577. } else {
  2578. mutatedBySubscriptions = true;
  2579. }
  2580. });
  2581. newValue = fn(oldValue);
  2582. tryingSet = true;
  2583. childDerivedSetter(newValue);
  2584. unsubscribe();
  2585. tryingSet = false;
  2586. if (mutatedBySubscriptions) {
  2587. newValue = get_store_value(childDerived);
  2588. }
  2589. if (isUpdated) {
  2590. doReflect(newValue);
  2591. }
  2592. }
  2593. return {
  2594. subscribe: childDerived.subscribe,
  2595. set(value) {
  2596. update2(() => value);
  2597. },
  2598. update: update2
  2599. };
  2600. }
  2601. function propertyStore(origin, propName) {
  2602. if (!Array.isArray(propName)) {
  2603. return writableDerived(
  2604. origin,
  2605. (object) => object[propName],
  2606. (reflecting, object) => {
  2607. object[propName] = reflecting;
  2608. return object;
  2609. }
  2610. );
  2611. } else {
  2612. let props = propName.concat();
  2613. return writableDerived(
  2614. origin,
  2615. (value) => {
  2616. for (let i = 0; i < props.length; ++i) {
  2617. value = value[props[i]];
  2618. }
  2619. return value;
  2620. },
  2621. (reflecting, object) => {
  2622. let target = object;
  2623. for (let i = 0; i < props.length - 1; ++i) {
  2624. target = target[props[i]];
  2625. }
  2626. target[props[props.length - 1]] = reflecting;
  2627. return object;
  2628. }
  2629. );
  2630. }
  2631. }
  2632. const storeState = writable$1(void 0);
  2633. ({
  2634. subscribe: storeState.subscribe,
  2635. get: () => game
  2636. });
  2637. Hooks.once("ready", () => storeState.set(game));
  2638. function cubicOut(t) {
  2639. const f = t - 1;
  2640. return f * f * f + 1;
  2641. }
  2642. function lerp$5(start, end, amount) {
  2643. return (1 - amount) * start + amount * end;
  2644. }
  2645. function degToRad(deg) {
  2646. return deg * (Math.PI / 180);
  2647. }
  2648. var EPSILON = 1e-6;
  2649. var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array;
  2650. var RANDOM = Math.random;
  2651. if (!Math.hypot)
  2652. Math.hypot = function() {
  2653. var y = 0, i = arguments.length;
  2654. while (i--) {
  2655. y += arguments[i] * arguments[i];
  2656. }
  2657. return Math.sqrt(y);
  2658. };
  2659. function create$6() {
  2660. var out = new ARRAY_TYPE(9);
  2661. if (ARRAY_TYPE != Float32Array) {
  2662. out[1] = 0;
  2663. out[2] = 0;
  2664. out[3] = 0;
  2665. out[5] = 0;
  2666. out[6] = 0;
  2667. out[7] = 0;
  2668. }
  2669. out[0] = 1;
  2670. out[4] = 1;
  2671. out[8] = 1;
  2672. return out;
  2673. }
  2674. function create$5() {
  2675. var out = new ARRAY_TYPE(16);
  2676. if (ARRAY_TYPE != Float32Array) {
  2677. out[1] = 0;
  2678. out[2] = 0;
  2679. out[3] = 0;
  2680. out[4] = 0;
  2681. out[6] = 0;
  2682. out[7] = 0;
  2683. out[8] = 0;
  2684. out[9] = 0;
  2685. out[11] = 0;
  2686. out[12] = 0;
  2687. out[13] = 0;
  2688. out[14] = 0;
  2689. }
  2690. out[0] = 1;
  2691. out[5] = 1;
  2692. out[10] = 1;
  2693. out[15] = 1;
  2694. return out;
  2695. }
  2696. function clone$5(a) {
  2697. var out = new ARRAY_TYPE(16);
  2698. out[0] = a[0];
  2699. out[1] = a[1];
  2700. out[2] = a[2];
  2701. out[3] = a[3];
  2702. out[4] = a[4];
  2703. out[5] = a[5];
  2704. out[6] = a[6];
  2705. out[7] = a[7];
  2706. out[8] = a[8];
  2707. out[9] = a[9];
  2708. out[10] = a[10];
  2709. out[11] = a[11];
  2710. out[12] = a[12];
  2711. out[13] = a[13];
  2712. out[14] = a[14];
  2713. out[15] = a[15];
  2714. return out;
  2715. }
  2716. function copy$5(out, a) {
  2717. out[0] = a[0];
  2718. out[1] = a[1];
  2719. out[2] = a[2];
  2720. out[3] = a[3];
  2721. out[4] = a[4];
  2722. out[5] = a[5];
  2723. out[6] = a[6];
  2724. out[7] = a[7];
  2725. out[8] = a[8];
  2726. out[9] = a[9];
  2727. out[10] = a[10];
  2728. out[11] = a[11];
  2729. out[12] = a[12];
  2730. out[13] = a[13];
  2731. out[14] = a[14];
  2732. out[15] = a[15];
  2733. return out;
  2734. }
  2735. function fromValues$5(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
  2736. var out = new ARRAY_TYPE(16);
  2737. out[0] = m00;
  2738. out[1] = m01;
  2739. out[2] = m02;
  2740. out[3] = m03;
  2741. out[4] = m10;
  2742. out[5] = m11;
  2743. out[6] = m12;
  2744. out[7] = m13;
  2745. out[8] = m20;
  2746. out[9] = m21;
  2747. out[10] = m22;
  2748. out[11] = m23;
  2749. out[12] = m30;
  2750. out[13] = m31;
  2751. out[14] = m32;
  2752. out[15] = m33;
  2753. return out;
  2754. }
  2755. function set$5(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
  2756. out[0] = m00;
  2757. out[1] = m01;
  2758. out[2] = m02;
  2759. out[3] = m03;
  2760. out[4] = m10;
  2761. out[5] = m11;
  2762. out[6] = m12;
  2763. out[7] = m13;
  2764. out[8] = m20;
  2765. out[9] = m21;
  2766. out[10] = m22;
  2767. out[11] = m23;
  2768. out[12] = m30;
  2769. out[13] = m31;
  2770. out[14] = m32;
  2771. out[15] = m33;
  2772. return out;
  2773. }
  2774. function identity$2(out) {
  2775. out[0] = 1;
  2776. out[1] = 0;
  2777. out[2] = 0;
  2778. out[3] = 0;
  2779. out[4] = 0;
  2780. out[5] = 1;
  2781. out[6] = 0;
  2782. out[7] = 0;
  2783. out[8] = 0;
  2784. out[9] = 0;
  2785. out[10] = 1;
  2786. out[11] = 0;
  2787. out[12] = 0;
  2788. out[13] = 0;
  2789. out[14] = 0;
  2790. out[15] = 1;
  2791. return out;
  2792. }
  2793. function transpose(out, a) {
  2794. if (out === a) {
  2795. var a01 = a[1], a02 = a[2], a03 = a[3];
  2796. var a12 = a[6], a13 = a[7];
  2797. var a23 = a[11];
  2798. out[1] = a[4];
  2799. out[2] = a[8];
  2800. out[3] = a[12];
  2801. out[4] = a01;
  2802. out[6] = a[9];
  2803. out[7] = a[13];
  2804. out[8] = a02;
  2805. out[9] = a12;
  2806. out[11] = a[14];
  2807. out[12] = a03;
  2808. out[13] = a13;
  2809. out[14] = a23;
  2810. } else {
  2811. out[0] = a[0];
  2812. out[1] = a[4];
  2813. out[2] = a[8];
  2814. out[3] = a[12];
  2815. out[4] = a[1];
  2816. out[5] = a[5];
  2817. out[6] = a[9];
  2818. out[7] = a[13];
  2819. out[8] = a[2];
  2820. out[9] = a[6];
  2821. out[10] = a[10];
  2822. out[11] = a[14];
  2823. out[12] = a[3];
  2824. out[13] = a[7];
  2825. out[14] = a[11];
  2826. out[15] = a[15];
  2827. }
  2828. return out;
  2829. }
  2830. function invert$2(out, a) {
  2831. var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
  2832. var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
  2833. var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
  2834. var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
  2835. var b00 = a00 * a11 - a01 * a10;
  2836. var b01 = a00 * a12 - a02 * a10;
  2837. var b02 = a00 * a13 - a03 * a10;
  2838. var b03 = a01 * a12 - a02 * a11;
  2839. var b04 = a01 * a13 - a03 * a11;
  2840. var b05 = a02 * a13 - a03 * a12;
  2841. var b06 = a20 * a31 - a21 * a30;
  2842. var b07 = a20 * a32 - a22 * a30;
  2843. var b08 = a20 * a33 - a23 * a30;
  2844. var b09 = a21 * a32 - a22 * a31;
  2845. var b10 = a21 * a33 - a23 * a31;
  2846. var b11 = a22 * a33 - a23 * a32;
  2847. var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
  2848. if (!det) {
  2849. return null;
  2850. }
  2851. det = 1 / det;
  2852. out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
  2853. out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
  2854. out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
  2855. out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
  2856. out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
  2857. out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
  2858. out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
  2859. out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
  2860. out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
  2861. out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
  2862. out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
  2863. out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
  2864. out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
  2865. out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
  2866. out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
  2867. out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
  2868. return out;
  2869. }
  2870. function adjoint(out, a) {
  2871. var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
  2872. var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
  2873. var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
  2874. var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
  2875. out[0] = a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22);
  2876. out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
  2877. out[2] = a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12);
  2878. out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
  2879. out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
  2880. out[5] = a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22);
  2881. out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
  2882. out[7] = a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12);
  2883. out[8] = a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21);
  2884. out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
  2885. out[10] = a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11);
  2886. out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
  2887. out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
  2888. out[13] = a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21);
  2889. out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
  2890. out[15] = a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11);
  2891. return out;
  2892. }
  2893. function determinant(a) {
  2894. var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
  2895. var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
  2896. var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
  2897. var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
  2898. var b00 = a00 * a11 - a01 * a10;
  2899. var b01 = a00 * a12 - a02 * a10;
  2900. var b02 = a00 * a13 - a03 * a10;
  2901. var b03 = a01 * a12 - a02 * a11;
  2902. var b04 = a01 * a13 - a03 * a11;
  2903. var b05 = a02 * a13 - a03 * a12;
  2904. var b06 = a20 * a31 - a21 * a30;
  2905. var b07 = a20 * a32 - a22 * a30;
  2906. var b08 = a20 * a33 - a23 * a30;
  2907. var b09 = a21 * a32 - a22 * a31;
  2908. var b10 = a21 * a33 - a23 * a31;
  2909. var b11 = a22 * a33 - a23 * a32;
  2910. return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
  2911. }
  2912. function multiply$5(out, a, b) {
  2913. var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
  2914. var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
  2915. var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
  2916. var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
  2917. var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
  2918. out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  2919. out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  2920. out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  2921. out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  2922. b0 = b[4];
  2923. b1 = b[5];
  2924. b2 = b[6];
  2925. b3 = b[7];
  2926. out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  2927. out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  2928. out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  2929. out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  2930. b0 = b[8];
  2931. b1 = b[9];
  2932. b2 = b[10];
  2933. b3 = b[11];
  2934. out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  2935. out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  2936. out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  2937. out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  2938. b0 = b[12];
  2939. b1 = b[13];
  2940. b2 = b[14];
  2941. b3 = b[15];
  2942. out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
  2943. out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
  2944. out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
  2945. out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
  2946. return out;
  2947. }
  2948. function translate$1(out, a, v) {
  2949. var x = v[0], y = v[1], z = v[2];
  2950. var a00, a01, a02, a03;
  2951. var a10, a11, a12, a13;
  2952. var a20, a21, a22, a23;
  2953. if (a === out) {
  2954. out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
  2955. out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
  2956. out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
  2957. out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
  2958. } else {
  2959. a00 = a[0];
  2960. a01 = a[1];
  2961. a02 = a[2];
  2962. a03 = a[3];
  2963. a10 = a[4];
  2964. a11 = a[5];
  2965. a12 = a[6];
  2966. a13 = a[7];
  2967. a20 = a[8];
  2968. a21 = a[9];
  2969. a22 = a[10];
  2970. a23 = a[11];
  2971. out[0] = a00;
  2972. out[1] = a01;
  2973. out[2] = a02;
  2974. out[3] = a03;
  2975. out[4] = a10;
  2976. out[5] = a11;
  2977. out[6] = a12;
  2978. out[7] = a13;
  2979. out[8] = a20;
  2980. out[9] = a21;
  2981. out[10] = a22;
  2982. out[11] = a23;
  2983. out[12] = a00 * x + a10 * y + a20 * z + a[12];
  2984. out[13] = a01 * x + a11 * y + a21 * z + a[13];
  2985. out[14] = a02 * x + a12 * y + a22 * z + a[14];
  2986. out[15] = a03 * x + a13 * y + a23 * z + a[15];
  2987. }
  2988. return out;
  2989. }
  2990. function scale$5(out, a, v) {
  2991. var x = v[0], y = v[1], z = v[2];
  2992. out[0] = a[0] * x;
  2993. out[1] = a[1] * x;
  2994. out[2] = a[2] * x;
  2995. out[3] = a[3] * x;
  2996. out[4] = a[4] * y;
  2997. out[5] = a[5] * y;
  2998. out[6] = a[6] * y;
  2999. out[7] = a[7] * y;
  3000. out[8] = a[8] * z;
  3001. out[9] = a[9] * z;
  3002. out[10] = a[10] * z;
  3003. out[11] = a[11] * z;
  3004. out[12] = a[12];
  3005. out[13] = a[13];
  3006. out[14] = a[14];
  3007. out[15] = a[15];
  3008. return out;
  3009. }
  3010. function rotate$1(out, a, rad, axis) {
  3011. var x = axis[0], y = axis[1], z = axis[2];
  3012. var len = Math.hypot(x, y, z);
  3013. var s, c, t;
  3014. var a00, a01, a02, a03;
  3015. var a10, a11, a12, a13;
  3016. var a20, a21, a22, a23;
  3017. var b00, b01, b02;
  3018. var b10, b11, b12;
  3019. var b20, b21, b22;
  3020. if (len < EPSILON) {
  3021. return null;
  3022. }
  3023. len = 1 / len;
  3024. x *= len;
  3025. y *= len;
  3026. z *= len;
  3027. s = Math.sin(rad);
  3028. c = Math.cos(rad);
  3029. t = 1 - c;
  3030. a00 = a[0];
  3031. a01 = a[1];
  3032. a02 = a[2];
  3033. a03 = a[3];
  3034. a10 = a[4];
  3035. a11 = a[5];
  3036. a12 = a[6];
  3037. a13 = a[7];
  3038. a20 = a[8];
  3039. a21 = a[9];
  3040. a22 = a[10];
  3041. a23 = a[11];
  3042. b00 = x * x * t + c;
  3043. b01 = y * x * t + z * s;
  3044. b02 = z * x * t - y * s;
  3045. b10 = x * y * t - z * s;
  3046. b11 = y * y * t + c;
  3047. b12 = z * y * t + x * s;
  3048. b20 = x * z * t + y * s;
  3049. b21 = y * z * t - x * s;
  3050. b22 = z * z * t + c;
  3051. out[0] = a00 * b00 + a10 * b01 + a20 * b02;
  3052. out[1] = a01 * b00 + a11 * b01 + a21 * b02;
  3053. out[2] = a02 * b00 + a12 * b01 + a22 * b02;
  3054. out[3] = a03 * b00 + a13 * b01 + a23 * b02;
  3055. out[4] = a00 * b10 + a10 * b11 + a20 * b12;
  3056. out[5] = a01 * b10 + a11 * b11 + a21 * b12;
  3057. out[6] = a02 * b10 + a12 * b11 + a22 * b12;
  3058. out[7] = a03 * b10 + a13 * b11 + a23 * b12;
  3059. out[8] = a00 * b20 + a10 * b21 + a20 * b22;
  3060. out[9] = a01 * b20 + a11 * b21 + a21 * b22;
  3061. out[10] = a02 * b20 + a12 * b21 + a22 * b22;
  3062. out[11] = a03 * b20 + a13 * b21 + a23 * b22;
  3063. if (a !== out) {
  3064. out[12] = a[12];
  3065. out[13] = a[13];
  3066. out[14] = a[14];
  3067. out[15] = a[15];
  3068. }
  3069. return out;
  3070. }
  3071. function rotateX$3(out, a, rad) {
  3072. var s = Math.sin(rad);
  3073. var c = Math.cos(rad);
  3074. var a10 = a[4];
  3075. var a11 = a[5];
  3076. var a12 = a[6];
  3077. var a13 = a[7];
  3078. var a20 = a[8];
  3079. var a21 = a[9];
  3080. var a22 = a[10];
  3081. var a23 = a[11];
  3082. if (a !== out) {
  3083. out[0] = a[0];
  3084. out[1] = a[1];
  3085. out[2] = a[2];
  3086. out[3] = a[3];
  3087. out[12] = a[12];
  3088. out[13] = a[13];
  3089. out[14] = a[14];
  3090. out[15] = a[15];
  3091. }
  3092. out[4] = a10 * c + a20 * s;
  3093. out[5] = a11 * c + a21 * s;
  3094. out[6] = a12 * c + a22 * s;
  3095. out[7] = a13 * c + a23 * s;
  3096. out[8] = a20 * c - a10 * s;
  3097. out[9] = a21 * c - a11 * s;
  3098. out[10] = a22 * c - a12 * s;
  3099. out[11] = a23 * c - a13 * s;
  3100. return out;
  3101. }
  3102. function rotateY$3(out, a, rad) {
  3103. var s = Math.sin(rad);
  3104. var c = Math.cos(rad);
  3105. var a00 = a[0];
  3106. var a01 = a[1];
  3107. var a02 = a[2];
  3108. var a03 = a[3];
  3109. var a20 = a[8];
  3110. var a21 = a[9];
  3111. var a22 = a[10];
  3112. var a23 = a[11];
  3113. if (a !== out) {
  3114. out[4] = a[4];
  3115. out[5] = a[5];
  3116. out[6] = a[6];
  3117. out[7] = a[7];
  3118. out[12] = a[12];
  3119. out[13] = a[13];
  3120. out[14] = a[14];
  3121. out[15] = a[15];
  3122. }
  3123. out[0] = a00 * c - a20 * s;
  3124. out[1] = a01 * c - a21 * s;
  3125. out[2] = a02 * c - a22 * s;
  3126. out[3] = a03 * c - a23 * s;
  3127. out[8] = a00 * s + a20 * c;
  3128. out[9] = a01 * s + a21 * c;
  3129. out[10] = a02 * s + a22 * c;
  3130. out[11] = a03 * s + a23 * c;
  3131. return out;
  3132. }
  3133. function rotateZ$3(out, a, rad) {
  3134. var s = Math.sin(rad);
  3135. var c = Math.cos(rad);
  3136. var a00 = a[0];
  3137. var a01 = a[1];
  3138. var a02 = a[2];
  3139. var a03 = a[3];
  3140. var a10 = a[4];
  3141. var a11 = a[5];
  3142. var a12 = a[6];
  3143. var a13 = a[7];
  3144. if (a !== out) {
  3145. out[8] = a[8];
  3146. out[9] = a[9];
  3147. out[10] = a[10];
  3148. out[11] = a[11];
  3149. out[12] = a[12];
  3150. out[13] = a[13];
  3151. out[14] = a[14];
  3152. out[15] = a[15];
  3153. }
  3154. out[0] = a00 * c + a10 * s;
  3155. out[1] = a01 * c + a11 * s;
  3156. out[2] = a02 * c + a12 * s;
  3157. out[3] = a03 * c + a13 * s;
  3158. out[4] = a10 * c - a00 * s;
  3159. out[5] = a11 * c - a01 * s;
  3160. out[6] = a12 * c - a02 * s;
  3161. out[7] = a13 * c - a03 * s;
  3162. return out;
  3163. }
  3164. function fromTranslation$1(out, v) {
  3165. out[0] = 1;
  3166. out[1] = 0;
  3167. out[2] = 0;
  3168. out[3] = 0;
  3169. out[4] = 0;
  3170. out[5] = 1;
  3171. out[6] = 0;
  3172. out[7] = 0;
  3173. out[8] = 0;
  3174. out[9] = 0;
  3175. out[10] = 1;
  3176. out[11] = 0;
  3177. out[12] = v[0];
  3178. out[13] = v[1];
  3179. out[14] = v[2];
  3180. out[15] = 1;
  3181. return out;
  3182. }
  3183. function fromScaling(out, v) {
  3184. out[0] = v[0];
  3185. out[1] = 0;
  3186. out[2] = 0;
  3187. out[3] = 0;
  3188. out[4] = 0;
  3189. out[5] = v[1];
  3190. out[6] = 0;
  3191. out[7] = 0;
  3192. out[8] = 0;
  3193. out[9] = 0;
  3194. out[10] = v[2];
  3195. out[11] = 0;
  3196. out[12] = 0;
  3197. out[13] = 0;
  3198. out[14] = 0;
  3199. out[15] = 1;
  3200. return out;
  3201. }
  3202. function fromRotation$1(out, rad, axis) {
  3203. var x = axis[0], y = axis[1], z = axis[2];
  3204. var len = Math.hypot(x, y, z);
  3205. var s, c, t;
  3206. if (len < EPSILON) {
  3207. return null;
  3208. }
  3209. len = 1 / len;
  3210. x *= len;
  3211. y *= len;
  3212. z *= len;
  3213. s = Math.sin(rad);
  3214. c = Math.cos(rad);
  3215. t = 1 - c;
  3216. out[0] = x * x * t + c;
  3217. out[1] = y * x * t + z * s;
  3218. out[2] = z * x * t - y * s;
  3219. out[3] = 0;
  3220. out[4] = x * y * t - z * s;
  3221. out[5] = y * y * t + c;
  3222. out[6] = z * y * t + x * s;
  3223. out[7] = 0;
  3224. out[8] = x * z * t + y * s;
  3225. out[9] = y * z * t - x * s;
  3226. out[10] = z * z * t + c;
  3227. out[11] = 0;
  3228. out[12] = 0;
  3229. out[13] = 0;
  3230. out[14] = 0;
  3231. out[15] = 1;
  3232. return out;
  3233. }
  3234. function fromXRotation(out, rad) {
  3235. var s = Math.sin(rad);
  3236. var c = Math.cos(rad);
  3237. out[0] = 1;
  3238. out[1] = 0;
  3239. out[2] = 0;
  3240. out[3] = 0;
  3241. out[4] = 0;
  3242. out[5] = c;
  3243. out[6] = s;
  3244. out[7] = 0;
  3245. out[8] = 0;
  3246. out[9] = -s;
  3247. out[10] = c;
  3248. out[11] = 0;
  3249. out[12] = 0;
  3250. out[13] = 0;
  3251. out[14] = 0;
  3252. out[15] = 1;
  3253. return out;
  3254. }
  3255. function fromYRotation(out, rad) {
  3256. var s = Math.sin(rad);
  3257. var c = Math.cos(rad);
  3258. out[0] = c;
  3259. out[1] = 0;
  3260. out[2] = -s;
  3261. out[3] = 0;
  3262. out[4] = 0;
  3263. out[5] = 1;
  3264. out[6] = 0;
  3265. out[7] = 0;
  3266. out[8] = s;
  3267. out[9] = 0;
  3268. out[10] = c;
  3269. out[11] = 0;
  3270. out[12] = 0;
  3271. out[13] = 0;
  3272. out[14] = 0;
  3273. out[15] = 1;
  3274. return out;
  3275. }
  3276. function fromZRotation(out, rad) {
  3277. var s = Math.sin(rad);
  3278. var c = Math.cos(rad);
  3279. out[0] = c;
  3280. out[1] = s;
  3281. out[2] = 0;
  3282. out[3] = 0;
  3283. out[4] = -s;
  3284. out[5] = c;
  3285. out[6] = 0;
  3286. out[7] = 0;
  3287. out[8] = 0;
  3288. out[9] = 0;
  3289. out[10] = 1;
  3290. out[11] = 0;
  3291. out[12] = 0;
  3292. out[13] = 0;
  3293. out[14] = 0;
  3294. out[15] = 1;
  3295. return out;
  3296. }
  3297. function fromRotationTranslation$1(out, q, v) {
  3298. var x = q[0], y = q[1], z = q[2], w = q[3];
  3299. var x2 = x + x;
  3300. var y2 = y + y;
  3301. var z2 = z + z;
  3302. var xx = x * x2;
  3303. var xy = x * y2;
  3304. var xz = x * z2;
  3305. var yy = y * y2;
  3306. var yz = y * z2;
  3307. var zz = z * z2;
  3308. var wx = w * x2;
  3309. var wy = w * y2;
  3310. var wz = w * z2;
  3311. out[0] = 1 - (yy + zz);
  3312. out[1] = xy + wz;
  3313. out[2] = xz - wy;
  3314. out[3] = 0;
  3315. out[4] = xy - wz;
  3316. out[5] = 1 - (xx + zz);
  3317. out[6] = yz + wx;
  3318. out[7] = 0;
  3319. out[8] = xz + wy;
  3320. out[9] = yz - wx;
  3321. out[10] = 1 - (xx + yy);
  3322. out[11] = 0;
  3323. out[12] = v[0];
  3324. out[13] = v[1];
  3325. out[14] = v[2];
  3326. out[15] = 1;
  3327. return out;
  3328. }
  3329. function fromQuat2(out, a) {
  3330. var translation = new ARRAY_TYPE(3);
  3331. 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];
  3332. var magnitude = bx * bx + by * by + bz * bz + bw * bw;
  3333. if (magnitude > 0) {
  3334. translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude;
  3335. translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude;
  3336. translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude;
  3337. } else {
  3338. translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
  3339. translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
  3340. translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
  3341. }
  3342. fromRotationTranslation$1(out, a, translation);
  3343. return out;
  3344. }
  3345. function getTranslation$1(out, mat) {
  3346. out[0] = mat[12];
  3347. out[1] = mat[13];
  3348. out[2] = mat[14];
  3349. return out;
  3350. }
  3351. function getScaling(out, mat) {
  3352. var m11 = mat[0];
  3353. var m12 = mat[1];
  3354. var m13 = mat[2];
  3355. var m21 = mat[4];
  3356. var m22 = mat[5];
  3357. var m23 = mat[6];
  3358. var m31 = mat[8];
  3359. var m32 = mat[9];
  3360. var m33 = mat[10];
  3361. out[0] = Math.hypot(m11, m12, m13);
  3362. out[1] = Math.hypot(m21, m22, m23);
  3363. out[2] = Math.hypot(m31, m32, m33);
  3364. return out;
  3365. }
  3366. function getRotation(out, mat) {
  3367. var scaling = new ARRAY_TYPE(3);
  3368. getScaling(scaling, mat);
  3369. var is1 = 1 / scaling[0];
  3370. var is2 = 1 / scaling[1];
  3371. var is3 = 1 / scaling[2];
  3372. var sm11 = mat[0] * is1;
  3373. var sm12 = mat[1] * is2;
  3374. var sm13 = mat[2] * is3;
  3375. var sm21 = mat[4] * is1;
  3376. var sm22 = mat[5] * is2;
  3377. var sm23 = mat[6] * is3;
  3378. var sm31 = mat[8] * is1;
  3379. var sm32 = mat[9] * is2;
  3380. var sm33 = mat[10] * is3;
  3381. var trace = sm11 + sm22 + sm33;
  3382. var S = 0;
  3383. if (trace > 0) {
  3384. S = Math.sqrt(trace + 1) * 2;
  3385. out[3] = 0.25 * S;
  3386. out[0] = (sm23 - sm32) / S;
  3387. out[1] = (sm31 - sm13) / S;
  3388. out[2] = (sm12 - sm21) / S;
  3389. } else if (sm11 > sm22 && sm11 > sm33) {
  3390. S = Math.sqrt(1 + sm11 - sm22 - sm33) * 2;
  3391. out[3] = (sm23 - sm32) / S;
  3392. out[0] = 0.25 * S;
  3393. out[1] = (sm12 + sm21) / S;
  3394. out[2] = (sm31 + sm13) / S;
  3395. } else if (sm22 > sm33) {
  3396. S = Math.sqrt(1 + sm22 - sm11 - sm33) * 2;
  3397. out[3] = (sm31 - sm13) / S;
  3398. out[0] = (sm12 + sm21) / S;
  3399. out[1] = 0.25 * S;
  3400. out[2] = (sm23 + sm32) / S;
  3401. } else {
  3402. S = Math.sqrt(1 + sm33 - sm11 - sm22) * 2;
  3403. out[3] = (sm12 - sm21) / S;
  3404. out[0] = (sm31 + sm13) / S;
  3405. out[1] = (sm23 + sm32) / S;
  3406. out[2] = 0.25 * S;
  3407. }
  3408. return out;
  3409. }
  3410. function fromRotationTranslationScale(out, q, v, s) {
  3411. var x = q[0], y = q[1], z = q[2], w = q[3];
  3412. var x2 = x + x;
  3413. var y2 = y + y;
  3414. var z2 = z + z;
  3415. var xx = x * x2;
  3416. var xy = x * y2;
  3417. var xz = x * z2;
  3418. var yy = y * y2;
  3419. var yz = y * z2;
  3420. var zz = z * z2;
  3421. var wx = w * x2;
  3422. var wy = w * y2;
  3423. var wz = w * z2;
  3424. var sx = s[0];
  3425. var sy = s[1];
  3426. var sz = s[2];
  3427. out[0] = (1 - (yy + zz)) * sx;
  3428. out[1] = (xy + wz) * sx;
  3429. out[2] = (xz - wy) * sx;
  3430. out[3] = 0;
  3431. out[4] = (xy - wz) * sy;
  3432. out[5] = (1 - (xx + zz)) * sy;
  3433. out[6] = (yz + wx) * sy;
  3434. out[7] = 0;
  3435. out[8] = (xz + wy) * sz;
  3436. out[9] = (yz - wx) * sz;
  3437. out[10] = (1 - (xx + yy)) * sz;
  3438. out[11] = 0;
  3439. out[12] = v[0];
  3440. out[13] = v[1];
  3441. out[14] = v[2];
  3442. out[15] = 1;
  3443. return out;
  3444. }
  3445. function fromRotationTranslationScaleOrigin(out, q, v, s, o) {
  3446. var x = q[0], y = q[1], z = q[2], w = q[3];
  3447. var x2 = x + x;
  3448. var y2 = y + y;
  3449. var z2 = z + z;
  3450. var xx = x * x2;
  3451. var xy = x * y2;
  3452. var xz = x * z2;
  3453. var yy = y * y2;
  3454. var yz = y * z2;
  3455. var zz = z * z2;
  3456. var wx = w * x2;
  3457. var wy = w * y2;
  3458. var wz = w * z2;
  3459. var sx = s[0];
  3460. var sy = s[1];
  3461. var sz = s[2];
  3462. var ox = o[0];
  3463. var oy = o[1];
  3464. var oz = o[2];
  3465. var out0 = (1 - (yy + zz)) * sx;
  3466. var out1 = (xy + wz) * sx;
  3467. var out2 = (xz - wy) * sx;
  3468. var out4 = (xy - wz) * sy;
  3469. var out5 = (1 - (xx + zz)) * sy;
  3470. var out6 = (yz + wx) * sy;
  3471. var out8 = (xz + wy) * sz;
  3472. var out9 = (yz - wx) * sz;
  3473. var out10 = (1 - (xx + yy)) * sz;
  3474. out[0] = out0;
  3475. out[1] = out1;
  3476. out[2] = out2;
  3477. out[3] = 0;
  3478. out[4] = out4;
  3479. out[5] = out5;
  3480. out[6] = out6;
  3481. out[7] = 0;
  3482. out[8] = out8;
  3483. out[9] = out9;
  3484. out[10] = out10;
  3485. out[11] = 0;
  3486. out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz);
  3487. out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz);
  3488. out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz);
  3489. out[15] = 1;
  3490. return out;
  3491. }
  3492. function fromQuat(out, q) {
  3493. var x = q[0], y = q[1], z = q[2], w = q[3];
  3494. var x2 = x + x;
  3495. var y2 = y + y;
  3496. var z2 = z + z;
  3497. var xx = x * x2;
  3498. var yx = y * x2;
  3499. var yy = y * y2;
  3500. var zx = z * x2;
  3501. var zy = z * y2;
  3502. var zz = z * z2;
  3503. var wx = w * x2;
  3504. var wy = w * y2;
  3505. var wz = w * z2;
  3506. out[0] = 1 - yy - zz;
  3507. out[1] = yx + wz;
  3508. out[2] = zx - wy;
  3509. out[3] = 0;
  3510. out[4] = yx - wz;
  3511. out[5] = 1 - xx - zz;
  3512. out[6] = zy + wx;
  3513. out[7] = 0;
  3514. out[8] = zx + wy;
  3515. out[9] = zy - wx;
  3516. out[10] = 1 - xx - yy;
  3517. out[11] = 0;
  3518. out[12] = 0;
  3519. out[13] = 0;
  3520. out[14] = 0;
  3521. out[15] = 1;
  3522. return out;
  3523. }
  3524. function frustum(out, left, right, bottom, top, near, far) {
  3525. var rl = 1 / (right - left);
  3526. var tb = 1 / (top - bottom);
  3527. var nf = 1 / (near - far);
  3528. out[0] = near * 2 * rl;
  3529. out[1] = 0;
  3530. out[2] = 0;
  3531. out[3] = 0;
  3532. out[4] = 0;
  3533. out[5] = near * 2 * tb;
  3534. out[6] = 0;
  3535. out[7] = 0;
  3536. out[8] = (right + left) * rl;
  3537. out[9] = (top + bottom) * tb;
  3538. out[10] = (far + near) * nf;
  3539. out[11] = -1;
  3540. out[12] = 0;
  3541. out[13] = 0;
  3542. out[14] = far * near * 2 * nf;
  3543. out[15] = 0;
  3544. return out;
  3545. }
  3546. function perspectiveNO(out, fovy, aspect, near, far) {
  3547. var f = 1 / Math.tan(fovy / 2), nf;
  3548. out[0] = f / aspect;
  3549. out[1] = 0;
  3550. out[2] = 0;
  3551. out[3] = 0;
  3552. out[4] = 0;
  3553. out[5] = f;
  3554. out[6] = 0;
  3555. out[7] = 0;
  3556. out[8] = 0;
  3557. out[9] = 0;
  3558. out[11] = -1;
  3559. out[12] = 0;
  3560. out[13] = 0;
  3561. out[15] = 0;
  3562. if (far != null && far !== Infinity) {
  3563. nf = 1 / (near - far);
  3564. out[10] = (far + near) * nf;
  3565. out[14] = 2 * far * near * nf;
  3566. } else {
  3567. out[10] = -1;
  3568. out[14] = -2 * near;
  3569. }
  3570. return out;
  3571. }
  3572. var perspective = perspectiveNO;
  3573. function perspectiveZO(out, fovy, aspect, near, far) {
  3574. var f = 1 / Math.tan(fovy / 2), nf;
  3575. out[0] = f / aspect;
  3576. out[1] = 0;
  3577. out[2] = 0;
  3578. out[3] = 0;
  3579. out[4] = 0;
  3580. out[5] = f;
  3581. out[6] = 0;
  3582. out[7] = 0;
  3583. out[8] = 0;
  3584. out[9] = 0;
  3585. out[11] = -1;
  3586. out[12] = 0;
  3587. out[13] = 0;
  3588. out[15] = 0;
  3589. if (far != null && far !== Infinity) {
  3590. nf = 1 / (near - far);
  3591. out[10] = far * nf;
  3592. out[14] = far * near * nf;
  3593. } else {
  3594. out[10] = -1;
  3595. out[14] = -near;
  3596. }
  3597. return out;
  3598. }
  3599. function perspectiveFromFieldOfView(out, fov, near, far) {
  3600. var upTan = Math.tan(fov.upDegrees * Math.PI / 180);
  3601. var downTan = Math.tan(fov.downDegrees * Math.PI / 180);
  3602. var leftTan = Math.tan(fov.leftDegrees * Math.PI / 180);
  3603. var rightTan = Math.tan(fov.rightDegrees * Math.PI / 180);
  3604. var xScale = 2 / (leftTan + rightTan);
  3605. var yScale = 2 / (upTan + downTan);
  3606. out[0] = xScale;
  3607. out[1] = 0;
  3608. out[2] = 0;
  3609. out[3] = 0;
  3610. out[4] = 0;
  3611. out[5] = yScale;
  3612. out[6] = 0;
  3613. out[7] = 0;
  3614. out[8] = -((leftTan - rightTan) * xScale * 0.5);
  3615. out[9] = (upTan - downTan) * yScale * 0.5;
  3616. out[10] = far / (near - far);
  3617. out[11] = -1;
  3618. out[12] = 0;
  3619. out[13] = 0;
  3620. out[14] = far * near / (near - far);
  3621. out[15] = 0;
  3622. return out;
  3623. }
  3624. function orthoNO(out, left, right, bottom, top, near, far) {
  3625. var lr = 1 / (left - right);
  3626. var bt = 1 / (bottom - top);
  3627. var nf = 1 / (near - far);
  3628. out[0] = -2 * lr;
  3629. out[1] = 0;
  3630. out[2] = 0;
  3631. out[3] = 0;
  3632. out[4] = 0;
  3633. out[5] = -2 * bt;
  3634. out[6] = 0;
  3635. out[7] = 0;
  3636. out[8] = 0;
  3637. out[9] = 0;
  3638. out[10] = 2 * nf;
  3639. out[11] = 0;
  3640. out[12] = (left + right) * lr;
  3641. out[13] = (top + bottom) * bt;
  3642. out[14] = (far + near) * nf;
  3643. out[15] = 1;
  3644. return out;
  3645. }
  3646. var ortho = orthoNO;
  3647. function orthoZO(out, left, right, bottom, top, near, far) {
  3648. var lr = 1 / (left - right);
  3649. var bt = 1 / (bottom - top);
  3650. var nf = 1 / (near - far);
  3651. out[0] = -2 * lr;
  3652. out[1] = 0;
  3653. out[2] = 0;
  3654. out[3] = 0;
  3655. out[4] = 0;
  3656. out[5] = -2 * bt;
  3657. out[6] = 0;
  3658. out[7] = 0;
  3659. out[8] = 0;
  3660. out[9] = 0;
  3661. out[10] = nf;
  3662. out[11] = 0;
  3663. out[12] = (left + right) * lr;
  3664. out[13] = (top + bottom) * bt;
  3665. out[14] = near * nf;
  3666. out[15] = 1;
  3667. return out;
  3668. }
  3669. function lookAt(out, eye, center, up) {
  3670. var x0, x1, x2, y0, y1, y2, z0, z1, z2, len;
  3671. var eyex = eye[0];
  3672. var eyey = eye[1];
  3673. var eyez = eye[2];
  3674. var upx = up[0];
  3675. var upy = up[1];
  3676. var upz = up[2];
  3677. var centerx = center[0];
  3678. var centery = center[1];
  3679. var centerz = center[2];
  3680. if (Math.abs(eyex - centerx) < EPSILON && Math.abs(eyey - centery) < EPSILON && Math.abs(eyez - centerz) < EPSILON) {
  3681. return identity$2(out);
  3682. }
  3683. z0 = eyex - centerx;
  3684. z1 = eyey - centery;
  3685. z2 = eyez - centerz;
  3686. len = 1 / Math.hypot(z0, z1, z2);
  3687. z0 *= len;
  3688. z1 *= len;
  3689. z2 *= len;
  3690. x0 = upy * z2 - upz * z1;
  3691. x1 = upz * z0 - upx * z2;
  3692. x2 = upx * z1 - upy * z0;
  3693. len = Math.hypot(x0, x1, x2);
  3694. if (!len) {
  3695. x0 = 0;
  3696. x1 = 0;
  3697. x2 = 0;
  3698. } else {
  3699. len = 1 / len;
  3700. x0 *= len;
  3701. x1 *= len;
  3702. x2 *= len;
  3703. }
  3704. y0 = z1 * x2 - z2 * x1;
  3705. y1 = z2 * x0 - z0 * x2;
  3706. y2 = z0 * x1 - z1 * x0;
  3707. len = Math.hypot(y0, y1, y2);
  3708. if (!len) {
  3709. y0 = 0;
  3710. y1 = 0;
  3711. y2 = 0;
  3712. } else {
  3713. len = 1 / len;
  3714. y0 *= len;
  3715. y1 *= len;
  3716. y2 *= len;
  3717. }
  3718. out[0] = x0;
  3719. out[1] = y0;
  3720. out[2] = z0;
  3721. out[3] = 0;
  3722. out[4] = x1;
  3723. out[5] = y1;
  3724. out[6] = z1;
  3725. out[7] = 0;
  3726. out[8] = x2;
  3727. out[9] = y2;
  3728. out[10] = z2;
  3729. out[11] = 0;
  3730. out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
  3731. out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
  3732. out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
  3733. out[15] = 1;
  3734. return out;
  3735. }
  3736. function targetTo(out, eye, target, up) {
  3737. var eyex = eye[0], eyey = eye[1], eyez = eye[2], upx = up[0], upy = up[1], upz = up[2];
  3738. var z0 = eyex - target[0], z1 = eyey - target[1], z2 = eyez - target[2];
  3739. var len = z0 * z0 + z1 * z1 + z2 * z2;
  3740. if (len > 0) {
  3741. len = 1 / Math.sqrt(len);
  3742. z0 *= len;
  3743. z1 *= len;
  3744. z2 *= len;
  3745. }
  3746. var x0 = upy * z2 - upz * z1, x1 = upz * z0 - upx * z2, x2 = upx * z1 - upy * z0;
  3747. len = x0 * x0 + x1 * x1 + x2 * x2;
  3748. if (len > 0) {
  3749. len = 1 / Math.sqrt(len);
  3750. x0 *= len;
  3751. x1 *= len;
  3752. x2 *= len;
  3753. }
  3754. out[0] = x0;
  3755. out[1] = x1;
  3756. out[2] = x2;
  3757. out[3] = 0;
  3758. out[4] = z1 * x2 - z2 * x1;
  3759. out[5] = z2 * x0 - z0 * x2;
  3760. out[6] = z0 * x1 - z1 * x0;
  3761. out[7] = 0;
  3762. out[8] = z0;
  3763. out[9] = z1;
  3764. out[10] = z2;
  3765. out[11] = 0;
  3766. out[12] = eyex;
  3767. out[13] = eyey;
  3768. out[14] = eyez;
  3769. out[15] = 1;
  3770. return out;
  3771. }
  3772. function str$5(a) {
  3773. 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] + ")";
  3774. }
  3775. function frob(a) {
  3776. 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]);
  3777. }
  3778. function add$5(out, a, b) {
  3779. out[0] = a[0] + b[0];
  3780. out[1] = a[1] + b[1];
  3781. out[2] = a[2] + b[2];
  3782. out[3] = a[3] + b[3];
  3783. out[4] = a[4] + b[4];
  3784. out[5] = a[5] + b[5];
  3785. out[6] = a[6] + b[6];
  3786. out[7] = a[7] + b[7];
  3787. out[8] = a[8] + b[8];
  3788. out[9] = a[9] + b[9];
  3789. out[10] = a[10] + b[10];
  3790. out[11] = a[11] + b[11];
  3791. out[12] = a[12] + b[12];
  3792. out[13] = a[13] + b[13];
  3793. out[14] = a[14] + b[14];
  3794. out[15] = a[15] + b[15];
  3795. return out;
  3796. }
  3797. function subtract$3(out, a, b) {
  3798. out[0] = a[0] - b[0];
  3799. out[1] = a[1] - b[1];
  3800. out[2] = a[2] - b[2];
  3801. out[3] = a[3] - b[3];
  3802. out[4] = a[4] - b[4];
  3803. out[5] = a[5] - b[5];
  3804. out[6] = a[6] - b[6];
  3805. out[7] = a[7] - b[7];
  3806. out[8] = a[8] - b[8];
  3807. out[9] = a[9] - b[9];
  3808. out[10] = a[10] - b[10];
  3809. out[11] = a[11] - b[11];
  3810. out[12] = a[12] - b[12];
  3811. out[13] = a[13] - b[13];
  3812. out[14] = a[14] - b[14];
  3813. out[15] = a[15] - b[15];
  3814. return out;
  3815. }
  3816. function multiplyScalar(out, a, b) {
  3817. out[0] = a[0] * b;
  3818. out[1] = a[1] * b;
  3819. out[2] = a[2] * b;
  3820. out[3] = a[3] * b;
  3821. out[4] = a[4] * b;
  3822. out[5] = a[5] * b;
  3823. out[6] = a[6] * b;
  3824. out[7] = a[7] * b;
  3825. out[8] = a[8] * b;
  3826. out[9] = a[9] * b;
  3827. out[10] = a[10] * b;
  3828. out[11] = a[11] * b;
  3829. out[12] = a[12] * b;
  3830. out[13] = a[13] * b;
  3831. out[14] = a[14] * b;
  3832. out[15] = a[15] * b;
  3833. return out;
  3834. }
  3835. function multiplyScalarAndAdd(out, a, b, scale2) {
  3836. out[0] = a[0] + b[0] * scale2;
  3837. out[1] = a[1] + b[1] * scale2;
  3838. out[2] = a[2] + b[2] * scale2;
  3839. out[3] = a[3] + b[3] * scale2;
  3840. out[4] = a[4] + b[4] * scale2;
  3841. out[5] = a[5] + b[5] * scale2;
  3842. out[6] = a[6] + b[6] * scale2;
  3843. out[7] = a[7] + b[7] * scale2;
  3844. out[8] = a[8] + b[8] * scale2;
  3845. out[9] = a[9] + b[9] * scale2;
  3846. out[10] = a[10] + b[10] * scale2;
  3847. out[11] = a[11] + b[11] * scale2;
  3848. out[12] = a[12] + b[12] * scale2;
  3849. out[13] = a[13] + b[13] * scale2;
  3850. out[14] = a[14] + b[14] * scale2;
  3851. out[15] = a[15] + b[15] * scale2;
  3852. return out;
  3853. }
  3854. function exactEquals$5(a, b) {
  3855. 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];
  3856. }
  3857. function equals$5(a, b) {
  3858. var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
  3859. var a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7];
  3860. var a8 = a[8], a9 = a[9], a10 = a[10], a11 = a[11];
  3861. var a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15];
  3862. var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
  3863. var b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7];
  3864. var b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11];
  3865. var b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
  3866. 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));
  3867. }
  3868. var mul$5 = multiply$5;
  3869. var sub$3 = subtract$3;
  3870. const mat4 = /* @__PURE__ */ Object.freeze({
  3871. __proto__: null,
  3872. add: add$5,
  3873. adjoint,
  3874. clone: clone$5,
  3875. copy: copy$5,
  3876. create: create$5,
  3877. determinant,
  3878. equals: equals$5,
  3879. exactEquals: exactEquals$5,
  3880. frob,
  3881. fromQuat,
  3882. fromQuat2,
  3883. fromRotation: fromRotation$1,
  3884. fromRotationTranslation: fromRotationTranslation$1,
  3885. fromRotationTranslationScale,
  3886. fromRotationTranslationScaleOrigin,
  3887. fromScaling,
  3888. fromTranslation: fromTranslation$1,
  3889. fromValues: fromValues$5,
  3890. fromXRotation,
  3891. fromYRotation,
  3892. fromZRotation,
  3893. frustum,
  3894. getRotation,
  3895. getScaling,
  3896. getTranslation: getTranslation$1,
  3897. identity: identity$2,
  3898. invert: invert$2,
  3899. lookAt,
  3900. mul: mul$5,
  3901. multiply: multiply$5,
  3902. multiplyScalar,
  3903. multiplyScalarAndAdd,
  3904. ortho,
  3905. orthoNO,
  3906. orthoZO,
  3907. perspective,
  3908. perspectiveFromFieldOfView,
  3909. perspectiveNO,
  3910. perspectiveZO,
  3911. rotate: rotate$1,
  3912. rotateX: rotateX$3,
  3913. rotateY: rotateY$3,
  3914. rotateZ: rotateZ$3,
  3915. scale: scale$5,
  3916. set: set$5,
  3917. str: str$5,
  3918. sub: sub$3,
  3919. subtract: subtract$3,
  3920. targetTo,
  3921. translate: translate$1,
  3922. transpose
  3923. });
  3924. function create$4() {
  3925. var out = new ARRAY_TYPE(3);
  3926. if (ARRAY_TYPE != Float32Array) {
  3927. out[0] = 0;
  3928. out[1] = 0;
  3929. out[2] = 0;
  3930. }
  3931. return out;
  3932. }
  3933. function clone$4(a) {
  3934. var out = new ARRAY_TYPE(3);
  3935. out[0] = a[0];
  3936. out[1] = a[1];
  3937. out[2] = a[2];
  3938. return out;
  3939. }
  3940. function length$4(a) {
  3941. var x = a[0];
  3942. var y = a[1];
  3943. var z = a[2];
  3944. return Math.hypot(x, y, z);
  3945. }
  3946. function fromValues$4(x, y, z) {
  3947. var out = new ARRAY_TYPE(3);
  3948. out[0] = x;
  3949. out[1] = y;
  3950. out[2] = z;
  3951. return out;
  3952. }
  3953. function copy$4(out, a) {
  3954. out[0] = a[0];
  3955. out[1] = a[1];
  3956. out[2] = a[2];
  3957. return out;
  3958. }
  3959. function set$4(out, x, y, z) {
  3960. out[0] = x;
  3961. out[1] = y;
  3962. out[2] = z;
  3963. return out;
  3964. }
  3965. function add$4(out, a, b) {
  3966. out[0] = a[0] + b[0];
  3967. out[1] = a[1] + b[1];
  3968. out[2] = a[2] + b[2];
  3969. return out;
  3970. }
  3971. function subtract$2(out, a, b) {
  3972. out[0] = a[0] - b[0];
  3973. out[1] = a[1] - b[1];
  3974. out[2] = a[2] - b[2];
  3975. return out;
  3976. }
  3977. function multiply$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 divide$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 ceil$2(out, a) {
  3990. out[0] = Math.ceil(a[0]);
  3991. out[1] = Math.ceil(a[1]);
  3992. out[2] = Math.ceil(a[2]);
  3993. return out;
  3994. }
  3995. function floor$2(out, a) {
  3996. out[0] = Math.floor(a[0]);
  3997. out[1] = Math.floor(a[1]);
  3998. out[2] = Math.floor(a[2]);
  3999. return out;
  4000. }
  4001. function min$2(out, a, b) {
  4002. out[0] = Math.min(a[0], b[0]);
  4003. out[1] = Math.min(a[1], b[1]);
  4004. out[2] = Math.min(a[2], b[2]);
  4005. return out;
  4006. }
  4007. function max$2(out, a, b) {
  4008. out[0] = Math.max(a[0], b[0]);
  4009. out[1] = Math.max(a[1], b[1]);
  4010. out[2] = Math.max(a[2], b[2]);
  4011. return out;
  4012. }
  4013. function round$2(out, a) {
  4014. out[0] = Math.round(a[0]);
  4015. out[1] = Math.round(a[1]);
  4016. out[2] = Math.round(a[2]);
  4017. return out;
  4018. }
  4019. function scale$4(out, a, b) {
  4020. out[0] = a[0] * b;
  4021. out[1] = a[1] * b;
  4022. out[2] = a[2] * b;
  4023. return out;
  4024. }
  4025. function scaleAndAdd$2(out, a, b, scale2) {
  4026. out[0] = a[0] + b[0] * scale2;
  4027. out[1] = a[1] + b[1] * scale2;
  4028. out[2] = a[2] + b[2] * scale2;
  4029. return out;
  4030. }
  4031. function distance$2(a, b) {
  4032. var x = b[0] - a[0];
  4033. var y = b[1] - a[1];
  4034. var z = b[2] - a[2];
  4035. return Math.hypot(x, y, z);
  4036. }
  4037. function squaredDistance$2(a, b) {
  4038. var x = b[0] - a[0];
  4039. var y = b[1] - a[1];
  4040. var z = b[2] - a[2];
  4041. return x * x + y * y + z * z;
  4042. }
  4043. function squaredLength$4(a) {
  4044. var x = a[0];
  4045. var y = a[1];
  4046. var z = a[2];
  4047. return x * x + y * y + z * z;
  4048. }
  4049. function negate$2(out, a) {
  4050. out[0] = -a[0];
  4051. out[1] = -a[1];
  4052. out[2] = -a[2];
  4053. return out;
  4054. }
  4055. function inverse$2(out, a) {
  4056. out[0] = 1 / a[0];
  4057. out[1] = 1 / a[1];
  4058. out[2] = 1 / a[2];
  4059. return out;
  4060. }
  4061. function normalize$4(out, a) {
  4062. var x = a[0];
  4063. var y = a[1];
  4064. var z = a[2];
  4065. var len = x * x + y * y + z * z;
  4066. if (len > 0) {
  4067. len = 1 / Math.sqrt(len);
  4068. }
  4069. out[0] = a[0] * len;
  4070. out[1] = a[1] * len;
  4071. out[2] = a[2] * len;
  4072. return out;
  4073. }
  4074. function dot$4(a, b) {
  4075. return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
  4076. }
  4077. function cross$2(out, a, b) {
  4078. var ax = a[0], ay = a[1], az = a[2];
  4079. var bx = b[0], by = b[1], bz = b[2];
  4080. out[0] = ay * bz - az * by;
  4081. out[1] = az * bx - ax * bz;
  4082. out[2] = ax * by - ay * bx;
  4083. return out;
  4084. }
  4085. function lerp$4(out, a, b, t) {
  4086. var ax = a[0];
  4087. var ay = a[1];
  4088. var az = a[2];
  4089. out[0] = ax + t * (b[0] - ax);
  4090. out[1] = ay + t * (b[1] - ay);
  4091. out[2] = az + t * (b[2] - az);
  4092. return out;
  4093. }
  4094. function hermite(out, a, b, c, d, t) {
  4095. var factorTimes2 = t * t;
  4096. var factor1 = factorTimes2 * (2 * t - 3) + 1;
  4097. var factor2 = factorTimes2 * (t - 2) + t;
  4098. var factor3 = factorTimes2 * (t - 1);
  4099. var factor4 = factorTimes2 * (3 - 2 * t);
  4100. out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
  4101. out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
  4102. out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
  4103. return out;
  4104. }
  4105. function bezier(out, a, b, c, d, t) {
  4106. var inverseFactor = 1 - t;
  4107. var inverseFactorTimesTwo = inverseFactor * inverseFactor;
  4108. var factorTimes2 = t * t;
  4109. var factor1 = inverseFactorTimesTwo * inverseFactor;
  4110. var factor2 = 3 * t * inverseFactorTimesTwo;
  4111. var factor3 = 3 * factorTimes2 * inverseFactor;
  4112. var factor4 = factorTimes2 * t;
  4113. out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
  4114. out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
  4115. out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
  4116. return out;
  4117. }
  4118. function random$3(out, scale2) {
  4119. scale2 = scale2 || 1;
  4120. var r = RANDOM() * 2 * Math.PI;
  4121. var z = RANDOM() * 2 - 1;
  4122. var zScale = Math.sqrt(1 - z * z) * scale2;
  4123. out[0] = Math.cos(r) * zScale;
  4124. out[1] = Math.sin(r) * zScale;
  4125. out[2] = z * scale2;
  4126. return out;
  4127. }
  4128. function transformMat4$2(out, a, m) {
  4129. var x = a[0], y = a[1], z = a[2];
  4130. var w = m[3] * x + m[7] * y + m[11] * z + m[15];
  4131. w = w || 1;
  4132. out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
  4133. out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
  4134. out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
  4135. return out;
  4136. }
  4137. function transformMat3$1(out, a, m) {
  4138. var x = a[0], y = a[1], z = a[2];
  4139. out[0] = x * m[0] + y * m[3] + z * m[6];
  4140. out[1] = x * m[1] + y * m[4] + z * m[7];
  4141. out[2] = x * m[2] + y * m[5] + z * m[8];
  4142. return out;
  4143. }
  4144. function transformQuat$1(out, a, q) {
  4145. var qx = q[0], qy = q[1], qz = q[2], qw = q[3];
  4146. var x = a[0], y = a[1], z = a[2];
  4147. var uvx = qy * z - qz * y, uvy = qz * x - qx * z, uvz = qx * y - qy * x;
  4148. var uuvx = qy * uvz - qz * uvy, uuvy = qz * uvx - qx * uvz, uuvz = qx * uvy - qy * uvx;
  4149. var w2 = qw * 2;
  4150. uvx *= w2;
  4151. uvy *= w2;
  4152. uvz *= w2;
  4153. uuvx *= 2;
  4154. uuvy *= 2;
  4155. uuvz *= 2;
  4156. out[0] = x + uvx + uuvx;
  4157. out[1] = y + uvy + uuvy;
  4158. out[2] = z + uvz + uuvz;
  4159. return out;
  4160. }
  4161. function rotateX$2(out, a, b, rad) {
  4162. var p = [], r = [];
  4163. p[0] = a[0] - b[0];
  4164. p[1] = a[1] - b[1];
  4165. p[2] = a[2] - b[2];
  4166. r[0] = p[0];
  4167. r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad);
  4168. r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad);
  4169. out[0] = r[0] + b[0];
  4170. out[1] = r[1] + b[1];
  4171. out[2] = r[2] + b[2];
  4172. return out;
  4173. }
  4174. function rotateY$2(out, a, b, rad) {
  4175. var p = [], r = [];
  4176. p[0] = a[0] - b[0];
  4177. p[1] = a[1] - b[1];
  4178. p[2] = a[2] - b[2];
  4179. r[0] = p[2] * Math.sin(rad) + p[0] * Math.cos(rad);
  4180. r[1] = p[1];
  4181. r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad);
  4182. out[0] = r[0] + b[0];
  4183. out[1] = r[1] + b[1];
  4184. out[2] = r[2] + b[2];
  4185. return out;
  4186. }
  4187. function rotateZ$2(out, a, b, rad) {
  4188. var p = [], r = [];
  4189. p[0] = a[0] - b[0];
  4190. p[1] = a[1] - b[1];
  4191. p[2] = a[2] - b[2];
  4192. r[0] = p[0] * Math.cos(rad) - p[1] * Math.sin(rad);
  4193. r[1] = p[0] * Math.sin(rad) + p[1] * Math.cos(rad);
  4194. r[2] = p[2];
  4195. out[0] = r[0] + b[0];
  4196. out[1] = r[1] + b[1];
  4197. out[2] = r[2] + b[2];
  4198. return out;
  4199. }
  4200. function angle$1(a, b) {
  4201. 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;
  4202. return Math.acos(Math.min(Math.max(cosine, -1), 1));
  4203. }
  4204. function zero$2(out) {
  4205. out[0] = 0;
  4206. out[1] = 0;
  4207. out[2] = 0;
  4208. return out;
  4209. }
  4210. function str$4(a) {
  4211. return "vec3(" + a[0] + ", " + a[1] + ", " + a[2] + ")";
  4212. }
  4213. function exactEquals$4(a, b) {
  4214. return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
  4215. }
  4216. function equals$4(a, b) {
  4217. var a0 = a[0], a1 = a[1], a2 = a[2];
  4218. var b0 = b[0], b1 = b[1], b2 = b[2];
  4219. 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));
  4220. }
  4221. var sub$2 = subtract$2;
  4222. var mul$4 = multiply$4;
  4223. var div$2 = divide$2;
  4224. var dist$2 = distance$2;
  4225. var sqrDist$2 = squaredDistance$2;
  4226. var len$4 = length$4;
  4227. var sqrLen$4 = squaredLength$4;
  4228. var forEach$2 = function() {
  4229. var vec = create$4();
  4230. return function(a, stride, offset2, count, fn, arg) {
  4231. var i, l;
  4232. if (!stride) {
  4233. stride = 3;
  4234. }
  4235. if (!offset2) {
  4236. offset2 = 0;
  4237. }
  4238. if (count) {
  4239. l = Math.min(count * stride + offset2, a.length);
  4240. } else {
  4241. l = a.length;
  4242. }
  4243. for (i = offset2; i < l; i += stride) {
  4244. vec[0] = a[i];
  4245. vec[1] = a[i + 1];
  4246. vec[2] = a[i + 2];
  4247. fn(vec, vec, arg);
  4248. a[i] = vec[0];
  4249. a[i + 1] = vec[1];
  4250. a[i + 2] = vec[2];
  4251. }
  4252. return a;
  4253. };
  4254. }();
  4255. const vec3 = /* @__PURE__ */ Object.freeze({
  4256. __proto__: null,
  4257. add: add$4,
  4258. angle: angle$1,
  4259. bezier,
  4260. ceil: ceil$2,
  4261. clone: clone$4,
  4262. copy: copy$4,
  4263. create: create$4,
  4264. cross: cross$2,
  4265. dist: dist$2,
  4266. distance: distance$2,
  4267. div: div$2,
  4268. divide: divide$2,
  4269. dot: dot$4,
  4270. equals: equals$4,
  4271. exactEquals: exactEquals$4,
  4272. floor: floor$2,
  4273. forEach: forEach$2,
  4274. fromValues: fromValues$4,
  4275. hermite,
  4276. inverse: inverse$2,
  4277. len: len$4,
  4278. length: length$4,
  4279. lerp: lerp$4,
  4280. max: max$2,
  4281. min: min$2,
  4282. mul: mul$4,
  4283. multiply: multiply$4,
  4284. negate: negate$2,
  4285. normalize: normalize$4,
  4286. random: random$3,
  4287. rotateX: rotateX$2,
  4288. rotateY: rotateY$2,
  4289. rotateZ: rotateZ$2,
  4290. round: round$2,
  4291. scale: scale$4,
  4292. scaleAndAdd: scaleAndAdd$2,
  4293. set: set$4,
  4294. sqrDist: sqrDist$2,
  4295. sqrLen: sqrLen$4,
  4296. squaredDistance: squaredDistance$2,
  4297. squaredLength: squaredLength$4,
  4298. str: str$4,
  4299. sub: sub$2,
  4300. subtract: subtract$2,
  4301. transformMat3: transformMat3$1,
  4302. transformMat4: transformMat4$2,
  4303. transformQuat: transformQuat$1,
  4304. zero: zero$2
  4305. });
  4306. function create$3() {
  4307. var out = new ARRAY_TYPE(4);
  4308. if (ARRAY_TYPE != Float32Array) {
  4309. out[0] = 0;
  4310. out[1] = 0;
  4311. out[2] = 0;
  4312. out[3] = 0;
  4313. }
  4314. return out;
  4315. }
  4316. function normalize$3(out, a) {
  4317. var x = a[0];
  4318. var y = a[1];
  4319. var z = a[2];
  4320. var w = a[3];
  4321. var len = x * x + y * y + z * z + w * w;
  4322. if (len > 0) {
  4323. len = 1 / Math.sqrt(len);
  4324. }
  4325. out[0] = x * len;
  4326. out[1] = y * len;
  4327. out[2] = z * len;
  4328. out[3] = w * len;
  4329. return out;
  4330. }
  4331. (function() {
  4332. var vec = create$3();
  4333. return function(a, stride, offset2, count, fn, arg) {
  4334. var i, l;
  4335. if (!stride) {
  4336. stride = 4;
  4337. }
  4338. if (!offset2) {
  4339. offset2 = 0;
  4340. }
  4341. if (count) {
  4342. l = Math.min(count * stride + offset2, a.length);
  4343. } else {
  4344. l = a.length;
  4345. }
  4346. for (i = offset2; i < l; i += stride) {
  4347. vec[0] = a[i];
  4348. vec[1] = a[i + 1];
  4349. vec[2] = a[i + 2];
  4350. vec[3] = a[i + 3];
  4351. fn(vec, vec, arg);
  4352. a[i] = vec[0];
  4353. a[i + 1] = vec[1];
  4354. a[i + 2] = vec[2];
  4355. a[i + 3] = vec[3];
  4356. }
  4357. return a;
  4358. };
  4359. })();
  4360. function create$2() {
  4361. var out = new ARRAY_TYPE(4);
  4362. if (ARRAY_TYPE != Float32Array) {
  4363. out[0] = 0;
  4364. out[1] = 0;
  4365. out[2] = 0;
  4366. }
  4367. out[3] = 1;
  4368. return out;
  4369. }
  4370. function setAxisAngle(out, axis, rad) {
  4371. rad = rad * 0.5;
  4372. var s = Math.sin(rad);
  4373. out[0] = s * axis[0];
  4374. out[1] = s * axis[1];
  4375. out[2] = s * axis[2];
  4376. out[3] = Math.cos(rad);
  4377. return out;
  4378. }
  4379. function slerp(out, a, b, t) {
  4380. var ax = a[0], ay = a[1], az = a[2], aw = a[3];
  4381. var bx = b[0], by = b[1], bz = b[2], bw = b[3];
  4382. var omega, cosom, sinom, scale0, scale1;
  4383. cosom = ax * bx + ay * by + az * bz + aw * bw;
  4384. if (cosom < 0) {
  4385. cosom = -cosom;
  4386. bx = -bx;
  4387. by = -by;
  4388. bz = -bz;
  4389. bw = -bw;
  4390. }
  4391. if (1 - cosom > EPSILON) {
  4392. omega = Math.acos(cosom);
  4393. sinom = Math.sin(omega);
  4394. scale0 = Math.sin((1 - t) * omega) / sinom;
  4395. scale1 = Math.sin(t * omega) / sinom;
  4396. } else {
  4397. scale0 = 1 - t;
  4398. scale1 = t;
  4399. }
  4400. out[0] = scale0 * ax + scale1 * bx;
  4401. out[1] = scale0 * ay + scale1 * by;
  4402. out[2] = scale0 * az + scale1 * bz;
  4403. out[3] = scale0 * aw + scale1 * bw;
  4404. return out;
  4405. }
  4406. function fromMat3(out, m) {
  4407. var fTrace = m[0] + m[4] + m[8];
  4408. var fRoot;
  4409. if (fTrace > 0) {
  4410. fRoot = Math.sqrt(fTrace + 1);
  4411. out[3] = 0.5 * fRoot;
  4412. fRoot = 0.5 / fRoot;
  4413. out[0] = (m[5] - m[7]) * fRoot;
  4414. out[1] = (m[6] - m[2]) * fRoot;
  4415. out[2] = (m[1] - m[3]) * fRoot;
  4416. } else {
  4417. var i = 0;
  4418. if (m[4] > m[0])
  4419. i = 1;
  4420. if (m[8] > m[i * 3 + i])
  4421. i = 2;
  4422. var j = (i + 1) % 3;
  4423. var k = (i + 2) % 3;
  4424. fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1);
  4425. out[i] = 0.5 * fRoot;
  4426. fRoot = 0.5 / fRoot;
  4427. out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot;
  4428. out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot;
  4429. out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot;
  4430. }
  4431. return out;
  4432. }
  4433. var normalize$2 = normalize$3;
  4434. (function() {
  4435. var tmpvec3 = create$4();
  4436. var xUnitVec3 = fromValues$4(1, 0, 0);
  4437. var yUnitVec3 = fromValues$4(0, 1, 0);
  4438. return function(out, a, b) {
  4439. var dot = dot$4(a, b);
  4440. if (dot < -0.999999) {
  4441. cross$2(tmpvec3, xUnitVec3, a);
  4442. if (len$4(tmpvec3) < 1e-6)
  4443. cross$2(tmpvec3, yUnitVec3, a);
  4444. normalize$4(tmpvec3, tmpvec3);
  4445. setAxisAngle(out, tmpvec3, Math.PI);
  4446. return out;
  4447. } else if (dot > 0.999999) {
  4448. out[0] = 0;
  4449. out[1] = 0;
  4450. out[2] = 0;
  4451. out[3] = 1;
  4452. return out;
  4453. } else {
  4454. cross$2(tmpvec3, a, b);
  4455. out[0] = tmpvec3[0];
  4456. out[1] = tmpvec3[1];
  4457. out[2] = tmpvec3[2];
  4458. out[3] = 1 + dot;
  4459. return normalize$2(out, out);
  4460. }
  4461. };
  4462. })();
  4463. (function() {
  4464. var temp1 = create$2();
  4465. var temp2 = create$2();
  4466. return function(out, a, b, c, d, t) {
  4467. slerp(temp1, a, d, t);
  4468. slerp(temp2, b, c, t);
  4469. slerp(out, temp1, temp2, 2 * t * (1 - t));
  4470. return out;
  4471. };
  4472. })();
  4473. (function() {
  4474. var matr = create$6();
  4475. return function(out, view, right, up) {
  4476. matr[0] = right[0];
  4477. matr[3] = right[1];
  4478. matr[6] = right[2];
  4479. matr[1] = up[0];
  4480. matr[4] = up[1];
  4481. matr[7] = up[2];
  4482. matr[2] = -view[0];
  4483. matr[5] = -view[1];
  4484. matr[8] = -view[2];
  4485. return normalize$2(out, fromMat3(out, matr));
  4486. };
  4487. })();
  4488. function create() {
  4489. var out = new ARRAY_TYPE(2);
  4490. if (ARRAY_TYPE != Float32Array) {
  4491. out[0] = 0;
  4492. out[1] = 0;
  4493. }
  4494. return out;
  4495. }
  4496. (function() {
  4497. var vec = create();
  4498. return function(a, stride, offset2, count, fn, arg) {
  4499. var i, l;
  4500. if (!stride) {
  4501. stride = 2;
  4502. }
  4503. if (!offset2) {
  4504. offset2 = 0;
  4505. }
  4506. if (count) {
  4507. l = Math.min(count * stride + offset2, a.length);
  4508. } else {
  4509. l = a.length;
  4510. }
  4511. for (i = offset2; i < l; i += stride) {
  4512. vec[0] = a[i];
  4513. vec[1] = a[i + 1];
  4514. fn(vec, vec, arg);
  4515. a[i] = vec[0];
  4516. a[i + 1] = vec[1];
  4517. }
  4518. return a;
  4519. };
  4520. })();
  4521. class AnimationControl {
  4522. /** @type {object} */
  4523. #animationData;
  4524. /** @type {Promise<void>} */
  4525. #finishedPromise;
  4526. #willFinish;
  4527. /**
  4528. * Defines a static empty / void animation control.
  4529. *
  4530. * @type {AnimationControl}
  4531. */
  4532. static #voidControl = new AnimationControl(null);
  4533. /**
  4534. * Provides a static void / undefined AnimationControl that is automatically resolved.
  4535. *
  4536. * @returns {AnimationControl} Void AnimationControl
  4537. */
  4538. static get voidControl() {
  4539. return this.#voidControl;
  4540. }
  4541. /**
  4542. * @param {object|null} [animationData] - Animation data from {@link AnimationAPI}.
  4543. *
  4544. * @param {boolean} [willFinish] - Promise that tracks animation finished state.
  4545. */
  4546. constructor(animationData, willFinish = false) {
  4547. this.#animationData = animationData;
  4548. this.#willFinish = willFinish;
  4549. if (isObject(animationData)) {
  4550. animationData.control = this;
  4551. }
  4552. }
  4553. /**
  4554. * Get a promise that resolves when animation is finished.
  4555. *
  4556. * @returns {Promise<void>}
  4557. */
  4558. get finished() {
  4559. if (!(this.#finishedPromise instanceof Promise)) {
  4560. this.#finishedPromise = this.#willFinish ? new Promise((resolve) => this.#animationData.resolve = resolve) : Promise.resolve();
  4561. }
  4562. return this.#finishedPromise;
  4563. }
  4564. /**
  4565. * Returns whether this animation is currently active / animating.
  4566. *
  4567. * Note: a delayed animation may not be started / active yet. Use {@link AnimationControl.isFinished} to determine
  4568. * if an animation is actually finished.
  4569. *
  4570. * @returns {boolean} Animation active state.
  4571. */
  4572. get isActive() {
  4573. return this.#animationData.active;
  4574. }
  4575. /**
  4576. * Returns whether this animation is completely finished.
  4577. *
  4578. * @returns {boolean} Animation finished state.
  4579. */
  4580. get isFinished() {
  4581. return this.#animationData.finished;
  4582. }
  4583. /**
  4584. * Cancels the animation.
  4585. */
  4586. cancel() {
  4587. const animationData = this.#animationData;
  4588. if (animationData === null || animationData === void 0) {
  4589. return;
  4590. }
  4591. animationData.cancelled = true;
  4592. }
  4593. }
  4594. class AnimationManager {
  4595. /**
  4596. * @type {object[]}
  4597. */
  4598. static activeList = [];
  4599. /**
  4600. * @type {object[]}
  4601. */
  4602. static newList = [];
  4603. /**
  4604. * @type {number}
  4605. */
  4606. static current;
  4607. /**
  4608. * Add animation data.
  4609. *
  4610. * @param {object} data -
  4611. */
  4612. static add(data) {
  4613. const now2 = performance.now();
  4614. data.start = now2 + (AnimationManager.current - now2);
  4615. AnimationManager.newList.push(data);
  4616. }
  4617. /**
  4618. * Manage all animation
  4619. */
  4620. static animate() {
  4621. const current = AnimationManager.current = performance.now();
  4622. if (AnimationManager.activeList.length === 0 && AnimationManager.newList.length === 0) {
  4623. globalThis.requestAnimationFrame(AnimationManager.animate);
  4624. return;
  4625. }
  4626. if (AnimationManager.newList.length) {
  4627. for (let cntr = AnimationManager.newList.length; --cntr >= 0; ) {
  4628. const data = AnimationManager.newList[cntr];
  4629. if (data.cancelled) {
  4630. AnimationManager.newList.splice(cntr, 1);
  4631. data.cleanup(data);
  4632. }
  4633. if (data.active) {
  4634. AnimationManager.newList.splice(cntr, 1);
  4635. AnimationManager.activeList.push(data);
  4636. }
  4637. }
  4638. }
  4639. for (let cntr = AnimationManager.activeList.length; --cntr >= 0; ) {
  4640. const data = AnimationManager.activeList[cntr];
  4641. if (data.cancelled || data.el !== void 0 && !data.el.isConnected) {
  4642. AnimationManager.activeList.splice(cntr, 1);
  4643. data.cleanup(data);
  4644. continue;
  4645. }
  4646. data.current = current - data.start;
  4647. if (data.current >= data.duration) {
  4648. for (let dataCntr = data.keys.length; --dataCntr >= 0; ) {
  4649. const key = data.keys[dataCntr];
  4650. data.newData[key] = data.destination[key];
  4651. }
  4652. data.position.set(data.newData);
  4653. AnimationManager.activeList.splice(cntr, 1);
  4654. data.cleanup(data);
  4655. continue;
  4656. }
  4657. const easedTime = data.ease(data.current / data.duration);
  4658. for (let dataCntr = data.keys.length; --dataCntr >= 0; ) {
  4659. const key = data.keys[dataCntr];
  4660. data.newData[key] = data.interpolate(data.initial[key], data.destination[key], easedTime);
  4661. }
  4662. data.position.set(data.newData);
  4663. }
  4664. globalThis.requestAnimationFrame(AnimationManager.animate);
  4665. }
  4666. /**
  4667. * Cancels all animations for given Position instance.
  4668. *
  4669. * @param {Position} position - Position instance.
  4670. */
  4671. static cancel(position) {
  4672. for (let cntr = AnimationManager.activeList.length; --cntr >= 0; ) {
  4673. const data = AnimationManager.activeList[cntr];
  4674. if (data.position === position) {
  4675. AnimationManager.activeList.splice(cntr, 1);
  4676. data.cancelled = true;
  4677. data.cleanup(data);
  4678. }
  4679. }
  4680. for (let cntr = AnimationManager.newList.length; --cntr >= 0; ) {
  4681. const data = AnimationManager.newList[cntr];
  4682. if (data.position === position) {
  4683. AnimationManager.newList.splice(cntr, 1);
  4684. data.cancelled = true;
  4685. data.cleanup(data);
  4686. }
  4687. }
  4688. }
  4689. /**
  4690. * Cancels all active and delayed animations.
  4691. */
  4692. static cancelAll() {
  4693. for (let cntr = AnimationManager.activeList.length; --cntr >= 0; ) {
  4694. const data = AnimationManager.activeList[cntr];
  4695. data.cancelled = true;
  4696. data.cleanup(data);
  4697. }
  4698. for (let cntr = AnimationManager.newList.length; --cntr >= 0; ) {
  4699. const data = AnimationManager.newList[cntr];
  4700. data.cancelled = true;
  4701. data.cleanup(data);
  4702. }
  4703. AnimationManager.activeList.length = 0;
  4704. AnimationManager.newList.length = 0;
  4705. }
  4706. /**
  4707. * Gets all {@link AnimationControl} instances for a given Position instance.
  4708. *
  4709. * @param {Position} position - Position instance.
  4710. *
  4711. * @returns {AnimationControl[]} All scheduled AnimationControl instances for the given Position instance.
  4712. */
  4713. static getScheduled(position) {
  4714. const results = [];
  4715. for (let cntr = AnimationManager.activeList.length; --cntr >= 0; ) {
  4716. const data = AnimationManager.activeList[cntr];
  4717. if (data.position === position) {
  4718. results.push(data.control);
  4719. }
  4720. }
  4721. for (let cntr = AnimationManager.newList.length; --cntr >= 0; ) {
  4722. const data = AnimationManager.newList[cntr];
  4723. if (data.position === position) {
  4724. results.push(data.control);
  4725. }
  4726. }
  4727. return results;
  4728. }
  4729. }
  4730. AnimationManager.animate();
  4731. const animateKeys = /* @__PURE__ */ new Set([
  4732. // Main keys
  4733. "left",
  4734. "top",
  4735. "maxWidth",
  4736. "maxHeight",
  4737. "minWidth",
  4738. "minHeight",
  4739. "width",
  4740. "height",
  4741. "rotateX",
  4742. "rotateY",
  4743. "rotateZ",
  4744. "scale",
  4745. "translateX",
  4746. "translateY",
  4747. "translateZ",
  4748. "zIndex",
  4749. // Aliases
  4750. "rotation"
  4751. ]);
  4752. const transformKeys = ["rotateX", "rotateY", "rotateZ", "scale", "translateX", "translateY", "translateZ"];
  4753. Object.freeze(transformKeys);
  4754. const relativeRegex = /^([-+*])=(-?[\d]*\.?[\d]+)$/;
  4755. const numericDefaults = {
  4756. // Other keys
  4757. height: 0,
  4758. left: 0,
  4759. maxHeight: null,
  4760. maxWidth: null,
  4761. minHeight: null,
  4762. minWidth: null,
  4763. top: 0,
  4764. transformOrigin: null,
  4765. width: 0,
  4766. zIndex: null,
  4767. rotateX: 0,
  4768. rotateY: 0,
  4769. rotateZ: 0,
  4770. scale: 1,
  4771. translateX: 0,
  4772. translateY: 0,
  4773. translateZ: 0,
  4774. rotation: 0
  4775. };
  4776. Object.freeze(numericDefaults);
  4777. function setNumericDefaults(data) {
  4778. if (data.rotateX === null) {
  4779. data.rotateX = 0;
  4780. }
  4781. if (data.rotateY === null) {
  4782. data.rotateY = 0;
  4783. }
  4784. if (data.rotateZ === null) {
  4785. data.rotateZ = 0;
  4786. }
  4787. if (data.translateX === null) {
  4788. data.translateX = 0;
  4789. }
  4790. if (data.translateY === null) {
  4791. data.translateY = 0;
  4792. }
  4793. if (data.translateZ === null) {
  4794. data.translateZ = 0;
  4795. }
  4796. if (data.scale === null) {
  4797. data.scale = 1;
  4798. }
  4799. if (data.rotation === null) {
  4800. data.rotation = 0;
  4801. }
  4802. }
  4803. const transformKeysBitwise = {
  4804. rotateX: 1,
  4805. rotateY: 2,
  4806. rotateZ: 4,
  4807. scale: 8,
  4808. translateX: 16,
  4809. translateY: 32,
  4810. translateZ: 64
  4811. };
  4812. Object.freeze(transformKeysBitwise);
  4813. const transformOriginDefault = "top left";
  4814. const transformOrigins = [
  4815. "top left",
  4816. "top center",
  4817. "top right",
  4818. "center left",
  4819. "center",
  4820. "center right",
  4821. "bottom left",
  4822. "bottom center",
  4823. "bottom right"
  4824. ];
  4825. Object.freeze(transformOrigins);
  4826. function convertRelative(positionData, position) {
  4827. for (const key in positionData) {
  4828. if (animateKeys.has(key)) {
  4829. const value = positionData[key];
  4830. if (typeof value !== "string") {
  4831. continue;
  4832. }
  4833. if (value === "auto" || value === "inherit") {
  4834. continue;
  4835. }
  4836. const regexResults = relativeRegex.exec(value);
  4837. if (!regexResults) {
  4838. throw new Error(
  4839. `convertRelative error: malformed relative key (${key}) with value (${value})`
  4840. );
  4841. }
  4842. const current = position[key];
  4843. switch (regexResults[1]) {
  4844. case "-":
  4845. positionData[key] = current - parseFloat(regexResults[2]);
  4846. break;
  4847. case "+":
  4848. positionData[key] = current + parseFloat(regexResults[2]);
  4849. break;
  4850. case "*":
  4851. positionData[key] = current * parseFloat(regexResults[2]);
  4852. break;
  4853. }
  4854. }
  4855. }
  4856. }
  4857. class AnimationAPI {
  4858. /** @type {PositionData} */
  4859. #data;
  4860. /** @type {Position} */
  4861. #position;
  4862. /**
  4863. * Tracks the number of animation control instances that are active.
  4864. *
  4865. * @type {number}
  4866. */
  4867. #instanceCount = 0;
  4868. /**
  4869. * Provides a bound function to pass as data to AnimationManager to invoke
  4870. *
  4871. * @type {Function}
  4872. * @see {AnimationAPI.#cleanupInstance}
  4873. */
  4874. #cleanup;
  4875. constructor(position, data) {
  4876. this.#position = position;
  4877. this.#data = data;
  4878. this.#cleanup = this.#cleanupInstance.bind(this);
  4879. }
  4880. /**
  4881. * Returns whether there are scheduled animations whether active or delayed for this Position.
  4882. *
  4883. * @returns {boolean} Are there active animation instances.
  4884. */
  4885. get isScheduled() {
  4886. return this.#instanceCount > 0;
  4887. }
  4888. /**
  4889. * Adds / schedules an animation w/ the AnimationManager. This contains the final steps common to all tweens.
  4890. *
  4891. * @param {object} initial -
  4892. *
  4893. * @param {object} destination -
  4894. *
  4895. * @param {number} duration -
  4896. *
  4897. * @param {HTMLElement} el -
  4898. *
  4899. * @param {number} delay -
  4900. *
  4901. * @param {Function} ease -
  4902. *
  4903. * @param {Function} interpolate -
  4904. *
  4905. * @returns {AnimationControl} The associated animation control.
  4906. */
  4907. #addAnimation(initial, destination, duration, el, delay, ease, interpolate2) {
  4908. setNumericDefaults(initial);
  4909. setNumericDefaults(destination);
  4910. for (const key in initial) {
  4911. if (!Number.isFinite(initial[key])) {
  4912. delete initial[key];
  4913. }
  4914. }
  4915. const keys = Object.keys(initial);
  4916. const newData = Object.assign({ immediateElementUpdate: true }, initial);
  4917. if (keys.length === 0) {
  4918. return AnimationControl.voidControl;
  4919. }
  4920. const animationData = {
  4921. active: true,
  4922. cleanup: this.#cleanup,
  4923. cancelled: false,
  4924. control: void 0,
  4925. current: 0,
  4926. destination,
  4927. duration: duration * 1e3,
  4928. // Internally the AnimationManager works in ms.
  4929. ease,
  4930. el,
  4931. finished: false,
  4932. initial,
  4933. interpolate: interpolate2,
  4934. keys,
  4935. newData,
  4936. position: this.#position,
  4937. resolve: void 0,
  4938. start: void 0
  4939. };
  4940. if (delay > 0) {
  4941. animationData.active = false;
  4942. setTimeout(() => {
  4943. if (!animationData.cancelled) {
  4944. animationData.active = true;
  4945. const now2 = performance.now();
  4946. animationData.start = now2 + (AnimationManager.current - now2);
  4947. }
  4948. }, delay * 1e3);
  4949. }
  4950. this.#instanceCount++;
  4951. AnimationManager.add(animationData);
  4952. return new AnimationControl(animationData, true);
  4953. }
  4954. /**
  4955. * Cancels all animation instances for this Position instance.
  4956. */
  4957. cancel() {
  4958. AnimationManager.cancel(this.#position);
  4959. }
  4960. /**
  4961. * Cleans up an animation instance.
  4962. *
  4963. * @param {object} data - Animation data for an animation instance.
  4964. */
  4965. #cleanupInstance(data) {
  4966. this.#instanceCount--;
  4967. data.active = false;
  4968. data.finished = true;
  4969. if (typeof data.resolve === "function") {
  4970. data.resolve(data.cancelled);
  4971. }
  4972. }
  4973. /**
  4974. * Returns all currently scheduled AnimationControl instances for this Position instance.
  4975. *
  4976. * @returns {AnimationControl[]} All currently scheduled animation controls for this Position instance.
  4977. */
  4978. getScheduled() {
  4979. return AnimationManager.getScheduled(this.#position);
  4980. }
  4981. /**
  4982. * Provides a tween from given position data to the current position.
  4983. *
  4984. * @param {PositionDataExtended} fromData - The starting position.
  4985. *
  4986. * @param {object} [opts] - Optional parameters.
  4987. *
  4988. * @param {number} [opts.delay=0] - Delay in seconds before animation starts.
  4989. *
  4990. * @param {number} [opts.duration=1] - Duration in seconds.
  4991. *
  4992. * @param {Function} [opts.ease=cubicOut] - Easing function.
  4993. *
  4994. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  4995. *
  4996. * @returns {AnimationControl} A control object that can cancel animation and provides a `finished` Promise.
  4997. */
  4998. from(fromData, { delay = 0, duration = 1, ease = cubicOut, interpolate: interpolate2 = lerp$5 } = {}) {
  4999. if (!isObject(fromData)) {
  5000. throw new TypeError(`AnimationAPI.from error: 'fromData' is not an object.`);
  5001. }
  5002. const position = this.#position;
  5003. const parent = position.parent;
  5004. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  5005. return AnimationControl.voidControl;
  5006. }
  5007. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  5008. const el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  5009. if (!Number.isFinite(delay) || delay < 0) {
  5010. throw new TypeError(`AnimationAPI.from error: 'delay' is not a positive number.`);
  5011. }
  5012. if (!Number.isFinite(duration) || duration < 0) {
  5013. throw new TypeError(`AnimationAPI.from error: 'duration' is not a positive number.`);
  5014. }
  5015. if (typeof ease !== "function") {
  5016. throw new TypeError(`AnimationAPI.from error: 'ease' is not a function.`);
  5017. }
  5018. if (typeof interpolate2 !== "function") {
  5019. throw new TypeError(`AnimationAPI.from error: 'interpolate' is not a function.`);
  5020. }
  5021. const initial = {};
  5022. const destination = {};
  5023. const data = this.#data;
  5024. for (const key in fromData) {
  5025. if (data[key] !== void 0 && fromData[key] !== data[key]) {
  5026. initial[key] = fromData[key];
  5027. destination[key] = data[key];
  5028. }
  5029. }
  5030. convertRelative(initial, data);
  5031. return this.#addAnimation(initial, destination, duration, el, delay, ease, interpolate2);
  5032. }
  5033. /**
  5034. * Provides a tween from given position data to the current position.
  5035. *
  5036. * @param {PositionDataExtended} fromData - The starting position.
  5037. *
  5038. * @param {PositionDataExtended} toData - The ending position.
  5039. *
  5040. * @param {object} [opts] - Optional parameters.
  5041. *
  5042. * @param {number} [opts.delay=0] - Delay in seconds before animation starts.
  5043. *
  5044. * @param {number} [opts.duration=1] - Duration in seconds.
  5045. *
  5046. * @param {Function} [opts.ease=cubicOut] - Easing function.
  5047. *
  5048. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  5049. *
  5050. * @returns {AnimationControl} A control object that can cancel animation and provides a `finished` Promise.
  5051. */
  5052. fromTo(fromData, toData, { delay = 0, duration = 1, ease = cubicOut, interpolate: interpolate2 = lerp$5 } = {}) {
  5053. if (!isObject(fromData)) {
  5054. throw new TypeError(`AnimationAPI.fromTo error: 'fromData' is not an object.`);
  5055. }
  5056. if (!isObject(toData)) {
  5057. throw new TypeError(`AnimationAPI.fromTo error: 'toData' is not an object.`);
  5058. }
  5059. const parent = this.#position.parent;
  5060. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  5061. return AnimationControl.voidControl;
  5062. }
  5063. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  5064. const el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  5065. if (!Number.isFinite(delay) || delay < 0) {
  5066. throw new TypeError(`AnimationAPI.fromTo error: 'delay' is not a positive number.`);
  5067. }
  5068. if (!Number.isFinite(duration) || duration < 0) {
  5069. throw new TypeError(`AnimationAPI.fromTo error: 'duration' is not a positive number.`);
  5070. }
  5071. if (typeof ease !== "function") {
  5072. throw new TypeError(`AnimationAPI.fromTo error: 'ease' is not a function.`);
  5073. }
  5074. if (typeof interpolate2 !== "function") {
  5075. throw new TypeError(`AnimationAPI.fromTo error: 'interpolate' is not a function.`);
  5076. }
  5077. const initial = {};
  5078. const destination = {};
  5079. const data = this.#data;
  5080. for (const key in fromData) {
  5081. if (toData[key] === void 0) {
  5082. console.warn(
  5083. `AnimationAPI.fromTo warning: key ('${key}') from 'fromData' missing in 'toData'; skipping this key.`
  5084. );
  5085. continue;
  5086. }
  5087. if (data[key] !== void 0) {
  5088. initial[key] = fromData[key];
  5089. destination[key] = toData[key];
  5090. }
  5091. }
  5092. convertRelative(initial, data);
  5093. convertRelative(destination, data);
  5094. return this.#addAnimation(initial, destination, duration, el, delay, ease, interpolate2);
  5095. }
  5096. /**
  5097. * Provides a tween to given position data from the current position.
  5098. *
  5099. * @param {PositionDataExtended} toData - The destination position.
  5100. *
  5101. * @param {object} [opts] - Optional parameters.
  5102. *
  5103. * @param {number} [opts.delay=0] - Delay in seconds before animation starts.
  5104. *
  5105. * @param {number} [opts.duration=1] - Duration in seconds.
  5106. *
  5107. * @param {Function} [opts.ease=cubicOut] - Easing function.
  5108. *
  5109. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  5110. *
  5111. * @returns {AnimationControl} A control object that can cancel animation and provides a `finished` Promise.
  5112. */
  5113. to(toData, { delay = 0, duration = 1, ease = cubicOut, interpolate: interpolate2 = lerp$5 } = {}) {
  5114. if (!isObject(toData)) {
  5115. throw new TypeError(`AnimationAPI.to error: 'toData' is not an object.`);
  5116. }
  5117. const parent = this.#position.parent;
  5118. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  5119. return AnimationControl.voidControl;
  5120. }
  5121. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  5122. const el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  5123. if (!Number.isFinite(delay) || delay < 0) {
  5124. throw new TypeError(`AnimationAPI.to error: 'delay' is not a positive number.`);
  5125. }
  5126. if (!Number.isFinite(duration) || duration < 0) {
  5127. throw new TypeError(`AnimationAPI.to error: 'duration' is not a positive number.`);
  5128. }
  5129. if (typeof ease !== "function") {
  5130. throw new TypeError(`AnimationAPI.to error: 'ease' is not a function.`);
  5131. }
  5132. if (typeof interpolate2 !== "function") {
  5133. throw new TypeError(`AnimationAPI.to error: 'interpolate' is not a function.`);
  5134. }
  5135. const initial = {};
  5136. const destination = {};
  5137. const data = this.#data;
  5138. for (const key in toData) {
  5139. if (data[key] !== void 0 && toData[key] !== data[key]) {
  5140. destination[key] = toData[key];
  5141. initial[key] = data[key];
  5142. }
  5143. }
  5144. convertRelative(destination, data);
  5145. return this.#addAnimation(initial, destination, duration, el, delay, ease, interpolate2);
  5146. }
  5147. /**
  5148. * Returns a function that provides an optimized way to constantly update a to-tween.
  5149. *
  5150. * @param {Iterable<string>} keys - The keys for quickTo.
  5151. *
  5152. * @param {object} [opts] - Optional parameters.
  5153. *
  5154. * @param {number} [opts.duration=1] - Duration in seconds.
  5155. *
  5156. * @param {Function} [opts.ease=cubicOut] - Easing function.
  5157. *
  5158. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  5159. *
  5160. * @returns {quickToCallback} quick-to tween function.
  5161. */
  5162. quickTo(keys, { duration = 1, ease = cubicOut, interpolate: interpolate2 = lerp$5 } = {}) {
  5163. if (!isIterable(keys)) {
  5164. throw new TypeError(`AnimationAPI.quickTo error: 'keys' is not an iterable list.`);
  5165. }
  5166. const parent = this.#position.parent;
  5167. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  5168. throw new Error(`AnimationAPI.quickTo error: 'parent' is not positionable.`);
  5169. }
  5170. if (!Number.isFinite(duration) || duration < 0) {
  5171. throw new TypeError(`AnimationAPI.quickTo error: 'duration' is not a positive number.`);
  5172. }
  5173. if (typeof ease !== "function") {
  5174. throw new TypeError(`AnimationAPI.quickTo error: 'ease' is not a function.`);
  5175. }
  5176. if (typeof interpolate2 !== "function") {
  5177. throw new TypeError(`AnimationAPI.quickTo error: 'interpolate' is not a function.`);
  5178. }
  5179. const initial = {};
  5180. const destination = {};
  5181. const data = this.#data;
  5182. for (const key of keys) {
  5183. if (typeof key !== "string") {
  5184. throw new TypeError(`AnimationAPI.quickTo error: key is not a string.`);
  5185. }
  5186. if (!animateKeys.has(key)) {
  5187. throw new Error(`AnimationAPI.quickTo error: key ('${key}') is not animatable.`);
  5188. }
  5189. if (data[key] !== void 0) {
  5190. destination[key] = data[key];
  5191. initial[key] = data[key];
  5192. }
  5193. }
  5194. const keysArray = [...keys];
  5195. Object.freeze(keysArray);
  5196. const newData = Object.assign({ immediateElementUpdate: true }, initial);
  5197. const animationData = {
  5198. active: true,
  5199. cleanup: this.#cleanup,
  5200. cancelled: false,
  5201. control: void 0,
  5202. current: 0,
  5203. destination,
  5204. duration: duration * 1e3,
  5205. // Internally the AnimationManager works in ms.
  5206. ease,
  5207. el: void 0,
  5208. finished: true,
  5209. // Note: start in finished state to add to AnimationManager on first callback.
  5210. initial,
  5211. interpolate: interpolate2,
  5212. keys,
  5213. newData,
  5214. position: this.#position,
  5215. resolve: void 0,
  5216. start: void 0
  5217. };
  5218. const quickToCB = (...args) => {
  5219. const argsLength = args.length;
  5220. if (argsLength === 0) {
  5221. return;
  5222. }
  5223. for (let cntr = keysArray.length; --cntr >= 0; ) {
  5224. const key = keysArray[cntr];
  5225. if (data[key] !== void 0) {
  5226. initial[key] = data[key];
  5227. }
  5228. }
  5229. if (isObject(args[0])) {
  5230. const objData = args[0];
  5231. for (const key in objData) {
  5232. if (destination[key] !== void 0) {
  5233. destination[key] = objData[key];
  5234. }
  5235. }
  5236. } else {
  5237. for (let cntr = 0; cntr < argsLength && cntr < keysArray.length; cntr++) {
  5238. const key = keysArray[cntr];
  5239. if (destination[key] !== void 0) {
  5240. destination[key] = args[cntr];
  5241. }
  5242. }
  5243. }
  5244. convertRelative(destination, data);
  5245. setNumericDefaults(initial);
  5246. setNumericDefaults(destination);
  5247. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  5248. animationData.el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  5249. if (animationData.finished) {
  5250. animationData.finished = false;
  5251. animationData.active = true;
  5252. animationData.current = 0;
  5253. this.#instanceCount++;
  5254. AnimationManager.add(animationData);
  5255. } else {
  5256. const now2 = performance.now();
  5257. animationData.start = now2 + (AnimationManager.current - now2);
  5258. animationData.current = 0;
  5259. }
  5260. };
  5261. quickToCB.keys = keysArray;
  5262. quickToCB.options = ({ duration: duration2, ease: ease2, interpolate: interpolate3 } = {}) => {
  5263. if (duration2 !== void 0 && (!Number.isFinite(duration2) || duration2 < 0)) {
  5264. throw new TypeError(`AnimationAPI.quickTo.options error: 'duration' is not a positive number.`);
  5265. }
  5266. if (ease2 !== void 0 && typeof ease2 !== "function") {
  5267. throw new TypeError(`AnimationAPI.quickTo.options error: 'ease' is not a function.`);
  5268. }
  5269. if (interpolate3 !== void 0 && typeof interpolate3 !== "function") {
  5270. throw new TypeError(`AnimationAPI.quickTo.options error: 'interpolate' is not a function.`);
  5271. }
  5272. if (duration2 >= 0) {
  5273. animationData.duration = duration2 * 1e3;
  5274. }
  5275. if (ease2) {
  5276. animationData.ease = ease2;
  5277. }
  5278. if (interpolate3) {
  5279. animationData.interpolate = interpolate3;
  5280. }
  5281. return quickToCB;
  5282. };
  5283. return quickToCB;
  5284. }
  5285. }
  5286. class AnimationGroupControl {
  5287. /** @type {AnimationControl[]} */
  5288. #animationControls;
  5289. /** @type {Promise<Awaited<unknown>[]>} */
  5290. #finishedPromise;
  5291. /**
  5292. * Defines a static empty / void animation control.
  5293. *
  5294. * @type {AnimationGroupControl}
  5295. */
  5296. static #voidControl = new AnimationGroupControl(null);
  5297. /**
  5298. * Provides a static void / undefined AnimationGroupControl that is automatically resolved.
  5299. *
  5300. * @returns {AnimationGroupControl} Void AnimationGroupControl
  5301. */
  5302. static get voidControl() {
  5303. return this.#voidControl;
  5304. }
  5305. /**
  5306. * @param {AnimationControl[]} animationControls - An array of AnimationControl instances.
  5307. */
  5308. constructor(animationControls) {
  5309. this.#animationControls = animationControls;
  5310. }
  5311. /**
  5312. * Get a promise that resolves when all animations are finished.
  5313. *
  5314. * @returns {Promise<Awaited<unknown>[]>|Promise<void>} Finished Promise for all animations.
  5315. */
  5316. get finished() {
  5317. const animationControls = this.#animationControls;
  5318. if (animationControls === null || animationControls === void 0) {
  5319. return Promise.resolve();
  5320. }
  5321. if (!(this.#finishedPromise instanceof Promise)) {
  5322. const promises = [];
  5323. for (let cntr = animationControls.length; --cntr >= 0; ) {
  5324. promises.push(animationControls[cntr].finished);
  5325. }
  5326. this.#finishedPromise = Promise.all(promises);
  5327. }
  5328. return this.#finishedPromise;
  5329. }
  5330. /**
  5331. * Returns whether there are active animation instances for this group.
  5332. *
  5333. * Note: a delayed animation may not be started / active yet. Use {@link AnimationGroupControl.isFinished} to
  5334. * determine if all animations in the group are finished.
  5335. *
  5336. * @returns {boolean} Are there active animation instances.
  5337. */
  5338. get isActive() {
  5339. const animationControls = this.#animationControls;
  5340. if (animationControls === null || animationControls === void 0) {
  5341. return false;
  5342. }
  5343. for (let cntr = animationControls.length; --cntr >= 0; ) {
  5344. if (animationControls[cntr].isActive) {
  5345. return true;
  5346. }
  5347. }
  5348. return false;
  5349. }
  5350. /**
  5351. * Returns whether all animations in the group are finished.
  5352. *
  5353. * @returns {boolean} Are all animation instances finished.
  5354. */
  5355. get isFinished() {
  5356. const animationControls = this.#animationControls;
  5357. if (animationControls === null || animationControls === void 0) {
  5358. return true;
  5359. }
  5360. for (let cntr = animationControls.length; --cntr >= 0; ) {
  5361. if (!animationControls[cntr].isFinished) {
  5362. return false;
  5363. }
  5364. }
  5365. return false;
  5366. }
  5367. /**
  5368. * Cancels the all animations.
  5369. */
  5370. cancel() {
  5371. const animationControls = this.#animationControls;
  5372. if (animationControls === null || animationControls === void 0) {
  5373. return;
  5374. }
  5375. for (let cntr = this.#animationControls.length; --cntr >= 0; ) {
  5376. this.#animationControls[cntr].cancel();
  5377. }
  5378. }
  5379. }
  5380. class AnimationGroupAPI {
  5381. /**
  5382. * Checks of the given object is a Position instance by checking for AnimationAPI.
  5383. *
  5384. * @param {*} object - Any data.
  5385. *
  5386. * @returns {boolean} Is Position.
  5387. */
  5388. static #isPosition(object) {
  5389. return isObject(object) && object.animate instanceof AnimationAPI;
  5390. }
  5391. /**
  5392. * Cancels any animation for given Position data.
  5393. *
  5394. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5395. */
  5396. static cancel(position) {
  5397. if (isIterable(position)) {
  5398. let index = -1;
  5399. for (const entry of position) {
  5400. index++;
  5401. const actualPosition = this.#isPosition(entry) ? entry : entry.position;
  5402. if (!this.#isPosition(actualPosition)) {
  5403. console.warn(`AnimationGroupAPI.cancel warning: No Position instance found at index: ${index}.`);
  5404. continue;
  5405. }
  5406. AnimationManager.cancel(actualPosition);
  5407. }
  5408. } else {
  5409. const actualPosition = this.#isPosition(position) ? position : position.position;
  5410. if (!this.#isPosition(actualPosition)) {
  5411. console.warn(`AnimationGroupAPI.cancel warning: No Position instance found.`);
  5412. return;
  5413. }
  5414. AnimationManager.cancel(actualPosition);
  5415. }
  5416. }
  5417. /**
  5418. * Cancels all Position animation.
  5419. */
  5420. static cancelAll() {
  5421. AnimationManager.cancelAll();
  5422. }
  5423. /**
  5424. * Gets all animation controls for the given position data.
  5425. *
  5426. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5427. *
  5428. * @returns {{position: Position, data: object|void, controls: AnimationControl[]}[]} Results array.
  5429. */
  5430. static getScheduled(position) {
  5431. const results = [];
  5432. if (isIterable(position)) {
  5433. let index = -1;
  5434. for (const entry of position) {
  5435. index++;
  5436. const isPosition = this.#isPosition(entry);
  5437. const actualPosition = isPosition ? entry : entry.position;
  5438. if (!this.#isPosition(actualPosition)) {
  5439. console.warn(`AnimationGroupAPI.getScheduled warning: No Position instance found at index: ${index}.`);
  5440. continue;
  5441. }
  5442. const controls = AnimationManager.getScheduled(actualPosition);
  5443. results.push({ position: actualPosition, data: isPosition ? void 0 : entry, controls });
  5444. }
  5445. } else {
  5446. const isPosition = this.#isPosition(position);
  5447. const actualPosition = isPosition ? position : position.position;
  5448. if (!this.#isPosition(actualPosition)) {
  5449. console.warn(`AnimationGroupAPI.getScheduled warning: No Position instance found.`);
  5450. return results;
  5451. }
  5452. const controls = AnimationManager.getScheduled(actualPosition);
  5453. results.push({ position: actualPosition, data: isPosition ? void 0 : position, controls });
  5454. }
  5455. return results;
  5456. }
  5457. /**
  5458. * Provides the `from` animation tween for one or more Position instances as a group.
  5459. *
  5460. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5461. *
  5462. * @param {object|Function} fromData -
  5463. *
  5464. * @param {object|Function} options -
  5465. *
  5466. * @returns {TJSBasicAnimation} Basic animation control.
  5467. */
  5468. static from(position, fromData, options) {
  5469. if (!isObject(fromData) && typeof fromData !== "function") {
  5470. throw new TypeError(`AnimationGroupAPI.from error: 'fromData' is not an object or function.`);
  5471. }
  5472. if (options !== void 0 && !isObject(options) && typeof options !== "function") {
  5473. throw new TypeError(`AnimationGroupAPI.from error: 'options' is not an object or function.`);
  5474. }
  5475. const animationControls = [];
  5476. let index = -1;
  5477. let callbackOptions;
  5478. const hasDataCallback = typeof fromData === "function";
  5479. const hasOptionCallback = typeof options === "function";
  5480. const hasCallback = hasDataCallback || hasOptionCallback;
  5481. if (hasCallback) {
  5482. callbackOptions = { index, position: void 0, data: void 0 };
  5483. }
  5484. let actualFromData = fromData;
  5485. let actualOptions = options;
  5486. if (isIterable(position)) {
  5487. for (const entry of position) {
  5488. index++;
  5489. const isPosition = this.#isPosition(entry);
  5490. const actualPosition = isPosition ? entry : entry.position;
  5491. if (!this.#isPosition(actualPosition)) {
  5492. console.warn(`AnimationGroupAPI.from warning: No Position instance found at index: ${index}.`);
  5493. continue;
  5494. }
  5495. if (hasCallback) {
  5496. callbackOptions.index = index;
  5497. callbackOptions.position = position;
  5498. callbackOptions.data = isPosition ? void 0 : entry;
  5499. }
  5500. if (hasDataCallback) {
  5501. actualFromData = fromData(callbackOptions);
  5502. if (actualFromData === null || actualFromData === void 0) {
  5503. continue;
  5504. }
  5505. if (typeof actualFromData !== "object") {
  5506. throw new TypeError(`AnimationGroupAPI.from error: fromData callback function iteration(${index}) failed to return an object.`);
  5507. }
  5508. }
  5509. if (hasOptionCallback) {
  5510. actualOptions = options(callbackOptions);
  5511. if (actualOptions === null || actualOptions === void 0) {
  5512. continue;
  5513. }
  5514. if (typeof actualOptions !== "object") {
  5515. throw new TypeError(`AnimationGroupAPI.from error: options callback function iteration(${index}) failed to return an object.`);
  5516. }
  5517. }
  5518. animationControls.push(actualPosition.animate.from(actualFromData, actualOptions));
  5519. }
  5520. } else {
  5521. const isPosition = this.#isPosition(position);
  5522. const actualPosition = isPosition ? position : position.position;
  5523. if (!this.#isPosition(actualPosition)) {
  5524. console.warn(`AnimationGroupAPI.from warning: No Position instance found.`);
  5525. return AnimationGroupControl.voidControl;
  5526. }
  5527. if (hasCallback) {
  5528. callbackOptions.index = 0;
  5529. callbackOptions.position = position;
  5530. callbackOptions.data = isPosition ? void 0 : position;
  5531. }
  5532. if (hasDataCallback) {
  5533. actualFromData = fromData(callbackOptions);
  5534. if (typeof actualFromData !== "object") {
  5535. throw new TypeError(
  5536. `AnimationGroupAPI.from error: fromData callback function failed to return an object.`
  5537. );
  5538. }
  5539. }
  5540. if (hasOptionCallback) {
  5541. actualOptions = options(callbackOptions);
  5542. if (typeof actualOptions !== "object") {
  5543. throw new TypeError(
  5544. `AnimationGroupAPI.from error: options callback function failed to return an object.`
  5545. );
  5546. }
  5547. }
  5548. animationControls.push(actualPosition.animate.from(actualFromData, actualOptions));
  5549. }
  5550. return new AnimationGroupControl(animationControls);
  5551. }
  5552. /**
  5553. * Provides the `fromTo` animation tween for one or more Position instances as a group.
  5554. *
  5555. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5556. *
  5557. * @param {object|Function} fromData -
  5558. *
  5559. * @param {object|Function} toData -
  5560. *
  5561. * @param {object|Function} options -
  5562. *
  5563. * @returns {TJSBasicAnimation} Basic animation control.
  5564. */
  5565. static fromTo(position, fromData, toData, options) {
  5566. if (!isObject(fromData) && typeof fromData !== "function") {
  5567. throw new TypeError(`AnimationGroupAPI.fromTo error: 'fromData' is not an object or function.`);
  5568. }
  5569. if (!isObject(toData) && typeof toData !== "function") {
  5570. throw new TypeError(`AnimationGroupAPI.fromTo error: 'toData' is not an object or function.`);
  5571. }
  5572. if (options !== void 0 && !isObject(options) && typeof options !== "function") {
  5573. throw new TypeError(`AnimationGroupAPI.fromTo error: 'options' is not an object or function.`);
  5574. }
  5575. const animationControls = [];
  5576. let index = -1;
  5577. let callbackOptions;
  5578. const hasFromCallback = typeof fromData === "function";
  5579. const hasToCallback = typeof toData === "function";
  5580. const hasOptionCallback = typeof options === "function";
  5581. const hasCallback = hasFromCallback || hasToCallback || hasOptionCallback;
  5582. if (hasCallback) {
  5583. callbackOptions = { index, position: void 0, data: void 0 };
  5584. }
  5585. let actualFromData = fromData;
  5586. let actualToData = toData;
  5587. let actualOptions = options;
  5588. if (isIterable(position)) {
  5589. for (const entry of position) {
  5590. index++;
  5591. const isPosition = this.#isPosition(entry);
  5592. const actualPosition = isPosition ? entry : entry.position;
  5593. if (!this.#isPosition(actualPosition)) {
  5594. console.warn(`AnimationGroupAPI.fromTo warning: No Position instance found at index: ${index}.`);
  5595. continue;
  5596. }
  5597. if (hasCallback) {
  5598. callbackOptions.index = index;
  5599. callbackOptions.position = position;
  5600. callbackOptions.data = isPosition ? void 0 : entry;
  5601. }
  5602. if (hasFromCallback) {
  5603. actualFromData = fromData(callbackOptions);
  5604. if (actualFromData === null || actualFromData === void 0) {
  5605. continue;
  5606. }
  5607. if (typeof actualFromData !== "object") {
  5608. throw new TypeError(`AnimationGroupAPI.fromTo error: fromData callback function iteration(${index}) failed to return an object.`);
  5609. }
  5610. }
  5611. if (hasToCallback) {
  5612. actualToData = toData(callbackOptions);
  5613. if (actualToData === null || actualToData === void 0) {
  5614. continue;
  5615. }
  5616. if (typeof actualToData !== "object") {
  5617. throw new TypeError(`AnimationGroupAPI.fromTo error: toData callback function iteration(${index}) failed to return an object.`);
  5618. }
  5619. }
  5620. if (hasOptionCallback) {
  5621. actualOptions = options(callbackOptions);
  5622. if (actualOptions === null || actualOptions === void 0) {
  5623. continue;
  5624. }
  5625. if (typeof actualOptions !== "object") {
  5626. throw new TypeError(`AnimationGroupAPI.fromTo error: options callback function iteration(${index}) failed to return an object.`);
  5627. }
  5628. }
  5629. animationControls.push(actualPosition.animate.fromTo(actualFromData, actualToData, actualOptions));
  5630. }
  5631. } else {
  5632. const isPosition = this.#isPosition(position);
  5633. const actualPosition = isPosition ? position : position.position;
  5634. if (!this.#isPosition(actualPosition)) {
  5635. console.warn(`AnimationGroupAPI.fromTo warning: No Position instance found.`);
  5636. return AnimationGroupControl.voidControl;
  5637. }
  5638. if (hasCallback) {
  5639. callbackOptions.index = 0;
  5640. callbackOptions.position = position;
  5641. callbackOptions.data = isPosition ? void 0 : position;
  5642. }
  5643. if (hasFromCallback) {
  5644. actualFromData = fromData(callbackOptions);
  5645. if (typeof actualFromData !== "object") {
  5646. throw new TypeError(
  5647. `AnimationGroupAPI.fromTo error: fromData callback function failed to return an object.`
  5648. );
  5649. }
  5650. }
  5651. if (hasToCallback) {
  5652. actualToData = toData(callbackOptions);
  5653. if (typeof actualToData !== "object") {
  5654. throw new TypeError(
  5655. `AnimationGroupAPI.fromTo error: toData callback function failed to return an object.`
  5656. );
  5657. }
  5658. }
  5659. if (hasOptionCallback) {
  5660. actualOptions = options(callbackOptions);
  5661. if (typeof actualOptions !== "object") {
  5662. throw new TypeError(
  5663. `AnimationGroupAPI.fromTo error: options callback function failed to return an object.`
  5664. );
  5665. }
  5666. }
  5667. animationControls.push(actualPosition.animate.fromTo(actualFromData, actualToData, actualOptions));
  5668. }
  5669. return new AnimationGroupControl(animationControls);
  5670. }
  5671. /**
  5672. * Provides the `to` animation tween for one or more Position instances as a group.
  5673. *
  5674. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5675. *
  5676. * @param {object|Function} toData -
  5677. *
  5678. * @param {object|Function} options -
  5679. *
  5680. * @returns {TJSBasicAnimation} Basic animation control.
  5681. */
  5682. static to(position, toData, options) {
  5683. if (!isObject(toData) && typeof toData !== "function") {
  5684. throw new TypeError(`AnimationGroupAPI.to error: 'toData' is not an object or function.`);
  5685. }
  5686. if (options !== void 0 && !isObject(options) && typeof options !== "function") {
  5687. throw new TypeError(`AnimationGroupAPI.to error: 'options' is not an object or function.`);
  5688. }
  5689. const animationControls = [];
  5690. let index = -1;
  5691. let callbackOptions;
  5692. const hasDataCallback = typeof toData === "function";
  5693. const hasOptionCallback = typeof options === "function";
  5694. const hasCallback = hasDataCallback || hasOptionCallback;
  5695. if (hasCallback) {
  5696. callbackOptions = { index, position: void 0, data: void 0 };
  5697. }
  5698. let actualToData = toData;
  5699. let actualOptions = options;
  5700. if (isIterable(position)) {
  5701. for (const entry of position) {
  5702. index++;
  5703. const isPosition = this.#isPosition(entry);
  5704. const actualPosition = isPosition ? entry : entry.position;
  5705. if (!this.#isPosition(actualPosition)) {
  5706. console.warn(`AnimationGroupAPI.to warning: No Position instance found at index: ${index}.`);
  5707. continue;
  5708. }
  5709. if (hasCallback) {
  5710. callbackOptions.index = index;
  5711. callbackOptions.position = position;
  5712. callbackOptions.data = isPosition ? void 0 : entry;
  5713. }
  5714. if (hasDataCallback) {
  5715. actualToData = toData(callbackOptions);
  5716. if (actualToData === null || actualToData === void 0) {
  5717. continue;
  5718. }
  5719. if (typeof actualToData !== "object") {
  5720. throw new TypeError(`AnimationGroupAPI.to error: toData callback function iteration(${index}) failed to return an object.`);
  5721. }
  5722. }
  5723. if (hasOptionCallback) {
  5724. actualOptions = options(callbackOptions);
  5725. if (actualOptions === null || actualOptions === void 0) {
  5726. continue;
  5727. }
  5728. if (typeof actualOptions !== "object") {
  5729. throw new TypeError(`AnimationGroupAPI.to error: options callback function iteration(${index}) failed to return an object.`);
  5730. }
  5731. }
  5732. animationControls.push(actualPosition.animate.to(actualToData, actualOptions));
  5733. }
  5734. } else {
  5735. const isPosition = this.#isPosition(position);
  5736. const actualPosition = isPosition ? position : position.position;
  5737. if (!this.#isPosition(actualPosition)) {
  5738. console.warn(`AnimationGroupAPI.to warning: No Position instance found.`);
  5739. return AnimationGroupControl.voidControl;
  5740. }
  5741. if (hasCallback) {
  5742. callbackOptions.index = 0;
  5743. callbackOptions.position = position;
  5744. callbackOptions.data = isPosition ? void 0 : position;
  5745. }
  5746. if (hasDataCallback) {
  5747. actualToData = toData(callbackOptions);
  5748. if (typeof actualToData !== "object") {
  5749. throw new TypeError(
  5750. `AnimationGroupAPI.to error: toData callback function failed to return an object.`
  5751. );
  5752. }
  5753. }
  5754. if (hasOptionCallback) {
  5755. actualOptions = options(callbackOptions);
  5756. if (typeof actualOptions !== "object") {
  5757. throw new TypeError(
  5758. `AnimationGroupAPI.to error: options callback function failed to return an object.`
  5759. );
  5760. }
  5761. }
  5762. animationControls.push(actualPosition.animate.to(actualToData, actualOptions));
  5763. }
  5764. return new AnimationGroupControl(animationControls);
  5765. }
  5766. /**
  5767. * Provides the `to` animation tween for one or more Position instances as a group.
  5768. *
  5769. * @param {Position|{position: Position}|Iterable<Position>|Iterable<{position: Position}>} position -
  5770. *
  5771. * @param {Iterable<string>} keys -
  5772. *
  5773. * @param {object|Function} options -
  5774. *
  5775. * @returns {quickToCallback} Basic animation control.
  5776. */
  5777. static quickTo(position, keys, options) {
  5778. if (!isIterable(keys)) {
  5779. throw new TypeError(`AnimationGroupAPI.quickTo error: 'keys' is not an iterable list.`);
  5780. }
  5781. if (options !== void 0 && !isObject(options) && typeof options !== "function") {
  5782. throw new TypeError(`AnimationGroupAPI.quickTo error: 'options' is not an object or function.`);
  5783. }
  5784. const quickToCallbacks = [];
  5785. let index = -1;
  5786. const hasOptionCallback = typeof options === "function";
  5787. const callbackOptions = { index, position: void 0, data: void 0 };
  5788. let actualOptions = options;
  5789. if (isIterable(position)) {
  5790. for (const entry of position) {
  5791. index++;
  5792. const isPosition = this.#isPosition(entry);
  5793. const actualPosition = isPosition ? entry : entry.position;
  5794. if (!this.#isPosition(actualPosition)) {
  5795. console.warn(`AnimationGroupAPI.quickTo warning: No Position instance found at index: ${index}.`);
  5796. continue;
  5797. }
  5798. callbackOptions.index = index;
  5799. callbackOptions.position = position;
  5800. callbackOptions.data = isPosition ? void 0 : entry;
  5801. if (hasOptionCallback) {
  5802. actualOptions = options(callbackOptions);
  5803. if (actualOptions === null || actualOptions === void 0) {
  5804. continue;
  5805. }
  5806. if (typeof actualOptions !== "object") {
  5807. throw new TypeError(`AnimationGroupAPI.quickTo error: options callback function iteration(${index}) failed to return an object.`);
  5808. }
  5809. }
  5810. quickToCallbacks.push(actualPosition.animate.quickTo(keys, actualOptions));
  5811. }
  5812. } else {
  5813. const isPosition = this.#isPosition(position);
  5814. const actualPosition = isPosition ? position : position.position;
  5815. if (!this.#isPosition(actualPosition)) {
  5816. console.warn(`AnimationGroupAPI.quickTo warning: No Position instance found.`);
  5817. return () => null;
  5818. }
  5819. callbackOptions.index = 0;
  5820. callbackOptions.position = position;
  5821. callbackOptions.data = isPosition ? void 0 : position;
  5822. if (hasOptionCallback) {
  5823. actualOptions = options(callbackOptions);
  5824. if (typeof actualOptions !== "object") {
  5825. throw new TypeError(
  5826. `AnimationGroupAPI.quickTo error: options callback function failed to return an object.`
  5827. );
  5828. }
  5829. }
  5830. quickToCallbacks.push(actualPosition.animate.quickTo(keys, actualOptions));
  5831. }
  5832. const keysArray = [...keys];
  5833. Object.freeze(keysArray);
  5834. const quickToCB = (...args) => {
  5835. const argsLength = args.length;
  5836. if (argsLength === 0) {
  5837. return;
  5838. }
  5839. if (typeof args[0] === "function") {
  5840. const dataCallback = args[0];
  5841. index = -1;
  5842. let cntr = 0;
  5843. if (isIterable(position)) {
  5844. for (const entry of position) {
  5845. index++;
  5846. const isPosition = this.#isPosition(entry);
  5847. const actualPosition = isPosition ? entry : entry.position;
  5848. if (!this.#isPosition(actualPosition)) {
  5849. continue;
  5850. }
  5851. callbackOptions.index = index;
  5852. callbackOptions.position = position;
  5853. callbackOptions.data = isPosition ? void 0 : entry;
  5854. const toData = dataCallback(callbackOptions);
  5855. if (toData === null || toData === void 0) {
  5856. continue;
  5857. }
  5858. const toDataIterable = isIterable(toData);
  5859. if (!Number.isFinite(toData) && !toDataIterable && typeof toData !== "object") {
  5860. throw new TypeError(`AnimationGroupAPI.quickTo error: toData callback function iteration(${index}) failed to return a finite number, iterable list, or object.`);
  5861. }
  5862. if (toDataIterable) {
  5863. quickToCallbacks[cntr++](...toData);
  5864. } else {
  5865. quickToCallbacks[cntr++](toData);
  5866. }
  5867. }
  5868. } else {
  5869. const isPosition = this.#isPosition(position);
  5870. const actualPosition = isPosition ? position : position.position;
  5871. if (!this.#isPosition(actualPosition)) {
  5872. return;
  5873. }
  5874. callbackOptions.index = 0;
  5875. callbackOptions.position = position;
  5876. callbackOptions.data = isPosition ? void 0 : position;
  5877. const toData = dataCallback(callbackOptions);
  5878. if (toData === null || toData === void 0) {
  5879. return;
  5880. }
  5881. const toDataIterable = isIterable(toData);
  5882. if (!Number.isFinite(toData) && !toDataIterable && typeof toData !== "object") {
  5883. throw new TypeError(`AnimationGroupAPI.quickTo error: toData callback function iteration(${index}) failed to return a finite number, iterable list, or object.`);
  5884. }
  5885. if (toDataIterable) {
  5886. quickToCallbacks[cntr++](...toData);
  5887. } else {
  5888. quickToCallbacks[cntr++](toData);
  5889. }
  5890. }
  5891. } else {
  5892. for (let cntr = quickToCallbacks.length; --cntr >= 0; ) {
  5893. quickToCallbacks[cntr](...args);
  5894. }
  5895. }
  5896. };
  5897. quickToCB.keys = keysArray;
  5898. quickToCB.options = (options2) => {
  5899. if (options2 !== void 0 && !isObject(options2) && typeof options2 !== "function") {
  5900. throw new TypeError(`AnimationGroupAPI.quickTo error: 'options' is not an object or function.`);
  5901. }
  5902. if (isObject(options2)) {
  5903. for (let cntr = quickToCallbacks.length; --cntr >= 0; ) {
  5904. quickToCallbacks[cntr].options(options2);
  5905. }
  5906. } else if (typeof options2 === "function") {
  5907. if (isIterable(position)) {
  5908. index = -1;
  5909. let cntr = 0;
  5910. for (const entry of position) {
  5911. index++;
  5912. const isPosition = this.#isPosition(entry);
  5913. const actualPosition = isPosition ? entry : entry.position;
  5914. if (!this.#isPosition(actualPosition)) {
  5915. console.warn(
  5916. `AnimationGroupAPI.quickTo.options warning: No Position instance found at index: ${index}.`
  5917. );
  5918. continue;
  5919. }
  5920. callbackOptions.index = index;
  5921. callbackOptions.position = position;
  5922. callbackOptions.data = isPosition ? void 0 : entry;
  5923. actualOptions = options2(callbackOptions);
  5924. if (actualOptions === null || actualOptions === void 0) {
  5925. continue;
  5926. }
  5927. if (typeof actualOptions !== "object") {
  5928. throw new TypeError(
  5929. `AnimationGroupAPI.quickTo.options error: options callback function iteration(${index}) failed to return an object.`
  5930. );
  5931. }
  5932. quickToCallbacks[cntr++].options(actualOptions);
  5933. }
  5934. } else {
  5935. const isPosition = this.#isPosition(position);
  5936. const actualPosition = isPosition ? position : position.position;
  5937. if (!this.#isPosition(actualPosition)) {
  5938. console.warn(`AnimationGroupAPI.quickTo.options warning: No Position instance found.`);
  5939. return quickToCB;
  5940. }
  5941. callbackOptions.index = 0;
  5942. callbackOptions.position = position;
  5943. callbackOptions.data = isPosition ? void 0 : position;
  5944. actualOptions = options2(callbackOptions);
  5945. if (typeof actualOptions !== "object") {
  5946. throw new TypeError(
  5947. `AnimationGroupAPI.quickTo error: options callback function failed to return an object.`
  5948. );
  5949. }
  5950. quickToCallbacks[0].options(actualOptions);
  5951. }
  5952. }
  5953. return quickToCB;
  5954. };
  5955. return quickToCB;
  5956. }
  5957. }
  5958. class Centered {
  5959. /**
  5960. * @type {HTMLElement}
  5961. */
  5962. #element;
  5963. /**
  5964. * Provides a manual setting of the element height. As things go `offsetHeight` causes a browser layout and is not
  5965. * performance oriented. If manually set this height is used instead of `offsetHeight`.
  5966. *
  5967. * @type {number}
  5968. */
  5969. #height;
  5970. /**
  5971. * Set from an optional value in the constructor to lock accessors preventing modification.
  5972. */
  5973. #lock;
  5974. /**
  5975. * Provides a manual setting of the element width. As things go `offsetWidth` causes a browser layout and is not
  5976. * performance oriented. If manually set this width is used instead of `offsetWidth`.
  5977. *
  5978. * @type {number}
  5979. */
  5980. #width;
  5981. constructor({ element: element2, lock = false, width: width2, height } = {}) {
  5982. this.element = element2;
  5983. this.width = width2;
  5984. this.height = height;
  5985. this.#lock = typeof lock === "boolean" ? lock : false;
  5986. }
  5987. get element() {
  5988. return this.#element;
  5989. }
  5990. get height() {
  5991. return this.#height;
  5992. }
  5993. get width() {
  5994. return this.#width;
  5995. }
  5996. set element(element2) {
  5997. if (this.#lock) {
  5998. return;
  5999. }
  6000. if (element2 === void 0 || element2 === null || element2 instanceof HTMLElement) {
  6001. this.#element = element2;
  6002. } else {
  6003. throw new TypeError(`'element' is not a HTMLElement, undefined, or null.`);
  6004. }
  6005. }
  6006. set height(height) {
  6007. if (this.#lock) {
  6008. return;
  6009. }
  6010. if (height === void 0 || Number.isFinite(height)) {
  6011. this.#height = height;
  6012. } else {
  6013. throw new TypeError(`'height' is not a finite number or undefined.`);
  6014. }
  6015. }
  6016. set width(width2) {
  6017. if (this.#lock) {
  6018. return;
  6019. }
  6020. if (width2 === void 0 || Number.isFinite(width2)) {
  6021. this.#width = width2;
  6022. } else {
  6023. throw new TypeError(`'width' is not a finite number or undefined.`);
  6024. }
  6025. }
  6026. setDimension(width2, height) {
  6027. if (this.#lock) {
  6028. return;
  6029. }
  6030. if (width2 === void 0 || Number.isFinite(width2)) {
  6031. this.#width = width2;
  6032. } else {
  6033. throw new TypeError(`'width' is not a finite number or undefined.`);
  6034. }
  6035. if (height === void 0 || Number.isFinite(height)) {
  6036. this.#height = height;
  6037. } else {
  6038. throw new TypeError(`'height' is not a finite number or undefined.`);
  6039. }
  6040. }
  6041. getLeft(width2) {
  6042. const boundsWidth = this.#width ?? this.#element?.offsetWidth ?? globalThis.innerWidth;
  6043. return (boundsWidth - width2) / 2;
  6044. }
  6045. getTop(height) {
  6046. const boundsHeight = this.#height ?? this.#element?.offsetHeight ?? globalThis.innerHeight;
  6047. return (boundsHeight - height) / 2;
  6048. }
  6049. }
  6050. const browserCentered = new Centered();
  6051. const positionInitial = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  6052. __proto__: null,
  6053. Centered,
  6054. browserCentered
  6055. }, Symbol.toStringTag, { value: "Module" }));
  6056. class PositionChangeSet {
  6057. constructor() {
  6058. this.left = false;
  6059. this.top = false;
  6060. this.width = false;
  6061. this.height = false;
  6062. this.maxHeight = false;
  6063. this.maxWidth = false;
  6064. this.minHeight = false;
  6065. this.minWidth = false;
  6066. this.zIndex = false;
  6067. this.transform = false;
  6068. this.transformOrigin = false;
  6069. }
  6070. hasChange() {
  6071. return this.left || this.top || this.width || this.height || this.maxHeight || this.maxWidth || this.minHeight || this.minWidth || this.zIndex || this.transform || this.transformOrigin;
  6072. }
  6073. set(value) {
  6074. this.left = value;
  6075. this.top = value;
  6076. this.width = value;
  6077. this.height = value;
  6078. this.maxHeight = value;
  6079. this.maxWidth = value;
  6080. this.minHeight = value;
  6081. this.minWidth = value;
  6082. this.zIndex = value;
  6083. this.transform = value;
  6084. this.transformOrigin = value;
  6085. }
  6086. }
  6087. class PositionData {
  6088. constructor({
  6089. height = null,
  6090. left = null,
  6091. maxHeight = null,
  6092. maxWidth = null,
  6093. minHeight = null,
  6094. minWidth = null,
  6095. rotateX = null,
  6096. rotateY = null,
  6097. rotateZ = null,
  6098. scale: scale2 = null,
  6099. translateX = null,
  6100. translateY = null,
  6101. translateZ = null,
  6102. top = null,
  6103. transformOrigin = null,
  6104. width: width2 = null,
  6105. zIndex = null
  6106. } = {}) {
  6107. this.height = height;
  6108. this.left = left;
  6109. this.maxHeight = maxHeight;
  6110. this.maxWidth = maxWidth;
  6111. this.minHeight = minHeight;
  6112. this.minWidth = minWidth;
  6113. this.rotateX = rotateX;
  6114. this.rotateY = rotateY;
  6115. this.rotateZ = rotateZ;
  6116. this.scale = scale2;
  6117. this.top = top;
  6118. this.transformOrigin = transformOrigin;
  6119. this.translateX = translateX;
  6120. this.translateY = translateY;
  6121. this.translateZ = translateZ;
  6122. this.width = width2;
  6123. this.zIndex = zIndex;
  6124. Object.seal(this);
  6125. }
  6126. /**
  6127. * Copies given data to this instance.
  6128. *
  6129. * @param {PositionData} data - Copy from this instance.
  6130. *
  6131. * @returns {PositionData} This instance.
  6132. */
  6133. copy(data) {
  6134. this.height = data.height;
  6135. this.left = data.left;
  6136. this.maxHeight = data.maxHeight;
  6137. this.maxWidth = data.maxWidth;
  6138. this.minHeight = data.minHeight;
  6139. this.minWidth = data.minWidth;
  6140. this.rotateX = data.rotateX;
  6141. this.rotateY = data.rotateY;
  6142. this.rotateZ = data.rotateZ;
  6143. this.scale = data.scale;
  6144. this.top = data.top;
  6145. this.transformOrigin = data.transformOrigin;
  6146. this.translateX = data.translateX;
  6147. this.translateY = data.translateY;
  6148. this.translateZ = data.translateZ;
  6149. this.width = data.width;
  6150. this.zIndex = data.zIndex;
  6151. return this;
  6152. }
  6153. }
  6154. class PositionStateAPI {
  6155. /** @type {PositionData} */
  6156. #data;
  6157. /**
  6158. * @type {Map<string, PositionDataExtended>}
  6159. */
  6160. #dataSaved = /* @__PURE__ */ new Map();
  6161. /** @type {Position} */
  6162. #position;
  6163. /** @type {Transforms} */
  6164. #transforms;
  6165. constructor(position, data, transforms) {
  6166. this.#position = position;
  6167. this.#data = data;
  6168. this.#transforms = transforms;
  6169. }
  6170. /**
  6171. * Returns any stored save state by name.
  6172. *
  6173. * @param {string} name - Saved data set name.
  6174. *
  6175. * @returns {PositionDataExtended} The saved data set.
  6176. */
  6177. get({ name }) {
  6178. if (typeof name !== "string") {
  6179. throw new TypeError(`Position - getSave error: 'name' is not a string.`);
  6180. }
  6181. return this.#dataSaved.get(name);
  6182. }
  6183. /**
  6184. * Returns any associated default data.
  6185. *
  6186. * @returns {PositionDataExtended} Associated default data.
  6187. */
  6188. getDefault() {
  6189. return this.#dataSaved.get("#defaultData");
  6190. }
  6191. /**
  6192. * Removes and returns any position state by name.
  6193. *
  6194. * @param {object} options - Options.
  6195. *
  6196. * @param {string} options.name - Name to remove and retrieve.
  6197. *
  6198. * @returns {PositionDataExtended} Saved position data.
  6199. */
  6200. remove({ name }) {
  6201. if (typeof name !== "string") {
  6202. throw new TypeError(`Position - remove: 'name' is not a string.`);
  6203. }
  6204. const data = this.#dataSaved.get(name);
  6205. this.#dataSaved.delete(name);
  6206. return data;
  6207. }
  6208. /**
  6209. * Resets data to default values and invokes set.
  6210. *
  6211. * @param {object} [opts] - Optional parameters.
  6212. *
  6213. * @param {boolean} [opts.keepZIndex=false] - When true keeps current z-index.
  6214. *
  6215. * @param {boolean} [opts.invokeSet=true] - When true invokes set method.
  6216. *
  6217. * @returns {boolean} Operation successful.
  6218. */
  6219. reset({ keepZIndex = false, invokeSet = true } = {}) {
  6220. const defaultData = this.#dataSaved.get("#defaultData");
  6221. if (typeof defaultData !== "object") {
  6222. return false;
  6223. }
  6224. if (this.#position.animate.isScheduled) {
  6225. this.#position.animate.cancel();
  6226. }
  6227. const zIndex = this.#position.zIndex;
  6228. const data = Object.assign({}, defaultData);
  6229. if (keepZIndex) {
  6230. data.zIndex = zIndex;
  6231. }
  6232. this.#transforms.reset(data);
  6233. if (this.#position.parent?.reactive?.minimized) {
  6234. this.#position.parent?.maximize?.({ animate: false, duration: 0 });
  6235. }
  6236. if (invokeSet) {
  6237. setTimeout(() => this.#position.set(data), 0);
  6238. }
  6239. return true;
  6240. }
  6241. /**
  6242. * Restores a saved positional state returning the data. Several optional parameters are available
  6243. * to control whether the restore action occurs silently (no store / inline styles updates), animates
  6244. * to the stored data, or simply sets the stored data. Restoring via {@link AnimationAPI.to} allows
  6245. * specification of the duration, easing, and interpolate functions along with configuring a Promise to be
  6246. * returned if awaiting the end of the animation.
  6247. *
  6248. * @param {object} params - Parameters
  6249. *
  6250. * @param {string} params.name - Saved data set name.
  6251. *
  6252. * @param {boolean} [params.remove=false] - Remove data set.
  6253. *
  6254. * @param {Iterable<string>} [params.properties] - Specific properties to set / animate.
  6255. *
  6256. * @param {boolean} [params.silent] - Set position data directly; no store or style updates.
  6257. *
  6258. * @param {boolean} [params.async=false] - If animating return a Promise that resolves with any saved data.
  6259. *
  6260. * @param {boolean} [params.animateTo=false] - Animate to restore data.
  6261. *
  6262. * @param {number} [params.duration=0.1] - Duration in seconds.
  6263. *
  6264. * @param {Function} [params.ease=linear] - Easing function.
  6265. *
  6266. * @param {Function} [params.interpolate=lerp] - Interpolation function.
  6267. *
  6268. * @returns {PositionDataExtended|Promise<PositionDataExtended>} Saved position data.
  6269. */
  6270. restore({
  6271. name,
  6272. remove = false,
  6273. properties,
  6274. silent = false,
  6275. async = false,
  6276. animateTo = false,
  6277. duration = 0.1,
  6278. ease = identity,
  6279. interpolate: interpolate2 = lerp$5
  6280. }) {
  6281. if (typeof name !== "string") {
  6282. throw new TypeError(`Position - restore error: 'name' is not a string.`);
  6283. }
  6284. const dataSaved = this.#dataSaved.get(name);
  6285. if (dataSaved) {
  6286. if (remove) {
  6287. this.#dataSaved.delete(name);
  6288. }
  6289. let data = dataSaved;
  6290. if (isIterable(properties)) {
  6291. data = {};
  6292. for (const property of properties) {
  6293. data[property] = dataSaved[property];
  6294. }
  6295. }
  6296. if (silent) {
  6297. for (const property in data) {
  6298. this.#data[property] = data[property];
  6299. }
  6300. return dataSaved;
  6301. } else if (animateTo) {
  6302. if (data.transformOrigin !== this.#position.transformOrigin) {
  6303. this.#position.transformOrigin = data.transformOrigin;
  6304. }
  6305. if (async) {
  6306. return this.#position.animate.to(data, { duration, ease, interpolate: interpolate2 }).finished.then(() => dataSaved);
  6307. } else {
  6308. this.#position.animate.to(data, { duration, ease, interpolate: interpolate2 });
  6309. }
  6310. } else {
  6311. this.#position.set(data);
  6312. }
  6313. }
  6314. return dataSaved;
  6315. }
  6316. /**
  6317. * Saves current position state with the opportunity to add extra data to the saved state.
  6318. *
  6319. * @param {object} opts - Options.
  6320. *
  6321. * @param {string} opts.name - name to index this saved data.
  6322. *
  6323. * @param {...*} [opts.extra] - Extra data to add to saved data.
  6324. *
  6325. * @returns {PositionData} Current position data
  6326. */
  6327. save({ name, ...extra }) {
  6328. if (typeof name !== "string") {
  6329. throw new TypeError(`Position - save error: 'name' is not a string.`);
  6330. }
  6331. const data = this.#position.get(extra);
  6332. this.#dataSaved.set(name, data);
  6333. return data;
  6334. }
  6335. /**
  6336. * Directly sets a position state.
  6337. *
  6338. * @param {object} opts - Options.
  6339. *
  6340. * @param {string} opts.name - name to index this saved data.
  6341. *
  6342. * @param {...*} [opts.data] - Position data to set.
  6343. */
  6344. set({ name, ...data }) {
  6345. if (typeof name !== "string") {
  6346. throw new TypeError(`Position - set error: 'name' is not a string.`);
  6347. }
  6348. this.#dataSaved.set(name, data);
  6349. }
  6350. }
  6351. class StyleCache {
  6352. constructor() {
  6353. this.el = void 0;
  6354. this.computed = void 0;
  6355. this.marginLeft = void 0;
  6356. this.marginTop = void 0;
  6357. this.maxHeight = void 0;
  6358. this.maxWidth = void 0;
  6359. this.minHeight = void 0;
  6360. this.minWidth = void 0;
  6361. this.hasWillChange = false;
  6362. this.resizeObserved = {
  6363. contentHeight: void 0,
  6364. contentWidth: void 0,
  6365. offsetHeight: void 0,
  6366. offsetWidth: void 0
  6367. };
  6368. const storeResizeObserved = writable$1(this.resizeObserved);
  6369. this.stores = {
  6370. element: writable$1(this.el),
  6371. resizeContentHeight: propertyStore(storeResizeObserved, "contentHeight"),
  6372. resizeContentWidth: propertyStore(storeResizeObserved, "contentWidth"),
  6373. resizeObserved: storeResizeObserved,
  6374. resizeOffsetHeight: propertyStore(storeResizeObserved, "offsetHeight"),
  6375. resizeOffsetWidth: propertyStore(storeResizeObserved, "offsetWidth")
  6376. };
  6377. }
  6378. /**
  6379. * Returns the cached offsetHeight from any attached `resizeObserver` action otherwise gets the offsetHeight from
  6380. * the element directly. The more optimized path is using `resizeObserver` as getting it from the element
  6381. * directly is more expensive and alters the execution order of an animation frame.
  6382. *
  6383. * @returns {number} The element offsetHeight.
  6384. */
  6385. get offsetHeight() {
  6386. if (this.el instanceof HTMLElement) {
  6387. return this.resizeObserved.offsetHeight !== void 0 ? this.resizeObserved.offsetHeight : this.el.offsetHeight;
  6388. }
  6389. throw new Error(`StyleCache - get offsetHeight error: no element assigned.`);
  6390. }
  6391. /**
  6392. * Returns the cached offsetWidth from any attached `resizeObserver` action otherwise gets the offsetWidth from
  6393. * the element directly. The more optimized path is using `resizeObserver` as getting it from the element
  6394. * directly is more expensive and alters the execution order of an animation frame.
  6395. *
  6396. * @returns {number} The element offsetHeight.
  6397. */
  6398. get offsetWidth() {
  6399. if (this.el instanceof HTMLElement) {
  6400. return this.resizeObserved.offsetWidth !== void 0 ? this.resizeObserved.offsetWidth : this.el.offsetWidth;
  6401. }
  6402. throw new Error(`StyleCache - get offsetWidth error: no element assigned.`);
  6403. }
  6404. /**
  6405. * @param {HTMLElement} el -
  6406. *
  6407. * @returns {boolean} Does element match cached element.
  6408. */
  6409. hasData(el) {
  6410. return this.el === el;
  6411. }
  6412. /**
  6413. * Resets the style cache.
  6414. */
  6415. reset() {
  6416. if (this.el instanceof HTMLElement && this.el.isConnected && !this.hasWillChange) {
  6417. this.el.style.willChange = null;
  6418. }
  6419. this.el = void 0;
  6420. this.computed = void 0;
  6421. this.marginLeft = void 0;
  6422. this.marginTop = void 0;
  6423. this.maxHeight = void 0;
  6424. this.maxWidth = void 0;
  6425. this.minHeight = void 0;
  6426. this.minWidth = void 0;
  6427. this.hasWillChange = false;
  6428. this.resizeObserved.contentHeight = void 0;
  6429. this.resizeObserved.contentWidth = void 0;
  6430. this.resizeObserved.offsetHeight = void 0;
  6431. this.resizeObserved.offsetWidth = void 0;
  6432. this.stores.element.set(void 0);
  6433. }
  6434. /**
  6435. * Updates the style cache with new data from the given element.
  6436. *
  6437. * @param {HTMLElement} el - An HTML element.
  6438. */
  6439. update(el) {
  6440. this.el = el;
  6441. this.computed = globalThis.getComputedStyle(el);
  6442. this.marginLeft = styleParsePixels(el.style.marginLeft) ?? styleParsePixels(this.computed.marginLeft);
  6443. this.marginTop = styleParsePixels(el.style.marginTop) ?? styleParsePixels(this.computed.marginTop);
  6444. this.maxHeight = styleParsePixels(el.style.maxHeight) ?? styleParsePixels(this.computed.maxHeight);
  6445. this.maxWidth = styleParsePixels(el.style.maxWidth) ?? styleParsePixels(this.computed.maxWidth);
  6446. this.minHeight = styleParsePixels(el.style.minHeight) ?? styleParsePixels(this.computed.minHeight);
  6447. this.minWidth = styleParsePixels(el.style.minWidth) ?? styleParsePixels(this.computed.minWidth);
  6448. const willChange = el.style.willChange !== "" ? el.style.willChange : this.computed.willChange;
  6449. this.hasWillChange = willChange !== "" && willChange !== "auto";
  6450. this.stores.element.set(el);
  6451. }
  6452. }
  6453. class TransformData {
  6454. constructor() {
  6455. Object.seal(this);
  6456. }
  6457. /**
  6458. * Stores the calculated bounding rectangle.
  6459. *
  6460. * @type {DOMRect}
  6461. */
  6462. #boundingRect = new DOMRect();
  6463. /**
  6464. * Stores the individual transformed corner points of the window in screenspace clockwise from:
  6465. * top left -> top right -> bottom right -> bottom left.
  6466. *
  6467. * @type {Vector3[]}
  6468. */
  6469. #corners = [vec3.create(), vec3.create(), vec3.create(), vec3.create()];
  6470. /**
  6471. * Stores the current gl-matrix mat4 data.
  6472. *
  6473. * @type {Matrix4}
  6474. */
  6475. #mat4 = mat4.create();
  6476. /**
  6477. * Stores the pre & post origin translations to apply to matrix transforms.
  6478. *
  6479. * @type {Matrix4[]}
  6480. */
  6481. #originTranslations = [mat4.create(), mat4.create()];
  6482. /**
  6483. * @returns {DOMRect} The bounding rectangle.
  6484. */
  6485. get boundingRect() {
  6486. return this.#boundingRect;
  6487. }
  6488. /**
  6489. * @returns {Vector3[]} The transformed corner points as vec3 in screen space.
  6490. */
  6491. get corners() {
  6492. return this.#corners;
  6493. }
  6494. /**
  6495. * @returns {string} Returns the CSS style string for the transform matrix.
  6496. */
  6497. get css() {
  6498. return `matrix3d(${this.mat4.join(",")})`;
  6499. }
  6500. /**
  6501. * @returns {Matrix4} The transform matrix.
  6502. */
  6503. get mat4() {
  6504. return this.#mat4;
  6505. }
  6506. /**
  6507. * @returns {Matrix4[]} The pre / post translation matrices for origin translation.
  6508. */
  6509. get originTranslations() {
  6510. return this.#originTranslations;
  6511. }
  6512. }
  6513. class AdapterValidators {
  6514. /** @type {boolean} */
  6515. #enabled = true;
  6516. /**
  6517. * @type {ValidatorData[]}
  6518. */
  6519. #validatorData;
  6520. #mapUnsubscribe = /* @__PURE__ */ new Map();
  6521. /**
  6522. * @returns {[AdapterValidators, ValidatorData[]]} Returns this and internal storage for validator adapter.
  6523. */
  6524. constructor() {
  6525. this.#validatorData = [];
  6526. Object.seal(this);
  6527. return [this, this.#validatorData];
  6528. }
  6529. /**
  6530. * @returns {boolean} Returns the enabled state.s
  6531. */
  6532. get enabled() {
  6533. return this.#enabled;
  6534. }
  6535. /**
  6536. * @returns {number} Returns the length of the validators array.
  6537. */
  6538. get length() {
  6539. return this.#validatorData.length;
  6540. }
  6541. /**
  6542. * @param {boolean} enabled - Sets enabled state.
  6543. */
  6544. set enabled(enabled) {
  6545. if (typeof enabled !== "boolean") {
  6546. throw new TypeError(`'enabled' is not a boolean.`);
  6547. }
  6548. this.#enabled = enabled;
  6549. }
  6550. /**
  6551. * Provides an iterator for validators.
  6552. *
  6553. * @returns {Generator<ValidatorData|undefined>} Generator / iterator of validators.
  6554. * @yields {ValidatorData}
  6555. */
  6556. *[Symbol.iterator]() {
  6557. if (this.#validatorData.length === 0) {
  6558. return;
  6559. }
  6560. for (const entry of this.#validatorData) {
  6561. yield { ...entry };
  6562. }
  6563. }
  6564. /**
  6565. * @param {...(ValidatorFn|ValidatorData)} validators -
  6566. */
  6567. add(...validators) {
  6568. for (const validator of validators) {
  6569. const validatorType = typeof validator;
  6570. if (validatorType !== "function" && validatorType !== "object" || validator === null) {
  6571. throw new TypeError(`AdapterValidator error: 'validator' is not a function or object.`);
  6572. }
  6573. let data = void 0;
  6574. let subscribeFn = void 0;
  6575. switch (validatorType) {
  6576. case "function":
  6577. data = {
  6578. id: void 0,
  6579. validator,
  6580. weight: 1
  6581. };
  6582. subscribeFn = validator.subscribe;
  6583. break;
  6584. case "object":
  6585. if (typeof validator.validator !== "function") {
  6586. throw new TypeError(`AdapterValidator error: 'validator' attribute is not a function.`);
  6587. }
  6588. if (validator.weight !== void 0 && typeof validator.weight !== "number" || (validator.weight < 0 || validator.weight > 1)) {
  6589. throw new TypeError(
  6590. `AdapterValidator error: 'weight' attribute is not a number between '0 - 1' inclusive.`
  6591. );
  6592. }
  6593. data = {
  6594. id: validator.id !== void 0 ? validator.id : void 0,
  6595. validator: validator.validator.bind(validator),
  6596. weight: validator.weight || 1,
  6597. instance: validator
  6598. };
  6599. subscribeFn = validator.validator.subscribe ?? validator.subscribe;
  6600. break;
  6601. }
  6602. const index = this.#validatorData.findIndex((value) => {
  6603. return data.weight < value.weight;
  6604. });
  6605. if (index >= 0) {
  6606. this.#validatorData.splice(index, 0, data);
  6607. } else {
  6608. this.#validatorData.push(data);
  6609. }
  6610. if (typeof subscribeFn === "function") {
  6611. const unsubscribe = subscribeFn();
  6612. if (typeof unsubscribe !== "function") {
  6613. throw new TypeError(
  6614. "AdapterValidator error: Filter has subscribe function, but no unsubscribe function is returned."
  6615. );
  6616. }
  6617. if (this.#mapUnsubscribe.has(data.validator)) {
  6618. throw new Error(
  6619. "AdapterValidator error: Filter added already has an unsubscribe function registered."
  6620. );
  6621. }
  6622. this.#mapUnsubscribe.set(data.validator, unsubscribe);
  6623. }
  6624. }
  6625. }
  6626. clear() {
  6627. this.#validatorData.length = 0;
  6628. for (const unsubscribe of this.#mapUnsubscribe.values()) {
  6629. unsubscribe();
  6630. }
  6631. this.#mapUnsubscribe.clear();
  6632. }
  6633. /**
  6634. * @param {...(ValidatorFn|ValidatorData)} validators -
  6635. */
  6636. remove(...validators) {
  6637. const length = this.#validatorData.length;
  6638. if (length === 0) {
  6639. return;
  6640. }
  6641. for (const data of validators) {
  6642. const actualValidator = typeof data === "function" ? data : isObject(data) ? data.validator : void 0;
  6643. if (!actualValidator) {
  6644. continue;
  6645. }
  6646. for (let cntr = this.#validatorData.length; --cntr >= 0; ) {
  6647. if (this.#validatorData[cntr].validator === actualValidator) {
  6648. this.#validatorData.splice(cntr, 1);
  6649. let unsubscribe = void 0;
  6650. if (typeof (unsubscribe = this.#mapUnsubscribe.get(actualValidator)) === "function") {
  6651. unsubscribe();
  6652. this.#mapUnsubscribe.delete(actualValidator);
  6653. }
  6654. }
  6655. }
  6656. }
  6657. }
  6658. /**
  6659. * Remove validators by the provided callback. The callback takes 3 parameters: `id`, `validator`, and `weight`.
  6660. * Any truthy value returned will remove that validator.
  6661. *
  6662. * @param {function(*, ValidatorFn, number): boolean} callback - Callback function to evaluate each validator
  6663. * entry.
  6664. */
  6665. removeBy(callback) {
  6666. const length = this.#validatorData.length;
  6667. if (length === 0) {
  6668. return;
  6669. }
  6670. if (typeof callback !== "function") {
  6671. throw new TypeError(`AdapterValidator error: 'callback' is not a function.`);
  6672. }
  6673. this.#validatorData = this.#validatorData.filter((data) => {
  6674. const remove = callback.call(callback, { ...data });
  6675. if (remove) {
  6676. let unsubscribe;
  6677. if (typeof (unsubscribe = this.#mapUnsubscribe.get(data.validator)) === "function") {
  6678. unsubscribe();
  6679. this.#mapUnsubscribe.delete(data.validator);
  6680. }
  6681. }
  6682. return !remove;
  6683. });
  6684. }
  6685. removeById(...ids) {
  6686. const length = this.#validatorData.length;
  6687. if (length === 0) {
  6688. return;
  6689. }
  6690. this.#validatorData = this.#validatorData.filter((data) => {
  6691. let remove = false;
  6692. for (const id of ids) {
  6693. remove |= data.id === id;
  6694. }
  6695. if (remove) {
  6696. let unsubscribe;
  6697. if (typeof (unsubscribe = this.#mapUnsubscribe.get(data.validator)) === "function") {
  6698. unsubscribe();
  6699. this.#mapUnsubscribe.delete(data.validator);
  6700. }
  6701. }
  6702. return !remove;
  6703. });
  6704. }
  6705. }
  6706. class BasicBounds {
  6707. /**
  6708. * When true constrains the min / max width or height to element.
  6709. *
  6710. * @type {boolean}
  6711. */
  6712. #constrain;
  6713. /**
  6714. * @type {HTMLElement}
  6715. */
  6716. #element;
  6717. /**
  6718. * When true the validator is active.
  6719. *
  6720. * @type {boolean}
  6721. */
  6722. #enabled;
  6723. /**
  6724. * Provides a manual setting of the element height. As things go `offsetHeight` causes a browser layout and is not
  6725. * performance oriented. If manually set this height is used instead of `offsetHeight`.
  6726. *
  6727. * @type {number}
  6728. */
  6729. #height;
  6730. /**
  6731. * Set from an optional value in the constructor to lock accessors preventing modification.
  6732. */
  6733. #lock;
  6734. /**
  6735. * Provides a manual setting of the element width. As things go `offsetWidth` causes a browser layout and is not
  6736. * performance oriented. If manually set this width is used instead of `offsetWidth`.
  6737. *
  6738. * @type {number}
  6739. */
  6740. #width;
  6741. constructor({ constrain = true, element: element2, enabled = true, lock = false, width: width2, height } = {}) {
  6742. this.element = element2;
  6743. this.constrain = constrain;
  6744. this.enabled = enabled;
  6745. this.width = width2;
  6746. this.height = height;
  6747. this.#lock = typeof lock === "boolean" ? lock : false;
  6748. }
  6749. get constrain() {
  6750. return this.#constrain;
  6751. }
  6752. get element() {
  6753. return this.#element;
  6754. }
  6755. get enabled() {
  6756. return this.#enabled;
  6757. }
  6758. get height() {
  6759. return this.#height;
  6760. }
  6761. get width() {
  6762. return this.#width;
  6763. }
  6764. set constrain(constrain) {
  6765. if (this.#lock) {
  6766. return;
  6767. }
  6768. if (typeof constrain !== "boolean") {
  6769. throw new TypeError(`'constrain' is not a boolean.`);
  6770. }
  6771. this.#constrain = constrain;
  6772. }
  6773. set element(element2) {
  6774. if (this.#lock) {
  6775. return;
  6776. }
  6777. if (element2 === void 0 || element2 === null || element2 instanceof HTMLElement) {
  6778. this.#element = element2;
  6779. } else {
  6780. throw new TypeError(`'element' is not a HTMLElement, undefined, or null.`);
  6781. }
  6782. }
  6783. set enabled(enabled) {
  6784. if (this.#lock) {
  6785. return;
  6786. }
  6787. if (typeof enabled !== "boolean") {
  6788. throw new TypeError(`'enabled' is not a boolean.`);
  6789. }
  6790. this.#enabled = enabled;
  6791. }
  6792. set height(height) {
  6793. if (this.#lock) {
  6794. return;
  6795. }
  6796. if (height === void 0 || Number.isFinite(height)) {
  6797. this.#height = height;
  6798. } else {
  6799. throw new TypeError(`'height' is not a finite number or undefined.`);
  6800. }
  6801. }
  6802. set width(width2) {
  6803. if (this.#lock) {
  6804. return;
  6805. }
  6806. if (width2 === void 0 || Number.isFinite(width2)) {
  6807. this.#width = width2;
  6808. } else {
  6809. throw new TypeError(`'width' is not a finite number or undefined.`);
  6810. }
  6811. }
  6812. setDimension(width2, height) {
  6813. if (this.#lock) {
  6814. return;
  6815. }
  6816. if (width2 === void 0 || Number.isFinite(width2)) {
  6817. this.#width = width2;
  6818. } else {
  6819. throw new TypeError(`'width' is not a finite number or undefined.`);
  6820. }
  6821. if (height === void 0 || Number.isFinite(height)) {
  6822. this.#height = height;
  6823. } else {
  6824. throw new TypeError(`'height' is not a finite number or undefined.`);
  6825. }
  6826. }
  6827. /**
  6828. * Provides a validator that respects transforms in positional data constraining the position to within the target
  6829. * elements bounds.
  6830. *
  6831. * @param {ValidationData} valData - The associated validation data for position updates.
  6832. *
  6833. * @returns {PositionData} Potentially adjusted position data.
  6834. */
  6835. validator(valData) {
  6836. if (!this.#enabled) {
  6837. return valData.position;
  6838. }
  6839. const boundsWidth = this.#width ?? this.#element?.offsetWidth ?? globalThis.innerWidth;
  6840. const boundsHeight = this.#height ?? this.#element?.offsetHeight ?? globalThis.innerHeight;
  6841. if (typeof valData.position.width === "number") {
  6842. const maxW = valData.maxWidth ?? (this.#constrain ? boundsWidth : Number.MAX_SAFE_INTEGER);
  6843. valData.position.width = valData.width = Math.clamped(valData.position.width, valData.minWidth, maxW);
  6844. if (valData.width + valData.position.left + valData.marginLeft > boundsWidth) {
  6845. valData.position.left = boundsWidth - valData.width - valData.marginLeft;
  6846. }
  6847. }
  6848. if (typeof valData.position.height === "number") {
  6849. const maxH = valData.maxHeight ?? (this.#constrain ? boundsHeight : Number.MAX_SAFE_INTEGER);
  6850. valData.position.height = valData.height = Math.clamped(valData.position.height, valData.minHeight, maxH);
  6851. if (valData.height + valData.position.top + valData.marginTop > boundsHeight) {
  6852. valData.position.top = boundsHeight - valData.height - valData.marginTop;
  6853. }
  6854. }
  6855. const maxL = Math.max(boundsWidth - valData.width - valData.marginLeft, 0);
  6856. valData.position.left = Math.round(Math.clamped(valData.position.left, 0, maxL));
  6857. const maxT = Math.max(boundsHeight - valData.height - valData.marginTop, 0);
  6858. valData.position.top = Math.round(Math.clamped(valData.position.top, 0, maxT));
  6859. return valData.position;
  6860. }
  6861. }
  6862. const s_TRANSFORM_DATA = new TransformData();
  6863. class TransformBounds {
  6864. /**
  6865. * When true constrains the min / max width or height to element.
  6866. *
  6867. * @type {boolean}
  6868. */
  6869. #constrain;
  6870. /**
  6871. * @type {HTMLElement}
  6872. */
  6873. #element;
  6874. /**
  6875. * When true the validator is active.
  6876. *
  6877. * @type {boolean}
  6878. */
  6879. #enabled;
  6880. /**
  6881. * Provides a manual setting of the element height. As things go `offsetHeight` causes a browser layout and is not
  6882. * performance oriented. If manually set this height is used instead of `offsetHeight`.
  6883. *
  6884. * @type {number}
  6885. */
  6886. #height;
  6887. /**
  6888. * Set from an optional value in the constructor to lock accessors preventing modification.
  6889. */
  6890. #lock;
  6891. /**
  6892. * Provides a manual setting of the element width. As things go `offsetWidth` causes a browser layout and is not
  6893. * performance oriented. If manually set this width is used instead of `offsetWidth`.
  6894. *
  6895. * @type {number}
  6896. */
  6897. #width;
  6898. constructor({ constrain = true, element: element2, enabled = true, lock = false, width: width2, height } = {}) {
  6899. this.element = element2;
  6900. this.constrain = constrain;
  6901. this.enabled = enabled;
  6902. this.width = width2;
  6903. this.height = height;
  6904. this.#lock = typeof lock === "boolean" ? lock : false;
  6905. }
  6906. get constrain() {
  6907. return this.#constrain;
  6908. }
  6909. get element() {
  6910. return this.#element;
  6911. }
  6912. get enabled() {
  6913. return this.#enabled;
  6914. }
  6915. get height() {
  6916. return this.#height;
  6917. }
  6918. get width() {
  6919. return this.#width;
  6920. }
  6921. set constrain(constrain) {
  6922. if (this.#lock) {
  6923. return;
  6924. }
  6925. if (typeof constrain !== "boolean") {
  6926. throw new TypeError(`'constrain' is not a boolean.`);
  6927. }
  6928. this.#constrain = constrain;
  6929. }
  6930. set element(element2) {
  6931. if (this.#lock) {
  6932. return;
  6933. }
  6934. if (element2 === void 0 || element2 === null || element2 instanceof HTMLElement) {
  6935. this.#element = element2;
  6936. } else {
  6937. throw new TypeError(`'element' is not a HTMLElement, undefined, or null.`);
  6938. }
  6939. }
  6940. set enabled(enabled) {
  6941. if (this.#lock) {
  6942. return;
  6943. }
  6944. if (typeof enabled !== "boolean") {
  6945. throw new TypeError(`'enabled' is not a boolean.`);
  6946. }
  6947. this.#enabled = enabled;
  6948. }
  6949. set height(height) {
  6950. if (this.#lock) {
  6951. return;
  6952. }
  6953. if (height === void 0 || Number.isFinite(height)) {
  6954. this.#height = height;
  6955. } else {
  6956. throw new TypeError(`'height' is not a finite number or undefined.`);
  6957. }
  6958. }
  6959. set width(width2) {
  6960. if (this.#lock) {
  6961. return;
  6962. }
  6963. if (width2 === void 0 || Number.isFinite(width2)) {
  6964. this.#width = width2;
  6965. } else {
  6966. throw new TypeError(`'width' is not a finite number or undefined.`);
  6967. }
  6968. }
  6969. setDimension(width2, height) {
  6970. if (this.#lock) {
  6971. return;
  6972. }
  6973. if (width2 === void 0 || Number.isFinite(width2)) {
  6974. this.#width = width2;
  6975. } else {
  6976. throw new TypeError(`'width' is not a finite number or undefined.`);
  6977. }
  6978. if (height === void 0 || Number.isFinite(height)) {
  6979. this.#height = height;
  6980. } else {
  6981. throw new TypeError(`'height' is not a finite number or undefined.`);
  6982. }
  6983. }
  6984. /**
  6985. * Provides a validator that respects transforms in positional data constraining the position to within the target
  6986. * elements bounds.
  6987. *
  6988. * @param {ValidationData} valData - The associated validation data for position updates.
  6989. *
  6990. * @returns {PositionData} Potentially adjusted position data.
  6991. */
  6992. validator(valData) {
  6993. if (!this.#enabled) {
  6994. return valData.position;
  6995. }
  6996. const boundsWidth = this.#width ?? this.#element?.offsetWidth ?? globalThis.innerWidth;
  6997. const boundsHeight = this.#height ?? this.#element?.offsetHeight ?? globalThis.innerHeight;
  6998. if (typeof valData.position.width === "number") {
  6999. const maxW = valData.maxWidth ?? (this.#constrain ? boundsWidth : Number.MAX_SAFE_INTEGER);
  7000. valData.position.width = Math.clamped(valData.width, valData.minWidth, maxW);
  7001. }
  7002. if (typeof valData.position.height === "number") {
  7003. const maxH = valData.maxHeight ?? (this.#constrain ? boundsHeight : Number.MAX_SAFE_INTEGER);
  7004. valData.position.height = Math.clamped(valData.height, valData.minHeight, maxH);
  7005. }
  7006. const data = valData.transforms.getData(valData.position, s_TRANSFORM_DATA, valData);
  7007. const initialX = data.boundingRect.x;
  7008. const initialY = data.boundingRect.y;
  7009. if (data.boundingRect.bottom + valData.marginTop > boundsHeight) {
  7010. data.boundingRect.y += boundsHeight - data.boundingRect.bottom - valData.marginTop;
  7011. }
  7012. if (data.boundingRect.right + valData.marginLeft > boundsWidth) {
  7013. data.boundingRect.x += boundsWidth - data.boundingRect.right - valData.marginLeft;
  7014. }
  7015. if (data.boundingRect.top - valData.marginTop < 0) {
  7016. data.boundingRect.y += Math.abs(data.boundingRect.top - valData.marginTop);
  7017. }
  7018. if (data.boundingRect.left - valData.marginLeft < 0) {
  7019. data.boundingRect.x += Math.abs(data.boundingRect.left - valData.marginLeft);
  7020. }
  7021. valData.position.left -= initialX - data.boundingRect.x;
  7022. valData.position.top -= initialY - data.boundingRect.y;
  7023. return valData.position;
  7024. }
  7025. }
  7026. const basicWindow = new BasicBounds({ lock: true });
  7027. const transformWindow = new TransformBounds({ lock: true });
  7028. const positionValidators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  7029. __proto__: null,
  7030. BasicBounds,
  7031. TransformBounds,
  7032. basicWindow,
  7033. transformWindow
  7034. }, Symbol.toStringTag, { value: "Module" }));
  7035. const s_SCALE_VECTOR = [1, 1, 1];
  7036. const s_TRANSLATE_VECTOR = [0, 0, 0];
  7037. const s_MAT4_RESULT = mat4.create();
  7038. const s_MAT4_TEMP = mat4.create();
  7039. const s_VEC3_TEMP = vec3.create();
  7040. class Transforms {
  7041. /**
  7042. * Stores the transform keys in the order added.
  7043. *
  7044. * @type {string[]}
  7045. */
  7046. #orderList = [];
  7047. constructor() {
  7048. this._data = {};
  7049. }
  7050. /**
  7051. * @returns {boolean} Whether there are active transforms in local data.
  7052. */
  7053. get isActive() {
  7054. return this.#orderList.length > 0;
  7055. }
  7056. /**
  7057. * @returns {number|undefined} Any local rotateX data.
  7058. */
  7059. get rotateX() {
  7060. return this._data.rotateX;
  7061. }
  7062. /**
  7063. * @returns {number|undefined} Any local rotateY data.
  7064. */
  7065. get rotateY() {
  7066. return this._data.rotateY;
  7067. }
  7068. /**
  7069. * @returns {number|undefined} Any local rotateZ data.
  7070. */
  7071. get rotateZ() {
  7072. return this._data.rotateZ;
  7073. }
  7074. /**
  7075. * @returns {number|undefined} Any local rotateZ scale.
  7076. */
  7077. get scale() {
  7078. return this._data.scale;
  7079. }
  7080. /**
  7081. * @returns {number|undefined} Any local translateZ data.
  7082. */
  7083. get translateX() {
  7084. return this._data.translateX;
  7085. }
  7086. /**
  7087. * @returns {number|undefined} Any local translateZ data.
  7088. */
  7089. get translateY() {
  7090. return this._data.translateY;
  7091. }
  7092. /**
  7093. * @returns {number|undefined} Any local translateZ data.
  7094. */
  7095. get translateZ() {
  7096. return this._data.translateZ;
  7097. }
  7098. /**
  7099. * Sets the local rotateX data if the value is a finite number otherwise removes the local data.
  7100. *
  7101. * @param {number|null|undefined} value - A value to set.
  7102. */
  7103. set rotateX(value) {
  7104. if (Number.isFinite(value)) {
  7105. if (this._data.rotateX === void 0) {
  7106. this.#orderList.push("rotateX");
  7107. }
  7108. this._data.rotateX = value;
  7109. } else {
  7110. if (this._data.rotateX !== void 0) {
  7111. const index = this.#orderList.findIndex((entry) => entry === "rotateX");
  7112. if (index >= 0) {
  7113. this.#orderList.splice(index, 1);
  7114. }
  7115. }
  7116. delete this._data.rotateX;
  7117. }
  7118. }
  7119. /**
  7120. * Sets the local rotateY data if the value is a finite number otherwise removes the local data.
  7121. *
  7122. * @param {number|null|undefined} value - A value to set.
  7123. */
  7124. set rotateY(value) {
  7125. if (Number.isFinite(value)) {
  7126. if (this._data.rotateY === void 0) {
  7127. this.#orderList.push("rotateY");
  7128. }
  7129. this._data.rotateY = value;
  7130. } else {
  7131. if (this._data.rotateY !== void 0) {
  7132. const index = this.#orderList.findIndex((entry) => entry === "rotateY");
  7133. if (index >= 0) {
  7134. this.#orderList.splice(index, 1);
  7135. }
  7136. }
  7137. delete this._data.rotateY;
  7138. }
  7139. }
  7140. /**
  7141. * Sets the local rotateZ data if the value is a finite number otherwise removes the local data.
  7142. *
  7143. * @param {number|null|undefined} value - A value to set.
  7144. */
  7145. set rotateZ(value) {
  7146. if (Number.isFinite(value)) {
  7147. if (this._data.rotateZ === void 0) {
  7148. this.#orderList.push("rotateZ");
  7149. }
  7150. this._data.rotateZ = value;
  7151. } else {
  7152. if (this._data.rotateZ !== void 0) {
  7153. const index = this.#orderList.findIndex((entry) => entry === "rotateZ");
  7154. if (index >= 0) {
  7155. this.#orderList.splice(index, 1);
  7156. }
  7157. }
  7158. delete this._data.rotateZ;
  7159. }
  7160. }
  7161. /**
  7162. * Sets the local scale data if the value is a finite number otherwise removes the local data.
  7163. *
  7164. * @param {number|null|undefined} value - A value to set.
  7165. */
  7166. set scale(value) {
  7167. if (Number.isFinite(value)) {
  7168. if (this._data.scale === void 0) {
  7169. this.#orderList.push("scale");
  7170. }
  7171. this._data.scale = value;
  7172. } else {
  7173. if (this._data.scale !== void 0) {
  7174. const index = this.#orderList.findIndex((entry) => entry === "scale");
  7175. if (index >= 0) {
  7176. this.#orderList.splice(index, 1);
  7177. }
  7178. }
  7179. delete this._data.scale;
  7180. }
  7181. }
  7182. /**
  7183. * Sets the local translateX data if the value is a finite number otherwise removes the local data.
  7184. *
  7185. * @param {number|null|undefined} value - A value to set.
  7186. */
  7187. set translateX(value) {
  7188. if (Number.isFinite(value)) {
  7189. if (this._data.translateX === void 0) {
  7190. this.#orderList.push("translateX");
  7191. }
  7192. this._data.translateX = value;
  7193. } else {
  7194. if (this._data.translateX !== void 0) {
  7195. const index = this.#orderList.findIndex((entry) => entry === "translateX");
  7196. if (index >= 0) {
  7197. this.#orderList.splice(index, 1);
  7198. }
  7199. }
  7200. delete this._data.translateX;
  7201. }
  7202. }
  7203. /**
  7204. * Sets the local translateY data if the value is a finite number otherwise removes the local data.
  7205. *
  7206. * @param {number|null|undefined} value - A value to set.
  7207. */
  7208. set translateY(value) {
  7209. if (Number.isFinite(value)) {
  7210. if (this._data.translateY === void 0) {
  7211. this.#orderList.push("translateY");
  7212. }
  7213. this._data.translateY = value;
  7214. } else {
  7215. if (this._data.translateY !== void 0) {
  7216. const index = this.#orderList.findIndex((entry) => entry === "translateY");
  7217. if (index >= 0) {
  7218. this.#orderList.splice(index, 1);
  7219. }
  7220. }
  7221. delete this._data.translateY;
  7222. }
  7223. }
  7224. /**
  7225. * Sets the local translateZ data if the value is a finite number otherwise removes the local data.
  7226. *
  7227. * @param {number|null|undefined} value - A value to set.
  7228. */
  7229. set translateZ(value) {
  7230. if (Number.isFinite(value)) {
  7231. if (this._data.translateZ === void 0) {
  7232. this.#orderList.push("translateZ");
  7233. }
  7234. this._data.translateZ = value;
  7235. } else {
  7236. if (this._data.translateZ !== void 0) {
  7237. const index = this.#orderList.findIndex((entry) => entry === "translateZ");
  7238. if (index >= 0) {
  7239. this.#orderList.splice(index, 1);
  7240. }
  7241. }
  7242. delete this._data.translateZ;
  7243. }
  7244. }
  7245. /**
  7246. * Returns the matrix3d CSS transform for the given position / transform data.
  7247. *
  7248. * @param {object} [data] - Optional position data otherwise use local stored transform data.
  7249. *
  7250. * @returns {string} The CSS matrix3d string.
  7251. */
  7252. getCSS(data = this._data) {
  7253. return `matrix3d(${this.getMat4(data, s_MAT4_RESULT).join(",")})`;
  7254. }
  7255. /**
  7256. * Returns the matrix3d CSS transform for the given position / transform data.
  7257. *
  7258. * @param {object} [data] - Optional position data otherwise use local stored transform data.
  7259. *
  7260. * @returns {string} The CSS matrix3d string.
  7261. */
  7262. getCSSOrtho(data = this._data) {
  7263. return `matrix3d(${this.getMat4Ortho(data, s_MAT4_RESULT).join(",")})`;
  7264. }
  7265. /**
  7266. * Collects all data including a bounding rect, transform matrix, and points array of the given {@link PositionData}
  7267. * instance with the applied local transform data.
  7268. *
  7269. * @param {PositionData} position - The position data to process.
  7270. *
  7271. * @param {TransformData} [output] - Optional TransformData output instance.
  7272. *
  7273. * @param {object} [validationData] - Optional validation data for adjustment parameters.
  7274. *
  7275. * @returns {TransformData} The output TransformData instance.
  7276. */
  7277. getData(position, output = new TransformData(), validationData = {}) {
  7278. const valWidth = validationData.width ?? 0;
  7279. const valHeight = validationData.height ?? 0;
  7280. const valOffsetTop = validationData.offsetTop ?? validationData.marginTop ?? 0;
  7281. const valOffsetLeft = validationData.offsetLeft ?? validationData.offsetLeft ?? 0;
  7282. position.top += valOffsetTop;
  7283. position.left += valOffsetLeft;
  7284. const width2 = Number.isFinite(position.width) ? position.width : valWidth;
  7285. const height = Number.isFinite(position.height) ? position.height : valHeight;
  7286. const rect = output.corners;
  7287. if (this.hasTransform(position)) {
  7288. rect[0][0] = rect[0][1] = rect[0][2] = 0;
  7289. rect[1][0] = width2;
  7290. rect[1][1] = rect[1][2] = 0;
  7291. rect[2][0] = width2;
  7292. rect[2][1] = height;
  7293. rect[2][2] = 0;
  7294. rect[3][0] = 0;
  7295. rect[3][1] = height;
  7296. rect[3][2] = 0;
  7297. const matrix = this.getMat4(position, output.mat4);
  7298. const translate = s_GET_ORIGIN_TRANSLATION(position.transformOrigin, width2, height, output.originTranslations);
  7299. if (transformOriginDefault === position.transformOrigin) {
  7300. vec3.transformMat4(rect[0], rect[0], matrix);
  7301. vec3.transformMat4(rect[1], rect[1], matrix);
  7302. vec3.transformMat4(rect[2], rect[2], matrix);
  7303. vec3.transformMat4(rect[3], rect[3], matrix);
  7304. } else {
  7305. vec3.transformMat4(rect[0], rect[0], translate[0]);
  7306. vec3.transformMat4(rect[0], rect[0], matrix);
  7307. vec3.transformMat4(rect[0], rect[0], translate[1]);
  7308. vec3.transformMat4(rect[1], rect[1], translate[0]);
  7309. vec3.transformMat4(rect[1], rect[1], matrix);
  7310. vec3.transformMat4(rect[1], rect[1], translate[1]);
  7311. vec3.transformMat4(rect[2], rect[2], translate[0]);
  7312. vec3.transformMat4(rect[2], rect[2], matrix);
  7313. vec3.transformMat4(rect[2], rect[2], translate[1]);
  7314. vec3.transformMat4(rect[3], rect[3], translate[0]);
  7315. vec3.transformMat4(rect[3], rect[3], matrix);
  7316. vec3.transformMat4(rect[3], rect[3], translate[1]);
  7317. }
  7318. rect[0][0] = position.left + rect[0][0];
  7319. rect[0][1] = position.top + rect[0][1];
  7320. rect[1][0] = position.left + rect[1][0];
  7321. rect[1][1] = position.top + rect[1][1];
  7322. rect[2][0] = position.left + rect[2][0];
  7323. rect[2][1] = position.top + rect[2][1];
  7324. rect[3][0] = position.left + rect[3][0];
  7325. rect[3][1] = position.top + rect[3][1];
  7326. } else {
  7327. rect[0][0] = position.left;
  7328. rect[0][1] = position.top;
  7329. rect[1][0] = position.left + width2;
  7330. rect[1][1] = position.top;
  7331. rect[2][0] = position.left + width2;
  7332. rect[2][1] = position.top + height;
  7333. rect[3][0] = position.left;
  7334. rect[3][1] = position.top + height;
  7335. mat4.identity(output.mat4);
  7336. }
  7337. let maxX = Number.MIN_SAFE_INTEGER;
  7338. let maxY = Number.MIN_SAFE_INTEGER;
  7339. let minX = Number.MAX_SAFE_INTEGER;
  7340. let minY = Number.MAX_SAFE_INTEGER;
  7341. for (let cntr = 4; --cntr >= 0; ) {
  7342. if (rect[cntr][0] > maxX) {
  7343. maxX = rect[cntr][0];
  7344. }
  7345. if (rect[cntr][0] < minX) {
  7346. minX = rect[cntr][0];
  7347. }
  7348. if (rect[cntr][1] > maxY) {
  7349. maxY = rect[cntr][1];
  7350. }
  7351. if (rect[cntr][1] < minY) {
  7352. minY = rect[cntr][1];
  7353. }
  7354. }
  7355. const boundingRect = output.boundingRect;
  7356. boundingRect.x = minX;
  7357. boundingRect.y = minY;
  7358. boundingRect.width = maxX - minX;
  7359. boundingRect.height = maxY - minY;
  7360. position.top -= valOffsetTop;
  7361. position.left -= valOffsetLeft;
  7362. return output;
  7363. }
  7364. /**
  7365. * Creates a transform matrix based on local data applied in order it was added.
  7366. *
  7367. * If no data object is provided then the source is the local transform data. If another data object is supplied
  7368. * then the stored local transform order is applied then all remaining transform keys are applied. This allows the
  7369. * construction of a transform matrix in advance of setting local data and is useful in collision detection.
  7370. *
  7371. * @param {object} [data] - PositionData instance or local transform data.
  7372. *
  7373. * @param {Matrix4} [output] - The output mat4 instance.
  7374. *
  7375. * @returns {Matrix4} Transform matrix.
  7376. */
  7377. getMat4(data = this._data, output = mat4.create()) {
  7378. const matrix = mat4.identity(output);
  7379. let seenKeys = 0;
  7380. const orderList = this.#orderList;
  7381. for (let cntr = 0; cntr < orderList.length; cntr++) {
  7382. const key = orderList[cntr];
  7383. switch (key) {
  7384. case "rotateX":
  7385. seenKeys |= transformKeysBitwise.rotateX;
  7386. mat4.multiply(matrix, matrix, mat4.fromXRotation(s_MAT4_TEMP, degToRad(data[key])));
  7387. break;
  7388. case "rotateY":
  7389. seenKeys |= transformKeysBitwise.rotateY;
  7390. mat4.multiply(matrix, matrix, mat4.fromYRotation(s_MAT4_TEMP, degToRad(data[key])));
  7391. break;
  7392. case "rotateZ":
  7393. seenKeys |= transformKeysBitwise.rotateZ;
  7394. mat4.multiply(matrix, matrix, mat4.fromZRotation(s_MAT4_TEMP, degToRad(data[key])));
  7395. break;
  7396. case "scale":
  7397. seenKeys |= transformKeysBitwise.scale;
  7398. s_SCALE_VECTOR[0] = s_SCALE_VECTOR[1] = data[key];
  7399. mat4.multiply(matrix, matrix, mat4.fromScaling(s_MAT4_TEMP, s_SCALE_VECTOR));
  7400. break;
  7401. case "translateX":
  7402. seenKeys |= transformKeysBitwise.translateX;
  7403. s_TRANSLATE_VECTOR[0] = data.translateX;
  7404. s_TRANSLATE_VECTOR[1] = 0;
  7405. s_TRANSLATE_VECTOR[2] = 0;
  7406. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7407. break;
  7408. case "translateY":
  7409. seenKeys |= transformKeysBitwise.translateY;
  7410. s_TRANSLATE_VECTOR[0] = 0;
  7411. s_TRANSLATE_VECTOR[1] = data.translateY;
  7412. s_TRANSLATE_VECTOR[2] = 0;
  7413. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7414. break;
  7415. case "translateZ":
  7416. seenKeys |= transformKeysBitwise.translateZ;
  7417. s_TRANSLATE_VECTOR[0] = 0;
  7418. s_TRANSLATE_VECTOR[1] = 0;
  7419. s_TRANSLATE_VECTOR[2] = data.translateZ;
  7420. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7421. break;
  7422. }
  7423. }
  7424. if (data !== this._data) {
  7425. for (let cntr = 0; cntr < transformKeys.length; cntr++) {
  7426. const key = transformKeys[cntr];
  7427. if (data[key] === null || (seenKeys & transformKeysBitwise[key]) > 0) {
  7428. continue;
  7429. }
  7430. switch (key) {
  7431. case "rotateX":
  7432. mat4.multiply(matrix, matrix, mat4.fromXRotation(s_MAT4_TEMP, degToRad(data[key])));
  7433. break;
  7434. case "rotateY":
  7435. mat4.multiply(matrix, matrix, mat4.fromYRotation(s_MAT4_TEMP, degToRad(data[key])));
  7436. break;
  7437. case "rotateZ":
  7438. mat4.multiply(matrix, matrix, mat4.fromZRotation(s_MAT4_TEMP, degToRad(data[key])));
  7439. break;
  7440. case "scale":
  7441. s_SCALE_VECTOR[0] = s_SCALE_VECTOR[1] = data[key];
  7442. mat4.multiply(matrix, matrix, mat4.fromScaling(s_MAT4_TEMP, s_SCALE_VECTOR));
  7443. break;
  7444. case "translateX":
  7445. s_TRANSLATE_VECTOR[0] = data[key];
  7446. s_TRANSLATE_VECTOR[1] = 0;
  7447. s_TRANSLATE_VECTOR[2] = 0;
  7448. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7449. break;
  7450. case "translateY":
  7451. s_TRANSLATE_VECTOR[0] = 0;
  7452. s_TRANSLATE_VECTOR[1] = data[key];
  7453. s_TRANSLATE_VECTOR[2] = 0;
  7454. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7455. break;
  7456. case "translateZ":
  7457. s_TRANSLATE_VECTOR[0] = 0;
  7458. s_TRANSLATE_VECTOR[1] = 0;
  7459. s_TRANSLATE_VECTOR[2] = data[key];
  7460. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7461. break;
  7462. }
  7463. }
  7464. }
  7465. return matrix;
  7466. }
  7467. /**
  7468. * Provides an orthographic enhancement to convert left / top positional data to a translate operation.
  7469. *
  7470. * This transform matrix takes into account that the remaining operations are , but adds any left / top attributes from passed in data to
  7471. * translate X / Y.
  7472. *
  7473. * If no data object is provided then the source is the local transform data. If another data object is supplied
  7474. * then the stored local transform order is applied then all remaining transform keys are applied. This allows the
  7475. * construction of a transform matrix in advance of setting local data and is useful in collision detection.
  7476. *
  7477. * @param {object} [data] - PositionData instance or local transform data.
  7478. *
  7479. * @param {Matrix4} [output] - The output mat4 instance.
  7480. *
  7481. * @returns {Matrix4} Transform matrix.
  7482. */
  7483. getMat4Ortho(data = this._data, output = mat4.create()) {
  7484. const matrix = mat4.identity(output);
  7485. s_TRANSLATE_VECTOR[0] = (data.left ?? 0) + (data.translateX ?? 0);
  7486. s_TRANSLATE_VECTOR[1] = (data.top ?? 0) + (data.translateY ?? 0);
  7487. s_TRANSLATE_VECTOR[2] = data.translateZ ?? 0;
  7488. mat4.multiply(matrix, matrix, mat4.fromTranslation(s_MAT4_TEMP, s_TRANSLATE_VECTOR));
  7489. if (data.scale !== null) {
  7490. s_SCALE_VECTOR[0] = s_SCALE_VECTOR[1] = data.scale;
  7491. mat4.multiply(matrix, matrix, mat4.fromScaling(s_MAT4_TEMP, s_SCALE_VECTOR));
  7492. }
  7493. if (data.rotateX === null && data.rotateY === null && data.rotateZ === null) {
  7494. return matrix;
  7495. }
  7496. let seenKeys = 0;
  7497. const orderList = this.#orderList;
  7498. for (let cntr = 0; cntr < orderList.length; cntr++) {
  7499. const key = orderList[cntr];
  7500. switch (key) {
  7501. case "rotateX":
  7502. seenKeys |= transformKeysBitwise.rotateX;
  7503. mat4.multiply(matrix, matrix, mat4.fromXRotation(s_MAT4_TEMP, degToRad(data[key])));
  7504. break;
  7505. case "rotateY":
  7506. seenKeys |= transformKeysBitwise.rotateY;
  7507. mat4.multiply(matrix, matrix, mat4.fromYRotation(s_MAT4_TEMP, degToRad(data[key])));
  7508. break;
  7509. case "rotateZ":
  7510. seenKeys |= transformKeysBitwise.rotateZ;
  7511. mat4.multiply(matrix, matrix, mat4.fromZRotation(s_MAT4_TEMP, degToRad(data[key])));
  7512. break;
  7513. }
  7514. }
  7515. if (data !== this._data) {
  7516. for (let cntr = 0; cntr < transformKeys.length; cntr++) {
  7517. const key = transformKeys[cntr];
  7518. if (data[key] === null || (seenKeys & transformKeysBitwise[key]) > 0) {
  7519. continue;
  7520. }
  7521. switch (key) {
  7522. case "rotateX":
  7523. mat4.multiply(matrix, matrix, mat4.fromXRotation(s_MAT4_TEMP, degToRad(data[key])));
  7524. break;
  7525. case "rotateY":
  7526. mat4.multiply(matrix, matrix, mat4.fromYRotation(s_MAT4_TEMP, degToRad(data[key])));
  7527. break;
  7528. case "rotateZ":
  7529. mat4.multiply(matrix, matrix, mat4.fromZRotation(s_MAT4_TEMP, degToRad(data[key])));
  7530. break;
  7531. }
  7532. }
  7533. }
  7534. return matrix;
  7535. }
  7536. /**
  7537. * Tests an object if it contains transform keys and the values are finite numbers.
  7538. *
  7539. * @param {object} data - An object to test for transform data.
  7540. *
  7541. * @returns {boolean} Whether the given PositionData has transforms.
  7542. */
  7543. hasTransform(data) {
  7544. for (const key of transformKeys) {
  7545. if (Number.isFinite(data[key])) {
  7546. return true;
  7547. }
  7548. }
  7549. return false;
  7550. }
  7551. /**
  7552. * Resets internal data from the given object containing valid transform keys.
  7553. *
  7554. * @param {object} data - An object with transform data.
  7555. */
  7556. reset(data) {
  7557. for (const key in data) {
  7558. if (transformKeys.includes(key)) {
  7559. if (Number.isFinite(data[key])) {
  7560. this._data[key] = data[key];
  7561. } else {
  7562. const index = this.#orderList.findIndex((entry) => entry === key);
  7563. if (index >= 0) {
  7564. this.#orderList.splice(index, 1);
  7565. }
  7566. delete this._data[key];
  7567. }
  7568. }
  7569. }
  7570. }
  7571. }
  7572. function s_GET_ORIGIN_TRANSLATION(transformOrigin, width2, height, output) {
  7573. const vector = s_VEC3_TEMP;
  7574. switch (transformOrigin) {
  7575. case "top left":
  7576. vector[0] = vector[1] = 0;
  7577. mat4.fromTranslation(output[0], vector);
  7578. mat4.fromTranslation(output[1], vector);
  7579. break;
  7580. case "top center":
  7581. vector[0] = -width2 * 0.5;
  7582. vector[1] = 0;
  7583. mat4.fromTranslation(output[0], vector);
  7584. vector[0] = width2 * 0.5;
  7585. mat4.fromTranslation(output[1], vector);
  7586. break;
  7587. case "top right":
  7588. vector[0] = -width2;
  7589. vector[1] = 0;
  7590. mat4.fromTranslation(output[0], vector);
  7591. vector[0] = width2;
  7592. mat4.fromTranslation(output[1], vector);
  7593. break;
  7594. case "center left":
  7595. vector[0] = 0;
  7596. vector[1] = -height * 0.5;
  7597. mat4.fromTranslation(output[0], vector);
  7598. vector[1] = height * 0.5;
  7599. mat4.fromTranslation(output[1], vector);
  7600. break;
  7601. case null:
  7602. case "center":
  7603. vector[0] = -width2 * 0.5;
  7604. vector[1] = -height * 0.5;
  7605. mat4.fromTranslation(output[0], vector);
  7606. vector[0] = width2 * 0.5;
  7607. vector[1] = height * 0.5;
  7608. mat4.fromTranslation(output[1], vector);
  7609. break;
  7610. case "center right":
  7611. vector[0] = -width2;
  7612. vector[1] = -height * 0.5;
  7613. mat4.fromTranslation(output[0], vector);
  7614. vector[0] = width2;
  7615. vector[1] = height * 0.5;
  7616. mat4.fromTranslation(output[1], vector);
  7617. break;
  7618. case "bottom left":
  7619. vector[0] = 0;
  7620. vector[1] = -height;
  7621. mat4.fromTranslation(output[0], vector);
  7622. vector[1] = height;
  7623. mat4.fromTranslation(output[1], vector);
  7624. break;
  7625. case "bottom center":
  7626. vector[0] = -width2 * 0.5;
  7627. vector[1] = -height;
  7628. mat4.fromTranslation(output[0], vector);
  7629. vector[0] = width2 * 0.5;
  7630. vector[1] = height;
  7631. mat4.fromTranslation(output[1], vector);
  7632. break;
  7633. case "bottom right":
  7634. vector[0] = -width2;
  7635. vector[1] = -height;
  7636. mat4.fromTranslation(output[0], vector);
  7637. vector[0] = width2;
  7638. vector[1] = height;
  7639. mat4.fromTranslation(output[1], vector);
  7640. break;
  7641. default:
  7642. mat4.identity(output[0]);
  7643. mat4.identity(output[1]);
  7644. break;
  7645. }
  7646. return output;
  7647. }
  7648. class UpdateElementData {
  7649. constructor() {
  7650. this.data = void 0;
  7651. this.dataSubscribers = new PositionData();
  7652. this.dimensionData = { width: 0, height: 0 };
  7653. this.changeSet = void 0;
  7654. this.options = void 0;
  7655. this.queued = false;
  7656. this.styleCache = void 0;
  7657. this.transforms = void 0;
  7658. this.transformData = new TransformData();
  7659. this.subscriptions = void 0;
  7660. this.storeDimension = writable$1(this.dimensionData);
  7661. this.storeTransform = writable$1(this.transformData, () => {
  7662. this.options.transformSubscribed = true;
  7663. return () => this.options.transformSubscribed = false;
  7664. });
  7665. this.queued = false;
  7666. Object.seal(this.dimensionData);
  7667. }
  7668. }
  7669. async function nextAnimationFrame(cntr = 1) {
  7670. if (!Number.isInteger(cntr) || cntr < 1) {
  7671. throw new TypeError(`nextAnimationFrame error: 'cntr' must be a positive integer greater than 0.`);
  7672. }
  7673. let currentTime = performance.now();
  7674. for (; --cntr >= 0; ) {
  7675. currentTime = await new Promise((resolve) => requestAnimationFrame(resolve));
  7676. }
  7677. return currentTime;
  7678. }
  7679. class UpdateElementManager {
  7680. static list = [];
  7681. static listCntr = 0;
  7682. static updatePromise;
  7683. static get promise() {
  7684. return this.updatePromise;
  7685. }
  7686. /**
  7687. * Potentially adds the given element and internal updateData instance to the list.
  7688. *
  7689. * @param {HTMLElement} el - An HTMLElement instance.
  7690. *
  7691. * @param {UpdateElementData} updateData - An UpdateElementData instance.
  7692. *
  7693. * @returns {Promise<number>} The unified next frame update promise. Returns `currentTime`.
  7694. */
  7695. static add(el, updateData) {
  7696. if (this.listCntr < this.list.length) {
  7697. const entry = this.list[this.listCntr];
  7698. entry[0] = el;
  7699. entry[1] = updateData;
  7700. } else {
  7701. this.list.push([el, updateData]);
  7702. }
  7703. this.listCntr++;
  7704. updateData.queued = true;
  7705. if (!this.updatePromise) {
  7706. this.updatePromise = this.wait();
  7707. }
  7708. return this.updatePromise;
  7709. }
  7710. /**
  7711. * Await on `nextAnimationFrame` and iterate over list map invoking callback functions.
  7712. *
  7713. * @returns {Promise<number>} The next frame Promise / currentTime from nextAnimationFrame.
  7714. */
  7715. static async wait() {
  7716. const currentTime = await nextAnimationFrame();
  7717. this.updatePromise = void 0;
  7718. for (let cntr = this.listCntr; --cntr >= 0; ) {
  7719. const entry = this.list[cntr];
  7720. const el = entry[0];
  7721. const updateData = entry[1];
  7722. entry[0] = void 0;
  7723. entry[1] = void 0;
  7724. updateData.queued = false;
  7725. if (!el.isConnected) {
  7726. continue;
  7727. }
  7728. if (updateData.options.ortho) {
  7729. s_UPDATE_ELEMENT_ORTHO(el, updateData);
  7730. } else {
  7731. s_UPDATE_ELEMENT(el, updateData);
  7732. }
  7733. if (updateData.options.calculateTransform || updateData.options.transformSubscribed) {
  7734. s_UPDATE_TRANSFORM(el, updateData);
  7735. }
  7736. this.updateSubscribers(updateData);
  7737. }
  7738. this.listCntr = 0;
  7739. return currentTime;
  7740. }
  7741. /**
  7742. * Potentially immediately updates the given element.
  7743. *
  7744. * @param {HTMLElement} el - An HTMLElement instance.
  7745. *
  7746. * @param {UpdateElementData} updateData - An UpdateElementData instance.
  7747. */
  7748. static immediate(el, updateData) {
  7749. if (!el.isConnected) {
  7750. return;
  7751. }
  7752. if (updateData.options.ortho) {
  7753. s_UPDATE_ELEMENT_ORTHO(el, updateData);
  7754. } else {
  7755. s_UPDATE_ELEMENT(el, updateData);
  7756. }
  7757. if (updateData.options.calculateTransform || updateData.options.transformSubscribed) {
  7758. s_UPDATE_TRANSFORM(el, updateData);
  7759. }
  7760. this.updateSubscribers(updateData);
  7761. }
  7762. /**
  7763. * @param {UpdateElementData} updateData - Data change set.
  7764. */
  7765. static updateSubscribers(updateData) {
  7766. const data = updateData.data;
  7767. const changeSet = updateData.changeSet;
  7768. if (!changeSet.hasChange()) {
  7769. return;
  7770. }
  7771. const output = updateData.dataSubscribers.copy(data);
  7772. const subscriptions = updateData.subscriptions;
  7773. if (subscriptions.length > 0) {
  7774. for (let cntr = 0; cntr < subscriptions.length; cntr++) {
  7775. subscriptions[cntr](output);
  7776. }
  7777. }
  7778. if (changeSet.width || changeSet.height) {
  7779. updateData.dimensionData.width = data.width;
  7780. updateData.dimensionData.height = data.height;
  7781. updateData.storeDimension.set(updateData.dimensionData);
  7782. }
  7783. changeSet.set(false);
  7784. }
  7785. }
  7786. function s_UPDATE_ELEMENT(el, updateData) {
  7787. const changeSet = updateData.changeSet;
  7788. const data = updateData.data;
  7789. if (changeSet.left) {
  7790. el.style.left = `${data.left}px`;
  7791. }
  7792. if (changeSet.top) {
  7793. el.style.top = `${data.top}px`;
  7794. }
  7795. if (changeSet.zIndex) {
  7796. el.style.zIndex = typeof data.zIndex === "number" ? `${data.zIndex}` : null;
  7797. }
  7798. if (changeSet.width) {
  7799. el.style.width = typeof data.width === "number" ? `${data.width}px` : data.width;
  7800. }
  7801. if (changeSet.height) {
  7802. el.style.height = typeof data.height === "number" ? `${data.height}px` : data.height;
  7803. }
  7804. if (changeSet.transformOrigin) {
  7805. el.style.transformOrigin = data.transformOrigin === "center" ? null : data.transformOrigin;
  7806. }
  7807. if (changeSet.transform) {
  7808. el.style.transform = updateData.transforms.isActive ? updateData.transforms.getCSS() : null;
  7809. }
  7810. }
  7811. function s_UPDATE_ELEMENT_ORTHO(el, updateData) {
  7812. const changeSet = updateData.changeSet;
  7813. const data = updateData.data;
  7814. if (changeSet.zIndex) {
  7815. el.style.zIndex = typeof data.zIndex === "number" ? `${data.zIndex}` : null;
  7816. }
  7817. if (changeSet.width) {
  7818. el.style.width = typeof data.width === "number" ? `${data.width}px` : data.width;
  7819. }
  7820. if (changeSet.height) {
  7821. el.style.height = typeof data.height === "number" ? `${data.height}px` : data.height;
  7822. }
  7823. if (changeSet.transformOrigin) {
  7824. el.style.transformOrigin = data.transformOrigin === "center" ? null : data.transformOrigin;
  7825. }
  7826. if (changeSet.left || changeSet.top || changeSet.transform) {
  7827. el.style.transform = updateData.transforms.getCSSOrtho(data);
  7828. }
  7829. }
  7830. function s_UPDATE_TRANSFORM(el, updateData) {
  7831. s_VALIDATION_DATA$1.height = updateData.data.height !== "auto" ? updateData.data.height : updateData.styleCache.offsetHeight;
  7832. s_VALIDATION_DATA$1.width = updateData.data.width !== "auto" ? updateData.data.width : updateData.styleCache.offsetWidth;
  7833. s_VALIDATION_DATA$1.marginLeft = updateData.styleCache.marginLeft;
  7834. s_VALIDATION_DATA$1.marginTop = updateData.styleCache.marginTop;
  7835. updateData.transforms.getData(updateData.data, updateData.transformData, s_VALIDATION_DATA$1);
  7836. updateData.storeTransform.set(updateData.transformData);
  7837. }
  7838. const s_VALIDATION_DATA$1 = {
  7839. height: void 0,
  7840. width: void 0,
  7841. marginLeft: void 0,
  7842. marginTop: void 0
  7843. };
  7844. class Position {
  7845. /**
  7846. * @type {PositionData}
  7847. */
  7848. #data = new PositionData();
  7849. /**
  7850. * Provides the animation API.
  7851. *
  7852. * @type {AnimationAPI}
  7853. */
  7854. #animate = new AnimationAPI(this, this.#data);
  7855. /**
  7856. * Provides a way to turn on / off the position handling.
  7857. *
  7858. * @type {boolean}
  7859. */
  7860. #enabled = true;
  7861. /**
  7862. * Stores the style attributes that changed on update.
  7863. *
  7864. * @type {PositionChangeSet}
  7865. */
  7866. #positionChangeSet = new PositionChangeSet();
  7867. /**
  7868. * Stores ongoing options that are set in the constructor or by transform store subscription.
  7869. *
  7870. * @type {PositionOptions}
  7871. */
  7872. #options = {
  7873. calculateTransform: false,
  7874. initialHelper: void 0,
  7875. ortho: true,
  7876. transformSubscribed: false
  7877. };
  7878. /**
  7879. * The associated parent for positional data tracking. Used in validators.
  7880. *
  7881. * @type {PositionParent}
  7882. */
  7883. #parent;
  7884. /**
  7885. * @type {StorePosition}
  7886. */
  7887. #stores;
  7888. /**
  7889. * Stores an instance of the computer styles for the target element.
  7890. *
  7891. * @type {StyleCache}
  7892. */
  7893. #styleCache;
  7894. /**
  7895. * Stores the subscribers.
  7896. *
  7897. * @type {(function(PositionData): void)[]}
  7898. */
  7899. #subscriptions = [];
  7900. /**
  7901. * @type {Transforms}
  7902. */
  7903. #transforms = new Transforms();
  7904. /**
  7905. * @type {UpdateElementData}
  7906. */
  7907. #updateElementData;
  7908. /**
  7909. * Stores the UpdateElementManager wait promise.
  7910. *
  7911. * @type {Promise}
  7912. */
  7913. #updateElementPromise;
  7914. /**
  7915. * @type {AdapterValidators}
  7916. */
  7917. #validators;
  7918. /**
  7919. * @type {ValidatorData[]}
  7920. */
  7921. #validatorData;
  7922. /**
  7923. * @type {PositionStateAPI}
  7924. */
  7925. #state = new PositionStateAPI(this, this.#data, this.#transforms);
  7926. /**
  7927. * @returns {AnimationGroupAPI} Public Animation API.
  7928. */
  7929. static get Animate() {
  7930. return AnimationGroupAPI;
  7931. }
  7932. /**
  7933. * @returns {{browserCentered?: Centered, Centered?: *}} Initial position helpers.
  7934. */
  7935. static get Initial() {
  7936. return positionInitial;
  7937. }
  7938. /**
  7939. * Returns TransformData class / constructor.
  7940. *
  7941. * @returns {TransformData} TransformData class / constructor.
  7942. */
  7943. static get TransformData() {
  7944. return TransformData;
  7945. }
  7946. /**
  7947. * Returns default validators.
  7948. *
  7949. * Note: `basicWindow` and `BasicBounds` will eventually be removed.
  7950. *
  7951. * @returns {{basicWindow?: BasicBounds, transformWindow?: TransformBounds, TransformBounds?: *, BasicBounds?: *}}
  7952. * Available validators.
  7953. */
  7954. static get Validators() {
  7955. return positionValidators;
  7956. }
  7957. /**
  7958. * Returns a duplicate of a given position instance copying any options and validators.
  7959. *
  7960. * // TODO: Consider more safety over options processing.
  7961. *
  7962. * @param {Position} position - A position instance.
  7963. *
  7964. * @param {PositionOptions} options - Position options.
  7965. *
  7966. * @returns {Position} A duplicate position instance.
  7967. */
  7968. static duplicate(position, options) {
  7969. if (!(position instanceof Position)) {
  7970. throw new TypeError(`'position' is not an instance of Position.`);
  7971. }
  7972. const newPosition = new Position(options);
  7973. newPosition.#options = Object.assign({}, position.#options, options);
  7974. newPosition.#validators.add(...position.#validators);
  7975. newPosition.set(position.#data);
  7976. return newPosition;
  7977. }
  7978. /**
  7979. * @param {PositionParent|PositionOptionsAll} [parent] - A potential parent element or object w/ `elementTarget`
  7980. * getter. May also be the PositionOptions object w/ 1 argument.
  7981. *
  7982. * @param {PositionOptionsAll} [options] - Default values.
  7983. */
  7984. constructor(parent, options) {
  7985. if (isPlainObject(parent)) {
  7986. options = parent;
  7987. } else {
  7988. this.#parent = parent;
  7989. }
  7990. const data = this.#data;
  7991. const transforms = this.#transforms;
  7992. this.#styleCache = new StyleCache();
  7993. const updateData = new UpdateElementData();
  7994. updateData.changeSet = this.#positionChangeSet;
  7995. updateData.data = this.#data;
  7996. updateData.options = this.#options;
  7997. updateData.styleCache = this.#styleCache;
  7998. updateData.subscriptions = this.#subscriptions;
  7999. updateData.transforms = this.#transforms;
  8000. this.#updateElementData = updateData;
  8001. if (isObject(options)) {
  8002. if (typeof options.calculateTransform === "boolean") {
  8003. this.#options.calculateTransform = options.calculateTransform;
  8004. }
  8005. if (typeof options.ortho === "boolean") {
  8006. this.#options.ortho = options.ortho;
  8007. }
  8008. if (Number.isFinite(options.height) || options.height === "auto" || options.height === "inherit" || options.height === null) {
  8009. data.height = updateData.dimensionData.height = typeof options.height === "number" ? Math.round(options.height) : options.height;
  8010. }
  8011. if (Number.isFinite(options.left) || options.left === null) {
  8012. data.left = typeof options.left === "number" ? Math.round(options.left) : options.left;
  8013. }
  8014. if (Number.isFinite(options.maxHeight) || options.maxHeight === null) {
  8015. data.maxHeight = typeof options.maxHeight === "number" ? Math.round(options.maxHeight) : options.maxHeight;
  8016. }
  8017. if (Number.isFinite(options.maxWidth) || options.maxWidth === null) {
  8018. data.maxWidth = typeof options.maxWidth === "number" ? Math.round(options.maxWidth) : options.maxWidth;
  8019. }
  8020. if (Number.isFinite(options.minHeight) || options.minHeight === null) {
  8021. data.minHeight = typeof options.minHeight === "number" ? Math.round(options.minHeight) : options.minHeight;
  8022. }
  8023. if (Number.isFinite(options.minWidth) || options.minWidth === null) {
  8024. data.minWidth = typeof options.minWidth === "number" ? Math.round(options.minWidth) : options.minWidth;
  8025. }
  8026. if (Number.isFinite(options.rotateX) || options.rotateX === null) {
  8027. transforms.rotateX = data.rotateX = options.rotateX;
  8028. }
  8029. if (Number.isFinite(options.rotateY) || options.rotateY === null) {
  8030. transforms.rotateY = data.rotateY = options.rotateY;
  8031. }
  8032. if (Number.isFinite(options.rotateZ) || options.rotateZ === null) {
  8033. transforms.rotateZ = data.rotateZ = options.rotateZ;
  8034. }
  8035. if (Number.isFinite(options.scale) || options.scale === null) {
  8036. transforms.scale = data.scale = options.scale;
  8037. }
  8038. if (Number.isFinite(options.top) || options.top === null) {
  8039. data.top = typeof options.top === "number" ? Math.round(options.top) : options.top;
  8040. }
  8041. if (typeof options.transformOrigin === "string" || options.transformOrigin === null) {
  8042. data.transformOrigin = transformOrigins.includes(options.transformOrigin) ? options.transformOrigin : null;
  8043. }
  8044. if (Number.isFinite(options.translateX) || options.translateX === null) {
  8045. transforms.translateX = data.translateX = options.translateX;
  8046. }
  8047. if (Number.isFinite(options.translateY) || options.translateY === null) {
  8048. transforms.translateY = data.translateY = options.translateY;
  8049. }
  8050. if (Number.isFinite(options.translateZ) || options.translateZ === null) {
  8051. transforms.translateZ = data.translateZ = options.translateZ;
  8052. }
  8053. if (Number.isFinite(options.width) || options.width === "auto" || options.width === "inherit" || options.width === null) {
  8054. data.width = updateData.dimensionData.width = typeof options.width === "number" ? Math.round(options.width) : options.width;
  8055. }
  8056. if (Number.isFinite(options.zIndex) || options.zIndex === null) {
  8057. data.zIndex = typeof options.zIndex === "number" ? Math.round(options.zIndex) : options.zIndex;
  8058. }
  8059. }
  8060. this.#stores = {
  8061. // The main properties for manipulating Position.
  8062. height: propertyStore(this, "height"),
  8063. left: propertyStore(this, "left"),
  8064. rotateX: propertyStore(this, "rotateX"),
  8065. rotateY: propertyStore(this, "rotateY"),
  8066. rotateZ: propertyStore(this, "rotateZ"),
  8067. scale: propertyStore(this, "scale"),
  8068. top: propertyStore(this, "top"),
  8069. transformOrigin: propertyStore(this, "transformOrigin"),
  8070. translateX: propertyStore(this, "translateX"),
  8071. translateY: propertyStore(this, "translateY"),
  8072. translateZ: propertyStore(this, "translateZ"),
  8073. width: propertyStore(this, "width"),
  8074. zIndex: propertyStore(this, "zIndex"),
  8075. // Stores that control validation when width / height is not `auto`.
  8076. maxHeight: propertyStore(this, "maxHeight"),
  8077. maxWidth: propertyStore(this, "maxWidth"),
  8078. minHeight: propertyStore(this, "minHeight"),
  8079. minWidth: propertyStore(this, "minWidth"),
  8080. // Readable stores based on updates or from resize observer changes.
  8081. dimension: { subscribe: updateData.storeDimension.subscribe },
  8082. element: { subscribe: this.#styleCache.stores.element.subscribe },
  8083. resizeContentHeight: { subscribe: this.#styleCache.stores.resizeContentHeight.subscribe },
  8084. resizeContentWidth: { subscribe: this.#styleCache.stores.resizeContentWidth.subscribe },
  8085. resizeOffsetHeight: { subscribe: this.#styleCache.stores.resizeOffsetHeight.subscribe },
  8086. resizeOffsetWidth: { subscribe: this.#styleCache.stores.resizeOffsetWidth.subscribe },
  8087. transform: { subscribe: updateData.storeTransform.subscribe },
  8088. // Protected store that should only be set by resizeObserver action.
  8089. resizeObserved: this.#styleCache.stores.resizeObserved
  8090. };
  8091. subscribeIgnoreFirst(this.#stores.resizeObserved, (resizeData) => {
  8092. const parent2 = this.#parent;
  8093. const el = parent2 instanceof HTMLElement ? parent2 : parent2?.elementTarget;
  8094. if (el instanceof HTMLElement && Number.isFinite(resizeData?.offsetWidth) && Number.isFinite(resizeData?.offsetHeight)) {
  8095. this.set(data);
  8096. }
  8097. });
  8098. this.#stores.transformOrigin.values = transformOrigins;
  8099. [this.#validators, this.#validatorData] = new AdapterValidators();
  8100. if (options?.initial || options?.positionInitial) {
  8101. const initialHelper = options.initial ?? options.positionInitial;
  8102. if (typeof initialHelper?.getLeft !== "function" || typeof initialHelper?.getTop !== "function") {
  8103. throw new Error(
  8104. `'options.initial' position helper does not contain 'getLeft' and / or 'getTop' functions.`
  8105. );
  8106. }
  8107. this.#options.initialHelper = options.initial;
  8108. }
  8109. if (options?.validator) {
  8110. if (isIterable(options?.validator)) {
  8111. this.validators.add(...options.validator);
  8112. } else {
  8113. this.validators.add(options.validator);
  8114. }
  8115. }
  8116. }
  8117. /**
  8118. * Returns the animation API.
  8119. *
  8120. * @returns {AnimationAPI} Animation API.
  8121. */
  8122. get animate() {
  8123. return this.#animate;
  8124. }
  8125. /**
  8126. * Returns the dimension data for the readable store.
  8127. *
  8128. * @returns {{width: number | 'auto', height: number | 'auto'}} Dimension data.
  8129. */
  8130. get dimension() {
  8131. return this.#updateElementData.dimensionData;
  8132. }
  8133. /**
  8134. * Returns the enabled state.
  8135. *
  8136. * @returns {boolean} Enabled state.
  8137. */
  8138. get enabled() {
  8139. return this.#enabled;
  8140. }
  8141. /**
  8142. * Returns the current HTMLElement being positioned.
  8143. *
  8144. * @returns {HTMLElement|undefined} Current HTMLElement being positioned.
  8145. */
  8146. get element() {
  8147. return this.#styleCache.el;
  8148. }
  8149. /**
  8150. * Returns a promise that is resolved on the next element update with the time of the update.
  8151. *
  8152. * @returns {Promise<number>} Promise resolved on element update.
  8153. */
  8154. get elementUpdated() {
  8155. return this.#updateElementPromise;
  8156. }
  8157. /**
  8158. * Returns the associated {@link PositionParent} instance.
  8159. *
  8160. * @returns {PositionParent} The PositionParent instance.
  8161. */
  8162. get parent() {
  8163. return this.#parent;
  8164. }
  8165. /**
  8166. * Returns the state API.
  8167. *
  8168. * @returns {PositionStateAPI} Position state API.
  8169. */
  8170. get state() {
  8171. return this.#state;
  8172. }
  8173. /**
  8174. * Returns the derived writable stores for individual data variables.
  8175. *
  8176. * @returns {StorePosition} Derived / writable stores.
  8177. */
  8178. get stores() {
  8179. return this.#stores;
  8180. }
  8181. /**
  8182. * Returns the transform data for the readable store.
  8183. *
  8184. * @returns {TransformData} Transform Data.
  8185. */
  8186. get transform() {
  8187. return this.#updateElementData.transformData;
  8188. }
  8189. /**
  8190. * Returns the validators.
  8191. *
  8192. * @returns {AdapterValidators} validators.
  8193. */
  8194. get validators() {
  8195. return this.#validators;
  8196. }
  8197. /**
  8198. * Sets the enabled state.
  8199. *
  8200. * @param {boolean} enabled - New enabled state.
  8201. */
  8202. set enabled(enabled) {
  8203. if (typeof enabled !== "boolean") {
  8204. throw new TypeError(`'enabled' is not a boolean.`);
  8205. }
  8206. this.#enabled = enabled;
  8207. }
  8208. /**
  8209. * Sets the associated {@link PositionParent} instance. Resets the style cache and default data.
  8210. *
  8211. * @param {PositionParent|void} parent - A PositionParent instance.
  8212. */
  8213. set parent(parent) {
  8214. if (parent !== void 0 && !(parent instanceof HTMLElement) && !isObject(parent)) {
  8215. throw new TypeError(`'parent' is not an HTMLElement, object, or undefined.`);
  8216. }
  8217. this.#parent = parent;
  8218. this.#state.remove({ name: "#defaultData" });
  8219. this.#styleCache.reset();
  8220. if (parent) {
  8221. this.set(this.#data);
  8222. }
  8223. }
  8224. // Data accessors ----------------------------------------------------------------------------------------------------
  8225. /**
  8226. * @returns {number|'auto'|'inherit'|null} height
  8227. */
  8228. get height() {
  8229. return this.#data.height;
  8230. }
  8231. /**
  8232. * @returns {number|null} left
  8233. */
  8234. get left() {
  8235. return this.#data.left;
  8236. }
  8237. /**
  8238. * @returns {number|null} maxHeight
  8239. */
  8240. get maxHeight() {
  8241. return this.#data.maxHeight;
  8242. }
  8243. /**
  8244. * @returns {number|null} maxWidth
  8245. */
  8246. get maxWidth() {
  8247. return this.#data.maxWidth;
  8248. }
  8249. /**
  8250. * @returns {number|null} minHeight
  8251. */
  8252. get minHeight() {
  8253. return this.#data.minHeight;
  8254. }
  8255. /**
  8256. * @returns {number|null} minWidth
  8257. */
  8258. get minWidth() {
  8259. return this.#data.minWidth;
  8260. }
  8261. /**
  8262. * @returns {number|null} rotateX
  8263. */
  8264. get rotateX() {
  8265. return this.#data.rotateX;
  8266. }
  8267. /**
  8268. * @returns {number|null} rotateY
  8269. */
  8270. get rotateY() {
  8271. return this.#data.rotateY;
  8272. }
  8273. /**
  8274. * @returns {number|null} rotateZ
  8275. */
  8276. get rotateZ() {
  8277. return this.#data.rotateZ;
  8278. }
  8279. /**
  8280. * @returns {number|null} alias for rotateZ
  8281. */
  8282. get rotation() {
  8283. return this.#data.rotateZ;
  8284. }
  8285. /**
  8286. * @returns {number|null} scale
  8287. */
  8288. get scale() {
  8289. return this.#data.scale;
  8290. }
  8291. /**
  8292. * @returns {number|null} top
  8293. */
  8294. get top() {
  8295. return this.#data.top;
  8296. }
  8297. /**
  8298. * @returns {string} transformOrigin
  8299. */
  8300. get transformOrigin() {
  8301. return this.#data.transformOrigin;
  8302. }
  8303. /**
  8304. * @returns {number|null} translateX
  8305. */
  8306. get translateX() {
  8307. return this.#data.translateX;
  8308. }
  8309. /**
  8310. * @returns {number|null} translateY
  8311. */
  8312. get translateY() {
  8313. return this.#data.translateY;
  8314. }
  8315. /**
  8316. * @returns {number|null} translateZ
  8317. */
  8318. get translateZ() {
  8319. return this.#data.translateZ;
  8320. }
  8321. /**
  8322. * @returns {number|'auto'|'inherit'|null} width
  8323. */
  8324. get width() {
  8325. return this.#data.width;
  8326. }
  8327. /**
  8328. * @returns {number|null} z-index
  8329. */
  8330. get zIndex() {
  8331. return this.#data.zIndex;
  8332. }
  8333. /**
  8334. * @param {number|string|null} height -
  8335. */
  8336. set height(height) {
  8337. this.#stores.height.set(height);
  8338. }
  8339. /**
  8340. * @param {number|string|null} left -
  8341. */
  8342. set left(left) {
  8343. this.#stores.left.set(left);
  8344. }
  8345. /**
  8346. * @param {number|string|null} maxHeight -
  8347. */
  8348. set maxHeight(maxHeight) {
  8349. this.#stores.maxHeight.set(maxHeight);
  8350. }
  8351. /**
  8352. * @param {number|string|null} maxWidth -
  8353. */
  8354. set maxWidth(maxWidth) {
  8355. this.#stores.maxWidth.set(maxWidth);
  8356. }
  8357. /**
  8358. * @param {number|string|null} minHeight -
  8359. */
  8360. set minHeight(minHeight) {
  8361. this.#stores.minHeight.set(minHeight);
  8362. }
  8363. /**
  8364. * @param {number|string|null} minWidth -
  8365. */
  8366. set minWidth(minWidth) {
  8367. this.#stores.minWidth.set(minWidth);
  8368. }
  8369. /**
  8370. * @param {number|string|null} rotateX -
  8371. */
  8372. set rotateX(rotateX) {
  8373. this.#stores.rotateX.set(rotateX);
  8374. }
  8375. /**
  8376. * @param {number|string|null} rotateY -
  8377. */
  8378. set rotateY(rotateY) {
  8379. this.#stores.rotateY.set(rotateY);
  8380. }
  8381. /**
  8382. * @param {number|string|null} rotateZ -
  8383. */
  8384. set rotateZ(rotateZ) {
  8385. this.#stores.rotateZ.set(rotateZ);
  8386. }
  8387. /**
  8388. * @param {number|string|null} rotateZ - alias for rotateZ
  8389. */
  8390. set rotation(rotateZ) {
  8391. this.#stores.rotateZ.set(rotateZ);
  8392. }
  8393. /**
  8394. * @param {number|string|null} scale -
  8395. */
  8396. set scale(scale2) {
  8397. this.#stores.scale.set(scale2);
  8398. }
  8399. /**
  8400. * @param {number|string|null} top -
  8401. */
  8402. set top(top) {
  8403. this.#stores.top.set(top);
  8404. }
  8405. /**
  8406. * @param {string} transformOrigin -
  8407. */
  8408. set transformOrigin(transformOrigin) {
  8409. if (transformOrigins.includes(transformOrigin)) {
  8410. this.#stores.transformOrigin.set(transformOrigin);
  8411. }
  8412. }
  8413. /**
  8414. * @param {number|string|null} translateX -
  8415. */
  8416. set translateX(translateX) {
  8417. this.#stores.translateX.set(translateX);
  8418. }
  8419. /**
  8420. * @param {number|string|null} translateY -
  8421. */
  8422. set translateY(translateY) {
  8423. this.#stores.translateY.set(translateY);
  8424. }
  8425. /**
  8426. * @param {number|string|null} translateZ -
  8427. */
  8428. set translateZ(translateZ) {
  8429. this.#stores.translateZ.set(translateZ);
  8430. }
  8431. /**
  8432. * @param {number|string|null} width -
  8433. */
  8434. set width(width2) {
  8435. this.#stores.width.set(width2);
  8436. }
  8437. /**
  8438. * @param {number|string|null} zIndex -
  8439. */
  8440. set zIndex(zIndex) {
  8441. this.#stores.zIndex.set(zIndex);
  8442. }
  8443. /**
  8444. * Assigns current position to object passed into method.
  8445. *
  8446. * @param {object|PositionData} [position] - Target to assign current position data.
  8447. *
  8448. * @param {PositionGetOptions} [options] - Defines options for specific keys and substituting null for numeric
  8449. * default values.
  8450. *
  8451. * @returns {PositionData} Passed in object with current position data.
  8452. */
  8453. get(position = {}, options) {
  8454. const keys = options?.keys;
  8455. const excludeKeys = options?.exclude;
  8456. const numeric = options?.numeric ?? false;
  8457. if (isIterable(keys)) {
  8458. if (numeric) {
  8459. for (const key of keys) {
  8460. position[key] = this[key] ?? numericDefaults[key];
  8461. }
  8462. } else {
  8463. for (const key of keys) {
  8464. position[key] = this[key];
  8465. }
  8466. }
  8467. if (isIterable(excludeKeys)) {
  8468. for (const key of excludeKeys) {
  8469. delete position[key];
  8470. }
  8471. }
  8472. return position;
  8473. } else {
  8474. const data = Object.assign(position, this.#data);
  8475. if (isIterable(excludeKeys)) {
  8476. for (const key of excludeKeys) {
  8477. delete data[key];
  8478. }
  8479. }
  8480. if (numeric) {
  8481. setNumericDefaults(data);
  8482. }
  8483. return data;
  8484. }
  8485. }
  8486. /**
  8487. * @returns {PositionData} Current position data.
  8488. */
  8489. toJSON() {
  8490. return Object.assign({}, this.#data);
  8491. }
  8492. /**
  8493. * All calculation and updates of position are implemented in {@link Position}. This allows position to be fully
  8494. * reactive and in control of updating inline styles for the application.
  8495. *
  8496. * Note: the logic for updating position is improved and changes a few aspects from the default
  8497. * {@link Application.setPosition}. The gate on `popOut` is removed, so to ensure no positional application occurs
  8498. * popOut applications can set `this.options.positionable` to false ensuring no positional inline styles are
  8499. * applied.
  8500. *
  8501. * The initial set call on an application with a target element will always set width / height as this is
  8502. * necessary for correct calculations.
  8503. *
  8504. * When a target element is present updated styles are applied after validation. To modify the behavior of set
  8505. * implement one or more validator functions and add them from the application via
  8506. * `this.position.validators.add(<Function>)`.
  8507. *
  8508. * Updates to any target element are decoupled from the underlying Position data. This method returns this instance
  8509. * that you can then await on the target element inline style update by using {@link Position.elementUpdated}.
  8510. *
  8511. * @param {PositionDataExtended} [position] - Position data to set.
  8512. *
  8513. * @returns {Position} This Position instance.
  8514. */
  8515. set(position = {}) {
  8516. if (typeof position !== "object") {
  8517. throw new TypeError(`Position - set error: 'position' is not an object.`);
  8518. }
  8519. const parent = this.#parent;
  8520. if (!this.#enabled) {
  8521. return this;
  8522. }
  8523. if (parent !== void 0 && typeof parent?.options?.positionable === "boolean" && !parent?.options?.positionable) {
  8524. return this;
  8525. }
  8526. const immediateElementUpdate = position.immediateElementUpdate === true;
  8527. const data = this.#data;
  8528. const transforms = this.#transforms;
  8529. const targetEl = parent instanceof HTMLElement ? parent : parent?.elementTarget;
  8530. const el = targetEl instanceof HTMLElement && targetEl.isConnected ? targetEl : void 0;
  8531. const changeSet = this.#positionChangeSet;
  8532. const styleCache = this.#styleCache;
  8533. if (el) {
  8534. if (!styleCache.hasData(el)) {
  8535. styleCache.update(el);
  8536. if (!styleCache.hasWillChange)
  8537. ;
  8538. changeSet.set(true);
  8539. this.#updateElementData.queued = false;
  8540. }
  8541. convertRelative(position, this);
  8542. position = this.#updatePosition(position, parent, el, styleCache);
  8543. if (position === null) {
  8544. return this;
  8545. }
  8546. }
  8547. if (Number.isFinite(position.left)) {
  8548. position.left = Math.round(position.left);
  8549. if (data.left !== position.left) {
  8550. data.left = position.left;
  8551. changeSet.left = true;
  8552. }
  8553. }
  8554. if (Number.isFinite(position.top)) {
  8555. position.top = Math.round(position.top);
  8556. if (data.top !== position.top) {
  8557. data.top = position.top;
  8558. changeSet.top = true;
  8559. }
  8560. }
  8561. if (Number.isFinite(position.maxHeight) || position.maxHeight === null) {
  8562. position.maxHeight = typeof position.maxHeight === "number" ? Math.round(position.maxHeight) : null;
  8563. if (data.maxHeight !== position.maxHeight) {
  8564. data.maxHeight = position.maxHeight;
  8565. changeSet.maxHeight = true;
  8566. }
  8567. }
  8568. if (Number.isFinite(position.maxWidth) || position.maxWidth === null) {
  8569. position.maxWidth = typeof position.maxWidth === "number" ? Math.round(position.maxWidth) : null;
  8570. if (data.maxWidth !== position.maxWidth) {
  8571. data.maxWidth = position.maxWidth;
  8572. changeSet.maxWidth = true;
  8573. }
  8574. }
  8575. if (Number.isFinite(position.minHeight) || position.minHeight === null) {
  8576. position.minHeight = typeof position.minHeight === "number" ? Math.round(position.minHeight) : null;
  8577. if (data.minHeight !== position.minHeight) {
  8578. data.minHeight = position.minHeight;
  8579. changeSet.minHeight = true;
  8580. }
  8581. }
  8582. if (Number.isFinite(position.minWidth) || position.minWidth === null) {
  8583. position.minWidth = typeof position.minWidth === "number" ? Math.round(position.minWidth) : null;
  8584. if (data.minWidth !== position.minWidth) {
  8585. data.minWidth = position.minWidth;
  8586. changeSet.minWidth = true;
  8587. }
  8588. }
  8589. if (Number.isFinite(position.rotateX) || position.rotateX === null) {
  8590. if (data.rotateX !== position.rotateX) {
  8591. data.rotateX = transforms.rotateX = position.rotateX;
  8592. changeSet.transform = true;
  8593. }
  8594. }
  8595. if (Number.isFinite(position.rotateY) || position.rotateY === null) {
  8596. if (data.rotateY !== position.rotateY) {
  8597. data.rotateY = transforms.rotateY = position.rotateY;
  8598. changeSet.transform = true;
  8599. }
  8600. }
  8601. if (Number.isFinite(position.rotateZ) || position.rotateZ === null) {
  8602. if (data.rotateZ !== position.rotateZ) {
  8603. data.rotateZ = transforms.rotateZ = position.rotateZ;
  8604. changeSet.transform = true;
  8605. }
  8606. }
  8607. if (Number.isFinite(position.scale) || position.scale === null) {
  8608. position.scale = typeof position.scale === "number" ? Math.max(0, Math.min(position.scale, 1e3)) : null;
  8609. if (data.scale !== position.scale) {
  8610. data.scale = transforms.scale = position.scale;
  8611. changeSet.transform = true;
  8612. }
  8613. }
  8614. if (typeof position.transformOrigin === "string" && transformOrigins.includes(
  8615. position.transformOrigin
  8616. ) || position.transformOrigin === null) {
  8617. if (data.transformOrigin !== position.transformOrigin) {
  8618. data.transformOrigin = position.transformOrigin;
  8619. changeSet.transformOrigin = true;
  8620. }
  8621. }
  8622. if (Number.isFinite(position.translateX) || position.translateX === null) {
  8623. if (data.translateX !== position.translateX) {
  8624. data.translateX = transforms.translateX = position.translateX;
  8625. changeSet.transform = true;
  8626. }
  8627. }
  8628. if (Number.isFinite(position.translateY) || position.translateY === null) {
  8629. if (data.translateY !== position.translateY) {
  8630. data.translateY = transforms.translateY = position.translateY;
  8631. changeSet.transform = true;
  8632. }
  8633. }
  8634. if (Number.isFinite(position.translateZ) || position.translateZ === null) {
  8635. if (data.translateZ !== position.translateZ) {
  8636. data.translateZ = transforms.translateZ = position.translateZ;
  8637. changeSet.transform = true;
  8638. }
  8639. }
  8640. if (Number.isFinite(position.zIndex)) {
  8641. position.zIndex = Math.round(position.zIndex);
  8642. if (data.zIndex !== position.zIndex) {
  8643. data.zIndex = position.zIndex;
  8644. changeSet.zIndex = true;
  8645. }
  8646. }
  8647. if (Number.isFinite(position.width) || position.width === "auto" || position.width === "inherit" || position.width === null) {
  8648. position.width = typeof position.width === "number" ? Math.round(position.width) : position.width;
  8649. if (data.width !== position.width) {
  8650. data.width = position.width;
  8651. changeSet.width = true;
  8652. }
  8653. }
  8654. if (Number.isFinite(position.height) || position.height === "auto" || position.height === "inherit" || position.height === null) {
  8655. position.height = typeof position.height === "number" ? Math.round(position.height) : position.height;
  8656. if (data.height !== position.height) {
  8657. data.height = position.height;
  8658. changeSet.height = true;
  8659. }
  8660. }
  8661. if (el) {
  8662. const defaultData = this.#state.getDefault();
  8663. if (typeof defaultData !== "object") {
  8664. this.#state.save({ name: "#defaultData", ...Object.assign({}, data) });
  8665. }
  8666. if (immediateElementUpdate) {
  8667. UpdateElementManager.immediate(el, this.#updateElementData);
  8668. this.#updateElementPromise = Promise.resolve(performance.now());
  8669. } else if (!this.#updateElementData.queued) {
  8670. this.#updateElementPromise = UpdateElementManager.add(el, this.#updateElementData);
  8671. }
  8672. } else {
  8673. UpdateElementManager.updateSubscribers(this.#updateElementData);
  8674. }
  8675. return this;
  8676. }
  8677. /**
  8678. *
  8679. * @param {function(PositionData): void} handler - Callback function that is invoked on update / changes. Receives
  8680. * a copy of the PositionData.
  8681. *
  8682. * @returns {(function(): void)} Unsubscribe function.
  8683. */
  8684. subscribe(handler) {
  8685. this.#subscriptions.push(handler);
  8686. handler(Object.assign({}, this.#data));
  8687. return () => {
  8688. const index = this.#subscriptions.findIndex((sub) => sub === handler);
  8689. if (index >= 0) {
  8690. this.#subscriptions.splice(index, 1);
  8691. }
  8692. };
  8693. }
  8694. /**
  8695. * @param {PositionDataExtended} opts -
  8696. *
  8697. * @param {number|null} opts.left -
  8698. *
  8699. * @param {number|null} opts.top -
  8700. *
  8701. * @param {number|null} opts.maxHeight -
  8702. *
  8703. * @param {number|null} opts.maxWidth -
  8704. *
  8705. * @param {number|null} opts.minHeight -
  8706. *
  8707. * @param {number|null} opts.minWidth -
  8708. *
  8709. * @param {number|'auto'|null} opts.width -
  8710. *
  8711. * @param {number|'auto'|null} opts.height -
  8712. *
  8713. * @param {number|null} opts.rotateX -
  8714. *
  8715. * @param {number|null} opts.rotateY -
  8716. *
  8717. * @param {number|null} opts.rotateZ -
  8718. *
  8719. * @param {number|null} opts.scale -
  8720. *
  8721. * @param {string} opts.transformOrigin -
  8722. *
  8723. * @param {number|null} opts.translateX -
  8724. *
  8725. * @param {number|null} opts.translateY -
  8726. *
  8727. * @param {number|null} opts.translateZ -
  8728. *
  8729. * @param {number|null} opts.zIndex -
  8730. *
  8731. * @param {number|null} opts.rotation - alias for rotateZ
  8732. *
  8733. * @param {*} opts.rest -
  8734. *
  8735. * @param {object} parent -
  8736. *
  8737. * @param {HTMLElement} el -
  8738. *
  8739. * @param {StyleCache} styleCache -
  8740. *
  8741. * @returns {null|PositionData} Updated position data or null if validation fails.
  8742. */
  8743. #updatePosition({
  8744. // Directly supported parameters
  8745. left,
  8746. top,
  8747. maxWidth,
  8748. maxHeight,
  8749. minWidth,
  8750. minHeight,
  8751. width: width2,
  8752. height,
  8753. rotateX,
  8754. rotateY,
  8755. rotateZ,
  8756. scale: scale2,
  8757. transformOrigin,
  8758. translateX,
  8759. translateY,
  8760. translateZ,
  8761. zIndex,
  8762. // Aliased parameters
  8763. rotation: rotation2,
  8764. ...rest
  8765. } = {}, parent, el, styleCache) {
  8766. let currentPosition = s_DATA_UPDATE.copy(this.#data);
  8767. if (el.style.width === "" || width2 !== void 0) {
  8768. if (width2 === "auto" || currentPosition.width === "auto" && width2 !== null) {
  8769. currentPosition.width = "auto";
  8770. width2 = styleCache.offsetWidth;
  8771. } else if (width2 === "inherit" || currentPosition.width === "inherit" && width2 !== null) {
  8772. currentPosition.width = "inherit";
  8773. width2 = styleCache.offsetWidth;
  8774. } else {
  8775. const newWidth = Number.isFinite(width2) ? width2 : currentPosition.width;
  8776. currentPosition.width = width2 = Number.isFinite(newWidth) ? Math.round(newWidth) : styleCache.offsetWidth;
  8777. }
  8778. } else {
  8779. width2 = Number.isFinite(currentPosition.width) ? currentPosition.width : styleCache.offsetWidth;
  8780. }
  8781. if (el.style.height === "" || height !== void 0) {
  8782. if (height === "auto" || currentPosition.height === "auto" && height !== null) {
  8783. currentPosition.height = "auto";
  8784. height = styleCache.offsetHeight;
  8785. } else if (height === "inherit" || currentPosition.height === "inherit" && height !== null) {
  8786. currentPosition.height = "inherit";
  8787. height = styleCache.offsetHeight;
  8788. } else {
  8789. const newHeight = Number.isFinite(height) ? height : currentPosition.height;
  8790. currentPosition.height = height = Number.isFinite(newHeight) ? Math.round(newHeight) : styleCache.offsetHeight;
  8791. }
  8792. } else {
  8793. height = Number.isFinite(currentPosition.height) ? currentPosition.height : styleCache.offsetHeight;
  8794. }
  8795. if (Number.isFinite(left)) {
  8796. currentPosition.left = left;
  8797. } else if (!Number.isFinite(currentPosition.left)) {
  8798. currentPosition.left = typeof this.#options.initialHelper?.getLeft === "function" ? this.#options.initialHelper.getLeft(width2) : 0;
  8799. }
  8800. if (Number.isFinite(top)) {
  8801. currentPosition.top = top;
  8802. } else if (!Number.isFinite(currentPosition.top)) {
  8803. currentPosition.top = typeof this.#options.initialHelper?.getTop === "function" ? this.#options.initialHelper.getTop(height) : 0;
  8804. }
  8805. if (Number.isFinite(maxHeight) || maxHeight === null) {
  8806. currentPosition.maxHeight = Number.isFinite(maxHeight) ? Math.round(maxHeight) : null;
  8807. }
  8808. if (Number.isFinite(maxWidth) || maxWidth === null) {
  8809. currentPosition.maxWidth = Number.isFinite(maxWidth) ? Math.round(maxWidth) : null;
  8810. }
  8811. if (Number.isFinite(minHeight) || minHeight === null) {
  8812. currentPosition.minHeight = Number.isFinite(minHeight) ? Math.round(minHeight) : null;
  8813. }
  8814. if (Number.isFinite(minWidth) || minWidth === null) {
  8815. currentPosition.minWidth = Number.isFinite(minWidth) ? Math.round(minWidth) : null;
  8816. }
  8817. if (Number.isFinite(rotateX) || rotateX === null) {
  8818. currentPosition.rotateX = rotateX;
  8819. }
  8820. if (Number.isFinite(rotateY) || rotateY === null) {
  8821. currentPosition.rotateY = rotateY;
  8822. }
  8823. if (rotateZ !== currentPosition.rotateZ && (Number.isFinite(rotateZ) || rotateZ === null)) {
  8824. currentPosition.rotateZ = rotateZ;
  8825. } else if (rotation2 !== currentPosition.rotateZ && (Number.isFinite(rotation2) || rotation2 === null)) {
  8826. currentPosition.rotateZ = rotation2;
  8827. }
  8828. if (Number.isFinite(translateX) || translateX === null) {
  8829. currentPosition.translateX = translateX;
  8830. }
  8831. if (Number.isFinite(translateY) || translateY === null) {
  8832. currentPosition.translateY = translateY;
  8833. }
  8834. if (Number.isFinite(translateZ) || translateZ === null) {
  8835. currentPosition.translateZ = translateZ;
  8836. }
  8837. if (Number.isFinite(scale2) || scale2 === null) {
  8838. currentPosition.scale = typeof scale2 === "number" ? Math.max(0, Math.min(scale2, 1e3)) : null;
  8839. }
  8840. if (typeof transformOrigin === "string" || transformOrigin === null) {
  8841. currentPosition.transformOrigin = transformOrigins.includes(transformOrigin) ? transformOrigin : null;
  8842. }
  8843. if (Number.isFinite(zIndex) || zIndex === null) {
  8844. currentPosition.zIndex = typeof zIndex === "number" ? Math.round(zIndex) : zIndex;
  8845. }
  8846. const validatorData = this.#validatorData;
  8847. if (this.#validators.enabled && validatorData.length) {
  8848. s_VALIDATION_DATA.parent = parent;
  8849. s_VALIDATION_DATA.el = el;
  8850. s_VALIDATION_DATA.computed = styleCache.computed;
  8851. s_VALIDATION_DATA.transforms = this.#transforms;
  8852. s_VALIDATION_DATA.height = height;
  8853. s_VALIDATION_DATA.width = width2;
  8854. s_VALIDATION_DATA.marginLeft = styleCache.marginLeft;
  8855. s_VALIDATION_DATA.marginTop = styleCache.marginTop;
  8856. s_VALIDATION_DATA.maxHeight = styleCache.maxHeight ?? currentPosition.maxHeight;
  8857. s_VALIDATION_DATA.maxWidth = styleCache.maxWidth ?? currentPosition.maxWidth;
  8858. const isMinimized = parent?.reactive?.minimized ?? false;
  8859. s_VALIDATION_DATA.minHeight = isMinimized ? currentPosition.minHeight ?? 0 : styleCache.minHeight || (currentPosition.minHeight ?? 0);
  8860. s_VALIDATION_DATA.minWidth = isMinimized ? currentPosition.minWidth ?? 0 : styleCache.minWidth || (currentPosition.minWidth ?? 0);
  8861. for (let cntr = 0; cntr < validatorData.length; cntr++) {
  8862. s_VALIDATION_DATA.position = currentPosition;
  8863. s_VALIDATION_DATA.rest = rest;
  8864. currentPosition = validatorData[cntr].validator(s_VALIDATION_DATA);
  8865. if (currentPosition === null) {
  8866. return null;
  8867. }
  8868. }
  8869. }
  8870. return currentPosition;
  8871. }
  8872. }
  8873. const s_DATA_UPDATE = new PositionData();
  8874. const s_VALIDATION_DATA = {
  8875. position: void 0,
  8876. parent: void 0,
  8877. el: void 0,
  8878. computed: void 0,
  8879. transforms: void 0,
  8880. height: void 0,
  8881. width: void 0,
  8882. marginLeft: void 0,
  8883. marginTop: void 0,
  8884. maxHeight: void 0,
  8885. maxWidth: void 0,
  8886. minHeight: void 0,
  8887. minWidth: void 0,
  8888. rest: void 0
  8889. };
  8890. Object.seal(s_VALIDATION_DATA);
  8891. class ApplicationState {
  8892. /** @type {ApplicationShellExt} */
  8893. #application;
  8894. /** @type {Map<string, ApplicationStateData>} */
  8895. #dataSaved = /* @__PURE__ */ new Map();
  8896. /**
  8897. * @param {ApplicationShellExt} application - The application.
  8898. */
  8899. constructor(application) {
  8900. this.#application = application;
  8901. Object.seal(this);
  8902. }
  8903. /**
  8904. * Returns current application state along with any extra data passed into method.
  8905. *
  8906. * @param {object} [extra] - Extra data to add to application state.
  8907. *
  8908. * @returns {ApplicationStateData} Passed in object with current application state.
  8909. */
  8910. get(extra = {}) {
  8911. return Object.assign(extra, {
  8912. position: this.#application?.position?.get(),
  8913. beforeMinimized: this.#application?.position?.state.get({ name: "#beforeMinimized" }),
  8914. options: Object.assign({}, this.#application?.options),
  8915. ui: { minimized: this.#application?.reactive?.minimized }
  8916. });
  8917. }
  8918. /**
  8919. * Returns any stored save state by name.
  8920. *
  8921. * @param {string} name - Saved data set name.
  8922. *
  8923. * @returns {ApplicationStateData} The saved data set.
  8924. */
  8925. getSave({ name }) {
  8926. if (typeof name !== "string") {
  8927. throw new TypeError(`ApplicationState - getSave error: 'name' is not a string.`);
  8928. }
  8929. return this.#dataSaved.get(name);
  8930. }
  8931. /**
  8932. * Removes and returns any application state by name.
  8933. *
  8934. * @param {object} options - Options.
  8935. *
  8936. * @param {string} options.name - Name to remove and retrieve.
  8937. *
  8938. * @returns {ApplicationStateData} Saved application data.
  8939. */
  8940. remove({ name }) {
  8941. if (typeof name !== "string") {
  8942. throw new TypeError(`ApplicationState - remove: 'name' is not a string.`);
  8943. }
  8944. const data = this.#dataSaved.get(name);
  8945. this.#dataSaved.delete(name);
  8946. return data;
  8947. }
  8948. /**
  8949. * Restores a saved application state returning the data. Several optional parameters are available
  8950. * to control whether the restore action occurs silently (no store / inline styles updates), animates
  8951. * to the stored data, or simply sets the stored data. Restoring via {@link AnimationAPI.to} allows
  8952. * specification of the duration, easing, and interpolate functions along with configuring a Promise to be
  8953. * returned if awaiting the end of the animation.
  8954. *
  8955. * @param {object} params - Parameters
  8956. *
  8957. * @param {string} params.name - Saved data set name.
  8958. *
  8959. * @param {boolean} [params.remove=false] - Remove data set.
  8960. *
  8961. * @param {boolean} [params.async=false] - If animating return a Promise that resolves with any saved data.
  8962. *
  8963. * @param {boolean} [params.animateTo=false] - Animate to restore data.
  8964. *
  8965. * @param {number} [params.duration=0.1] - Duration in seconds.
  8966. *
  8967. * @param {Function} [params.ease=linear] - Easing function.
  8968. *
  8969. * @param {Function} [params.interpolate=lerp] - Interpolation function.
  8970. *
  8971. * @returns {ApplicationStateData|Promise<ApplicationStateData>} Saved application data.
  8972. */
  8973. restore({
  8974. name,
  8975. remove = false,
  8976. async = false,
  8977. animateTo = false,
  8978. duration = 0.1,
  8979. ease = identity,
  8980. interpolate: interpolate2 = lerp$5
  8981. }) {
  8982. if (typeof name !== "string") {
  8983. throw new TypeError(`ApplicationState - restore error: 'name' is not a string.`);
  8984. }
  8985. const dataSaved = this.#dataSaved.get(name);
  8986. if (dataSaved) {
  8987. if (remove) {
  8988. this.#dataSaved.delete(name);
  8989. }
  8990. if (async) {
  8991. return this.set(dataSaved, { async, animateTo, duration, ease, interpolate: interpolate2 }).then(() => dataSaved);
  8992. } else {
  8993. this.set(dataSaved, { async, animateTo, duration, ease, interpolate: interpolate2 });
  8994. }
  8995. }
  8996. return dataSaved;
  8997. }
  8998. /**
  8999. * Saves current application state with the opportunity to add extra data to the saved state.
  9000. *
  9001. * @param {object} options - Options.
  9002. *
  9003. * @param {string} options.name - name to index this saved data.
  9004. *
  9005. * @param {...*} [options.extra] - Extra data to add to saved data.
  9006. *
  9007. * @returns {ApplicationStateData} Current application data
  9008. */
  9009. save({ name, ...extra }) {
  9010. if (typeof name !== "string") {
  9011. throw new TypeError(`ApplicationState - save error: 'name' is not a string.`);
  9012. }
  9013. const data = this.get(extra);
  9014. this.#dataSaved.set(name, data);
  9015. return data;
  9016. }
  9017. /**
  9018. * Restores a saved application state returning the data. Several optional parameters are available
  9019. * to control whether the restore action occurs silently (no store / inline styles updates), animates
  9020. * to the stored data, or simply sets the stored data. Restoring via {@link AnimationAPI.to} allows
  9021. * specification of the duration, easing, and interpolate functions along with configuring a Promise to be
  9022. * returned if awaiting the end of the animation.
  9023. *
  9024. * Note: If serializing application state any minimized apps will use the before minimized state on initial render
  9025. * of the app as it is currently not possible to render apps with Foundry VTT core API in the minimized state.
  9026. *
  9027. * TODO: THIS METHOD NEEDS TO BE REFACTORED WHEN TRL IS MADE INTO A STANDALONE FRAMEWORK.
  9028. *
  9029. * @param {ApplicationStateData} data - Saved data set name.
  9030. *
  9031. * @param {object} [opts] - Optional parameters
  9032. *
  9033. * @param {boolean} [opts.async=false] - If animating return a Promise that resolves with any saved data.
  9034. *
  9035. * @param {boolean} [opts.animateTo=false] - Animate to restore data.
  9036. *
  9037. * @param {number} [opts.duration=0.1] - Duration in seconds.
  9038. *
  9039. * @param {Function} [opts.ease=linear] - Easing function.
  9040. *
  9041. * @param {Function} [opts.interpolate=lerp] - Interpolation function.
  9042. *
  9043. * @returns {ApplicationShellExt|Promise<ApplicationShellExt>} When synchronous the application or Promise when
  9044. * animating resolving with application.
  9045. */
  9046. set(data, { async = false, animateTo = false, duration = 0.1, ease = identity, interpolate: interpolate2 = lerp$5 } = {}) {
  9047. if (!isObject(data)) {
  9048. throw new TypeError(`ApplicationState - restore error: 'data' is not an object.`);
  9049. }
  9050. const application = this.#application;
  9051. if (!isObject(data?.position)) {
  9052. console.warn(`ApplicationState.set warning: 'data.position' is not an object.`);
  9053. return application;
  9054. }
  9055. const rendered = application.rendered;
  9056. if (animateTo && !rendered) {
  9057. console.warn(`ApplicationState.set warning: Application is not rendered and 'animateTo' is true.`);
  9058. return application;
  9059. }
  9060. if (animateTo) {
  9061. if (data.position.transformOrigin !== application.position.transformOrigin) {
  9062. application.position.transformOrigin = data.position.transformOrigin;
  9063. }
  9064. if (isObject(data?.ui)) {
  9065. const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
  9066. if (application?.reactive?.minimized && !minimized) {
  9067. application.maximize({ animate: false, duration: 0 });
  9068. }
  9069. }
  9070. const promise2 = application.position.animate.to(
  9071. data.position,
  9072. { duration, ease, interpolate: interpolate2 }
  9073. ).finished.then((cancelled) => {
  9074. if (cancelled) {
  9075. return application;
  9076. }
  9077. if (isObject(data?.options)) {
  9078. application?.reactive.mergeOptions(data.options);
  9079. }
  9080. if (isObject(data?.ui)) {
  9081. const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
  9082. if (!application?.reactive?.minimized && minimized) {
  9083. application.minimize({ animate: false, duration: 0 });
  9084. }
  9085. }
  9086. if (isObject(data?.beforeMinimized)) {
  9087. application.position.state.set({ name: "#beforeMinimized", ...data.beforeMinimized });
  9088. }
  9089. return application;
  9090. });
  9091. if (async) {
  9092. return promise2;
  9093. }
  9094. } else {
  9095. if (rendered) {
  9096. if (isObject(data?.options)) {
  9097. application?.reactive.mergeOptions(data.options);
  9098. }
  9099. if (isObject(data?.ui)) {
  9100. const minimized = typeof data.ui?.minimized === "boolean" ? data.ui.minimized : false;
  9101. if (application?.reactive?.minimized && !minimized) {
  9102. application.maximize({ animate: false, duration: 0 });
  9103. } else if (!application?.reactive?.minimized && minimized) {
  9104. application.minimize({ animate: false, duration });
  9105. }
  9106. }
  9107. if (isObject(data?.beforeMinimized)) {
  9108. application.position.state.set({ name: "#beforeMinimized", ...data.beforeMinimized });
  9109. }
  9110. application.position.set(data.position);
  9111. } else {
  9112. let positionData = data.position;
  9113. if (isObject(data.beforeMinimized)) {
  9114. positionData = data.beforeMinimized;
  9115. positionData.left = data.position.left;
  9116. positionData.top = data.position.top;
  9117. }
  9118. application.position.set(positionData);
  9119. }
  9120. }
  9121. return application;
  9122. }
  9123. }
  9124. class GetSvelteData {
  9125. /**
  9126. * @type {MountedAppShell[]|null[]}
  9127. */
  9128. #applicationShellHolder;
  9129. /**
  9130. * @type {SvelteData[]}
  9131. */
  9132. #svelteData;
  9133. /**
  9134. * Keep a direct reference to the SvelteData array in an associated {@link SvelteApplication}.
  9135. *
  9136. * @param {MountedAppShell[]|null[]} applicationShellHolder - A reference to the MountedAppShell array.
  9137. *
  9138. * @param {SvelteData[]} svelteData - A reference to the SvelteData array of mounted components.
  9139. */
  9140. constructor(applicationShellHolder, svelteData) {
  9141. this.#applicationShellHolder = applicationShellHolder;
  9142. this.#svelteData = svelteData;
  9143. }
  9144. /**
  9145. * Returns any mounted {@link MountedAppShell}.
  9146. *
  9147. * @returns {MountedAppShell|null} Any mounted application shell.
  9148. */
  9149. get applicationShell() {
  9150. return this.#applicationShellHolder[0];
  9151. }
  9152. /**
  9153. * Returns the indexed Svelte component.
  9154. *
  9155. * @param {number} index -
  9156. *
  9157. * @returns {object} The loaded Svelte component.
  9158. */
  9159. component(index) {
  9160. const data = this.#svelteData[index];
  9161. return isObject(data) ? data?.component : void 0;
  9162. }
  9163. /**
  9164. * Returns the Svelte component entries iterator.
  9165. *
  9166. * @returns {Generator<Array<number|SvelteComponent>>} Svelte component entries iterator.
  9167. * @yields
  9168. */
  9169. *componentEntries() {
  9170. for (let cntr = 0; cntr < this.#svelteData.length; cntr++) {
  9171. yield [cntr, this.#svelteData[cntr].component];
  9172. }
  9173. }
  9174. /**
  9175. * Returns the Svelte component values iterator.
  9176. *
  9177. * @returns {Generator<SvelteComponent>} Svelte component values iterator.
  9178. * @yields
  9179. */
  9180. *componentValues() {
  9181. for (let cntr = 0; cntr < this.#svelteData.length; cntr++) {
  9182. yield this.#svelteData[cntr].component;
  9183. }
  9184. }
  9185. /**
  9186. * Returns the indexed SvelteData entry.
  9187. *
  9188. * @param {number} index -
  9189. *
  9190. * @returns {SvelteData} The loaded Svelte config + component.
  9191. */
  9192. data(index) {
  9193. return this.#svelteData[index];
  9194. }
  9195. /**
  9196. * Returns the {@link SvelteData} instance for a given component.
  9197. *
  9198. * @param {object} component - Svelte component.
  9199. *
  9200. * @returns {SvelteData} - The loaded Svelte config + component.
  9201. */
  9202. dataByComponent(component) {
  9203. for (const data of this.#svelteData) {
  9204. if (data.component === component) {
  9205. return data;
  9206. }
  9207. }
  9208. return void 0;
  9209. }
  9210. /**
  9211. * Returns the SvelteData entries iterator.
  9212. *
  9213. * @returns {IterableIterator<[number, SvelteData]>} SvelteData entries iterator.
  9214. */
  9215. dataEntries() {
  9216. return this.#svelteData.entries();
  9217. }
  9218. /**
  9219. * Returns the SvelteData values iterator.
  9220. *
  9221. * @returns {IterableIterator<SvelteData>} SvelteData values iterator.
  9222. */
  9223. dataValues() {
  9224. return this.#svelteData.values();
  9225. }
  9226. /**
  9227. * Returns the length of the mounted Svelte component list.
  9228. *
  9229. * @returns {number} Length of mounted Svelte component list.
  9230. */
  9231. get length() {
  9232. return this.#svelteData.length;
  9233. }
  9234. }
  9235. function loadSvelteConfig({ app, template, config, elementRootUpdate } = {}) {
  9236. const svelteOptions = isObject(config.options) ? config.options : {};
  9237. let target;
  9238. if (config.target instanceof HTMLElement) {
  9239. target = config.target;
  9240. } else if (template instanceof HTMLElement && typeof config.target === "string") {
  9241. target = template.querySelector(config.target);
  9242. } else {
  9243. target = document.createDocumentFragment();
  9244. }
  9245. if (target === void 0) {
  9246. console.log(
  9247. `%c[TRL] loadSvelteConfig error - could not find target selector, '${config.target}', for config:
  9248. `,
  9249. "background: rgb(57,34,34)",
  9250. config
  9251. );
  9252. throw new Error();
  9253. }
  9254. const NewSvelteComponent = config.class;
  9255. const svelteConfig = parseSvelteConfig({ ...config, target }, app);
  9256. const externalContext = svelteConfig.context.get("#external");
  9257. externalContext.application = app;
  9258. externalContext.elementRootUpdate = elementRootUpdate;
  9259. externalContext.sessionStorage = app.reactive.sessionStorage;
  9260. let eventbus;
  9261. if (isObject(app._eventbus) && typeof app._eventbus.createProxy === "function") {
  9262. eventbus = app._eventbus.createProxy();
  9263. externalContext.eventbus = eventbus;
  9264. }
  9265. Object.seal(externalContext);
  9266. svelteConfig.context.set("external", new Proxy({}, {
  9267. get(targetUnused, prop) {
  9268. console.warn(`[TRL] Deprecation warning: Please change getContext('external') to getContext('#external').`);
  9269. return externalContext[prop];
  9270. }
  9271. }));
  9272. const component = new NewSvelteComponent(svelteConfig);
  9273. svelteConfig.eventbus = eventbus;
  9274. let element2;
  9275. if (isApplicationShell(component)) {
  9276. element2 = component.elementRoot;
  9277. }
  9278. if (target instanceof DocumentFragment && target.firstElementChild) {
  9279. if (element2 === void 0) {
  9280. element2 = target.firstElementChild;
  9281. }
  9282. template.append(target);
  9283. } else if (config.target instanceof HTMLElement && element2 === void 0) {
  9284. if (config.target instanceof HTMLElement && typeof svelteOptions.selectorElement !== "string") {
  9285. console.log(
  9286. `%c[TRL] loadSvelteConfig error - HTMLElement target with no 'selectorElement' defined.
  9287. Note: If configuring an application shell and directly targeting a HTMLElement did you bind an'elementRoot' and include '<svelte:options accessors={true}/>'?
  9288. Offending config:
  9289. `,
  9290. "background: rgb(57,34,34)",
  9291. config
  9292. );
  9293. throw new Error();
  9294. }
  9295. element2 = target.querySelector(svelteOptions.selectorElement);
  9296. if (element2 === null || element2 === void 0) {
  9297. console.log(
  9298. `%c[TRL] loadSvelteConfig error - HTMLElement target with 'selectorElement', '${svelteOptions.selectorElement}', not found for config:
  9299. `,
  9300. "background: rgb(57,34,34)",
  9301. config
  9302. );
  9303. throw new Error();
  9304. }
  9305. }
  9306. const injectHTML = !(config.target instanceof HTMLElement);
  9307. return { config: svelteConfig, component, element: element2, injectHTML };
  9308. }
  9309. class SvelteReactive {
  9310. /**
  9311. * @type {SvelteApplication}
  9312. */
  9313. #application;
  9314. /**
  9315. * @type {boolean}
  9316. */
  9317. #initialized = false;
  9318. /** @type {TJSSessionStorage} */
  9319. #sessionStorage;
  9320. /**
  9321. * The Application option store which is injected into mounted Svelte component context under the `external` key.
  9322. *
  9323. * @type {StoreAppOptions}
  9324. */
  9325. #storeAppOptions;
  9326. /**
  9327. * Stores the update function for `#storeAppOptions`.
  9328. *
  9329. * @type {import('svelte/store').Writable.update}
  9330. */
  9331. #storeAppOptionsUpdate;
  9332. /**
  9333. * Stores the UI state data to make it accessible via getters.
  9334. *
  9335. * @type {object}
  9336. */
  9337. #dataUIState;
  9338. /**
  9339. * The UI option store which is injected into mounted Svelte component context under the `external` key.
  9340. *
  9341. * @type {StoreUIOptions}
  9342. */
  9343. #storeUIState;
  9344. /**
  9345. * Stores the update function for `#storeUIState`.
  9346. *
  9347. * @type {import('svelte/store').Writable.update}
  9348. */
  9349. #storeUIStateUpdate;
  9350. /**
  9351. * Stores the unsubscribe functions from local store subscriptions.
  9352. *
  9353. * @type {import('svelte/store').Unsubscriber[]}
  9354. */
  9355. #storeUnsubscribe = [];
  9356. /**
  9357. * @param {SvelteApplication} application - The host Foundry application.
  9358. */
  9359. constructor(application) {
  9360. this.#application = application;
  9361. const optionsSessionStorage = application?.options?.sessionStorage;
  9362. if (optionsSessionStorage !== void 0 && !(optionsSessionStorage instanceof TJSSessionStorage)) {
  9363. throw new TypeError(`'options.sessionStorage' is not an instance of TJSSessionStorage.`);
  9364. }
  9365. this.#sessionStorage = optionsSessionStorage !== void 0 ? optionsSessionStorage : new TJSSessionStorage();
  9366. }
  9367. /**
  9368. * Initializes reactive support. Package private for internal use.
  9369. *
  9370. * @returns {SvelteStores|void} Internal methods to interact with Svelte stores.
  9371. * @package
  9372. */
  9373. initialize() {
  9374. if (this.#initialized) {
  9375. return;
  9376. }
  9377. this.#initialized = true;
  9378. this.#storesInitialize();
  9379. return {
  9380. appOptionsUpdate: this.#storeAppOptionsUpdate,
  9381. uiOptionsUpdate: this.#storeUIStateUpdate,
  9382. subscribe: this.#storesSubscribe.bind(this),
  9383. unsubscribe: this.#storesUnsubscribe.bind(this)
  9384. };
  9385. }
  9386. // Store getters -----------------------------------------------------------------------------------------------------
  9387. /**
  9388. * @returns {TJSSessionStorage} Returns TJSSessionStorage instance.
  9389. */
  9390. get sessionStorage() {
  9391. return this.#sessionStorage;
  9392. }
  9393. /**
  9394. * Returns the store for app options.
  9395. *
  9396. * @returns {StoreAppOptions} App options store.
  9397. */
  9398. get storeAppOptions() {
  9399. return this.#storeAppOptions;
  9400. }
  9401. /**
  9402. * Returns the store for UI options.
  9403. *
  9404. * @returns {StoreUIOptions} UI options store.
  9405. */
  9406. get storeUIState() {
  9407. return this.#storeUIState;
  9408. }
  9409. // Only reactive getters ---------------------------------------------------------------------------------------------
  9410. /**
  9411. * Returns the current dragging UI state.
  9412. *
  9413. * @returns {boolean} Dragging UI state.
  9414. */
  9415. get dragging() {
  9416. return this.#dataUIState.dragging;
  9417. }
  9418. /**
  9419. * Returns the current minimized UI state.
  9420. *
  9421. * @returns {boolean} Minimized UI state.
  9422. */
  9423. get minimized() {
  9424. return this.#dataUIState.minimized;
  9425. }
  9426. /**
  9427. * Returns the current resizing UI state.
  9428. *
  9429. * @returns {boolean} Resizing UI state.
  9430. */
  9431. get resizing() {
  9432. return this.#dataUIState.resizing;
  9433. }
  9434. // Reactive getter / setters -----------------------------------------------------------------------------------------
  9435. /**
  9436. * Returns the draggable app option.
  9437. *
  9438. * @returns {boolean} Draggable app option.
  9439. */
  9440. get draggable() {
  9441. return this.#application?.options?.draggable;
  9442. }
  9443. /**
  9444. * Returns the focusAuto app option.
  9445. *
  9446. * @returns {boolean} When true auto-management of app focus is enabled.
  9447. */
  9448. get focusAuto() {
  9449. return this.#application?.options?.focusAuto;
  9450. }
  9451. /**
  9452. * Returns the focusKeep app option.
  9453. *
  9454. * @returns {boolean} When `focusAuto` and `focusKeep` is true; keeps internal focus.
  9455. */
  9456. get focusKeep() {
  9457. return this.#application?.options?.focusKeep;
  9458. }
  9459. /**
  9460. * Returns the focusTrap app option.
  9461. *
  9462. * @returns {boolean} When true focus trapping / wrapping is enabled keeping focus inside app.
  9463. */
  9464. get focusTrap() {
  9465. return this.#application?.options?.focusTrap;
  9466. }
  9467. /**
  9468. * Returns the headerButtonNoClose app option.
  9469. *
  9470. * @returns {boolean} Remove the close the button in header app option.
  9471. */
  9472. get headerButtonNoClose() {
  9473. return this.#application?.options?.headerButtonNoClose;
  9474. }
  9475. /**
  9476. * Returns the headerButtonNoLabel app option.
  9477. *
  9478. * @returns {boolean} Remove the labels from buttons in header app option.
  9479. */
  9480. get headerButtonNoLabel() {
  9481. return this.#application?.options?.headerButtonNoLabel;
  9482. }
  9483. /**
  9484. * Returns the headerIcon app option.
  9485. *
  9486. * @returns {string|void} URL for header app icon.
  9487. */
  9488. get headerIcon() {
  9489. return this.#application?.options?.headerIcon;
  9490. }
  9491. /**
  9492. * Returns the headerNoTitleMinimized app option.
  9493. *
  9494. * @returns {boolean} When true removes the header title when minimized.
  9495. */
  9496. get headerNoTitleMinimized() {
  9497. return this.#application?.options?.headerNoTitleMinimized;
  9498. }
  9499. /**
  9500. * Returns the minimizable app option.
  9501. *
  9502. * @returns {boolean} Minimizable app option.
  9503. */
  9504. get minimizable() {
  9505. return this.#application?.options?.minimizable;
  9506. }
  9507. /**
  9508. * Returns the Foundry popOut state; {@link Application.popOut}
  9509. *
  9510. * @returns {boolean} Positionable app option.
  9511. */
  9512. get popOut() {
  9513. return this.#application.popOut;
  9514. }
  9515. /**
  9516. * Returns the positionable app option; {@link SvelteApplicationOptions.positionable}
  9517. *
  9518. * @returns {boolean} Positionable app option.
  9519. */
  9520. get positionable() {
  9521. return this.#application?.options?.positionable;
  9522. }
  9523. /**
  9524. * Returns the resizable option.
  9525. *
  9526. * @returns {boolean} Resizable app option.
  9527. */
  9528. get resizable() {
  9529. return this.#application?.options?.resizable;
  9530. }
  9531. /**
  9532. * Returns the title accessor from the parent Application class; {@link Application.title}
  9533. * TODO: Application v2; note that super.title localizes `this.options.title`; IMHO it shouldn't.
  9534. *
  9535. * @returns {string} Title.
  9536. */
  9537. get title() {
  9538. return this.#application.title;
  9539. }
  9540. /**
  9541. * Sets `this.options.draggable` which is reactive for application shells.
  9542. *
  9543. * @param {boolean} draggable - Sets the draggable option.
  9544. */
  9545. set draggable(draggable2) {
  9546. if (typeof draggable2 === "boolean") {
  9547. this.setOptions("draggable", draggable2);
  9548. }
  9549. }
  9550. /**
  9551. * Sets `this.options.focusAuto` which is reactive for application shells.
  9552. *
  9553. * @param {boolean} focusAuto - Sets the focusAuto option.
  9554. */
  9555. set focusAuto(focusAuto) {
  9556. if (typeof focusAuto === "boolean") {
  9557. this.setOptions("focusAuto", focusAuto);
  9558. }
  9559. }
  9560. /**
  9561. * Sets `this.options.focusKeep` which is reactive for application shells.
  9562. *
  9563. * @param {boolean} focusKeep - Sets the focusKeep option.
  9564. */
  9565. set focusKeep(focusKeep) {
  9566. if (typeof focusKeep === "boolean") {
  9567. this.setOptions("focusKeep", focusKeep);
  9568. }
  9569. }
  9570. /**
  9571. * Sets `this.options.focusTrap` which is reactive for application shells.
  9572. *
  9573. * @param {boolean} focusTrap - Sets the focusTrap option.
  9574. */
  9575. set focusTrap(focusTrap) {
  9576. if (typeof focusTrap === "boolean") {
  9577. this.setOptions("focusTrap", focusTrap);
  9578. }
  9579. }
  9580. /**
  9581. * Sets `this.options.headerButtonNoClose` which is reactive for application shells.
  9582. *
  9583. * @param {boolean} headerButtonNoClose - Sets the headerButtonNoClose option.
  9584. */
  9585. set headerButtonNoClose(headerButtonNoClose) {
  9586. if (typeof headerButtonNoClose === "boolean") {
  9587. this.setOptions("headerButtonNoClose", headerButtonNoClose);
  9588. }
  9589. }
  9590. /**
  9591. * Sets `this.options.headerButtonNoLabel` which is reactive for application shells.
  9592. *
  9593. * @param {boolean} headerButtonNoLabel - Sets the headerButtonNoLabel option.
  9594. */
  9595. set headerButtonNoLabel(headerButtonNoLabel) {
  9596. if (typeof headerButtonNoLabel === "boolean") {
  9597. this.setOptions("headerButtonNoLabel", headerButtonNoLabel);
  9598. }
  9599. }
  9600. /**
  9601. * Sets `this.options.headerIcon` which is reactive for application shells.
  9602. *
  9603. * @param {string|void} headerIcon - Sets the headerButtonNoLabel option.
  9604. */
  9605. set headerIcon(headerIcon) {
  9606. if (headerIcon === void 0 || typeof headerIcon === "string") {
  9607. this.setOptions("headerIcon", headerIcon);
  9608. }
  9609. }
  9610. /**
  9611. * Sets `this.options.headerNoTitleMinimized` which is reactive for application shells.
  9612. *
  9613. * @param {boolean} headerNoTitleMinimized - Sets the headerNoTitleMinimized option.
  9614. */
  9615. set headerNoTitleMinimized(headerNoTitleMinimized) {
  9616. if (typeof headerNoTitleMinimized === "boolean") {
  9617. this.setOptions("headerNoTitleMinimized", headerNoTitleMinimized);
  9618. }
  9619. }
  9620. /**
  9621. * Sets `this.options.minimizable` which is reactive for application shells that are also pop out.
  9622. *
  9623. * @param {boolean} minimizable - Sets the minimizable option.
  9624. */
  9625. set minimizable(minimizable) {
  9626. if (typeof minimizable === "boolean") {
  9627. this.setOptions("minimizable", minimizable);
  9628. }
  9629. }
  9630. /**
  9631. * Sets `this.options.popOut` which is reactive for application shells. This will add / remove this application
  9632. * from `ui.windows`.
  9633. *
  9634. * @param {boolean} popOut - Sets the popOut option.
  9635. */
  9636. set popOut(popOut) {
  9637. if (typeof popOut === "boolean") {
  9638. this.setOptions("popOut", popOut);
  9639. }
  9640. }
  9641. /**
  9642. * Sets `this.options.positionable` enabling / disabling {@link SvelteApplication.position.set}.
  9643. *
  9644. * @param {boolean} positionable - Sets the positionable option.
  9645. */
  9646. set positionable(positionable) {
  9647. if (typeof positionable === "boolean") {
  9648. this.setOptions("positionable", positionable);
  9649. }
  9650. }
  9651. /**
  9652. * Sets `this.options.resizable` which is reactive for application shells.
  9653. *
  9654. * @param {boolean} resizable - Sets the resizable option.
  9655. */
  9656. set resizable(resizable) {
  9657. if (typeof resizable === "boolean") {
  9658. this.setOptions("resizable", resizable);
  9659. }
  9660. }
  9661. /**
  9662. * Sets `this.options.title` which is reactive for application shells.
  9663. *
  9664. * Note: Will set empty string if title is undefined or null.
  9665. *
  9666. * @param {string|undefined|null} title - Application title; will be localized, so a translation key is fine.
  9667. */
  9668. set title(title) {
  9669. if (typeof title === "string") {
  9670. this.setOptions("title", title);
  9671. } else if (title === void 0 || title === null) {
  9672. this.setOptions("title", "");
  9673. }
  9674. }
  9675. // Reactive Options API -------------------------------------------------------------------------------------------
  9676. /**
  9677. * Provides a way to safely get this applications options given an accessor string which describes the
  9678. * entries to walk. To access deeper entries into the object format the accessor string with `.` between entries
  9679. * to walk.
  9680. *
  9681. * // TODO DOCUMENT the accessor in more detail.
  9682. *
  9683. * @param {string} accessor - The path / key to set. You can set multiple levels.
  9684. *
  9685. * @param {*} [defaultValue] - A default value returned if the accessor is not found.
  9686. *
  9687. * @returns {*} Value at the accessor.
  9688. */
  9689. getOptions(accessor, defaultValue) {
  9690. return safeAccess(this.#application.options, accessor, defaultValue);
  9691. }
  9692. /**
  9693. * Provides a way to merge `options` into this applications options and update the appOptions store.
  9694. *
  9695. * @param {object} options - The options object to merge with `this.options`.
  9696. */
  9697. mergeOptions(options) {
  9698. this.#storeAppOptionsUpdate((instanceOptions) => deepMerge(instanceOptions, options));
  9699. }
  9700. /**
  9701. * Provides a way to safely set this applications options given an accessor string which describes the
  9702. * entries to walk. To access deeper entries into the object format the accessor string with `.` between entries
  9703. * to walk.
  9704. *
  9705. * Additionally if an application shell Svelte component is mounted and exports the `appOptions` property then
  9706. * the application options is set to `appOptions` potentially updating the application shell / Svelte component.
  9707. *
  9708. * // TODO DOCUMENT the accessor in more detail.
  9709. *
  9710. * @param {string} accessor - The path / key to set. You can set multiple levels.
  9711. *
  9712. * @param {*} value - Value to set.
  9713. */
  9714. setOptions(accessor, value) {
  9715. const success = safeSet(this.#application.options, accessor, value);
  9716. if (success) {
  9717. this.#storeAppOptionsUpdate(() => this.#application.options);
  9718. }
  9719. }
  9720. /**
  9721. * Initializes the Svelte stores and derived stores for the application options and UI state.
  9722. *
  9723. * While writable stores are created the update method is stored in private variables locally and derived Readable
  9724. * stores are provided for essential options which are commonly used.
  9725. *
  9726. * These stores are injected into all Svelte components mounted under the `external` context: `storeAppOptions` and
  9727. * ` storeUIState`.
  9728. */
  9729. #storesInitialize() {
  9730. const writableAppOptions = writable$1(this.#application.options);
  9731. this.#storeAppOptionsUpdate = writableAppOptions.update;
  9732. const storeAppOptions = {
  9733. subscribe: writableAppOptions.subscribe,
  9734. draggable: propertyStore(writableAppOptions, "draggable"),
  9735. focusAuto: propertyStore(writableAppOptions, "focusAuto"),
  9736. focusKeep: propertyStore(writableAppOptions, "focusKeep"),
  9737. focusTrap: propertyStore(writableAppOptions, "focusTrap"),
  9738. headerButtonNoClose: propertyStore(writableAppOptions, "headerButtonNoClose"),
  9739. headerButtonNoLabel: propertyStore(writableAppOptions, "headerButtonNoLabel"),
  9740. headerIcon: propertyStore(writableAppOptions, "headerIcon"),
  9741. headerNoTitleMinimized: propertyStore(writableAppOptions, "headerNoTitleMinimized"),
  9742. minimizable: propertyStore(writableAppOptions, "minimizable"),
  9743. popOut: propertyStore(writableAppOptions, "popOut"),
  9744. positionable: propertyStore(writableAppOptions, "positionable"),
  9745. resizable: propertyStore(writableAppOptions, "resizable"),
  9746. title: propertyStore(writableAppOptions, "title")
  9747. };
  9748. Object.freeze(storeAppOptions);
  9749. this.#storeAppOptions = storeAppOptions;
  9750. this.#dataUIState = {
  9751. dragging: false,
  9752. headerButtons: [],
  9753. minimized: this.#application._minimized,
  9754. resizing: false
  9755. };
  9756. const writableUIOptions = writable$1(this.#dataUIState);
  9757. this.#storeUIStateUpdate = writableUIOptions.update;
  9758. const storeUIState = {
  9759. subscribe: writableUIOptions.subscribe,
  9760. dragging: propertyStore(writableUIOptions, "dragging"),
  9761. headerButtons: derived(writableUIOptions, ($options, set) => set($options.headerButtons)),
  9762. minimized: derived(writableUIOptions, ($options, set) => set($options.minimized)),
  9763. resizing: propertyStore(writableUIOptions, "resizing")
  9764. };
  9765. Object.freeze(storeUIState);
  9766. this.#storeUIState = storeUIState;
  9767. }
  9768. /**
  9769. * Registers local store subscriptions for app options. `popOut` controls registering this app with `ui.windows`.
  9770. *
  9771. * @see SvelteApplication._injectHTML
  9772. */
  9773. #storesSubscribe() {
  9774. this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.headerButtonNoClose, (value) => {
  9775. this.updateHeaderButtons({ headerButtonNoClose: value });
  9776. }));
  9777. this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.headerButtonNoLabel, (value) => {
  9778. this.updateHeaderButtons({ headerButtonNoLabel: value });
  9779. }));
  9780. this.#storeUnsubscribe.push(subscribeIgnoreFirst(this.#storeAppOptions.popOut, (value) => {
  9781. if (value && this.#application.rendered) {
  9782. globalThis.ui.windows[this.#application.appId] = this.#application;
  9783. } else {
  9784. delete globalThis.ui.windows[this.#application.appId];
  9785. }
  9786. }));
  9787. }
  9788. /**
  9789. * Unsubscribes from any locally monitored stores.
  9790. *
  9791. * @see SvelteApplication.close
  9792. */
  9793. #storesUnsubscribe() {
  9794. this.#storeUnsubscribe.forEach((unsubscribe) => unsubscribe());
  9795. this.#storeUnsubscribe = [];
  9796. }
  9797. /**
  9798. * Updates the UI Options store with the current header buttons. You may dynamically add / remove header buttons
  9799. * if using an application shell Svelte component. In either overriding `_getHeaderButtons` or responding to the
  9800. * Hooks fired return a new button array and the uiOptions store is updated and the application shell will render
  9801. * the new buttons.
  9802. *
  9803. * Optionally you can set in the SvelteApplication app options {@link SvelteApplicationOptions.headerButtonNoClose}
  9804. * to remove the close button and {@link SvelteApplicationOptions.headerButtonNoLabel} to true and labels will be
  9805. * removed from the header buttons.
  9806. *
  9807. * @param {object} opts - Optional parameters (for internal use)
  9808. *
  9809. * @param {boolean} opts.headerButtonNoClose - The value for `headerButtonNoClose`.
  9810. *
  9811. * @param {boolean} opts.headerButtonNoLabel - The value for `headerButtonNoLabel`.
  9812. */
  9813. updateHeaderButtons({
  9814. headerButtonNoClose = this.#application.options.headerButtonNoClose,
  9815. headerButtonNoLabel = this.#application.options.headerButtonNoLabel
  9816. } = {}) {
  9817. let buttons = this.#application._getHeaderButtons();
  9818. if (typeof headerButtonNoClose === "boolean" && headerButtonNoClose) {
  9819. buttons = buttons.filter((button) => button.class !== "close");
  9820. }
  9821. if (typeof headerButtonNoLabel === "boolean" && headerButtonNoLabel) {
  9822. for (const button of buttons) {
  9823. button.label = void 0;
  9824. }
  9825. }
  9826. this.#storeUIStateUpdate((options) => {
  9827. options.headerButtons = buttons;
  9828. return options;
  9829. });
  9830. }
  9831. }
  9832. class SvelteApplication extends Application {
  9833. /**
  9834. * Stores the first mounted component which follows the application shell contract.
  9835. *
  9836. * @type {MountedAppShell[]|null[]} Application shell.
  9837. */
  9838. #applicationShellHolder = [null];
  9839. /**
  9840. * Stores and manages application state for saving / restoring / serializing.
  9841. *
  9842. * @type {ApplicationState}
  9843. */
  9844. #applicationState;
  9845. /**
  9846. * Stores the target element which may not necessarily be the main element.
  9847. *
  9848. * @type {HTMLElement}
  9849. */
  9850. #elementTarget = null;
  9851. /**
  9852. * Stores the content element which is set for application shells.
  9853. *
  9854. * @type {HTMLElement}
  9855. */
  9856. #elementContent = null;
  9857. /**
  9858. * Stores initial z-index from `_renderOuter` to set to target element / Svelte component.
  9859. *
  9860. * @type {number}
  9861. */
  9862. #initialZIndex = 95;
  9863. /**
  9864. * Stores on mount state which is checked in _render to trigger onSvelteMount callback.
  9865. *
  9866. * @type {boolean}
  9867. */
  9868. #onMount = false;
  9869. /**
  9870. * The position store.
  9871. *
  9872. * @type {Position}
  9873. */
  9874. #position;
  9875. /**
  9876. * Contains the Svelte stores and reactive accessors.
  9877. *
  9878. * @type {SvelteReactive}
  9879. */
  9880. #reactive;
  9881. /**
  9882. * Stores SvelteData entries with instantiated Svelte components.
  9883. *
  9884. * @type {SvelteData[]}
  9885. */
  9886. #svelteData = [];
  9887. /**
  9888. * Provides a helper class that combines multiple methods for interacting with the mounted components tracked in
  9889. * {@link SvelteData}.
  9890. *
  9891. * @type {GetSvelteData}
  9892. */
  9893. #getSvelteData = new GetSvelteData(this.#applicationShellHolder, this.#svelteData);
  9894. /**
  9895. * Contains methods to interact with the Svelte stores.
  9896. *
  9897. * @type {SvelteStores}
  9898. */
  9899. #stores;
  9900. /**
  9901. * @param {SvelteApplicationOptions} options - The options for the application.
  9902. *
  9903. * @inheritDoc
  9904. */
  9905. constructor(options = {}) {
  9906. super(options);
  9907. this.#applicationState = new ApplicationState(this);
  9908. this.#position = new Position(this, {
  9909. ...this.position,
  9910. ...this.options,
  9911. initial: this.options.positionInitial,
  9912. ortho: this.options.positionOrtho,
  9913. validator: this.options.positionValidator
  9914. });
  9915. delete this.position;
  9916. Object.defineProperty(this, "position", {
  9917. get: () => this.#position,
  9918. set: (position) => {
  9919. if (isObject(position)) {
  9920. this.#position.set(position);
  9921. }
  9922. }
  9923. });
  9924. this.#reactive = new SvelteReactive(this);
  9925. this.#stores = this.#reactive.initialize();
  9926. }
  9927. /**
  9928. * Specifies the default options that SvelteApplication supports.
  9929. *
  9930. * @returns {SvelteApplicationOptions} options - Application options.
  9931. * @see https://foundryvtt.com/api/interfaces/client.ApplicationOptions.html
  9932. */
  9933. static get defaultOptions() {
  9934. return deepMerge(super.defaultOptions, {
  9935. defaultCloseAnimation: true,
  9936. // If false the default slide close animation is not run.
  9937. draggable: true,
  9938. // If true then application shells are draggable.
  9939. focusAuto: true,
  9940. // When true auto-management of app focus is enabled.
  9941. focusKeep: false,
  9942. // When `focusAuto` and `focusKeep` is true; keeps internal focus.
  9943. focusSource: void 0,
  9944. // Stores any A11yFocusSource data that is applied when app is closed.
  9945. focusTrap: true,
  9946. // When true focus trapping / wrapping is enabled keeping focus inside app.
  9947. headerButtonNoClose: false,
  9948. // If true then the close header button is removed.
  9949. headerButtonNoLabel: false,
  9950. // If true then header button labels are removed for application shells.
  9951. headerIcon: void 0,
  9952. // Sets a header icon given an image URL.
  9953. headerNoTitleMinimized: false,
  9954. // If true then header title is hidden when application is minimized.
  9955. minHeight: MIN_WINDOW_HEIGHT,
  9956. // Assigned to position. Number specifying minimum window height.
  9957. minWidth: MIN_WINDOW_WIDTH,
  9958. // Assigned to position. Number specifying minimum window width.
  9959. positionable: true,
  9960. // If false then `position.set` does not take effect.
  9961. positionInitial: Position.Initial.browserCentered,
  9962. // A helper for initial position placement.
  9963. positionOrtho: true,
  9964. // When true Position is optimized for orthographic use.
  9965. positionValidator: Position.Validators.transformWindow,
  9966. // A function providing the default validator.
  9967. sessionStorage: void 0,
  9968. // An instance of SessionStorage to share across SvelteApplications.
  9969. svelte: void 0,
  9970. // A Svelte configuration object.
  9971. transformOrigin: "top left"
  9972. // By default, 'top / left' respects rotation when minimizing.
  9973. });
  9974. }
  9975. /**
  9976. * Returns the content element if an application shell is mounted.
  9977. *
  9978. * @returns {HTMLElement} Content element.
  9979. */
  9980. get elementContent() {
  9981. return this.#elementContent;
  9982. }
  9983. /**
  9984. * Returns the target element or main element if no target defined.
  9985. *
  9986. * @returns {HTMLElement} Target element.
  9987. */
  9988. get elementTarget() {
  9989. return this.#elementTarget;
  9990. }
  9991. /**
  9992. * Returns the reactive accessors & Svelte stores for SvelteApplication.
  9993. *
  9994. * @returns {SvelteReactive} The reactive accessors & Svelte stores.
  9995. */
  9996. get reactive() {
  9997. return this.#reactive;
  9998. }
  9999. /**
  10000. * Returns the application state manager.
  10001. *
  10002. * @returns {ApplicationState} The application state manager.
  10003. */
  10004. get state() {
  10005. return this.#applicationState;
  10006. }
  10007. /**
  10008. * Returns the Svelte helper class w/ various methods to access mounted Svelte components.
  10009. *
  10010. * @returns {GetSvelteData} GetSvelteData
  10011. */
  10012. get svelte() {
  10013. return this.#getSvelteData;
  10014. }
  10015. /**
  10016. * In this case of when a template is defined in app options `html` references the inner HTML / template. However,
  10017. * to activate classic v1 tabs for a Svelte component the element target is passed as an array simulating JQuery as
  10018. * the element is retrieved immediately and the core listeners use standard DOM queries.
  10019. *
  10020. * @inheritDoc
  10021. * @protected
  10022. * @ignore
  10023. */
  10024. _activateCoreListeners(html) {
  10025. super._activateCoreListeners(typeof this.options.template === "string" ? html : [this.#elementTarget]);
  10026. }
  10027. /**
  10028. * Provide an override to set this application as the active window regardless of z-index. Changes behaviour from
  10029. * Foundry core. This is important / used for instance in dialog key handling for left / right button selection.
  10030. *
  10031. * @param {object} [opts] - Optional parameters.
  10032. *
  10033. * @param {boolean} [opts.force=false] - Force bring to top; will increment z-index by popOut order.
  10034. *
  10035. */
  10036. bringToTop({ force = false } = {}) {
  10037. if (force || this.popOut) {
  10038. super.bringToTop();
  10039. }
  10040. if (document.activeElement !== document.body && !this.elementTarget.contains(document.activeElement)) {
  10041. if (document.activeElement instanceof HTMLElement) {
  10042. document.activeElement.blur();
  10043. }
  10044. document.body.focus();
  10045. }
  10046. globalThis.ui.activeWindow = this;
  10047. }
  10048. /**
  10049. * Note: This method is fully overridden and duplicated as Svelte components need to be destroyed manually and the
  10050. * best visual result is to destroy them after the default slide up animation occurs, but before the element
  10051. * is removed from the DOM.
  10052. *
  10053. * If you destroy the Svelte components before the slide up animation the Svelte elements are removed immediately
  10054. * from the DOM. The purpose of overriding ensures the slide up animation is always completed before
  10055. * the Svelte components are destroyed and then the element is removed from the DOM.
  10056. *
  10057. * Close the application and un-register references to it within UI mappings.
  10058. * This function returns a Promise which resolves once the window closing animation concludes
  10059. *
  10060. * @param {object} [options] - Optional parameters.
  10061. *
  10062. * @param {boolean} [options.force] - Force close regardless of render state.
  10063. *
  10064. * @returns {Promise<void>} A Promise which resolves once the application is closed.
  10065. * @ignore
  10066. */
  10067. async close(options = {}) {
  10068. const states = Application.RENDER_STATES;
  10069. if (!options.force && ![states.RENDERED, states.ERROR].includes(this._state)) {
  10070. return;
  10071. }
  10072. this.#stores.unsubscribe();
  10073. this._state = states.CLOSING;
  10074. const el = this.#elementTarget;
  10075. if (!el) {
  10076. return this._state = states.CLOSED;
  10077. }
  10078. const content = el.querySelector(".window-content");
  10079. if (content) {
  10080. content.style.overflow = "hidden";
  10081. for (let cntr = content.children.length; --cntr >= 0; ) {
  10082. content.children[cntr].style.overflow = "hidden";
  10083. }
  10084. }
  10085. for (const cls of this.constructor._getInheritanceChain()) {
  10086. Hooks.call(`close${cls.name}`, this, el);
  10087. }
  10088. const animate = typeof this.options.defaultCloseAnimation === "boolean" ? this.options.defaultCloseAnimation : true;
  10089. if (animate) {
  10090. el.style.minHeight = "0";
  10091. const { paddingBottom, paddingTop } = globalThis.getComputedStyle(el);
  10092. await el.animate([
  10093. { maxHeight: `${el.clientHeight}px`, paddingTop, paddingBottom },
  10094. { maxHeight: 0, paddingTop: 0, paddingBottom: 0 }
  10095. ], { duration: 250, easing: "ease-in", fill: "forwards" }).finished;
  10096. }
  10097. const svelteDestroyPromises = [];
  10098. for (const entry of this.#svelteData) {
  10099. svelteDestroyPromises.push(outroAndDestroy(entry.component));
  10100. const eventbus = entry.config.eventbus;
  10101. if (isObject(eventbus) && typeof eventbus.off === "function") {
  10102. eventbus.off();
  10103. entry.config.eventbus = void 0;
  10104. }
  10105. }
  10106. await Promise.all(svelteDestroyPromises);
  10107. this.#svelteData.length = 0;
  10108. el.remove();
  10109. this.position.state.restore({
  10110. name: "#beforeMinimized",
  10111. properties: ["width", "height"],
  10112. silent: true,
  10113. remove: true
  10114. });
  10115. this.#applicationShellHolder[0] = null;
  10116. this._element = null;
  10117. this.#elementContent = null;
  10118. this.#elementTarget = null;
  10119. delete globalThis.ui.windows[this.appId];
  10120. this._minimized = false;
  10121. this._scrollPositions = null;
  10122. this._state = states.CLOSED;
  10123. this.#onMount = false;
  10124. this.#stores.uiOptionsUpdate((storeOptions) => deepMerge(storeOptions, { minimized: this._minimized }));
  10125. A11yHelper.applyFocusSource(this.options.focusSource);
  10126. delete this.options.focusSource;
  10127. }
  10128. /**
  10129. * Inject the Svelte components defined in `this.options.svelte`. The Svelte component can attach to the existing
  10130. * pop-out of Application or provide no template and render into a document fragment which is then attached to the
  10131. * DOM.
  10132. *
  10133. * @param {JQuery} html -
  10134. *
  10135. * @inheritDoc
  10136. * @ignore
  10137. */
  10138. _injectHTML(html) {
  10139. if (this.popOut && html.length === 0 && Array.isArray(this.options.svelte)) {
  10140. throw new Error(
  10141. "SvelteApplication - _injectHTML - A popout app with no template can only support one Svelte component."
  10142. );
  10143. }
  10144. this.reactive.updateHeaderButtons();
  10145. const elementRootUpdate = () => {
  10146. let cntr = 0;
  10147. return (elementRoot) => {
  10148. if (elementRoot !== null && elementRoot !== void 0 && cntr++ > 0) {
  10149. this.#updateApplicationShell();
  10150. return true;
  10151. }
  10152. return false;
  10153. };
  10154. };
  10155. if (Array.isArray(this.options.svelte)) {
  10156. for (const svelteConfig of this.options.svelte) {
  10157. const svelteData = loadSvelteConfig({
  10158. app: this,
  10159. template: html[0],
  10160. config: svelteConfig,
  10161. elementRootUpdate
  10162. });
  10163. if (isApplicationShell(svelteData.component)) {
  10164. if (this.svelte.applicationShell !== null) {
  10165. throw new Error(
  10166. `SvelteApplication - _injectHTML - An application shell is already mounted; offending config:
  10167. ${JSON.stringify(svelteConfig)}`
  10168. );
  10169. }
  10170. this.#applicationShellHolder[0] = svelteData.component;
  10171. if (isHMRProxy(svelteData.component) && Array.isArray(svelteData.component?.$$?.on_hmr)) {
  10172. svelteData.component.$$.on_hmr.push(() => () => this.#updateApplicationShell());
  10173. }
  10174. }
  10175. this.#svelteData.push(svelteData);
  10176. }
  10177. } else if (isObject(this.options.svelte)) {
  10178. const svelteData = loadSvelteConfig({
  10179. app: this,
  10180. template: html[0],
  10181. config: this.options.svelte,
  10182. elementRootUpdate
  10183. });
  10184. if (isApplicationShell(svelteData.component)) {
  10185. if (this.svelte.applicationShell !== null) {
  10186. throw new Error(
  10187. `SvelteApplication - _injectHTML - An application shell is already mounted; offending config:
  10188. ${JSON.stringify(this.options.svelte)}`
  10189. );
  10190. }
  10191. this.#applicationShellHolder[0] = svelteData.component;
  10192. if (isHMRProxy(svelteData.component) && Array.isArray(svelteData.component?.$$?.on_hmr)) {
  10193. svelteData.component.$$.on_hmr.push(() => () => this.#updateApplicationShell());
  10194. }
  10195. }
  10196. this.#svelteData.push(svelteData);
  10197. }
  10198. const isDocumentFragment = html.length && html[0] instanceof DocumentFragment;
  10199. let injectHTML = true;
  10200. for (const svelteData of this.#svelteData) {
  10201. if (!svelteData.injectHTML) {
  10202. injectHTML = false;
  10203. break;
  10204. }
  10205. }
  10206. if (injectHTML) {
  10207. super._injectHTML(html);
  10208. }
  10209. if (this.svelte.applicationShell !== null) {
  10210. this._element = $(this.svelte.applicationShell.elementRoot);
  10211. this.#elementContent = hasGetter(this.svelte.applicationShell, "elementContent") ? this.svelte.applicationShell.elementContent : null;
  10212. this.#elementTarget = hasGetter(this.svelte.applicationShell, "elementTarget") ? this.svelte.applicationShell.elementTarget : null;
  10213. } else if (isDocumentFragment) {
  10214. for (const svelteData of this.#svelteData) {
  10215. if (svelteData.element instanceof HTMLElement) {
  10216. this._element = $(svelteData.element);
  10217. break;
  10218. }
  10219. }
  10220. }
  10221. if (this.#elementTarget === null) {
  10222. this.#elementTarget = typeof this.options.selectorTarget === "string" ? this._element[0].querySelector(this.options.selectorTarget) : this._element[0];
  10223. }
  10224. if (this.#elementTarget === null || this.#elementTarget === void 0) {
  10225. throw new Error(`SvelteApplication - _injectHTML: Target element '${this.options.selectorTarget}' not found.`);
  10226. }
  10227. if (typeof this.options.positionable === "boolean" && this.options.positionable) {
  10228. this.#elementTarget.style.zIndex = typeof this.options.zIndex === "number" ? this.options.zIndex : this.#initialZIndex ?? 95;
  10229. }
  10230. this.#stores.subscribe();
  10231. }
  10232. /**
  10233. * Provides a mechanism to update the UI options store for maximized.
  10234. *
  10235. * Note: the sanity check is duplicated from {@link Application.maximize} the store is updated _before_
  10236. * performing the rest of animations. This allows application shells to remove / show any resize handlers
  10237. * correctly. Extra constraint data is stored in a saved position state in {@link SvelteApplication.minimize}
  10238. * to animate the content area.
  10239. *
  10240. * @param {object} [opts] - Optional parameters.
  10241. *
  10242. * @param {boolean} [opts.animate=true] - When true perform default maximizing animation.
  10243. *
  10244. * @param {number} [opts.duration=0.1] - Controls content area animation duration in seconds.
  10245. */
  10246. async maximize({ animate = true, duration = 0.1 } = {}) {
  10247. if (!this.popOut || [false, null].includes(this._minimized)) {
  10248. return;
  10249. }
  10250. this._minimized = null;
  10251. const durationMS = duration * 1e3;
  10252. const element2 = this.elementTarget;
  10253. const header = element2.querySelector(".window-header");
  10254. const content = element2.querySelector(".window-content");
  10255. const positionBefore = this.position.state.get({ name: "#beforeMinimized" });
  10256. if (animate) {
  10257. await this.position.state.restore({
  10258. name: "#beforeMinimized",
  10259. async: true,
  10260. animateTo: true,
  10261. properties: ["width"],
  10262. duration: 0.1
  10263. });
  10264. }
  10265. element2.classList.remove("minimized");
  10266. for (let cntr = header.children.length; --cntr >= 0; ) {
  10267. header.children[cntr].style.display = null;
  10268. }
  10269. content.style.display = null;
  10270. let constraints;
  10271. if (animate) {
  10272. ({ constraints } = this.position.state.restore({
  10273. name: "#beforeMinimized",
  10274. animateTo: true,
  10275. properties: ["height"],
  10276. remove: true,
  10277. duration
  10278. }));
  10279. } else {
  10280. ({ constraints } = this.position.state.remove({ name: "#beforeMinimized" }));
  10281. }
  10282. await content.animate([
  10283. { maxHeight: 0, paddingTop: 0, paddingBottom: 0, offset: 0 },
  10284. { ...constraints, offset: 1 },
  10285. { maxHeight: "100%", offset: 1 }
  10286. ], { duration: durationMS, fill: "forwards" }).finished;
  10287. this.position.set({
  10288. minHeight: positionBefore.minHeight ?? this.options?.minHeight ?? MIN_WINDOW_HEIGHT,
  10289. minWidth: positionBefore.minWidth ?? this.options?.minWidth ?? MIN_WINDOW_WIDTH
  10290. });
  10291. element2.style.minWidth = null;
  10292. element2.style.minHeight = null;
  10293. this._minimized = false;
  10294. setTimeout(() => {
  10295. content.style.overflow = null;
  10296. for (let cntr = content.children.length; --cntr >= 0; ) {
  10297. content.children[cntr].style.overflow = null;
  10298. }
  10299. }, 50);
  10300. this.#stores.uiOptionsUpdate((options) => deepMerge(options, { minimized: false }));
  10301. }
  10302. /**
  10303. * Provides a mechanism to update the UI options store for minimized.
  10304. *
  10305. * Note: the sanity check is duplicated from {@link Application.minimize} the store is updated _before_
  10306. * performing the rest of animations. This allows application shells to remove / show any resize handlers
  10307. * correctly. Extra constraint data is stored in a saved position state in {@link SvelteApplication.minimize}
  10308. * to animate the content area.
  10309. *
  10310. * @param {object} [opts] - Optional parameters
  10311. *
  10312. * @param {boolean} [opts.animate=true] - When true perform default minimizing animation.
  10313. *
  10314. * @param {number} [opts.duration=0.1] - Controls content area animation duration in seconds.
  10315. */
  10316. async minimize({ animate = true, duration = 0.1 } = {}) {
  10317. if (!this.rendered || !this.popOut || [true, null].includes(this._minimized)) {
  10318. return;
  10319. }
  10320. this.#stores.uiOptionsUpdate((options) => deepMerge(options, { minimized: true }));
  10321. this._minimized = null;
  10322. const durationMS = duration * 1e3;
  10323. const element2 = this.elementTarget;
  10324. const header = element2.querySelector(".window-header");
  10325. const content = element2.querySelector(".window-content");
  10326. const beforeMinWidth = this.position.minWidth;
  10327. const beforeMinHeight = this.position.minHeight;
  10328. this.position.set({ minWidth: 100, minHeight: 30 });
  10329. element2.style.minWidth = "100px";
  10330. element2.style.minHeight = "30px";
  10331. if (content) {
  10332. content.style.overflow = "hidden";
  10333. for (let cntr = content.children.length; --cntr >= 0; ) {
  10334. content.children[cntr].style.overflow = "hidden";
  10335. }
  10336. }
  10337. const { paddingBottom, paddingTop } = globalThis.getComputedStyle(content);
  10338. const constraints = {
  10339. maxHeight: `${content.clientHeight}px`,
  10340. paddingTop,
  10341. paddingBottom
  10342. };
  10343. if (animate) {
  10344. const animation2 = content.animate([
  10345. constraints,
  10346. { maxHeight: 0, paddingTop: 0, paddingBottom: 0 }
  10347. ], { duration: durationMS, fill: "forwards" });
  10348. animation2.finished.then(() => content.style.display = "none");
  10349. } else {
  10350. setTimeout(() => content.style.display = "none", durationMS);
  10351. }
  10352. const saved = this.position.state.save({ name: "#beforeMinimized", constraints });
  10353. saved.minWidth = beforeMinWidth;
  10354. saved.minHeight = beforeMinHeight;
  10355. const headerOffsetHeight = header.offsetHeight;
  10356. this.position.minHeight = headerOffsetHeight;
  10357. if (animate) {
  10358. await this.position.animate.to({ height: headerOffsetHeight }, { duration }).finished;
  10359. }
  10360. for (let cntr = header.children.length; --cntr >= 0; ) {
  10361. const className = header.children[cntr].className;
  10362. if (className.includes("window-title") || className.includes("close")) {
  10363. continue;
  10364. }
  10365. if (className.includes("keep-minimized")) {
  10366. header.children[cntr].style.display = "block";
  10367. continue;
  10368. }
  10369. header.children[cntr].style.display = "none";
  10370. }
  10371. if (animate) {
  10372. await this.position.animate.to({ width: MIN_WINDOW_WIDTH }, { duration: 0.1 }).finished;
  10373. }
  10374. element2.classList.add("minimized");
  10375. this._minimized = true;
  10376. }
  10377. /**
  10378. * Provides a callback after all Svelte components are initialized.
  10379. *
  10380. * @param {object} [opts] - Optional parameters.
  10381. *
  10382. * @param {HTMLElement} [opts.element] - HTMLElement container for main application element.
  10383. *
  10384. * @param {HTMLElement} [opts.elementContent] - HTMLElement container for content area of application shells.
  10385. *
  10386. * @param {HTMLElement} [opts.elementTarget] - HTMLElement container for main application target element.
  10387. */
  10388. onSvelteMount({ element: element2, elementContent, elementTarget } = {}) {
  10389. }
  10390. // eslint-disable-line no-unused-vars
  10391. /**
  10392. * Provides a callback after the main application shell is remounted. This may occur during HMR / hot module
  10393. * replacement or directly invoked from the `elementRootUpdate` callback passed to the application shell component
  10394. * context.
  10395. *
  10396. * @param {object} [opts] - Optional parameters.
  10397. *
  10398. * @param {HTMLElement} [opts.element] - HTMLElement container for main application element.
  10399. *
  10400. * @param {HTMLElement} [opts.elementContent] - HTMLElement container for content area of application shells.
  10401. *
  10402. * @param {HTMLElement} [opts.elementTarget] - HTMLElement container for main application target element.
  10403. */
  10404. onSvelteRemount({ element: element2, elementContent, elementTarget } = {}) {
  10405. }
  10406. // eslint-disable-line no-unused-vars
  10407. /**
  10408. * Override replacing HTML as Svelte components control the rendering process. Only potentially change the outer
  10409. * application frame / title for pop-out applications.
  10410. *
  10411. * @inheritDoc
  10412. * @ignore
  10413. */
  10414. _replaceHTML(element2, html) {
  10415. if (!element2.length) {
  10416. return;
  10417. }
  10418. this.reactive.updateHeaderButtons();
  10419. }
  10420. /**
  10421. * Provides an override verifying that a new Application being rendered for the first time doesn't have a
  10422. * corresponding DOM element already loaded. This is a check that only occurs when `this._state` is
  10423. * `Application.RENDER_STATES.NONE`. It is useful in particular when SvelteApplication has a static ID
  10424. * explicitly set in `this.options.id` and long intro / outro transitions are assigned. If a new application
  10425. * sharing this static ID attempts to open / render for the first time while an existing DOM element sharing
  10426. * this static ID exists then the initial render is cancelled below rather than crashing later in the render
  10427. * cycle {@link Position.set}.
  10428. *
  10429. * @inheritDoc
  10430. * @protected
  10431. * @ignore
  10432. */
  10433. async _render(force = false, options = {}) {
  10434. if (isObject(options?.focusSource)) {
  10435. this.options.focusSource = options.focusSource;
  10436. }
  10437. if (this._state === Application.RENDER_STATES.NONE && document.querySelector(`#${this.id}`) instanceof HTMLElement) {
  10438. console.warn(`SvelteApplication - _render: A DOM element already exists for CSS ID '${this.id}'. Cancelling initial render for new application with appId '${this.appId}'.`);
  10439. return;
  10440. }
  10441. await super._render(force, options);
  10442. if (!this.#onMount) {
  10443. this.onSvelteMount({ element: this._element[0], elementContent: this.#elementContent, elementTarget: this.#elementTarget });
  10444. this.#onMount = true;
  10445. }
  10446. }
  10447. /**
  10448. * Render the inner application content. Only render a template if one is defined otherwise provide an empty
  10449. * JQuery element per the core Foundry API.
  10450. *
  10451. * @param {object} data The data used to render the inner template
  10452. *
  10453. * @returns {Promise.<JQuery>} A promise resolving to the constructed jQuery object
  10454. *
  10455. * @protected
  10456. * @ignore
  10457. */
  10458. async _renderInner(data) {
  10459. const html = typeof this.template === "string" ? await renderTemplate(this.template, data) : document.createDocumentFragment();
  10460. return $(html);
  10461. }
  10462. /**
  10463. * Stores the initial z-index set in `_renderOuter` which is used in `_injectHTML` to set the target element
  10464. * z-index after the Svelte component is mounted.
  10465. *
  10466. * @returns {Promise<JQuery>} Outer frame / unused.
  10467. * @protected
  10468. * @ignore
  10469. */
  10470. async _renderOuter() {
  10471. const html = await super._renderOuter();
  10472. this.#initialZIndex = html[0].style.zIndex;
  10473. return html;
  10474. }
  10475. /**
  10476. * All calculation and updates of position are implemented in {@link Position.set}. This allows position to be fully
  10477. * reactive and in control of updating inline styles for the application.
  10478. *
  10479. * This method remains for backward compatibility with Foundry. If you have a custom override quite likely you need
  10480. * to update to using the {@link Position.validators} functionality.
  10481. *
  10482. * @param {PositionDataExtended} [position] - Position data.
  10483. *
  10484. * @returns {Position} The updated position object for the application containing the new values
  10485. */
  10486. setPosition(position) {
  10487. return this.position.set(position);
  10488. }
  10489. /**
  10490. * This method is invoked by the `elementRootUpdate` callback that is added to the external context passed to
  10491. * Svelte components. When invoked it updates the local element roots tracked by SvelteApplication.
  10492. *
  10493. * This method may also be invoked by HMR / hot module replacement via `svelte-hmr`.
  10494. */
  10495. #updateApplicationShell() {
  10496. const applicationShell = this.svelte.applicationShell;
  10497. if (applicationShell !== null) {
  10498. this._element = $(applicationShell.elementRoot);
  10499. this.#elementContent = hasGetter(applicationShell, "elementContent") ? applicationShell.elementContent : null;
  10500. this.#elementTarget = hasGetter(applicationShell, "elementTarget") ? applicationShell.elementTarget : null;
  10501. if (this.#elementTarget === null) {
  10502. this.#elementTarget = typeof this.options.selectorTarget === "string" ? this._element[0].querySelector(this.options.selectorTarget) : this._element[0];
  10503. }
  10504. if (typeof this.options.positionable === "boolean" && this.options.positionable) {
  10505. this.#elementTarget.style.zIndex = typeof this.options.zIndex === "number" ? this.options.zIndex : this.#initialZIndex ?? 95;
  10506. super.bringToTop();
  10507. this.position.set(this.position.get());
  10508. }
  10509. super._activateCoreListeners([this.#elementTarget]);
  10510. this.onSvelteRemount({ element: this._element[0], elementContent: this.#elementContent, elementTarget: this.#elementTarget });
  10511. }
  10512. }
  10513. }
  10514. const s_STYLE_KEY = "#__trl-root-styles";
  10515. const cssVariables = new StyleManager({ docKey: s_STYLE_KEY, version: 1 });
  10516. const TJSContainer_svelte_svelte_type_style_lang = "";
  10517. function resizeObserver(node, target) {
  10518. ResizeObserverManager.add(node, target);
  10519. return {
  10520. update: (newTarget) => {
  10521. ResizeObserverManager.remove(node, target);
  10522. target = newTarget;
  10523. ResizeObserverManager.add(node, target);
  10524. },
  10525. destroy: () => {
  10526. ResizeObserverManager.remove(node, target);
  10527. }
  10528. };
  10529. }
  10530. resizeObserver.updateCache = function(el) {
  10531. if (!(el instanceof HTMLElement)) {
  10532. throw new TypeError(`resizeObserverUpdate error: 'el' is not an HTMLElement.`);
  10533. }
  10534. const subscribers = s_MAP.get(el);
  10535. if (Array.isArray(subscribers)) {
  10536. const computed = globalThis.getComputedStyle(el);
  10537. const borderBottom = styleParsePixels(el.style.borderBottom) ?? styleParsePixels(computed.borderBottom) ?? 0;
  10538. const borderLeft = styleParsePixels(el.style.borderLeft) ?? styleParsePixels(computed.borderLeft) ?? 0;
  10539. const borderRight = styleParsePixels(el.style.borderRight) ?? styleParsePixels(computed.borderRight) ?? 0;
  10540. const borderTop = styleParsePixels(el.style.borderTop) ?? styleParsePixels(computed.borderTop) ?? 0;
  10541. const paddingBottom = styleParsePixels(el.style.paddingBottom) ?? styleParsePixels(computed.paddingBottom) ?? 0;
  10542. const paddingLeft = styleParsePixels(el.style.paddingLeft) ?? styleParsePixels(computed.paddingLeft) ?? 0;
  10543. const paddingRight = styleParsePixels(el.style.paddingRight) ?? styleParsePixels(computed.paddingRight) ?? 0;
  10544. const paddingTop = styleParsePixels(el.style.paddingTop) ?? styleParsePixels(computed.paddingTop) ?? 0;
  10545. const additionalWidth = borderLeft + borderRight + paddingLeft + paddingRight;
  10546. const additionalHeight = borderTop + borderBottom + paddingTop + paddingBottom;
  10547. for (const subscriber of subscribers) {
  10548. subscriber.styles.additionalWidth = additionalWidth;
  10549. subscriber.styles.additionalHeight = additionalHeight;
  10550. s_UPDATE_SUBSCRIBER(subscriber, subscriber.contentWidth, subscriber.contentHeight);
  10551. }
  10552. }
  10553. };
  10554. const s_MAP = /* @__PURE__ */ new Map();
  10555. class ResizeObserverManager {
  10556. /**
  10557. * Add an HTMLElement and ResizeObserverTarget instance for monitoring. Create cached style attributes for the
  10558. * given element include border & padding dimensions for offset width / height calculations.
  10559. *
  10560. * @param {HTMLElement} el - The element to observe.
  10561. *
  10562. * @param {ResizeObserverTarget} target - A target that contains one of several mechanisms for updating resize data.
  10563. */
  10564. static add(el, target) {
  10565. const updateType = s_GET_UPDATE_TYPE(target);
  10566. if (updateType === 0) {
  10567. throw new Error(`'target' does not match supported ResizeObserverManager update mechanisms.`);
  10568. }
  10569. const computed = globalThis.getComputedStyle(el);
  10570. const borderBottom = styleParsePixels(el.style.borderBottom) ?? styleParsePixels(computed.borderBottom) ?? 0;
  10571. const borderLeft = styleParsePixels(el.style.borderLeft) ?? styleParsePixels(computed.borderLeft) ?? 0;
  10572. const borderRight = styleParsePixels(el.style.borderRight) ?? styleParsePixels(computed.borderRight) ?? 0;
  10573. const borderTop = styleParsePixels(el.style.borderTop) ?? styleParsePixels(computed.borderTop) ?? 0;
  10574. const paddingBottom = styleParsePixels(el.style.paddingBottom) ?? styleParsePixels(computed.paddingBottom) ?? 0;
  10575. const paddingLeft = styleParsePixels(el.style.paddingLeft) ?? styleParsePixels(computed.paddingLeft) ?? 0;
  10576. const paddingRight = styleParsePixels(el.style.paddingRight) ?? styleParsePixels(computed.paddingRight) ?? 0;
  10577. const paddingTop = styleParsePixels(el.style.paddingTop) ?? styleParsePixels(computed.paddingTop) ?? 0;
  10578. const data = {
  10579. updateType,
  10580. target,
  10581. // Stores most recent contentRect.width and contentRect.height values from ResizeObserver.
  10582. contentWidth: 0,
  10583. contentHeight: 0,
  10584. // Convenience data for total border & padding for offset width & height calculations.
  10585. styles: {
  10586. additionalWidth: borderLeft + borderRight + paddingLeft + paddingRight,
  10587. additionalHeight: borderTop + borderBottom + paddingTop + paddingBottom
  10588. }
  10589. };
  10590. if (s_MAP.has(el)) {
  10591. const subscribers = s_MAP.get(el);
  10592. subscribers.push(data);
  10593. } else {
  10594. s_MAP.set(el, [data]);
  10595. }
  10596. s_RESIZE_OBSERVER.observe(el);
  10597. }
  10598. /**
  10599. * Removes all targets from monitoring when just an element is provided otherwise removes a specific target
  10600. * from the monitoring map. If no more targets remain then the element is removed from monitoring.
  10601. *
  10602. * @param {HTMLElement} el - Element to remove from monitoring.
  10603. *
  10604. * @param {ResizeObserverTarget} [target] - A specific target to remove from monitoring.
  10605. */
  10606. static remove(el, target = void 0) {
  10607. const subscribers = s_MAP.get(el);
  10608. if (Array.isArray(subscribers)) {
  10609. const index = subscribers.findIndex((entry) => entry.target === target);
  10610. if (index >= 0) {
  10611. s_UPDATE_SUBSCRIBER(subscribers[index], void 0, void 0);
  10612. subscribers.splice(index, 1);
  10613. }
  10614. if (subscribers.length === 0) {
  10615. s_MAP.delete(el);
  10616. s_RESIZE_OBSERVER.unobserve(el);
  10617. }
  10618. }
  10619. }
  10620. }
  10621. const s_UPDATE_TYPES = {
  10622. none: 0,
  10623. attribute: 1,
  10624. function: 2,
  10625. resizeObserved: 3,
  10626. setContentBounds: 4,
  10627. setDimension: 5,
  10628. storeObject: 6,
  10629. storesObject: 7
  10630. };
  10631. const s_RESIZE_OBSERVER = new ResizeObserver((entries) => {
  10632. for (const entry of entries) {
  10633. const subscribers = s_MAP.get(entry?.target);
  10634. if (Array.isArray(subscribers)) {
  10635. const contentWidth = entry.contentRect.width;
  10636. const contentHeight = entry.contentRect.height;
  10637. for (const subscriber of subscribers) {
  10638. s_UPDATE_SUBSCRIBER(subscriber, contentWidth, contentHeight);
  10639. }
  10640. }
  10641. }
  10642. });
  10643. function s_GET_UPDATE_TYPE(target) {
  10644. if (target?.resizeObserved instanceof Function) {
  10645. return s_UPDATE_TYPES.resizeObserved;
  10646. }
  10647. if (target?.setDimension instanceof Function) {
  10648. return s_UPDATE_TYPES.setDimension;
  10649. }
  10650. if (target?.setContentBounds instanceof Function) {
  10651. return s_UPDATE_TYPES.setContentBounds;
  10652. }
  10653. const targetType = typeof target;
  10654. if (targetType !== null && (targetType === "object" || targetType === "function")) {
  10655. if (isUpdatableStore(target.resizeObserved)) {
  10656. return s_UPDATE_TYPES.storeObject;
  10657. }
  10658. const stores = target?.stores;
  10659. if (isObject(stores) || typeof stores === "function") {
  10660. if (isUpdatableStore(stores.resizeObserved)) {
  10661. return s_UPDATE_TYPES.storesObject;
  10662. }
  10663. }
  10664. }
  10665. if (targetType !== null && targetType === "object") {
  10666. return s_UPDATE_TYPES.attribute;
  10667. }
  10668. if (targetType === "function") {
  10669. return s_UPDATE_TYPES.function;
  10670. }
  10671. return s_UPDATE_TYPES.none;
  10672. }
  10673. function s_UPDATE_SUBSCRIBER(subscriber, contentWidth, contentHeight) {
  10674. const styles = subscriber.styles;
  10675. subscriber.contentWidth = contentWidth;
  10676. subscriber.contentHeight = contentHeight;
  10677. const offsetWidth = Number.isFinite(contentWidth) ? contentWidth + styles.additionalWidth : void 0;
  10678. const offsetHeight = Number.isFinite(contentHeight) ? contentHeight + styles.additionalHeight : void 0;
  10679. const target = subscriber.target;
  10680. switch (subscriber.updateType) {
  10681. case s_UPDATE_TYPES.attribute:
  10682. target.contentWidth = contentWidth;
  10683. target.contentHeight = contentHeight;
  10684. target.offsetWidth = offsetWidth;
  10685. target.offsetHeight = offsetHeight;
  10686. break;
  10687. case s_UPDATE_TYPES.function:
  10688. target?.(offsetWidth, offsetHeight, contentWidth, contentHeight);
  10689. break;
  10690. case s_UPDATE_TYPES.resizeObserved:
  10691. target.resizeObserved?.(offsetWidth, offsetHeight, contentWidth, contentHeight);
  10692. break;
  10693. case s_UPDATE_TYPES.setContentBounds:
  10694. target.setContentBounds?.(contentWidth, contentHeight);
  10695. break;
  10696. case s_UPDATE_TYPES.setDimension:
  10697. target.setDimension?.(offsetWidth, offsetHeight);
  10698. break;
  10699. case s_UPDATE_TYPES.storeObject:
  10700. target.resizeObserved.update((object) => {
  10701. object.contentHeight = contentHeight;
  10702. object.contentWidth = contentWidth;
  10703. object.offsetHeight = offsetHeight;
  10704. object.offsetWidth = offsetWidth;
  10705. return object;
  10706. });
  10707. break;
  10708. case s_UPDATE_TYPES.storesObject:
  10709. target.stores.resizeObserved.update((object) => {
  10710. object.contentHeight = contentHeight;
  10711. object.contentWidth = contentWidth;
  10712. object.offsetHeight = offsetHeight;
  10713. object.offsetWidth = offsetWidth;
  10714. return object;
  10715. });
  10716. break;
  10717. }
  10718. }
  10719. function applyStyles(node, properties) {
  10720. function setProperties() {
  10721. if (typeof properties !== "object") {
  10722. return;
  10723. }
  10724. for (const prop of Object.keys(properties)) {
  10725. node.style.setProperty(`${prop}`, properties[prop]);
  10726. }
  10727. }
  10728. setProperties();
  10729. return {
  10730. update(newProperties) {
  10731. properties = newProperties;
  10732. setProperties();
  10733. }
  10734. };
  10735. }
  10736. function draggable(node, {
  10737. position,
  10738. active: active2 = true,
  10739. button = 0,
  10740. storeDragging = void 0,
  10741. ease = false,
  10742. easeOptions = { duration: 0.1, ease: cubicOut },
  10743. hasTargetClassList,
  10744. ignoreTargetClassList
  10745. }) {
  10746. if (hasTargetClassList !== void 0 && !isIterable(hasTargetClassList)) {
  10747. throw new TypeError(`'hasTargetClassList' is not iterable.`);
  10748. }
  10749. if (ignoreTargetClassList !== void 0 && !isIterable(ignoreTargetClassList)) {
  10750. throw new TypeError(`'ignoreTargetClassList' is not iterable.`);
  10751. }
  10752. let initialPosition = null;
  10753. let initialDragPoint = {};
  10754. let dragging = false;
  10755. let quickTo = position.animate.quickTo(["top", "left"], easeOptions);
  10756. const handlers = {
  10757. dragDown: ["pointerdown", (e) => onDragPointerDown(e), false],
  10758. dragMove: ["pointermove", (e) => onDragPointerChange(e), false],
  10759. dragUp: ["pointerup", (e) => onDragPointerUp(e), false]
  10760. };
  10761. function activateListeners() {
  10762. node.addEventListener(...handlers.dragDown);
  10763. node.classList.add("draggable");
  10764. }
  10765. function removeListeners() {
  10766. if (typeof storeDragging?.set === "function") {
  10767. storeDragging.set(false);
  10768. }
  10769. node.removeEventListener(...handlers.dragDown);
  10770. node.removeEventListener(...handlers.dragMove);
  10771. node.removeEventListener(...handlers.dragUp);
  10772. node.classList.remove("draggable");
  10773. }
  10774. if (active2) {
  10775. activateListeners();
  10776. }
  10777. function onDragPointerDown(event) {
  10778. if (event.button !== button || !event.isPrimary) {
  10779. return;
  10780. }
  10781. if (!position.enabled) {
  10782. return;
  10783. }
  10784. if (ignoreTargetClassList !== void 0 && event.target instanceof HTMLElement) {
  10785. for (const targetClass of ignoreTargetClassList) {
  10786. if (event.target.classList.contains(targetClass)) {
  10787. return;
  10788. }
  10789. }
  10790. }
  10791. if (hasTargetClassList !== void 0 && event.target instanceof HTMLElement) {
  10792. let foundTarget = false;
  10793. for (const targetClass of hasTargetClassList) {
  10794. if (event.target.classList.contains(targetClass)) {
  10795. foundTarget = true;
  10796. break;
  10797. }
  10798. }
  10799. if (!foundTarget) {
  10800. return;
  10801. }
  10802. }
  10803. event.preventDefault();
  10804. dragging = false;
  10805. initialPosition = position.get();
  10806. initialDragPoint = { x: event.clientX, y: event.clientY };
  10807. node.addEventListener(...handlers.dragMove);
  10808. node.addEventListener(...handlers.dragUp);
  10809. node.setPointerCapture(event.pointerId);
  10810. }
  10811. function onDragPointerChange(event) {
  10812. if ((event.buttons & 1) === 0) {
  10813. onDragPointerUp(event);
  10814. return;
  10815. }
  10816. if (event.button !== -1 || !event.isPrimary) {
  10817. return;
  10818. }
  10819. event.preventDefault();
  10820. if (!dragging && typeof storeDragging?.set === "function") {
  10821. dragging = true;
  10822. storeDragging.set(true);
  10823. }
  10824. const newLeft = initialPosition.left + (event.clientX - initialDragPoint.x);
  10825. const newTop = initialPosition.top + (event.clientY - initialDragPoint.y);
  10826. if (ease) {
  10827. quickTo(newTop, newLeft);
  10828. } else {
  10829. s_POSITION_DATA.left = newLeft;
  10830. s_POSITION_DATA.top = newTop;
  10831. position.set(s_POSITION_DATA);
  10832. }
  10833. }
  10834. function onDragPointerUp(event) {
  10835. event.preventDefault();
  10836. dragging = false;
  10837. if (typeof storeDragging?.set === "function") {
  10838. storeDragging.set(false);
  10839. }
  10840. node.removeEventListener(...handlers.dragMove);
  10841. node.removeEventListener(...handlers.dragUp);
  10842. }
  10843. return {
  10844. // The default of active being true won't automatically add listeners twice.
  10845. update: (options) => {
  10846. if (typeof options.active === "boolean") {
  10847. active2 = options.active;
  10848. if (active2) {
  10849. activateListeners();
  10850. } else {
  10851. removeListeners();
  10852. }
  10853. }
  10854. if (typeof options.button === "number") {
  10855. button = options.button;
  10856. }
  10857. if (options.position !== void 0 && options.position !== position) {
  10858. position = options.position;
  10859. quickTo = position.animate.quickTo(["top", "left"], easeOptions);
  10860. }
  10861. if (typeof options.ease === "boolean") {
  10862. ease = options.ease;
  10863. }
  10864. if (isObject(options.easeOptions)) {
  10865. easeOptions = options.easeOptions;
  10866. quickTo.options(easeOptions);
  10867. }
  10868. if (options.hasTargetClassList !== void 0) {
  10869. if (!isIterable(options.hasTargetClassList)) {
  10870. throw new TypeError(`'hasTargetClassList' is not iterable.`);
  10871. } else {
  10872. hasTargetClassList = options.hasTargetClassList;
  10873. }
  10874. }
  10875. if (options.ignoreTargetClassList !== void 0) {
  10876. if (!isIterable(options.ignoreTargetClassList)) {
  10877. throw new TypeError(`'ignoreTargetClassList' is not iterable.`);
  10878. } else {
  10879. ignoreTargetClassList = options.ignoreTargetClassList;
  10880. }
  10881. }
  10882. },
  10883. destroy: () => removeListeners()
  10884. };
  10885. }
  10886. class DraggableOptions {
  10887. #ease = false;
  10888. #easeOptions = { duration: 0.1, ease: cubicOut };
  10889. /**
  10890. * Stores the subscribers.
  10891. *
  10892. * @type {(function(DraggableOptions): void)[]}
  10893. */
  10894. #subscriptions = [];
  10895. constructor({ ease, easeOptions } = {}) {
  10896. Object.defineProperty(this, "ease", {
  10897. get: () => {
  10898. return this.#ease;
  10899. },
  10900. set: (newEase) => {
  10901. if (typeof newEase !== "boolean") {
  10902. throw new TypeError(`'ease' is not a boolean.`);
  10903. }
  10904. this.#ease = newEase;
  10905. this.#updateSubscribers();
  10906. },
  10907. enumerable: true
  10908. });
  10909. Object.defineProperty(this, "easeOptions", {
  10910. get: () => {
  10911. return this.#easeOptions;
  10912. },
  10913. set: (newEaseOptions) => {
  10914. if (newEaseOptions === null || typeof newEaseOptions !== "object") {
  10915. throw new TypeError(`'easeOptions' is not an object.`);
  10916. }
  10917. if (newEaseOptions.duration !== void 0) {
  10918. if (!Number.isFinite(newEaseOptions.duration)) {
  10919. throw new TypeError(`'easeOptions.duration' is not a finite number.`);
  10920. }
  10921. if (newEaseOptions.duration < 0) {
  10922. throw new Error(`'easeOptions.duration' is less than 0.`);
  10923. }
  10924. this.#easeOptions.duration = newEaseOptions.duration;
  10925. }
  10926. if (newEaseOptions.ease !== void 0) {
  10927. if (typeof newEaseOptions.ease !== "function" && typeof newEaseOptions.ease !== "string") {
  10928. throw new TypeError(`'easeOptions.ease' is not a function or string.`);
  10929. }
  10930. this.#easeOptions.ease = newEaseOptions.ease;
  10931. }
  10932. this.#updateSubscribers();
  10933. },
  10934. enumerable: true
  10935. });
  10936. if (ease !== void 0) {
  10937. this.ease = ease;
  10938. }
  10939. if (easeOptions !== void 0) {
  10940. this.easeOptions = easeOptions;
  10941. }
  10942. }
  10943. /**
  10944. * @returns {number} Get ease duration
  10945. */
  10946. get easeDuration() {
  10947. return this.#easeOptions.duration;
  10948. }
  10949. /**
  10950. * @returns {string|Function} Get easing function value.
  10951. */
  10952. get easeValue() {
  10953. return this.#easeOptions.ease;
  10954. }
  10955. /**
  10956. * @param {number} duration - Set ease duration.
  10957. */
  10958. set easeDuration(duration) {
  10959. if (!Number.isFinite(duration)) {
  10960. throw new TypeError(`'duration' is not a finite number.`);
  10961. }
  10962. if (duration < 0) {
  10963. throw new Error(`'duration' is less than 0.`);
  10964. }
  10965. this.#easeOptions.duration = duration;
  10966. this.#updateSubscribers();
  10967. }
  10968. /**
  10969. * @param {string|Function} value - Get easing function value.
  10970. */
  10971. set easeValue(value) {
  10972. if (typeof value !== "function" && typeof value !== "string") {
  10973. throw new TypeError(`'value' is not a function or string.`);
  10974. }
  10975. this.#easeOptions.ease = value;
  10976. this.#updateSubscribers();
  10977. }
  10978. /**
  10979. * Resets all options data to default values.
  10980. */
  10981. reset() {
  10982. this.#ease = false;
  10983. this.#easeOptions = { duration: 0.1, ease: cubicOut };
  10984. this.#updateSubscribers();
  10985. }
  10986. /**
  10987. * Resets easing options to default values.
  10988. */
  10989. resetEase() {
  10990. this.#easeOptions = { duration: 0.1, ease: cubicOut };
  10991. this.#updateSubscribers();
  10992. }
  10993. /**
  10994. *
  10995. * @param {function(DraggableOptions): void} handler - Callback function that is invoked on update / changes.
  10996. * Receives the DraggableOptions object / instance.
  10997. *
  10998. * @returns {(function(): void)} Unsubscribe function.
  10999. */
  11000. subscribe(handler) {
  11001. this.#subscriptions.push(handler);
  11002. handler(this);
  11003. return () => {
  11004. const index = this.#subscriptions.findIndex((sub) => sub === handler);
  11005. if (index >= 0) {
  11006. this.#subscriptions.splice(index, 1);
  11007. }
  11008. };
  11009. }
  11010. #updateSubscribers() {
  11011. const subscriptions = this.#subscriptions;
  11012. if (subscriptions.length > 0) {
  11013. for (let cntr = 0; cntr < subscriptions.length; cntr++) {
  11014. subscriptions[cntr](this);
  11015. }
  11016. }
  11017. }
  11018. }
  11019. draggable.options = (options) => new DraggableOptions(options);
  11020. const s_POSITION_DATA = { left: 0, top: 0 };
  11021. const s_DEFAULT_TRANSITION_OPTIONS = {};
  11022. const TJSGlassPane_svelte_svelte_type_style_lang = "";
  11023. class AppShellContextInternal {
  11024. /** @type {InternalAppStores} */
  11025. #stores;
  11026. constructor() {
  11027. this.#stores = {
  11028. elementContent: writable$1(void 0),
  11029. elementRoot: writable$1(void 0)
  11030. };
  11031. Object.freeze(this.#stores);
  11032. Object.seal(this);
  11033. }
  11034. /**
  11035. * @returns {InternalAppStores} The internal context stores for elementContent / elementRoot
  11036. */
  11037. get stores() {
  11038. return this.#stores;
  11039. }
  11040. }
  11041. function localize(stringId, data) {
  11042. const result = typeof data !== "object" ? globalThis.game.i18n.localize(stringId) : globalThis.game.i18n.format(stringId, data);
  11043. return result !== void 0 ? result : "";
  11044. }
  11045. const TJSHeaderButton_svelte_svelte_type_style_lang = "";
  11046. function create_if_block$a(ctx) {
  11047. let span;
  11048. let t;
  11049. return {
  11050. c() {
  11051. span = element("span");
  11052. t = text$1(
  11053. /*label*/
  11054. ctx[3]
  11055. );
  11056. attr(span, "class", "svelte-ese-166l8wd");
  11057. toggle_class(
  11058. span,
  11059. "has-icon",
  11060. /*icon*/
  11061. ctx[4] !== void 0
  11062. );
  11063. },
  11064. m(target, anchor) {
  11065. insert(target, span, anchor);
  11066. append(span, t);
  11067. },
  11068. p(ctx2, dirty) {
  11069. if (dirty & /*label*/
  11070. 8)
  11071. set_data(
  11072. t,
  11073. /*label*/
  11074. ctx2[3]
  11075. );
  11076. if (dirty & /*icon*/
  11077. 16) {
  11078. toggle_class(
  11079. span,
  11080. "has-icon",
  11081. /*icon*/
  11082. ctx2[4] !== void 0
  11083. );
  11084. }
  11085. },
  11086. d(detaching) {
  11087. if (detaching)
  11088. detach(span);
  11089. }
  11090. };
  11091. }
  11092. function create_fragment$p(ctx) {
  11093. let a;
  11094. let html_tag;
  11095. let html_anchor;
  11096. let a_class_value;
  11097. let applyStyles_action;
  11098. let mounted;
  11099. let dispose;
  11100. let if_block = (
  11101. /*label*/
  11102. ctx[3] && create_if_block$a(ctx)
  11103. );
  11104. return {
  11105. c() {
  11106. a = element("a");
  11107. html_tag = new HtmlTag(false);
  11108. html_anchor = empty();
  11109. if (if_block)
  11110. if_block.c();
  11111. html_tag.a = html_anchor;
  11112. attr(a, "class", a_class_value = "header-button " + /*button*/
  11113. ctx[0].class + " svelte-ese-166l8wd");
  11114. attr(
  11115. a,
  11116. "aria-label",
  11117. /*label*/
  11118. ctx[3]
  11119. );
  11120. attr(a, "tabindex", "0");
  11121. attr(a, "role", "button");
  11122. toggle_class(
  11123. a,
  11124. "keep-minimized",
  11125. /*keepMinimized*/
  11126. ctx[2]
  11127. );
  11128. },
  11129. m(target, anchor) {
  11130. insert(target, a, anchor);
  11131. html_tag.m(
  11132. /*icon*/
  11133. ctx[4],
  11134. a
  11135. );
  11136. append(a, html_anchor);
  11137. if (if_block)
  11138. if_block.m(a, null);
  11139. if (!mounted) {
  11140. dispose = [
  11141. listen(a, "click", stop_propagation(prevent_default(
  11142. /*onClick*/
  11143. ctx[5]
  11144. ))),
  11145. listen(a, "contextmenu", stop_propagation(prevent_default(
  11146. /*onContextMenu*/
  11147. ctx[6]
  11148. ))),
  11149. listen(
  11150. a,
  11151. "keydown",
  11152. /*onKeydown*/
  11153. ctx[7]
  11154. ),
  11155. listen(
  11156. a,
  11157. "keyup",
  11158. /*onKeyup*/
  11159. ctx[8]
  11160. ),
  11161. action_destroyer(applyStyles_action = applyStyles.call(
  11162. null,
  11163. a,
  11164. /*styles*/
  11165. ctx[1]
  11166. ))
  11167. ];
  11168. mounted = true;
  11169. }
  11170. },
  11171. p(ctx2, [dirty]) {
  11172. if (dirty & /*icon*/
  11173. 16)
  11174. html_tag.p(
  11175. /*icon*/
  11176. ctx2[4]
  11177. );
  11178. if (
  11179. /*label*/
  11180. ctx2[3]
  11181. ) {
  11182. if (if_block) {
  11183. if_block.p(ctx2, dirty);
  11184. } else {
  11185. if_block = create_if_block$a(ctx2);
  11186. if_block.c();
  11187. if_block.m(a, null);
  11188. }
  11189. } else if (if_block) {
  11190. if_block.d(1);
  11191. if_block = null;
  11192. }
  11193. if (dirty & /*button*/
  11194. 1 && a_class_value !== (a_class_value = "header-button " + /*button*/
  11195. ctx2[0].class + " svelte-ese-166l8wd")) {
  11196. attr(a, "class", a_class_value);
  11197. }
  11198. if (dirty & /*label*/
  11199. 8) {
  11200. attr(
  11201. a,
  11202. "aria-label",
  11203. /*label*/
  11204. ctx2[3]
  11205. );
  11206. }
  11207. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
  11208. 2)
  11209. applyStyles_action.update.call(
  11210. null,
  11211. /*styles*/
  11212. ctx2[1]
  11213. );
  11214. if (dirty & /*button, keepMinimized*/
  11215. 5) {
  11216. toggle_class(
  11217. a,
  11218. "keep-minimized",
  11219. /*keepMinimized*/
  11220. ctx2[2]
  11221. );
  11222. }
  11223. },
  11224. i: noop,
  11225. o: noop,
  11226. d(detaching) {
  11227. if (detaching)
  11228. detach(a);
  11229. if (if_block)
  11230. if_block.d();
  11231. mounted = false;
  11232. run_all(dispose);
  11233. }
  11234. };
  11235. }
  11236. const s_REGEX_HTML = /^\s*<.*>$/;
  11237. function instance$p($$self, $$props, $$invalidate) {
  11238. let title;
  11239. let icon;
  11240. let label;
  11241. let keepMinimized;
  11242. let keyCode;
  11243. let styles;
  11244. let { button = void 0 } = $$props;
  11245. function onClick(event) {
  11246. const invoke = button?.onPress ?? button?.onclick;
  11247. if (typeof invoke === "function") {
  11248. invoke.call(button, event);
  11249. $$invalidate(0, button);
  11250. }
  11251. }
  11252. function onContextMenu(event) {
  11253. const invoke = button?.onContextMenu;
  11254. if (typeof invoke === "function") {
  11255. invoke.call(button, event);
  11256. $$invalidate(0, button);
  11257. }
  11258. }
  11259. function onKeydown(event) {
  11260. if (event.code === keyCode) {
  11261. event.preventDefault();
  11262. event.stopPropagation();
  11263. }
  11264. }
  11265. function onKeyup(event) {
  11266. if (event.code === keyCode) {
  11267. const invoke = button.onPress ?? button.onclick;
  11268. if (typeof invoke === "function") {
  11269. invoke.call(button, event);
  11270. $$invalidate(0, button);
  11271. }
  11272. event.preventDefault();
  11273. event.stopPropagation();
  11274. }
  11275. }
  11276. $$self.$$set = ($$props2) => {
  11277. if ("button" in $$props2)
  11278. $$invalidate(0, button = $$props2.button);
  11279. };
  11280. $$self.$$.update = () => {
  11281. if ($$self.$$.dirty & /*button*/
  11282. 1) {
  11283. $$invalidate(9, title = isObject(button) && typeof button.title === "string" ? localize(button.title) : "");
  11284. }
  11285. if ($$self.$$.dirty & /*button, title*/
  11286. 513) {
  11287. $$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>`);
  11288. }
  11289. if ($$self.$$.dirty & /*button*/
  11290. 1) {
  11291. $$invalidate(3, label = isObject(button) && typeof button.label === "string" ? localize(button.label) : void 0);
  11292. }
  11293. if ($$self.$$.dirty & /*button*/
  11294. 1) {
  11295. $$invalidate(2, keepMinimized = isObject(button) && typeof button.keepMinimized === "boolean" ? button.keepMinimized : false);
  11296. }
  11297. if ($$self.$$.dirty & /*button*/
  11298. 1) {
  11299. keyCode = isObject(button) && typeof button.keyCode === "string" ? button.keyCode : "Enter";
  11300. }
  11301. if ($$self.$$.dirty & /*button*/
  11302. 1) {
  11303. $$invalidate(1, styles = isObject(button) && isObject(button.styles) ? button.styles : void 0);
  11304. }
  11305. };
  11306. return [
  11307. button,
  11308. styles,
  11309. keepMinimized,
  11310. label,
  11311. icon,
  11312. onClick,
  11313. onContextMenu,
  11314. onKeydown,
  11315. onKeyup,
  11316. title
  11317. ];
  11318. }
  11319. class TJSHeaderButton extends SvelteComponent {
  11320. constructor(options) {
  11321. super();
  11322. init(this, options, instance$p, create_fragment$p, safe_not_equal, { button: 0 });
  11323. }
  11324. get button() {
  11325. return this.$$.ctx[0];
  11326. }
  11327. set button(button) {
  11328. this.$$set({ button });
  11329. flush();
  11330. }
  11331. }
  11332. const TJSApplicationHeader_svelte_svelte_type_style_lang = "";
  11333. function get_each_context$7(ctx, list, i) {
  11334. const child_ctx = ctx.slice();
  11335. child_ctx[31] = list[i];
  11336. return child_ctx;
  11337. }
  11338. function get_each_context_1$2(ctx, list, i) {
  11339. const child_ctx = ctx.slice();
  11340. child_ctx[31] = list[i];
  11341. return child_ctx;
  11342. }
  11343. function create_if_block$9(ctx) {
  11344. let img;
  11345. let img_src_value;
  11346. return {
  11347. c() {
  11348. img = element("img");
  11349. attr(img, "class", "tjs-app-icon keep-minimized svelte-ese-1wviwl9");
  11350. if (!src_url_equal(img.src, img_src_value = /*$storeHeaderIcon*/
  11351. ctx[6]))
  11352. attr(img, "src", img_src_value);
  11353. attr(img, "alt", "icon");
  11354. },
  11355. m(target, anchor) {
  11356. insert(target, img, anchor);
  11357. },
  11358. p(ctx2, dirty) {
  11359. if (dirty[0] & /*$storeHeaderIcon*/
  11360. 64 && !src_url_equal(img.src, img_src_value = /*$storeHeaderIcon*/
  11361. ctx2[6])) {
  11362. attr(img, "src", img_src_value);
  11363. }
  11364. },
  11365. d(detaching) {
  11366. if (detaching)
  11367. detach(img);
  11368. }
  11369. };
  11370. }
  11371. function create_each_block_1$2(ctx) {
  11372. let switch_instance;
  11373. let switch_instance_anchor;
  11374. let current;
  11375. const switch_instance_spread_levels = [
  11376. /*button*/
  11377. ctx[31].props
  11378. ];
  11379. var switch_value = (
  11380. /*button*/
  11381. ctx[31].class
  11382. );
  11383. function switch_props(ctx2) {
  11384. let switch_instance_props = {};
  11385. for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
  11386. switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
  11387. }
  11388. return { props: switch_instance_props };
  11389. }
  11390. if (switch_value) {
  11391. switch_instance = construct_svelte_component(switch_value, switch_props());
  11392. }
  11393. return {
  11394. c() {
  11395. if (switch_instance)
  11396. create_component(switch_instance.$$.fragment);
  11397. switch_instance_anchor = empty();
  11398. },
  11399. m(target, anchor) {
  11400. if (switch_instance)
  11401. mount_component(switch_instance, target, anchor);
  11402. insert(target, switch_instance_anchor, anchor);
  11403. current = true;
  11404. },
  11405. p(ctx2, dirty) {
  11406. const switch_instance_changes = dirty[0] & /*buttonsLeft*/
  11407. 2 ? get_spread_update(switch_instance_spread_levels, [get_spread_object(
  11408. /*button*/
  11409. ctx2[31].props
  11410. )]) : {};
  11411. if (switch_value !== (switch_value = /*button*/
  11412. ctx2[31].class)) {
  11413. if (switch_instance) {
  11414. group_outros();
  11415. const old_component = switch_instance;
  11416. transition_out(old_component.$$.fragment, 1, 0, () => {
  11417. destroy_component(old_component, 1);
  11418. });
  11419. check_outros();
  11420. }
  11421. if (switch_value) {
  11422. switch_instance = construct_svelte_component(switch_value, switch_props());
  11423. create_component(switch_instance.$$.fragment);
  11424. transition_in(switch_instance.$$.fragment, 1);
  11425. mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
  11426. } else {
  11427. switch_instance = null;
  11428. }
  11429. } else if (switch_value) {
  11430. switch_instance.$set(switch_instance_changes);
  11431. }
  11432. },
  11433. i(local) {
  11434. if (current)
  11435. return;
  11436. if (switch_instance)
  11437. transition_in(switch_instance.$$.fragment, local);
  11438. current = true;
  11439. },
  11440. o(local) {
  11441. if (switch_instance)
  11442. transition_out(switch_instance.$$.fragment, local);
  11443. current = false;
  11444. },
  11445. d(detaching) {
  11446. if (detaching)
  11447. detach(switch_instance_anchor);
  11448. if (switch_instance)
  11449. destroy_component(switch_instance, detaching);
  11450. }
  11451. };
  11452. }
  11453. function create_each_block$7(ctx) {
  11454. let switch_instance;
  11455. let switch_instance_anchor;
  11456. let current;
  11457. const switch_instance_spread_levels = [
  11458. /*button*/
  11459. ctx[31].props
  11460. ];
  11461. var switch_value = (
  11462. /*button*/
  11463. ctx[31].class
  11464. );
  11465. function switch_props(ctx2) {
  11466. let switch_instance_props = {};
  11467. for (let i = 0; i < switch_instance_spread_levels.length; i += 1) {
  11468. switch_instance_props = assign(switch_instance_props, switch_instance_spread_levels[i]);
  11469. }
  11470. return { props: switch_instance_props };
  11471. }
  11472. if (switch_value) {
  11473. switch_instance = construct_svelte_component(switch_value, switch_props());
  11474. }
  11475. return {
  11476. c() {
  11477. if (switch_instance)
  11478. create_component(switch_instance.$$.fragment);
  11479. switch_instance_anchor = empty();
  11480. },
  11481. m(target, anchor) {
  11482. if (switch_instance)
  11483. mount_component(switch_instance, target, anchor);
  11484. insert(target, switch_instance_anchor, anchor);
  11485. current = true;
  11486. },
  11487. p(ctx2, dirty) {
  11488. const switch_instance_changes = dirty[0] & /*buttonsRight*/
  11489. 4 ? get_spread_update(switch_instance_spread_levels, [get_spread_object(
  11490. /*button*/
  11491. ctx2[31].props
  11492. )]) : {};
  11493. if (switch_value !== (switch_value = /*button*/
  11494. ctx2[31].class)) {
  11495. if (switch_instance) {
  11496. group_outros();
  11497. const old_component = switch_instance;
  11498. transition_out(old_component.$$.fragment, 1, 0, () => {
  11499. destroy_component(old_component, 1);
  11500. });
  11501. check_outros();
  11502. }
  11503. if (switch_value) {
  11504. switch_instance = construct_svelte_component(switch_value, switch_props());
  11505. create_component(switch_instance.$$.fragment);
  11506. transition_in(switch_instance.$$.fragment, 1);
  11507. mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
  11508. } else {
  11509. switch_instance = null;
  11510. }
  11511. } else if (switch_value) {
  11512. switch_instance.$set(switch_instance_changes);
  11513. }
  11514. },
  11515. i(local) {
  11516. if (current)
  11517. return;
  11518. if (switch_instance)
  11519. transition_in(switch_instance.$$.fragment, local);
  11520. current = true;
  11521. },
  11522. o(local) {
  11523. if (switch_instance)
  11524. transition_out(switch_instance.$$.fragment, local);
  11525. current = false;
  11526. },
  11527. d(detaching) {
  11528. if (detaching)
  11529. detach(switch_instance_anchor);
  11530. if (switch_instance)
  11531. destroy_component(switch_instance, detaching);
  11532. }
  11533. };
  11534. }
  11535. function create_key_block(ctx) {
  11536. let header;
  11537. let t0;
  11538. let h4;
  11539. let t1_value = localize(
  11540. /*$storeTitle*/
  11541. ctx[7]
  11542. ) + "";
  11543. let t1;
  11544. let t2;
  11545. let t3;
  11546. let span;
  11547. let t4;
  11548. let draggable_action;
  11549. let minimizable_action;
  11550. let current;
  11551. let mounted;
  11552. let dispose;
  11553. let if_block = typeof /*$storeHeaderIcon*/
  11554. ctx[6] === "string" && create_if_block$9(ctx);
  11555. let each_value_1 = (
  11556. /*buttonsLeft*/
  11557. ctx[1]
  11558. );
  11559. let each_blocks_1 = [];
  11560. for (let i = 0; i < each_value_1.length; i += 1) {
  11561. each_blocks_1[i] = create_each_block_1$2(get_each_context_1$2(ctx, each_value_1, i));
  11562. }
  11563. const out = (i) => transition_out(each_blocks_1[i], 1, 1, () => {
  11564. each_blocks_1[i] = null;
  11565. });
  11566. let each_value = (
  11567. /*buttonsRight*/
  11568. ctx[2]
  11569. );
  11570. let each_blocks = [];
  11571. for (let i = 0; i < each_value.length; i += 1) {
  11572. each_blocks[i] = create_each_block$7(get_each_context$7(ctx, each_value, i));
  11573. }
  11574. const out_1 = (i) => transition_out(each_blocks[i], 1, 1, () => {
  11575. each_blocks[i] = null;
  11576. });
  11577. return {
  11578. c() {
  11579. header = element("header");
  11580. if (if_block)
  11581. if_block.c();
  11582. t0 = space();
  11583. h4 = element("h4");
  11584. t1 = text$1(t1_value);
  11585. t2 = space();
  11586. for (let i = 0; i < each_blocks_1.length; i += 1) {
  11587. each_blocks_1[i].c();
  11588. }
  11589. t3 = space();
  11590. span = element("span");
  11591. t4 = space();
  11592. for (let i = 0; i < each_blocks.length; i += 1) {
  11593. each_blocks[i].c();
  11594. }
  11595. attr(h4, "class", "window-title svelte-ese-1wviwl9");
  11596. set_style(
  11597. h4,
  11598. "display",
  11599. /*displayHeaderTitle*/
  11600. ctx[4]
  11601. );
  11602. attr(span, "class", "tjs-window-header-spacer keep-minimized svelte-ese-1wviwl9");
  11603. attr(header, "class", "window-header flexrow svelte-ese-1wviwl9");
  11604. },
  11605. m(target, anchor) {
  11606. insert(target, header, anchor);
  11607. if (if_block)
  11608. if_block.m(header, null);
  11609. append(header, t0);
  11610. append(header, h4);
  11611. append(h4, t1);
  11612. append(header, t2);
  11613. for (let i = 0; i < each_blocks_1.length; i += 1) {
  11614. each_blocks_1[i].m(header, null);
  11615. }
  11616. append(header, t3);
  11617. append(header, span);
  11618. append(header, t4);
  11619. for (let i = 0; i < each_blocks.length; i += 1) {
  11620. each_blocks[i].m(header, null);
  11621. }
  11622. current = true;
  11623. if (!mounted) {
  11624. dispose = [
  11625. action_destroyer(draggable_action = /*draggable*/
  11626. ctx[0].call(
  11627. null,
  11628. header,
  11629. /*dragOptions*/
  11630. ctx[3]
  11631. )),
  11632. action_destroyer(minimizable_action = /*minimizable*/
  11633. ctx[18].call(
  11634. null,
  11635. header,
  11636. /*$storeMinimizable*/
  11637. ctx[5]
  11638. )),
  11639. listen(
  11640. header,
  11641. "pointerdown",
  11642. /*onPointerdown*/
  11643. ctx[19]
  11644. )
  11645. ];
  11646. mounted = true;
  11647. }
  11648. },
  11649. p(ctx2, dirty) {
  11650. if (typeof /*$storeHeaderIcon*/
  11651. ctx2[6] === "string") {
  11652. if (if_block) {
  11653. if_block.p(ctx2, dirty);
  11654. } else {
  11655. if_block = create_if_block$9(ctx2);
  11656. if_block.c();
  11657. if_block.m(header, t0);
  11658. }
  11659. } else if (if_block) {
  11660. if_block.d(1);
  11661. if_block = null;
  11662. }
  11663. if ((!current || dirty[0] & /*$storeTitle*/
  11664. 128) && t1_value !== (t1_value = localize(
  11665. /*$storeTitle*/
  11666. ctx2[7]
  11667. ) + ""))
  11668. set_data(t1, t1_value);
  11669. if (dirty[0] & /*displayHeaderTitle*/
  11670. 16) {
  11671. set_style(
  11672. h4,
  11673. "display",
  11674. /*displayHeaderTitle*/
  11675. ctx2[4]
  11676. );
  11677. }
  11678. if (dirty[0] & /*buttonsLeft*/
  11679. 2) {
  11680. each_value_1 = /*buttonsLeft*/
  11681. ctx2[1];
  11682. let i;
  11683. for (i = 0; i < each_value_1.length; i += 1) {
  11684. const child_ctx = get_each_context_1$2(ctx2, each_value_1, i);
  11685. if (each_blocks_1[i]) {
  11686. each_blocks_1[i].p(child_ctx, dirty);
  11687. transition_in(each_blocks_1[i], 1);
  11688. } else {
  11689. each_blocks_1[i] = create_each_block_1$2(child_ctx);
  11690. each_blocks_1[i].c();
  11691. transition_in(each_blocks_1[i], 1);
  11692. each_blocks_1[i].m(header, t3);
  11693. }
  11694. }
  11695. group_outros();
  11696. for (i = each_value_1.length; i < each_blocks_1.length; i += 1) {
  11697. out(i);
  11698. }
  11699. check_outros();
  11700. }
  11701. if (dirty[0] & /*buttonsRight*/
  11702. 4) {
  11703. each_value = /*buttonsRight*/
  11704. ctx2[2];
  11705. let i;
  11706. for (i = 0; i < each_value.length; i += 1) {
  11707. const child_ctx = get_each_context$7(ctx2, each_value, i);
  11708. if (each_blocks[i]) {
  11709. each_blocks[i].p(child_ctx, dirty);
  11710. transition_in(each_blocks[i], 1);
  11711. } else {
  11712. each_blocks[i] = create_each_block$7(child_ctx);
  11713. each_blocks[i].c();
  11714. transition_in(each_blocks[i], 1);
  11715. each_blocks[i].m(header, null);
  11716. }
  11717. }
  11718. group_outros();
  11719. for (i = each_value.length; i < each_blocks.length; i += 1) {
  11720. out_1(i);
  11721. }
  11722. check_outros();
  11723. }
  11724. if (draggable_action && is_function(draggable_action.update) && dirty[0] & /*dragOptions*/
  11725. 8)
  11726. draggable_action.update.call(
  11727. null,
  11728. /*dragOptions*/
  11729. ctx2[3]
  11730. );
  11731. if (minimizable_action && is_function(minimizable_action.update) && dirty[0] & /*$storeMinimizable*/
  11732. 32)
  11733. minimizable_action.update.call(
  11734. null,
  11735. /*$storeMinimizable*/
  11736. ctx2[5]
  11737. );
  11738. },
  11739. i(local) {
  11740. if (current)
  11741. return;
  11742. for (let i = 0; i < each_value_1.length; i += 1) {
  11743. transition_in(each_blocks_1[i]);
  11744. }
  11745. for (let i = 0; i < each_value.length; i += 1) {
  11746. transition_in(each_blocks[i]);
  11747. }
  11748. current = true;
  11749. },
  11750. o(local) {
  11751. each_blocks_1 = each_blocks_1.filter(Boolean);
  11752. for (let i = 0; i < each_blocks_1.length; i += 1) {
  11753. transition_out(each_blocks_1[i]);
  11754. }
  11755. each_blocks = each_blocks.filter(Boolean);
  11756. for (let i = 0; i < each_blocks.length; i += 1) {
  11757. transition_out(each_blocks[i]);
  11758. }
  11759. current = false;
  11760. },
  11761. d(detaching) {
  11762. if (detaching)
  11763. detach(header);
  11764. if (if_block)
  11765. if_block.d();
  11766. destroy_each(each_blocks_1, detaching);
  11767. destroy_each(each_blocks, detaching);
  11768. mounted = false;
  11769. run_all(dispose);
  11770. }
  11771. };
  11772. }
  11773. function create_fragment$o(ctx) {
  11774. let previous_key = (
  11775. /*draggable*/
  11776. ctx[0]
  11777. );
  11778. let key_block_anchor;
  11779. let current;
  11780. let key_block = create_key_block(ctx);
  11781. return {
  11782. c() {
  11783. key_block.c();
  11784. key_block_anchor = empty();
  11785. },
  11786. m(target, anchor) {
  11787. key_block.m(target, anchor);
  11788. insert(target, key_block_anchor, anchor);
  11789. current = true;
  11790. },
  11791. p(ctx2, dirty) {
  11792. if (dirty[0] & /*draggable*/
  11793. 1 && safe_not_equal(previous_key, previous_key = /*draggable*/
  11794. ctx2[0])) {
  11795. group_outros();
  11796. transition_out(key_block, 1, 1, noop);
  11797. check_outros();
  11798. key_block = create_key_block(ctx2);
  11799. key_block.c();
  11800. transition_in(key_block, 1);
  11801. key_block.m(key_block_anchor.parentNode, key_block_anchor);
  11802. } else {
  11803. key_block.p(ctx2, dirty);
  11804. }
  11805. },
  11806. i(local) {
  11807. if (current)
  11808. return;
  11809. transition_in(key_block);
  11810. current = true;
  11811. },
  11812. o(local) {
  11813. transition_out(key_block);
  11814. current = false;
  11815. },
  11816. d(detaching) {
  11817. if (detaching)
  11818. detach(key_block_anchor);
  11819. key_block.d(detaching);
  11820. }
  11821. };
  11822. }
  11823. function instance$o($$self, $$props, $$invalidate) {
  11824. let $focusKeep;
  11825. let $focusAuto;
  11826. let $elementRoot;
  11827. let $storeHeaderButtons;
  11828. let $storeMinimized;
  11829. let $storeHeaderNoTitleMinimized;
  11830. let $storeDraggable;
  11831. let $storeMinimizable;
  11832. let $storeHeaderIcon;
  11833. let $storeTitle;
  11834. let { draggable: draggable$1 = void 0 } = $$props;
  11835. let { draggableOptions = void 0 } = $$props;
  11836. const { application } = getContext("#external");
  11837. const { focusAuto, focusKeep } = application.reactive.storeAppOptions;
  11838. component_subscribe($$self, focusAuto, (value) => $$invalidate(26, $focusAuto = value));
  11839. component_subscribe($$self, focusKeep, (value) => $$invalidate(25, $focusKeep = value));
  11840. const { elementRoot } = getContext("#internal").stores;
  11841. component_subscribe($$self, elementRoot, (value) => $$invalidate(27, $elementRoot = value));
  11842. const storeTitle = application.reactive.storeAppOptions.title;
  11843. component_subscribe($$self, storeTitle, (value) => $$invalidate(7, $storeTitle = value));
  11844. const storeDraggable = application.reactive.storeAppOptions.draggable;
  11845. component_subscribe($$self, storeDraggable, (value) => $$invalidate(24, $storeDraggable = value));
  11846. const storeDragging = application.reactive.storeUIState.dragging;
  11847. const storeHeaderButtons = application.reactive.storeUIState.headerButtons;
  11848. component_subscribe($$self, storeHeaderButtons, (value) => $$invalidate(21, $storeHeaderButtons = value));
  11849. const storeHeaderIcon = application.reactive.storeAppOptions.headerIcon;
  11850. component_subscribe($$self, storeHeaderIcon, (value) => $$invalidate(6, $storeHeaderIcon = value));
  11851. const storeHeaderNoTitleMinimized = application.reactive.storeAppOptions.headerNoTitleMinimized;
  11852. component_subscribe($$self, storeHeaderNoTitleMinimized, (value) => $$invalidate(23, $storeHeaderNoTitleMinimized = value));
  11853. const storeMinimizable = application.reactive.storeAppOptions.minimizable;
  11854. component_subscribe($$self, storeMinimizable, (value) => $$invalidate(5, $storeMinimizable = value));
  11855. const storeMinimized = application.reactive.storeUIState.minimized;
  11856. component_subscribe($$self, storeMinimized, (value) => $$invalidate(22, $storeMinimized = value));
  11857. const s_DRAG_TARGET_CLASSLIST = Object.freeze(["tjs-app-icon", "tjs-window-header-spacer", "window-header", "window-title"]);
  11858. let dragOptions;
  11859. let displayHeaderTitle;
  11860. let buttonsLeft;
  11861. let buttonsRight;
  11862. function minimizable(node, booleanStore) {
  11863. const callback = (event) => {
  11864. if (event.target.classList.contains("window-title") || event.target.classList.contains("window-header") || event.target.classList.contains("keep-minimized")) {
  11865. application._onToggleMinimize(event);
  11866. }
  11867. };
  11868. function activateListeners() {
  11869. node.addEventListener("dblclick", callback);
  11870. }
  11871. function removeListeners() {
  11872. node.removeEventListener("dblclick", callback);
  11873. }
  11874. if (booleanStore) {
  11875. activateListeners();
  11876. }
  11877. return {
  11878. update: (booleanStore2) => {
  11879. if (booleanStore2) {
  11880. activateListeners();
  11881. } else {
  11882. removeListeners();
  11883. }
  11884. },
  11885. destroy: () => removeListeners()
  11886. };
  11887. }
  11888. function onPointerdown(event) {
  11889. const rootEl = $elementRoot;
  11890. if ($focusAuto && rootEl instanceof HTMLElement && rootEl?.isConnected) {
  11891. if ($focusKeep) {
  11892. const focusOutside = document.activeElement instanceof HTMLElement && !rootEl.contains(document.activeElement);
  11893. if (focusOutside) {
  11894. rootEl.focus();
  11895. } else {
  11896. event.preventDefault();
  11897. }
  11898. } else {
  11899. rootEl.focus();
  11900. }
  11901. }
  11902. }
  11903. $$self.$$set = ($$props2) => {
  11904. if ("draggable" in $$props2)
  11905. $$invalidate(0, draggable$1 = $$props2.draggable);
  11906. if ("draggableOptions" in $$props2)
  11907. $$invalidate(20, draggableOptions = $$props2.draggableOptions);
  11908. };
  11909. $$self.$$.update = () => {
  11910. if ($$self.$$.dirty[0] & /*draggable*/
  11911. 1) {
  11912. $$invalidate(0, draggable$1 = typeof draggable$1 === "function" ? draggable$1 : draggable);
  11913. }
  11914. if ($$self.$$.dirty[0] & /*draggableOptions, $storeDraggable*/
  11915. 17825792) {
  11916. $$invalidate(3, dragOptions = Object.assign(
  11917. {},
  11918. {
  11919. ease: true,
  11920. easeOptions: { duration: 0.08, ease: cubicOut }
  11921. },
  11922. isObject(draggableOptions) ? draggableOptions : {},
  11923. {
  11924. position: application.position,
  11925. active: $storeDraggable,
  11926. storeDragging,
  11927. hasTargetClassList: s_DRAG_TARGET_CLASSLIST
  11928. }
  11929. ));
  11930. }
  11931. if ($$self.$$.dirty[0] & /*$storeHeaderNoTitleMinimized, $storeMinimized*/
  11932. 12582912) {
  11933. $$invalidate(4, displayHeaderTitle = $storeHeaderNoTitleMinimized && $storeMinimized ? "none" : null);
  11934. }
  11935. if ($$self.$$.dirty[0] & /*$storeHeaderButtons, buttonsLeft, buttonsRight*/
  11936. 2097158) {
  11937. {
  11938. $$invalidate(1, buttonsLeft = []);
  11939. $$invalidate(2, buttonsRight = []);
  11940. for (const button of $storeHeaderButtons) {
  11941. const buttonsList = typeof button?.alignLeft === "boolean" && button?.alignLeft ? buttonsLeft : buttonsRight;
  11942. buttonsList.push(isSvelteComponent(button) ? { class: button, props: {} } : {
  11943. class: TJSHeaderButton,
  11944. props: { button }
  11945. });
  11946. }
  11947. }
  11948. }
  11949. };
  11950. return [
  11951. draggable$1,
  11952. buttonsLeft,
  11953. buttonsRight,
  11954. dragOptions,
  11955. displayHeaderTitle,
  11956. $storeMinimizable,
  11957. $storeHeaderIcon,
  11958. $storeTitle,
  11959. focusAuto,
  11960. focusKeep,
  11961. elementRoot,
  11962. storeTitle,
  11963. storeDraggable,
  11964. storeHeaderButtons,
  11965. storeHeaderIcon,
  11966. storeHeaderNoTitleMinimized,
  11967. storeMinimizable,
  11968. storeMinimized,
  11969. minimizable,
  11970. onPointerdown,
  11971. draggableOptions,
  11972. $storeHeaderButtons,
  11973. $storeMinimized,
  11974. $storeHeaderNoTitleMinimized,
  11975. $storeDraggable
  11976. ];
  11977. }
  11978. class TJSApplicationHeader extends SvelteComponent {
  11979. constructor(options) {
  11980. super();
  11981. init(this, options, instance$o, create_fragment$o, safe_not_equal, { draggable: 0, draggableOptions: 20 }, null, [-1, -1]);
  11982. }
  11983. }
  11984. const TJSFocusWrap_svelte_svelte_type_style_lang = "";
  11985. function create_fragment$n(ctx) {
  11986. let div;
  11987. let mounted;
  11988. let dispose;
  11989. return {
  11990. c() {
  11991. div = element("div");
  11992. attr(div, "class", "tjs-focus-wrap svelte-ese-kjcljd");
  11993. attr(div, "tabindex", "0");
  11994. },
  11995. m(target, anchor) {
  11996. insert(target, div, anchor);
  11997. ctx[4](div);
  11998. if (!mounted) {
  11999. dispose = listen(
  12000. div,
  12001. "focus",
  12002. /*onFocus*/
  12003. ctx[1]
  12004. );
  12005. mounted = true;
  12006. }
  12007. },
  12008. p: noop,
  12009. i: noop,
  12010. o: noop,
  12011. d(detaching) {
  12012. if (detaching)
  12013. detach(div);
  12014. ctx[4](null);
  12015. mounted = false;
  12016. dispose();
  12017. }
  12018. };
  12019. }
  12020. function instance$n($$self, $$props, $$invalidate) {
  12021. let { elementRoot = void 0 } = $$props;
  12022. let { enabled = true } = $$props;
  12023. let ignoreElements, wrapEl;
  12024. function onFocus() {
  12025. if (!enabled) {
  12026. return;
  12027. }
  12028. if (elementRoot instanceof HTMLElement) {
  12029. const firstFocusEl = A11yHelper.getFirstFocusableElement(elementRoot, ignoreElements);
  12030. if (firstFocusEl instanceof HTMLElement && firstFocusEl !== wrapEl) {
  12031. firstFocusEl.focus();
  12032. } else {
  12033. elementRoot.focus();
  12034. }
  12035. }
  12036. }
  12037. function div_binding($$value) {
  12038. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12039. wrapEl = $$value;
  12040. $$invalidate(0, wrapEl);
  12041. });
  12042. }
  12043. $$self.$$set = ($$props2) => {
  12044. if ("elementRoot" in $$props2)
  12045. $$invalidate(2, elementRoot = $$props2.elementRoot);
  12046. if ("enabled" in $$props2)
  12047. $$invalidate(3, enabled = $$props2.enabled);
  12048. };
  12049. $$self.$$.update = () => {
  12050. if ($$self.$$.dirty & /*wrapEl*/
  12051. 1) {
  12052. if (wrapEl) {
  12053. ignoreElements = /* @__PURE__ */ new Set([wrapEl]);
  12054. }
  12055. }
  12056. };
  12057. return [wrapEl, onFocus, elementRoot, enabled, div_binding];
  12058. }
  12059. class TJSFocusWrap extends SvelteComponent {
  12060. constructor(options) {
  12061. super();
  12062. init(this, options, instance$n, create_fragment$n, safe_not_equal, { elementRoot: 2, enabled: 3 });
  12063. }
  12064. }
  12065. function create_fragment$m(ctx) {
  12066. let div;
  12067. let resizable_action;
  12068. let mounted;
  12069. let dispose;
  12070. return {
  12071. c() {
  12072. div = element("div");
  12073. div.innerHTML = `<i class="fas fa-arrows-alt-h"></i>`;
  12074. attr(div, "class", "window-resizable-handle");
  12075. },
  12076. m(target, anchor) {
  12077. insert(target, div, anchor);
  12078. ctx[10](div);
  12079. if (!mounted) {
  12080. dispose = action_destroyer(resizable_action = /*resizable*/
  12081. ctx[6].call(null, div, {
  12082. active: (
  12083. /*$storeResizable*/
  12084. ctx[1]
  12085. ),
  12086. storeResizing: (
  12087. /*storeResizing*/
  12088. ctx[5]
  12089. )
  12090. }));
  12091. mounted = true;
  12092. }
  12093. },
  12094. p(ctx2, [dirty]) {
  12095. if (resizable_action && is_function(resizable_action.update) && dirty & /*$storeResizable*/
  12096. 2)
  12097. resizable_action.update.call(null, {
  12098. active: (
  12099. /*$storeResizable*/
  12100. ctx2[1]
  12101. ),
  12102. storeResizing: (
  12103. /*storeResizing*/
  12104. ctx2[5]
  12105. )
  12106. });
  12107. },
  12108. i: noop,
  12109. o: noop,
  12110. d(detaching) {
  12111. if (detaching)
  12112. detach(div);
  12113. ctx[10](null);
  12114. mounted = false;
  12115. dispose();
  12116. }
  12117. };
  12118. }
  12119. function instance$m($$self, $$props, $$invalidate) {
  12120. let $storeElementRoot;
  12121. let $storeMinimized;
  12122. let $storeResizable;
  12123. let { isResizable = false } = $$props;
  12124. const application = getContext("#external").application;
  12125. const storeElementRoot = getContext("storeElementRoot");
  12126. component_subscribe($$self, storeElementRoot, (value) => $$invalidate(8, $storeElementRoot = value));
  12127. const storeResizable = application.reactive.storeAppOptions.resizable;
  12128. component_subscribe($$self, storeResizable, (value) => $$invalidate(1, $storeResizable = value));
  12129. const storeMinimized = application.reactive.storeUIState.minimized;
  12130. component_subscribe($$self, storeMinimized, (value) => $$invalidate(9, $storeMinimized = value));
  12131. const storeResizing = application.reactive.storeUIState.resizing;
  12132. let elementResize;
  12133. function resizable(node, { active: active2 = true, storeResizing: storeResizing2 = void 0 } = {}) {
  12134. let position = null;
  12135. let initialPosition = {};
  12136. let resizing = false;
  12137. const handlers = {
  12138. resizeDown: ["pointerdown", (e) => onResizePointerDown(e), false],
  12139. resizeMove: ["pointermove", (e) => onResizePointerMove(e), false],
  12140. resizeUp: ["pointerup", (e) => onResizePointerUp(e), false]
  12141. };
  12142. function activateListeners() {
  12143. node.addEventListener(...handlers.resizeDown);
  12144. $$invalidate(7, isResizable = true);
  12145. node.style.display = "block";
  12146. }
  12147. function removeListeners() {
  12148. if (typeof storeResizing2?.set === "function") {
  12149. storeResizing2.set(false);
  12150. }
  12151. node.removeEventListener(...handlers.resizeDown);
  12152. node.removeEventListener(...handlers.resizeMove);
  12153. node.removeEventListener(...handlers.resizeUp);
  12154. node.style.display = "none";
  12155. $$invalidate(7, isResizable = false);
  12156. }
  12157. if (active2) {
  12158. activateListeners();
  12159. } else {
  12160. node.style.display = "none";
  12161. }
  12162. function onResizePointerDown(event) {
  12163. event.preventDefault();
  12164. resizing = false;
  12165. position = application.position.get();
  12166. if (position.height === "auto") {
  12167. position.height = $storeElementRoot.clientHeight;
  12168. }
  12169. if (position.width === "auto") {
  12170. position.width = $storeElementRoot.clientWidth;
  12171. }
  12172. initialPosition = { x: event.clientX, y: event.clientY };
  12173. node.addEventListener(...handlers.resizeMove);
  12174. node.addEventListener(...handlers.resizeUp);
  12175. node.setPointerCapture(event.pointerId);
  12176. }
  12177. function onResizePointerMove(event) {
  12178. event.preventDefault();
  12179. if (!resizing && typeof storeResizing2?.set === "function") {
  12180. resizing = true;
  12181. storeResizing2.set(true);
  12182. }
  12183. application.position.set({
  12184. width: position.width + (event.clientX - initialPosition.x),
  12185. height: position.height + (event.clientY - initialPosition.y)
  12186. });
  12187. }
  12188. function onResizePointerUp(event) {
  12189. resizing = false;
  12190. if (typeof storeResizing2?.set === "function") {
  12191. storeResizing2.set(false);
  12192. }
  12193. event.preventDefault();
  12194. node.removeEventListener(...handlers.resizeMove);
  12195. node.removeEventListener(...handlers.resizeUp);
  12196. application._onResize(event);
  12197. }
  12198. return {
  12199. update: ({ active: active3 }) => {
  12200. if (active3) {
  12201. activateListeners();
  12202. } else {
  12203. removeListeners();
  12204. }
  12205. },
  12206. destroy: () => removeListeners()
  12207. };
  12208. }
  12209. function div_binding($$value) {
  12210. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12211. elementResize = $$value;
  12212. $$invalidate(0, elementResize), $$invalidate(7, isResizable), $$invalidate(9, $storeMinimized), $$invalidate(8, $storeElementRoot);
  12213. });
  12214. }
  12215. $$self.$$set = ($$props2) => {
  12216. if ("isResizable" in $$props2)
  12217. $$invalidate(7, isResizable = $$props2.isResizable);
  12218. };
  12219. $$self.$$.update = () => {
  12220. if ($$self.$$.dirty & /*elementResize, isResizable, $storeMinimized, $storeElementRoot*/
  12221. 897) {
  12222. if (elementResize) {
  12223. $$invalidate(0, elementResize.style.display = isResizable && !$storeMinimized ? "block" : "none", elementResize);
  12224. const elementRoot = $storeElementRoot;
  12225. if (elementRoot) {
  12226. elementRoot.classList[isResizable ? "add" : "remove"]("resizable");
  12227. }
  12228. }
  12229. }
  12230. };
  12231. return [
  12232. elementResize,
  12233. $storeResizable,
  12234. storeElementRoot,
  12235. storeResizable,
  12236. storeMinimized,
  12237. storeResizing,
  12238. resizable,
  12239. isResizable,
  12240. $storeElementRoot,
  12241. $storeMinimized,
  12242. div_binding
  12243. ];
  12244. }
  12245. class ResizableHandle extends SvelteComponent {
  12246. constructor(options) {
  12247. super();
  12248. init(this, options, instance$m, create_fragment$m, safe_not_equal, { isResizable: 7 });
  12249. }
  12250. }
  12251. const ApplicationShell_svelte_svelte_type_style_lang = "";
  12252. function create_else_block$2(ctx) {
  12253. let div;
  12254. let tjsapplicationheader;
  12255. let t0;
  12256. let section;
  12257. let applyStyles_action;
  12258. let t1;
  12259. let resizablehandle;
  12260. let t2;
  12261. let tjsfocuswrap;
  12262. let div_id_value;
  12263. let div_class_value;
  12264. let div_data_appid_value;
  12265. let applyStyles_action_1;
  12266. let current;
  12267. let mounted;
  12268. let dispose;
  12269. tjsapplicationheader = new TJSApplicationHeader({
  12270. props: {
  12271. draggable: (
  12272. /*draggable*/
  12273. ctx[6]
  12274. ),
  12275. draggableOptions: (
  12276. /*draggableOptions*/
  12277. ctx[7]
  12278. )
  12279. }
  12280. });
  12281. const default_slot_template = (
  12282. /*#slots*/
  12283. ctx[36].default
  12284. );
  12285. const default_slot = create_slot(
  12286. default_slot_template,
  12287. ctx,
  12288. /*$$scope*/
  12289. ctx[35],
  12290. null
  12291. );
  12292. resizablehandle = new ResizableHandle({});
  12293. tjsfocuswrap = new TJSFocusWrap({
  12294. props: {
  12295. elementRoot: (
  12296. /*elementRoot*/
  12297. ctx[1]
  12298. ),
  12299. enabled: (
  12300. /*focusWrapEnabled*/
  12301. ctx[11]
  12302. )
  12303. }
  12304. });
  12305. return {
  12306. c() {
  12307. div = element("div");
  12308. create_component(tjsapplicationheader.$$.fragment);
  12309. t0 = space();
  12310. section = element("section");
  12311. if (default_slot)
  12312. default_slot.c();
  12313. t1 = space();
  12314. create_component(resizablehandle.$$.fragment);
  12315. t2 = space();
  12316. create_component(tjsfocuswrap.$$.fragment);
  12317. attr(section, "class", "window-content svelte-ese-oz81f7");
  12318. attr(section, "tabindex", "-1");
  12319. attr(div, "id", div_id_value = /*application*/
  12320. ctx[10].id);
  12321. attr(div, "class", div_class_value = "app window-app " + /*application*/
  12322. ctx[10].options.classes.join(" ") + " svelte-ese-oz81f7");
  12323. attr(div, "data-appid", div_data_appid_value = /*application*/
  12324. ctx[10].appId);
  12325. attr(div, "tabindex", "-1");
  12326. },
  12327. m(target, anchor) {
  12328. insert(target, div, anchor);
  12329. mount_component(tjsapplicationheader, div, null);
  12330. append(div, t0);
  12331. append(div, section);
  12332. if (default_slot) {
  12333. default_slot.m(section, null);
  12334. }
  12335. ctx[39](section);
  12336. append(div, t1);
  12337. mount_component(resizablehandle, div, null);
  12338. append(div, t2);
  12339. mount_component(tjsfocuswrap, div, null);
  12340. ctx[40](div);
  12341. current = true;
  12342. if (!mounted) {
  12343. dispose = [
  12344. listen(
  12345. section,
  12346. "pointerdown",
  12347. /*onPointerdownContent*/
  12348. ctx[21]
  12349. ),
  12350. action_destroyer(applyStyles_action = applyStyles.call(
  12351. null,
  12352. section,
  12353. /*stylesContent*/
  12354. ctx[9]
  12355. )),
  12356. action_destroyer(
  12357. /*contentResizeObserver*/
  12358. ctx[13].call(
  12359. null,
  12360. section,
  12361. /*resizeObservedContent*/
  12362. ctx[22]
  12363. )
  12364. ),
  12365. listen(div, "close:popup", stop_propagation(prevent_default(
  12366. /*onClosePopup*/
  12367. ctx[18]
  12368. ))),
  12369. listen(
  12370. div,
  12371. "keydown",
  12372. /*onKeydown*/
  12373. ctx[19],
  12374. true
  12375. ),
  12376. listen(
  12377. div,
  12378. "pointerdown",
  12379. /*onPointerdownApp*/
  12380. ctx[20]
  12381. ),
  12382. action_destroyer(applyStyles_action_1 = applyStyles.call(
  12383. null,
  12384. div,
  12385. /*stylesApp*/
  12386. ctx[8]
  12387. )),
  12388. action_destroyer(
  12389. /*appResizeObserver*/
  12390. ctx[12].call(
  12391. null,
  12392. div,
  12393. /*resizeObservedApp*/
  12394. ctx[23]
  12395. )
  12396. )
  12397. ];
  12398. mounted = true;
  12399. }
  12400. },
  12401. p(ctx2, dirty) {
  12402. const tjsapplicationheader_changes = {};
  12403. if (dirty[0] & /*draggable*/
  12404. 64)
  12405. tjsapplicationheader_changes.draggable = /*draggable*/
  12406. ctx2[6];
  12407. if (dirty[0] & /*draggableOptions*/
  12408. 128)
  12409. tjsapplicationheader_changes.draggableOptions = /*draggableOptions*/
  12410. ctx2[7];
  12411. tjsapplicationheader.$set(tjsapplicationheader_changes);
  12412. if (default_slot) {
  12413. if (default_slot.p && (!current || dirty[1] & /*$$scope*/
  12414. 16)) {
  12415. update_slot_base(
  12416. default_slot,
  12417. default_slot_template,
  12418. ctx2,
  12419. /*$$scope*/
  12420. ctx2[35],
  12421. !current ? get_all_dirty_from_scope(
  12422. /*$$scope*/
  12423. ctx2[35]
  12424. ) : get_slot_changes(
  12425. default_slot_template,
  12426. /*$$scope*/
  12427. ctx2[35],
  12428. dirty,
  12429. null
  12430. ),
  12431. null
  12432. );
  12433. }
  12434. }
  12435. if (applyStyles_action && is_function(applyStyles_action.update) && dirty[0] & /*stylesContent*/
  12436. 512)
  12437. applyStyles_action.update.call(
  12438. null,
  12439. /*stylesContent*/
  12440. ctx2[9]
  12441. );
  12442. const tjsfocuswrap_changes = {};
  12443. if (dirty[0] & /*elementRoot*/
  12444. 2)
  12445. tjsfocuswrap_changes.elementRoot = /*elementRoot*/
  12446. ctx2[1];
  12447. if (dirty[0] & /*focusWrapEnabled*/
  12448. 2048)
  12449. tjsfocuswrap_changes.enabled = /*focusWrapEnabled*/
  12450. ctx2[11];
  12451. tjsfocuswrap.$set(tjsfocuswrap_changes);
  12452. if (!current || dirty[0] & /*application*/
  12453. 1024 && div_id_value !== (div_id_value = /*application*/
  12454. ctx2[10].id)) {
  12455. attr(div, "id", div_id_value);
  12456. }
  12457. if (!current || dirty[0] & /*application*/
  12458. 1024 && div_class_value !== (div_class_value = "app window-app " + /*application*/
  12459. ctx2[10].options.classes.join(" ") + " svelte-ese-oz81f7")) {
  12460. attr(div, "class", div_class_value);
  12461. }
  12462. if (!current || dirty[0] & /*application*/
  12463. 1024 && div_data_appid_value !== (div_data_appid_value = /*application*/
  12464. ctx2[10].appId)) {
  12465. attr(div, "data-appid", div_data_appid_value);
  12466. }
  12467. if (applyStyles_action_1 && is_function(applyStyles_action_1.update) && dirty[0] & /*stylesApp*/
  12468. 256)
  12469. applyStyles_action_1.update.call(
  12470. null,
  12471. /*stylesApp*/
  12472. ctx2[8]
  12473. );
  12474. },
  12475. i(local) {
  12476. if (current)
  12477. return;
  12478. transition_in(tjsapplicationheader.$$.fragment, local);
  12479. transition_in(default_slot, local);
  12480. transition_in(resizablehandle.$$.fragment, local);
  12481. transition_in(tjsfocuswrap.$$.fragment, local);
  12482. current = true;
  12483. },
  12484. o(local) {
  12485. transition_out(tjsapplicationheader.$$.fragment, local);
  12486. transition_out(default_slot, local);
  12487. transition_out(resizablehandle.$$.fragment, local);
  12488. transition_out(tjsfocuswrap.$$.fragment, local);
  12489. current = false;
  12490. },
  12491. d(detaching) {
  12492. if (detaching)
  12493. detach(div);
  12494. destroy_component(tjsapplicationheader);
  12495. if (default_slot)
  12496. default_slot.d(detaching);
  12497. ctx[39](null);
  12498. destroy_component(resizablehandle);
  12499. destroy_component(tjsfocuswrap);
  12500. ctx[40](null);
  12501. mounted = false;
  12502. run_all(dispose);
  12503. }
  12504. };
  12505. }
  12506. function create_if_block$8(ctx) {
  12507. let div;
  12508. let tjsapplicationheader;
  12509. let t0;
  12510. let section;
  12511. let applyStyles_action;
  12512. let t1;
  12513. let resizablehandle;
  12514. let t2;
  12515. let tjsfocuswrap;
  12516. let div_id_value;
  12517. let div_class_value;
  12518. let div_data_appid_value;
  12519. let applyStyles_action_1;
  12520. let div_intro;
  12521. let div_outro;
  12522. let current;
  12523. let mounted;
  12524. let dispose;
  12525. tjsapplicationheader = new TJSApplicationHeader({
  12526. props: {
  12527. draggable: (
  12528. /*draggable*/
  12529. ctx[6]
  12530. ),
  12531. draggableOptions: (
  12532. /*draggableOptions*/
  12533. ctx[7]
  12534. )
  12535. }
  12536. });
  12537. const default_slot_template = (
  12538. /*#slots*/
  12539. ctx[36].default
  12540. );
  12541. const default_slot = create_slot(
  12542. default_slot_template,
  12543. ctx,
  12544. /*$$scope*/
  12545. ctx[35],
  12546. null
  12547. );
  12548. resizablehandle = new ResizableHandle({});
  12549. tjsfocuswrap = new TJSFocusWrap({
  12550. props: { elementRoot: (
  12551. /*elementRoot*/
  12552. ctx[1]
  12553. ) }
  12554. });
  12555. return {
  12556. c() {
  12557. div = element("div");
  12558. create_component(tjsapplicationheader.$$.fragment);
  12559. t0 = space();
  12560. section = element("section");
  12561. if (default_slot)
  12562. default_slot.c();
  12563. t1 = space();
  12564. create_component(resizablehandle.$$.fragment);
  12565. t2 = space();
  12566. create_component(tjsfocuswrap.$$.fragment);
  12567. attr(section, "class", "window-content svelte-ese-oz81f7");
  12568. attr(section, "tabindex", "-1");
  12569. attr(div, "id", div_id_value = /*application*/
  12570. ctx[10].id);
  12571. attr(div, "class", div_class_value = "app window-app " + /*application*/
  12572. ctx[10].options.classes.join(" ") + " svelte-ese-oz81f7");
  12573. attr(div, "data-appid", div_data_appid_value = /*application*/
  12574. ctx[10].appId);
  12575. attr(div, "tabindex", "-1");
  12576. },
  12577. m(target, anchor) {
  12578. insert(target, div, anchor);
  12579. mount_component(tjsapplicationheader, div, null);
  12580. append(div, t0);
  12581. append(div, section);
  12582. if (default_slot) {
  12583. default_slot.m(section, null);
  12584. }
  12585. ctx[37](section);
  12586. append(div, t1);
  12587. mount_component(resizablehandle, div, null);
  12588. append(div, t2);
  12589. mount_component(tjsfocuswrap, div, null);
  12590. ctx[38](div);
  12591. current = true;
  12592. if (!mounted) {
  12593. dispose = [
  12594. listen(
  12595. section,
  12596. "pointerdown",
  12597. /*onPointerdownContent*/
  12598. ctx[21]
  12599. ),
  12600. action_destroyer(applyStyles_action = applyStyles.call(
  12601. null,
  12602. section,
  12603. /*stylesContent*/
  12604. ctx[9]
  12605. )),
  12606. action_destroyer(
  12607. /*contentResizeObserver*/
  12608. ctx[13].call(
  12609. null,
  12610. section,
  12611. /*resizeObservedContent*/
  12612. ctx[22]
  12613. )
  12614. ),
  12615. listen(div, "close:popup", stop_propagation(prevent_default(
  12616. /*onClosePopup*/
  12617. ctx[18]
  12618. ))),
  12619. listen(
  12620. div,
  12621. "keydown",
  12622. /*onKeydown*/
  12623. ctx[19],
  12624. true
  12625. ),
  12626. listen(
  12627. div,
  12628. "pointerdown",
  12629. /*onPointerdownApp*/
  12630. ctx[20]
  12631. ),
  12632. action_destroyer(applyStyles_action_1 = applyStyles.call(
  12633. null,
  12634. div,
  12635. /*stylesApp*/
  12636. ctx[8]
  12637. )),
  12638. action_destroyer(
  12639. /*appResizeObserver*/
  12640. ctx[12].call(
  12641. null,
  12642. div,
  12643. /*resizeObservedApp*/
  12644. ctx[23]
  12645. )
  12646. )
  12647. ];
  12648. mounted = true;
  12649. }
  12650. },
  12651. p(new_ctx, dirty) {
  12652. ctx = new_ctx;
  12653. const tjsapplicationheader_changes = {};
  12654. if (dirty[0] & /*draggable*/
  12655. 64)
  12656. tjsapplicationheader_changes.draggable = /*draggable*/
  12657. ctx[6];
  12658. if (dirty[0] & /*draggableOptions*/
  12659. 128)
  12660. tjsapplicationheader_changes.draggableOptions = /*draggableOptions*/
  12661. ctx[7];
  12662. tjsapplicationheader.$set(tjsapplicationheader_changes);
  12663. if (default_slot) {
  12664. if (default_slot.p && (!current || dirty[1] & /*$$scope*/
  12665. 16)) {
  12666. update_slot_base(
  12667. default_slot,
  12668. default_slot_template,
  12669. ctx,
  12670. /*$$scope*/
  12671. ctx[35],
  12672. !current ? get_all_dirty_from_scope(
  12673. /*$$scope*/
  12674. ctx[35]
  12675. ) : get_slot_changes(
  12676. default_slot_template,
  12677. /*$$scope*/
  12678. ctx[35],
  12679. dirty,
  12680. null
  12681. ),
  12682. null
  12683. );
  12684. }
  12685. }
  12686. if (applyStyles_action && is_function(applyStyles_action.update) && dirty[0] & /*stylesContent*/
  12687. 512)
  12688. applyStyles_action.update.call(
  12689. null,
  12690. /*stylesContent*/
  12691. ctx[9]
  12692. );
  12693. const tjsfocuswrap_changes = {};
  12694. if (dirty[0] & /*elementRoot*/
  12695. 2)
  12696. tjsfocuswrap_changes.elementRoot = /*elementRoot*/
  12697. ctx[1];
  12698. tjsfocuswrap.$set(tjsfocuswrap_changes);
  12699. if (!current || dirty[0] & /*application*/
  12700. 1024 && div_id_value !== (div_id_value = /*application*/
  12701. ctx[10].id)) {
  12702. attr(div, "id", div_id_value);
  12703. }
  12704. if (!current || dirty[0] & /*application*/
  12705. 1024 && div_class_value !== (div_class_value = "app window-app " + /*application*/
  12706. ctx[10].options.classes.join(" ") + " svelte-ese-oz81f7")) {
  12707. attr(div, "class", div_class_value);
  12708. }
  12709. if (!current || dirty[0] & /*application*/
  12710. 1024 && div_data_appid_value !== (div_data_appid_value = /*application*/
  12711. ctx[10].appId)) {
  12712. attr(div, "data-appid", div_data_appid_value);
  12713. }
  12714. if (applyStyles_action_1 && is_function(applyStyles_action_1.update) && dirty[0] & /*stylesApp*/
  12715. 256)
  12716. applyStyles_action_1.update.call(
  12717. null,
  12718. /*stylesApp*/
  12719. ctx[8]
  12720. );
  12721. },
  12722. i(local) {
  12723. if (current)
  12724. return;
  12725. transition_in(tjsapplicationheader.$$.fragment, local);
  12726. transition_in(default_slot, local);
  12727. transition_in(resizablehandle.$$.fragment, local);
  12728. transition_in(tjsfocuswrap.$$.fragment, local);
  12729. add_render_callback(() => {
  12730. if (div_outro)
  12731. div_outro.end(1);
  12732. div_intro = create_in_transition(
  12733. div,
  12734. /*inTransition*/
  12735. ctx[2],
  12736. /*inTransitionOptions*/
  12737. ctx[4]
  12738. );
  12739. div_intro.start();
  12740. });
  12741. current = true;
  12742. },
  12743. o(local) {
  12744. transition_out(tjsapplicationheader.$$.fragment, local);
  12745. transition_out(default_slot, local);
  12746. transition_out(resizablehandle.$$.fragment, local);
  12747. transition_out(tjsfocuswrap.$$.fragment, local);
  12748. if (div_intro)
  12749. div_intro.invalidate();
  12750. div_outro = create_out_transition(
  12751. div,
  12752. /*outTransition*/
  12753. ctx[3],
  12754. /*outTransitionOptions*/
  12755. ctx[5]
  12756. );
  12757. current = false;
  12758. },
  12759. d(detaching) {
  12760. if (detaching)
  12761. detach(div);
  12762. destroy_component(tjsapplicationheader);
  12763. if (default_slot)
  12764. default_slot.d(detaching);
  12765. ctx[37](null);
  12766. destroy_component(resizablehandle);
  12767. destroy_component(tjsfocuswrap);
  12768. ctx[38](null);
  12769. if (detaching && div_outro)
  12770. div_outro.end();
  12771. mounted = false;
  12772. run_all(dispose);
  12773. }
  12774. };
  12775. }
  12776. function create_fragment$l(ctx) {
  12777. let current_block_type_index;
  12778. let if_block;
  12779. let if_block_anchor;
  12780. let current;
  12781. const if_block_creators = [create_if_block$8, create_else_block$2];
  12782. const if_blocks = [];
  12783. function select_block_type(ctx2, dirty) {
  12784. if (
  12785. /*inTransition*/
  12786. ctx2[2] || /*outTransition*/
  12787. ctx2[3]
  12788. )
  12789. return 0;
  12790. return 1;
  12791. }
  12792. current_block_type_index = select_block_type(ctx);
  12793. if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
  12794. return {
  12795. c() {
  12796. if_block.c();
  12797. if_block_anchor = empty();
  12798. },
  12799. m(target, anchor) {
  12800. if_blocks[current_block_type_index].m(target, anchor);
  12801. insert(target, if_block_anchor, anchor);
  12802. current = true;
  12803. },
  12804. p(ctx2, dirty) {
  12805. let previous_block_index = current_block_type_index;
  12806. current_block_type_index = select_block_type(ctx2);
  12807. if (current_block_type_index === previous_block_index) {
  12808. if_blocks[current_block_type_index].p(ctx2, dirty);
  12809. } else {
  12810. group_outros();
  12811. transition_out(if_blocks[previous_block_index], 1, 1, () => {
  12812. if_blocks[previous_block_index] = null;
  12813. });
  12814. check_outros();
  12815. if_block = if_blocks[current_block_type_index];
  12816. if (!if_block) {
  12817. if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
  12818. if_block.c();
  12819. } else {
  12820. if_block.p(ctx2, dirty);
  12821. }
  12822. transition_in(if_block, 1);
  12823. if_block.m(if_block_anchor.parentNode, if_block_anchor);
  12824. }
  12825. },
  12826. i(local) {
  12827. if (current)
  12828. return;
  12829. transition_in(if_block);
  12830. current = true;
  12831. },
  12832. o(local) {
  12833. transition_out(if_block);
  12834. current = false;
  12835. },
  12836. d(detaching) {
  12837. if_blocks[current_block_type_index].d(detaching);
  12838. if (detaching)
  12839. detach(if_block_anchor);
  12840. }
  12841. };
  12842. }
  12843. function instance$l($$self, $$props, $$invalidate) {
  12844. let $focusKeep;
  12845. let $focusAuto;
  12846. let $minimized;
  12847. let $focusTrap;
  12848. let { $$slots: slots = {}, $$scope } = $$props;
  12849. let { elementContent = void 0 } = $$props;
  12850. let { elementRoot = void 0 } = $$props;
  12851. let { draggable: draggable2 = void 0 } = $$props;
  12852. let { draggableOptions = void 0 } = $$props;
  12853. let { stylesApp = void 0 } = $$props;
  12854. let { stylesContent = void 0 } = $$props;
  12855. let { appOffsetHeight = false } = $$props;
  12856. let { appOffsetWidth = false } = $$props;
  12857. const appResizeObserver = !!appOffsetHeight || !!appOffsetWidth ? resizeObserver : () => null;
  12858. let { contentOffsetHeight = false } = $$props;
  12859. let { contentOffsetWidth = false } = $$props;
  12860. const contentResizeObserver = !!contentOffsetHeight || !!contentOffsetWidth ? resizeObserver : () => null;
  12861. const internal = new AppShellContextInternal();
  12862. const s_IGNORE_CLASSES = { ignoreClasses: ["tjs-focus-wrap"] };
  12863. setContext("#internal", internal);
  12864. const { application } = getContext("#external");
  12865. const { focusAuto, focusKeep, focusTrap } = application.reactive.storeAppOptions;
  12866. component_subscribe($$self, focusAuto, (value) => $$invalidate(32, $focusAuto = value));
  12867. component_subscribe($$self, focusKeep, (value) => $$invalidate(41, $focusKeep = value));
  12868. component_subscribe($$self, focusTrap, (value) => $$invalidate(34, $focusTrap = value));
  12869. const { minimized } = application.reactive.storeUIState;
  12870. component_subscribe($$self, minimized, (value) => $$invalidate(33, $minimized = value));
  12871. let focusWrapEnabled;
  12872. let { transition = void 0 } = $$props;
  12873. let { inTransition = void 0 } = $$props;
  12874. let { outTransition = void 0 } = $$props;
  12875. let { transitionOptions = void 0 } = $$props;
  12876. let { inTransitionOptions = s_DEFAULT_TRANSITION_OPTIONS } = $$props;
  12877. let { outTransitionOptions = s_DEFAULT_TRANSITION_OPTIONS } = $$props;
  12878. let oldTransition = void 0;
  12879. let oldTransitionOptions = void 0;
  12880. onMount(() => elementRoot.focus());
  12881. function onClosePopup(event) {
  12882. if (!$focusAuto) {
  12883. return;
  12884. }
  12885. const targetEl = event?.detail?.target;
  12886. if (!(targetEl instanceof HTMLElement)) {
  12887. return;
  12888. }
  12889. if (A11yHelper.isFocusable(targetEl)) {
  12890. return;
  12891. }
  12892. const elementRootContains = elementRoot.contains(targetEl);
  12893. if (targetEl === elementRoot) {
  12894. elementRoot.focus();
  12895. } else if (targetEl === elementContent) {
  12896. elementContent.focus();
  12897. } else if (elementRootContains) {
  12898. if (elementContent.contains(targetEl)) {
  12899. elementContent.focus();
  12900. } else {
  12901. elementRoot.focus();
  12902. }
  12903. }
  12904. }
  12905. function onKeydown(event) {
  12906. if (focusWrapEnabled && event.shiftKey && event.code === "Tab") {
  12907. const allFocusable = A11yHelper.getFocusableElements(elementRoot, s_IGNORE_CLASSES);
  12908. const firstFocusEl = allFocusable.length > 0 ? allFocusable[0] : void 0;
  12909. const lastFocusEl = allFocusable.length > 0 ? allFocusable[allFocusable.length - 1] : void 0;
  12910. if (elementRoot === document.activeElement || firstFocusEl === document.activeElement) {
  12911. if (lastFocusEl instanceof HTMLElement && firstFocusEl !== lastFocusEl) {
  12912. lastFocusEl.focus();
  12913. }
  12914. event.preventDefault();
  12915. event.stopPropagation();
  12916. }
  12917. }
  12918. if (typeof application?.options?.popOut === "boolean" && application.options.popOut && application !== globalThis.ui?.activeWindow) {
  12919. application.bringToTop.call(application);
  12920. }
  12921. }
  12922. function onPointerdownApp() {
  12923. if (typeof application?.options?.popOut === "boolean" && application.options.popOut && application !== globalThis.ui?.activeWindow) {
  12924. application.bringToTop.call(application);
  12925. }
  12926. }
  12927. function onPointerdownContent(event) {
  12928. const focusable = A11yHelper.isFocusable(event.target);
  12929. if (!focusable && $focusAuto) {
  12930. if ($focusKeep) {
  12931. const focusOutside = document.activeElement instanceof HTMLElement && !elementRoot.contains(document.activeElement);
  12932. if (focusOutside) {
  12933. elementContent.focus();
  12934. } else {
  12935. event.preventDefault();
  12936. }
  12937. } else {
  12938. elementContent.focus();
  12939. }
  12940. }
  12941. }
  12942. function resizeObservedContent(offsetWidth, offsetHeight) {
  12943. $$invalidate(27, contentOffsetWidth = offsetWidth);
  12944. $$invalidate(26, contentOffsetHeight = offsetHeight);
  12945. }
  12946. function resizeObservedApp(offsetWidth, offsetHeight, contentWidth, contentHeight) {
  12947. application.position.stores.resizeObserved.update((object) => {
  12948. object.contentWidth = contentWidth;
  12949. object.contentHeight = contentHeight;
  12950. object.offsetWidth = offsetWidth;
  12951. object.offsetHeight = offsetHeight;
  12952. return object;
  12953. });
  12954. $$invalidate(24, appOffsetHeight = offsetHeight);
  12955. $$invalidate(25, appOffsetWidth = offsetWidth);
  12956. }
  12957. function section_binding($$value) {
  12958. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12959. elementContent = $$value;
  12960. $$invalidate(0, elementContent);
  12961. });
  12962. }
  12963. function div_binding($$value) {
  12964. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12965. elementRoot = $$value;
  12966. $$invalidate(1, elementRoot);
  12967. });
  12968. }
  12969. function section_binding_1($$value) {
  12970. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12971. elementContent = $$value;
  12972. $$invalidate(0, elementContent);
  12973. });
  12974. }
  12975. function div_binding_1($$value) {
  12976. binding_callbacks[$$value ? "unshift" : "push"](() => {
  12977. elementRoot = $$value;
  12978. $$invalidate(1, elementRoot);
  12979. });
  12980. }
  12981. $$self.$$set = ($$props2) => {
  12982. if ("elementContent" in $$props2)
  12983. $$invalidate(0, elementContent = $$props2.elementContent);
  12984. if ("elementRoot" in $$props2)
  12985. $$invalidate(1, elementRoot = $$props2.elementRoot);
  12986. if ("draggable" in $$props2)
  12987. $$invalidate(6, draggable2 = $$props2.draggable);
  12988. if ("draggableOptions" in $$props2)
  12989. $$invalidate(7, draggableOptions = $$props2.draggableOptions);
  12990. if ("stylesApp" in $$props2)
  12991. $$invalidate(8, stylesApp = $$props2.stylesApp);
  12992. if ("stylesContent" in $$props2)
  12993. $$invalidate(9, stylesContent = $$props2.stylesContent);
  12994. if ("appOffsetHeight" in $$props2)
  12995. $$invalidate(24, appOffsetHeight = $$props2.appOffsetHeight);
  12996. if ("appOffsetWidth" in $$props2)
  12997. $$invalidate(25, appOffsetWidth = $$props2.appOffsetWidth);
  12998. if ("contentOffsetHeight" in $$props2)
  12999. $$invalidate(26, contentOffsetHeight = $$props2.contentOffsetHeight);
  13000. if ("contentOffsetWidth" in $$props2)
  13001. $$invalidate(27, contentOffsetWidth = $$props2.contentOffsetWidth);
  13002. if ("transition" in $$props2)
  13003. $$invalidate(28, transition = $$props2.transition);
  13004. if ("inTransition" in $$props2)
  13005. $$invalidate(2, inTransition = $$props2.inTransition);
  13006. if ("outTransition" in $$props2)
  13007. $$invalidate(3, outTransition = $$props2.outTransition);
  13008. if ("transitionOptions" in $$props2)
  13009. $$invalidate(29, transitionOptions = $$props2.transitionOptions);
  13010. if ("inTransitionOptions" in $$props2)
  13011. $$invalidate(4, inTransitionOptions = $$props2.inTransitionOptions);
  13012. if ("outTransitionOptions" in $$props2)
  13013. $$invalidate(5, outTransitionOptions = $$props2.outTransitionOptions);
  13014. if ("$$scope" in $$props2)
  13015. $$invalidate(35, $$scope = $$props2.$$scope);
  13016. };
  13017. $$self.$$.update = () => {
  13018. if ($$self.$$.dirty[0] & /*elementContent*/
  13019. 1) {
  13020. if (elementContent !== void 0 && elementContent !== null) {
  13021. getContext("#internal").stores.elementContent.set(elementContent);
  13022. }
  13023. }
  13024. if ($$self.$$.dirty[0] & /*elementRoot*/
  13025. 2) {
  13026. if (elementRoot !== void 0 && elementRoot !== null) {
  13027. getContext("#internal").stores.elementRoot.set(elementRoot);
  13028. }
  13029. }
  13030. if ($$self.$$.dirty[1] & /*$focusAuto, $focusTrap, $minimized*/
  13031. 14) {
  13032. $$invalidate(11, focusWrapEnabled = $focusAuto && $focusTrap && !$minimized);
  13033. }
  13034. if ($$self.$$.dirty[0] & /*oldTransition, transition*/
  13035. 1342177280) {
  13036. if (oldTransition !== transition) {
  13037. const newTransition = typeof transition === "function" ? transition : void 0;
  13038. $$invalidate(2, inTransition = newTransition);
  13039. $$invalidate(3, outTransition = newTransition);
  13040. $$invalidate(30, oldTransition = newTransition);
  13041. }
  13042. }
  13043. if ($$self.$$.dirty[0] & /*transitionOptions*/
  13044. 536870912 | $$self.$$.dirty[1] & /*oldTransitionOptions*/
  13045. 1) {
  13046. if (oldTransitionOptions !== transitionOptions) {
  13047. const newOptions = transitionOptions !== s_DEFAULT_TRANSITION_OPTIONS && isObject(transitionOptions) ? transitionOptions : s_DEFAULT_TRANSITION_OPTIONS;
  13048. $$invalidate(4, inTransitionOptions = newOptions);
  13049. $$invalidate(5, outTransitionOptions = newOptions);
  13050. $$invalidate(31, oldTransitionOptions = newOptions);
  13051. }
  13052. }
  13053. if ($$self.$$.dirty[0] & /*inTransition*/
  13054. 4) {
  13055. if (typeof inTransition !== "function") {
  13056. $$invalidate(2, inTransition = void 0);
  13057. }
  13058. }
  13059. if ($$self.$$.dirty[0] & /*outTransition, application*/
  13060. 1032) {
  13061. {
  13062. if (typeof outTransition !== "function") {
  13063. $$invalidate(3, outTransition = void 0);
  13064. }
  13065. if (application && typeof application?.options?.defaultCloseAnimation === "boolean") {
  13066. $$invalidate(10, application.options.defaultCloseAnimation = outTransition === void 0, application);
  13067. }
  13068. }
  13069. }
  13070. if ($$self.$$.dirty[0] & /*inTransitionOptions*/
  13071. 16) {
  13072. if (typeof inTransitionOptions !== "object") {
  13073. $$invalidate(4, inTransitionOptions = s_DEFAULT_TRANSITION_OPTIONS);
  13074. }
  13075. }
  13076. if ($$self.$$.dirty[0] & /*outTransitionOptions*/
  13077. 32) {
  13078. if (typeof outTransitionOptions !== "object") {
  13079. $$invalidate(5, outTransitionOptions = s_DEFAULT_TRANSITION_OPTIONS);
  13080. }
  13081. }
  13082. };
  13083. return [
  13084. elementContent,
  13085. elementRoot,
  13086. inTransition,
  13087. outTransition,
  13088. inTransitionOptions,
  13089. outTransitionOptions,
  13090. draggable2,
  13091. draggableOptions,
  13092. stylesApp,
  13093. stylesContent,
  13094. application,
  13095. focusWrapEnabled,
  13096. appResizeObserver,
  13097. contentResizeObserver,
  13098. focusAuto,
  13099. focusKeep,
  13100. focusTrap,
  13101. minimized,
  13102. onClosePopup,
  13103. onKeydown,
  13104. onPointerdownApp,
  13105. onPointerdownContent,
  13106. resizeObservedContent,
  13107. resizeObservedApp,
  13108. appOffsetHeight,
  13109. appOffsetWidth,
  13110. contentOffsetHeight,
  13111. contentOffsetWidth,
  13112. transition,
  13113. transitionOptions,
  13114. oldTransition,
  13115. oldTransitionOptions,
  13116. $focusAuto,
  13117. $minimized,
  13118. $focusTrap,
  13119. $$scope,
  13120. slots,
  13121. section_binding,
  13122. div_binding,
  13123. section_binding_1,
  13124. div_binding_1
  13125. ];
  13126. }
  13127. class ApplicationShell extends SvelteComponent {
  13128. constructor(options) {
  13129. super();
  13130. init(
  13131. this,
  13132. options,
  13133. instance$l,
  13134. create_fragment$l,
  13135. safe_not_equal,
  13136. {
  13137. elementContent: 0,
  13138. elementRoot: 1,
  13139. draggable: 6,
  13140. draggableOptions: 7,
  13141. stylesApp: 8,
  13142. stylesContent: 9,
  13143. appOffsetHeight: 24,
  13144. appOffsetWidth: 25,
  13145. contentOffsetHeight: 26,
  13146. contentOffsetWidth: 27,
  13147. transition: 28,
  13148. inTransition: 2,
  13149. outTransition: 3,
  13150. transitionOptions: 29,
  13151. inTransitionOptions: 4,
  13152. outTransitionOptions: 5
  13153. },
  13154. null,
  13155. [-1, -1]
  13156. );
  13157. }
  13158. get elementContent() {
  13159. return this.$$.ctx[0];
  13160. }
  13161. set elementContent(elementContent) {
  13162. this.$$set({ elementContent });
  13163. flush();
  13164. }
  13165. get elementRoot() {
  13166. return this.$$.ctx[1];
  13167. }
  13168. set elementRoot(elementRoot) {
  13169. this.$$set({ elementRoot });
  13170. flush();
  13171. }
  13172. get draggable() {
  13173. return this.$$.ctx[6];
  13174. }
  13175. set draggable(draggable2) {
  13176. this.$$set({ draggable: draggable2 });
  13177. flush();
  13178. }
  13179. get draggableOptions() {
  13180. return this.$$.ctx[7];
  13181. }
  13182. set draggableOptions(draggableOptions) {
  13183. this.$$set({ draggableOptions });
  13184. flush();
  13185. }
  13186. get stylesApp() {
  13187. return this.$$.ctx[8];
  13188. }
  13189. set stylesApp(stylesApp) {
  13190. this.$$set({ stylesApp });
  13191. flush();
  13192. }
  13193. get stylesContent() {
  13194. return this.$$.ctx[9];
  13195. }
  13196. set stylesContent(stylesContent) {
  13197. this.$$set({ stylesContent });
  13198. flush();
  13199. }
  13200. get appOffsetHeight() {
  13201. return this.$$.ctx[24];
  13202. }
  13203. set appOffsetHeight(appOffsetHeight) {
  13204. this.$$set({ appOffsetHeight });
  13205. flush();
  13206. }
  13207. get appOffsetWidth() {
  13208. return this.$$.ctx[25];
  13209. }
  13210. set appOffsetWidth(appOffsetWidth) {
  13211. this.$$set({ appOffsetWidth });
  13212. flush();
  13213. }
  13214. get contentOffsetHeight() {
  13215. return this.$$.ctx[26];
  13216. }
  13217. set contentOffsetHeight(contentOffsetHeight) {
  13218. this.$$set({ contentOffsetHeight });
  13219. flush();
  13220. }
  13221. get contentOffsetWidth() {
  13222. return this.$$.ctx[27];
  13223. }
  13224. set contentOffsetWidth(contentOffsetWidth) {
  13225. this.$$set({ contentOffsetWidth });
  13226. flush();
  13227. }
  13228. get transition() {
  13229. return this.$$.ctx[28];
  13230. }
  13231. set transition(transition) {
  13232. this.$$set({ transition });
  13233. flush();
  13234. }
  13235. get inTransition() {
  13236. return this.$$.ctx[2];
  13237. }
  13238. set inTransition(inTransition) {
  13239. this.$$set({ inTransition });
  13240. flush();
  13241. }
  13242. get outTransition() {
  13243. return this.$$.ctx[3];
  13244. }
  13245. set outTransition(outTransition) {
  13246. this.$$set({ outTransition });
  13247. flush();
  13248. }
  13249. get transitionOptions() {
  13250. return this.$$.ctx[29];
  13251. }
  13252. set transitionOptions(transitionOptions) {
  13253. this.$$set({ transitionOptions });
  13254. flush();
  13255. }
  13256. get inTransitionOptions() {
  13257. return this.$$.ctx[4];
  13258. }
  13259. set inTransitionOptions(inTransitionOptions) {
  13260. this.$$set({ inTransitionOptions });
  13261. flush();
  13262. }
  13263. get outTransitionOptions() {
  13264. return this.$$.ctx[5];
  13265. }
  13266. set outTransitionOptions(outTransitionOptions) {
  13267. this.$$set({ outTransitionOptions });
  13268. flush();
  13269. }
  13270. }
  13271. const EmptyApplicationShell_svelte_svelte_type_style_lang = "";
  13272. const TJSApplicationShell_svelte_svelte_type_style_lang = "";
  13273. const DialogContent_svelte_svelte_type_style_lang = "";
  13274. cssVariables.setProperties({
  13275. // Anchor text shadow / header buttons
  13276. "--tjs-default-text-shadow-focus-hover": "0 0 8px var(--color-shadow-primary)",
  13277. // TJSApplicationShell app background.
  13278. "--tjs-app-background": `url("${globalThis.foundry.utils.getRoute("/ui/denim075.png")}")`
  13279. }, false);
  13280. Hooks.on("PopOut:loading", (app) => {
  13281. if (app instanceof SvelteApplication) {
  13282. app.position.enabled = false;
  13283. }
  13284. });
  13285. Hooks.on("PopOut:popin", (app) => {
  13286. if (app instanceof SvelteApplication) {
  13287. app.position.enabled = true;
  13288. }
  13289. });
  13290. Hooks.on("PopOut:close", (app) => {
  13291. if (app instanceof SvelteApplication) {
  13292. app.position.enabled = true;
  13293. }
  13294. });
  13295. class loading_bar {
  13296. constructor() {
  13297. this.total = 0;
  13298. this.current = 0;
  13299. this.lastPct = 0;
  13300. }
  13301. init(context, total) {
  13302. this.context = context;
  13303. this.total = total;
  13304. this.current = 0;
  13305. this.lastPct = 0;
  13306. SceneNavigation.displayProgressBar({ label: this.context, pct: 1 });
  13307. }
  13308. incrementProgress() {
  13309. this.current += 1;
  13310. const pct = Math.round(this.current / this.total * 100);
  13311. if (pct !== this.lastPct) {
  13312. debug(`${pct}% loaded...`);
  13313. SceneNavigation.displayProgressBar({ label: this.context, pct });
  13314. }
  13315. this.lastPct = pct;
  13316. }
  13317. }
  13318. const LoadingBar = new loading_bar();
  13319. const SequencerFileCache = {
  13320. _videos: {},
  13321. _preloadedFiles: /* @__PURE__ */ new Set(),
  13322. _totalCacheSize: 0,
  13323. _validTypes: ["video/webm", "video/x-webm", "application/octet-stream"],
  13324. async loadVideo(inSrc) {
  13325. if (!this._videos[inSrc]) {
  13326. const blob = await fetch(inSrc, {
  13327. mode: "cors",
  13328. credentials: "same-origin"
  13329. }).then((r) => r.blob()).catch((err) => {
  13330. console.error(err);
  13331. });
  13332. if (this._validTypes.indexOf(blob?.type) === -1)
  13333. return false;
  13334. while (this._totalCacheSize + blob.size > 524288e3) {
  13335. const entries = Object.entries(this._videos);
  13336. entries.sort((a, b) => {
  13337. return b[1].lastUsed - a[1].lastUsed;
  13338. });
  13339. const [oldSrc] = entries[0];
  13340. this._preloadedFiles.delete(oldSrc);
  13341. this._totalCacheSize -= this._videos[oldSrc].blob.size;
  13342. delete this._videos[oldSrc];
  13343. }
  13344. this._totalCacheSize += blob.size;
  13345. this._preloadedFiles.add(inSrc);
  13346. this._videos[inSrc] = {
  13347. blob,
  13348. lastUsed: +new Date()
  13349. };
  13350. }
  13351. this._videos[inSrc].lastUsed = +new Date();
  13352. return this._videos[inSrc].blob;
  13353. },
  13354. srcExists(inSrc) {
  13355. if (this._preloadedFiles.has(inSrc)) {
  13356. return true;
  13357. }
  13358. return srcExists(inSrc);
  13359. },
  13360. async loadFile(inSrc, preload = false) {
  13361. if (inSrc.toLowerCase().endsWith(".webm")) {
  13362. let blob = await this.loadVideo(inSrc);
  13363. if (!blob)
  13364. return false;
  13365. this._preloadedFiles.add(inSrc);
  13366. if (preload)
  13367. return true;
  13368. return get_video_texture(blob);
  13369. } else if (AudioHelper.hasAudioExtension(inSrc)) {
  13370. try {
  13371. const audio2 = await AudioHelper.preloadSound(inSrc);
  13372. if (audio2) {
  13373. this._preloadedFiles.add(inSrc);
  13374. }
  13375. return audio2;
  13376. } catch (err) {
  13377. console.error(`Failed to load audio: ${inSrc}`);
  13378. return false;
  13379. }
  13380. }
  13381. const texture = await loadTexture(inSrc);
  13382. if (texture) {
  13383. this._preloadedFiles.add(inSrc);
  13384. }
  13385. return texture;
  13386. }
  13387. };
  13388. async function get_video_texture(inBlob) {
  13389. return new Promise(async (resolve) => {
  13390. const video = document.createElement("video");
  13391. video.preload = "auto";
  13392. video.crossOrigin = "anonymous";
  13393. video.controls = true;
  13394. video.autoplay = false;
  13395. video.autoload = true;
  13396. video.muted = true;
  13397. video.src = URL.createObjectURL(inBlob);
  13398. let canplay = true;
  13399. video.oncanplay = async () => {
  13400. if (!canplay)
  13401. return;
  13402. canplay = false;
  13403. video.height = video.videoHeight;
  13404. video.width = video.videoWidth;
  13405. const baseTexture = PIXI.BaseTexture.from(video, {
  13406. resourceOptions: { autoPlay: false }
  13407. });
  13408. if (game.settings.get(CONSTANTS.MODULE_NAME, "enable-fix-pixi")) {
  13409. baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
  13410. }
  13411. const texture = new PIXI.Texture(baseTexture);
  13412. resolve(texture);
  13413. };
  13414. video.onerror = () => {
  13415. URL.revokeObjectURL(video.src);
  13416. reject();
  13417. };
  13418. });
  13419. }
  13420. const flipBookTextureCache = {};
  13421. class SequencerFileBase {
  13422. static make(inData, inDBPath, inMetadata) {
  13423. const originalFile = inData?.file ?? inData;
  13424. const file = foundry.utils.duplicate(originalFile);
  13425. const isRangeFind = typeof file !== "string" && !Array.isArray(originalFile) ? Object.keys(originalFile).filter((key) => key.endsWith("ft")).length > 0 : false;
  13426. return isRangeFind ? new SequencerFileRangeFind(inData, inDBPath, inMetadata) : new SequencerFile(inData, inDBPath, inMetadata);
  13427. }
  13428. }
  13429. class SequencerFile extends SequencerFileBase {
  13430. rangeFind = false;
  13431. constructor(inData, inDBPath, inMetadata) {
  13432. super();
  13433. inData = foundry.utils.duplicate(inData);
  13434. inMetadata = foundry.utils.duplicate(inMetadata);
  13435. this.originalData = inData;
  13436. this.originalMetadata = inMetadata;
  13437. for (let [key, value] of Object.entries(inMetadata)) {
  13438. this[key] = value;
  13439. }
  13440. this.dbPath = inDBPath;
  13441. this.moduleName = inDBPath.split(".")[0];
  13442. this.originalFile = inData?.file ?? inData;
  13443. this.file = foundry.utils.duplicate(this.originalFile);
  13444. this.fileIndex = null;
  13445. this.fileTextureMap = Object.fromEntries(
  13446. this.getAllFiles().map((file) => {
  13447. return [file, false];
  13448. })
  13449. );
  13450. this.twister = false;
  13451. }
  13452. clone() {
  13453. return SequencerFile.make(
  13454. this.originalData,
  13455. this.dbPath,
  13456. this.originalMetadata
  13457. );
  13458. }
  13459. async validate() {
  13460. let isValid = true;
  13461. const directories = {};
  13462. const allFiles = this.getAllFiles();
  13463. for (const file of allFiles) {
  13464. let directory = file.split("/");
  13465. directory.pop();
  13466. directory = directory.join("/");
  13467. if (directories[directory] === void 0) {
  13468. directories[directory] = await getFiles(directory);
  13469. }
  13470. }
  13471. for (const file of allFiles) {
  13472. let directory = file.split("/");
  13473. directory.pop();
  13474. directory = directory.join("/");
  13475. if (directories[directory].indexOf(file) === -1) {
  13476. console.warn(
  13477. `"${this.dbPath}" has an incorrect file path, could not find file. Points to:
  13478. ${file}`
  13479. );
  13480. isValid = false;
  13481. }
  13482. }
  13483. return isValid;
  13484. }
  13485. getAllFiles() {
  13486. return [this.file].deepFlatten();
  13487. }
  13488. getFile() {
  13489. if (Array.isArray(this.file)) {
  13490. this.fileIndex = is_real_number(this.fileIndex) ? this.fileIndex : random_array_element(this.file, {
  13491. twister: this.twister,
  13492. index: true
  13493. });
  13494. return this.file[this.fileIndex];
  13495. }
  13496. return this.file;
  13497. }
  13498. getTimestamps() {
  13499. if (Array.isArray(this.originalMetadata?.timestamps)) {
  13500. return this.originalMetadata?.timestamps?.[this.fileIndex] ?? this.originalMetadata?.timestamps[0];
  13501. }
  13502. return this.originalMetadata?.timestamps;
  13503. }
  13504. getPreviewFile(entry) {
  13505. let parts = entry.split(".");
  13506. let files2 = this.getAllFiles();
  13507. if (Array.isArray(files2)) {
  13508. if (is_real_number(parts[parts.length - 1])) {
  13509. files2 = files2[parts[parts.length - 1]];
  13510. } else {
  13511. const index = Math.floor(interpolate(0, files2.length - 1, 0.5));
  13512. files2 = files2?.[index - 1] ?? files2[index];
  13513. }
  13514. }
  13515. return files2;
  13516. }
  13517. destroy() {
  13518. if (this.originalMetadata?.flipbook)
  13519. return;
  13520. for (let texture of Object.values(this.fileTextureMap)) {
  13521. if (!texture)
  13522. continue;
  13523. try {
  13524. texture?.baseTexture?.resource?.source?.removeAttribute("src");
  13525. } catch (err) {
  13526. }
  13527. try {
  13528. texture?.baseTexture?.resource?.source?.pause();
  13529. } catch (err) {
  13530. }
  13531. try {
  13532. texture?.baseTexture?.resource?.source?.remove();
  13533. } catch (err) {
  13534. }
  13535. try {
  13536. texture?.baseTexture?.resource?.source?.load();
  13537. } catch (err) {
  13538. }
  13539. texture.destroy();
  13540. }
  13541. }
  13542. async _getTexture(file) {
  13543. if (this.fileTextureMap[file])
  13544. return this.fileTextureMap[file];
  13545. this.fileTextureMap[file] = await SequencerFileCache.loadFile(file);
  13546. return this.fileTextureMap[file];
  13547. }
  13548. _adjustScaleForPadding(distance, width2) {
  13549. return distance / (width2 - (this.template ? this.template[1] + this.template[2] : 0));
  13550. }
  13551. _adjustAnchorForPadding(width2) {
  13552. return this.template ? this.template[1] / width2 : void 0;
  13553. }
  13554. async _getFlipBookSheet(filePath) {
  13555. if (!this.originalMetadata?.flipbook)
  13556. return false;
  13557. if (flipBookTextureCache[filePath]) {
  13558. return flipBookTextureCache[filePath];
  13559. }
  13560. flipBookTextureCache[filePath] = this.file.map((file) => {
  13561. return PIXI.Texture.from(file);
  13562. });
  13563. return flipBookTextureCache[filePath];
  13564. }
  13565. async getTexture(distance) {
  13566. const filePath = this.getFile();
  13567. const texture = await this._getTexture(this.getFile());
  13568. const sheet = await this._getFlipBookSheet(filePath);
  13569. return {
  13570. filePath,
  13571. texture,
  13572. sheet,
  13573. spriteScale: this._adjustScaleForPadding(distance, texture.width),
  13574. spriteAnchor: this._adjustAnchorForPadding(texture.width)
  13575. };
  13576. }
  13577. }
  13578. class SequencerFileRangeFind extends SequencerFile {
  13579. rangeFind = true;
  13580. constructor(...args) {
  13581. super(...args);
  13582. this._fileDistanceMap = false;
  13583. }
  13584. static get ftToDistanceMap() {
  13585. return {
  13586. "90ft": canvas.grid.size * 15,
  13587. "60ft": canvas.grid.size * 9,
  13588. "30ft": canvas.grid.size * 5,
  13589. "15ft": canvas.grid.size * 2,
  13590. "05ft": 0
  13591. };
  13592. }
  13593. get _gridSizeDiff() {
  13594. return canvas.grid.size / this.template[0];
  13595. }
  13596. getAllFiles() {
  13597. return Object.values(this.file).deepFlatten();
  13598. }
  13599. getFile(inFt) {
  13600. if (inFt && this.file[inFt]) {
  13601. if (Array.isArray(this.file[inFt])) {
  13602. const fileIndex = is_real_number(this.fileIndex) ? Math.min(this.file[inFt].length - 1, this.fileIndex) : random_array_element(this.file[inFt], {
  13603. twister: this.twister,
  13604. index: true
  13605. });
  13606. return this.file[inFt][fileIndex];
  13607. }
  13608. return this.file[inFt];
  13609. }
  13610. return this.file;
  13611. }
  13612. getPreviewFile(entry) {
  13613. let parts = entry.split(".");
  13614. const ft = parts.find(
  13615. (part) => Object.keys(SequencerFileRangeFind.ftToDistanceMap).indexOf(part) > -1
  13616. );
  13617. if (!ft) {
  13618. return super.getPreviewFile(entry);
  13619. }
  13620. const fileIndex = parts.slice(parts.indexOf(ft) + 1)?.[0];
  13621. if (is_real_number(Number(fileIndex))) {
  13622. this.fileIndex = Number(fileIndex);
  13623. }
  13624. return this.getFile(ft);
  13625. }
  13626. async getTexture(distance = 400) {
  13627. const { filePath, texture } = await this._getTextureForDistance(distance);
  13628. return {
  13629. filePath,
  13630. texture,
  13631. spriteScale: this._adjustScaleForPadding(distance, texture.width),
  13632. spriteAnchor: this._adjustAnchorForPadding(texture.width)
  13633. };
  13634. }
  13635. _getMatchingDistance(inEntry) {
  13636. return SequencerFileRangeFind.ftToDistanceMap[inEntry] / this._gridSizeDiff;
  13637. }
  13638. _rangeFind(inDistance) {
  13639. if (!this._fileDistanceMap) {
  13640. let distances = Object.keys(this.file).filter(
  13641. (entry) => Object.keys(SequencerFileRangeFind.ftToDistanceMap).indexOf(entry) > -1
  13642. ).map((ft) => {
  13643. return {
  13644. file: this.getFile(ft),
  13645. minDistance: this._getMatchingDistance(ft)
  13646. };
  13647. });
  13648. let uniqueDistances = [
  13649. ...new Set(distances.map((item) => item.minDistance))
  13650. ];
  13651. uniqueDistances.sort((a, b) => a - b);
  13652. let max = Math.max(...uniqueDistances);
  13653. let min = Math.min(...uniqueDistances);
  13654. this._fileDistanceMap = distances.map((entry) => {
  13655. entry.distances = {
  13656. min: entry.minDistance === min ? 0 : entry.minDistance,
  13657. max: entry.minDistance === max ? Infinity : uniqueDistances[uniqueDistances.indexOf(entry.minDistance) + 1]
  13658. };
  13659. return entry;
  13660. });
  13661. }
  13662. const possibleFiles = this._fileDistanceMap.filter((entry) => {
  13663. const relativeDistance = inDistance / this._gridSizeDiff;
  13664. return relativeDistance >= entry.distances.min && relativeDistance < entry.distances.max;
  13665. }).map((entry) => entry.file).flat();
  13666. return possibleFiles.length > 1 ? random_array_element(possibleFiles, { twister: this.twister }) : possibleFiles[0];
  13667. }
  13668. async _getTextureForDistance(distance) {
  13669. const filePath = this._rangeFind(distance);
  13670. const texture = await this._getTexture(filePath);
  13671. return { filePath, texture };
  13672. }
  13673. }
  13674. class Database {
  13675. #entriesStore = writable$1({});
  13676. privateModules = [];
  13677. flattenedEntries = [];
  13678. inverseFlattenedEntries = /* @__PURE__ */ new Map();
  13679. get entries() {
  13680. return get_store_value(this.#entriesStore);
  13681. }
  13682. set entries(entries) {
  13683. this.#entriesStore.set(entries);
  13684. }
  13685. get entriesStore() {
  13686. return this.#entriesStore;
  13687. }
  13688. get publicModules() {
  13689. return Object.keys(this.entries).filter(
  13690. (module2) => !this.privateModules.includes(module2)
  13691. );
  13692. }
  13693. get publicFlattenedEntries() {
  13694. return this.flattenedEntries.filter((entry) => {
  13695. return this.privateModules.indexOf(entry.split(".")[0]) === -1;
  13696. });
  13697. }
  13698. get publicFlattenedSimpleEntries() {
  13699. return make_array_unique(
  13700. this.publicFlattenedEntries.map((entry) => {
  13701. return entry.split(CONSTANTS.FEET_REGEX)[0];
  13702. })
  13703. );
  13704. }
  13705. /**
  13706. * Retrieves an object of every public entry
  13707. *
  13708. * @return {object}
  13709. */
  13710. get filePathDatabasePaths() {
  13711. const fileDatabaseObject = {};
  13712. Object.entries(this.entries).map((entry) => entry[1]).deepFlatten().forEach((sequencerFile) => {
  13713. if (sequencerFile?.rangeFind) {
  13714. Object.entries(sequencerFile.file).forEach((entry) => {
  13715. fileDatabaseObject[entry[1]] = sequencerFile.dbPath + "." + entry[0];
  13716. });
  13717. } else {
  13718. fileDatabaseObject[sequencerFile.file] = sequencerFile.dbPath;
  13719. }
  13720. });
  13721. return fileDatabaseObject;
  13722. }
  13723. /**
  13724. * Registers a set of entries to the database on the given module name
  13725. *
  13726. * @param {string} inModuleName The namespace to assign to the inserted entries
  13727. * @param {object} inEntries The entries to merge into the database
  13728. * @param {boolean} isPrivate Whether to mark these entries as private and not show in Effect Player or Database Viewer
  13729. * @return {boolean}
  13730. */
  13731. registerEntries(inModuleName, inEntries, isPrivate = false) {
  13732. if (inModuleName.includes("."))
  13733. return this._throwError(
  13734. "registerEntries",
  13735. "module name must not contain periods"
  13736. );
  13737. if (this.entries[inModuleName])
  13738. custom_warning(
  13739. "Sequencer",
  13740. `registerEntries | module "${inModuleName}" has already been registered to the database! Do you have two similar modules active?`,
  13741. true
  13742. );
  13743. this._flatten(inEntries, inModuleName);
  13744. const processedEntries = this._processEntries(inModuleName, inEntries);
  13745. if (isPrivate)
  13746. this.privateModules.push(inModuleName);
  13747. this.entries = foundry.utils.mergeObject(this.entries, {
  13748. [inModuleName]: processedEntries
  13749. });
  13750. debug(
  13751. `Sequencer | Database | Entries for "${inModuleName}" registered`
  13752. );
  13753. Hooks.callAll("registerSequencerDatabaseEntries", inModuleName);
  13754. return true;
  13755. }
  13756. /**
  13757. * Validates the entries under a certain module name, checking whether paths to assets are correct or not
  13758. *
  13759. * @param {string} inModuleName The namespace to assign to the inserted entries
  13760. * @return {boolean}
  13761. */
  13762. async validateEntries(inModuleName) {
  13763. let entries = this.getEntry(inModuleName);
  13764. if (!Array.isArray(entries)) {
  13765. entries = [entries];
  13766. }
  13767. ui.notifications.info(
  13768. `Validating paths registered to "${inModuleName}"...`
  13769. );
  13770. let isValid = true;
  13771. LoadingBar.init(
  13772. `Validating paths registered to "${inModuleName}"...`,
  13773. entries.length
  13774. );
  13775. for (let entry of entries) {
  13776. const result = await entry.validate();
  13777. LoadingBar.incrementProgress();
  13778. isValid = !(!result || !isValid);
  13779. }
  13780. if (!isValid) {
  13781. ui.notifications.error(
  13782. `Validation of paths registered to "${inModuleName}" failed! Errors logged in console.`
  13783. );
  13784. } else {
  13785. ui.notifications.info(
  13786. `Validation of paths registered to "${inModuleName}" complete! No errors found!`
  13787. );
  13788. }
  13789. }
  13790. /**
  13791. * Quickly checks if the entry exists in the database
  13792. *
  13793. * @param {string} inString The entry to find in the database
  13794. * @return {boolean} If the entry exists in the database
  13795. */
  13796. entryExists(inString) {
  13797. if (typeof inString !== "string")
  13798. return this._throwError("entryExists", "inString must be of type string");
  13799. inString = inString.trim();
  13800. if (inString === "")
  13801. return this._throwError("entryExists", "inString cannot be empty");
  13802. inString = inString.replace(/\[[0-9]+]$/, "");
  13803. return this.flattenedEntries.find((entry) => entry.startsWith(inString));
  13804. }
  13805. /**
  13806. * Gets the entry in the database by a dot-notated string
  13807. *
  13808. * @param {string} inString The entry to find in the database
  13809. * @param {boolean} softFail Whether it should soft fail (no error) when no entry was found
  13810. * @return {array|SequencerFile|boolean} The found entry in the database, or false if not found (with warning)
  13811. */
  13812. getEntry(inString, { softFail = false } = {}) {
  13813. if (typeof inString !== "string") {
  13814. if (softFail)
  13815. return false;
  13816. return this._throwError("getEntry", "inString must be of type string");
  13817. }
  13818. inString = inString.trim();
  13819. if (inString === "") {
  13820. if (softFail)
  13821. return false;
  13822. return this._throwError("getEntry", "inString cannot be empty");
  13823. }
  13824. inString = inString.replace(/\[[0-9]+]$/, "");
  13825. if (!this.entryExists(inString)) {
  13826. if (softFail)
  13827. return false;
  13828. return this._throwError(
  13829. "getEntry",
  13830. `Could not find ${inString} in database`
  13831. );
  13832. }
  13833. let ft = false;
  13834. let index = false;
  13835. if (CONSTANTS.FEET_REGEX.test(inString)) {
  13836. ft = inString.match(CONSTANTS.FEET_REGEX)[0];
  13837. const split = inString.split(ft).filter((str) => str !== "");
  13838. if (split.length > 1) {
  13839. index = split[1].split(".")[0];
  13840. }
  13841. inString = split[0];
  13842. }
  13843. const module2 = inString.split(".")[0];
  13844. const exactEntries = this.entries[module2].filter((entry) => {
  13845. return entry.dbPath === inString;
  13846. });
  13847. let filteredEntries = (exactEntries.length ? exactEntries : this.entries[module2].filter((entry) => {
  13848. return entry.dbPath.startsWith(inString);
  13849. })).map((entry) => {
  13850. let foundFile = entry;
  13851. if (ft)
  13852. foundFile = entry.file?.[ft] ?? foundFile;
  13853. if (index)
  13854. foundFile = foundFile?.[index] ?? foundFile;
  13855. return foundFile;
  13856. });
  13857. if (!filteredEntries.length)
  13858. return this._throwError(
  13859. "getEntry",
  13860. `Could not find ${inString} in database`
  13861. );
  13862. const foundEntry = filteredEntries.length === 1 ? filteredEntries[0] : filteredEntries;
  13863. if (index && filteredEntries.length === 1) {
  13864. foundEntry.fileIndex = Number(index);
  13865. }
  13866. return foundEntry;
  13867. }
  13868. /**
  13869. * Gets all files under a database path
  13870. *
  13871. * @param {string} inDBPath The module to get all files from
  13872. * @return {array|boolean} The found entries in the database under the module's name, or false if not found (with warning)
  13873. */
  13874. getAllFileEntries(inDBPath) {
  13875. if (typeof inDBPath !== "string")
  13876. return this._throwError(
  13877. "getAllFileEntries",
  13878. "inDBPath must be of type string"
  13879. );
  13880. inDBPath = inDBPath.trim();
  13881. if (inDBPath === "")
  13882. return this._throwError("getAllFileEntries", "inDBPath cannot be empty");
  13883. if (!this.entryExists(inDBPath))
  13884. return this._throwError(
  13885. "getAllFileEntries",
  13886. `Could not find ${inDBPath} in database`
  13887. );
  13888. const entries = this._recurseGetFilePaths(inDBPath);
  13889. return make_array_unique(entries.flat());
  13890. }
  13891. /**
  13892. * Get all valid entries under a certain path
  13893. *
  13894. * @param {string} inPath The database path to get entries under
  13895. * @return {array|boolean} An array containing the next layer of valid paths
  13896. */
  13897. getPathsUnder(inPath, {
  13898. ranges = false,
  13899. arrays = false,
  13900. match = false,
  13901. fullyQualified = false
  13902. } = {}) {
  13903. if (typeof inPath !== "string")
  13904. return this._throwError("getPathsUnder", "inPath must be of type string");
  13905. inPath = inPath.trim();
  13906. if (inPath === "")
  13907. return this._throwError("getPathsUnder", "inPath cannot be empty");
  13908. inPath = inPath.replace(/\[[0-9]+]$/, "");
  13909. if (!this.entryExists(inPath))
  13910. return this._throwError(
  13911. "getPathsUnder",
  13912. `Could not find ${inPath} in database`
  13913. );
  13914. const entries = this.flattenedEntries.filter((e) => {
  13915. return (e.startsWith(inPath + ".") || e === inPath) && (!match || match && e.match(match));
  13916. });
  13917. if (entries.length === 0)
  13918. return [];
  13919. return make_array_unique(
  13920. entries.map((e) => !arrays ? e.split(CONSTANTS.ARRAY_REGEX)[0] : e).map((e) => !ranges ? e.split(CONSTANTS.FEET_REGEX)[0] : e).map((e) => !fullyQualified ? e.split(inPath)[1] : e).map((e) => !fullyQualified ? e ? e.split(".")[1] : "" : e).filter(Boolean)
  13921. );
  13922. }
  13923. /**
  13924. * Get all valid entries under a certain path
  13925. *
  13926. * @param {string} inPath The database path to search for
  13927. * @param {boolean} publicOnly Whether to only search for public modules
  13928. * @return {array|boolean} An array containing potential database paths
  13929. */
  13930. searchFor(inPath, publicOnly = true) {
  13931. const modules = publicOnly ? this.publicModules : Object.keys(this.entries);
  13932. const originalEntries = publicOnly ? this.publicFlattenedEntries : this.flattenedEntries;
  13933. if ((!inPath || inPath === "") && !modules.includes(inPath))
  13934. return modules;
  13935. if (typeof inPath !== "string") {
  13936. return this._throwError("searchFor", "inString must be of type string");
  13937. }
  13938. inPath = inPath.trim();
  13939. if (inPath === "")
  13940. return this._throwError("searchFor", "inString cannot be empty");
  13941. inPath = inPath.replace(/\[[0-9]+]$/, "");
  13942. inPath = inPath.trim();
  13943. let entries = originalEntries.filter(
  13944. (e) => e.startsWith(inPath) && e !== inPath
  13945. );
  13946. if (inPath.endsWith("."))
  13947. inPath = inPath.substring(0, inPath.length - 1);
  13948. let length = inPath.split(".").length + 1;
  13949. let foundEntries = entries.map((e) => {
  13950. let path = e.split(CONSTANTS.FEET_REGEX)[0];
  13951. return path.split(".").slice(0, length).join(".");
  13952. });
  13953. if (foundEntries.length === 0) {
  13954. const regexString = str_to_search_regex_str(inPath).replace(/\s+/g, "|");
  13955. const searchParts = regexString.split("|").length;
  13956. const regexSearch = new RegExp(regexString, "gu");
  13957. foundEntries = originalEntries.filter((e) => {
  13958. return e.match(regexSearch)?.length >= searchParts;
  13959. }).map((e) => {
  13960. return e.split(CONSTANTS.FEET_REGEX)[0];
  13961. });
  13962. }
  13963. return make_array_unique(foundEntries);
  13964. }
  13965. /**
  13966. * Throws an error without THROWING one. Duh.
  13967. *
  13968. * @param inFunctionName
  13969. * @param inError
  13970. * @returns {boolean}
  13971. * @private
  13972. */
  13973. _throwError(inFunctionName, inError) {
  13974. let error = `Sequencer | Database | ${inFunctionName} - ${inError}`;
  13975. ui.notifications.error(error);
  13976. return false;
  13977. }
  13978. /**
  13979. * Gets all file paths from the entirety of
  13980. *
  13981. * @param inDBPath
  13982. * @returns {Array}
  13983. * @private
  13984. */
  13985. _recurseGetFilePaths(inDBPath) {
  13986. const module2 = inDBPath.split(".")[0];
  13987. return this.entries[module2].filter((entry) => entry.dbPath.startsWith(inDBPath)).map((entry) => {
  13988. return entry.getAllFiles();
  13989. }).flat();
  13990. }
  13991. /**
  13992. * Flattens a given object to just their db path and file path
  13993. *
  13994. * @param entries
  13995. * @param inModule
  13996. * @private
  13997. */
  13998. _flatten(entries, inModule) {
  13999. let flattened = flatten_object(
  14000. foundry.utils.duplicate({ [inModule]: entries })
  14001. );
  14002. this.flattenedEntries = make_array_unique(
  14003. this.flattenedEntries.concat(
  14004. Object.keys(flattened).map((file) => file.split(".file")[0])
  14005. )
  14006. );
  14007. this.inverseFlattenedEntries = Object.keys(flattened).reduce(
  14008. (acc, entry) => {
  14009. return acc.set(flattened[entry], entry.split(".file")[0]);
  14010. },
  14011. this.inverseFlattenedEntries
  14012. );
  14013. }
  14014. /**
  14015. * Processes and recurse into a large object containing file paths at any given depth
  14016. *
  14017. * @param moduleName
  14018. * @param entries
  14019. * @returns {object}
  14020. * @private
  14021. */
  14022. _processEntries(moduleName, entries) {
  14023. const allPaths = new Set(
  14024. this.flattenedEntries.filter((e) => e.split(".")[0] === moduleName).map((e) => e.split(CONSTANTS.FEET_REGEX)[0])
  14025. );
  14026. const allTemplates = foundry.utils.mergeObject(entries?._templates ?? {}, {
  14027. default: [100, 0, 0]
  14028. });
  14029. if (entries?._templates) {
  14030. delete entries?._templates;
  14031. }
  14032. const moduleEntries = [];
  14033. const mediaFileExtensions = Object.keys(CONST.FILE_CATEGORIES.IMAGE).concat(Object.keys(CONST.FILE_CATEGORIES.VIDEO)).concat(Object.keys(CONST.FILE_CATEGORIES.AUDIO));
  14034. for (let wholeDBPath of allPaths) {
  14035. let metadata = this._getCleanData(entries);
  14036. let dbPath = wholeDBPath.split(".");
  14037. dbPath.shift();
  14038. let combinedPath = "";
  14039. for (let part of dbPath) {
  14040. combinedPath = combinedPath ? combinedPath + "." + part : part;
  14041. const entry = getProperty(entries, combinedPath);
  14042. if (Array.isArray(entry) || typeof entry === "string" || entry?.file) {
  14043. metadata = this._getCleanData(entry, { existingData: metadata });
  14044. break;
  14045. }
  14046. metadata = this._getCleanData(entry, { existingData: metadata });
  14047. }
  14048. if (!metadata.template)
  14049. metadata.template = "default";
  14050. if (typeof metadata.template === "string") {
  14051. metadata.template = allTemplates?.[metadata.template] ?? allTemplates?.["default"];
  14052. }
  14053. let data = getProperty(entries, dbPath.join("."));
  14054. if (!Array.isArray(data) && !(typeof data === "string")) {
  14055. data = this._getCleanData(data, { metadata: false });
  14056. }
  14057. if (typeof data === "string") {
  14058. const existingEntry = this.entryExists(data);
  14059. const extension = data.split(".")[data.split(".").length - 1].toLowerCase();
  14060. if (!existingEntry && extension && !mediaFileExtensions.includes(extension)) {
  14061. console.warn(
  14062. `Sequencer | Database | registerEntries - failed to register ${wholeDBPath} to ${data}!`
  14063. );
  14064. this.flattenedEntries.splice(
  14065. this.flattenedEntries.indexOf(wholeDBPath),
  14066. 1
  14067. );
  14068. continue;
  14069. } else if (existingEntry) {
  14070. const sequencerFile = this.getEntry(data);
  14071. const clone = sequencerFile.clone();
  14072. clone.dbPath = wholeDBPath;
  14073. clone.metadata = foundry.utils.mergeObject(
  14074. clone.metadata ?? {},
  14075. metadata ?? {}
  14076. );
  14077. moduleEntries.push(clone);
  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 (Array.isArray(entry)) {
  14566. entry = random_array_element(entry);
  14567. }
  14568. if (entry instanceof SequencerFileBase) {
  14569. const specificFt = dbPath.match(CONSTANTS.FEET_REGEX);
  14570. if (specificFt) {
  14571. const ft = specificFt[0].replaceAll(".", "");
  14572. entry = entry.getFile(ft);
  14573. } else {
  14574. const files2 = entry.getAllFiles();
  14575. if (Array.isArray(files2)) {
  14576. const index = Math.floor(interpolate(0, files2.length - 1, 0.5));
  14577. entry = files2[index];
  14578. }
  14579. }
  14580. }
  14581. tempInput.value = `${entry?.file ?? entry}`;
  14582. }
  14583. if (quotes) {
  14584. tempInput.value = `"${tempInput.value}"`;
  14585. }
  14586. document.body.appendChild(tempInput);
  14587. tempInput.select();
  14588. document.execCommand("copy");
  14589. document.body.removeChild(tempInput);
  14590. document.execCommand("copy");
  14591. }
  14592. function playFile(entry) {
  14593. const { file, isAudio, isImage, isVideo } = getFileData(entry);
  14594. databaseStore.elements.audio.classList.toggle("hidden", !isAudio);
  14595. databaseStore.elements.image.classList.toggle("hidden", !isImage);
  14596. databaseStore.elements.player.classList.toggle("hidden", !isVideo);
  14597. if (isImage) {
  14598. databaseStore.elements.image.src = file;
  14599. databaseStore.metadata.set({
  14600. type: "Image",
  14601. duration: "n/a"
  14602. });
  14603. return;
  14604. }
  14605. const element2 = isAudio ? databaseStore.elements.audio : databaseStore.elements.player;
  14606. element2.onerror = () => {
  14607. const error = `Sequencer Database Viewer | Could not play file: ${file}`;
  14608. ui.notifications.error(error);
  14609. console.error(error);
  14610. };
  14611. element2.oncanplay = () => {
  14612. element2.play();
  14613. };
  14614. element2.onloadedmetadata = () => {
  14615. databaseStore.metadata.set({
  14616. type: isVideo ? "Video" : isAudio ? "Audio" : "Image",
  14617. duration: isImage ? "n/a" : element2.duration * 1e3 + "ms"
  14618. });
  14619. };
  14620. element2.src = file;
  14621. }
  14622. const treeStore = writable$1({});
  14623. const visibleTreeStore = writable$1([]);
  14624. let flattenedEntries = [];
  14625. const entriesStore = SequencerDatabase.entriesStore;
  14626. const packStore = writable$1(SequencerDatabase.publicModules);
  14627. const selectedPackStore = writable$1("all");
  14628. const searchStore = writable$1("");
  14629. const cleanSearchStore = writable$1("");
  14630. const searchRegexStore = writable$1(new RegExp("", "gu"));
  14631. SequencerDatabase.entriesStore.subscribe(() => {
  14632. packStore.set(SequencerDatabase.publicModules);
  14633. });
  14634. const databaseStore = {
  14635. metadata: writable$1(false),
  14636. allRanges: writable$1(false),
  14637. subLists: writable$1(false),
  14638. listView: writable$1(false),
  14639. packStore,
  14640. selectedPackStore,
  14641. visibleTreeStore,
  14642. search: searchStore,
  14643. cleanSearchStore,
  14644. searchRegex: searchRegexStore,
  14645. elements: {},
  14646. copyPath,
  14647. playFile,
  14648. openTreePath
  14649. };
  14650. entriesStore.subscribe(() => {
  14651. filterFlattenedEntries();
  14652. });
  14653. databaseStore.allRanges.subscribe(() => {
  14654. filterFlattenedEntries();
  14655. });
  14656. databaseStore.subLists.subscribe(() => {
  14657. filterFlattenedEntries();
  14658. });
  14659. databaseStore.selectedPackStore.subscribe(() => {
  14660. filterFlattenedEntries();
  14661. });
  14662. searchStore.subscribe((val) => {
  14663. const cleanSearch = str_to_search_regex_str(val).replace(/\s+/g, "|");
  14664. cleanSearchStore.set(cleanSearch);
  14665. searchRegexStore.set(new RegExp(cleanSearch, "gu"));
  14666. updateVisualTree();
  14667. });
  14668. function filterFlattenedEntries() {
  14669. const selectedPack = get_store_value(selectedPackStore);
  14670. const search = get_store_value(searchStore);
  14671. const searchRegex = get_store_value(searchRegexStore);
  14672. const subLists = get_store_value(databaseStore.subLists);
  14673. const allRanges = get_store_value(databaseStore.allRanges);
  14674. flattenedEntries = make_array_unique(
  14675. SequencerDatabase.publicFlattenedEntries.filter((e) => {
  14676. return (selectedPack === "all" || e.startsWith(selectedPack + ".")) && (!search || e.match(searchRegex));
  14677. }).map((e) => !subLists ? e.split(CONSTANTS.ARRAY_REGEX)[0] : e).map((e) => !allRanges ? e.split(CONSTANTS.FEET_REGEX)[0] : e)
  14678. );
  14679. treeStore.set(
  14680. flattenedEntries.reduce((acc, entry) => {
  14681. let path = "";
  14682. for (const part of entry.split(".")) {
  14683. const fullPath = path ? path + "." + part : part;
  14684. path = path ? path + ".children." + part : part;
  14685. if (!getProperty(acc, path)) {
  14686. setProperty(
  14687. acc,
  14688. path,
  14689. foundry.utils.mergeObject(
  14690. {
  14691. path: part,
  14692. fullPath,
  14693. open: false,
  14694. children: {}
  14695. },
  14696. getProperty(acc, path)
  14697. )
  14698. );
  14699. }
  14700. }
  14701. return acc;
  14702. }, {})
  14703. );
  14704. }
  14705. function openTreePath(fullPath, open, openAll = false) {
  14706. treeStore.update((tree) => {
  14707. const fullTreePath = fullPath.split(".").join(".children.");
  14708. const node = getProperty(tree, fullTreePath);
  14709. setProperty(tree, fullTreePath + ".open", open);
  14710. if ((!open || openAll) && !foundry.utils.isEmpty(node.children)) {
  14711. recurseOpenTree(node.children, open);
  14712. }
  14713. return tree;
  14714. });
  14715. }
  14716. function recurseOpenTree(children2, open) {
  14717. for (const node of Object.values(children2)) {
  14718. node.open = open;
  14719. if (!foundry.utils.isEmpty(node.children)) {
  14720. recurseOpenTree(node.children, open);
  14721. }
  14722. }
  14723. }
  14724. treeStore.subscribe(() => {
  14725. updateVisualTree();
  14726. });
  14727. function updateVisualTree() {
  14728. const tree = get_store_value(treeStore);
  14729. const visibleTree = recurseTree(tree).deepFlatten().filter((e) => e.visible);
  14730. visibleTreeStore.set(visibleTree);
  14731. }
  14732. function recurseTree(tree, path = "", depth = 0) {
  14733. const search = get_store_value(searchStore);
  14734. const searchRegex = get_store_value(searchRegexStore);
  14735. const searchParts = get_store_value(cleanSearchStore).split("|");
  14736. return Object.entries(tree).map(([key, data]) => {
  14737. const fullPath = path ? path + "." + key : key;
  14738. const children2 = recurseTree(
  14739. data.children,
  14740. fullPath,
  14741. depth + 1
  14742. ).deepFlatten();
  14743. const matchParts = make_array_unique(fullPath.match(searchRegex) || []);
  14744. const open = data.open || search && (matchParts.length >= searchParts.length || children2.filter((e) => e.visible).length);
  14745. let visible = !search || matchParts.length >= searchParts.length;
  14746. if (visible) {
  14747. children2.forEach((e) => e.visible = true);
  14748. } else {
  14749. visible = children2.filter((e) => e.visible).length;
  14750. }
  14751. const entry = {
  14752. class: TreeViewEntry,
  14753. path: key,
  14754. fullPath,
  14755. open,
  14756. visible,
  14757. hasChildren: !foundry.utils.isEmpty(data.children),
  14758. depth
  14759. };
  14760. const leaf = [entry];
  14761. if ((data.open || entry.open) && entry.hasChildren) {
  14762. leaf.push(...children2, {
  14763. fullPath: randomID(),
  14764. class: TreeViewSeparator
  14765. });
  14766. }
  14767. return leaf;
  14768. });
  14769. }
  14770. const DatabaseEntry_svelte_svelte_type_style_lang = "";
  14771. function create_fragment$i(ctx) {
  14772. let div2;
  14773. let button0;
  14774. let t0;
  14775. let button1;
  14776. let i1;
  14777. let t1;
  14778. let button2;
  14779. let i2;
  14780. let t2;
  14781. let div1;
  14782. let div0;
  14783. let t3;
  14784. let t4;
  14785. let mounted;
  14786. let dispose;
  14787. return {
  14788. c() {
  14789. div2 = element("div");
  14790. button0 = element("button");
  14791. button0.innerHTML = `<i class="fas fa-play svelte-ese-flzvpb"></i>`;
  14792. t0 = space();
  14793. button1 = element("button");
  14794. i1 = element("i");
  14795. t1 = space();
  14796. button2 = element("button");
  14797. i2 = element("i");
  14798. t2 = space();
  14799. div1 = element("div");
  14800. div0 = element("div");
  14801. t3 = space();
  14802. t4 = text$1(
  14803. /*entry*/
  14804. ctx[0]
  14805. );
  14806. attr(button0, "type", "button");
  14807. attr(button0, "class", "btn_play svelte-ese-flzvpb");
  14808. attr(i1, "class", "fas fa-file svelte-ese-flzvpb");
  14809. toggle_class(
  14810. i1,
  14811. "flash-it",
  14812. /*flashFilePath*/
  14813. ctx[1]
  14814. );
  14815. attr(button1, "type", "button");
  14816. attr(button1, "class", "btn_copy_filepath svelte-ese-flzvpb");
  14817. attr(i2, "class", "fas fa-database svelte-ese-flzvpb");
  14818. toggle_class(
  14819. i2,
  14820. "flash-it",
  14821. /*flashDBPath*/
  14822. ctx[2]
  14823. );
  14824. attr(button2, "type", "button");
  14825. attr(button2, "class", "btn_copy_databasepath svelte-ese-flzvpb");
  14826. attr(div0, "class", "database-entry-text-highlight svelte-ese-flzvpb");
  14827. attr(div1, "class", "database-entry-text svelte-ese-flzvpb");
  14828. attr(
  14829. div1,
  14830. "title",
  14831. /*entry*/
  14832. ctx[0]
  14833. );
  14834. attr(div2, "class", "database-entry svelte-ese-flzvpb");
  14835. attr(
  14836. div2,
  14837. "data-id",
  14838. /*entry*/
  14839. ctx[0]
  14840. );
  14841. },
  14842. m(target, anchor) {
  14843. insert(target, div2, anchor);
  14844. append(div2, button0);
  14845. append(div2, t0);
  14846. append(div2, button1);
  14847. append(button1, i1);
  14848. append(div2, t1);
  14849. append(div2, button2);
  14850. append(button2, i2);
  14851. append(div2, t2);
  14852. append(div2, div1);
  14853. append(div1, div0);
  14854. div0.innerHTML = /*highlight*/
  14855. ctx[3];
  14856. append(div1, t3);
  14857. append(div1, t4);
  14858. if (!mounted) {
  14859. dispose = [
  14860. listen(
  14861. button0,
  14862. "click",
  14863. /*click_handler*/
  14864. ctx[6]
  14865. ),
  14866. listen(
  14867. button1,
  14868. "click",
  14869. /*click_handler_1*/
  14870. ctx[7]
  14871. ),
  14872. listen(
  14873. button2,
  14874. "click",
  14875. /*click_handler_2*/
  14876. ctx[8]
  14877. )
  14878. ];
  14879. mounted = true;
  14880. }
  14881. },
  14882. p(ctx2, [dirty]) {
  14883. if (dirty & /*flashFilePath*/
  14884. 2) {
  14885. toggle_class(
  14886. i1,
  14887. "flash-it",
  14888. /*flashFilePath*/
  14889. ctx2[1]
  14890. );
  14891. }
  14892. if (dirty & /*flashDBPath*/
  14893. 4) {
  14894. toggle_class(
  14895. i2,
  14896. "flash-it",
  14897. /*flashDBPath*/
  14898. ctx2[2]
  14899. );
  14900. }
  14901. if (dirty & /*highlight*/
  14902. 8)
  14903. div0.innerHTML = /*highlight*/
  14904. ctx2[3];
  14905. if (dirty & /*entry*/
  14906. 1)
  14907. set_data(
  14908. t4,
  14909. /*entry*/
  14910. ctx2[0]
  14911. );
  14912. if (dirty & /*entry*/
  14913. 1) {
  14914. attr(
  14915. div1,
  14916. "title",
  14917. /*entry*/
  14918. ctx2[0]
  14919. );
  14920. }
  14921. if (dirty & /*entry*/
  14922. 1) {
  14923. attr(
  14924. div2,
  14925. "data-id",
  14926. /*entry*/
  14927. ctx2[0]
  14928. );
  14929. }
  14930. },
  14931. i: noop,
  14932. o: noop,
  14933. d(detaching) {
  14934. if (detaching)
  14935. detach(div2);
  14936. mounted = false;
  14937. run_all(dispose);
  14938. }
  14939. };
  14940. }
  14941. function instance$i($$self, $$props, $$invalidate) {
  14942. let highlight;
  14943. let $searchRegex;
  14944. createEventDispatcher();
  14945. let { entry } = $$props;
  14946. const searchRegex = databaseStore.searchRegex;
  14947. component_subscribe($$self, searchRegex, (value) => $$invalidate(5, $searchRegex = value));
  14948. let flashFilePath = false;
  14949. let flashDBPath = false;
  14950. const click_handler = () => {
  14951. databaseStore.playFile(entry);
  14952. };
  14953. const click_handler_1 = (e) => {
  14954. databaseStore.copyPath(entry, true, e.ctrlKey);
  14955. $$invalidate(1, flashFilePath = true);
  14956. setTimeout(
  14957. () => {
  14958. $$invalidate(1, flashFilePath = false);
  14959. },
  14960. 400
  14961. );
  14962. };
  14963. const click_handler_2 = (e) => {
  14964. databaseStore.copyPath(entry, false, e.ctrlKey);
  14965. $$invalidate(2, flashDBPath = true);
  14966. setTimeout(
  14967. () => {
  14968. $$invalidate(2, flashDBPath = false);
  14969. },
  14970. 400
  14971. );
  14972. };
  14973. $$self.$$set = ($$props2) => {
  14974. if ("entry" in $$props2)
  14975. $$invalidate(0, entry = $$props2.entry);
  14976. };
  14977. $$self.$$.update = () => {
  14978. if ($$self.$$.dirty & /*entry, $searchRegex*/
  14979. 33) {
  14980. $$invalidate(3, highlight = entry.replace($searchRegex, "<mark>$&</mark>"));
  14981. }
  14982. };
  14983. return [
  14984. entry,
  14985. flashFilePath,
  14986. flashDBPath,
  14987. highlight,
  14988. searchRegex,
  14989. $searchRegex,
  14990. click_handler,
  14991. click_handler_1,
  14992. click_handler_2
  14993. ];
  14994. }
  14995. class DatabaseEntry extends SvelteComponent {
  14996. constructor(options) {
  14997. super();
  14998. init(this, options, instance$i, create_fragment$i, safe_not_equal, { entry: 0 });
  14999. }
  15000. }
  15001. const DIRECTION_TYPE = {
  15002. FRONT: "FRONT",
  15003. // scroll up or left
  15004. BEHIND: "BEHIND"
  15005. // scroll down or right
  15006. };
  15007. const CALC_TYPE = {
  15008. INIT: "INIT",
  15009. FIXED: "FIXED",
  15010. DYNAMIC: "DYNAMIC"
  15011. };
  15012. const LEADING_BUFFER = 2;
  15013. class Virtual {
  15014. param;
  15015. callUpdate;
  15016. firstRangeTotalSize = 0;
  15017. firstRangeAverageSize = 0;
  15018. lastCalcIndex = 0;
  15019. fixedSizeValue = 0;
  15020. calcType = CALC_TYPE.INIT;
  15021. offset = 0;
  15022. direction = "";
  15023. range;
  15024. constructor(param, callUpdate) {
  15025. this.init(param, callUpdate);
  15026. }
  15027. init(param, callUpdate) {
  15028. this.param = param;
  15029. this.callUpdate = callUpdate;
  15030. this.sizes = /* @__PURE__ */ new Map();
  15031. this.firstRangeTotalSize = 0;
  15032. this.firstRangeAverageSize = 0;
  15033. this.lastCalcIndex = 0;
  15034. this.fixedSizeValue = 0;
  15035. this.calcType = CALC_TYPE.INIT;
  15036. this.offset = 0;
  15037. this.direction = "";
  15038. this.range = /* @__PURE__ */ Object.create(null);
  15039. if (param) {
  15040. this.checkRange(0, param.keeps - 1);
  15041. }
  15042. }
  15043. destroy() {
  15044. this.init(null, null);
  15045. }
  15046. // return current render range
  15047. getRange() {
  15048. const range = /* @__PURE__ */ Object.create(null);
  15049. range.start = this.range.start;
  15050. range.end = this.range.end;
  15051. range.padFront = this.range.padFront;
  15052. range.padBehind = this.range.padBehind;
  15053. return range;
  15054. }
  15055. isBehind() {
  15056. return this.direction === DIRECTION_TYPE.BEHIND;
  15057. }
  15058. isFront() {
  15059. return this.direction === DIRECTION_TYPE.FRONT;
  15060. }
  15061. // return start index offset
  15062. getOffset(start) {
  15063. return (start < 1 ? 0 : this.getIndexOffset(start)) + this.param.slotHeaderSize;
  15064. }
  15065. updateParam(key, value) {
  15066. if (this.param && key in this.param) {
  15067. if (key === "uniqueIds") {
  15068. this.sizes.forEach((v, key2) => {
  15069. if (!value.includes(key2)) {
  15070. this.sizes.delete(key2);
  15071. }
  15072. });
  15073. }
  15074. this.param[key] = value;
  15075. }
  15076. }
  15077. // save each size map by id
  15078. saveSize(id, size) {
  15079. this.sizes.set(id, size);
  15080. if (this.calcType === CALC_TYPE.INIT) {
  15081. this.fixedSizeValue = size;
  15082. this.calcType = CALC_TYPE.FIXED;
  15083. } else if (this.calcType === CALC_TYPE.FIXED && this.fixedSizeValue !== size) {
  15084. this.calcType = CALC_TYPE.DYNAMIC;
  15085. delete this.fixedSizeValue;
  15086. }
  15087. if (this.calcType !== CALC_TYPE.FIXED && typeof this.firstRangeTotalSize !== "undefined") {
  15088. if (this.sizes.size < Math.min(this.param.keeps, this.param.uniqueIds.length)) {
  15089. this.firstRangeTotalSize = [...this.sizes.values()].reduce((acc, val) => acc + val, 0);
  15090. this.firstRangeAverageSize = Math.round(this.firstRangeTotalSize / this.sizes.size);
  15091. } else {
  15092. delete this.firstRangeTotalSize;
  15093. }
  15094. }
  15095. }
  15096. // in some special situation (e.g. length change) we need to update in a row
  15097. // try going to render next range by a leading buffer according to current direction
  15098. handleDataSourcesChange() {
  15099. let start = this.range.start;
  15100. if (this.isFront()) {
  15101. start = start - LEADING_BUFFER;
  15102. } else if (this.isBehind()) {
  15103. start = start + LEADING_BUFFER;
  15104. }
  15105. start = Math.max(start, 0);
  15106. this.updateRange(this.range.start, this.getEndByStart(start));
  15107. }
  15108. // when slot size change, we also need force update
  15109. handleSlotSizeChange() {
  15110. this.handleDataSourcesChange();
  15111. }
  15112. // calculating range on scroll
  15113. handleScroll(offset2) {
  15114. this.direction = offset2 < this.offset ? DIRECTION_TYPE.FRONT : DIRECTION_TYPE.BEHIND;
  15115. this.offset = offset2;
  15116. if (!this.param) {
  15117. return;
  15118. }
  15119. if (this.direction === DIRECTION_TYPE.FRONT) {
  15120. this.handleFront();
  15121. } else if (this.direction === DIRECTION_TYPE.BEHIND) {
  15122. this.handleBehind();
  15123. }
  15124. }
  15125. // ----------- public method end -----------
  15126. handleFront() {
  15127. const overs = this.getScrollOvers();
  15128. if (overs > this.range.start) {
  15129. return;
  15130. }
  15131. const start = Math.max(overs - this.param.buffer, 0);
  15132. this.checkRange(start, this.getEndByStart(start));
  15133. }
  15134. handleBehind() {
  15135. const overs = this.getScrollOvers();
  15136. if (overs < this.range.start + this.param.buffer) {
  15137. return;
  15138. }
  15139. this.checkRange(overs, this.getEndByStart(overs));
  15140. }
  15141. // return the pass overs according to current scroll offset
  15142. getScrollOvers() {
  15143. const offset2 = this.offset - this.param.slotHeaderSize;
  15144. if (offset2 <= 0) {
  15145. return 0;
  15146. }
  15147. if (this.isFixedType()) {
  15148. return Math.floor(offset2 / this.fixedSizeValue);
  15149. }
  15150. let low = 0;
  15151. let middle = 0;
  15152. let middleOffset = 0;
  15153. let high = this.param.uniqueIds.length;
  15154. while (low <= high) {
  15155. middle = low + Math.floor((high - low) / 2);
  15156. middleOffset = this.getIndexOffset(middle);
  15157. if (middleOffset === offset2) {
  15158. return middle;
  15159. } else if (middleOffset < offset2) {
  15160. low = middle + 1;
  15161. } else if (middleOffset > offset2) {
  15162. high = middle - 1;
  15163. }
  15164. }
  15165. return low > 0 ? --low : 0;
  15166. }
  15167. // return a scroll offset from given index, can efficiency be improved more here?
  15168. // although the call frequency is very high, its only a superposition of numbers
  15169. getIndexOffset(givenIndex) {
  15170. if (!givenIndex) {
  15171. return 0;
  15172. }
  15173. let offset2 = 0;
  15174. let indexSize = 0;
  15175. for (let index = 0; index < givenIndex; index++) {
  15176. indexSize = this.sizes.get(this.param.uniqueIds[index]);
  15177. offset2 = offset2 + (typeof indexSize === "number" ? indexSize : this.getEstimateSize());
  15178. }
  15179. this.lastCalcIndex = Math.max(this.lastCalcIndex, givenIndex - 1);
  15180. this.lastCalcIndex = Math.min(this.lastCalcIndex, this.getLastIndex());
  15181. return offset2;
  15182. }
  15183. // is fixed size type
  15184. isFixedType() {
  15185. return this.calcType === CALC_TYPE.FIXED;
  15186. }
  15187. // return the real last index
  15188. getLastIndex() {
  15189. return this.param.uniqueIds.length - 1;
  15190. }
  15191. // in some conditions range is broke, we need correct it
  15192. // and then decide whether need update to next range
  15193. checkRange(start, end) {
  15194. const keeps = this.param.keeps;
  15195. const total = this.param.uniqueIds.length;
  15196. if (total <= keeps) {
  15197. start = 0;
  15198. end = this.getLastIndex();
  15199. } else if (end - start < keeps - 1) {
  15200. start = end - keeps + 1;
  15201. }
  15202. if (this.range.start !== start) {
  15203. this.updateRange(start, end);
  15204. }
  15205. }
  15206. // setting to a new range and rerender
  15207. updateRange(start, end) {
  15208. this.range.start = start;
  15209. this.range.end = end;
  15210. this.range.padFront = this.getPadFront();
  15211. this.range.padBehind = this.getPadBehind();
  15212. this.callUpdate(this.getRange());
  15213. }
  15214. // return end base on start
  15215. getEndByStart(start) {
  15216. const theoryEnd = start + this.param.keeps - 1;
  15217. const truelyEnd = Math.min(theoryEnd, this.getLastIndex());
  15218. return truelyEnd;
  15219. }
  15220. // return total front offset
  15221. getPadFront() {
  15222. if (this.isFixedType()) {
  15223. return this.fixedSizeValue * this.range.start;
  15224. } else {
  15225. return this.getIndexOffset(this.range.start);
  15226. }
  15227. }
  15228. // return total behind offset
  15229. getPadBehind() {
  15230. const end = this.range.end;
  15231. const lastIndex = this.getLastIndex();
  15232. if (this.isFixedType()) {
  15233. return (lastIndex - end) * this.fixedSizeValue;
  15234. }
  15235. if (this.lastCalcIndex === lastIndex) {
  15236. return this.getIndexOffset(lastIndex) - this.getIndexOffset(end);
  15237. } else {
  15238. return (lastIndex - end) * this.getEstimateSize();
  15239. }
  15240. }
  15241. // get the item estimate size
  15242. getEstimateSize() {
  15243. return this.isFixedType() ? this.fixedSizeValue : this.firstRangeAverageSize || this.param.estimateSize;
  15244. }
  15245. }
  15246. function create_fragment$h(ctx) {
  15247. let div;
  15248. let current;
  15249. const default_slot_template = (
  15250. /*#slots*/
  15251. ctx[5].default
  15252. );
  15253. const default_slot = create_slot(
  15254. default_slot_template,
  15255. ctx,
  15256. /*$$scope*/
  15257. ctx[4],
  15258. null
  15259. );
  15260. return {
  15261. c() {
  15262. div = element("div");
  15263. if (default_slot)
  15264. default_slot.c();
  15265. attr(div, "class", "virtual-scroll-item");
  15266. },
  15267. m(target, anchor) {
  15268. insert(target, div, anchor);
  15269. if (default_slot) {
  15270. default_slot.m(div, null);
  15271. }
  15272. ctx[6](div);
  15273. current = true;
  15274. },
  15275. p(ctx2, [dirty]) {
  15276. if (default_slot) {
  15277. if (default_slot.p && (!current || dirty & /*$$scope*/
  15278. 16)) {
  15279. update_slot_base(
  15280. default_slot,
  15281. default_slot_template,
  15282. ctx2,
  15283. /*$$scope*/
  15284. ctx2[4],
  15285. !current ? get_all_dirty_from_scope(
  15286. /*$$scope*/
  15287. ctx2[4]
  15288. ) : get_slot_changes(
  15289. default_slot_template,
  15290. /*$$scope*/
  15291. ctx2[4],
  15292. dirty,
  15293. null
  15294. ),
  15295. null
  15296. );
  15297. }
  15298. }
  15299. },
  15300. i(local) {
  15301. if (current)
  15302. return;
  15303. transition_in(default_slot, local);
  15304. current = true;
  15305. },
  15306. o(local) {
  15307. transition_out(default_slot, local);
  15308. current = false;
  15309. },
  15310. d(detaching) {
  15311. if (detaching)
  15312. detach(div);
  15313. if (default_slot)
  15314. default_slot.d(detaching);
  15315. ctx[6](null);
  15316. }
  15317. };
  15318. }
  15319. function instance$h($$self, $$props, $$invalidate) {
  15320. let { $$slots: slots = {}, $$scope } = $$props;
  15321. let { horizontal = false } = $$props;
  15322. let { uniqueKey } = $$props;
  15323. let { type = "item" } = $$props;
  15324. let resizeObserver2;
  15325. let itemDiv;
  15326. let previousSize;
  15327. const dispatch2 = createEventDispatcher();
  15328. const shapeKey = horizontal ? "offsetWidth" : "offsetHeight";
  15329. onMount(() => {
  15330. if (typeof ResizeObserver !== "undefined") {
  15331. resizeObserver2 = new ResizeObserver(dispatchSizeChange);
  15332. resizeObserver2.observe(itemDiv);
  15333. }
  15334. });
  15335. afterUpdate(dispatchSizeChange);
  15336. onDestroy(() => {
  15337. if (resizeObserver2) {
  15338. resizeObserver2.disconnect();
  15339. resizeObserver2 = null;
  15340. }
  15341. });
  15342. function dispatchSizeChange() {
  15343. const size = itemDiv ? itemDiv[shapeKey] : 0;
  15344. if (size === previousSize)
  15345. return;
  15346. previousSize = size;
  15347. dispatch2("resize", { id: uniqueKey, size, type });
  15348. }
  15349. function div_binding($$value) {
  15350. binding_callbacks[$$value ? "unshift" : "push"](() => {
  15351. itemDiv = $$value;
  15352. $$invalidate(0, itemDiv);
  15353. });
  15354. }
  15355. $$self.$$set = ($$props2) => {
  15356. if ("horizontal" in $$props2)
  15357. $$invalidate(1, horizontal = $$props2.horizontal);
  15358. if ("uniqueKey" in $$props2)
  15359. $$invalidate(2, uniqueKey = $$props2.uniqueKey);
  15360. if ("type" in $$props2)
  15361. $$invalidate(3, type = $$props2.type);
  15362. if ("$$scope" in $$props2)
  15363. $$invalidate(4, $$scope = $$props2.$$scope);
  15364. };
  15365. return [itemDiv, horizontal, uniqueKey, type, $$scope, slots, div_binding];
  15366. }
  15367. class Item extends SvelteComponent {
  15368. constructor(options) {
  15369. super();
  15370. init(this, options, instance$h, create_fragment$h, safe_not_equal, { horizontal: 1, uniqueKey: 2, type: 3 });
  15371. }
  15372. }
  15373. const get_footer_slot_changes = (dirty) => ({ data: dirty[0] & /*displayItems*/
  15374. 4 });
  15375. const get_footer_slot_context = (ctx) => ({ data: (
  15376. /*dataItem*/
  15377. ctx[39]
  15378. ) });
  15379. function get_each_context$6(ctx, list, i) {
  15380. const child_ctx = ctx.slice();
  15381. child_ctx[39] = list[i];
  15382. return child_ctx;
  15383. }
  15384. const get_default_slot_changes = (dirty) => ({ data: dirty[0] & /*displayItems*/
  15385. 4 });
  15386. const get_default_slot_context = (ctx) => ({ data: (
  15387. /*dataItem*/
  15388. ctx[39]
  15389. ) });
  15390. const get_header_slot_changes = (dirty) => ({ data: dirty[0] & /*displayItems*/
  15391. 4 });
  15392. const get_header_slot_context = (ctx) => ({ data: (
  15393. /*dataItem*/
  15394. ctx[39]
  15395. ) });
  15396. function create_if_block_1$3(ctx) {
  15397. let item;
  15398. let current;
  15399. item = new Item({
  15400. props: {
  15401. type: "slot",
  15402. uniqueKey: "header",
  15403. $$slots: { default: [create_default_slot_2$1] },
  15404. $$scope: { ctx }
  15405. }
  15406. });
  15407. item.$on(
  15408. "resize",
  15409. /*onItemResized*/
  15410. ctx[6]
  15411. );
  15412. return {
  15413. c() {
  15414. create_component(item.$$.fragment);
  15415. },
  15416. m(target, anchor) {
  15417. mount_component(item, target, anchor);
  15418. current = true;
  15419. },
  15420. p(ctx2, dirty) {
  15421. const item_changes = {};
  15422. if (dirty[0] & /*$$scope, displayItems*/
  15423. 536870916) {
  15424. item_changes.$$scope = { dirty, ctx: ctx2 };
  15425. }
  15426. item.$set(item_changes);
  15427. },
  15428. i(local) {
  15429. if (current)
  15430. return;
  15431. transition_in(item.$$.fragment, local);
  15432. current = true;
  15433. },
  15434. o(local) {
  15435. transition_out(item.$$.fragment, local);
  15436. current = false;
  15437. },
  15438. d(detaching) {
  15439. destroy_component(item, detaching);
  15440. }
  15441. };
  15442. }
  15443. function create_default_slot_2$1(ctx) {
  15444. let current;
  15445. const header_slot_template = (
  15446. /*#slots*/
  15447. ctx[26].header
  15448. );
  15449. const header_slot = create_slot(
  15450. header_slot_template,
  15451. ctx,
  15452. /*$$scope*/
  15453. ctx[29],
  15454. get_header_slot_context
  15455. );
  15456. return {
  15457. c() {
  15458. if (header_slot)
  15459. header_slot.c();
  15460. },
  15461. m(target, anchor) {
  15462. if (header_slot) {
  15463. header_slot.m(target, anchor);
  15464. }
  15465. current = true;
  15466. },
  15467. p(ctx2, dirty) {
  15468. if (header_slot) {
  15469. if (header_slot.p && (!current || dirty[0] & /*$$scope, displayItems*/
  15470. 536870916)) {
  15471. update_slot_base(
  15472. header_slot,
  15473. header_slot_template,
  15474. ctx2,
  15475. /*$$scope*/
  15476. ctx2[29],
  15477. !current ? get_all_dirty_from_scope(
  15478. /*$$scope*/
  15479. ctx2[29]
  15480. ) : get_slot_changes(
  15481. header_slot_template,
  15482. /*$$scope*/
  15483. ctx2[29],
  15484. dirty,
  15485. get_header_slot_changes
  15486. ),
  15487. get_header_slot_context
  15488. );
  15489. }
  15490. }
  15491. },
  15492. i(local) {
  15493. if (current)
  15494. return;
  15495. transition_in(header_slot, local);
  15496. current = true;
  15497. },
  15498. o(local) {
  15499. transition_out(header_slot, local);
  15500. current = false;
  15501. },
  15502. d(detaching) {
  15503. if (header_slot)
  15504. header_slot.d(detaching);
  15505. }
  15506. };
  15507. }
  15508. function create_default_slot_1$1(ctx) {
  15509. let t;
  15510. let current;
  15511. const default_slot_template = (
  15512. /*#slots*/
  15513. ctx[26].default
  15514. );
  15515. const default_slot = create_slot(
  15516. default_slot_template,
  15517. ctx,
  15518. /*$$scope*/
  15519. ctx[29],
  15520. get_default_slot_context
  15521. );
  15522. return {
  15523. c() {
  15524. if (default_slot)
  15525. default_slot.c();
  15526. t = space();
  15527. },
  15528. m(target, anchor) {
  15529. if (default_slot) {
  15530. default_slot.m(target, anchor);
  15531. }
  15532. insert(target, t, anchor);
  15533. current = true;
  15534. },
  15535. p(ctx2, dirty) {
  15536. if (default_slot) {
  15537. if (default_slot.p && (!current || dirty[0] & /*$$scope, displayItems*/
  15538. 536870916)) {
  15539. update_slot_base(
  15540. default_slot,
  15541. default_slot_template,
  15542. ctx2,
  15543. /*$$scope*/
  15544. ctx2[29],
  15545. !current ? get_all_dirty_from_scope(
  15546. /*$$scope*/
  15547. ctx2[29]
  15548. ) : get_slot_changes(
  15549. default_slot_template,
  15550. /*$$scope*/
  15551. ctx2[29],
  15552. dirty,
  15553. get_default_slot_changes
  15554. ),
  15555. get_default_slot_context
  15556. );
  15557. }
  15558. }
  15559. },
  15560. i(local) {
  15561. if (current)
  15562. return;
  15563. transition_in(default_slot, local);
  15564. current = true;
  15565. },
  15566. o(local) {
  15567. transition_out(default_slot, local);
  15568. current = false;
  15569. },
  15570. d(detaching) {
  15571. if (default_slot)
  15572. default_slot.d(detaching);
  15573. if (detaching)
  15574. detach(t);
  15575. }
  15576. };
  15577. }
  15578. function create_each_block$6(key_2, ctx) {
  15579. let first;
  15580. let item;
  15581. let current;
  15582. item = new Item({
  15583. props: {
  15584. uniqueKey: (
  15585. /*dataItem*/
  15586. ctx[39][
  15587. /*key*/
  15588. ctx[0]
  15589. ]
  15590. ),
  15591. horizontal: (
  15592. /*isHorizontal*/
  15593. ctx[1]
  15594. ),
  15595. type: "item",
  15596. $$slots: { default: [create_default_slot_1$1] },
  15597. $$scope: { ctx }
  15598. }
  15599. });
  15600. item.$on(
  15601. "resize",
  15602. /*onItemResized*/
  15603. ctx[6]
  15604. );
  15605. return {
  15606. key: key_2,
  15607. first: null,
  15608. c() {
  15609. first = empty();
  15610. create_component(item.$$.fragment);
  15611. this.first = first;
  15612. },
  15613. m(target, anchor) {
  15614. insert(target, first, anchor);
  15615. mount_component(item, target, anchor);
  15616. current = true;
  15617. },
  15618. p(new_ctx, dirty) {
  15619. ctx = new_ctx;
  15620. const item_changes = {};
  15621. if (dirty[0] & /*displayItems, key*/
  15622. 5)
  15623. item_changes.uniqueKey = /*dataItem*/
  15624. ctx[39][
  15625. /*key*/
  15626. ctx[0]
  15627. ];
  15628. if (dirty[0] & /*isHorizontal*/
  15629. 2)
  15630. item_changes.horizontal = /*isHorizontal*/
  15631. ctx[1];
  15632. if (dirty[0] & /*$$scope, displayItems*/
  15633. 536870916) {
  15634. item_changes.$$scope = { dirty, ctx };
  15635. }
  15636. item.$set(item_changes);
  15637. },
  15638. i(local) {
  15639. if (current)
  15640. return;
  15641. transition_in(item.$$.fragment, local);
  15642. current = true;
  15643. },
  15644. o(local) {
  15645. transition_out(item.$$.fragment, local);
  15646. current = false;
  15647. },
  15648. d(detaching) {
  15649. if (detaching)
  15650. detach(first);
  15651. destroy_component(item, detaching);
  15652. }
  15653. };
  15654. }
  15655. function create_if_block$6(ctx) {
  15656. let item;
  15657. let current;
  15658. item = new Item({
  15659. props: {
  15660. type: "slot",
  15661. uniqueKey: "footer",
  15662. $$slots: { default: [create_default_slot$2] },
  15663. $$scope: { ctx }
  15664. }
  15665. });
  15666. item.$on(
  15667. "resize",
  15668. /*onItemResized*/
  15669. ctx[6]
  15670. );
  15671. return {
  15672. c() {
  15673. create_component(item.$$.fragment);
  15674. },
  15675. m(target, anchor) {
  15676. mount_component(item, target, anchor);
  15677. current = true;
  15678. },
  15679. p(ctx2, dirty) {
  15680. const item_changes = {};
  15681. if (dirty[0] & /*$$scope, displayItems*/
  15682. 536870916) {
  15683. item_changes.$$scope = { dirty, ctx: ctx2 };
  15684. }
  15685. item.$set(item_changes);
  15686. },
  15687. i(local) {
  15688. if (current)
  15689. return;
  15690. transition_in(item.$$.fragment, local);
  15691. current = true;
  15692. },
  15693. o(local) {
  15694. transition_out(item.$$.fragment, local);
  15695. current = false;
  15696. },
  15697. d(detaching) {
  15698. destroy_component(item, detaching);
  15699. }
  15700. };
  15701. }
  15702. function create_default_slot$2(ctx) {
  15703. let current;
  15704. const footer_slot_template = (
  15705. /*#slots*/
  15706. ctx[26].footer
  15707. );
  15708. const footer_slot = create_slot(
  15709. footer_slot_template,
  15710. ctx,
  15711. /*$$scope*/
  15712. ctx[29],
  15713. get_footer_slot_context
  15714. );
  15715. return {
  15716. c() {
  15717. if (footer_slot)
  15718. footer_slot.c();
  15719. },
  15720. m(target, anchor) {
  15721. if (footer_slot) {
  15722. footer_slot.m(target, anchor);
  15723. }
  15724. current = true;
  15725. },
  15726. p(ctx2, dirty) {
  15727. if (footer_slot) {
  15728. if (footer_slot.p && (!current || dirty[0] & /*$$scope, displayItems*/
  15729. 536870916)) {
  15730. update_slot_base(
  15731. footer_slot,
  15732. footer_slot_template,
  15733. ctx2,
  15734. /*$$scope*/
  15735. ctx2[29],
  15736. !current ? get_all_dirty_from_scope(
  15737. /*$$scope*/
  15738. ctx2[29]
  15739. ) : get_slot_changes(
  15740. footer_slot_template,
  15741. /*$$scope*/
  15742. ctx2[29],
  15743. dirty,
  15744. get_footer_slot_changes
  15745. ),
  15746. get_footer_slot_context
  15747. );
  15748. }
  15749. }
  15750. },
  15751. i(local) {
  15752. if (current)
  15753. return;
  15754. transition_in(footer_slot, local);
  15755. current = true;
  15756. },
  15757. o(local) {
  15758. transition_out(footer_slot, local);
  15759. current = false;
  15760. },
  15761. d(detaching) {
  15762. if (footer_slot)
  15763. footer_slot.d(detaching);
  15764. }
  15765. };
  15766. }
  15767. function create_fragment$g(ctx) {
  15768. let div2;
  15769. let t0;
  15770. let div0;
  15771. let each_blocks = [];
  15772. let each_1_lookup = /* @__PURE__ */ new Map();
  15773. let t1;
  15774. let t2;
  15775. let div1;
  15776. let current;
  15777. let mounted;
  15778. let dispose;
  15779. let if_block0 = (
  15780. /*$$slots*/
  15781. ctx[8].header && create_if_block_1$3(ctx)
  15782. );
  15783. let each_value = (
  15784. /*displayItems*/
  15785. ctx[2]
  15786. );
  15787. const get_key = (ctx2) => (
  15788. /*dataItem*/
  15789. ctx2[39][
  15790. /*key*/
  15791. ctx2[0]
  15792. ]
  15793. );
  15794. for (let i = 0; i < each_value.length; i += 1) {
  15795. let child_ctx = get_each_context$6(ctx, each_value, i);
  15796. let key = get_key(child_ctx);
  15797. each_1_lookup.set(key, each_blocks[i] = create_each_block$6(key, child_ctx));
  15798. }
  15799. let if_block1 = (
  15800. /*$$slots*/
  15801. ctx[8].footer && create_if_block$6(ctx)
  15802. );
  15803. return {
  15804. c() {
  15805. div2 = element("div");
  15806. if (if_block0)
  15807. if_block0.c();
  15808. t0 = space();
  15809. div0 = element("div");
  15810. for (let i = 0; i < each_blocks.length; i += 1) {
  15811. each_blocks[i].c();
  15812. }
  15813. t1 = space();
  15814. if (if_block1)
  15815. if_block1.c();
  15816. t2 = space();
  15817. div1 = element("div");
  15818. set_style(
  15819. div0,
  15820. "padding",
  15821. /*paddingStyle*/
  15822. ctx[3]
  15823. );
  15824. attr(div1, "class", "shepherd");
  15825. set_style(
  15826. div1,
  15827. "width",
  15828. /*isHorizontal*/
  15829. ctx[1] ? "0px" : "100%"
  15830. );
  15831. set_style(
  15832. div1,
  15833. "height",
  15834. /*isHorizontal*/
  15835. ctx[1] ? "100%" : "0px"
  15836. );
  15837. set_style(div2, "overflow-y", "auto");
  15838. set_style(div2, "height", "inherit");
  15839. },
  15840. m(target, anchor) {
  15841. insert(target, div2, anchor);
  15842. if (if_block0)
  15843. if_block0.m(div2, null);
  15844. append(div2, t0);
  15845. append(div2, div0);
  15846. for (let i = 0; i < each_blocks.length; i += 1) {
  15847. each_blocks[i].m(div0, null);
  15848. }
  15849. append(div2, t1);
  15850. if (if_block1)
  15851. if_block1.m(div2, null);
  15852. append(div2, t2);
  15853. append(div2, div1);
  15854. ctx[27](div1);
  15855. ctx[28](div2);
  15856. current = true;
  15857. if (!mounted) {
  15858. dispose = listen(
  15859. div2,
  15860. "scroll",
  15861. /*onScroll*/
  15862. ctx[7]
  15863. );
  15864. mounted = true;
  15865. }
  15866. },
  15867. p(ctx2, dirty) {
  15868. if (
  15869. /*$$slots*/
  15870. ctx2[8].header
  15871. ) {
  15872. if (if_block0) {
  15873. if_block0.p(ctx2, dirty);
  15874. if (dirty[0] & /*$$slots*/
  15875. 256) {
  15876. transition_in(if_block0, 1);
  15877. }
  15878. } else {
  15879. if_block0 = create_if_block_1$3(ctx2);
  15880. if_block0.c();
  15881. transition_in(if_block0, 1);
  15882. if_block0.m(div2, t0);
  15883. }
  15884. } else if (if_block0) {
  15885. group_outros();
  15886. transition_out(if_block0, 1, 1, () => {
  15887. if_block0 = null;
  15888. });
  15889. check_outros();
  15890. }
  15891. if (dirty[0] & /*displayItems, key, isHorizontal, onItemResized, $$scope*/
  15892. 536870983) {
  15893. each_value = /*displayItems*/
  15894. ctx2[2];
  15895. group_outros();
  15896. 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);
  15897. check_outros();
  15898. }
  15899. if (!current || dirty[0] & /*paddingStyle*/
  15900. 8) {
  15901. set_style(
  15902. div0,
  15903. "padding",
  15904. /*paddingStyle*/
  15905. ctx2[3]
  15906. );
  15907. }
  15908. if (
  15909. /*$$slots*/
  15910. ctx2[8].footer
  15911. ) {
  15912. if (if_block1) {
  15913. if_block1.p(ctx2, dirty);
  15914. if (dirty[0] & /*$$slots*/
  15915. 256) {
  15916. transition_in(if_block1, 1);
  15917. }
  15918. } else {
  15919. if_block1 = create_if_block$6(ctx2);
  15920. if_block1.c();
  15921. transition_in(if_block1, 1);
  15922. if_block1.m(div2, t2);
  15923. }
  15924. } else if (if_block1) {
  15925. group_outros();
  15926. transition_out(if_block1, 1, 1, () => {
  15927. if_block1 = null;
  15928. });
  15929. check_outros();
  15930. }
  15931. if (!current || dirty[0] & /*isHorizontal*/
  15932. 2) {
  15933. set_style(
  15934. div1,
  15935. "width",
  15936. /*isHorizontal*/
  15937. ctx2[1] ? "0px" : "100%"
  15938. );
  15939. }
  15940. if (!current || dirty[0] & /*isHorizontal*/
  15941. 2) {
  15942. set_style(
  15943. div1,
  15944. "height",
  15945. /*isHorizontal*/
  15946. ctx2[1] ? "100%" : "0px"
  15947. );
  15948. }
  15949. },
  15950. i(local) {
  15951. if (current)
  15952. return;
  15953. transition_in(if_block0);
  15954. for (let i = 0; i < each_value.length; i += 1) {
  15955. transition_in(each_blocks[i]);
  15956. }
  15957. transition_in(if_block1);
  15958. current = true;
  15959. },
  15960. o(local) {
  15961. transition_out(if_block0);
  15962. for (let i = 0; i < each_blocks.length; i += 1) {
  15963. transition_out(each_blocks[i]);
  15964. }
  15965. transition_out(if_block1);
  15966. current = false;
  15967. },
  15968. d(detaching) {
  15969. if (detaching)
  15970. detach(div2);
  15971. if (if_block0)
  15972. if_block0.d();
  15973. for (let i = 0; i < each_blocks.length; i += 1) {
  15974. each_blocks[i].d();
  15975. }
  15976. if (if_block1)
  15977. if_block1.d();
  15978. ctx[27](null);
  15979. ctx[28](null);
  15980. mounted = false;
  15981. dispose();
  15982. }
  15983. };
  15984. }
  15985. function instance$g($$self, $$props, $$invalidate) {
  15986. let { $$slots: slots = {}, $$scope } = $$props;
  15987. const $$slots = compute_slots(slots);
  15988. let { key = "id" } = $$props;
  15989. let { data } = $$props;
  15990. let { keeps = 30 } = $$props;
  15991. let { estimateSize = 50 } = $$props;
  15992. let { isHorizontal = false } = $$props;
  15993. let { start = 0 } = $$props;
  15994. let { offset: offset2 = 0 } = $$props;
  15995. let { pageMode = false } = $$props;
  15996. let { topThreshold = 0 } = $$props;
  15997. let { bottomThreshold = 0 } = $$props;
  15998. let displayItems = [];
  15999. let paddingStyle;
  16000. let directionKey = isHorizontal ? "scrollLeft" : "scrollTop";
  16001. let range = null;
  16002. let virtual = new Virtual(
  16003. {
  16004. slotHeaderSize: 0,
  16005. slotFooterSize: 0,
  16006. keeps,
  16007. estimateSize,
  16008. buffer: Math.round(keeps / 3),
  16009. // recommend for a third of keeps
  16010. uniqueIds: getUniqueIdFromDataSources()
  16011. },
  16012. onRangeChanged
  16013. );
  16014. let root;
  16015. let shepherd;
  16016. const dispatch2 = createEventDispatcher();
  16017. function getSize(id) {
  16018. return virtual.sizes.get(id);
  16019. }
  16020. function getSizes() {
  16021. return virtual.sizes.size;
  16022. }
  16023. function getOffset() {
  16024. if (pageMode) {
  16025. return document.documentElement[directionKey] || document.body[directionKey];
  16026. } else {
  16027. return root ? Math.ceil(root[directionKey]) : 0;
  16028. }
  16029. }
  16030. function getClientSize() {
  16031. const key2 = isHorizontal ? "clientWidth" : "clientHeight";
  16032. if (pageMode) {
  16033. return document.documentElement[key2] || document.body[key2];
  16034. } else {
  16035. return root ? Math.ceil(root[key2]) : 0;
  16036. }
  16037. }
  16038. function getScrollSize() {
  16039. const key2 = isHorizontal ? "scrollWidth" : "scrollHeight";
  16040. if (pageMode) {
  16041. return document.documentElement[key2] || document.body[key2];
  16042. } else {
  16043. return root ? Math.ceil(root[key2]) : 0;
  16044. }
  16045. }
  16046. function updatePageModeFront() {
  16047. if (root) {
  16048. const rect = root.getBoundingClientRect();
  16049. const { defaultView } = root.ownerDocument;
  16050. const offsetFront = isHorizontal ? rect.left + defaultView.pageXOffset : rect.top + defaultView.pageYOffset;
  16051. virtual.updateParam("slotHeaderSize", offsetFront);
  16052. }
  16053. }
  16054. function scrollToOffset(offset3) {
  16055. if (pageMode) {
  16056. document.body[directionKey] = offset3;
  16057. document.documentElement[directionKey] = offset3;
  16058. } else if (root) {
  16059. $$invalidate(4, root[directionKey] = offset3, root);
  16060. }
  16061. }
  16062. function scrollToIndex(index) {
  16063. if (index >= data.length - 1) {
  16064. scrollToBottom();
  16065. } else {
  16066. const offset3 = virtual.getOffset(index);
  16067. scrollToOffset(offset3);
  16068. }
  16069. }
  16070. function scrollToBottom() {
  16071. if (shepherd) {
  16072. const offset3 = shepherd[isHorizontal ? "offsetLeft" : "offsetTop"];
  16073. scrollToOffset(offset3);
  16074. setTimeout(
  16075. () => {
  16076. if (getOffset() + getClientSize() + 1 < getScrollSize()) {
  16077. scrollToBottom();
  16078. }
  16079. },
  16080. 3
  16081. );
  16082. }
  16083. }
  16084. onMount(() => {
  16085. if (start) {
  16086. scrollToIndex(start);
  16087. } else if (offset2) {
  16088. scrollToOffset(offset2);
  16089. }
  16090. if (pageMode) {
  16091. updatePageModeFront();
  16092. document.addEventListener("scroll", onScroll, { passive: false });
  16093. }
  16094. });
  16095. onDestroy(() => {
  16096. virtual.destroy();
  16097. if (pageMode) {
  16098. document.removeEventListener("scroll", onScroll);
  16099. }
  16100. });
  16101. function getUniqueIdFromDataSources() {
  16102. return data.map((dataSource) => dataSource[key]);
  16103. }
  16104. function onItemResized(event) {
  16105. const { id, size, type } = event.detail;
  16106. if (type === "item")
  16107. virtual.saveSize(id, size);
  16108. else if (type === "slot") {
  16109. if (id === "header")
  16110. virtual.updateParam("slotHeaderSize", size);
  16111. else if (id === "footer")
  16112. virtual.updateParam("slotFooterSize", size);
  16113. }
  16114. }
  16115. function onRangeChanged(range_) {
  16116. range = range_;
  16117. $$invalidate(3, paddingStyle = $$invalidate(3, paddingStyle = isHorizontal ? `0px ${range.padBehind}px 0px ${range.padFront}px` : `${range.padFront}px 0px ${range.padBehind}px`));
  16118. $$invalidate(2, displayItems = data.slice(range.start, range.end + 1));
  16119. }
  16120. function onScroll(event) {
  16121. const offset3 = getOffset();
  16122. const clientSize = getClientSize();
  16123. const scrollSize = getScrollSize();
  16124. if (offset3 < 0 || offset3 + clientSize > scrollSize || !scrollSize) {
  16125. return;
  16126. }
  16127. virtual.handleScroll(offset3);
  16128. emitEvent(offset3, clientSize, scrollSize, event);
  16129. }
  16130. function emitEvent(offset3, clientSize, scrollSize, event) {
  16131. dispatch2("scroll", { event, range: virtual.getRange() });
  16132. if (virtual.isFront() && !!data.length && offset3 - topThreshold <= 0) {
  16133. dispatch2("top");
  16134. } else if (virtual.isBehind() && offset3 + clientSize + bottomThreshold >= scrollSize) {
  16135. dispatch2("bottom");
  16136. }
  16137. }
  16138. function handleKeepsChange(keeps2) {
  16139. virtual.updateParam("keeps", keeps2);
  16140. virtual.handleSlotSizeChange();
  16141. }
  16142. async function handleDataSourcesChange(data2) {
  16143. virtual.updateParam("uniqueIds", getUniqueIdFromDataSources());
  16144. virtual.handleDataSourcesChange();
  16145. }
  16146. function div1_binding($$value) {
  16147. binding_callbacks[$$value ? "unshift" : "push"](() => {
  16148. shepherd = $$value;
  16149. $$invalidate(5, shepherd);
  16150. });
  16151. }
  16152. function div2_binding($$value) {
  16153. binding_callbacks[$$value ? "unshift" : "push"](() => {
  16154. root = $$value;
  16155. $$invalidate(4, root);
  16156. });
  16157. }
  16158. $$self.$$set = ($$props2) => {
  16159. if ("key" in $$props2)
  16160. $$invalidate(0, key = $$props2.key);
  16161. if ("data" in $$props2)
  16162. $$invalidate(9, data = $$props2.data);
  16163. if ("keeps" in $$props2)
  16164. $$invalidate(10, keeps = $$props2.keeps);
  16165. if ("estimateSize" in $$props2)
  16166. $$invalidate(11, estimateSize = $$props2.estimateSize);
  16167. if ("isHorizontal" in $$props2)
  16168. $$invalidate(1, isHorizontal = $$props2.isHorizontal);
  16169. if ("start" in $$props2)
  16170. $$invalidate(12, start = $$props2.start);
  16171. if ("offset" in $$props2)
  16172. $$invalidate(13, offset2 = $$props2.offset);
  16173. if ("pageMode" in $$props2)
  16174. $$invalidate(14, pageMode = $$props2.pageMode);
  16175. if ("topThreshold" in $$props2)
  16176. $$invalidate(15, topThreshold = $$props2.topThreshold);
  16177. if ("bottomThreshold" in $$props2)
  16178. $$invalidate(16, bottomThreshold = $$props2.bottomThreshold);
  16179. if ("$$scope" in $$props2)
  16180. $$invalidate(29, $$scope = $$props2.$$scope);
  16181. };
  16182. $$self.$$.update = () => {
  16183. if ($$self.$$.dirty[0] & /*offset*/
  16184. 8192) {
  16185. scrollToOffset(offset2);
  16186. }
  16187. if ($$self.$$.dirty[0] & /*start*/
  16188. 4096) {
  16189. scrollToIndex(start);
  16190. }
  16191. if ($$self.$$.dirty[0] & /*keeps*/
  16192. 1024) {
  16193. handleKeepsChange(keeps);
  16194. }
  16195. if ($$self.$$.dirty[0] & /*data*/
  16196. 512) {
  16197. handleDataSourcesChange();
  16198. }
  16199. };
  16200. return [
  16201. key,
  16202. isHorizontal,
  16203. displayItems,
  16204. paddingStyle,
  16205. root,
  16206. shepherd,
  16207. onItemResized,
  16208. onScroll,
  16209. $$slots,
  16210. data,
  16211. keeps,
  16212. estimateSize,
  16213. start,
  16214. offset2,
  16215. pageMode,
  16216. topThreshold,
  16217. bottomThreshold,
  16218. getSize,
  16219. getSizes,
  16220. getOffset,
  16221. getClientSize,
  16222. getScrollSize,
  16223. updatePageModeFront,
  16224. scrollToOffset,
  16225. scrollToIndex,
  16226. scrollToBottom,
  16227. slots,
  16228. div1_binding,
  16229. div2_binding,
  16230. $$scope
  16231. ];
  16232. }
  16233. class VirtualScroll extends SvelteComponent {
  16234. constructor(options) {
  16235. super();
  16236. init(
  16237. this,
  16238. options,
  16239. instance$g,
  16240. create_fragment$g,
  16241. safe_not_equal,
  16242. {
  16243. key: 0,
  16244. data: 9,
  16245. keeps: 10,
  16246. estimateSize: 11,
  16247. isHorizontal: 1,
  16248. start: 12,
  16249. offset: 13,
  16250. pageMode: 14,
  16251. topThreshold: 15,
  16252. bottomThreshold: 16,
  16253. getSize: 17,
  16254. getSizes: 18,
  16255. getOffset: 19,
  16256. getClientSize: 20,
  16257. getScrollSize: 21,
  16258. updatePageModeFront: 22,
  16259. scrollToOffset: 23,
  16260. scrollToIndex: 24,
  16261. scrollToBottom: 25
  16262. },
  16263. null,
  16264. [-1, -1]
  16265. );
  16266. }
  16267. get getSize() {
  16268. return this.$$.ctx[17];
  16269. }
  16270. get getSizes() {
  16271. return this.$$.ctx[18];
  16272. }
  16273. get getOffset() {
  16274. return this.$$.ctx[19];
  16275. }
  16276. get getClientSize() {
  16277. return this.$$.ctx[20];
  16278. }
  16279. get getScrollSize() {
  16280. return this.$$.ctx[21];
  16281. }
  16282. get updatePageModeFront() {
  16283. return this.$$.ctx[22];
  16284. }
  16285. get scrollToOffset() {
  16286. return this.$$.ctx[23];
  16287. }
  16288. get scrollToIndex() {
  16289. return this.$$.ctx[24];
  16290. }
  16291. get scrollToBottom() {
  16292. return this.$$.ctx[25];
  16293. }
  16294. }
  16295. const databaseShell_svelte_svelte_type_style_lang = "";
  16296. function get_each_context$5(ctx, list, i) {
  16297. const child_ctx = ctx.slice();
  16298. child_ctx[41] = list[i];
  16299. child_ctx[43] = i;
  16300. return child_ctx;
  16301. }
  16302. function create_each_block$5(ctx) {
  16303. let option;
  16304. let t_value = (
  16305. /*pack*/
  16306. ctx[41] + ""
  16307. );
  16308. let t;
  16309. let option_value_value;
  16310. return {
  16311. c() {
  16312. option = element("option");
  16313. t = text$1(t_value);
  16314. option.__value = option_value_value = /*pack*/
  16315. ctx[41];
  16316. option.value = option.__value;
  16317. },
  16318. m(target, anchor) {
  16319. insert(target, option, anchor);
  16320. append(option, t);
  16321. },
  16322. p(ctx2, dirty) {
  16323. if (dirty[0] & /*$packStore*/
  16324. 128 && t_value !== (t_value = /*pack*/
  16325. ctx2[41] + ""))
  16326. set_data(t, t_value);
  16327. if (dirty[0] & /*$packStore*/
  16328. 128 && option_value_value !== (option_value_value = /*pack*/
  16329. ctx2[41])) {
  16330. option.__value = option_value_value;
  16331. option.value = option.__value;
  16332. }
  16333. },
  16334. d(detaching) {
  16335. if (detaching)
  16336. detach(option);
  16337. }
  16338. };
  16339. }
  16340. function create_else_block_1(ctx) {
  16341. let div;
  16342. let virtualscroll;
  16343. let current;
  16344. virtualscroll = new VirtualScroll({
  16345. props: {
  16346. data: (
  16347. /*$visibleTreeStore*/
  16348. ctx[9]
  16349. ),
  16350. key: "fullPath",
  16351. $$slots: {
  16352. default: [
  16353. create_default_slot_2,
  16354. ({ data }) => ({ 40: data }),
  16355. ({ data }) => [0, data ? 512 : 0]
  16356. ]
  16357. },
  16358. $$scope: { ctx }
  16359. }
  16360. });
  16361. return {
  16362. c() {
  16363. div = element("div");
  16364. create_component(virtualscroll.$$.fragment);
  16365. attr(div, "class", "sequencer-database-entries-tree svelte-ese-gdt8h0");
  16366. },
  16367. m(target, anchor) {
  16368. insert(target, div, anchor);
  16369. mount_component(virtualscroll, div, null);
  16370. current = true;
  16371. },
  16372. p(ctx2, dirty) {
  16373. const virtualscroll_changes = {};
  16374. if (dirty[0] & /*$visibleTreeStore*/
  16375. 512)
  16376. virtualscroll_changes.data = /*$visibleTreeStore*/
  16377. ctx2[9];
  16378. if (dirty[1] & /*$$scope, data*/
  16379. 8704) {
  16380. virtualscroll_changes.$$scope = { dirty, ctx: ctx2 };
  16381. }
  16382. virtualscroll.$set(virtualscroll_changes);
  16383. },
  16384. i(local) {
  16385. if (current)
  16386. return;
  16387. transition_in(virtualscroll.$$.fragment, local);
  16388. current = true;
  16389. },
  16390. o(local) {
  16391. transition_out(virtualscroll.$$.fragment, local);
  16392. current = false;
  16393. },
  16394. d(detaching) {
  16395. if (detaching)
  16396. detach(div);
  16397. destroy_component(virtualscroll);
  16398. }
  16399. };
  16400. }
  16401. function create_if_block_1$2(ctx) {
  16402. let div;
  16403. let virtualscroll;
  16404. let current;
  16405. virtualscroll = new VirtualScroll({
  16406. props: {
  16407. data: (
  16408. /*filteredEntries*/
  16409. ctx[6]
  16410. ),
  16411. key: "entry",
  16412. $$slots: {
  16413. default: [
  16414. create_default_slot_1,
  16415. ({ data }) => ({ 40: data }),
  16416. ({ data }) => [0, data ? 512 : 0]
  16417. ]
  16418. },
  16419. $$scope: { ctx }
  16420. }
  16421. });
  16422. return {
  16423. c() {
  16424. div = element("div");
  16425. create_component(virtualscroll.$$.fragment);
  16426. attr(div, "class", "sequencer-database-entries svelte-ese-gdt8h0");
  16427. },
  16428. m(target, anchor) {
  16429. insert(target, div, anchor);
  16430. mount_component(virtualscroll, div, null);
  16431. current = true;
  16432. },
  16433. p(ctx2, dirty) {
  16434. const virtualscroll_changes = {};
  16435. if (dirty[0] & /*filteredEntries*/
  16436. 64)
  16437. virtualscroll_changes.data = /*filteredEntries*/
  16438. ctx2[6];
  16439. if (dirty[1] & /*$$scope, data*/
  16440. 8704) {
  16441. virtualscroll_changes.$$scope = { dirty, ctx: ctx2 };
  16442. }
  16443. virtualscroll.$set(virtualscroll_changes);
  16444. },
  16445. i(local) {
  16446. if (current)
  16447. return;
  16448. transition_in(virtualscroll.$$.fragment, local);
  16449. current = true;
  16450. },
  16451. o(local) {
  16452. transition_out(virtualscroll.$$.fragment, local);
  16453. current = false;
  16454. },
  16455. d(detaching) {
  16456. if (detaching)
  16457. detach(div);
  16458. destroy_component(virtualscroll);
  16459. }
  16460. };
  16461. }
  16462. function create_default_slot_2(ctx) {
  16463. let switch_instance;
  16464. let switch_instance_anchor;
  16465. let current;
  16466. var switch_value = (
  16467. /*data*/
  16468. ctx[40].class
  16469. );
  16470. function switch_props(ctx2) {
  16471. return { props: { data: (
  16472. /*data*/
  16473. ctx2[40]
  16474. ) } };
  16475. }
  16476. if (switch_value) {
  16477. switch_instance = construct_svelte_component(switch_value, switch_props(ctx));
  16478. }
  16479. return {
  16480. c() {
  16481. if (switch_instance)
  16482. create_component(switch_instance.$$.fragment);
  16483. switch_instance_anchor = empty();
  16484. },
  16485. m(target, anchor) {
  16486. if (switch_instance)
  16487. mount_component(switch_instance, target, anchor);
  16488. insert(target, switch_instance_anchor, anchor);
  16489. current = true;
  16490. },
  16491. p(ctx2, dirty) {
  16492. const switch_instance_changes = {};
  16493. if (dirty[1] & /*data*/
  16494. 512)
  16495. switch_instance_changes.data = /*data*/
  16496. ctx2[40];
  16497. if (switch_value !== (switch_value = /*data*/
  16498. ctx2[40].class)) {
  16499. if (switch_instance) {
  16500. group_outros();
  16501. const old_component = switch_instance;
  16502. transition_out(old_component.$$.fragment, 1, 0, () => {
  16503. destroy_component(old_component, 1);
  16504. });
  16505. check_outros();
  16506. }
  16507. if (switch_value) {
  16508. switch_instance = construct_svelte_component(switch_value, switch_props(ctx2));
  16509. create_component(switch_instance.$$.fragment);
  16510. transition_in(switch_instance.$$.fragment, 1);
  16511. mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
  16512. } else {
  16513. switch_instance = null;
  16514. }
  16515. } else if (switch_value) {
  16516. switch_instance.$set(switch_instance_changes);
  16517. }
  16518. },
  16519. i(local) {
  16520. if (current)
  16521. return;
  16522. if (switch_instance)
  16523. transition_in(switch_instance.$$.fragment, local);
  16524. current = true;
  16525. },
  16526. o(local) {
  16527. if (switch_instance)
  16528. transition_out(switch_instance.$$.fragment, local);
  16529. current = false;
  16530. },
  16531. d(detaching) {
  16532. if (detaching)
  16533. detach(switch_instance_anchor);
  16534. if (switch_instance)
  16535. destroy_component(switch_instance, detaching);
  16536. }
  16537. };
  16538. }
  16539. function create_default_slot_1(ctx) {
  16540. let databaseentry;
  16541. let current;
  16542. databaseentry = new DatabaseEntry({ props: { entry: (
  16543. /*data*/
  16544. ctx[40].entry
  16545. ) } });
  16546. return {
  16547. c() {
  16548. create_component(databaseentry.$$.fragment);
  16549. },
  16550. m(target, anchor) {
  16551. mount_component(databaseentry, target, anchor);
  16552. current = true;
  16553. },
  16554. p(ctx2, dirty) {
  16555. const databaseentry_changes = {};
  16556. if (dirty[1] & /*data*/
  16557. 512)
  16558. databaseentry_changes.entry = /*data*/
  16559. ctx2[40].entry;
  16560. databaseentry.$set(databaseentry_changes);
  16561. },
  16562. i(local) {
  16563. if (current)
  16564. return;
  16565. transition_in(databaseentry.$$.fragment, local);
  16566. current = true;
  16567. },
  16568. o(local) {
  16569. transition_out(databaseentry.$$.fragment, local);
  16570. current = false;
  16571. },
  16572. d(detaching) {
  16573. destroy_component(databaseentry, detaching);
  16574. }
  16575. };
  16576. }
  16577. function create_else_block$1(ctx) {
  16578. let t;
  16579. return {
  16580. c() {
  16581. t = text$1("No file loaded...");
  16582. },
  16583. m(target, anchor) {
  16584. insert(target, t, anchor);
  16585. },
  16586. p: noop,
  16587. d(detaching) {
  16588. if (detaching)
  16589. detach(t);
  16590. }
  16591. };
  16592. }
  16593. function create_if_block$5(ctx) {
  16594. let t0;
  16595. let t1_value = (
  16596. /*$metadata*/
  16597. ctx[10].type + ""
  16598. );
  16599. let t1;
  16600. let t2;
  16601. let t3_value = (
  16602. /*$metadata*/
  16603. ctx[10].duration + ""
  16604. );
  16605. let t3;
  16606. return {
  16607. c() {
  16608. t0 = text$1("Type: ");
  16609. t1 = text$1(t1_value);
  16610. t2 = text$1(" | Duration: ");
  16611. t3 = text$1(t3_value);
  16612. },
  16613. m(target, anchor) {
  16614. insert(target, t0, anchor);
  16615. insert(target, t1, anchor);
  16616. insert(target, t2, anchor);
  16617. insert(target, t3, anchor);
  16618. },
  16619. p(ctx2, dirty) {
  16620. if (dirty[0] & /*$metadata*/
  16621. 1024 && t1_value !== (t1_value = /*$metadata*/
  16622. ctx2[10].type + ""))
  16623. set_data(t1, t1_value);
  16624. if (dirty[0] & /*$metadata*/
  16625. 1024 && t3_value !== (t3_value = /*$metadata*/
  16626. ctx2[10].duration + ""))
  16627. set_data(t3, t3_value);
  16628. },
  16629. d(detaching) {
  16630. if (detaching)
  16631. detach(t0);
  16632. if (detaching)
  16633. detach(t1);
  16634. if (detaching)
  16635. detach(t2);
  16636. if (detaching)
  16637. detach(t3);
  16638. }
  16639. };
  16640. }
  16641. function create_default_slot$1(ctx) {
  16642. let div4;
  16643. let div0;
  16644. let select;
  16645. let option;
  16646. let t1;
  16647. let input0;
  16648. let t2;
  16649. let input1;
  16650. let t3;
  16651. let label0;
  16652. let t5;
  16653. let input2;
  16654. let t6;
  16655. let label1;
  16656. let t8;
  16657. let input3;
  16658. let t9;
  16659. let label2;
  16660. let t11;
  16661. let div3;
  16662. let current_block_type_index;
  16663. let if_block0;
  16664. let t12;
  16665. let div2;
  16666. let video;
  16667. let t13;
  16668. let img;
  16669. let t14;
  16670. let audio2;
  16671. let t15;
  16672. let div1;
  16673. let current;
  16674. let mounted;
  16675. let dispose;
  16676. let each_value = (
  16677. /*$packStore*/
  16678. ctx[7]
  16679. );
  16680. let each_blocks = [];
  16681. for (let i = 0; i < each_value.length; i += 1) {
  16682. each_blocks[i] = create_each_block$5(get_each_context$5(ctx, each_value, i));
  16683. }
  16684. const if_block_creators = [create_if_block_1$2, create_else_block_1];
  16685. const if_blocks = [];
  16686. function select_block_type(ctx2, dirty) {
  16687. if (
  16688. /*$listView*/
  16689. ctx2[4]
  16690. )
  16691. return 0;
  16692. return 1;
  16693. }
  16694. current_block_type_index = select_block_type(ctx);
  16695. if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
  16696. function select_block_type_1(ctx2, dirty) {
  16697. if (
  16698. /*$metadata*/
  16699. ctx2[10]
  16700. )
  16701. return create_if_block$5;
  16702. return create_else_block$1;
  16703. }
  16704. let current_block_type = select_block_type_1(ctx);
  16705. let if_block1 = current_block_type(ctx);
  16706. return {
  16707. c() {
  16708. div4 = element("div");
  16709. div0 = element("div");
  16710. select = element("select");
  16711. option = element("option");
  16712. option.textContent = `${localize("SEQUENCER.Database.AllPacks")}`;
  16713. for (let i = 0; i < each_blocks.length; i += 1) {
  16714. each_blocks[i].c();
  16715. }
  16716. t1 = space();
  16717. input0 = element("input");
  16718. t2 = space();
  16719. input1 = element("input");
  16720. t3 = space();
  16721. label0 = element("label");
  16722. label0.textContent = `${localize("SEQUENCER.Database.ShowAllRanges")}`;
  16723. t5 = space();
  16724. input2 = element("input");
  16725. t6 = space();
  16726. label1 = element("label");
  16727. label1.textContent = `${localize("SEQUENCER.Database.ShowSubLists")}`;
  16728. t8 = space();
  16729. input3 = element("input");
  16730. t9 = space();
  16731. label2 = element("label");
  16732. label2.textContent = `${localize("SEQUENCER.Database.ListView")}`;
  16733. t11 = space();
  16734. div3 = element("div");
  16735. if_block0.c();
  16736. t12 = space();
  16737. div2 = element("div");
  16738. video = element("video");
  16739. t13 = space();
  16740. img = element("img");
  16741. t14 = space();
  16742. audio2 = element("audio");
  16743. audio2.innerHTML = `<source type="audio/ogg"/>`;
  16744. t15 = space();
  16745. div1 = element("div");
  16746. if_block1.c();
  16747. option.__value = "all";
  16748. option.value = option.__value;
  16749. attr(select, "name", "pack-select");
  16750. attr(select, "class", "svelte-ese-gdt8h0");
  16751. if (
  16752. /*$selectedPackStore*/
  16753. ctx[2] === void 0
  16754. )
  16755. add_render_callback(() => (
  16756. /*select_change_handler*/
  16757. ctx[26].call(select)
  16758. ));
  16759. attr(input0, "class", "ml-2 svelte-ese-gdt8h0");
  16760. attr(input0, "placeholder", localize("SEQUENCER.Database.Search"));
  16761. attr(input0, "type", "text");
  16762. attr(input1, "class", "ml-2");
  16763. attr(input1, "id", "database-all-ranges");
  16764. attr(input1, "type", "checkbox");
  16765. attr(label0, "class", "all-ranges-label svelte-ese-gdt8h0");
  16766. attr(label0, "for", "database-all-ranges");
  16767. attr(input2, "class", "ml-2");
  16768. attr(input2, "id", "include-sub-lists");
  16769. attr(input2, "type", "checkbox");
  16770. attr(label1, "class", "all-ranges-label svelte-ese-gdt8h0");
  16771. attr(label1, "for", "include-sub-lists");
  16772. attr(input3, "class", "ml-2");
  16773. attr(input3, "id", "treeview");
  16774. attr(input3, "type", "checkbox");
  16775. attr(label2, "class", "all-ranges-label svelte-ese-gdt8h0");
  16776. attr(label2, "for", "treeview");
  16777. attr(div0, "class", "sequencer-database-header svelte-ese-gdt8h0");
  16778. video.autoplay = true;
  16779. attr(video, "class", "database-player svelte-ese-gdt8h0");
  16780. attr(video, "height", "335");
  16781. video.loop = true;
  16782. attr(video, "preload", "");
  16783. attr(video, "width", "335");
  16784. attr(img, "class", "database-image hidden svelte-ese-gdt8h0");
  16785. attr(audio2, "class", "database-audio hidden svelte-ese-gdt8h0");
  16786. attr(div1, "class", "sequencer-database-metadata-container svelte-ese-gdt8h0");
  16787. attr(div2, "class", "sequencer-database-player-container svelte-ese-gdt8h0");
  16788. attr(div3, "class", "sequencer-database-entries-container svelte-ese-gdt8h0");
  16789. attr(div4, "class", "sequencer-database-content svelte-ese-gdt8h0");
  16790. },
  16791. m(target, anchor) {
  16792. insert(target, div4, anchor);
  16793. append(div4, div0);
  16794. append(div0, select);
  16795. append(select, option);
  16796. for (let i = 0; i < each_blocks.length; i += 1) {
  16797. each_blocks[i].m(select, null);
  16798. }
  16799. select_option(
  16800. select,
  16801. /*$selectedPackStore*/
  16802. ctx[2]
  16803. );
  16804. append(div0, t1);
  16805. append(div0, input0);
  16806. set_input_value(
  16807. input0,
  16808. /*$search*/
  16809. ctx[1]
  16810. );
  16811. append(div0, t2);
  16812. append(div0, input1);
  16813. input1.checked = /*$allRanges*/
  16814. ctx[3];
  16815. append(div0, t3);
  16816. append(div0, label0);
  16817. append(div0, t5);
  16818. append(div0, input2);
  16819. input2.checked = /*$subLists*/
  16820. ctx[8];
  16821. append(div0, t6);
  16822. append(div0, label1);
  16823. append(div0, t8);
  16824. append(div0, input3);
  16825. input3.checked = /*$listView*/
  16826. ctx[4];
  16827. append(div0, t9);
  16828. append(div0, label2);
  16829. append(div4, t11);
  16830. append(div4, div3);
  16831. if_blocks[current_block_type_index].m(div3, null);
  16832. append(div3, t12);
  16833. append(div3, div2);
  16834. append(div2, video);
  16835. ctx[31](video);
  16836. append(div2, t13);
  16837. append(div2, img);
  16838. ctx[34](img);
  16839. append(div2, t14);
  16840. append(div2, audio2);
  16841. ctx[35](audio2);
  16842. append(div2, t15);
  16843. append(div2, div1);
  16844. if_block1.m(div1, null);
  16845. current = true;
  16846. if (!mounted) {
  16847. dispose = [
  16848. listen(
  16849. select,
  16850. "change",
  16851. /*select_change_handler*/
  16852. ctx[26]
  16853. ),
  16854. listen(
  16855. input0,
  16856. "input",
  16857. /*input0_input_handler*/
  16858. ctx[27]
  16859. ),
  16860. listen(
  16861. input1,
  16862. "change",
  16863. /*input1_change_handler*/
  16864. ctx[28]
  16865. ),
  16866. listen(
  16867. input2,
  16868. "change",
  16869. /*input2_change_handler*/
  16870. ctx[29]
  16871. ),
  16872. listen(
  16873. input3,
  16874. "change",
  16875. /*input3_change_handler*/
  16876. ctx[30]
  16877. ),
  16878. listen(
  16879. video,
  16880. "mouseenter",
  16881. /*mouseenter_handler*/
  16882. ctx[32]
  16883. ),
  16884. listen(
  16885. video,
  16886. "mouseleave",
  16887. /*mouseleave_handler*/
  16888. ctx[33]
  16889. ),
  16890. listen(
  16891. audio2,
  16892. "mouseenter",
  16893. /*mouseenter_handler_1*/
  16894. ctx[36]
  16895. ),
  16896. listen(
  16897. audio2,
  16898. "mouseleave",
  16899. /*mouseleave_handler_1*/
  16900. ctx[37]
  16901. )
  16902. ];
  16903. mounted = true;
  16904. }
  16905. },
  16906. p(ctx2, dirty) {
  16907. if (dirty[0] & /*$packStore*/
  16908. 128) {
  16909. each_value = /*$packStore*/
  16910. ctx2[7];
  16911. let i;
  16912. for (i = 0; i < each_value.length; i += 1) {
  16913. const child_ctx = get_each_context$5(ctx2, each_value, i);
  16914. if (each_blocks[i]) {
  16915. each_blocks[i].p(child_ctx, dirty);
  16916. } else {
  16917. each_blocks[i] = create_each_block$5(child_ctx);
  16918. each_blocks[i].c();
  16919. each_blocks[i].m(select, null);
  16920. }
  16921. }
  16922. for (; i < each_blocks.length; i += 1) {
  16923. each_blocks[i].d(1);
  16924. }
  16925. each_blocks.length = each_value.length;
  16926. }
  16927. if (dirty[0] & /*$selectedPackStore, $packStore*/
  16928. 132) {
  16929. select_option(
  16930. select,
  16931. /*$selectedPackStore*/
  16932. ctx2[2]
  16933. );
  16934. }
  16935. if (dirty[0] & /*$search*/
  16936. 2 && input0.value !== /*$search*/
  16937. ctx2[1]) {
  16938. set_input_value(
  16939. input0,
  16940. /*$search*/
  16941. ctx2[1]
  16942. );
  16943. }
  16944. if (dirty[0] & /*$allRanges*/
  16945. 8) {
  16946. input1.checked = /*$allRanges*/
  16947. ctx2[3];
  16948. }
  16949. if (dirty[0] & /*$subLists*/
  16950. 256) {
  16951. input2.checked = /*$subLists*/
  16952. ctx2[8];
  16953. }
  16954. if (dirty[0] & /*$listView*/
  16955. 16) {
  16956. input3.checked = /*$listView*/
  16957. ctx2[4];
  16958. }
  16959. let previous_block_index = current_block_type_index;
  16960. current_block_type_index = select_block_type(ctx2);
  16961. if (current_block_type_index === previous_block_index) {
  16962. if_blocks[current_block_type_index].p(ctx2, dirty);
  16963. } else {
  16964. group_outros();
  16965. transition_out(if_blocks[previous_block_index], 1, 1, () => {
  16966. if_blocks[previous_block_index] = null;
  16967. });
  16968. check_outros();
  16969. if_block0 = if_blocks[current_block_type_index];
  16970. if (!if_block0) {
  16971. if_block0 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
  16972. if_block0.c();
  16973. } else {
  16974. if_block0.p(ctx2, dirty);
  16975. }
  16976. transition_in(if_block0, 1);
  16977. if_block0.m(div3, t12);
  16978. }
  16979. if (current_block_type === (current_block_type = select_block_type_1(ctx2)) && if_block1) {
  16980. if_block1.p(ctx2, dirty);
  16981. } else {
  16982. if_block1.d(1);
  16983. if_block1 = current_block_type(ctx2);
  16984. if (if_block1) {
  16985. if_block1.c();
  16986. if_block1.m(div1, null);
  16987. }
  16988. }
  16989. },
  16990. i(local) {
  16991. if (current)
  16992. return;
  16993. transition_in(if_block0);
  16994. current = true;
  16995. },
  16996. o(local) {
  16997. transition_out(if_block0);
  16998. current = false;
  16999. },
  17000. d(detaching) {
  17001. if (detaching)
  17002. detach(div4);
  17003. destroy_each(each_blocks, detaching);
  17004. if_blocks[current_block_type_index].d();
  17005. ctx[31](null);
  17006. ctx[34](null);
  17007. ctx[35](null);
  17008. if_block1.d();
  17009. mounted = false;
  17010. run_all(dispose);
  17011. }
  17012. };
  17013. }
  17014. function create_fragment$f(ctx) {
  17015. let applicationshell;
  17016. let updating_elementRoot;
  17017. let current;
  17018. function applicationshell_elementRoot_binding(value) {
  17019. ctx[38](value);
  17020. }
  17021. let applicationshell_props = {
  17022. $$slots: { default: [create_default_slot$1] },
  17023. $$scope: { ctx }
  17024. };
  17025. if (
  17026. /*elementRoot*/
  17027. ctx[0] !== void 0
  17028. ) {
  17029. applicationshell_props.elementRoot = /*elementRoot*/
  17030. ctx[0];
  17031. }
  17032. applicationshell = new ApplicationShell({ props: applicationshell_props });
  17033. binding_callbacks.push(() => bind(applicationshell, "elementRoot", applicationshell_elementRoot_binding));
  17034. return {
  17035. c() {
  17036. create_component(applicationshell.$$.fragment);
  17037. },
  17038. m(target, anchor) {
  17039. mount_component(applicationshell, target, anchor);
  17040. current = true;
  17041. },
  17042. p(ctx2, dirty) {
  17043. const applicationshell_changes = {};
  17044. if (dirty[0] & /*$metadata, databaseStore, filteredEntries, $listView, $visibleTreeStore, $subLists, $allRanges, $search, $selectedPackStore, $packStore*/
  17045. 2046 | dirty[1] & /*$$scope*/
  17046. 8192) {
  17047. applicationshell_changes.$$scope = { dirty, ctx: ctx2 };
  17048. }
  17049. if (!updating_elementRoot && dirty[0] & /*elementRoot*/
  17050. 1) {
  17051. updating_elementRoot = true;
  17052. applicationshell_changes.elementRoot = /*elementRoot*/
  17053. ctx2[0];
  17054. add_flush_callback(() => updating_elementRoot = false);
  17055. }
  17056. applicationshell.$set(applicationshell_changes);
  17057. },
  17058. i(local) {
  17059. if (current)
  17060. return;
  17061. transition_in(applicationshell.$$.fragment, local);
  17062. current = true;
  17063. },
  17064. o(local) {
  17065. transition_out(applicationshell.$$.fragment, local);
  17066. current = false;
  17067. },
  17068. d(detaching) {
  17069. destroy_component(applicationshell, detaching);
  17070. }
  17071. };
  17072. }
  17073. function instance$f($$self, $$props, $$invalidate) {
  17074. let $search;
  17075. let $selectedPackStore;
  17076. let $searchRegex;
  17077. let $cleanSearchStore;
  17078. let $allRanges;
  17079. let $entriesStore;
  17080. let $listView;
  17081. let $packStore;
  17082. let $subLists;
  17083. let $visibleTreeStore;
  17084. let $metadata;
  17085. getContext("#external");
  17086. let { elementRoot } = $$props;
  17087. let entries = [];
  17088. let filteredEntries = [];
  17089. const selectedPackStore2 = databaseStore.selectedPackStore;
  17090. component_subscribe($$self, selectedPackStore2, (value) => $$invalidate(2, $selectedPackStore = value));
  17091. const packStore2 = databaseStore.packStore;
  17092. component_subscribe($$self, packStore2, (value) => $$invalidate(7, $packStore = value));
  17093. const metadata = databaseStore.metadata;
  17094. component_subscribe($$self, metadata, (value) => $$invalidate(10, $metadata = value));
  17095. const allRanges = databaseStore.allRanges;
  17096. component_subscribe($$self, allRanges, (value) => $$invalidate(3, $allRanges = value));
  17097. const subLists = databaseStore.subLists;
  17098. component_subscribe($$self, subLists, (value) => $$invalidate(8, $subLists = value));
  17099. const listView = databaseStore.listView;
  17100. component_subscribe($$self, listView, (value) => $$invalidate(4, $listView = value));
  17101. const visibleTreeStore2 = databaseStore.visibleTreeStore;
  17102. component_subscribe($$self, visibleTreeStore2, (value) => $$invalidate(9, $visibleTreeStore = value));
  17103. const search = databaseStore.search;
  17104. component_subscribe($$self, search, (value) => $$invalidate(1, $search = value));
  17105. const cleanSearchStore2 = databaseStore.cleanSearchStore;
  17106. component_subscribe($$self, cleanSearchStore2, (value) => $$invalidate(24, $cleanSearchStore = value));
  17107. const searchRegex = databaseStore.searchRegex;
  17108. component_subscribe($$self, searchRegex, (value) => $$invalidate(23, $searchRegex = value));
  17109. const entriesStore2 = SequencerDatabase.entriesStore;
  17110. component_subscribe($$self, entriesStore2, (value) => $$invalidate(25, $entriesStore = value));
  17111. listView.set(game.settings.get(CONSTANTS.MODULE_NAME, "db-list-view"));
  17112. function select_change_handler() {
  17113. $selectedPackStore = select_value(this);
  17114. selectedPackStore2.set($selectedPackStore);
  17115. }
  17116. function input0_input_handler() {
  17117. $search = this.value;
  17118. search.set($search);
  17119. }
  17120. function input1_change_handler() {
  17121. $allRanges = this.checked;
  17122. allRanges.set($allRanges);
  17123. }
  17124. function input2_change_handler() {
  17125. $subLists = this.checked;
  17126. subLists.set($subLists);
  17127. }
  17128. function input3_change_handler() {
  17129. $listView = this.checked;
  17130. listView.set($listView);
  17131. }
  17132. function video_binding($$value) {
  17133. binding_callbacks[$$value ? "unshift" : "push"](() => {
  17134. databaseStore.elements.player = $$value;
  17135. $$invalidate(5, databaseStore);
  17136. });
  17137. }
  17138. const mouseenter_handler = () => {
  17139. $$invalidate(5, databaseStore.elements.player.controls = true, databaseStore);
  17140. };
  17141. const mouseleave_handler = () => {
  17142. $$invalidate(5, databaseStore.elements.player.controls = false, databaseStore);
  17143. };
  17144. function img_binding($$value) {
  17145. binding_callbacks[$$value ? "unshift" : "push"](() => {
  17146. databaseStore.elements.image = $$value;
  17147. $$invalidate(5, databaseStore);
  17148. });
  17149. }
  17150. function audio_binding($$value) {
  17151. binding_callbacks[$$value ? "unshift" : "push"](() => {
  17152. databaseStore.elements.audio = $$value;
  17153. $$invalidate(5, databaseStore);
  17154. });
  17155. }
  17156. const mouseenter_handler_1 = () => {
  17157. $$invalidate(5, databaseStore.elements.audio.controls = true, databaseStore);
  17158. };
  17159. const mouseleave_handler_1 = () => {
  17160. $$invalidate(5, databaseStore.elements.audio.controls = false, databaseStore);
  17161. };
  17162. function applicationshell_elementRoot_binding(value) {
  17163. elementRoot = value;
  17164. $$invalidate(0, elementRoot);
  17165. }
  17166. $$self.$$set = ($$props2) => {
  17167. if ("elementRoot" in $$props2)
  17168. $$invalidate(0, elementRoot = $$props2.elementRoot);
  17169. };
  17170. $$self.$$.update = () => {
  17171. if ($$self.$$.dirty[0] & /*$listView*/
  17172. 16) {
  17173. game.settings.set(CONSTANTS.MODULE_NAME, "db-list-view", $listView);
  17174. }
  17175. if ($$self.$$.dirty[0] & /*$entriesStore, $allRanges*/
  17176. 33554440) {
  17177. {
  17178. const specificRanges = $allRanges ? Sequencer.Database.publicFlattenedEntries : Sequencer.Database.publicFlattenedSimpleEntries;
  17179. $$invalidate(22, entries = specificRanges.map((entry) => {
  17180. return { pack: entry.split(".")[0], entry };
  17181. }));
  17182. }
  17183. }
  17184. if ($$self.$$.dirty[0] & /*$cleanSearchStore, entries, $searchRegex, $selectedPackStore, $search*/
  17185. 29360134) {
  17186. {
  17187. const searchParts = $cleanSearchStore.split("|");
  17188. $$invalidate(6, filteredEntries = entries.filter((part) => {
  17189. const matchParts = make_array_unique(part.entry.match($searchRegex) || []);
  17190. return ($selectedPackStore === "all" || $selectedPackStore === part.pack) && ($search === "" || matchParts.length >= searchParts.length);
  17191. }));
  17192. }
  17193. }
  17194. };
  17195. return [
  17196. elementRoot,
  17197. $search,
  17198. $selectedPackStore,
  17199. $allRanges,
  17200. $listView,
  17201. databaseStore,
  17202. filteredEntries,
  17203. $packStore,
  17204. $subLists,
  17205. $visibleTreeStore,
  17206. $metadata,
  17207. selectedPackStore2,
  17208. packStore2,
  17209. metadata,
  17210. allRanges,
  17211. subLists,
  17212. listView,
  17213. visibleTreeStore2,
  17214. search,
  17215. cleanSearchStore2,
  17216. searchRegex,
  17217. entriesStore2,
  17218. entries,
  17219. $searchRegex,
  17220. $cleanSearchStore,
  17221. $entriesStore,
  17222. select_change_handler,
  17223. input0_input_handler,
  17224. input1_change_handler,
  17225. input2_change_handler,
  17226. input3_change_handler,
  17227. video_binding,
  17228. mouseenter_handler,
  17229. mouseleave_handler,
  17230. img_binding,
  17231. audio_binding,
  17232. mouseenter_handler_1,
  17233. mouseleave_handler_1,
  17234. applicationshell_elementRoot_binding
  17235. ];
  17236. }
  17237. class Database_shell extends SvelteComponent {
  17238. constructor(options) {
  17239. super();
  17240. init(this, options, instance$f, create_fragment$f, safe_not_equal, { elementRoot: 0 }, null, [-1, -1]);
  17241. }
  17242. get elementRoot() {
  17243. return this.$$.ctx[0];
  17244. }
  17245. set elementRoot(elementRoot) {
  17246. this.$$set({ elementRoot });
  17247. flush();
  17248. }
  17249. }
  17250. class DatabaseViewerApp extends SvelteApplication {
  17251. static get defaultOptions() {
  17252. return foundry.utils.mergeObject(super.defaultOptions, {
  17253. title: game.i18n.localize("SEQUENCER.Database.Title"),
  17254. classes: ["dialog"],
  17255. width: 900,
  17256. height: 425,
  17257. svelte: {
  17258. class: Database_shell,
  17259. target: document.body
  17260. }
  17261. });
  17262. }
  17263. static getActiveApp() {
  17264. return Object.values(ui.windows).find((app) => {
  17265. return app instanceof this && app._state > Application.RENDER_STATES.CLOSED;
  17266. });
  17267. }
  17268. static async show(options = {}) {
  17269. const existingApp = this.getActiveApp();
  17270. if (existingApp)
  17271. return existingApp.render(false, { focus: true });
  17272. return new Promise((resolve) => {
  17273. options.resolve = resolve;
  17274. new this(options).render(true, { focus: true });
  17275. });
  17276. }
  17277. }
  17278. const HowTo_svelte_svelte_type_style_lang = "";
  17279. function create_if_block$4(ctx) {
  17280. let p;
  17281. let raw0_value = localize("SEQUENCER.HowTo.PermissionsExplanation") + "";
  17282. let t0;
  17283. let button;
  17284. let i;
  17285. let t1;
  17286. let html_tag;
  17287. let raw1_value = localize("SEQUENCER.HowTo.OpenModuleSettings") + "";
  17288. let mounted;
  17289. let dispose;
  17290. return {
  17291. c() {
  17292. p = element("p");
  17293. t0 = space();
  17294. button = element("button");
  17295. i = element("i");
  17296. t1 = space();
  17297. html_tag = new HtmlTag(false);
  17298. attr(i, "class", "fas fa-plug");
  17299. html_tag.a = null;
  17300. attr(button, "type", "button");
  17301. attr(button, "class", "w-100 open-module-settings");
  17302. },
  17303. m(target, anchor) {
  17304. insert(target, p, anchor);
  17305. p.innerHTML = raw0_value;
  17306. insert(target, t0, anchor);
  17307. insert(target, button, anchor);
  17308. append(button, i);
  17309. append(button, t1);
  17310. html_tag.m(raw1_value, button);
  17311. if (!mounted) {
  17312. dispose = listen(
  17313. button,
  17314. "click",
  17315. /*openSettings*/
  17316. ctx[0]
  17317. );
  17318. mounted = true;
  17319. }
  17320. },
  17321. p: noop,
  17322. d(detaching) {
  17323. if (detaching)
  17324. detach(p);
  17325. if (detaching)
  17326. detach(t0);
  17327. if (detaching)
  17328. detach(button);
  17329. mounted = false;
  17330. dispose();
  17331. }
  17332. };
  17333. }
  17334. function create_fragment$e(ctx) {
  17335. let div;
  17336. let p0;
  17337. let raw0_value = localize("SEQUENCER.HowTo.Welcome") + "";
  17338. let t0;
  17339. let p1;
  17340. let raw1_value = localize("SEQUENCER.HowTo.Explanation") + "";
  17341. let t1;
  17342. let t2;
  17343. let p2;
  17344. let raw2_value = localize("SEQUENCER.HowTo.PlayerExplanation") + "";
  17345. let t3;
  17346. let p3;
  17347. let raw3_value = localize("SEQUENCER.HowTo.LayerExplanation") + "";
  17348. let t4;
  17349. let ol;
  17350. let li0;
  17351. let strong0;
  17352. let raw4_value = localize("SEQUENCER.HowTo.Click") + "";
  17353. let br0;
  17354. let t5;
  17355. let html_tag;
  17356. let raw5_value = localize("SEQUENCER.HowTo.ClickLabel") + "";
  17357. let t6;
  17358. let li1;
  17359. let strong1;
  17360. let raw6_value = localize("SEQUENCER.HowTo.ClickDrag") + "";
  17361. let br1;
  17362. let t7;
  17363. let html_tag_1;
  17364. let raw7_value = localize("SEQUENCER.HowTo.ClickDragLabel") + "";
  17365. let t8;
  17366. let li2;
  17367. let strong2;
  17368. let raw8_value = localize("SEQUENCER.HowTo.Shift") + "";
  17369. let br2;
  17370. let t9;
  17371. let html_tag_2;
  17372. let raw9_value = localize("SEQUENCER.HowTo.ShiftLabel") + "";
  17373. let t10;
  17374. let li3;
  17375. let strong3;
  17376. let raw10_value = localize("SEQUENCER.HowTo.ShiftControl") + "";
  17377. let br3;
  17378. let t11;
  17379. let html_tag_3;
  17380. let raw11_value = localize("SEQUENCER.HowTo.ShiftControlLabel") + "";
  17381. let t12;
  17382. let li4;
  17383. let strong4;
  17384. let raw12_value = localize("SEQUENCER.HowTo.MoreToCome") + "";
  17385. let if_block = game.user.isGM && create_if_block$4(ctx);
  17386. return {
  17387. c() {
  17388. div = element("div");
  17389. p0 = element("p");
  17390. t0 = space();
  17391. p1 = element("p");
  17392. t1 = space();
  17393. if (if_block)
  17394. if_block.c();
  17395. t2 = space();
  17396. p2 = element("p");
  17397. t3 = space();
  17398. p3 = element("p");
  17399. t4 = space();
  17400. ol = element("ol");
  17401. li0 = element("li");
  17402. strong0 = element("strong");
  17403. br0 = element("br");
  17404. t5 = space();
  17405. html_tag = new HtmlTag(false);
  17406. t6 = space();
  17407. li1 = element("li");
  17408. strong1 = element("strong");
  17409. br1 = element("br");
  17410. t7 = space();
  17411. html_tag_1 = new HtmlTag(false);
  17412. t8 = space();
  17413. li2 = element("li");
  17414. strong2 = element("strong");
  17415. br2 = element("br");
  17416. t9 = space();
  17417. html_tag_2 = new HtmlTag(false);
  17418. t10 = space();
  17419. li3 = element("li");
  17420. strong3 = element("strong");
  17421. br3 = element("br");
  17422. t11 = space();
  17423. html_tag_3 = new HtmlTag(false);
  17424. t12 = space();
  17425. li4 = element("li");
  17426. strong4 = element("strong");
  17427. html_tag.a = null;
  17428. attr(li0, "class", "svelte-ese-1gfsmnk");
  17429. html_tag_1.a = null;
  17430. attr(li1, "class", "svelte-ese-1gfsmnk");
  17431. html_tag_2.a = null;
  17432. attr(li2, "class", "svelte-ese-1gfsmnk");
  17433. html_tag_3.a = null;
  17434. attr(li3, "class", "svelte-ese-1gfsmnk");
  17435. attr(li4, "class", "svelte-ese-1gfsmnk");
  17436. attr(div, "class", "howto-container svelte-ese-1gfsmnk");
  17437. },
  17438. m(target, anchor) {
  17439. insert(target, div, anchor);
  17440. append(div, p0);
  17441. p0.innerHTML = raw0_value;
  17442. append(div, t0);
  17443. append(div, p1);
  17444. p1.innerHTML = raw1_value;
  17445. append(div, t1);
  17446. if (if_block)
  17447. if_block.m(div, null);
  17448. append(div, t2);
  17449. append(div, p2);
  17450. p2.innerHTML = raw2_value;
  17451. append(div, t3);
  17452. append(div, p3);
  17453. p3.innerHTML = raw3_value;
  17454. append(div, t4);
  17455. append(div, ol);
  17456. append(ol, li0);
  17457. append(li0, strong0);
  17458. strong0.innerHTML = raw4_value;
  17459. append(li0, br0);
  17460. append(li0, t5);
  17461. html_tag.m(raw5_value, li0);
  17462. append(ol, t6);
  17463. append(ol, li1);
  17464. append(li1, strong1);
  17465. strong1.innerHTML = raw6_value;
  17466. append(li1, br1);
  17467. append(li1, t7);
  17468. html_tag_1.m(raw7_value, li1);
  17469. append(ol, t8);
  17470. append(ol, li2);
  17471. append(li2, strong2);
  17472. strong2.innerHTML = raw8_value;
  17473. append(li2, br2);
  17474. append(li2, t9);
  17475. html_tag_2.m(raw9_value, li2);
  17476. append(ol, t10);
  17477. append(ol, li3);
  17478. append(li3, strong3);
  17479. strong3.innerHTML = raw10_value;
  17480. append(li3, br3);
  17481. append(li3, t11);
  17482. html_tag_3.m(raw11_value, li3);
  17483. append(ol, t12);
  17484. append(ol, li4);
  17485. append(li4, strong4);
  17486. strong4.innerHTML = raw12_value;
  17487. },
  17488. p(ctx2, [dirty]) {
  17489. if (game.user.isGM)
  17490. if_block.p(ctx2, dirty);
  17491. },
  17492. i: noop,
  17493. o: noop,
  17494. d(detaching) {
  17495. if (detaching)
  17496. detach(div);
  17497. if (if_block)
  17498. if_block.d();
  17499. }
  17500. };
  17501. }
  17502. function instance$e($$self) {
  17503. async function openSettings() {
  17504. const settings = new SettingsConfig();
  17505. await settings.render(true);
  17506. await wait$1(75);
  17507. const focusElement = settings.element.find('select[name="sequencer.permissions-effect-create"]');
  17508. settings.element.find("div.scrollable").scrollTop(focusElement.offset().top);
  17509. await wait$1(250);
  17510. focusElement.css("box-shadow", "rgba(200, 64, 67, 0.75) inset 0 0 10px");
  17511. await wait$1(5e3);
  17512. focusElement.css("box-shadow", "none");
  17513. }
  17514. return [openSettings];
  17515. }
  17516. class HowTo extends SvelteComponent {
  17517. constructor(options) {
  17518. super();
  17519. init(this, options, instance$e, create_fragment$e, safe_not_equal, {});
  17520. }
  17521. }
  17522. const Tabs_svelte_svelte_type_style_lang = "";
  17523. function get_each_context$4(ctx, list, i) {
  17524. const child_ctx = ctx.slice();
  17525. child_ctx[6] = list[i];
  17526. child_ctx[8] = i;
  17527. return child_ctx;
  17528. }
  17529. function create_if_block_1$1(ctx) {
  17530. let div;
  17531. return {
  17532. c() {
  17533. div = element("div");
  17534. set_style(div, "border-right", "1px solid rgba(0,0,0,0.5)");
  17535. set_style(div, "margin", "0 10px");
  17536. },
  17537. m(target, anchor) {
  17538. insert(target, div, anchor);
  17539. },
  17540. d(detaching) {
  17541. if (detaching)
  17542. detach(div);
  17543. }
  17544. };
  17545. }
  17546. function create_if_block$3(ctx) {
  17547. let i;
  17548. let i_class_value;
  17549. return {
  17550. c() {
  17551. i = element("i");
  17552. attr(i, "class", i_class_value = "icon " + /*tab*/
  17553. ctx[6].icon + " svelte-ese-123fyp");
  17554. },
  17555. m(target, anchor) {
  17556. insert(target, i, anchor);
  17557. },
  17558. p(ctx2, dirty) {
  17559. if (dirty & /*tabs*/
  17560. 2 && i_class_value !== (i_class_value = "icon " + /*tab*/
  17561. ctx2[6].icon + " svelte-ese-123fyp")) {
  17562. attr(i, "class", i_class_value);
  17563. }
  17564. },
  17565. d(detaching) {
  17566. if (detaching)
  17567. detach(i);
  17568. }
  17569. };
  17570. }
  17571. function create_each_block$4(key_1, ctx) {
  17572. let first;
  17573. let t0;
  17574. let div;
  17575. let t1;
  17576. let t2_value = localize(
  17577. /*tab*/
  17578. ctx[6].label
  17579. ) + "";
  17580. let t2;
  17581. let t3;
  17582. let mounted;
  17583. let dispose;
  17584. let if_block0 = (
  17585. /*separateElements*/
  17586. ctx[3] && /*index*/
  17587. ctx[8] > 0 && create_if_block_1$1()
  17588. );
  17589. let if_block1 = (
  17590. /*tab*/
  17591. ctx[6].icon && create_if_block$3(ctx)
  17592. );
  17593. function click_handler() {
  17594. return (
  17595. /*click_handler*/
  17596. ctx[5](
  17597. /*tab*/
  17598. ctx[6]
  17599. )
  17600. );
  17601. }
  17602. return {
  17603. key: key_1,
  17604. first: null,
  17605. c() {
  17606. first = empty();
  17607. if (if_block0)
  17608. if_block0.c();
  17609. t0 = space();
  17610. div = element("div");
  17611. if (if_block1)
  17612. if_block1.c();
  17613. t1 = space();
  17614. t2 = text$1(t2_value);
  17615. t3 = space();
  17616. attr(div, "class", "item item-piles-flexrow item-piles-clickable-link svelte-ese-123fyp");
  17617. attr(div, "data-tab", "rest");
  17618. toggle_class(
  17619. div,
  17620. "underscore",
  17621. /*underscore*/
  17622. ctx[2]
  17623. );
  17624. toggle_class(
  17625. div,
  17626. "active",
  17627. /*activeTab*/
  17628. ctx[0] === /*tab*/
  17629. ctx[6].value
  17630. );
  17631. this.first = first;
  17632. },
  17633. m(target, anchor) {
  17634. insert(target, first, anchor);
  17635. if (if_block0)
  17636. if_block0.m(target, anchor);
  17637. insert(target, t0, anchor);
  17638. insert(target, div, anchor);
  17639. if (if_block1)
  17640. if_block1.m(div, null);
  17641. append(div, t1);
  17642. append(div, t2);
  17643. append(div, t3);
  17644. if (!mounted) {
  17645. dispose = listen(div, "click", click_handler);
  17646. mounted = true;
  17647. }
  17648. },
  17649. p(new_ctx, dirty) {
  17650. ctx = new_ctx;
  17651. if (
  17652. /*separateElements*/
  17653. ctx[3] && /*index*/
  17654. ctx[8] > 0
  17655. ) {
  17656. if (if_block0)
  17657. ;
  17658. else {
  17659. if_block0 = create_if_block_1$1();
  17660. if_block0.c();
  17661. if_block0.m(t0.parentNode, t0);
  17662. }
  17663. } else if (if_block0) {
  17664. if_block0.d(1);
  17665. if_block0 = null;
  17666. }
  17667. if (
  17668. /*tab*/
  17669. ctx[6].icon
  17670. ) {
  17671. if (if_block1) {
  17672. if_block1.p(ctx, dirty);
  17673. } else {
  17674. if_block1 = create_if_block$3(ctx);
  17675. if_block1.c();
  17676. if_block1.m(div, t1);
  17677. }
  17678. } else if (if_block1) {
  17679. if_block1.d(1);
  17680. if_block1 = null;
  17681. }
  17682. if (dirty & /*tabs*/
  17683. 2 && t2_value !== (t2_value = localize(
  17684. /*tab*/
  17685. ctx[6].label
  17686. ) + ""))
  17687. set_data(t2, t2_value);
  17688. if (dirty & /*underscore*/
  17689. 4) {
  17690. toggle_class(
  17691. div,
  17692. "underscore",
  17693. /*underscore*/
  17694. ctx[2]
  17695. );
  17696. }
  17697. if (dirty & /*activeTab, tabs*/
  17698. 3) {
  17699. toggle_class(
  17700. div,
  17701. "active",
  17702. /*activeTab*/
  17703. ctx[0] === /*tab*/
  17704. ctx[6].value
  17705. );
  17706. }
  17707. },
  17708. d(detaching) {
  17709. if (detaching)
  17710. detach(first);
  17711. if (if_block0)
  17712. if_block0.d(detaching);
  17713. if (detaching)
  17714. detach(t0);
  17715. if (detaching)
  17716. detach(div);
  17717. if (if_block1)
  17718. if_block1.d();
  17719. mounted = false;
  17720. dispose();
  17721. }
  17722. };
  17723. }
  17724. function create_fragment$d(ctx) {
  17725. let nav;
  17726. let each_blocks = [];
  17727. let each_1_lookup = /* @__PURE__ */ new Map();
  17728. let nav_style_value;
  17729. let each_value = (
  17730. /*tabs*/
  17731. ctx[1].filter(func)
  17732. );
  17733. const get_key = (ctx2) => (
  17734. /*tab*/
  17735. ctx2[6].value
  17736. );
  17737. for (let i = 0; i < each_value.length; i += 1) {
  17738. let child_ctx = get_each_context$4(ctx, each_value, i);
  17739. let key = get_key(child_ctx);
  17740. each_1_lookup.set(key, each_blocks[i] = create_each_block$4(key, child_ctx));
  17741. }
  17742. return {
  17743. c() {
  17744. nav = element("nav");
  17745. for (let i = 0; i < each_blocks.length; i += 1) {
  17746. each_blocks[i].c();
  17747. }
  17748. attr(nav, "class", "tabs svelte-ese-123fyp");
  17749. attr(nav, "data-group", "primary");
  17750. attr(nav, "style", nav_style_value = /*$$props*/
  17751. ctx[4].style);
  17752. },
  17753. m(target, anchor) {
  17754. insert(target, nav, anchor);
  17755. for (let i = 0; i < each_blocks.length; i += 1) {
  17756. each_blocks[i].m(nav, null);
  17757. }
  17758. },
  17759. p(ctx2, [dirty]) {
  17760. if (dirty & /*underscore, activeTab, tabs, localize, separateElements*/
  17761. 15) {
  17762. each_value = /*tabs*/
  17763. ctx2[1].filter(func);
  17764. 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);
  17765. }
  17766. if (dirty & /*$$props*/
  17767. 16 && nav_style_value !== (nav_style_value = /*$$props*/
  17768. ctx2[4].style)) {
  17769. attr(nav, "style", nav_style_value);
  17770. }
  17771. },
  17772. i: noop,
  17773. o: noop,
  17774. d(detaching) {
  17775. if (detaching)
  17776. detach(nav);
  17777. for (let i = 0; i < each_blocks.length; i += 1) {
  17778. each_blocks[i].d();
  17779. }
  17780. }
  17781. };
  17782. }
  17783. const func = (tab) => !tab.hidden;
  17784. function instance$d($$self, $$props, $$invalidate) {
  17785. let { activeTab } = $$props;
  17786. let { tabs } = $$props;
  17787. let { underscore = false } = $$props;
  17788. let { separateElements = false } = $$props;
  17789. const click_handler = (tab) => {
  17790. $$invalidate(0, activeTab = tab.value);
  17791. };
  17792. $$self.$$set = ($$new_props) => {
  17793. $$invalidate(4, $$props = assign(assign({}, $$props), exclude_internal_props($$new_props)));
  17794. if ("activeTab" in $$new_props)
  17795. $$invalidate(0, activeTab = $$new_props.activeTab);
  17796. if ("tabs" in $$new_props)
  17797. $$invalidate(1, tabs = $$new_props.tabs);
  17798. if ("underscore" in $$new_props)
  17799. $$invalidate(2, underscore = $$new_props.underscore);
  17800. if ("separateElements" in $$new_props)
  17801. $$invalidate(3, separateElements = $$new_props.separateElements);
  17802. };
  17803. $$props = exclude_internal_props($$props);
  17804. return [activeTab, tabs, underscore, separateElements, $$props, click_handler];
  17805. }
  17806. class Tabs extends SvelteComponent {
  17807. constructor(options) {
  17808. super();
  17809. init(this, options, instance$d, create_fragment$d, safe_not_equal, {
  17810. activeTab: 0,
  17811. tabs: 1,
  17812. underscore: 2,
  17813. separateElements: 3
  17814. });
  17815. }
  17816. }
  17817. const SequenceManager = {
  17818. VisibleEffects: writable$1({}),
  17819. RunningSounds: writable$1({}),
  17820. RunningSequences: writable$1({})
  17821. };
  17822. SequenceManager.VisibleEffects.get = (id) => {
  17823. return get_store_value(SequenceManager.VisibleEffects)[id];
  17824. };
  17825. SequenceManager.VisibleEffects.add = (id, data) => {
  17826. SequenceManager.VisibleEffects.update((effects) => {
  17827. effects[id] = data;
  17828. return effects;
  17829. });
  17830. };
  17831. SequenceManager.VisibleEffects.delete = (id) => {
  17832. SequenceManager.VisibleEffects.update((effects) => {
  17833. delete effects[id];
  17834. return effects;
  17835. });
  17836. };
  17837. SequenceManager.VisibleEffects.values = () => {
  17838. return Object.values(get_store_value(SequenceManager.VisibleEffects));
  17839. };
  17840. SequenceManager.RunningSounds.get = (id) => {
  17841. return get_store_value(SequenceManager.RunningSounds)[id];
  17842. };
  17843. SequenceManager.RunningSounds.add = (id, data) => {
  17844. SequenceManager.RunningSounds.update((effects) => {
  17845. effects[id] = data;
  17846. return effects;
  17847. });
  17848. };
  17849. SequenceManager.RunningSounds.delete = (id) => {
  17850. SequenceManager.RunningSounds.update((effects) => {
  17851. delete effects[id];
  17852. return effects;
  17853. });
  17854. };
  17855. SequenceManager.RunningSounds.values = () => {
  17856. return Object.values(get_store_value(SequenceManager.RunningSounds));
  17857. };
  17858. SequenceManager.RunningSounds.keys = () => {
  17859. return Object.keys(get_store_value(SequenceManager.RunningSounds));
  17860. };
  17861. SequenceManager.RunningSequences.get = (id) => {
  17862. return get_store_value(SequenceManager.RunningSequences)[id];
  17863. };
  17864. SequenceManager.RunningSequences.add = (id, sequence) => {
  17865. SequenceManager.RunningSequences.update((sequences) => {
  17866. sequences[id] = sequence;
  17867. return sequences;
  17868. });
  17869. };
  17870. SequenceManager.RunningSequences.delete = (id) => {
  17871. SequenceManager.RunningSequences.update((sequences) => {
  17872. delete sequences[id];
  17873. return sequences;
  17874. });
  17875. };
  17876. SequenceManager.RunningSequences.clearFinishedSequences = () => {
  17877. SequenceManager.RunningSequences.update((sequences) => {
  17878. for (const sequence of Object.values(sequences)) {
  17879. if (get_store_value(sequence.status) === CONSTANTS.STATUS.COMPLETE || get_store_value(sequence.status) === CONSTANTS.STATUS.ABORTED) {
  17880. delete sequences[sequence.id];
  17881. }
  17882. }
  17883. return sequences;
  17884. });
  17885. };
  17886. SequenceManager.RunningSequences.stopAll = () => {
  17887. SequenceManager.RunningSequences.update((sequences) => {
  17888. for (const sequence of Object.values(sequences)) {
  17889. sequence._abort();
  17890. }
  17891. return sequences;
  17892. });
  17893. };
  17894. SequenceManager.RunningSequences.values = () => {
  17895. return Object.values(get_store_value(SequenceManager.RunningSequences));
  17896. };
  17897. class ColorMatrixFilter extends globalThis.PIXI.filters.ColorMatrixFilter {
  17898. /**
  17899. * Properties & default values:
  17900. * - hue [false]
  17901. * - brightness [1]
  17902. * - contrast [1]
  17903. * - saturate [1]
  17904. */
  17905. constructor(inData) {
  17906. super();
  17907. this.isValid = true;
  17908. this.values = {};
  17909. for (let [key, value] of Object.entries(inData)) {
  17910. this.setValue(key, value);
  17911. if (!this.isValid)
  17912. break;
  17913. }
  17914. }
  17915. setValue(key, value) {
  17916. try {
  17917. this.values[key] = value;
  17918. this[key](value, true);
  17919. } catch (err) {
  17920. ui.notifications.warn(
  17921. `Sequencer | ${this.constructor.name} | Could not set property ${key}`
  17922. );
  17923. }
  17924. }
  17925. }
  17926. class BlurFilter extends globalThis.PIXI.filters.BlurFilter {
  17927. /**
  17928. * Properties & default values:
  17929. * - strength [8]
  17930. * - blur [2]
  17931. * - blurX [2]
  17932. * - blurY [2]
  17933. * - quality [4]
  17934. * - resolution [PIXI.settings.FILTER_RESOLUTION]
  17935. * - kernelSize [5]
  17936. */
  17937. constructor(inData = {}) {
  17938. inData = foundry.utils.mergeObject(
  17939. {
  17940. strength: 1,
  17941. quality: 4,
  17942. resolution: PIXI.settings.FILTER_RESOLUTION,
  17943. kernelSize: 5
  17944. },
  17945. inData
  17946. );
  17947. super(...Object.values(inData));
  17948. this.isValid = true;
  17949. for (let [key, value] of Object.entries(inData)) {
  17950. try {
  17951. this[key] = value;
  17952. } catch (err) {
  17953. ui.notifications.warn(
  17954. `Sequencer | ${this.constructor.name} | Could not set property ${key}`
  17955. );
  17956. this.isValid = false;
  17957. }
  17958. }
  17959. }
  17960. }
  17961. class NoiseFilter extends globalThis.PIXI.filters.NoiseFilter {
  17962. /**
  17963. * Properties & default values:
  17964. * - noise [0.5]
  17965. * - seed [Math.random()]
  17966. */
  17967. constructor(inData = {}) {
  17968. super();
  17969. inData = foundry.utils.mergeObject(
  17970. {
  17971. noise: 0.5,
  17972. seed: Math.random()
  17973. },
  17974. inData
  17975. );
  17976. this.isValid = true;
  17977. for (let [key, value] of Object.entries(inData)) {
  17978. try {
  17979. this[key] = value;
  17980. } catch (err) {
  17981. ui.notifications.warn(
  17982. `Sequencer | ${this.constructor.name} | Could not set property ${key}`
  17983. );
  17984. this.isValid = false;
  17985. }
  17986. }
  17987. }
  17988. }
  17989. class GlowFilter extends globalThis.PIXI.filters.GlowFilter {
  17990. /**
  17991. * Properties & default values:
  17992. * - distance [10]
  17993. * - outerStrength [4]
  17994. * - innerStrength [0]
  17995. * - color [0xffffff]
  17996. * - quality [0.1]
  17997. * - knockout [false]
  17998. */
  17999. constructor(inData = {}) {
  18000. inData = foundry.utils.mergeObject(
  18001. {
  18002. distance: 10,
  18003. outerStrength: 4,
  18004. innerStrength: 0,
  18005. color: 16777215,
  18006. quality: 0.1,
  18007. knockout: false
  18008. },
  18009. inData
  18010. );
  18011. super(inData);
  18012. this.isValid = true;
  18013. }
  18014. }
  18015. let shader = `
  18016. uniform sampler2D uSampler;
  18017. varying vec2 vTextureCoord;
  18018. float alpha;
  18019. void main() {
  18020. vec4 pixel = texture2D(uSampler, vTextureCoord);
  18021. alpha = smoothstep(0.6,1.0,pixel.a);
  18022. gl_FragColor = vec4(alpha, alpha, alpha, pixel.a);
  18023. }
  18024. `;
  18025. class ClipFilter extends PIXI.Filter {
  18026. constructor() {
  18027. super(null, shader);
  18028. }
  18029. }
  18030. const filters = {
  18031. ColorMatrix: ColorMatrixFilter,
  18032. Blur: BlurFilter,
  18033. Noise: NoiseFilter,
  18034. Glow: GlowFilter,
  18035. Clip: ClipFilter
  18036. };
  18037. const SequencerAnimationEngine = {
  18038. _animations: [],
  18039. _debug: void 0,
  18040. _deltas: [],
  18041. ticker: false,
  18042. dt: 0,
  18043. isRunning: false,
  18044. addAnimation(origin, attributes = [], timeDifference = 0) {
  18045. if (!Array.isArray(attributes))
  18046. attributes = [attributes];
  18047. return new Promise((resolve) => {
  18048. this._animations.push({
  18049. origin,
  18050. attributes: attributes.map((attribute) => {
  18051. attribute.targetId = get_object_identifier(attribute.target) + "-" + attribute.propertyName;
  18052. attribute.started = false;
  18053. attribute.initialized = false;
  18054. attribute.finishing = false;
  18055. attribute.complete = false;
  18056. attribute.progress = 0;
  18057. attribute.value = 0;
  18058. attribute.duration = attribute.duration ?? 0;
  18059. attribute.durationDone = timeDifference ?? 0;
  18060. if (attribute?.looping) {
  18061. attribute.loopDuration = attribute.loopDuration ?? attribute.duration ?? 0;
  18062. attribute.loopDurationDone = timeDifference % attribute.loopDuration;
  18063. attribute.loops = attribute.loops ?? 0;
  18064. attribute.loopsDone = Math.floor(
  18065. attribute.durationDone / attribute.duration
  18066. );
  18067. attribute.index = attribute.loopsDone % attribute.values.length;
  18068. attribute.nextIndex = (attribute.loopsDone + 1) % attribute.values.length;
  18069. if (!attribute.pingPong && attribute.nextIndex === 0) {
  18070. attribute.index = 0;
  18071. attribute.nextIndex = 1;
  18072. }
  18073. }
  18074. return attribute;
  18075. }),
  18076. complete: false,
  18077. totalDt: timeDifference,
  18078. resolve
  18079. });
  18080. if (!this.ticker || !this.ticker.started) {
  18081. this.start();
  18082. }
  18083. debug(`Added animations to Animation Engine`);
  18084. });
  18085. },
  18086. endAnimations(target) {
  18087. this._animations = this._animations.filter(
  18088. (animation2) => animation2.origin !== target
  18089. );
  18090. },
  18091. start() {
  18092. debug(`Animation Engine Started`);
  18093. if (!this.ticker) {
  18094. this.ticker = new PIXI.Ticker();
  18095. this.ticker.add(this.nextFrame.bind(this));
  18096. }
  18097. this.ticker.start();
  18098. },
  18099. nextFrame() {
  18100. if (this._animations.length === 0) {
  18101. debug(`Animation Engine Paused`);
  18102. this.ticker.stop();
  18103. this._startingValues = {};
  18104. return;
  18105. }
  18106. this._animations.forEach((animation2) => this._animate(animation2));
  18107. this._animations = this._animations.filter(
  18108. (animation2) => !animation2.complete
  18109. );
  18110. this._applyDeltas();
  18111. for (const targetId of Object.keys(this._startingValues)) {
  18112. if (!this._animations.some(
  18113. (_a) => _a.attributes.some((_b) => _b.targetId === targetId)
  18114. )) {
  18115. delete this._startingValues[targetId];
  18116. }
  18117. }
  18118. },
  18119. _startingValues: {},
  18120. _applyDeltas() {
  18121. const deltas = [];
  18122. for (const animation2 of this._animations) {
  18123. for (const attribute of animation2.attributes) {
  18124. if (!attribute.started || attribute.complete)
  18125. continue;
  18126. if (attribute.finishing) {
  18127. attribute.complete = true;
  18128. }
  18129. let delta = deltas.find(
  18130. (delta2) => attribute.targetId === delta2.targetId && attribute.setPropertyName === delta2.setPropertyName
  18131. );
  18132. if (!delta) {
  18133. delta = {
  18134. targetId: attribute.targetId,
  18135. target: attribute.target,
  18136. getPropertyName: attribute.getPropertyName ?? attribute.propertyName,
  18137. setPropertyName: attribute.propertyName,
  18138. value: 0
  18139. };
  18140. if (attribute.target instanceof PIXI.filters.ColorMatrixFilter) {
  18141. delta.value = attribute.previousValue;
  18142. }
  18143. deltas.push(delta);
  18144. }
  18145. delta.value += attribute.delta;
  18146. }
  18147. }
  18148. for (let delta of deltas) {
  18149. const finalValue = deep_get(delta.target, delta.getPropertyName) + delta.value;
  18150. try {
  18151. deep_set(delta.target, delta.setPropertyName, finalValue);
  18152. } catch (err) {
  18153. }
  18154. }
  18155. },
  18156. _animate(animation2) {
  18157. animation2.totalDt += this.ticker.elapsedMS;
  18158. animation2.attributes.filter((attribute) => !attribute.complete).forEach(
  18159. (attribute) => this._animateAttribute(animation2.totalDt, attribute)
  18160. );
  18161. animation2.complete = animation2.attributes.filter((attribute) => !attribute.complete).length === 0;
  18162. if (animation2.complete) {
  18163. animation2.resolve();
  18164. }
  18165. },
  18166. _animateAttribute(totalDt, attribute) {
  18167. if (totalDt < attribute.delay)
  18168. return;
  18169. if (!attribute.started) {
  18170. const funkyProperty = attribute.propertyName.includes("scale") || attribute.propertyName.includes("alpha");
  18171. if (this._startingValues[attribute.targetId] === void 0 || attribute.absolute) {
  18172. const getProperty2 = funkyProperty || attribute.from === void 0;
  18173. this._startingValues[attribute.targetId] = getProperty2 ? deep_get(
  18174. attribute.target,
  18175. attribute.getPropertyName ?? attribute.propertyName
  18176. ) : attribute.from;
  18177. if (!attribute.propertyName.includes("scale")) {
  18178. deep_set(
  18179. attribute.target,
  18180. attribute.propertyName,
  18181. this._startingValues[attribute.targetId]
  18182. );
  18183. }
  18184. }
  18185. attribute.previousValue = this._startingValues[attribute.targetId];
  18186. if (attribute?.looping) {
  18187. attribute.values = attribute.values.map((value) => {
  18188. return value + attribute.previousValue - (funkyProperty ? 1 : 0);
  18189. });
  18190. } else if (attribute.from === void 0) {
  18191. attribute.from = attribute.previousValue;
  18192. }
  18193. }
  18194. attribute.started = true;
  18195. if (attribute?.looping && attribute?.indefinite) {
  18196. this._handleIndefiniteLoop(attribute);
  18197. } else if (attribute?.looping) {
  18198. this._handleLoops(attribute);
  18199. } else {
  18200. this._handleDefault(attribute);
  18201. }
  18202. attribute.delta = attribute.value - attribute.previousValue;
  18203. attribute.previousValue = attribute.value;
  18204. },
  18205. _handleBaseLoop(attribute) {
  18206. if (!attribute.initialized) {
  18207. if (attribute.values.length === 1) {
  18208. attribute.values.unshift(
  18209. deep_get(attribute.target, attribute.propertyName)
  18210. );
  18211. }
  18212. attribute.initialized = true;
  18213. }
  18214. attribute.loopDurationDone += this.ticker.deltaMS;
  18215. attribute.progress = attribute.loopDurationDone / attribute.loopDuration;
  18216. attribute.value = interpolate(
  18217. attribute.values[attribute.index],
  18218. attribute.values[attribute.nextIndex],
  18219. attribute.progress,
  18220. attribute.ease
  18221. );
  18222. if (attribute.progress >= 1) {
  18223. attribute.loopDurationDone -= attribute.loopDuration;
  18224. attribute.index = (attribute.index + 1) % attribute.values.length;
  18225. attribute.nextIndex = (attribute.nextIndex + 1) % attribute.values.length;
  18226. if (!attribute.pingPong && attribute.nextIndex === 0) {
  18227. attribute.index = 0;
  18228. attribute.nextIndex = 1;
  18229. }
  18230. attribute.loopsDone++;
  18231. attribute.value = interpolate(
  18232. attribute.values[attribute.index],
  18233. attribute.values[attribute.nextIndex],
  18234. attribute.progress % 1,
  18235. attribute.ease
  18236. );
  18237. }
  18238. },
  18239. _handleIndefiniteLoop(attribute) {
  18240. return this._handleBaseLoop(attribute);
  18241. },
  18242. _handleLoops(attribute) {
  18243. this._handleBaseLoop(attribute);
  18244. attribute.durationDone += this.ticker.deltaMS;
  18245. attribute.overallProgress = attribute.durationDone / attribute.duration;
  18246. if (attribute.progress >= 1 && attribute.loopsDone === attribute.loops * 2) {
  18247. attribute.finishing = true;
  18248. attribute.value = attribute.values[attribute.index];
  18249. }
  18250. if (attribute.overallProgress >= 1) {
  18251. attribute.finishing = true;
  18252. }
  18253. },
  18254. _handleDefault(attribute) {
  18255. attribute.durationDone += this.ticker.deltaMS;
  18256. attribute.progress = attribute.durationDone / attribute.duration;
  18257. attribute.value = interpolate(
  18258. attribute.from,
  18259. attribute.to,
  18260. attribute.progress,
  18261. attribute.ease
  18262. );
  18263. if (attribute.progress >= 1) {
  18264. attribute.value = attribute.to;
  18265. attribute.finishing = true;
  18266. }
  18267. }
  18268. };
  18269. class SequencerAudioHelper {
  18270. /**
  18271. * Play an audio file.
  18272. *
  18273. * @param {{src: string, loop?: boolean, volume?: number, _fadeIn?: {duration: number}, _fadeOut?: {duration: number}, duration?: number}} data The data that describes the audio to play.
  18274. * @param {boolean} [push=false] A flag indicating whether or not to make other clients play the audio, too.
  18275. * @returns {Number} A promise that resolves when the audio file has finished playing.
  18276. */
  18277. static async play(data, push = true) {
  18278. if (push)
  18279. sequencerSocket.executeForOthers(SOCKET_HANDLERS.PLAY_SOUND, data);
  18280. return this._play(data);
  18281. }
  18282. /**
  18283. * @param {{src: string, loop?: boolean, volume: number, _fadeIn?: {duration: number}, _fadeOut?: {duration: number}, duration?: number}} data
  18284. * @returns {Number}
  18285. * @private
  18286. */
  18287. static async _play(data) {
  18288. if (!game.settings.get("sequencer", "soundsEnabled") || game.user.viewedScene !== data.sceneId || data?.users?.length && !data?.users?.includes(game.userId)) {
  18289. return new Promise((resolve) => setTimeout(resolve, data.duration));
  18290. }
  18291. Hooks.callAll("createSequencerSound", data);
  18292. debug(`Playing sound:`, data);
  18293. data.volume = (data.volume ?? 0.8) * game.settings.get("core", "globalInterfaceVolume");
  18294. const sound = await game.audio.play(data.src, {
  18295. volume: data.fadeIn ? 0 : data.volume,
  18296. loop: data.loop,
  18297. offset: data.startTime
  18298. });
  18299. SequenceManager.RunningSounds.add(data.id, sound);
  18300. if (data.fadeIn) {
  18301. SequencerAnimationEngine.addAnimation(data.id, {
  18302. target: sound,
  18303. propertyName: "volume",
  18304. from: 0,
  18305. to: data.volume,
  18306. duration: Math.min(data.fadeIn.duration, data.duration),
  18307. ease: data.fadeIn.ease,
  18308. delay: Math.min(data.fadeIn.delay, data.duration)
  18309. });
  18310. }
  18311. if (data.fadeOut) {
  18312. SequencerAnimationEngine.addAnimation(data.id, {
  18313. target: sound,
  18314. propertyName: "volume",
  18315. from: data.volume,
  18316. to: 0,
  18317. duration: Math.min(data.fadeOut.duration, data.duration),
  18318. ease: data.fadeOut.ease,
  18319. delay: Math.max(
  18320. data.duration - data.fadeOut.duration + data.fadeOut.delay,
  18321. 0
  18322. )
  18323. });
  18324. }
  18325. if (data.duration) {
  18326. setTimeout(() => {
  18327. sound.stop();
  18328. }, data.duration);
  18329. }
  18330. new Promise((resolve) => {
  18331. sound.on("stop", resolve);
  18332. sound.on("end", resolve);
  18333. }).then(() => {
  18334. SequenceManager.RunningSounds.delete(data.id);
  18335. Hooks.callAll("endedSequencerSound", data);
  18336. });
  18337. return data.duration;
  18338. }
  18339. static stop(ids, push = true) {
  18340. if (push && game.user.isGM)
  18341. sequencerSocket.executeForOthers(SOCKET_HANDLERS.STOP_SOUNDS, ids);
  18342. return this._stop(ids);
  18343. }
  18344. static _stop(ids) {
  18345. for (const id of ids) {
  18346. const sound = SequenceManager.RunningSounds.get(id);
  18347. if (sound) {
  18348. sound.stop();
  18349. }
  18350. }
  18351. }
  18352. }
  18353. let lockedView = false;
  18354. class SequencerFoundryReplicator {
  18355. static registerHooks() {
  18356. Hooks.on("canvasPan", () => {
  18357. if (!lockedView)
  18358. return;
  18359. canvas.stage.pivot.set(lockedView.x, lockedView.y);
  18360. canvas.stage.scale.set(lockedView.scale, lockedView.scale);
  18361. canvas.updateBlur(lockedView.scale);
  18362. canvas.controls._onCanvasPan();
  18363. canvas.hud.align();
  18364. });
  18365. }
  18366. static _validateObject(inObject, sceneId) {
  18367. if (is_UUID(inObject) || !is_object_canvas_data(inObject)) {
  18368. inObject = get_object_from_scene(inObject, sceneId);
  18369. }
  18370. return inObject?._object ?? inObject;
  18371. }
  18372. static _getPositionFromData(data) {
  18373. const source = this._validateObject(data.source, data.sceneId);
  18374. const position = source instanceof PlaceableObject ? get_object_position(source) : source?.worldPosition || source?.center || source;
  18375. const multiplier = data.randomOffset;
  18376. const twister = new MersenneTwister(data.seed);
  18377. if (source && multiplier) {
  18378. let randomOffset = get_random_offset(
  18379. source,
  18380. multiplier,
  18381. twister
  18382. );
  18383. position.x -= randomOffset.x;
  18384. position.y -= randomOffset.y;
  18385. }
  18386. let extraOffset = data.offset;
  18387. if (extraOffset) {
  18388. let newOffset = {
  18389. x: extraOffset.x,
  18390. y: extraOffset.y
  18391. };
  18392. if (extraOffset.gridUnits) {
  18393. newOffset.x *= canvas.grid.size;
  18394. newOffset.y *= canvas.grid.size;
  18395. }
  18396. if (extraOffset.local) {
  18397. newOffset = rotateAroundPoint(
  18398. 0,
  18399. 0,
  18400. newOffset.x,
  18401. newOffset.y,
  18402. source?.rotation ?? 0
  18403. );
  18404. }
  18405. position.x -= newOffset.x;
  18406. position.y -= newOffset.y;
  18407. }
  18408. return position;
  18409. }
  18410. static playScrollingText(data, push = true) {
  18411. if (push) {
  18412. sequencerSocket.executeForOthers(
  18413. SOCKET_HANDLERS.CREATE_SCROLLING_TEXT,
  18414. data
  18415. );
  18416. }
  18417. return this._playScrollingText(data);
  18418. }
  18419. static _playScrollingText(data) {
  18420. if (game.user.viewedScene !== data.sceneId)
  18421. return;
  18422. if (data.users.length && !data.users.includes(game.userId))
  18423. return;
  18424. canvas.interface.createScrollingText(
  18425. this._getPositionFromData(data),
  18426. data.content,
  18427. data.options
  18428. );
  18429. return data.options?.duration ?? 2e3;
  18430. }
  18431. static panCanvas(data, push = true) {
  18432. if (push) {
  18433. sequencerSocket.executeForOthers(SOCKET_HANDLERS.PAN_CANVAS, data);
  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. if (data.source) {
  18443. const position = this._getPositionFromData(data);
  18444. canvas.animatePan({
  18445. x: position.x,
  18446. y: position.y,
  18447. scale: data.scale,
  18448. duration: data.duration,
  18449. speed: data.speed
  18450. });
  18451. if (data.speed) {
  18452. let ray = new Ray(canvas.stage.pivot, {
  18453. x: position.x,
  18454. y: position.y
  18455. });
  18456. data.duration = Math.round(ray.distance * 1e3 / data.speed);
  18457. }
  18458. if (data.lockView > 0) {
  18459. setTimeout(() => {
  18460. lockedView = {
  18461. x: position.x,
  18462. y: position.y,
  18463. scale: data.scale
  18464. };
  18465. }, data.duration);
  18466. setTimeout(() => {
  18467. lockedView = false;
  18468. }, data.lockView + data.duration);
  18469. }
  18470. } else {
  18471. data.duration = 0;
  18472. }
  18473. if (data.shake) {
  18474. setTimeout(() => {
  18475. this._shake(data.shake);
  18476. }, data.duration);
  18477. }
  18478. return data.duration + Math.max(data.lockView ?? 0, data.shake?.duration ?? 0);
  18479. }
  18480. static _shake(shakeData) {
  18481. let x = random_float_between(-1, 1);
  18482. let y = random_float_between(-1, 1);
  18483. let rot = shakeData.rotation ? random_float_between(-1, 1) : 0;
  18484. let positions = [{ x, y, rot }];
  18485. for (let index = 0; index < Math.floor(shakeData.duration / shakeData.frequency); index++) {
  18486. x = flip_negate(x, Math.random());
  18487. y = flip_negate(y, Math.random());
  18488. rot = shakeData.rotation ? flip_negate(rot, Math.random()) : 0;
  18489. positions.push({ x, y, rot });
  18490. }
  18491. let currentDuration = 0;
  18492. positions = positions.map((pos) => {
  18493. let fadeStrength = 1;
  18494. if (shakeData.fadeInDuration && currentDuration <= shakeData.fadeInDuration) {
  18495. fadeStrength = Math.max(
  18496. 0,
  18497. Math.min(1, currentDuration / shakeData.fadeInDuration)
  18498. );
  18499. }
  18500. if (shakeData.fadeOutDuration && currentDuration >= shakeData.duration - shakeData.fadeOutDuration) {
  18501. fadeStrength = Math.max(
  18502. 0,
  18503. Math.min(
  18504. 1,
  18505. (shakeData.duration - currentDuration) / shakeData.fadeOutDuration
  18506. )
  18507. );
  18508. }
  18509. pos.x *= shakeData.strength * fadeStrength;
  18510. pos.y *= shakeData.strength * fadeStrength;
  18511. if (shakeData.rotation) {
  18512. pos.rot *= shakeData.strength / 7.5 * fadeStrength;
  18513. } else {
  18514. pos.rot = 0;
  18515. }
  18516. currentDuration += shakeData.frequency;
  18517. return {
  18518. transform: `translate(${pos.x}px, ${pos.y}px) rotate(${pos.rot}deg)`
  18519. };
  18520. });
  18521. document.getElementById("board").animate(positions, {
  18522. duration: shakeData.duration
  18523. });
  18524. }
  18525. }
  18526. const SOCKET_HANDLERS = {
  18527. PLAY_EFFECT: "playEffect",
  18528. END_EFFECTS: "endEffects",
  18529. UPDATE_EFFECT: "updateEffects",
  18530. ADD_EFFECT_ANIMATIONS: "addEffectAnimations",
  18531. PLAY_SOUND: "playSound",
  18532. STOP_SOUNDS: "stopSounds",
  18533. PRELOAD: "preload",
  18534. PRELOAD_RESPONSE: "preloadResponse",
  18535. PRELOAD_DONE: "preloadDone",
  18536. UPDATE_DOCUMENT: "updateDocument",
  18537. ADD_FLAGS: "addFlags",
  18538. REMOVE_FLAGS: "removeFlags",
  18539. UPDATE_POSITION: "updatePosition",
  18540. CREATE_SCROLLING_TEXT: "createScrollingText",
  18541. PAN_CANVAS: "panCanvas",
  18542. RUN_SEQUENCE_LOCALLY: "runSequenceLocally"
  18543. };
  18544. let sequencerSocket;
  18545. function registerSocket() {
  18546. if (sequencerSocket)
  18547. return;
  18548. sequencerSocket = socketlib.registerModule(CONSTANTS.MODULE_NAME);
  18549. sequencerSocket.register(
  18550. SOCKET_HANDLERS.PLAY_EFFECT,
  18551. (...args) => Sequencer.EffectManager._playEffect(...args)
  18552. );
  18553. sequencerSocket.register(
  18554. SOCKET_HANDLERS.END_EFFECTS,
  18555. (...args) => Sequencer.EffectManager._endManyEffects(...args)
  18556. );
  18557. sequencerSocket.register(
  18558. SOCKET_HANDLERS.UPDATE_EFFECT,
  18559. (...args) => Sequencer.EffectManager._updateEffect(...args)
  18560. );
  18561. sequencerSocket.register(
  18562. SOCKET_HANDLERS.ADD_EFFECT_ANIMATIONS,
  18563. (...args) => Sequencer.EffectManager._addEffectAnimations(...args)
  18564. );
  18565. sequencerSocket.register(
  18566. SOCKET_HANDLERS.PLAY_SOUND,
  18567. (...args) => SequencerAudioHelper._play(...args)
  18568. );
  18569. sequencerSocket.register(
  18570. SOCKET_HANDLERS.STOP_SOUNDS,
  18571. (...args) => SequencerAudioHelper._stop(...args)
  18572. );
  18573. sequencerSocket.register(
  18574. SOCKET_HANDLERS.PRELOAD,
  18575. (...args) => Sequencer.Preloader.respond(...args)
  18576. );
  18577. sequencerSocket.register(
  18578. SOCKET_HANDLERS.PRELOAD_RESPONSE,
  18579. (...args) => Sequencer.Preloader.handleResponse(...args)
  18580. );
  18581. sequencerSocket.register(
  18582. SOCKET_HANDLERS.UPDATE_DOCUMENT,
  18583. (...args) => updateDocument(...args)
  18584. );
  18585. sequencerSocket.register(
  18586. SOCKET_HANDLERS.ADD_FLAGS,
  18587. (...args) => flagManager._addFlags(...args)
  18588. );
  18589. sequencerSocket.register(
  18590. SOCKET_HANDLERS.REMOVE_FLAGS,
  18591. (...args) => flagManager._removeFlags(...args)
  18592. );
  18593. sequencerSocket.register(
  18594. SOCKET_HANDLERS.UPDATE_POSITION,
  18595. (...args) => Sequencer.EffectManager._updatePosition(...args)
  18596. );
  18597. sequencerSocket.register(
  18598. SOCKET_HANDLERS.CREATE_SCROLLING_TEXT,
  18599. (data) => SequencerFoundryReplicator._playScrollingText(data)
  18600. );
  18601. sequencerSocket.register(
  18602. SOCKET_HANDLERS.PAN_CANVAS,
  18603. (data) => SequencerFoundryReplicator._panCanvas(data)
  18604. );
  18605. sequencerSocket.register(SOCKET_HANDLERS.RUN_SEQUENCE_LOCALLY, (data) => {
  18606. debug("Playing remote Sequence");
  18607. new Sequence().fromJSON(data).play();
  18608. });
  18609. }
  18610. async function updateDocument(documentUuid, updates, animate) {
  18611. const document2 = await fromUuid(documentUuid);
  18612. return document2.update(updates, animate);
  18613. }
  18614. const flagManager = {
  18615. flagAddBuffer: /* @__PURE__ */ new Map(),
  18616. flagRemoveBuffer: /* @__PURE__ */ new Map(),
  18617. _latestFlagVersion: false,
  18618. get latestFlagVersion() {
  18619. if (!this._latestFlagVersion) {
  18620. const versions = Object.keys(this.migrations);
  18621. versions.sort((a, b) => {
  18622. return isNewerVersion(a, b) ? -1 : 1;
  18623. });
  18624. this._latestFlagVersion = versions[0];
  18625. }
  18626. return this._latestFlagVersion;
  18627. },
  18628. /**
  18629. * Sanitizes the effect data, accounting for changes to the structure in previous versions
  18630. *
  18631. * @param inDocument
  18632. * @returns {array}
  18633. */
  18634. getFlags(inDocument) {
  18635. let effects = getProperty(inDocument, CONSTANTS.EFFECTS_FLAG);
  18636. if (!effects?.length)
  18637. return [];
  18638. effects = foundry.utils.deepClone(effects);
  18639. const changes = [];
  18640. for (let [effectId, effectData] of effects) {
  18641. let effectVersion = effectData?.flagVersion ?? "1.0.0";
  18642. if (effectData.flagVersion === this.latestFlagVersion)
  18643. continue;
  18644. for (let [version, migration] of Object.entries(this.migrations)) {
  18645. if (!isNewerVersion(version, effectVersion))
  18646. continue;
  18647. effectData = migration(inDocument, effectData);
  18648. }
  18649. debug(
  18650. `Migrated effect with ID ${effectId} from version ${effectVersion} to version ${this.latestFlagVersion}`
  18651. );
  18652. effectData.flagVersion = this.latestFlagVersion;
  18653. changes.push(effectData);
  18654. }
  18655. if (changes.length) {
  18656. flagManager.addFlags(inDocument.uuid, changes);
  18657. }
  18658. return effects;
  18659. },
  18660. migrations: {
  18661. "2.0.0": (inDocument, effectData) => {
  18662. effectData._id = effectData.id;
  18663. effectData.creationTimestamp = effectData.timestamp;
  18664. if (effectData.template) {
  18665. effectData.template = {
  18666. gridSize: effectData.template[0],
  18667. startPoint: effectData.template[1],
  18668. endPoint: effectData.template[2]
  18669. };
  18670. }
  18671. if (effectData.attachTo) {
  18672. effectData.attachTo = {
  18673. active: true,
  18674. align: "center",
  18675. rotation: true,
  18676. bindVisibility: true,
  18677. bindAlpha: true
  18678. };
  18679. effectData.source = inDocument.uuid;
  18680. const objectSize = get_object_dimensions(inDocument, true);
  18681. effectData.offset = {
  18682. x: effectData.position.x - objectSize.width,
  18683. y: effectData.position.y - objectSize.height
  18684. };
  18685. } else if (effectData.position) {
  18686. effectData.source = effectData.position;
  18687. }
  18688. if (effectData.reachTowards) {
  18689. effectData.stretchTo = {
  18690. attachTo: false,
  18691. onlyX: false
  18692. };
  18693. }
  18694. if (effectData.filters) {
  18695. effectData.filters = Object.entries(effectData.filters).map((entry) => {
  18696. return {
  18697. className: entry[0],
  18698. ...entry[1]
  18699. };
  18700. });
  18701. }
  18702. effectData.moveSpeed = effectData.speed;
  18703. effectData.target = null;
  18704. effectData.forcedIndex = null;
  18705. effectData.flipX = false;
  18706. effectData.flipY = false;
  18707. effectData.nameOffsetMap = {};
  18708. effectData.sequenceId = 0;
  18709. delete effectData.id;
  18710. delete effectData.timestamp;
  18711. delete effectData.position;
  18712. delete effectData.reachTowards;
  18713. delete effectData.speed;
  18714. delete effectData.audioVolume;
  18715. delete effectData.gridSizeDifference;
  18716. delete effectData.template;
  18717. if (effectData.animatedProperties) {
  18718. delete effectData.animatedProperties.fadeInAudio;
  18719. delete effectData.animatedProperties.fadeOutAudio;
  18720. }
  18721. effectData = foundry.utils.mergeObject(
  18722. effectData,
  18723. effectData.animatedProperties
  18724. );
  18725. delete effectData.animatedProperties;
  18726. return effectData;
  18727. },
  18728. "2.0.6": (inDocument, effectData) => {
  18729. effectData.private = null;
  18730. return effectData;
  18731. },
  18732. "2.0.8": (inDocument, effectData) => {
  18733. if (effectData.stretchTo) {
  18734. effectData.stretchTo.tiling = false;
  18735. }
  18736. return effectData;
  18737. },
  18738. "2.0.9": (inDocument, effectData) => {
  18739. effectData.tilingTexture = false;
  18740. if (effectData.stretchTo?.tiling !== void 0) {
  18741. if (effectData.stretchTo.tiling) {
  18742. effectData.tilingTexture = {
  18743. scale: { x: 1, y: 1 },
  18744. position: { x: 0, y: 0 }
  18745. };
  18746. }
  18747. delete effectData.stretchTo.tiling;
  18748. }
  18749. return effectData;
  18750. },
  18751. "2.1.0": (inDocument, effectData) => {
  18752. if (effectData.randomOffset) {
  18753. effectData.randomOffset = {
  18754. source: !effectData.target ? effectData.randomOffset : false,
  18755. target: !!effectData.target ? effectData.randomOffset : false
  18756. };
  18757. }
  18758. if (effectData.nameOffsetMap) {
  18759. Object.values(effectData.nameOffsetMap).forEach((offsetMap) => {
  18760. if (offsetMap.randomOffset) {
  18761. offsetMap.randomOffset = {
  18762. source: !offsetMap.target ? offsetMap.randomOffset : false,
  18763. target: !!offsetMap.target ? offsetMap.randomOffset : false
  18764. };
  18765. }
  18766. });
  18767. }
  18768. return effectData;
  18769. }
  18770. },
  18771. /**
  18772. * Adds effects to a given document
  18773. *
  18774. * @param inObjectUUID
  18775. * @param inEffects
  18776. */
  18777. addFlags: (inObjectUUID, inEffects) => {
  18778. if (!Array.isArray(inEffects))
  18779. inEffects = [inEffects];
  18780. sequencerSocket.executeAsGM(
  18781. SOCKET_HANDLERS.ADD_FLAGS,
  18782. inObjectUUID,
  18783. inEffects
  18784. );
  18785. },
  18786. /**
  18787. * Removes effects from a given document
  18788. *
  18789. * @param inObjectUUID
  18790. * @param inEffects
  18791. * @param removeAll
  18792. */
  18793. removeFlags: (inObjectUUID, inEffects, removeAll = false) => {
  18794. sequencerSocket.executeAsGM(
  18795. SOCKET_HANDLERS.REMOVE_FLAGS,
  18796. inObjectUUID,
  18797. inEffects,
  18798. removeAll
  18799. );
  18800. },
  18801. _addFlags: (inObjectUUID, inEffects) => {
  18802. if (!Array.isArray(inEffects))
  18803. inEffects = [inEffects];
  18804. let flagsToSet = flagManager.flagAddBuffer.get(inObjectUUID) ?? {
  18805. effects: []
  18806. };
  18807. flagsToSet.effects.push(...inEffects);
  18808. flagManager.flagAddBuffer.set(inObjectUUID, flagsToSet);
  18809. flagManager.updateFlags();
  18810. },
  18811. _removeFlags: (inObjectUUID, inEffects, removeAll = false) => {
  18812. if (inEffects && !Array.isArray(inEffects))
  18813. inEffects = [inEffects];
  18814. let flagsToSet = flagManager.flagRemoveBuffer.get(inObjectUUID) ?? {
  18815. effects: [],
  18816. removeAll
  18817. };
  18818. if (inEffects)
  18819. flagsToSet.effects.push(...inEffects);
  18820. flagManager.flagRemoveBuffer.set(inObjectUUID, flagsToSet);
  18821. flagManager.updateFlags();
  18822. },
  18823. updateFlags: debounce(async () => {
  18824. let flagsToAdd = Array.from(flagManager.flagAddBuffer);
  18825. let flagsToRemove = Array.from(flagManager.flagRemoveBuffer);
  18826. flagManager.flagAddBuffer.clear();
  18827. flagManager.flagRemoveBuffer.clear();
  18828. flagsToAdd.forEach((entry) => entry[1].original = true);
  18829. flagsToRemove.forEach((entry) => entry[1].original = true);
  18830. const objects = /* @__PURE__ */ new Set([
  18831. ...flagsToAdd.map((effect) => effect[0]).filter(Boolean),
  18832. ...flagsToRemove.map((effect) => effect[0]).filter(Boolean)
  18833. ]);
  18834. flagsToAdd = new Map(flagsToAdd);
  18835. flagsToRemove = new Map(flagsToRemove);
  18836. const actorUpdates = {};
  18837. const sceneObjectsToUpdate = {};
  18838. for (let objectUUID of objects) {
  18839. let object = fromUuidSync(objectUUID);
  18840. if (!object) {
  18841. debug(
  18842. `Failed to set flags on non-existent object with UUID: ${objectUUID}`
  18843. );
  18844. continue;
  18845. }
  18846. let toAdd = flagsToAdd.get(objectUUID) ?? { effects: [] };
  18847. let toRemove = flagsToRemove.get(objectUUID) ?? {
  18848. effects: [],
  18849. removeAll: false
  18850. };
  18851. const existingFlags = new Map(
  18852. getProperty(object, CONSTANTS.EFFECTS_FLAG) ?? []
  18853. );
  18854. if (toRemove?.removeAll) {
  18855. toRemove.effects = Array.from(existingFlags).map((entry) => entry[0]);
  18856. }
  18857. for (let effect of toAdd.effects) {
  18858. if (typeof effect === "string") {
  18859. effect = existingFlags.get(effect);
  18860. if (!effect)
  18861. continue;
  18862. }
  18863. existingFlags.set(effect._id, effect);
  18864. }
  18865. for (let effect of toRemove.effects) {
  18866. if (typeof effect === "string") {
  18867. effect = existingFlags.get(effect);
  18868. if (!effect)
  18869. continue;
  18870. }
  18871. existingFlags.delete(effect._id);
  18872. }
  18873. let flagsToSet = Array.from(existingFlags);
  18874. const options = {};
  18875. const isLinkedToken = object instanceof TokenDocument && object.actorLink;
  18876. const isLinkedActor = object instanceof Actor && object.prototypeToken.actorLink;
  18877. if ((isLinkedToken || isLinkedActor) && (toAdd.original || toRemove.original)) {
  18878. const actor = isLinkedActor ? object : object.actor;
  18879. actorUpdates[actor.id] = flagsToSet.filter(
  18880. (effect) => effect[1]?.persistOptions?.persistTokenPrototype
  18881. );
  18882. flagsToSet = flagsToSet.filter(
  18883. (effect) => !effect[1]?.persistOptions?.persistTokenPrototype
  18884. );
  18885. if (isLinkedToken && game.modules.get("multilevel-tokens")?.active && getProperty(object, "flags.multilevel-tokens.stoken")) {
  18886. options["mlt_bypass"] = true;
  18887. }
  18888. }
  18889. if (object?.documentName === "Scene") {
  18890. const sceneId = object.id;
  18891. sceneObjectsToUpdate[sceneId] = sceneObjectsToUpdate[sceneId] ?? {
  18892. updates: {},
  18893. documents: {}
  18894. };
  18895. sceneObjectsToUpdate[sceneId].updates[CONSTANTS.EFFECTS_FLAG] = flagsToSet;
  18896. } else if (!(object instanceof Actor)) {
  18897. const sceneId = object.parent.id;
  18898. const docName = object.documentName;
  18899. sceneObjectsToUpdate[sceneId] = sceneObjectsToUpdate[sceneId] ?? {
  18900. updates: {},
  18901. documents: {}
  18902. };
  18903. sceneObjectsToUpdate[sceneId].documents[docName] = sceneObjectsToUpdate[sceneId].documents[docName] ?? {
  18904. options: {},
  18905. updates: []
  18906. };
  18907. sceneObjectsToUpdate[sceneId].documents[docName].options = options;
  18908. sceneObjectsToUpdate[sceneId].documents[docName].updates.push({
  18909. _id: object.id,
  18910. [CONSTANTS.EFFECTS_FLAG]: flagsToSet
  18911. });
  18912. }
  18913. }
  18914. for (const [sceneId, sceneData] of Object.entries(sceneObjectsToUpdate)) {
  18915. const scene = game.scenes.get(sceneId);
  18916. if (!foundry.utils.isEmpty(sceneData.updates)) {
  18917. await scene.update(sceneData.updates);
  18918. }
  18919. for (const [documentType, documentData] of Object.entries(
  18920. sceneData.documents
  18921. )) {
  18922. await scene.updateEmbeddedDocuments(
  18923. documentType,
  18924. documentData.updates,
  18925. documentData.options
  18926. );
  18927. debug(
  18928. `Flags set for documents of type "${documentType}" in scene with ID "${sceneId}"`
  18929. );
  18930. }
  18931. }
  18932. await Actor.updateDocuments(
  18933. Object.entries(actorUpdates).map(([actorId, effects]) => ({
  18934. _id: actorId,
  18935. [CONSTANTS.EFFECTS_FLAG]: effects
  18936. }))
  18937. );
  18938. }, 250)
  18939. };
  18940. const PositionContainer = /* @__PURE__ */ new Map();
  18941. const TemporaryPositionsContainer = /* @__PURE__ */ new Map();
  18942. class SequencerEffectManager {
  18943. /**
  18944. * Returns all of the currently running effects on the canvas
  18945. *
  18946. * @returns {Array}
  18947. */
  18948. static get effects() {
  18949. return Array.from(SequenceManager.VisibleEffects.values());
  18950. }
  18951. static _updatePosition(uuid, position) {
  18952. TemporaryPositionsContainer.set(uuid, position);
  18953. }
  18954. static getPositionForUUID(uuid) {
  18955. return TemporaryPositionsContainer.get(uuid);
  18956. }
  18957. /**
  18958. * Opens the Sequencer Effects UI with the effects tab open
  18959. */
  18960. static show() {
  18961. return EffectsUIApp.show({ tab: "manager" });
  18962. }
  18963. /**
  18964. * Play an effect on the canvas.
  18965. *
  18966. * @param {object} data The data that describes the audio to play
  18967. * @param {boolean} [push=true] A flag indicating whether or not to make other clients play the effect
  18968. * @returns {CanvasEffect} A CanvasEffect object
  18969. */
  18970. static async play(data, push = true) {
  18971. if (!user_can_do("permissions-effect-create")) {
  18972. custom_warning(
  18973. "Sequencer",
  18974. "EffectManager | play | Players do not have permissions to play effects. This can be configured in Sequencer's module settings."
  18975. );
  18976. return;
  18977. }
  18978. if (push)
  18979. sequencerSocket.executeForOthers(SOCKET_HANDLERS.PLAY_EFFECT, data);
  18980. if (data?.persistOptions?.persistTokenPrototype) {
  18981. this._playPrototypeTokenEffects(data, push);
  18982. }
  18983. return this._playEffect(data);
  18984. }
  18985. /**
  18986. * Get effects that are playing on the canvas based on a set of filters
  18987. *
  18988. * @param {object} inFilter An object containing filters that determine which effects to return
  18989. * - object: An ID or a PlaceableObject
  18990. * - name: The name of the effect
  18991. * - sceneId: the ID of the scene to search within
  18992. * @returns {Array} An array containing effects that match the given filter
  18993. */
  18994. static getEffects(inFilter = {}) {
  18995. const filters2 = this._validateFilters(inFilter);
  18996. if (!inFilter)
  18997. throw custom_error(
  18998. "Sequencer",
  18999. "EffectManager | getEffects | Incorrect or incomplete parameters provided"
  19000. );
  19001. return this._filterEffects(filters2);
  19002. }
  19003. /**
  19004. * Updates effects based on a set of filters
  19005. *
  19006. * @param {object} inFilter An object containing filters that determine which effects to return
  19007. * - object: An ID or a PlaceableObject
  19008. * - name: The name of the effect
  19009. * - sceneId: the ID of the scene to search within
  19010. * - effects: a single CanvasEffect or its ID, or an array of such
  19011. * @param {object} inUpdates
  19012. * @returns {promise}
  19013. */
  19014. static updateEffects(inFilter, inUpdates) {
  19015. inFilter = this._validateFilters(inFilter);
  19016. if (!inFilter)
  19017. throw custom_error(
  19018. "Sequencer",
  19019. "EffectManager | updateEffects | Incorrect or incomplete parameters provided"
  19020. );
  19021. CanvasEffect.validateUpdate(inUpdates);
  19022. const effectsToUpdate = this._filterEffects(inFilter).filter(
  19023. (effect) => effect.userCanUpdate
  19024. );
  19025. return Promise.allSettled(
  19026. effectsToUpdate.map((effect) => effect.update(inUpdates))
  19027. );
  19028. }
  19029. /**
  19030. * End effects that are playing on the canvas based on a set of filters
  19031. *
  19032. * @param {object} inFilter An object containing filters that determine which effects to end
  19033. * - object: An ID or a PlaceableObject
  19034. * - name: The name of the effect
  19035. * - sceneId: the ID of the scene to search within
  19036. * - effects: a single CanvasEffect or its ID, or an array of such
  19037. * @param {boolean} [push=true] A flag indicating whether or not to make other clients end the effects
  19038. * @returns {promise} A promise that resolves when the effects have ended
  19039. */
  19040. static async endEffects(inFilter = {}, push = true) {
  19041. inFilter = this._validateFilters(inFilter);
  19042. if (!inFilter)
  19043. throw custom_error(
  19044. "Sequencer",
  19045. "EffectManager | endEffects | Incorrect or incomplete parameters provided"
  19046. );
  19047. const effectsToEnd = this._getEffectsByFilter(inFilter);
  19048. if (!effectsToEnd.length)
  19049. return;
  19050. if (push)
  19051. sequencerSocket.executeForOthers(
  19052. SOCKET_HANDLERS.END_EFFECTS,
  19053. effectsToEnd
  19054. );
  19055. return this._endManyEffects(effectsToEnd);
  19056. }
  19057. /**
  19058. * End all effects that are playing on the canvas
  19059. *
  19060. * @param {string} [inSceneId] A parameter which determines which scene to end all effects on, defaults to current viewed scene
  19061. * @param {boolean} [push=true] A flag indicating whether or not to make other clients end all effects
  19062. * @returns {promise} A promise that resolves when all of the effects have _ended
  19063. */
  19064. static async endAllEffects(inSceneId = game.user.viewedScene, push = true) {
  19065. const inFilter = this._validateFilters({ sceneId: inSceneId });
  19066. if (!inFilter)
  19067. throw custom_error(
  19068. "Sequencer",
  19069. "EffectManager | endAllEffects | Incorrect or incomplete parameters provided"
  19070. );
  19071. const effectsToEnd = this._getEffectsByFilter(inFilter);
  19072. if (!effectsToEnd.length)
  19073. return;
  19074. if (push)
  19075. sequencerSocket.executeForOthers(
  19076. SOCKET_HANDLERS.END_EFFECTS,
  19077. effectsToEnd
  19078. );
  19079. return this._endManyEffects(effectsToEnd);
  19080. }
  19081. static _getEffectsByFilter(inFilter) {
  19082. return make_array_unique(
  19083. this._filterEffects(inFilter).filter((effect) => effect.userCanDelete).map((effect) => {
  19084. return effect.data?.persistOptions?.persistTokenPrototype ? effect.data?.persistOptions?.id ?? effect.id : effect.id;
  19085. })
  19086. );
  19087. }
  19088. /**
  19089. * If an effect has been named its position will be cached, which can be retrieved with this method
  19090. *
  19091. * @param {string} inName
  19092. * @returns {object|boolean}
  19093. * @private
  19094. */
  19095. static getEffectPositionByName(inName) {
  19096. if (!(typeof inName === "string"))
  19097. throw custom_error(
  19098. "Sequencer",
  19099. "EffectManager | getEffectPositionByName | inName must be of type string"
  19100. );
  19101. return PositionContainer.get(inName) ?? false;
  19102. }
  19103. /**
  19104. * Filters the existing effects based on the given filter
  19105. *
  19106. * @param inFilter
  19107. * @returns {array}
  19108. * @private
  19109. */
  19110. static _filterEffects(inFilter) {
  19111. if (inFilter.name) {
  19112. inFilter.name = new RegExp(
  19113. str_to_search_regex_str(safe_str(inFilter.name)),
  19114. "gu"
  19115. );
  19116. }
  19117. let effects = this.effects;
  19118. if (inFilter.sceneId && inFilter.sceneId !== canvas.scene.id) {
  19119. effects = get_all_documents_from_scene(inFilter.sceneId).map((doc) => {
  19120. return getProperty(doc, CONSTANTS.EFFECTS_FLAG);
  19121. }).filter((flags) => !!flags).map((flags) => {
  19122. return flags.map((flag) => CanvasEffect.make(flag[1]));
  19123. }).deepFlatten();
  19124. }
  19125. return effects.filter((effect) => {
  19126. 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);
  19127. });
  19128. }
  19129. /**
  19130. * Validates an object actually exists, and gets its UUID
  19131. *
  19132. * @param object
  19133. * @param sceneId
  19134. * @returns {string}
  19135. * @private
  19136. */
  19137. static _validateObject(object, sceneId) {
  19138. if (!(object instanceof foundry.abstract.Document || object instanceof PlaceableObject || typeof object === "string")) {
  19139. throw custom_error(
  19140. "Sequencer",
  19141. "EffectManager | object must be instance of PlaceableObject or of type string"
  19142. );
  19143. } else if (object instanceof PlaceableObject || object instanceof foundry.abstract.Document) {
  19144. object = get_object_identifier(object?.document ?? object);
  19145. } else if (typeof object === "string") {
  19146. const actualObject = get_object_from_scene(object, sceneId);
  19147. if (!actualObject) {
  19148. throw custom_error(
  19149. "Sequencer",
  19150. `EffectManager | could not find object with ID: ${object}`
  19151. );
  19152. }
  19153. const uuid = get_object_identifier(actualObject);
  19154. if (!uuid) {
  19155. throw custom_error(
  19156. "Sequencer",
  19157. `EffectManager | could could not establish identifier of object with ID: ${object}`
  19158. );
  19159. }
  19160. object = uuid;
  19161. }
  19162. return object;
  19163. }
  19164. /**
  19165. * Validates the filter given to any of the above public methods
  19166. *
  19167. * @param inFilter
  19168. * @returns {boolean}
  19169. * @private
  19170. */
  19171. static _validateFilters(inFilter) {
  19172. if (inFilter?.sceneId) {
  19173. if (typeof inFilter.sceneId !== "string")
  19174. throw custom_error(
  19175. "Sequencer",
  19176. "EffectManager | inFilter.sceneId must be of type string"
  19177. );
  19178. if (!game.scenes.get(inFilter.sceneId))
  19179. throw custom_error(
  19180. "Sequencer",
  19181. "EffectManager | inFilter.sceneId must be a valid scene id (could not find scene)"
  19182. );
  19183. } else {
  19184. inFilter.sceneId = game.user.viewedScene;
  19185. }
  19186. if (inFilter?.object) {
  19187. inFilter.source = this._validateObject(inFilter.object, inFilter.sceneId);
  19188. delete inFilter.object;
  19189. }
  19190. if (inFilter?.source) {
  19191. inFilter.source = this._validateObject(inFilter.source, inFilter.sceneId);
  19192. }
  19193. if (inFilter?.target) {
  19194. inFilter.target = this._validateObject(inFilter.target, inFilter.sceneId);
  19195. }
  19196. if (inFilter?.name && typeof inFilter?.name !== "string")
  19197. throw custom_error(
  19198. "Sequencer",
  19199. "EffectManager | inFilter.name must be of type string"
  19200. );
  19201. if (inFilter?.origin && typeof inFilter?.origin !== "string")
  19202. throw custom_error(
  19203. "Sequencer",
  19204. "EffectManager | inFilter.origin must be of type string"
  19205. );
  19206. if (inFilter?.effects) {
  19207. if (!Array.isArray(inFilter.effects))
  19208. inFilter.effects = [inFilter.effects];
  19209. inFilter.effects = inFilter.effects.map((effect) => {
  19210. if (!(typeof effect === "string" || effect instanceof CanvasEffect))
  19211. throw custom_error(
  19212. "Sequencer",
  19213. "EffectManager | collections in inFilter.effects must be of type string or CanvasEffect"
  19214. );
  19215. if (effect instanceof CanvasEffect)
  19216. return effect.id;
  19217. return effect;
  19218. });
  19219. }
  19220. if (!inFilter.name && !inFilter.origin && !inFilter.target && !inFilter.sceneId && !inFilter.effects && !inFilter.origin)
  19221. return false;
  19222. return foundry.utils.mergeObject(
  19223. {
  19224. effects: false,
  19225. name: false,
  19226. source: false,
  19227. target: false,
  19228. sceneId: false,
  19229. origin: false
  19230. },
  19231. inFilter
  19232. );
  19233. }
  19234. /**
  19235. * Actually plays the effect on the canvas
  19236. *
  19237. * @param data
  19238. * @param setFlags
  19239. * @returns {Promise<{duration: Promise<number>, promise: Promise<void>}>}
  19240. * @private
  19241. */
  19242. static async _playEffect(data, setFlags = true) {
  19243. const effect = CanvasEffect.make(data);
  19244. if (data.persist && setFlags && effect.context && effect.owner && !data.temporary && !data.remote) {
  19245. flagManager.addFlags(effect.context.uuid, effect.data);
  19246. }
  19247. if (!effect.shouldPlay)
  19248. return;
  19249. const playData = effect.play();
  19250. SequenceManager.VisibleEffects.add(effect.id, effect);
  19251. if (effect.data.name) {
  19252. effect._ticker.add(() => {
  19253. if (effect.isDestroyed)
  19254. return;
  19255. PositionContainer.set(effect.data.name, {
  19256. start: effect.sourcePosition,
  19257. end: effect.targetPosition
  19258. });
  19259. });
  19260. }
  19261. if (data.temporary && effect.owner) {
  19262. let lastSourcePosition = {};
  19263. let lastTargetPosition = {};
  19264. effect._ticker.add(() => {
  19265. if (effect.source && !effect.isSourceDestroyed) {
  19266. const sourceData = effect.getSourceData();
  19267. if (JSON.stringify(sourceData) !== lastSourcePosition) {
  19268. sequencerSocket.executeForOthers(
  19269. SOCKET_HANDLERS.UPDATE_POSITION,
  19270. data.source,
  19271. sourceData
  19272. );
  19273. lastSourcePosition = JSON.stringify(sourceData);
  19274. }
  19275. }
  19276. if (effect.target && !effect.isTargetDestroyed) {
  19277. const targetData = effect.getTargetData();
  19278. if (JSON.stringify(targetData) !== lastTargetPosition) {
  19279. sequencerSocket.executeForOthers(
  19280. SOCKET_HANDLERS.UPDATE_POSITION,
  19281. data.target,
  19282. targetData
  19283. );
  19284. lastTargetPosition = JSON.stringify(targetData);
  19285. }
  19286. }
  19287. });
  19288. }
  19289. if (!data.persist) {
  19290. playData.promise.then(() => this._removeEffect(effect));
  19291. }
  19292. return playData;
  19293. }
  19294. /**
  19295. * Updates a single effect with the given data
  19296. *
  19297. * @param inEffectId
  19298. * @param inUpdates
  19299. * @returns {promise|boolean}
  19300. * @private
  19301. */
  19302. static _updateEffect(inEffectId, inUpdates) {
  19303. const effect = SequenceManager.VisibleEffects.get(inEffectId);
  19304. if (!effect)
  19305. return false;
  19306. return effect._update(inUpdates);
  19307. }
  19308. /**
  19309. * Updates a single effect with new animations
  19310. *
  19311. * @param inEffectId
  19312. * @param inAnimations
  19313. * @param inLoopingAnimations
  19314. * @returns {promise|boolean}
  19315. * @private
  19316. */
  19317. static _addEffectAnimations(inEffectId, inAnimations, inLoopingAnimations) {
  19318. const effect = SequenceManager.VisibleEffects.get(inEffectId);
  19319. if (!effect)
  19320. return false;
  19321. return effect._addAnimations(inAnimations, inLoopingAnimations);
  19322. }
  19323. /**
  19324. * Sets up persisting effects when the scene is first loaded
  19325. *
  19326. * @returns {promise}
  19327. */
  19328. static async initializePersistentEffects() {
  19329. await this.tearDownPersists();
  19330. const allObjects = get_all_documents_from_scene();
  19331. allObjects.push(canvas.scene);
  19332. const docEffectsMap = allObjects.reduce((acc, doc) => {
  19333. let effects = flagManager.getFlags(doc);
  19334. effects.forEach((e) => {
  19335. if (is_UUID(e[1].source) && e[1].source !== doc.uuid) {
  19336. e[1].delete = true;
  19337. }
  19338. });
  19339. if (doc instanceof TokenDocument && doc?.actorLink) {
  19340. const actorEffects = flagManager.getFlags(doc?.actor);
  19341. actorEffects.forEach((e) => {
  19342. e[1]._id = randomID();
  19343. e[1].source = doc.uuid;
  19344. e[1].sceneId = doc.parent.id;
  19345. });
  19346. effects = effects.concat(actorEffects);
  19347. }
  19348. if (effects.length) {
  19349. acc[doc.uuid] = effects;
  19350. }
  19351. return acc;
  19352. }, {});
  19353. const promises = Object.entries(docEffectsMap).map(([uuid, effects]) => {
  19354. return this._playEffectMap(effects, fromUuidSync(uuid));
  19355. }).flat();
  19356. return Promise.all(promises).then(() => {
  19357. Hooks.callAll("sequencerEffectManagerReady");
  19358. });
  19359. }
  19360. /**
  19361. * Tears down persisting effects when the scene is unloaded
  19362. */
  19363. static tearDownPersists() {
  19364. return Promise.allSettled(
  19365. this.effects.map((effect) => {
  19366. SequenceManager.VisibleEffects.delete(effect.id);
  19367. return effect.destroy();
  19368. })
  19369. );
  19370. }
  19371. static setup() {
  19372. Hooks.on("preCreateToken", this._patchCreationData.bind(this));
  19373. Hooks.on("preCreateDrawing", this._patchCreationData.bind(this));
  19374. Hooks.on("preCreateTile", this._patchCreationData.bind(this));
  19375. Hooks.on("preCreateMeasuredTemplate", this._patchCreationData.bind(this));
  19376. Hooks.on("createToken", this._documentCreated.bind(this));
  19377. Hooks.on("createDrawing", this._documentCreated.bind(this));
  19378. Hooks.on("createTile", this._documentCreated.bind(this));
  19379. Hooks.on("createMeasuredTemplate", this._documentCreated.bind(this));
  19380. }
  19381. /**
  19382. * Patches an object's creation data before it's created so that the effect plays on it correctly
  19383. *
  19384. * @param inDocument
  19385. * @param data
  19386. * @param options
  19387. * @returns {*}
  19388. */
  19389. static async _patchCreationData(inDocument, data, options) {
  19390. const effects = flagManager.getFlags(inDocument);
  19391. if (!effects?.length)
  19392. return;
  19393. const updates = {};
  19394. let documentUuid;
  19395. if (!inDocument._id) {
  19396. const documentId = randomID();
  19397. documentUuid = inDocument.uuid + documentId;
  19398. updates["_id"] = documentId;
  19399. options.keepId = true;
  19400. } else {
  19401. documentUuid = inDocument.uuid;
  19402. }
  19403. updates[CONSTANTS.EFFECTS_FLAG] = this._patchEffectDataForDocument(
  19404. documentUuid,
  19405. effects
  19406. );
  19407. return inDocument.updateSource(updates);
  19408. }
  19409. static _patchEffectDataForDocument(inDocumentUuid, effects) {
  19410. return effects.map((effect) => {
  19411. effect[0] = randomID();
  19412. const effectData = effect[1];
  19413. effectData._id = effect[0];
  19414. if (is_UUID(effectData.source)) {
  19415. if (effectData.masks.includes(effectData.source)) {
  19416. const index = effectData.masks.indexOf(effectData.source);
  19417. effectData.masks[index] = inDocumentUuid;
  19418. }
  19419. effectData.source = inDocumentUuid;
  19420. }
  19421. effectData.sceneId = inDocumentUuid.split(".")[1];
  19422. return effect;
  19423. });
  19424. }
  19425. /**
  19426. * Plays the effects of a given document on creation
  19427. *
  19428. * @param inDocument
  19429. * @returns {*}
  19430. */
  19431. static async _documentCreated(inDocument) {
  19432. let effects = flagManager.getFlags(inDocument);
  19433. if (inDocument instanceof TokenDocument && inDocument?.actorLink) {
  19434. let actorEffects = flagManager.getFlags(inDocument.actor);
  19435. if (actorEffects.length) {
  19436. actorEffects = this._patchEffectDataForDocument(
  19437. inDocument.uuid,
  19438. actorEffects
  19439. );
  19440. }
  19441. effects = effects.concat(actorEffects);
  19442. }
  19443. if (!effects?.length)
  19444. return;
  19445. return this._playEffectMap(effects, inDocument);
  19446. }
  19447. /**
  19448. * Plays multiple effects at the same time
  19449. *
  19450. * @param inEffects
  19451. * @param inDocument
  19452. * @returns {Promise<{duration: Promise<number>, promise: Promise<void>}[]>}
  19453. * @private
  19454. */
  19455. static _playEffectMap(inEffects, inDocument) {
  19456. if (inEffects instanceof Map)
  19457. inEffects = Array.from(inEffects);
  19458. return Promise.all(
  19459. inEffects.map((effect) => {
  19460. if (!CanvasEffect.checkValid(effect[1])) {
  19461. if (!game.user.isGM)
  19462. return;
  19463. custom_warning(
  19464. `Sequencer`,
  19465. `Removed effect from ${inDocument.uuid} as it no longer had a valid source or target`
  19466. );
  19467. return flagManager.removeFlags(inDocument.uuid, effect);
  19468. }
  19469. return this._playEffect(effect[1], false).then((result) => {
  19470. if (!result) {
  19471. debug("Error playing effect");
  19472. }
  19473. }).catch((err) => {
  19474. debug("Error playing effect:", err);
  19475. });
  19476. })
  19477. );
  19478. }
  19479. /**
  19480. * Ends one or many effects at the same time, returning a promise that resolves once every effect has fully ended
  19481. *
  19482. * @param inEffectIds
  19483. * @returns {Promise}
  19484. * @private
  19485. */
  19486. static async _endManyEffects(inEffectIds = false) {
  19487. const actorEffectsToEnd = this.effects.filter((effect) => {
  19488. return effect.context?.actorLink && inEffectIds.includes(effect.data?.persistOptions?.id);
  19489. });
  19490. const effectsByActorUuid = Object.values(
  19491. group_by(actorEffectsToEnd, "context.actor.uuid")
  19492. );
  19493. const regularEffectsToEnd = this.effects.filter((effect) => {
  19494. return inEffectIds.includes(effect.id) || !effect.context?.actorLink && inEffectIds.includes(effect.data?.persistOptions?.id);
  19495. });
  19496. const effectsByContextUuid = Object.values(
  19497. group_by(regularEffectsToEnd, "context.uuid")
  19498. );
  19499. effectsByContextUuid.forEach((effects) => {
  19500. effects = effects.filter(
  19501. (effect) => effect.data.persist && !effect.data.temporary
  19502. );
  19503. if (!effects.length)
  19504. return;
  19505. const effectData = effects.map((effect) => effect.data);
  19506. flagManager.removeFlags(
  19507. effects[0].context.uuid,
  19508. effectData,
  19509. !inEffectIds
  19510. );
  19511. });
  19512. effectsByActorUuid.forEach((effects) => {
  19513. effects = effects.filter(
  19514. (effect) => effect.data.persist && !effect.data.temporary
  19515. );
  19516. if (!effects.length)
  19517. return;
  19518. const effectContext = effects[0].context;
  19519. const effectData = effects.map((effect) => effect.data);
  19520. if (!(effectContext instanceof TokenDocument && effectContext.actorLink && effectContext.actor.prototypeToken.actorLink)) {
  19521. return;
  19522. }
  19523. const persistentEffectData = effectData.filter(
  19524. (data) => data?.persistOptions?.persistTokenPrototype
  19525. );
  19526. if (!persistentEffectData.length)
  19527. return;
  19528. const actorEffects = flagManager.getFlags(effectContext.actor);
  19529. const applicableActorEffects = actorEffects.filter((effect) => {
  19530. return effect[1]?.persistOptions?.persistTokenPrototype && persistentEffectData.some(
  19531. (persistentEffect) => persistentEffect.persistOptions.id === effect[1]?.persistOptions?.id
  19532. );
  19533. }).map((e) => e[0]);
  19534. flagManager.removeFlags(
  19535. effectContext.actor.uuid,
  19536. applicableActorEffects,
  19537. !inEffectIds
  19538. );
  19539. });
  19540. const effectsToEnd = effectsByContextUuid.concat(effectsByActorUuid).deepFlatten();
  19541. return Promise.allSettled(
  19542. effectsToEnd.map((effect) => this._removeEffect(effect))
  19543. );
  19544. }
  19545. static _effectContextFilter(inUUID, effectData) {
  19546. return effectData?.source === inUUID || effectData?.target === inUUID || (effectData?.tiedDocuments ?? []).indexOf(inUUID) > -1;
  19547. }
  19548. /**
  19549. * Handles the deletion of objects that effects are attached to
  19550. *
  19551. * @param inUUID
  19552. * @returns {Promise}
  19553. */
  19554. static objectDeleted(inUUID) {
  19555. const documentsToCheck = game.scenes.filter((scene) => scene.id !== game.user.viewedScene).map((scene) => [scene, ...get_all_documents_from_scene(scene.id)]).deepFlatten();
  19556. const documentEffectsToEnd = documentsToCheck.map((obj) => {
  19557. const objEffects = flagManager.getFlags(obj);
  19558. const effectsToEnd = objEffects.filter(
  19559. ([effectId, effectData]) => this._effectContextFilter(inUUID, effectData)
  19560. );
  19561. return {
  19562. document: obj,
  19563. effects: effectsToEnd.map((effect) => effect[0])
  19564. };
  19565. }).filter((obj) => obj.effects.length);
  19566. const visibleEffectsToEnd = this.effects.filter((effect) => this._effectContextFilter(inUUID, effect.data)).map((e) => e.id);
  19567. return Promise.allSettled([
  19568. this._endManyEffects(visibleEffectsToEnd),
  19569. ...documentEffectsToEnd.map((obj) => {
  19570. return flagManager.removeFlags(obj.document.uuid, obj.effects);
  19571. })
  19572. ]);
  19573. }
  19574. /**
  19575. * Removes the effect from the manager and ends it, returning a promise that resolves once the effect has fully _ended
  19576. *
  19577. * @param effect
  19578. * @returns {Promise}
  19579. * @private
  19580. */
  19581. static _removeEffect(effect) {
  19582. SequenceManager.VisibleEffects.delete(effect.id);
  19583. TemporaryPositionsContainer.delete(effect.data.source);
  19584. TemporaryPositionsContainer.delete(effect.data.target);
  19585. return effect.endEffect();
  19586. }
  19587. static async _playPrototypeTokenEffects(data, push) {
  19588. if (!is_UUID(data.source))
  19589. return;
  19590. const object = fromUuidSync(data.source);
  19591. if (!(object instanceof TokenDocument))
  19592. return;
  19593. const tokenEffectsToPlay = game.scenes.map(
  19594. (scene) => scene.tokens.filter((token) => {
  19595. return token.actorLink && token.actor === object.actor && token !== object;
  19596. })
  19597. ).deepFlatten();
  19598. for (const tokenDocument of tokenEffectsToPlay) {
  19599. const duplicatedData = foundry.utils.deepClone(data);
  19600. duplicatedData._id = randomID();
  19601. duplicatedData.sceneId = tokenDocument.uuid.split(".")[1];
  19602. duplicatedData.masks = duplicatedData.masks.map((uuid) => uuid.replace(duplicatedData.source, tokenDocument.uuid)).filter((uuid) => uuid.includes(duplicatedData.sceneId));
  19603. duplicatedData.source = tokenDocument.uuid;
  19604. if (CanvasEffect.checkValid(duplicatedData)) {
  19605. if (push)
  19606. sequencerSocket.executeForOthers(
  19607. SOCKET_HANDLERS.PLAY_EFFECT,
  19608. duplicatedData
  19609. );
  19610. if (duplicatedData.sceneId === game.user.viewedScene) {
  19611. await this._playEffect(duplicatedData, false);
  19612. }
  19613. }
  19614. }
  19615. }
  19616. }
  19617. class BaseEffectsLayer extends InteractionLayer {
  19618. static get layerOptions() {
  19619. return foundry.utils.mergeObject(super.layerOptions, {
  19620. elevation: 1e8,
  19621. name: "sequencerEffects"
  19622. });
  19623. }
  19624. }
  19625. class SequencerInterfaceLayer extends InteractionLayer {
  19626. constructor(...args) {
  19627. super(...args);
  19628. }
  19629. static get layerOptions() {
  19630. return foundry.utils.mergeObject(super.layerOptions, {
  19631. elevation: 1e8,
  19632. name: "sequencerInterfaceEffects"
  19633. });
  19634. }
  19635. deactivate() {
  19636. super.deactivate();
  19637. if (!this.active)
  19638. return;
  19639. this._clearChildren();
  19640. this.active = false;
  19641. InteractionManager.tearDown();
  19642. }
  19643. _setup() {
  19644. if (!this.UIContainer || this.UIContainer._destroyed) {
  19645. this.UIContainer = new PIXI.Container();
  19646. this.UIContainer.sortableChildren = true;
  19647. this.UIContainer.parentName = "sequencerUIContainer";
  19648. this.UIContainer.zIndex = 1e13;
  19649. this.addChild(this.UIContainer);
  19650. this.linePoint = this.UIContainer.addChild(new PIXI.Graphics());
  19651. this.line = this.UIContainer.addChild(new PIXI.Graphics());
  19652. this.lineHead = this.UIContainer.addChild(new PIXI.Graphics());
  19653. this.suggestionPoint = this.UIContainer.addChild(new PIXI.Graphics());
  19654. this.effectHoverBoxes = this.UIContainer.addChild(new PIXI.Graphics());
  19655. this.effectSelectionBorder = this.UIContainer.addChild(
  19656. new PIXI.Graphics()
  19657. );
  19658. this.effectSourcePosition = this.UIContainer.addChild(
  19659. new PIXI.Graphics()
  19660. );
  19661. this.effectTargetPosition = this.UIContainer.addChild(
  19662. new PIXI.Graphics()
  19663. );
  19664. this.suggestionPoint.filters = [new PIXI.filters.AlphaFilter(0.75)];
  19665. this.effectSourcePosition.filters = [new PIXI.filters.AlphaFilter(0.75)];
  19666. this.effectTargetPosition.filters = [new PIXI.filters.AlphaFilter(0.75)];
  19667. this.effectSelectionBorder.zIndex = 1;
  19668. this.effectSourcePosition.interactive = true;
  19669. this.effectSourcePosition.on("mousedown", () => {
  19670. SelectionManager.sourcePointSelected();
  19671. });
  19672. this.effectTargetPosition.interactive = true;
  19673. this.effectTargetPosition.on("mousedown", () => {
  19674. SelectionManager.targetPointSelected();
  19675. });
  19676. }
  19677. }
  19678. async _draw(...args) {
  19679. }
  19680. render(...args) {
  19681. super.render(...args);
  19682. this._setup();
  19683. this._clearChildren();
  19684. this._drawHoveredEffectElements();
  19685. if (!this.active)
  19686. return;
  19687. this._drawLine();
  19688. this._drawPoints();
  19689. this._drawSelectedEffectElements();
  19690. this._drawSuggestionPoint();
  19691. }
  19692. _clearChildren() {
  19693. if (!this.UIContainer)
  19694. return;
  19695. this.UIContainer.children.forEach((child) => {
  19696. child.clear();
  19697. });
  19698. }
  19699. _drawLine() {
  19700. if (!EffectPlayer.startPos || !EffectPlayer.endPos || game?.activeTool !== "play-effect")
  19701. return;
  19702. this.line.lineStyle(3, CONSTANTS.COLOR.PRIMARY, 1);
  19703. this.line.moveTo(EffectPlayer.startPos.x, EffectPlayer.startPos.y);
  19704. this.line.lineTo(EffectPlayer.endPos.x, EffectPlayer.endPos.y);
  19705. }
  19706. _drawPoints() {
  19707. if (game?.activeTool !== "play-effect")
  19708. return;
  19709. const startPos = EffectPlayer.startPos || EffectPlayer.cursorPos;
  19710. this.linePoint.beginFill(CONSTANTS.COLOR.PRIMARY);
  19711. this.linePoint.drawCircle(startPos.x, startPos.y, 5);
  19712. if (EffectPlayer.sourceAttachFound) {
  19713. this._drawCrossAtLocation(this.linePoint, startPos);
  19714. }
  19715. if (!EffectPlayer.endPos)
  19716. return;
  19717. const angle = new Ray(startPos, EffectPlayer.endPos).angle;
  19718. this.lineHead.beginFill(CONSTANTS.COLOR.PRIMARY);
  19719. this.lineHead.moveTo(0, -5);
  19720. this.lineHead.lineTo(-15, 30);
  19721. this.lineHead.lineTo(15, 30);
  19722. this.lineHead.endFill();
  19723. this.lineHead.rotation = angle + Math.PI / 2;
  19724. this.lineHead.position.set(EffectPlayer.endPos.x, EffectPlayer.endPos.y);
  19725. if (EffectPlayer.targetAttachFound) {
  19726. this.linePoint.beginFill(CONSTANTS.COLOR.SECONDARY);
  19727. this._drawCrossAtLocation(this.linePoint, EffectPlayer.endPos);
  19728. }
  19729. }
  19730. _drawHoveredEffectElements() {
  19731. const effects = new Set(SelectionManager.hoveredEffects);
  19732. if (SelectionManager.hoveredEffectUI)
  19733. effects.add(SelectionManager.hoveredEffectUI);
  19734. for (const effect of effects) {
  19735. if (!effect || effect === SelectionManager.selectedEffect || effect.data.screenSpace || effect._isEnding)
  19736. continue;
  19737. this._drawBoxAroundEffect(this.effectHoverBoxes, effect);
  19738. }
  19739. }
  19740. _drawSelectedEffectElements() {
  19741. if (!SelectionManager.selectedEffect)
  19742. return;
  19743. this._drawBoxAroundEffect(
  19744. this.effectSelectionBorder,
  19745. SelectionManager.selectedEffect,
  19746. true
  19747. );
  19748. this._drawEffectStartEndPoints(SelectionManager.selectedEffect);
  19749. }
  19750. _drawBoxAroundEffect(graphic, effect, selected = false) {
  19751. if (!effect || effect._destroyed || !effect.spriteContainer || !effect.ready)
  19752. return;
  19753. graphic.lineStyle(3, selected ? CONSTANTS.COLOR.PRIMARY : 16777215, 0.9);
  19754. let boundingBox = effect.sprite.getLocalBounds();
  19755. let dimensions = {
  19756. x: effect.position.x + boundingBox.x * effect.sprite.scale.x,
  19757. y: effect.position.y + boundingBox.y * effect.sprite.scale.y,
  19758. width: boundingBox.width * effect.sprite.scale.x,
  19759. height: boundingBox.height * effect.sprite.scale.y
  19760. };
  19761. if (effect.data.shapes.length) {
  19762. for (const shape of Object.values(effect.shapes)) {
  19763. boundingBox = shape.getLocalBounds();
  19764. dimensions = {
  19765. x: Math.min(
  19766. dimensions.x,
  19767. effect.position.x + boundingBox.x * shape.scale.x
  19768. ),
  19769. y: Math.min(
  19770. dimensions.y,
  19771. effect.position.y + boundingBox.y * shape.scale.y
  19772. ),
  19773. width: Math.max(dimensions.width, boundingBox.width * shape.scale.x),
  19774. height: Math.max(
  19775. dimensions.height,
  19776. boundingBox.height * shape.scale.y
  19777. )
  19778. };
  19779. }
  19780. }
  19781. const rotation2 = Math.normalizeRadians(
  19782. effect.rotationContainer.rotation + effect.spriteContainer.rotation + effect.sprite.rotation
  19783. );
  19784. this._drawRectangle(graphic, effect.position, rotation2, dimensions);
  19785. }
  19786. _drawRectangle(graphic, position, rotation2, dimensions) {
  19787. graphic.moveTo(
  19788. ...rotate_coordinate(
  19789. position,
  19790. {
  19791. x: dimensions.x,
  19792. y: dimensions.y
  19793. },
  19794. -rotation2
  19795. )
  19796. );
  19797. graphic.lineTo(
  19798. ...rotate_coordinate(
  19799. position,
  19800. {
  19801. x: dimensions.x + dimensions.width,
  19802. y: dimensions.y
  19803. },
  19804. -rotation2
  19805. )
  19806. );
  19807. graphic.lineTo(
  19808. ...rotate_coordinate(
  19809. position,
  19810. {
  19811. x: dimensions.x + dimensions.width,
  19812. y: dimensions.y + dimensions.height
  19813. },
  19814. -rotation2
  19815. )
  19816. );
  19817. graphic.lineTo(
  19818. ...rotate_coordinate(
  19819. position,
  19820. {
  19821. x: dimensions.x,
  19822. y: dimensions.y + dimensions.height
  19823. },
  19824. -rotation2
  19825. )
  19826. );
  19827. graphic.lineTo(
  19828. ...rotate_coordinate(
  19829. position,
  19830. {
  19831. x: dimensions.x,
  19832. y: dimensions.y
  19833. },
  19834. -rotation2
  19835. )
  19836. );
  19837. graphic.lineTo(
  19838. ...rotate_coordinate(
  19839. position,
  19840. {
  19841. x: dimensions.x + dimensions.width,
  19842. y: dimensions.y
  19843. },
  19844. -rotation2
  19845. )
  19846. );
  19847. }
  19848. /**
  19849. * Draws the start/end point circles
  19850. * @private
  19851. */
  19852. _drawEffectStartEndPoints(effect) {
  19853. if (!effect || effect._destroyed || !effect.spriteContainer)
  19854. return;
  19855. if (!effect.data.stretchTo || !effect.sourcePosition || !effect.targetPosition)
  19856. return;
  19857. this.effectSourcePosition.beginFill(CONSTANTS.COLOR.PRIMARY);
  19858. this.effectSourcePosition.drawCircle(
  19859. effect.sourcePosition.x,
  19860. effect.sourcePosition.y,
  19861. canvas.grid.size * 0.25
  19862. );
  19863. if (typeof effect.data.source === "string") {
  19864. this._drawCrossAtLocation(
  19865. this.effectSourcePosition,
  19866. effect.sourcePosition
  19867. );
  19868. }
  19869. this.effectTargetPosition.beginFill(CONSTANTS.COLOR.SECONDARY);
  19870. this.effectTargetPosition.drawCircle(
  19871. effect.targetPosition.x,
  19872. effect.targetPosition.y,
  19873. canvas.grid.size * 0.25
  19874. );
  19875. this.effectTargetPosition.alpha = 0.75;
  19876. if (typeof effect.data.target === "string") {
  19877. this._drawCrossAtLocation(
  19878. this.effectTargetPosition,
  19879. effect.targetPosition
  19880. );
  19881. }
  19882. }
  19883. _drawSuggestionPoint() {
  19884. if (!SelectionManager.suggestedProperties || !SelectionManager.selectedEffect)
  19885. return;
  19886. const effect = SelectionManager.selectedEffect;
  19887. const suggestion = SelectionManager.suggestedProperties;
  19888. this.suggestionPoint.position.set(0, 0);
  19889. this.suggestionPoint.rotation = 0;
  19890. if (effect.data.stretchTo) {
  19891. this.suggestionPoint.beginFill(suggestion.color);
  19892. this.suggestionPoint.drawCircle(
  19893. suggestion.position.x,
  19894. suggestion.position.y,
  19895. canvas.grid.size * 0.25
  19896. );
  19897. if (suggestion.showCursor) {
  19898. this._drawCrossAtLocation(this.suggestionPoint, suggestion.position);
  19899. }
  19900. return;
  19901. }
  19902. const boundingBox = effect.spriteContainer.getLocalBounds();
  19903. const dimensions = {
  19904. x: boundingBox.x * effect.scale.x,
  19905. y: boundingBox.y * effect.scale.y,
  19906. width: boundingBox.width * effect.scale.x,
  19907. height: boundingBox.height * effect.scale.y
  19908. };
  19909. this.suggestionPoint.lineStyle(3, CONSTANTS.COLOR.PRIMARY, 0.9);
  19910. this.suggestionPoint.position.set(
  19911. suggestion.position.x,
  19912. suggestion.position.y
  19913. );
  19914. this._drawRectangle(
  19915. this.suggestionPoint,
  19916. suggestion.position,
  19917. effect.rotation,
  19918. dimensions,
  19919. true
  19920. );
  19921. if (suggestion.showCursor) {
  19922. this.suggestionPoint.beginFill(CONSTANTS.COLOR.SECONDARY);
  19923. this._drawCrossAtLocation(this.suggestionPoint);
  19924. }
  19925. if (suggestion.showPoint) {
  19926. this.suggestionPoint.drawCircle(0, 0, canvas.grid.size * 0.2);
  19927. }
  19928. }
  19929. _drawCrossAtLocation(inElement, inPosition = { x: 0, y: 0 }) {
  19930. inElement.drawRect(
  19931. inPosition.x + canvas.grid.size * -0.05,
  19932. inPosition.y + canvas.grid.size * -0.5,
  19933. canvas.grid.size * 0.1,
  19934. canvas.grid.size
  19935. );
  19936. inElement.drawRect(
  19937. inPosition.x + canvas.grid.size * -0.5,
  19938. inPosition.y + canvas.grid.size * -0.05,
  19939. canvas.grid.size,
  19940. canvas.grid.size * 0.1
  19941. );
  19942. }
  19943. }
  19944. class UIEffectsLayer extends InteractionLayer {
  19945. static get layerOptions() {
  19946. return foundry.utils.mergeObject(super.layerOptions, {
  19947. zIndex: 999999999999999,
  19948. name: "sequencerEffectsAboveEverything"
  19949. });
  19950. }
  19951. updateTransform() {
  19952. if (this.sortableChildren && this.sortDirty) {
  19953. this.sortChildren();
  19954. }
  19955. this._boundsID++;
  19956. this.transform.updateTransform(PIXI.Transform.IDENTITY);
  19957. this.worldAlpha = this.alpha;
  19958. for (let child of this.children) {
  19959. if (child.visible) {
  19960. child.updateTransform();
  19961. }
  19962. }
  19963. }
  19964. }
  19965. let layer = false;
  19966. class SequencerAboveUILayer {
  19967. constructor(name, zIndex = 0.1) {
  19968. this.canvas = document.createElement("canvas");
  19969. this.canvas.id = name;
  19970. this.canvas.style.cssText = `
  19971. position:absolute;
  19972. touch-action: none;
  19973. pointer-events: none;
  19974. width:100%;
  19975. height:100%;
  19976. z-index:${zIndex};
  19977. padding: 0;
  19978. margin: 0;
  19979. `;
  19980. document.body.appendChild(this.canvas);
  19981. this.app = new PIXI.Application({
  19982. width: window.innerWidth,
  19983. height: window.innerHeight,
  19984. view: this.canvas,
  19985. antialias: true,
  19986. backgroundAlpha: 0,
  19987. sharedTicker: true
  19988. });
  19989. this.app.resizeTo = window;
  19990. this.app.stage.renderable = false;
  19991. }
  19992. static setup() {
  19993. if (!game.settings.get("sequencer", "enable-above-ui-screenspace"))
  19994. return;
  19995. layer = new this("sequencerUILayerAbove", 1e4);
  19996. }
  19997. static getLayer() {
  19998. return layer ? layer.app.stage : canvas.uiEffectsLayer;
  19999. }
  20000. static addChild(...args) {
  20001. const result = this.getLayer().addChild(...args);
  20002. layer.app.stage.renderable = layer.app.stage.children.length > 0;
  20003. return result;
  20004. }
  20005. static sortChildren() {
  20006. return this.getLayer().sortChildren();
  20007. }
  20008. static removeContainerByEffect(inEffect) {
  20009. const child = this.getLayer().children.find((child2) => child2 === inEffect);
  20010. if (!child)
  20011. return;
  20012. this.getLayer().removeChild(child);
  20013. layer.app.stage.renderable = layer.app.stage.children.length > 0;
  20014. }
  20015. updateTransform() {
  20016. if (this.app.stage.sortableChildren && this.app.stage.sortDirty) {
  20017. this.app.stage.sortChildren();
  20018. }
  20019. this.app.stage._boundsID++;
  20020. this.app.stage.transform.updateTransform(PIXI.Transform.IDENTITY);
  20021. this.app.stage.worldAlpha = this.app.stage.alpha;
  20022. for (let child of this.app.stage.children) {
  20023. if (child.visible) {
  20024. child.updateTransform();
  20025. }
  20026. }
  20027. }
  20028. }
  20029. class VisionSamplerShader extends BaseSamplerShader {
  20030. /** @override */
  20031. static classPluginName = null;
  20032. /** @inheritdoc */
  20033. static vertexShader = `
  20034. precision ${PIXI.settings.PRECISION_VERTEX} float;
  20035. attribute vec2 aVertexPosition;
  20036. attribute vec2 aTextureCoord;
  20037. uniform mat3 projectionMatrix;
  20038. uniform vec2 screenDimensions;
  20039. varying vec2 vUvsMask;
  20040. varying vec2 vUvs;
  20041. void main() {
  20042. vUvs = aTextureCoord;
  20043. vUvsMask = aVertexPosition / screenDimensions;
  20044. gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
  20045. }
  20046. `;
  20047. /** @inheritdoc */
  20048. static fragmentShader = `
  20049. precision ${PIXI.settings.PRECISION_FRAGMENT} float;
  20050. varying vec2 vUvs;
  20051. varying vec2 vUvsMask;
  20052. uniform vec4 tintAlpha;
  20053. uniform sampler2D sampler;
  20054. uniform sampler2D maskSampler;
  20055. uniform bool enableVisionMasking;
  20056. void main() {
  20057. float mask = enableVisionMasking ? texture2D(maskSampler, vUvsMask).r : 1.0;
  20058. gl_FragColor = texture2D(sampler, vUvs) * tintAlpha * mask;
  20059. }
  20060. `;
  20061. /** @inheritdoc */
  20062. static defaultUniforms = {
  20063. tintAlpha: [1, 1, 1, 1],
  20064. sampler: 0,
  20065. maskSampler: 0,
  20066. screenDimensions: [1, 1],
  20067. enableVisionMasking: false
  20068. };
  20069. /** @override */
  20070. _preRender(mesh) {
  20071. super._preRender(mesh);
  20072. this.uniforms.maskSampler = canvas.masks.vision.renderTexture;
  20073. this.uniforms.screenDimensions = canvas.screenDimensions;
  20074. this.uniforms.enableVisionMasking = canvas.effects.visibility.visible;
  20075. }
  20076. }
  20077. class MaskFilter extends AbstractBaseFilter {
  20078. /** @override */
  20079. static fragmentShader = ` varying vec2 vTextureCoord;
  20080. uniform sampler2D uSampler;
  20081. uniform sampler2D uMaskSampler;
  20082. void main(void) {
  20083. gl_FragColor = texture2D(uSampler, vTextureCoord)
  20084. * texture2D(uMaskSampler, vTextureCoord).a;
  20085. }`;
  20086. /** @override */
  20087. static defaultUniforms = { uMaskSampler: null };
  20088. /** @type {DisplayObject[]|null} */
  20089. masks = [];
  20090. /** @override */
  20091. apply(filterManager, input, output, clearMode, currentState) {
  20092. const maskFilterTexture = filterManager.getFilterTexture();
  20093. const originalFilterTexture = this.#push(
  20094. filterManager,
  20095. currentState,
  20096. maskFilterTexture
  20097. );
  20098. const renderer = filterManager.renderer;
  20099. for (const mask of this.masks) {
  20100. if (mask?.obj?.destroyed)
  20101. continue;
  20102. const renderable = mask.renderable;
  20103. mask.renderable = true;
  20104. mask.render(renderer);
  20105. mask.renderable = renderable;
  20106. }
  20107. renderer.batch.flush();
  20108. this.#pop(filterManager, currentState, originalFilterTexture);
  20109. this.uniforms.uMaskSampler = maskFilterTexture;
  20110. filterManager.applyFilter(this, input, output, clearMode);
  20111. filterManager.returnFilterTexture(maskFilterTexture);
  20112. }
  20113. #push(filterManager, currentState, maskFilterTexture) {
  20114. const originalFilterTexture = currentState.renderTexture;
  20115. currentState.renderTexture = maskFilterTexture;
  20116. filterManager.defaultFilterStack.push(currentState);
  20117. filterManager.bindAndClear(maskFilterTexture);
  20118. return originalFilterTexture;
  20119. }
  20120. #pop(filterManager, currentState, originalFilterTexture) {
  20121. currentState.renderTexture = originalFilterTexture;
  20122. filterManager.defaultFilterStack.pop();
  20123. if (filterManager.activeState === currentState) {
  20124. return;
  20125. }
  20126. filterManager.activeState = currentState;
  20127. const globalUniforms = filterManager.globalUniforms.uniforms;
  20128. globalUniforms.outputFrame = currentState.sourceFrame;
  20129. globalUniforms.resolution = currentState.resolution;
  20130. const inputSize = globalUniforms.inputSize;
  20131. const inputPixel = globalUniforms.inputPixel;
  20132. const inputClamp = globalUniforms.inputClamp;
  20133. inputSize[0] = currentState.destinationFrame.width;
  20134. inputSize[1] = currentState.destinationFrame.height;
  20135. inputSize[2] = 1 / inputSize[0];
  20136. inputSize[3] = 1 / inputSize[1];
  20137. inputPixel[0] = Math.round(inputSize[0] * currentState.resolution);
  20138. inputPixel[1] = Math.round(inputSize[1] * currentState.resolution);
  20139. inputPixel[2] = 1 / inputPixel[0];
  20140. inputPixel[3] = 1 / inputPixel[1];
  20141. inputClamp[0] = 0.5 * inputPixel[2];
  20142. inputClamp[1] = 0.5 * inputPixel[3];
  20143. inputClamp[2] = currentState.sourceFrame.width * inputSize[2] - 0.5 * inputPixel[2];
  20144. inputClamp[3] = currentState.sourceFrame.height * inputSize[3] - 0.5 * inputPixel[3];
  20145. if (currentState.legacy) {
  20146. const filterArea = globalUniforms.filterArea;
  20147. filterArea[0] = currentState.destinationFrame.width;
  20148. filterArea[1] = currentState.destinationFrame.height;
  20149. filterArea[2] = currentState.sourceFrame.x;
  20150. filterArea[3] = currentState.sourceFrame.y;
  20151. globalUniforms.filterClamp = globalUniforms.inputClamp;
  20152. }
  20153. filterManager.globalUniforms.update();
  20154. }
  20155. }
  20156. const hooksManager = {
  20157. _hooks: /* @__PURE__ */ new Map(),
  20158. _hooksRegistered: /* @__PURE__ */ new Set(),
  20159. addHook(effectUuid, hookName, callable, callNow = false) {
  20160. if (!this._hooksRegistered.has(hookName)) {
  20161. debug("registering hook for: " + hookName);
  20162. this._hooksRegistered.add(hookName);
  20163. Hooks.on(hookName, (...args) => {
  20164. this._hookCalled(hookName, ...args);
  20165. });
  20166. }
  20167. const key = hookName + "-" + effectUuid;
  20168. if (!this._hooks.has(key)) {
  20169. this._hooks.set(key, []);
  20170. }
  20171. this._hooks.get(key).push(callable);
  20172. if (callNow) {
  20173. setTimeout(() => {
  20174. callable();
  20175. }, 20);
  20176. }
  20177. },
  20178. _hookCalled(hookName, ...args) {
  20179. Array.from(this._hooks).filter((entry) => entry[0].startsWith(hookName + "-")).map((hooks) => hooks[1]).deepFlatten().forEach((callback) => callback(...args));
  20180. },
  20181. removeHooks(effectUuid) {
  20182. Array.from(this._hooks).filter((entry) => entry[0].endsWith("-" + effectUuid)).forEach((entry) => this._hooks.delete(entry[0]));
  20183. }
  20184. };
  20185. class CanvasEffect extends PIXI.Container {
  20186. #elevation = 0;
  20187. #sort = 0;
  20188. constructor(inData) {
  20189. super();
  20190. this.sortableChildren = true;
  20191. this.actualCreationTime = +new Date();
  20192. this.data = inData;
  20193. this._resolve = null;
  20194. this._durationResolve = null;
  20195. this.ready = false;
  20196. this._ended = false;
  20197. this._isEnding = false;
  20198. this._cachedSourceData = {};
  20199. this._cachedTargetData = {};
  20200. this.uuid = false;
  20201. }
  20202. static get protectedValues() {
  20203. return [
  20204. "_id",
  20205. "sequenceId",
  20206. "creationTimestamp",
  20207. "creatorUserId",
  20208. "moduleName",
  20209. "index",
  20210. "repetition",
  20211. "moves",
  20212. "fadeIn",
  20213. "fadeOut",
  20214. "scaleIn",
  20215. "scaleOut",
  20216. "rotateIn",
  20217. "rotateOut",
  20218. "fadeInAudio",
  20219. "fadeOutAudio",
  20220. "animations",
  20221. "nameOffsetMap",
  20222. "persist"
  20223. ];
  20224. }
  20225. /** @type {number} */
  20226. get elevation() {
  20227. return this.#elevation;
  20228. }
  20229. set elevation(value) {
  20230. this.#elevation = value;
  20231. }
  20232. /** @type {number} */
  20233. get sort() {
  20234. return this.#sort;
  20235. }
  20236. set sort(value) {
  20237. this.#sort = value;
  20238. }
  20239. get context() {
  20240. return this.data.attachTo?.active && this.sourceDocument ? this.sourceDocument : game.scenes.get(this.data.sceneId);
  20241. }
  20242. get isIsometricActive() {
  20243. const sceneIsIsometric = getProperty(
  20244. game.scenes.get(this.data.sceneId),
  20245. CONSTANTS.INTEGRATIONS.ISOMETRIC.SCENE_ENABLED
  20246. );
  20247. return CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE && sceneIsIsometric;
  20248. }
  20249. /**
  20250. * The ID of the effect
  20251. *
  20252. * @returns {string}
  20253. */
  20254. get id() {
  20255. return this.data._id;
  20256. }
  20257. /**
  20258. * Whether this effect is destroyed or is in the process of being destroyed
  20259. */
  20260. get isDestroyed() {
  20261. return this.source && this.isSourceDestroyed || this.target && this.isTargetDestroyed;
  20262. }
  20263. /**
  20264. * Whether the source of this effect is temporary
  20265. *
  20266. * @returns {boolean}
  20267. */
  20268. get isSourceTemporary() {
  20269. return this.data.attachTo?.active && this.sourceDocument && !is_UUID(this.sourceDocument?.uuid);
  20270. }
  20271. /**
  20272. * Whether the source of this effect has been destroyed
  20273. *
  20274. * @returns {boolean}
  20275. */
  20276. get isSourceDestroyed() {
  20277. return this.source && (this.source?.destroyed || !this.sourceDocument?.object);
  20278. }
  20279. /**
  20280. * Whether the target of this effect is temporary
  20281. *
  20282. * @returns {boolean}
  20283. */
  20284. get isTargetTemporary() {
  20285. return (this.data.stretchTo?.attachTo || this.data.rotateTowards?.attachTo) && this.targetDocument && !is_UUID(this.targetDocument.uuid);
  20286. }
  20287. /**
  20288. * Whether the target of this effect has been destroyed
  20289. *
  20290. * @returns {boolean}
  20291. */
  20292. get isTargetDestroyed() {
  20293. return this.target && (this.target?.destroyed || !this.targetDocument?.object);
  20294. }
  20295. /**
  20296. * The source object (or source location) of the effect
  20297. *
  20298. * @returns {boolean|object}
  20299. */
  20300. get source() {
  20301. if (!this._source && this.data.source) {
  20302. this._source = this._getObjectByID(this.data.source);
  20303. this._source = this._source?._object ?? this._source;
  20304. }
  20305. return this._source;
  20306. }
  20307. /**
  20308. * Retrieves the source document
  20309. *
  20310. * @returns {Document|PlaceableObject}
  20311. */
  20312. get sourceDocument() {
  20313. return this.source?.document ?? this.source;
  20314. }
  20315. /**
  20316. * Retrieves the PIXI object for the source object
  20317. *
  20318. * @returns {*|PIXI.Sprite|TileHUD<Application.Options>}
  20319. */
  20320. get sourceMesh() {
  20321. return this.source?.mesh ?? this.source?.template;
  20322. }
  20323. /**
  20324. * The source position with the relevant offsets calculated
  20325. *
  20326. * @returns {{x: number, y: number}}
  20327. */
  20328. get sourcePosition() {
  20329. let position = this.getSourceData().position;
  20330. let offset2 = this._getOffset(this.data.source, true);
  20331. return {
  20332. x: position.x - offset2.x,
  20333. y: position.y - offset2.y
  20334. };
  20335. }
  20336. /**
  20337. * The target object (or target location) of the effect
  20338. *
  20339. * @returns {boolean|object}
  20340. */
  20341. get target() {
  20342. if (!this._target && this.data.target) {
  20343. this._target = this._getObjectByID(this.data.target);
  20344. this._target = this._target?._object ?? this._target;
  20345. }
  20346. return this._target;
  20347. }
  20348. /**
  20349. * Retrieves the document of the target
  20350. *
  20351. * @returns {Document|PlaceableObject}
  20352. */
  20353. get targetDocument() {
  20354. return this.target?.document ?? this.target;
  20355. }
  20356. /**
  20357. * Retrieves the PIXI object for the target object
  20358. *
  20359. * @returns {*|PIXI.Sprite|TileHUD<Application.Options>}
  20360. */
  20361. get targetMesh() {
  20362. return this.target?.mesh ?? this.target?.template;
  20363. }
  20364. /**
  20365. * The target position with the relevant offsets calculated
  20366. *
  20367. * @returns {{x: number, y: number}}
  20368. */
  20369. get targetPosition() {
  20370. const position = this.getTargetData().position;
  20371. const offset2 = this._getOffset(this.data.target);
  20372. return {
  20373. x: position.x - offset2.x,
  20374. y: position.y - offset2.y
  20375. };
  20376. }
  20377. /**
  20378. * Returns this effect's world position
  20379. *
  20380. * @returns {{x: number, y: number}}
  20381. */
  20382. get worldPosition() {
  20383. const t = canvas.stage.worldTransform;
  20384. return {
  20385. x: (this.sprite.worldTransform.tx - t.tx) / canvas.stage.scale.x,
  20386. y: (this.sprite.worldTransform.ty - t.ty) / canvas.stage.scale.y
  20387. };
  20388. }
  20389. /**
  20390. * Whether the current user is the owner of this effect
  20391. *
  20392. * @returns {boolean}
  20393. */
  20394. get owner() {
  20395. return this.data.creatorUserId === game.user.id;
  20396. }
  20397. /**
  20398. * Whether the current user can update this effect
  20399. *
  20400. * @returns {boolean}
  20401. */
  20402. get userCanUpdate() {
  20403. return game.user.isGM || this.owner || this.data.attachTo?.active && this.sourceDocument.canUserModify(game.user, "update");
  20404. }
  20405. /**
  20406. * Whether the current user can delete this effect
  20407. *
  20408. * @returns {boolean}
  20409. */
  20410. get userCanDelete() {
  20411. return this.userCanUpdate || user_can_do("permissions-effect-delete");
  20412. }
  20413. /**
  20414. * Whether this effect is on the current scene
  20415. *
  20416. * @returns {boolean}
  20417. */
  20418. get onCurrentScene() {
  20419. return this.data.sceneId === game.user.viewedScene;
  20420. }
  20421. /**
  20422. * Whether this effect should be shown as faded or not - effects created by users for other users should be shown
  20423. * for all
  20424. *
  20425. * @returns {boolean}
  20426. */
  20427. get shouldShowFadedVersion() {
  20428. return this.data.users && this.data.users.length && !(this.data.users.length === 1 && this.data.users.includes(this.data.creatorUserId)) && !this.data.users.includes(game.userId);
  20429. }
  20430. /**
  20431. * Getter for the current playing video of the effect
  20432. *
  20433. * @returns {null|*}
  20434. */
  20435. get video() {
  20436. return this._video;
  20437. }
  20438. /**
  20439. * Setter for the current playing video of the effect
  20440. */
  20441. set video(inVideo) {
  20442. if (!inVideo)
  20443. return;
  20444. inVideo.playbackRate = this.data.playbackRate ? this.data.playbackRate : 1;
  20445. inVideo.muted = !this.data.volume;
  20446. inVideo.volume = (this.data.volume ?? 0) * game.settings.get("core", "globalInterfaceVolume");
  20447. if (!this._video) {
  20448. this._video = inVideo;
  20449. return;
  20450. }
  20451. const isLooping = this._video?.loop;
  20452. const currentTime = this._video.currentTime;
  20453. this._video = inVideo;
  20454. this._video.currentTime = this.playNaturally ? 0 : Math.min(currentTime, this._video.duration);
  20455. this._video.loop = isLooping;
  20456. this.updateTexture();
  20457. }
  20458. async playMedia() {
  20459. if (this.animatedSprite) {
  20460. await this.sprite.play();
  20461. } else if (this.video) {
  20462. try {
  20463. await this.video.play().then(() => {
  20464. this.updateTexture();
  20465. });
  20466. } catch (err) {
  20467. }
  20468. }
  20469. this._setupTimestampHook(this.mediaCurrentTime * 1e3);
  20470. }
  20471. updateTexture() {
  20472. if (this._texture.valid) {
  20473. this._texture.update();
  20474. }
  20475. }
  20476. async pauseMedia() {
  20477. if (this.animatedSprite) {
  20478. return this.sprite.stop();
  20479. } else if (this.video) {
  20480. return this.video.pause();
  20481. }
  20482. }
  20483. get mediaLooping() {
  20484. if (this.animatedSprite) {
  20485. return this.sprite.loop;
  20486. }
  20487. return this.video?.loop ?? false;
  20488. }
  20489. set mediaLooping(looping) {
  20490. if (this.animatedSprite) {
  20491. this.sprite.loop = looping;
  20492. return;
  20493. }
  20494. if (this.video) {
  20495. this.video.loop = looping;
  20496. }
  20497. }
  20498. get mediaIsPlaying() {
  20499. if (this.animatedSprite) {
  20500. return this.sprite.playing;
  20501. }
  20502. return this.video;
  20503. }
  20504. get mediaCurrentTime() {
  20505. if (this.animatedSprite) {
  20506. return this.sprite.currentFrame / this.sprite.totalFrames * (this.sprite.totalFrames / 24);
  20507. }
  20508. return this.video?.currentTime ?? null;
  20509. }
  20510. get mediaPlaybackRate() {
  20511. if (this.animatedSprite) {
  20512. return this.sprite.animationSpeed;
  20513. } else if (this.video) {
  20514. return this.video.playbackRate;
  20515. }
  20516. }
  20517. set mediaPlaybackRate(inPlaybackRate) {
  20518. if (this.animatedSprite) {
  20519. this.sprite.animationSpeed = 0.4 * inPlaybackRate;
  20520. } else if (this.video) {
  20521. this.video.playbackRate = inPlaybackRate;
  20522. }
  20523. }
  20524. set mediaCurrentTime(newTime) {
  20525. if (this.animatedSprite) {
  20526. const newFrame = Math.floor(newTime * this.sprite.totalFrames);
  20527. const clampedFrame = Math.max(
  20528. 0,
  20529. Math.min(newFrame, this.sprite.totalFrames)
  20530. );
  20531. if (this.mediaIsPlaying) {
  20532. this.sprite.gotoAndPlay(clampedFrame);
  20533. } else {
  20534. this.sprite.gotoAndStop(clampedFrame);
  20535. }
  20536. } else if (this.video) {
  20537. this.video.currentTime = newTime;
  20538. }
  20539. }
  20540. get mediaDuration() {
  20541. if (this.animatedSprite) {
  20542. return this.sprite.totalFrames / this.sprite.animationSpeed / PIXI.Ticker.shared.FPS;
  20543. } else if (this.video) {
  20544. return this.video?.duration / this.mediaPlaybackRate;
  20545. }
  20546. return 1;
  20547. }
  20548. get hasAnimatedMedia() {
  20549. return !!(this.video || this.animatedSprite);
  20550. }
  20551. /**
  20552. * The template of the effect, determining the effect's internal grid size, and start/end padding
  20553. *
  20554. * @returns {object}
  20555. */
  20556. get template() {
  20557. return foundry.utils.mergeObject(
  20558. {
  20559. gridSize: 100,
  20560. startPoint: 0,
  20561. endPoint: 0
  20562. },
  20563. this._template ?? {}
  20564. );
  20565. }
  20566. /**
  20567. * 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.
  20568. *
  20569. * @returns {number}
  20570. */
  20571. get gridSizeDifference() {
  20572. return canvas.grid.size / this.template.gridSize;
  20573. }
  20574. /**
  20575. * Whether the effect should be flipped on any given axis
  20576. *
  20577. * @returns {number}
  20578. */
  20579. get flipX() {
  20580. return this.data.flipX ? -1 : 1;
  20581. }
  20582. get flipY() {
  20583. return this.data.flipY ? -1 : 1;
  20584. }
  20585. /**
  20586. * Whether this effect should play at all, depending on a multitude of factors
  20587. *
  20588. * @returns {boolean}
  20589. */
  20590. get shouldPlay() {
  20591. 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));
  20592. }
  20593. get shouldPlayVisible() {
  20594. let playVisible = this.shouldPlay && game.settings.get("sequencer", "effectsEnabled") && game.user.viewedScene === this.data.sceneId;
  20595. if (isNewerVersion(game.version, "10.289") && game.settings.get("core", "photosensitiveMode")) {
  20596. playVisible = false;
  20597. throttled_custom_warning(
  20598. this.data.moduleName,
  20599. "Photosensitive Mode is turned on, so Sequencer's visual effects aren't being rendered"
  20600. );
  20601. }
  20602. return playVisible;
  20603. }
  20604. /**
  20605. * Whether this effect should play naturally, or be constrained to a subsection of the video
  20606. *
  20607. * @returns {boolean}
  20608. */
  20609. get playNaturally() {
  20610. return (!this.data.time || this._startTime === 0 && this._endTime === this.mediaDuration) && this._animationTimes.loopStart === void 0 && this._animationTimes.loopEnd === void 0;
  20611. }
  20612. static make(inData) {
  20613. return !inData.persist ? new CanvasEffect(inData) : new PersistentCanvasEffect(inData);
  20614. }
  20615. static checkValid(effectData) {
  20616. if (effectData.delete) {
  20617. return false;
  20618. }
  20619. let sourceExists = true;
  20620. let targetExists = true;
  20621. if (effectData.source && is_UUID(effectData.source)) {
  20622. sourceExists = fromUuidSync(effectData.source);
  20623. }
  20624. if (effectData.target && is_UUID(effectData.target)) {
  20625. targetExists = fromUuidSync(effectData.target);
  20626. }
  20627. for (let tiedDocumentUuid of effectData?.tiedDocuments ?? []) {
  20628. if (tiedDocumentUuid && is_UUID(tiedDocumentUuid)) {
  20629. let tiedDocumentExists = fromUuidSync(tiedDocumentUuid);
  20630. if (!tiedDocumentExists)
  20631. return false;
  20632. }
  20633. }
  20634. if (effectData.source && is_UUID(effectData.source) && effectData.target && is_UUID(effectData.target)) {
  20635. const sourceScene = effectData.source.split(".")[1];
  20636. const targetScene = effectData.target.split(".")[1];
  20637. if (sourceScene !== targetScene || sourceScene !== effectData.sceneId)
  20638. return false;
  20639. }
  20640. return sourceExists && targetExists;
  20641. }
  20642. /**
  20643. * Validates that the update contains the appropriate data
  20644. *
  20645. * @param inUpdates
  20646. */
  20647. static validateUpdate(inUpdates) {
  20648. const updateKeys = Object.keys(inUpdates);
  20649. const protectedValues = updateKeys.filter(
  20650. (key) => CanvasEffect.protectedValues.includes(key)
  20651. );
  20652. if (protectedValues.length) {
  20653. throw custom_error(
  20654. "Sequencer",
  20655. `CanvasEffect | update | You cannot update the following keys of an effect's data: ${protectedValues.join(
  20656. "\n - "
  20657. )}`
  20658. );
  20659. }
  20660. if (updateKeys.includes("source")) {
  20661. if (!(is_UUID(inUpdates.source) || is_object_canvas_data(inUpdates.source))) {
  20662. throw custom_error(
  20663. "Sequencer",
  20664. `CanvasEffect | update | source must be of type document UUID or object with X and Y coordinates`
  20665. );
  20666. }
  20667. }
  20668. if (updateKeys.includes("target")) {
  20669. if (!(is_UUID(inUpdates.target) || is_object_canvas_data(inUpdates.target))) {
  20670. throw custom_error(
  20671. "Sequencer",
  20672. `CanvasEffect | update | target must be of type document UUID or object with X and Y coordinates`
  20673. );
  20674. }
  20675. }
  20676. }
  20677. getHook(type, uuid) {
  20678. if (!is_UUID(uuid))
  20679. return false;
  20680. const parts = uuid.split(".");
  20681. return type + parts[parts.length - 2];
  20682. }
  20683. /**
  20684. * Gets the source hook name
  20685. *
  20686. * @param {string} type
  20687. * @returns {string|boolean}
  20688. */
  20689. getSourceHook(type = "") {
  20690. return this.getHook(type, this.data.source);
  20691. }
  20692. /**
  20693. * The source object's current position, or its current position
  20694. *
  20695. * @returns {boolean|object}
  20696. */
  20697. getSourceData() {
  20698. if (this.data.temporary && !this.owner) {
  20699. return SequencerEffectManager.getPositionForUUID(this.data.source);
  20700. }
  20701. const position = this.source instanceof PlaceableObject && !this.isSourceTemporary ? get_object_position(this.source) : this.source?.worldPosition || this.source?.center || this.source;
  20702. const { width: width2, height } = get_object_dimensions(this.source);
  20703. if (this.isIsometricActive && this.source instanceof PlaceableObject) {
  20704. position.x += (this.sourceDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size;
  20705. position.y -= (this.sourceDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size;
  20706. if (this.data.isometric?.overlay || this.target instanceof PlaceableObject) {
  20707. position.x += (this.source?.height ?? height) / 2;
  20708. position.y -= (this.source?.height ?? height) / 2;
  20709. }
  20710. }
  20711. if (position !== void 0) {
  20712. this._cachedSourceData.position = position;
  20713. }
  20714. if (width2 !== void 0 && height !== void 0) {
  20715. this._cachedSourceData.width = width2;
  20716. this._cachedSourceData.height = height;
  20717. }
  20718. let rotation2 = 0;
  20719. if (this.source instanceof MeasuredTemplate && this.sourceDocument?.t !== "rect") {
  20720. rotation2 = Math.normalizeRadians(
  20721. Math.toRadians(this.sourceDocument?.direction)
  20722. );
  20723. } else if (!(this.source instanceof MeasuredTemplate)) {
  20724. rotation2 = this.sourceDocument?.rotation ? Math.normalizeRadians(Math.toRadians(this.sourceDocument?.rotation)) : 0;
  20725. }
  20726. if (rotation2 !== void 0) {
  20727. this._cachedSourceData.rotation = rotation2;
  20728. }
  20729. const alpha = this.sourceDocument instanceof TokenDocument || this.sourceDocument instanceof TileDocument ? this.sourceDocument?.alpha ?? 1 : 1;
  20730. if (alpha !== void 0) {
  20731. this._cachedSourceData.alpha = alpha;
  20732. }
  20733. return {
  20734. ...this._cachedSourceData
  20735. };
  20736. }
  20737. /**
  20738. * Gets the target hook name
  20739. *
  20740. * @param {string} type
  20741. * @returns {string|boolean}
  20742. */
  20743. getTargetHook(type = "") {
  20744. return this.getHook(type, this.data.target);
  20745. }
  20746. /**
  20747. * The target object's current position, or its current position
  20748. *
  20749. * @returns {boolean|object}
  20750. */
  20751. getTargetData() {
  20752. if (this.data.temporary && !this.owner) {
  20753. return SequencerEffectManager.getPositionForUUID(this.data.target) ?? this.getSourceData();
  20754. }
  20755. const position = this.target instanceof PlaceableObject && !this.isTargetTemporary ? get_object_position(this.target, { measure: true }) : this.target?.worldPosition || this.target?.center || this.target;
  20756. const { width: width2, height } = get_object_dimensions(this.target);
  20757. if (this.isIsometricActive && this.target instanceof PlaceableObject) {
  20758. const targetHeight = this.target?.height ?? height;
  20759. position.x += (this.targetDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size + targetHeight;
  20760. position.y -= (this.targetDocument.elevation ?? 0) / canvas.scene.grid.distance * canvas.grid.size + targetHeight;
  20761. }
  20762. if (width2 !== void 0 && height !== void 0) {
  20763. this._cachedTargetData.width = width2;
  20764. this._cachedTargetData.height = height;
  20765. }
  20766. if (position !== void 0) {
  20767. this._cachedTargetData.position = position;
  20768. }
  20769. let rotation2 = 0;
  20770. if (this.target instanceof MeasuredTemplate && this.targetDocument?.t !== "rect") {
  20771. rotation2 = Math.normalizeRadians(
  20772. Math.toRadians(this.targetDocument?.direction)
  20773. );
  20774. } else if (!(this.target instanceof MeasuredTemplate)) {
  20775. rotation2 = this.targetDocument?.rotation ? Math.normalizeRadians(Math.toRadians(this.targetDocument?.rotation)) : 0;
  20776. }
  20777. if (rotation2 !== void 0) {
  20778. this._cachedTargetData.rotation = rotation2;
  20779. }
  20780. const alpha = this.targetDocument instanceof TokenDocument || this.targetDocument instanceof TileDocument ? this.targetDocument?.alpha ?? 1 : 1;
  20781. if (alpha !== void 0) {
  20782. this._cachedTargetData.alpha = alpha;
  20783. }
  20784. return {
  20785. ...this._cachedTargetData
  20786. };
  20787. }
  20788. /**
  20789. * Calculates the offset for a given offset property and name mapping
  20790. *
  20791. * @param {string} offsetMapName
  20792. * @param {boolean} source
  20793. * @returns {{x: number, y: number}|*}
  20794. * @private
  20795. */
  20796. _getOffset(offsetMapName, source = false) {
  20797. const key = source ? "source" : "target";
  20798. const offset2 = {
  20799. x: 0,
  20800. y: 0
  20801. };
  20802. let twister = this._twister;
  20803. let nameOffsetMap = this._nameOffsetMap?.[this.data.name];
  20804. if (nameOffsetMap) {
  20805. twister = nameOffsetMap.twister;
  20806. }
  20807. if (this.data.missed && (!source || !this.data.target)) {
  20808. let missedOffset = this._offsetCache[key]?.missedOffset || calculate_missed_position(this.source, this.target, twister);
  20809. this._offsetCache[key].missedOffset = missedOffset;
  20810. offset2.x -= missedOffset.x;
  20811. offset2.y -= missedOffset.y;
  20812. }
  20813. const obj = source ? this.source : this.target;
  20814. const multiplier = source ? this.data.randomOffset?.source : this.data.randomOffset?.target;
  20815. if (obj && multiplier) {
  20816. let randomOffset = this._offsetCache[key]?.randomOffset || get_random_offset(obj, multiplier, twister);
  20817. this._offsetCache[key].randomOffset = randomOffset;
  20818. offset2.x -= randomOffset.x;
  20819. offset2.y -= randomOffset.y;
  20820. }
  20821. let extraOffset = this.data?.offset?.[key];
  20822. if (extraOffset) {
  20823. let newOffset = {
  20824. x: extraOffset.x,
  20825. y: extraOffset.y
  20826. };
  20827. if (extraOffset.gridUnits) {
  20828. newOffset.x *= canvas.grid.size;
  20829. newOffset.y *= canvas.grid.size;
  20830. }
  20831. if (extraOffset.local) {
  20832. if (!this._cachedSourceData?.position || !this._cachedTargetData?.position) {
  20833. this.getSourceData();
  20834. this.getTargetData();
  20835. }
  20836. const startPos = this._cachedSourceData.position;
  20837. const endPos = this._cachedTargetData.position;
  20838. const angle = this.target ? new Ray(startPos, endPos).angle : Ray.fromAngle(
  20839. startPos.x,
  20840. startPos.y,
  20841. this._cachedSourceData.rotation,
  20842. 1
  20843. ).angle;
  20844. newOffset = rotateAroundPoint(
  20845. 0,
  20846. 0,
  20847. newOffset.x,
  20848. newOffset.y,
  20849. -angle
  20850. );
  20851. }
  20852. offset2.x -= newOffset.x;
  20853. offset2.y -= newOffset.y;
  20854. }
  20855. let offsetMap = this._nameOffsetMap?.[offsetMapName];
  20856. if (!this._offsetCache[key]["nameCache"][offsetMapName]) {
  20857. this._offsetCache[key]["nameCache"][offsetMapName] = {};
  20858. }
  20859. if (offsetMap) {
  20860. if (offsetMap.missed) {
  20861. const missedOffset = this._offsetCache[key]["nameCache"][offsetMapName]?.missedOffset || calculate_missed_position(
  20862. offsetMap.sourceObj,
  20863. offsetMap.targetObj,
  20864. offsetMap.twister
  20865. );
  20866. this._offsetCache[key]["nameCache"][offsetMapName].missedOffset = missedOffset;
  20867. offset2.x -= missedOffset.x;
  20868. offset2.y -= missedOffset.y;
  20869. }
  20870. const obj2 = offsetMap.targetObj || offsetMap.sourceObj;
  20871. const multiplier2 = offsetMap.randomOffset?.source || offsetMap.randomOffset?.target;
  20872. if (obj2 && multiplier2) {
  20873. let randomOffset = this._offsetCache[key]["nameCache"][offsetMapName]?.randomOffset || get_random_offset(obj2, multiplier2, offsetMap.twister);
  20874. this._offsetCache[key]["nameCache"][offsetMapName].randomOffset = randomOffset;
  20875. offset2.x -= randomOffset.x;
  20876. offset2.y -= randomOffset.y;
  20877. }
  20878. if (offsetMap.offset) {
  20879. offset2.x += offsetMap.offset.x;
  20880. offset2.y += offsetMap.offset.y;
  20881. }
  20882. }
  20883. return offset2;
  20884. }
  20885. /**
  20886. * Initializes the name offset map by establishing targets
  20887. *
  20888. * @param inOffsetMap
  20889. * @returns {{setup}|*}
  20890. * @private
  20891. */
  20892. _setupOffsetMap(inOffsetMap) {
  20893. if (!inOffsetMap.setup) {
  20894. inOffsetMap.setup = true;
  20895. inOffsetMap.sourceObj = inOffsetMap.source ? this._validateObject(inOffsetMap.source) : false;
  20896. inOffsetMap.targetObj = inOffsetMap.target ? this._validateObject(inOffsetMap.target) : false;
  20897. const repetition = this.data.repetition % inOffsetMap.repetitions;
  20898. const seed = get_hash(`${inOffsetMap.seed}-${repetition}`);
  20899. inOffsetMap.twister = new MersenneTwister(seed);
  20900. }
  20901. return inOffsetMap;
  20902. }
  20903. /**
  20904. * Plays the effect, returning two promises; one that resolves once the duration has been established, and another
  20905. * when the effect has finished playing
  20906. *
  20907. * @returns {Object}
  20908. */
  20909. play() {
  20910. const durationPromise = new Promise((resolve, reject2) => {
  20911. this._durationResolve = resolve;
  20912. });
  20913. const finishPromise = new Promise(async (resolve, reject2) => {
  20914. this._resolve = resolve;
  20915. Hooks.callAll("createSequencerEffect", this);
  20916. debug(`Playing effect:`, this.data);
  20917. this._initialize();
  20918. });
  20919. return {
  20920. duration: durationPromise,
  20921. promise: finishPromise
  20922. };
  20923. }
  20924. /**
  20925. * Ends the effect
  20926. */
  20927. endEffect() {
  20928. if (this._ended)
  20929. return;
  20930. Hooks.callAll("endedSequencerEffect", this);
  20931. this.destroy();
  20932. }
  20933. destroy(...args) {
  20934. this._destroyDependencies();
  20935. return super.destroy(...args);
  20936. }
  20937. /**
  20938. * Updates this effect with the given parameters
  20939. * @param inUpdates
  20940. * @returns {Promise}
  20941. */
  20942. async update(inUpdates) {
  20943. if (!this.userCanUpdate)
  20944. throw custom_error(
  20945. "Sequencer",
  20946. "CanvasEffect | Update | You do not have permission to update this effect"
  20947. );
  20948. CanvasEffect.validateUpdate(inUpdates);
  20949. const newData = foundry.utils.deepClone(this.data);
  20950. const updateKeys = Object.keys(inUpdates);
  20951. updateKeys.forEach((key) => {
  20952. setProperty(newData, key, inUpdates[key]);
  20953. });
  20954. if (Object.keys(foundry.utils.diffObject(newData, this.data)).length === 0) {
  20955. debug(
  20956. `Skipped updating effect with ID ${this.id} - no changes needed`
  20957. );
  20958. return;
  20959. }
  20960. if (this.data.persist) {
  20961. const originalSourceUUID = is_UUID(this.data.source) && this.data.attachTo ? this.data.source : "Scene." + this.data.sceneId;
  20962. const newSourceUUID = is_UUID(newData.source) && newData.attachTo ? newData.source : "Scene." + newData.sceneId;
  20963. if (originalSourceUUID !== newSourceUUID) {
  20964. flagManager.removeFlags(originalSourceUUID, newData);
  20965. }
  20966. flagManager.addFlags(newSourceUUID, newData);
  20967. }
  20968. debug(`Updated effect with ID ${this.id}`);
  20969. return sequencerSocket.executeForEveryone(
  20970. SOCKET_HANDLERS.UPDATE_EFFECT,
  20971. this.id,
  20972. newData
  20973. );
  20974. }
  20975. async addAnimatedProperties({ animations = [], loopingAnimation = [] } = {}) {
  20976. const animationsToAdd = [];
  20977. if (!Array.isArray(animations)) {
  20978. throw custom_error(
  20979. this.data.moduleName,
  20980. `animations must be an array of arrays`
  20981. );
  20982. }
  20983. for (const animationData of animations) {
  20984. if (!Array.isArray(animationData)) {
  20985. throw custom_error(
  20986. this.data.moduleName,
  20987. `each entry in animations must be an array, each with target, property name, and animation options`
  20988. );
  20989. }
  20990. const result = validateAnimation(...animationData);
  20991. if (typeof result === "string") {
  20992. throw custom_error(this.data.moduleName, result);
  20993. }
  20994. result.creationTimestamp = +new Date();
  20995. animationsToAdd.push(result);
  20996. }
  20997. if (!Array.isArray(loopingAnimation)) {
  20998. throw custom_error(
  20999. this.data.moduleName,
  21000. `loopingAnimation must be an array of arrays`
  21001. );
  21002. }
  21003. for (const animationData of loopingAnimation) {
  21004. if (!Array.isArray(animationData)) {
  21005. throw custom_error(
  21006. this.data.moduleName,
  21007. `each entry in loopingAnimation must be an array, each with target, property name, and animation options`
  21008. );
  21009. }
  21010. const result = validateLoopingAnimation(...animationData);
  21011. if (typeof result === "string") {
  21012. throw custom_error(this.data.moduleName, result);
  21013. }
  21014. result.creationTimestamp = +new Date();
  21015. animationsToAdd.push(result);
  21016. }
  21017. if (this.data.persist) {
  21018. const originalSourceUUID = is_UUID(this.data.source) && this.data.attachTo ? this.data.source : "Scene." + this.data.sceneId;
  21019. const newData = foundry.utils.deepClone(this.data);
  21020. newData.animations = (newData.animations ?? []).concat(
  21021. foundry.utils.deepClone(animationsToAdd)
  21022. );
  21023. flagManager.addFlags(originalSourceUUID, newData);
  21024. }
  21025. return sequencerSocket.executeForEveryone(
  21026. SOCKET_HANDLERS.ADD_EFFECT_ANIMATIONS,
  21027. this.id,
  21028. animationsToAdd
  21029. );
  21030. }
  21031. async _addAnimations(inAnimations) {
  21032. this._playAnimations(inAnimations);
  21033. this.data.animations = (this.data.animations ?? []).concat(inAnimations);
  21034. }
  21035. /**
  21036. * Updates the effect
  21037. *
  21038. * @param inUpdates
  21039. * @returns {Promise}
  21040. * @private
  21041. */
  21042. _update(inUpdates) {
  21043. this.data = inUpdates;
  21044. Hooks.callAll("updateSequencerEffect", this);
  21045. this._destroyDependencies();
  21046. return this._reinitialize();
  21047. }
  21048. /**
  21049. * Determines whether a position is within the bounds of this effect
  21050. *
  21051. * @param inPosition
  21052. * @returns {boolean}
  21053. */
  21054. isPositionWithinBounds(inPosition) {
  21055. if (!this.spriteContainer)
  21056. return false;
  21057. return is_position_within_bounds(
  21058. inPosition,
  21059. this.spriteContainer,
  21060. this.parent
  21061. );
  21062. }
  21063. /**
  21064. * Initializes the effect and places it on the canvas
  21065. *
  21066. * @param {boolean} play
  21067. * @returns {Promise}
  21068. * @private
  21069. */
  21070. async _initialize(play = true) {
  21071. this.ready = false;
  21072. this._initializeVariables();
  21073. await this._contextLostCallback();
  21074. await this._loadTexture();
  21075. this._addToContainer();
  21076. this._createSprite();
  21077. this._calculateDuration();
  21078. this._createShapes();
  21079. await this._setupMasks();
  21080. await this._transformSprite();
  21081. this._playCustomAnimations();
  21082. this._playPresetAnimations();
  21083. this._setEndTimeout();
  21084. this._timeoutVisibility();
  21085. if (play)
  21086. await this.playMedia();
  21087. this.ready = true;
  21088. }
  21089. /**
  21090. * Reinitializes the effect after it has been updated
  21091. *
  21092. * @param play
  21093. * @returns {Promise}
  21094. * @private
  21095. */
  21096. async _reinitialize(play = true) {
  21097. this.renderable = false;
  21098. if (!this.shouldPlay) {
  21099. return Sequencer.EffectManager._removeEffect(this);
  21100. }
  21101. this.actualCreationTime = +new Date();
  21102. return this._initialize(play);
  21103. }
  21104. /**
  21105. * Initializes variables core to the function of the effect
  21106. * This is run as a part of the construction of the effect
  21107. *
  21108. * @private
  21109. */
  21110. _initializeVariables() {
  21111. this.rotationContainer = this.addChild(new PIXI.Container());
  21112. this.rotationContainer.id = this.id + "-rotationContainer";
  21113. this.isometricContainer = this.rotationContainer.addChild(
  21114. new PIXI.Container()
  21115. );
  21116. this.isometricContainer.id = this.id + "-isometricContainer";
  21117. this.spriteContainer = this.isometricContainer.addChild(
  21118. new PIXI.Container()
  21119. );
  21120. this.spriteContainer.id = this.id + "-spriteContainer";
  21121. this._template = this.data.template;
  21122. this._ended = null;
  21123. this._maskContainer = null;
  21124. this._maskSprite = null;
  21125. this._file = null;
  21126. this._loopOffset = 0;
  21127. this.effectFilters = {};
  21128. this._animationDuration = 0;
  21129. this._animationTimes = {};
  21130. this._twister = new MersenneTwister(this.data.creationTimestamp);
  21131. this._video = null;
  21132. this._distanceCache = null;
  21133. this._isRangeFind = false;
  21134. this._customAngle = 0;
  21135. this._currentFilePath = this.data.file;
  21136. this._relatedSprites = {};
  21137. this._hooks = [];
  21138. if (this._resetTimeout) {
  21139. clearTimeout(this._resetTimeout);
  21140. }
  21141. this._resetTimeout = null;
  21142. this._source = false;
  21143. this._target = false;
  21144. this._offsetCache = {
  21145. source: { nameCache: {} },
  21146. target: { nameCache: {} }
  21147. };
  21148. this._nameOffsetMap = Object.fromEntries(
  21149. Object.entries(
  21150. foundry.utils.deepClone(this.data.nameOffsetMap ?? {})
  21151. ).map((entry) => {
  21152. return [entry[0], this._setupOffsetMap(entry[1])];
  21153. })
  21154. );
  21155. this.uuid = !is_UUID(this.context.uuid) ? this.id : this.context.uuid + ".data.flags.sequencer.effects." + this.id;
  21156. const maxPerformance = game.settings.get("core", "performanceMode") === 3;
  21157. const maxFPS = game.settings.get("core", "maxFPS");
  21158. this._ticker = new PIXI.Ticker();
  21159. this._ticker.maxFPS = maxPerformance && maxFPS === 60 ? 60 : maxFPS;
  21160. this._ticker.start();
  21161. }
  21162. /**
  21163. * Destroys all dependencies to this element, such as tickers, animations, textures, and child elements
  21164. *
  21165. * @private
  21166. */
  21167. _destroyDependencies() {
  21168. if (this._ended)
  21169. return;
  21170. this._ended = true;
  21171. this.mask = null;
  21172. hooksManager.removeHooks(this.uuid);
  21173. try {
  21174. this._ticker.stop();
  21175. this._ticker.destroy();
  21176. } catch (err) {
  21177. }
  21178. this._ticker = null;
  21179. Object.values(this._relatedSprites).forEach(
  21180. (sprite) => sprite.destroy({ children: true })
  21181. );
  21182. SequencerAnimationEngine.endAnimations(this.id);
  21183. if (this._maskContainer)
  21184. this._maskContainer.destroy({ children: true });
  21185. if (this._maskSprite) {
  21186. try {
  21187. this._maskSprite.texture.destroy(true);
  21188. this._maskSprite.destroy();
  21189. } catch (err) {
  21190. }
  21191. }
  21192. if (this._file instanceof SequencerFileBase) {
  21193. this._file.destroy();
  21194. }
  21195. if (this.video) {
  21196. try {
  21197. this.video.removeAttribute("src");
  21198. this.video.pause();
  21199. this.video.load();
  21200. } catch (err) {
  21201. }
  21202. }
  21203. try {
  21204. if (this.data.screenSpace) {
  21205. SequencerAboveUILayer.removeContainerByEffect(this);
  21206. }
  21207. } catch (err) {
  21208. }
  21209. this.removeChildren().forEach((child) => child.destroy({ children: true }));
  21210. }
  21211. /**
  21212. * Plays preset animations
  21213. *
  21214. * @private
  21215. */
  21216. _playPresetAnimations() {
  21217. this._moveTowards();
  21218. this._fadeIn();
  21219. this._fadeInAudio();
  21220. this._scaleIn();
  21221. this._rotateIn();
  21222. this._fadeOut();
  21223. this._fadeOutAudio();
  21224. this._scaleOut();
  21225. this._rotateOut();
  21226. }
  21227. /**
  21228. * Gets an object based on an identifier, checking if it exists within the named offset map, whether it's a
  21229. * coordinate object, or if it's an UUID that needs to be fetched from the scene
  21230. *
  21231. * @param inIdentifier
  21232. * @returns {*}
  21233. * @private
  21234. */
  21235. _getObjectByID(inIdentifier) {
  21236. let source = inIdentifier;
  21237. let offsetMap = this._nameOffsetMap?.[inIdentifier];
  21238. if (offsetMap) {
  21239. source = offsetMap?.targetObj || offsetMap?.sourceObj || source;
  21240. } else {
  21241. source = this._validateObject(source);
  21242. }
  21243. return source;
  21244. }
  21245. /**
  21246. * Validates the given parameter, whether it's a UUID or a coordinate object, and returns the proper one
  21247. *
  21248. * @param inObject
  21249. * @returns {*}
  21250. * @private
  21251. */
  21252. _validateObject(inObject) {
  21253. if (is_UUID(inObject) || !is_object_canvas_data(inObject)) {
  21254. inObject = get_object_from_scene(inObject, this.data.sceneId);
  21255. inObject = inObject?._object ?? inObject;
  21256. }
  21257. return inObject;
  21258. }
  21259. /**
  21260. * Adds this effect to the appropriate container on the right layer
  21261. *
  21262. * @private
  21263. */
  21264. _addToContainer() {
  21265. let layer2;
  21266. if (this.data.screenSpaceAboveUI) {
  21267. layer2 = SequencerAboveUILayer;
  21268. } else if (this.data.screenSpace) {
  21269. layer2 = canvas.sequencerEffectsUILayer;
  21270. } else if (this.data.aboveInterface) {
  21271. layer2 = canvas.controls;
  21272. } else if (this.data.aboveLighting) {
  21273. layer2 = canvas.interface;
  21274. } else {
  21275. layer2 = canvas.primary;
  21276. }
  21277. layer2.addChild(this);
  21278. layer2.sortChildren();
  21279. }
  21280. /**
  21281. * Loads the texture for this effect, handling cases where it's a simple path or a database path
  21282. *
  21283. * @private
  21284. */
  21285. async _loadTexture() {
  21286. if (this.data.file === "") {
  21287. return;
  21288. }
  21289. if (this.data.customRange) {
  21290. this._file = SequencerFileBase.make(
  21291. this.data.file,
  21292. Object.values(this.template),
  21293. "temporary.range.file"
  21294. );
  21295. } else {
  21296. if (!Sequencer.Database.entryExists(this.data.file)) {
  21297. let texture = await SequencerFileCache.loadFile(this.data.file);
  21298. this.video = this.data.file.toLowerCase().endsWith(".webm") ? texture?.baseTexture?.resource?.source ?? false : false;
  21299. this._texture = texture;
  21300. this._file = texture;
  21301. this._currentFilePath = this.data.file;
  21302. return;
  21303. }
  21304. this._file = Sequencer.Database.getEntry(this.data.file).clone();
  21305. }
  21306. this._file.fileIndex = this.data.forcedIndex;
  21307. this._file.twister = this._twister;
  21308. this._isRangeFind = this._file?.rangeFind;
  21309. this.spriteSheet = false;
  21310. if (this.data.stretchTo) {
  21311. let ray = new Ray(this.sourcePosition, this.targetPosition);
  21312. this._rotateTowards(ray);
  21313. ray = new Ray(this.sourcePosition, this.targetPosition);
  21314. let { filePath, texture, sheet } = await this._getTextureForDistance(
  21315. ray.distance
  21316. );
  21317. this._currentFilePath = filePath;
  21318. this._texture = texture;
  21319. this.spriteSheet = sheet;
  21320. } else if (!this._isRangeFind || this._isRangeFind && !this.data.stretchTo) {
  21321. const { filePath, texture, sheet } = await this._file.getTexture();
  21322. this._currentFilePath = filePath;
  21323. this._texture = texture;
  21324. this.spriteSheet = sheet;
  21325. }
  21326. if (this._isRangeFind && (this.data.stretchTo?.attachTo?.active || this.data.attachTo?.active)) {
  21327. let spriteType = this.data.tilingTexture ? PIXI.TilingSprite : SpriteMesh;
  21328. this._relatedSprites[this._currentFilePath] = new spriteType(
  21329. this._texture,
  21330. this.data.xray ? null : VisionSamplerShader
  21331. );
  21332. new Promise(async (resolve) => {
  21333. for (let filePath of this._file.getAllFiles()) {
  21334. if (filePath === this._currentFilePath)
  21335. continue;
  21336. let texture = await this._file._getTexture(filePath);
  21337. let spriteType2 = this.data.tilingTexture ? PIXI.TilingSprite : SpriteMesh;
  21338. let sprite = new spriteType2(
  21339. texture,
  21340. this.data.xray ? null : VisionSamplerShader
  21341. );
  21342. sprite.renderable = false;
  21343. this._relatedSprites[filePath] = sprite;
  21344. }
  21345. resolve();
  21346. });
  21347. }
  21348. this._template = this._file.template ?? this._template;
  21349. this.video = this._currentFilePath.toLowerCase().endsWith(".webm") ? this._texture?.baseTexture?.resource?.source : false;
  21350. }
  21351. /**
  21352. * Calculates the duration of this effect, based on animation durations, the video source duration, end/start times, etc
  21353. *
  21354. * @private
  21355. */
  21356. _calculateDuration() {
  21357. let playbackRate = this.data.playbackRate ? this.data.playbackRate : 1;
  21358. this.mediaPlaybackRate = playbackRate;
  21359. this._animationDuration = this.data.duration || this.mediaDuration * 1e3;
  21360. if (this.data.moveSpeed && this.data.moves) {
  21361. let distance = distance_between(
  21362. this.sourcePosition,
  21363. this.targetPosition
  21364. );
  21365. let durationFromSpeed = distance / this.data.moveSpeed * 1e3;
  21366. this._animationDuration = Math.max(durationFromSpeed, this.data.duration);
  21367. } else if (!this.data.duration && !this.hasAnimatedMedia) {
  21368. let fadeDuration = (this.data.fadeIn?.duration ?? 0) + (this.data.fadeOut?.duration ?? 0);
  21369. let scaleDuration = (this.data.scaleIn?.duration ?? 0) + (this.data.scaleOut?.duration ?? 0);
  21370. let rotateDuration = (this.data.rotateIn?.duration ?? 0) + (this.data.rotateOut?.duration ?? 0);
  21371. let moveDuration = 0;
  21372. if (this.data.moves) {
  21373. let distance = distance_between(
  21374. this.sourcePosition,
  21375. this.targetPosition
  21376. );
  21377. moveDuration = (this.data.moveSpeed ? distance / this.data.moveSpeed * 1e3 : 1e3) + this.data.moves.delay;
  21378. }
  21379. let animationDurations = this.data.animations ? Math.max(
  21380. ...this.data.animations.map((animation2) => {
  21381. if (animation2.looping) {
  21382. if (animation2.loops === 0)
  21383. return 0;
  21384. return (animation2?.duration ?? 0) * (animation2?.loops ?? 0) + (animation2?.delay ?? 0);
  21385. } else {
  21386. return (animation2?.duration ?? 0) + (animation2?.delay ?? 0);
  21387. }
  21388. })
  21389. ) : 0;
  21390. this._animationDuration = Math.max(
  21391. fadeDuration,
  21392. scaleDuration,
  21393. rotateDuration,
  21394. moveDuration,
  21395. animationDurations
  21396. );
  21397. this._animationDuration = this._animationDuration || 1e3;
  21398. }
  21399. this._startTime = 0;
  21400. if (this.data.time?.start && this.mediaCurrentTime !== null) {
  21401. let currentTime = !this.data.time.start.isPerc ? this.data.time.start.value ?? 0 : this._animationDuration * this.data.time.start.value;
  21402. this.mediaCurrentTime = currentTime / 1e3;
  21403. this._startTime = this.mediaCurrentTime;
  21404. }
  21405. if (this.data.time?.end) {
  21406. 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;
  21407. }
  21408. this._endTime = this._animationDuration / 1e3;
  21409. if (this._file?.markers && this._startTime === 0 && this._endTime === this.mediaDuration) {
  21410. this._animationTimes.loopStart = this._file.markers.loop.start / playbackRate / 1e3;
  21411. this._animationTimes.loopEnd = this._file.markers.loop.end / playbackRate / 1e3;
  21412. this._animationTimes.forcedEnd = this._file.markers.forcedEnd / playbackRate / 1e3;
  21413. }
  21414. this._durationResolve(this._animationDuration);
  21415. this.mediaLooping = this._animationDuration / 1e3 > this.mediaDuration && !this.data.noLoop;
  21416. }
  21417. /**
  21418. * If this effect is animatable, hold off on rendering it for a bit so that the animations have time to initialize to
  21419. * prevent it from spawning and then jumping to the right place
  21420. *
  21421. * @private
  21422. */
  21423. _timeoutVisibility() {
  21424. setTimeout(
  21425. () => {
  21426. this._setupHooks();
  21427. },
  21428. this.data.animations ? 50 : 0
  21429. );
  21430. }
  21431. /**
  21432. * If this effect is attached to an object, check whether the object has been destroyed, if so, end the effect
  21433. *
  21434. * @private
  21435. */
  21436. _contextLostCallback() {
  21437. if (this.isSourceTemporary) {
  21438. this._ticker.add(() => {
  21439. if (this.isSourceDestroyed) {
  21440. this._ticker.stop();
  21441. this._source = this.sourcePosition;
  21442. SequencerEffectManager.endEffects({ effects: this });
  21443. }
  21444. });
  21445. }
  21446. if (this.isTargetTemporary) {
  21447. this._ticker.add(() => {
  21448. if (this.isTargetDestroyed) {
  21449. this._ticker.stop();
  21450. this._target = this.targetPosition;
  21451. SequencerEffectManager.endEffects({ effects: this });
  21452. }
  21453. });
  21454. }
  21455. }
  21456. /**
  21457. * Creates the sprite, and the relevant containers that manage the position and offsets of the overall visual look of the sprite
  21458. *
  21459. * @private
  21460. */
  21461. _createSprite() {
  21462. this.renderable = false;
  21463. const args = [this.spriteSheet ? this.spriteSheet : null];
  21464. if (!this.data.xray && !this.spriteSheet && !this.data.screenSpace && !this.data.screenSpaceAboveUI) {
  21465. args.push(VisionSamplerShader);
  21466. }
  21467. const spriteType = this.spriteSheet ? PIXI.AnimatedSprite : SpriteMesh;
  21468. const sprite = new spriteType(...args);
  21469. this.sprite = this.spriteContainer.addChild(sprite);
  21470. this.sprite.id = this.id + "-sprite";
  21471. Object.values(this._relatedSprites).forEach((sprite2) => {
  21472. this.sprite.addChild(sprite2);
  21473. });
  21474. this.animatedSprite = false;
  21475. if (this.spriteSheet) {
  21476. this.animatedSprite = true;
  21477. this.sprite.animationSpeed = 0.4;
  21478. this.sprite.loop = false;
  21479. }
  21480. let textSprite;
  21481. if (this.data.text) {
  21482. const text2 = this.data.text.text;
  21483. const fontSettings = foundry.utils.deepClone(this.data.text);
  21484. fontSettings.fontSize = (fontSettings?.fontSize ?? 26) * (150 / canvas.grid.size);
  21485. textSprite = new PIXI.Text(text2, fontSettings);
  21486. textSprite.resolution = 5;
  21487. textSprite.zIndex = 1;
  21488. textSprite.anchor.set(
  21489. this.data.text?.anchor?.x ?? 0.5,
  21490. this.data.text?.anchor?.y ?? 0.5
  21491. );
  21492. }
  21493. this.sprite.filters = [];
  21494. if (this.data.filters) {
  21495. for (let index = 0; index < this.data.filters.length; index++) {
  21496. const filterData = this.data.filters[index];
  21497. const filter2 = new filters[filterData.className](filterData.data);
  21498. filter2.id = this.id + "-" + filterData.className + "-" + index.toString();
  21499. this.sprite.filters.push(filter2);
  21500. const filterKeyName = filterData.name || filterData.className;
  21501. this.effectFilters[filterKeyName] = filter2;
  21502. }
  21503. }
  21504. this.alphaFilter = new PIXI.filters.AlphaFilter(this.data.opacity);
  21505. this.alphaFilter.id = this.id + "-alphaFilter";
  21506. this.sprite.filters.push(this.alphaFilter);
  21507. let spriteOffsetX = this.data.spriteOffset?.x ?? 0;
  21508. let spriteOffsetY = this.data.spriteOffset?.y ?? 0;
  21509. if (this.data.spriteOffset?.gridUnits) {
  21510. spriteOffsetX *= canvas.grid.size;
  21511. spriteOffsetY *= canvas.grid.size;
  21512. }
  21513. this.sprite.position.set(spriteOffsetX, spriteOffsetY);
  21514. this.sprite.anchor.set(
  21515. this.data.spriteAnchor?.x ?? 0.5,
  21516. this.data.spriteAnchor?.y ?? 0.5
  21517. );
  21518. let spriteRotation = this.data.spriteRotation ?? 0;
  21519. if (this.data.randomSpriteRotation) {
  21520. spriteRotation += random_float_between(-360, 360, this._twister);
  21521. }
  21522. this.sprite.rotation = Math.normalizeRadians(
  21523. Math.toRadians(spriteRotation)
  21524. );
  21525. this._customAngle = this.data.angle ?? 0;
  21526. if (this.data.randomRotation) {
  21527. this._customAngle += random_float_between(-360, 360, this._twister);
  21528. }
  21529. this.spriteContainer.rotation = -Math.normalizeRadians(
  21530. Math.toRadians(this._customAngle)
  21531. );
  21532. if (CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE) {
  21533. this.isometricContainer.rotation = Math.PI / 4;
  21534. }
  21535. if (this.data.tint) {
  21536. this.sprite.tint = this.data.tint;
  21537. }
  21538. if (textSprite) {
  21539. if (this.data.tint) {
  21540. textSprite.tint = this.data.tint;
  21541. }
  21542. this.sprite.addChild(textSprite);
  21543. }
  21544. this.filters = [
  21545. new PIXI.filters.ColorMatrixFilter({
  21546. saturation: this.shouldShowFadedVersion ? -1 : 1
  21547. }),
  21548. new PIXI.filters.AlphaFilter(
  21549. this.shouldShowFadedVersion ? game.settings.get(CONSTANTS.MODULE_NAME, "user-effect-opacity") / 100 : 1
  21550. )
  21551. ];
  21552. this.updateElevation();
  21553. }
  21554. _createShapes() {
  21555. const nonMaskShapes = (this.data?.shapes ?? []).filter(
  21556. (shape) => !shape.isMask
  21557. );
  21558. this.shapes = {};
  21559. for (const shape of nonMaskShapes) {
  21560. const graphic = createShape(shape);
  21561. graphic.filters = this.sprite.filters;
  21562. this.spriteContainer.addChild(graphic);
  21563. this.shapes[shape?.name ?? "shape-" + randomID()] = graphic;
  21564. }
  21565. }
  21566. updateElevation() {
  21567. const targetElevation = Math.max(
  21568. get_object_elevation(this.source ?? {}),
  21569. get_object_elevation(this.target ?? {})
  21570. ) + 1;
  21571. let effectElevation = this.data.elevation?.elevation ?? 0;
  21572. if (!this.data.elevation?.absolute) {
  21573. effectElevation += targetElevation;
  21574. }
  21575. const isIsometric = getProperty(
  21576. game.scenes.get(this.data.sceneId),
  21577. CONSTANTS.INTEGRATIONS.ISOMETRIC.SCENE_ENABLED
  21578. );
  21579. if (CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE && isIsometric) {
  21580. const sourceSort = this.source ? this.sourceMesh.sort + (this.data.isometric?.overlay ? 1 : -1) : 0;
  21581. const targetSort = this.target ? this.targetMesh.sort + (this.data.isometric?.overlay ? 1 : -1) : 0;
  21582. this.sort = Math.max(sourceSort, targetSort);
  21583. } else {
  21584. this.sort = !is_real_number(this.data.zIndex) ? this.data.index + (this?.parent?.children?.length ?? 0) : 1e5 + this.data.zIndex;
  21585. }
  21586. this.elevation = effectElevation;
  21587. this.zIndex = this.sort;
  21588. this.sort += 100;
  21589. if (this.parent) {
  21590. this.parent.sortChildren();
  21591. }
  21592. }
  21593. updateTransform() {
  21594. super.updateTransform();
  21595. if (this.data.screenSpace || this.data.screenSpaceAboveUI) {
  21596. const [screenWidth, screenHeight] = canvas.screenDimensions;
  21597. this.position.set(
  21598. (this.data.screenSpacePosition?.x ?? 0) + screenWidth * (this.data.screenSpaceAnchor?.x ?? this.data.anchor?.x ?? 0.5),
  21599. (this.data.screenSpacePosition?.y ?? 0) + screenHeight * (this.data.screenSpaceAnchor?.y ?? this.data.anchor?.y ?? 0.5)
  21600. );
  21601. if (this.data.screenSpaceScale) {
  21602. const scaleData = this.data.screenSpaceScale ?? { x: 1, y: 1 };
  21603. let scaleX = scaleData.x;
  21604. let scaleY = scaleData.y;
  21605. if (scaleData.fitX) {
  21606. scaleX = scaleX * (screenWidth / this.sprite.width);
  21607. }
  21608. if (scaleData.fitY) {
  21609. scaleY = scaleY * (screenHeight / this.sprite.height);
  21610. }
  21611. scaleX = scaleData.ratioX ? scaleY : scaleX;
  21612. scaleY = scaleData.ratioY ? scaleX : scaleY;
  21613. this.scale.set(scaleX, scaleY);
  21614. }
  21615. }
  21616. }
  21617. async _setupMasks() {
  21618. const maskShapes = this.data.shapes.filter((shape) => shape.isMask);
  21619. if (!this.data?.masks?.length && !maskShapes.length)
  21620. return;
  21621. const maskFilter = MaskFilter.create();
  21622. for (const uuid of this.data.masks) {
  21623. const documentObj = fromUuidSync(uuid);
  21624. if (!documentObj || documentObj.parent.id !== this.data.sceneId)
  21625. continue;
  21626. const obj = documentObj.object;
  21627. let shape = obj?.mesh;
  21628. let shapeToAdd = shape;
  21629. if (obj instanceof MeasuredTemplate || obj instanceof Drawing) {
  21630. shape = obj?.shape?.geometry?.graphicsData?.[0]?.shape ?? obj?.shape;
  21631. if (game.modules.get("walledtemplates")?.active && obj.walledtemplates?.walledTemplate) {
  21632. let wt = obj.walledtemplates.walledTemplate;
  21633. wt.options.padding = 3 * canvas.dimensions.distancePixels;
  21634. shape = wt.computeShape();
  21635. wt.options.padding = 0;
  21636. }
  21637. shapeToAdd = new PIXI.LegacyGraphics().beginFill().drawShape(shape).endFill();
  21638. if (obj instanceof MeasuredTemplate) {
  21639. shapeToAdd.position.set(documentObj.x, documentObj.y);
  21640. } else {
  21641. const {
  21642. x,
  21643. y,
  21644. shape: { width: width2, height },
  21645. rotation: rotation2
  21646. } = documentObj;
  21647. shapeToAdd.pivot.set(width2 / 2, height / 2);
  21648. shapeToAdd.position.set(x + width2 / 2, y + height / 2);
  21649. shapeToAdd.angle = rotation2;
  21650. }
  21651. shapeToAdd.cullable = true;
  21652. shapeToAdd.custom = true;
  21653. shapeToAdd.renderable = false;
  21654. shapeToAdd.uuid = uuid;
  21655. canvas.stage.addChild(shapeToAdd);
  21656. }
  21657. shapeToAdd.obj = obj;
  21658. const updateMethod = (doc) => {
  21659. if (doc !== documentObj)
  21660. return;
  21661. const mask = maskFilter.masks.find((shape2) => shape2.uuid === uuid);
  21662. if (!mask)
  21663. return;
  21664. if (!mask.custom)
  21665. return;
  21666. mask.clear();
  21667. if (obj instanceof MeasuredTemplate) {
  21668. mask.position.set(documentObj.x, documentObj.y);
  21669. let maskObj = documentObj.object;
  21670. if (game.modules.get("walledtemplates")?.active && maskObj.walledtemplates?.walledTemplate) {
  21671. let wt = maskObj.walledtemplates.walledTemplate;
  21672. wt.options.padding = 3 * canvas.dimensions.distancePixels;
  21673. shape = wt.computeShape();
  21674. wt.options.padding = 0;
  21675. }
  21676. } else {
  21677. const {
  21678. x,
  21679. y,
  21680. shape: { width: width2, height },
  21681. rotation: rotation2
  21682. } = documentObj;
  21683. mask.pivot.set(width2 / 2, height / 2);
  21684. mask.position.set(x + width2 / 2, y + height / 2);
  21685. mask.angle = rotation2;
  21686. }
  21687. mask.beginFill().drawShape(shape).endFill();
  21688. };
  21689. if (game.modules.get("walledtemplates")?.active) {
  21690. hooksManager.addHook(this.uuid, "createWall", () => {
  21691. setTimeout(() => {
  21692. updateMethod(documentObj);
  21693. }, 100);
  21694. });
  21695. hooksManager.addHook(this.uuid, "updateWall", () => {
  21696. setTimeout(() => {
  21697. updateMethod(documentObj);
  21698. }, 100);
  21699. });
  21700. hooksManager.addHook(this.uuid, "deleteWall", () => {
  21701. setTimeout(() => {
  21702. updateMethod(documentObj);
  21703. }, 100);
  21704. });
  21705. }
  21706. hooksManager.addHook(this.uuid, this.getHook("update", uuid), (doc) => {
  21707. setTimeout(() => {
  21708. updateMethod(doc);
  21709. }, 100);
  21710. });
  21711. maskFilter.masks.push(shapeToAdd);
  21712. }
  21713. for (const shapeData of maskShapes) {
  21714. const shape = createShape(shapeData);
  21715. shape.cullable = true;
  21716. shape.custom = true;
  21717. shape.renderable = false;
  21718. this.spriteContainer.addChild(shape);
  21719. this.shapes[shapeData?.name ?? "shape-" + randomID()] = shape;
  21720. maskFilter.masks.push(shape);
  21721. }
  21722. this.sprite.filters.push(maskFilter);
  21723. }
  21724. /**
  21725. * Sets up the hooks relating to this effect's source and target
  21726. *
  21727. * @private
  21728. */
  21729. _setupHooks() {
  21730. const attachedToSource = this.data.attachTo?.active && is_UUID(this.data.source);
  21731. const attachedToTarget = (this.data.stretchTo?.attachTo || this.data.rotateTowards?.attachTo) && is_UUID(this.data.target);
  21732. const baseRenderable = this.shouldPlayVisible;
  21733. let renderable = baseRenderable;
  21734. let alpha = null;
  21735. if (attachedToSource) {
  21736. hooksManager.addHook(this.uuid, this.getSourceHook("delete"), (doc) => {
  21737. const uuid = doc.uuid;
  21738. if (doc !== this.sourceDocument)
  21739. return;
  21740. this._source = this._cachedSourceData.position;
  21741. SequencerEffectManager.objectDeleted(uuid);
  21742. });
  21743. if (this.data.attachTo?.bindVisibility) {
  21744. hooksManager.addHook(
  21745. this.uuid,
  21746. "sightRefresh",
  21747. () => {
  21748. const sourceVisible = this.source && (this.sourceMesh?.visible ?? true);
  21749. const sourceHidden = this.sourceDocument && (this.sourceDocument?.hidden ?? false);
  21750. const targetVisible = this.target && (!attachedToTarget || (this.targetMesh?.visible ?? true));
  21751. this.renderable = baseRenderable && (sourceVisible || targetVisible) && this._checkWallCollisions();
  21752. this.alpha = sourceVisible && sourceHidden ? 0.5 : 1;
  21753. renderable = baseRenderable && this.renderable;
  21754. },
  21755. true
  21756. );
  21757. }
  21758. if (this.data.attachTo?.bindAlpha || this.data.attachTo?.bindElevation) {
  21759. hooksManager.addHook(this.uuid, this.getSourceHook("update"), (doc) => {
  21760. if (doc !== this.sourceDocument)
  21761. return;
  21762. if (this.data.attachTo?.bindAlpha) {
  21763. this.spriteContainer.alpha = this.getSourceData().alpha;
  21764. }
  21765. if (this.data.attachTo?.bindElevation) {
  21766. this.updateElevation();
  21767. }
  21768. });
  21769. }
  21770. if (this.data.attachTo?.bindAlpha) {
  21771. alpha = this.getSourceData().alpha;
  21772. }
  21773. }
  21774. if (attachedToTarget) {
  21775. hooksManager.addHook(this.uuid, this.getTargetHook("delete"), (doc) => {
  21776. if (doc !== this.target)
  21777. return;
  21778. this._target = this._cachedTargetData.position;
  21779. const uuid = doc.uuid;
  21780. SequencerEffectManager.objectDeleted(uuid);
  21781. });
  21782. hooksManager.addHook(this.uuid, this.getTargetHook("update"), (doc) => {
  21783. if (doc !== this.target)
  21784. return;
  21785. this.updateElevation();
  21786. });
  21787. }
  21788. for (let uuid of this.data?.tiedDocuments ?? []) {
  21789. const tiedDocument = fromUuidSync(uuid);
  21790. if (tiedDocument) {
  21791. hooksManager.addHook(
  21792. this.uuid,
  21793. this.getHook("delete", tiedDocument.uuid),
  21794. (doc) => {
  21795. if (tiedDocument !== doc)
  21796. return;
  21797. SequencerEffectManager.objectDeleted(doc.uuid);
  21798. }
  21799. );
  21800. }
  21801. }
  21802. setTimeout(() => {
  21803. this.renderable = renderable;
  21804. this.spriteContainer.alpha = alpha ?? 1;
  21805. }, 25);
  21806. }
  21807. /**
  21808. * Calculates the padding and scale to stretch an effect across the given distance
  21809. *
  21810. * If the file is a SequencerFileBase instance, it will also pick the appropriate file for the right distance
  21811. *
  21812. * @param distance
  21813. * @returns {Object}
  21814. * @private
  21815. */
  21816. async _getTextureForDistance(distance) {
  21817. if (!this._distanceCache || this._distanceCache?.distance !== distance) {
  21818. let scaleX = 1;
  21819. let scaleY = 1;
  21820. let texture;
  21821. let filePath;
  21822. let spriteAnchor = this.data.anchor?.x ?? 1;
  21823. if (this._file instanceof SequencerFileBase) {
  21824. const scaledDistance = distance / (this.data.scale.x ?? 1);
  21825. const result = await this._file.getTexture(scaledDistance);
  21826. filePath = result.filePath;
  21827. texture = result.texture;
  21828. spriteAnchor = result.spriteAnchor ?? this.data.anchor?.x ?? 0;
  21829. scaleX = result.spriteScale;
  21830. if (this.data.stretchTo?.onlyX) {
  21831. const widthWithPadding = texture.width - (this.template.startPoint + this.template.endPoint);
  21832. scaleY = widthWithPadding / texture.width;
  21833. } else {
  21834. scaleY = result.spriteScale;
  21835. }
  21836. } else if (this._file instanceof PIXI.Texture) {
  21837. filePath = this.data.file;
  21838. texture = this._file;
  21839. spriteAnchor = this.template.startPoint / texture.width;
  21840. const widthWithPadding = texture.width - (this.template.startPoint + this.template.endPoint);
  21841. let spriteScale = distance / widthWithPadding;
  21842. scaleX = spriteScale;
  21843. if (this.data.stretchTo?.onlyX) {
  21844. scaleY = widthWithPadding / texture.width;
  21845. } else {
  21846. scaleY = spriteScale;
  21847. }
  21848. }
  21849. this._distanceCache = {
  21850. filePath,
  21851. texture,
  21852. spriteAnchor,
  21853. scaleX,
  21854. scaleY,
  21855. distance
  21856. };
  21857. }
  21858. return this._distanceCache;
  21859. }
  21860. /**
  21861. * Applies the distance scaling to the sprite based on the previous method
  21862. *
  21863. * @returns {Promise<void>}
  21864. * @private
  21865. */
  21866. async _applyDistanceScaling() {
  21867. const ray = new Ray(this.sourcePosition, this.targetPosition);
  21868. this._rotateTowards(ray);
  21869. let { filePath, texture, spriteAnchor, scaleX, scaleY, distance } = await this._getTextureForDistance(ray.distance);
  21870. if (this._currentFilePath !== filePath || this._relatedSprites[filePath] === void 0) {
  21871. this._texture = texture;
  21872. this.video = filePath.toLowerCase().endsWith(".webm") ? texture?.baseTexture?.resource?.source ?? false : false;
  21873. Object.values(this._relatedSprites).forEach((subsprite) => {
  21874. subsprite.renderable = false;
  21875. });
  21876. this._currentFilePath = filePath;
  21877. if (this._relatedSprites[filePath]) {
  21878. this._relatedSprites[filePath].renderable = true;
  21879. } else {
  21880. let sprite;
  21881. let spriteType = this.data.tilingTexture ? PIXI.TilingSprite : SpriteMesh;
  21882. if (this.data.xray) {
  21883. sprite = new spriteType(texture);
  21884. } else {
  21885. sprite = new spriteType(texture, VisionSamplerShader);
  21886. }
  21887. this._relatedSprites[filePath] = sprite;
  21888. if (this.data.tint) {
  21889. sprite.tint = this.data.tint;
  21890. }
  21891. this.sprite.addChild(sprite);
  21892. }
  21893. }
  21894. this.playMedia();
  21895. if (this._relatedSprites[filePath]) {
  21896. if (this.data.attachTo?.active) {
  21897. this._applyAttachmentOffset();
  21898. }
  21899. const sprite = this._relatedSprites[filePath];
  21900. if (!sprite.parent) {
  21901. this.sprite.addChild(sprite);
  21902. }
  21903. if (this.data.tilingTexture) {
  21904. const scaleX2 = (this.data.scale.x ?? 1) * this.gridSizeDifference;
  21905. const scaleY2 = (this.data.scale.y ?? 1) * this.gridSizeDifference;
  21906. sprite.width = distance / scaleX2;
  21907. sprite.height = texture.height;
  21908. sprite.scale.set(scaleX2 * this.flipX, scaleY2 * this.flipY);
  21909. sprite.tileScale.x = this.data.tilingTexture.scale.x;
  21910. sprite.tileScale.y = this.data.tilingTexture.scale.y;
  21911. sprite.tilePosition = this.data.tilingTexture.position;
  21912. } else {
  21913. sprite.scale.set(
  21914. scaleX * (this.data.scale.x ?? 1) * this.flipX,
  21915. scaleY * (this.data.scale.y ?? 1) * this.flipY
  21916. );
  21917. }
  21918. sprite.anchor.set(
  21919. this.flipX === 1 ? spriteAnchor : 1 - spriteAnchor,
  21920. this.data.anchor?.y ?? 0.5
  21921. );
  21922. }
  21923. }
  21924. _checkWallCollisions() {
  21925. if (!this.data.stretchTo?.attachTo || !this.data.stretchTo?.requiresLineOfSight)
  21926. return true;
  21927. const ray = new Ray(this.sourcePosition, this.targetPosition);
  21928. const blockingObjects = canvas.walls.checkCollision(ray, { type: "sight" });
  21929. if (!blockingObjects.length && !this.data.stretchTo?.hideLineOfSight) {
  21930. this._ticker.stop();
  21931. SequencerEffectManager.endEffects({ effects: this });
  21932. }
  21933. return !blockingObjects.length;
  21934. }
  21935. /**
  21936. * Rotates the effect towards the target
  21937. *
  21938. * @param ray
  21939. * @private
  21940. */
  21941. _rotateTowards(ray) {
  21942. if (!ray) {
  21943. const sourcePosition = this.flipX === 1 ? this.sourcePosition : this.targetPosition;
  21944. const targetPosition = this.flipX === 1 ? this.targetPosition : this.sourcePosition;
  21945. ray = new Ray(sourcePosition, targetPosition);
  21946. }
  21947. this.rotationContainer.rotation = Math.normalizeRadians(
  21948. ray.angle + Math.toRadians(this.data.rotateTowards?.rotationOffset ?? 0)
  21949. );
  21950. this._tweakRotationForIsometric();
  21951. }
  21952. _tweakRotationForIsometric() {
  21953. if (!CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE)
  21954. return;
  21955. if (this.data.stretchTo) {
  21956. let skew = Math.normalizeRadians(
  21957. this.rotationContainer.rotation - Math.PI / 4
  21958. );
  21959. if (Math.abs(skew) >= Math.PI / 2 - 0.5 && Math.abs(skew) <= Math.PI / 2 + 0.5) {
  21960. skew -= Math.PI / 2;
  21961. }
  21962. this.isometricContainer.skew.set(Math.normalizeRadians(skew), 0);
  21963. this.isometricContainer.rotation = 0;
  21964. } else if (this.data?.isometric?.overlay) {
  21965. this.rotationContainer.rotation = 0;
  21966. let skew = Math.PI / 4 + this.rotationContainer.rotation;
  21967. this.isometricContainer.skew.set(
  21968. Math.normalizeRadians(skew - Math.PI / 4),
  21969. 0
  21970. );
  21971. this.isometricContainer.scale.set(
  21972. 1,
  21973. window.scale ?? CONSTANTS.INTEGRATIONS.ISOMETRIC.ISOMETRIC_CONVERSION
  21974. );
  21975. } else {
  21976. this.isometricContainer.rotation = 0;
  21977. }
  21978. }
  21979. /**
  21980. * Transforms the sprite, rotating it, stretching it, scaling it, sizing it according its data
  21981. *
  21982. * @private
  21983. */
  21984. async _transformSprite() {
  21985. if (this.data.stretchTo) {
  21986. if (this.data.stretchTo?.attachTo) {
  21987. this._transformStretchToAttachedSprite();
  21988. }
  21989. await this._applyDistanceScaling();
  21990. } else {
  21991. if (!this.sprite?.texture?.valid && this._texture?.valid) {
  21992. this.sprite.texture = this._texture;
  21993. }
  21994. }
  21995. if (this.video && (this._startTime || this._loopOffset > 0) && this.video?.currentTime !== void 0) {
  21996. await wait$1(20);
  21997. this.updateTexture();
  21998. }
  21999. if (!this.data.stretchTo) {
  22000. this._transformNoStretchSprite();
  22001. }
  22002. if (this.data.attachTo?.active && !this.data.stretchTo?.attachTo) {
  22003. await this._transformAttachedNoStretchSprite();
  22004. } else {
  22005. if (!this.data.screenSpace) {
  22006. this.position.set(this.sourcePosition.x, this.sourcePosition.y);
  22007. }
  22008. }
  22009. if (this.data.rotateTowards) {
  22010. this._rotateTowards();
  22011. if (this.data.rotateTowards?.attachTo) {
  22012. this._transformRotateTowardsAttachedSprite();
  22013. }
  22014. }
  22015. this._tweakRotationForIsometric();
  22016. if (!this.data.anchor && this.data.rotateTowards) {
  22017. const textureWidth = (this._texture?.width ?? this.sprite.width) / 2;
  22018. const startPointRatio = this.template.startPoint / textureWidth;
  22019. this.spriteContainer.pivot.set(
  22020. this.sprite.width * (-0.5 + startPointRatio),
  22021. 0
  22022. );
  22023. } else {
  22024. this.spriteContainer.pivot.set(
  22025. interpolate(
  22026. this.sprite.width * -0.5,
  22027. this.sprite.width * 0.5,
  22028. this.data.anchor?.x ?? 0.5
  22029. ),
  22030. interpolate(
  22031. this.sprite.height * -0.5,
  22032. this.sprite.height * 0.5,
  22033. this.data.anchor?.y ?? 0.5
  22034. )
  22035. );
  22036. }
  22037. }
  22038. _transformStretchToAttachedSprite() {
  22039. this._ticker.add(async () => {
  22040. try {
  22041. await this._applyDistanceScaling();
  22042. } catch (err) {
  22043. }
  22044. });
  22045. }
  22046. _transformNoStretchSprite() {
  22047. if (this.data.tilingTexture) {
  22048. this.sprite.tileScale = {
  22049. x: this.data.tilingTexture.scale.x * this.gridSizeDifference,
  22050. y: this.data.tilingTexture.scale.y * this.gridSizeDifference
  22051. };
  22052. this.sprite.tilePosition = this.data.tilingTexture.position;
  22053. }
  22054. const baseScaleX = (this.data.scale?.x ?? 1) * (this.data.spriteScale?.x ?? 1) * this.flipX;
  22055. const baseScaleY = (this.data.scale?.y ?? 1) * (this.data.spriteScale?.y ?? 1) * this.flipY;
  22056. const heightWidthRatio = this.sprite.height / this.sprite.width;
  22057. const widthHeightRatio = this.sprite.width / this.sprite.height;
  22058. const ratioToUse = heightWidthRatio > widthHeightRatio;
  22059. if (this.data.scaleToObject) {
  22060. let { width: width2, height } = this.target ? this.getTargetData() : this.getSourceData();
  22061. const target = this.targetDocument || this.sourceDocument;
  22062. if (target instanceof TokenDocument) {
  22063. width2 *= this.data.scaleToObject?.considerTokenScale ? target.texture.scaleX : 1;
  22064. height *= this.data.scaleToObject?.considerTokenScale ? target.texture.scaleY : 1;
  22065. }
  22066. if (this.data.scaleToObject?.uniform) {
  22067. let newWidth = Math.max(width2, height);
  22068. height = Math.max(width2, height);
  22069. width2 = newWidth;
  22070. } else {
  22071. width2 = width2 * (ratioToUse ? widthHeightRatio : 1);
  22072. height = height * (!ratioToUse ? heightWidthRatio : 1);
  22073. }
  22074. this.sprite.width = width2 * (this.data.scaleToObject?.scale ?? 1) * baseScaleX;
  22075. this.sprite.height = height * (this.data.scaleToObject?.scale ?? 1) * baseScaleY;
  22076. } else if (this.data.size) {
  22077. let { height, width: width2 } = this.data.size;
  22078. if (this.data.size.width === "auto" || this.data.size.height === "auto") {
  22079. height = this.sprite.height;
  22080. width2 = this.sprite.width;
  22081. if (this.data.size.width === "auto") {
  22082. height = this.data.size.height;
  22083. if (this.data.size.gridUnits) {
  22084. height *= canvas.grid.size;
  22085. }
  22086. width2 = height * widthHeightRatio;
  22087. } else if (this.data.size.height === "auto") {
  22088. width2 = this.data.size.width;
  22089. if (this.data.size.gridUnits) {
  22090. width2 *= canvas.grid.size;
  22091. }
  22092. height = width2 * heightWidthRatio;
  22093. }
  22094. } else if (this.data.size.gridUnits) {
  22095. height *= canvas.grid.size;
  22096. width2 *= canvas.grid.size;
  22097. }
  22098. this.sprite.width = width2 * baseScaleX;
  22099. this.sprite.height = height * baseScaleY;
  22100. } else {
  22101. this.sprite.scale.set(
  22102. baseScaleX * this.gridSizeDifference,
  22103. baseScaleY * this.gridSizeDifference
  22104. );
  22105. }
  22106. }
  22107. async _transformAttachedNoStretchSprite() {
  22108. 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;
  22109. this._ticker.add(() => {
  22110. if (this.isDestroyed)
  22111. return;
  22112. if (applyRotation) {
  22113. this.rotationContainer.rotation = this.getSourceData().rotation;
  22114. }
  22115. this._tweakRotationForIsometric();
  22116. try {
  22117. this._applyAttachmentOffset();
  22118. } catch (err) {
  22119. debug_error(err);
  22120. }
  22121. });
  22122. }
  22123. _applyAttachmentOffset() {
  22124. let offset2 = { x: 0, y: 0 };
  22125. if (this.data.attachTo?.align && this.data.attachTo?.align !== "center") {
  22126. offset2 = align({
  22127. context: this.source,
  22128. spriteWidth: this.sprite.width,
  22129. spriteHeight: this.sprite.height,
  22130. align: this.data.attachTo?.align,
  22131. edge: this.data.attachTo?.edge
  22132. });
  22133. }
  22134. this.position.set(
  22135. this.sourcePosition.x - offset2.x,
  22136. this.sourcePosition.y - offset2.y
  22137. );
  22138. }
  22139. _transformRotateTowardsAttachedSprite() {
  22140. this._ticker.add(async () => {
  22141. if (this.isDestroyed)
  22142. return;
  22143. try {
  22144. this._rotateTowards();
  22145. } catch (err) {
  22146. debug_error(err);
  22147. }
  22148. });
  22149. }
  22150. /**
  22151. * Provided an animation targeting the rotation of the sprite's primary container, this method will counter-rotate
  22152. * the sprite in an equal fashion so that the sprite's rotation remains static relative to this animation
  22153. *
  22154. * @param animation
  22155. * @returns {*[]}
  22156. * @private
  22157. */
  22158. _counterAnimateRotation(animation2) {
  22159. if (animation2.target === this.spriteContainer && this.data.zeroSpriteRotation) {
  22160. delete animation2.target;
  22161. let counterAnimation = foundry.utils.deepClone(animation2);
  22162. animation2.target = this.spriteContainer;
  22163. counterAnimation.target = this.sprite;
  22164. if (counterAnimation.values) {
  22165. counterAnimation.values = counterAnimation.values.map(
  22166. (value) => value * -1
  22167. );
  22168. } else {
  22169. counterAnimation.from *= -1;
  22170. counterAnimation.to *= -1;
  22171. }
  22172. if (!Array.isArray(animation2)) {
  22173. animation2 = [animation2, counterAnimation];
  22174. } else {
  22175. animation2.push(counterAnimation);
  22176. }
  22177. }
  22178. return animation2;
  22179. }
  22180. /**
  22181. * Plays the custom animations of this effect
  22182. *
  22183. * @returns {number}
  22184. * @private
  22185. */
  22186. _playCustomAnimations() {
  22187. if (!this.data.animations)
  22188. return 0;
  22189. this._playAnimations(
  22190. foundry.utils.deepClone(this.data.animations) ?? [],
  22191. this.actualCreationTime - this.data.creationTimestamp
  22192. );
  22193. }
  22194. _playAnimations(animations, timeDifference = 0) {
  22195. let animationsToSend = [];
  22196. const oneShotAnimations = animations.filter(
  22197. (animation2) => !animation2.looping && !animation2.fromEnd
  22198. );
  22199. for (let animation2 of oneShotAnimations) {
  22200. animation2.target = foundry.utils.getProperty(this, animation2.target);
  22201. if (!animation2.target)
  22202. continue;
  22203. if (animation2.propertyName.indexOf("rotation") > -1) {
  22204. animation2.from = animation2.from * (Math.PI / 180);
  22205. animation2.to = animation2.to * (Math.PI / 180);
  22206. }
  22207. if (["position.x", "position.y", "height", "width"].includes(
  22208. animation2.propertyName
  22209. ) && animation2.gridUnits) {
  22210. animation2.from *= canvas.grid.size;
  22211. animation2.to *= canvas.grid.size;
  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. const loopingAnimations = animations.filter(
  22221. (animation2) => animation2.looping
  22222. );
  22223. for (let animation2 of loopingAnimations) {
  22224. animation2.target = foundry.utils.getProperty(this, animation2.target);
  22225. if (!animation2.target)
  22226. continue;
  22227. if (animation2.propertyName.indexOf("rotation") > -1) {
  22228. animation2.values = animation2.values.map((angle) => {
  22229. return angle * (Math.PI / 180);
  22230. });
  22231. }
  22232. if (["position.x", "position.y", "height", "width"].includes(
  22233. animation2.propertyName
  22234. ) && animation2.gridUnits) {
  22235. animation2.values = animation2.values.map((value) => {
  22236. return value * canvas.grid.size;
  22237. });
  22238. }
  22239. if (["hue"].includes(animation2.propertyName)) {
  22240. animation2.getPropertyName = "values." + animation2.propertyName;
  22241. }
  22242. animationsToSend = animationsToSend.concat(
  22243. this._counterAnimateRotation(animation2)
  22244. );
  22245. }
  22246. if (!(this instanceof PersistentCanvasEffect)) {
  22247. animationsToSend = animationsToSend.concat(
  22248. this._getFromEndCustomAnimations()
  22249. );
  22250. }
  22251. setTimeout(() => {
  22252. SequencerAnimationEngine.addAnimation(
  22253. this.id,
  22254. animationsToSend,
  22255. timeDifference
  22256. );
  22257. }, 20);
  22258. }
  22259. _getFromEndCustomAnimations(immediate = false) {
  22260. let fromEndAnimations = [];
  22261. const animations = foundry.utils.deepClone(this.data.animations) ?? [];
  22262. const oneShotEndingAnimations = animations.filter(
  22263. (animation2) => !animation2.looping && animation2.fromEnd
  22264. );
  22265. for (let animation2 of oneShotEndingAnimations) {
  22266. animation2.target = foundry.utils.getProperty(this, animation2.target);
  22267. if (!animation2.target)
  22268. continue;
  22269. animation2.delay = is_real_number(immediate) ? Math.max(immediate - animation2.duration + animation2.delay, 0) : Math.max(
  22270. this._animationDuration - animation2.duration + animation2.delay,
  22271. 0
  22272. );
  22273. if (animation2.propertyName.indexOf("rotation") > -1) {
  22274. animation2.from = animation2.from * (Math.PI / 180);
  22275. animation2.to = animation2.to * (Math.PI / 180);
  22276. }
  22277. if (["position.x", "position.y", "height", "width"].includes(
  22278. animation2.propertyName
  22279. ) && animation2.gridUnits) {
  22280. animation2.from *= canvas.grid.size;
  22281. animation2.to *= canvas.grid.size;
  22282. }
  22283. fromEndAnimations = fromEndAnimations.concat(
  22284. this._counterAnimateRotation(animation2)
  22285. );
  22286. }
  22287. return fromEndAnimations;
  22288. }
  22289. /**
  22290. * Fades in the effect at the start of the effect
  22291. *
  22292. * @returns {number|*}
  22293. * @private
  22294. */
  22295. _fadeIn() {
  22296. if (!this.data.fadeIn || !this.sprite)
  22297. return 0;
  22298. let fadeIn = this.data.fadeIn;
  22299. if (this.actualCreationTime - (this.data.creationTimestamp + fadeIn.duration + fadeIn.delay) > 0) {
  22300. return;
  22301. }
  22302. this.alphaFilter.alpha = 0;
  22303. SequencerAnimationEngine.addAnimation(this.id, {
  22304. target: this.alphaFilter,
  22305. propertyName: "alpha",
  22306. to: this.data.opacity,
  22307. duration: fadeIn.duration,
  22308. ease: fadeIn.ease,
  22309. delay: fadeIn.delay,
  22310. absolute: true
  22311. });
  22312. return fadeIn.duration + fadeIn.delay;
  22313. }
  22314. /**
  22315. * Fades in the effect's audio at the start of the effect
  22316. *
  22317. * @returns {number|*}
  22318. * @private
  22319. */
  22320. _fadeInAudio() {
  22321. if (!this.data.fadeInAudio || !this.sprite || !this.video)
  22322. return 0;
  22323. let fadeInAudio = this.data.fadeInAudio;
  22324. if (this.actualCreationTime - (this.data.creationTimestamp + fadeInAudio.duration + fadeInAudio.delay) > 0)
  22325. return;
  22326. this.video.volume = 0;
  22327. SequencerAnimationEngine.addAnimation(this.id, {
  22328. target: this,
  22329. propertyName: "video.volume",
  22330. to: (this.data.volume ?? 0) * game.settings.get("core", "globalInterfaceVolume"),
  22331. duration: fadeInAudio.duration,
  22332. ease: fadeInAudio.ease,
  22333. delay: fadeInAudio.delay,
  22334. absolute: true
  22335. });
  22336. return fadeInAudio.duration + fadeInAudio.delay;
  22337. }
  22338. /**
  22339. * Fades out the effect at the end of the effect's duration
  22340. *
  22341. * @returns {number|*}
  22342. * @private
  22343. */
  22344. _fadeOut(immediate = false) {
  22345. if (!this.data.fadeOut || !this.sprite)
  22346. return 0;
  22347. let fadeOut = this.data.fadeOut;
  22348. fadeOut.delay = is_real_number(immediate) ? Math.max(immediate - fadeOut.duration + fadeOut.delay, 0) : Math.max(this._animationDuration - fadeOut.duration + fadeOut.delay, 0);
  22349. SequencerAnimationEngine.addAnimation(this.id, {
  22350. target: this.alphaFilter,
  22351. propertyName: "alpha",
  22352. to: 0,
  22353. duration: fadeOut.duration,
  22354. ease: fadeOut.ease,
  22355. delay: fadeOut.delay,
  22356. absolute: true
  22357. });
  22358. return fadeOut.duration + fadeOut.delay;
  22359. }
  22360. /**
  22361. * Fades out the effect at the end of the effect's duration
  22362. *
  22363. * @returns {number|*}
  22364. * @private
  22365. */
  22366. _fadeOutAudio(immediate = false) {
  22367. if (!this.data.fadeOutAudio || !this.sprite || !this.video)
  22368. return 0;
  22369. let fadeOutAudio = this.data.fadeOutAudio;
  22370. fadeOutAudio.delay = is_real_number(immediate) ? Math.max(immediate - fadeOutAudio.duration + fadeOutAudio.delay, 0) : Math.max(
  22371. this._animationDuration - fadeOutAudio.duration + fadeOutAudio.delay,
  22372. 0
  22373. );
  22374. SequencerAnimationEngine.addAnimation(this.id, {
  22375. target: this,
  22376. propertyName: "video.volume",
  22377. to: 0,
  22378. duration: fadeOutAudio.duration,
  22379. ease: fadeOutAudio.ease,
  22380. delay: fadeOutAudio.delay,
  22381. absolute: true
  22382. });
  22383. return fadeOutAudio.duration + fadeOutAudio.delay;
  22384. }
  22385. /**
  22386. * Determines the scale to animate from or to
  22387. * @param property
  22388. * @returns {{x: number, y: number}}
  22389. * @private
  22390. */
  22391. _determineScale(property) {
  22392. let scale2 = {
  22393. x: this.sprite.scale.x,
  22394. y: this.sprite.scale.y
  22395. };
  22396. if (is_real_number(property.value)) {
  22397. scale2.x *= property.value * this.gridSizeDifference * this.flipX;
  22398. scale2.y *= property.value * this.gridSizeDifference * this.flipY;
  22399. } else {
  22400. scale2.x *= property.value.x * this.gridSizeDifference * this.flipX;
  22401. scale2.y *= property.value.y * this.gridSizeDifference * this.flipY;
  22402. }
  22403. return scale2;
  22404. }
  22405. /**
  22406. * Scales the effect in at the start of the effect
  22407. *
  22408. * @returns {number|*}
  22409. * @private
  22410. */
  22411. _scaleIn() {
  22412. if (!this.data.scaleIn || !this.sprite)
  22413. return 0;
  22414. let scaleIn = this.data.scaleIn;
  22415. let fromScale = this._determineScale(scaleIn);
  22416. if (this.actualCreationTime - (this.data.creationTimestamp + scaleIn.duration + scaleIn.delay) > 0)
  22417. return;
  22418. let toScale = {
  22419. x: this.sprite.scale.x,
  22420. y: this.sprite.scale.y
  22421. };
  22422. this.sprite.scale.set(fromScale.x, fromScale.y);
  22423. SequencerAnimationEngine.addAnimation(this.id, [
  22424. {
  22425. target: this.sprite,
  22426. propertyName: "scale.x",
  22427. from: fromScale.x,
  22428. to: toScale.x,
  22429. duration: scaleIn.duration,
  22430. ease: scaleIn.ease,
  22431. delay: scaleIn.delay,
  22432. absolute: true
  22433. },
  22434. {
  22435. target: this.sprite,
  22436. propertyName: "scale.y",
  22437. from: fromScale.y,
  22438. to: toScale.y,
  22439. duration: scaleIn.duration,
  22440. ease: scaleIn.ease,
  22441. delay: scaleIn.delay,
  22442. absolute: true
  22443. }
  22444. ]);
  22445. return scaleIn.duration + scaleIn.delay;
  22446. }
  22447. /**
  22448. * Scales the effect out at the end of the effect's duration
  22449. *
  22450. * @returns {number|*}
  22451. * @private
  22452. */
  22453. _scaleOut(immediate = false) {
  22454. if (!this.data.scaleOut || !this.sprite)
  22455. return 0;
  22456. let scaleOut = this.data.scaleOut;
  22457. let scale2 = this._determineScale(scaleOut);
  22458. scaleOut.delay = is_real_number(immediate) ? Math.max(immediate - scaleOut.duration + scaleOut.delay, 0) : Math.max(
  22459. this._animationDuration - scaleOut.duration + scaleOut.delay,
  22460. 0
  22461. );
  22462. SequencerAnimationEngine.addAnimation(this.id, [
  22463. {
  22464. target: this.sprite,
  22465. propertyName: "scale.x",
  22466. to: scale2.x,
  22467. duration: scaleOut.duration,
  22468. ease: scaleOut.ease,
  22469. delay: scaleOut.delay,
  22470. absolute: true
  22471. },
  22472. {
  22473. target: this.sprite,
  22474. propertyName: "scale.y",
  22475. to: scale2.y,
  22476. duration: scaleOut.duration,
  22477. ease: scaleOut.ease,
  22478. delay: scaleOut.delay,
  22479. absolute: true
  22480. }
  22481. ]);
  22482. return scaleOut.duration + scaleOut.delay;
  22483. }
  22484. /**
  22485. * Rotates the effect in at the start of the effect
  22486. *
  22487. * @returns {number|*}
  22488. * @private
  22489. */
  22490. _rotateIn() {
  22491. if (!this.data.rotateIn || !this.sprite)
  22492. return 0;
  22493. let rotateIn = this.data.rotateIn;
  22494. if (this.actualCreationTime - (this.data.creationTimestamp + rotateIn.duration + rotateIn.delay) > 0)
  22495. return;
  22496. let original_radians = this.spriteContainer.rotation;
  22497. this.spriteContainer.rotation = rotateIn.value * (Math.PI / 180);
  22498. SequencerAnimationEngine.addAnimation(
  22499. this.id,
  22500. this._counterAnimateRotation({
  22501. target: this.spriteContainer,
  22502. propertyName: "rotation",
  22503. to: original_radians,
  22504. duration: rotateIn.duration,
  22505. ease: rotateIn.ease,
  22506. delay: rotateIn.delay,
  22507. absolute: true
  22508. })
  22509. );
  22510. return rotateIn.duration + rotateIn.delay;
  22511. }
  22512. /**
  22513. * Rotates the effect out at the end of the effect's duration
  22514. *
  22515. * @returns {number|*}
  22516. * @private
  22517. */
  22518. _rotateOut(immediate = false) {
  22519. if (!this.data.rotateOut || !this.sprite)
  22520. return 0;
  22521. let rotateOut = this.data.rotateOut;
  22522. rotateOut.delay = is_real_number(immediate) ? Math.max(immediate - rotateOut.duration + rotateOut.delay, 0) : Math.max(
  22523. this._animationDuration - rotateOut.duration + rotateOut.delay,
  22524. 0
  22525. );
  22526. SequencerAnimationEngine.addAnimation(
  22527. this.id,
  22528. this._counterAnimateRotation({
  22529. target: this.spriteContainer,
  22530. propertyName: "rotation",
  22531. to: rotateOut.value * (Math.PI / 180),
  22532. duration: rotateOut.duration,
  22533. ease: rotateOut.ease,
  22534. delay: rotateOut.delay,
  22535. absolute: true
  22536. })
  22537. );
  22538. return rotateOut.duration + rotateOut.delay;
  22539. }
  22540. /**
  22541. * Causes the effect to move towards the given location
  22542. *
  22543. * @returns {number|*}
  22544. * @private
  22545. */
  22546. _moveTowards() {
  22547. if (!this.data.moves || !this.sprite)
  22548. return 0;
  22549. let moves2 = this.data.moves;
  22550. let movementDuration = this._animationDuration;
  22551. if (this.data.moveSpeed) {
  22552. const distance = distance_between(
  22553. this.sourcePosition,
  22554. this.targetPosition
  22555. );
  22556. movementDuration = distance / this.data.moveSpeed * 1e3;
  22557. }
  22558. if (this.data.moves.rotate)
  22559. this._rotateTowards();
  22560. const duration = movementDuration - moves2.delay;
  22561. if (this.actualCreationTime - (this.data.creationTimestamp + duration + moves2.delay) > 0)
  22562. return;
  22563. SequencerAnimationEngine.addAnimation(this.id, [
  22564. {
  22565. target: this,
  22566. propertyName: "position.x",
  22567. to: this.targetPosition.x,
  22568. duration,
  22569. ease: moves2.ease,
  22570. delay: moves2.delay
  22571. },
  22572. {
  22573. target: this,
  22574. propertyName: "position.y",
  22575. to: this.targetPosition.y,
  22576. duration,
  22577. ease: moves2.ease,
  22578. delay: moves2.delay
  22579. }
  22580. ]);
  22581. return duration + moves2.delay;
  22582. }
  22583. /**
  22584. * If this effect is temporary, this sets the timeout for when the effect should resolve and get removed;
  22585. *
  22586. * @private
  22587. */
  22588. _setEndTimeout() {
  22589. setTimeout(() => {
  22590. this._resolve(this.data);
  22591. this.endEffect();
  22592. }, this._animationDuration);
  22593. }
  22594. _setupTimestampHook(offset2) {
  22595. if (!this._file?.originalMetadata?.timestamps || this._ended)
  22596. return;
  22597. const timestamps = this._file.getTimestamps();
  22598. const timestampArray = Array.isArray(timestamps) ? timestamps : [timestamps];
  22599. for (const timestamp of timestampArray) {
  22600. if (!is_real_number(timestamp))
  22601. continue;
  22602. let realTimestamp = timestamp - offset2 / this.mediaPlaybackRate;
  22603. if (realTimestamp < 0) {
  22604. realTimestamp += this._endTime;
  22605. }
  22606. setTimeout(() => {
  22607. if (this._ended)
  22608. return;
  22609. Hooks.callAll("sequencerEffectTimestamp", this, this._file);
  22610. if (this.mediaLooping) {
  22611. const offsets = (this._endTime - this.mediaCurrentTime) * -1e3;
  22612. this._setupTimestampHook(offsets);
  22613. }
  22614. }, realTimestamp);
  22615. }
  22616. }
  22617. }
  22618. class PersistentCanvasEffect extends CanvasEffect {
  22619. /**
  22620. * @OVERRIDE
  22621. * @returns {Promise<void>}
  22622. * @private
  22623. */
  22624. async _initialize() {
  22625. await super._initialize(false);
  22626. await this._startEffect();
  22627. }
  22628. /**
  22629. * @OVERRIDE
  22630. * @returns {Promise<void>}
  22631. * @private
  22632. */
  22633. async _reinitialize() {
  22634. await super._reinitialize(false);
  22635. }
  22636. /** @OVERRIDE */
  22637. _playPresetAnimations() {
  22638. this._moveTowards();
  22639. this._fadeIn();
  22640. this._scaleIn();
  22641. this._rotateIn();
  22642. }
  22643. /**
  22644. * Starts the loop of this effect, calculating the difference between the effect's creation time, and the actual
  22645. * creation time on the client
  22646. *
  22647. * @returns {Promise<void>}
  22648. * @private
  22649. */
  22650. async _startEffect() {
  22651. if (!this.hasAnimatedMedia)
  22652. return;
  22653. let creationTimeDifference = this.actualCreationTime - this.data.creationTimestamp;
  22654. if (!this.data.noLoop) {
  22655. return this._startLoop(creationTimeDifference);
  22656. }
  22657. if (creationTimeDifference < this._animationDuration) {
  22658. this.mediaCurrentTime = creationTimeDifference / 1e3;
  22659. if (this._endTime !== this.mediaDuration) {
  22660. setTimeout(() => {
  22661. this.mediaCurrentTime = this._endTime;
  22662. this.updateTexture();
  22663. }, this._endTime * 1e3 - creationTimeDifference);
  22664. }
  22665. await this.playMedia();
  22666. return;
  22667. }
  22668. await this.pauseMedia();
  22669. this.mediaCurrentTime = this._endTime;
  22670. if (this.sprite.texture && this.video) {
  22671. const oldRenderable = this.renderable;
  22672. this.renderable = false;
  22673. setTimeout(() => {
  22674. this.renderable = oldRenderable;
  22675. this.updateTexture();
  22676. }, 350);
  22677. }
  22678. }
  22679. /**
  22680. * Kicks off the loop, or just sets the video to loop
  22681. *
  22682. * @param creationTimeDifference
  22683. * @returns {Promise<void>}
  22684. * @private
  22685. */
  22686. async _startLoop(creationTimeDifference) {
  22687. this.mediaLooping = this.playNaturally;
  22688. if (!this._animationTimes.loopStart) {
  22689. this._loopOffset = creationTimeDifference % this._animationDuration / 1e3;
  22690. } else if (creationTimeDifference / 1e3 > this._animationTimes.loopStart) {
  22691. const loopDuration = this._animationTimes.loopEnd - this._animationTimes.loopStart;
  22692. this._loopOffset = creationTimeDifference % (loopDuration * 1e3) / 1e3;
  22693. }
  22694. return this._resetLoop();
  22695. }
  22696. /**
  22697. * Continuously reset the video to the right time so that the start and end time can be preserved
  22698. *
  22699. * @returns {Promise<void>}
  22700. * @private
  22701. */
  22702. async _resetLoop(firstLoop = true) {
  22703. if (this._ended)
  22704. return;
  22705. let loopWaitTime = 0;
  22706. if (this._animationTimes.loopStart) {
  22707. if (this._isEnding)
  22708. return;
  22709. this.mediaCurrentTime = (firstLoop ? 0 : this._animationTimes.loopStart) + (this._loopOffset > 0 ? this._loopOffset : 0);
  22710. loopWaitTime = (this._animationTimes.loopEnd - this.mediaCurrentTime) * 1e3;
  22711. } else {
  22712. this.mediaCurrentTime = this._startTime + this._loopOffset;
  22713. loopWaitTime = this._animationDuration - this._loopOffset * 1e3;
  22714. }
  22715. await this.playMedia();
  22716. if (this.mediaLooping) {
  22717. return;
  22718. }
  22719. this._resetTimeout = setTimeout(() => {
  22720. if (this._ended)
  22721. return;
  22722. this._loopOffset = 0;
  22723. this._resetLoop(false);
  22724. }, loopWaitTime);
  22725. }
  22726. /** @OVERRIDE */
  22727. _timeoutVisibility() {
  22728. let creationTimeDifference = this.actualCreationTime - this.data.creationTimestamp;
  22729. let timeout = creationTimeDifference === 0 && !this.data.animations ? 0 : 50;
  22730. setTimeout(() => {
  22731. this._setupHooks();
  22732. }, timeout);
  22733. }
  22734. /** @OVERRIDE */
  22735. _setEndTimeout() {
  22736. let creationTimeDifference = this.actualCreationTime - this.data.creationTimestamp;
  22737. if (!this.data.noLoop || creationTimeDifference >= this._animationDuration || !(this.hasAnimatedMedia || this.data.text))
  22738. return;
  22739. setTimeout(() => {
  22740. this.pauseMedia();
  22741. }, this._animationDuration);
  22742. }
  22743. /** @OVERRIDE */
  22744. async endEffect() {
  22745. if (this._isEnding)
  22746. return;
  22747. this._isEnding = true;
  22748. let fullWaitDuration = 0;
  22749. let extraEndDuration = this.data.extraEndDuration ?? 0;
  22750. if (this._animationTimes?.forcedEnd) {
  22751. this.mediaCurrentTime = this._animationTimes.forcedEnd;
  22752. fullWaitDuration = (this.mediaDuration - (this._animationTimes?.forcedEnd ?? 0)) * 1e3;
  22753. } else if (this._animationTimes?.loopEnd) {
  22754. fullWaitDuration = (this.mediaDuration - this.mediaCurrentTime) * 1e3;
  22755. this.mediaLooping = false;
  22756. extraEndDuration = Math.max(extraEndDuration, fullWaitDuration);
  22757. }
  22758. const fromEndCustomAnimations = this._getFromEndCustomAnimations(extraEndDuration);
  22759. const durations = [
  22760. this._fadeOut(extraEndDuration),
  22761. this._fadeOutAudio(extraEndDuration),
  22762. this._scaleOut(extraEndDuration),
  22763. this._rotateOut(extraEndDuration),
  22764. this.data.extraEndDuration,
  22765. fullWaitDuration,
  22766. ...fromEndCustomAnimations.map(
  22767. (animation2) => animation2.duration + animation2.delay
  22768. )
  22769. ].filter(Boolean);
  22770. SequencerAnimationEngine.addAnimation(this.id, fromEndCustomAnimations);
  22771. const waitDuration = Math.max(...durations, 0);
  22772. this._resolve(waitDuration);
  22773. return new Promise(
  22774. (resolve) => setTimeout(() => {
  22775. super.endEffect();
  22776. resolve(this.data);
  22777. }, waitDuration)
  22778. );
  22779. }
  22780. }
  22781. function createShape(shape) {
  22782. const graphic = new PIXI.LegacyGraphics();
  22783. graphic.beginFill(
  22784. shape?.fillColor ?? 16777215,
  22785. shape?.fillAlpha ?? shape?.isMask ? 1 : 0
  22786. );
  22787. graphic.lineStyle(
  22788. shape.lineSize ?? (shape?.isMask ? 1 : 0),
  22789. shape?.lineColor ?? 16777215
  22790. );
  22791. const offsetX = (shape.offset?.x ?? 0) * (shape.offset?.gridUnits ? canvas.grid.size : 1);
  22792. const offsetY = (shape.offset?.y ?? 0) * (shape.offset?.gridUnits ? canvas.grid.size : 1);
  22793. const sizeMultiplier = shape.gridUnits ? canvas.grid.size : 1;
  22794. graphic.offset = {
  22795. x: offsetX,
  22796. y: offsetY
  22797. };
  22798. switch (shape.type) {
  22799. case CONSTANTS.SHAPES.CIRC:
  22800. graphic.drawCircle(
  22801. graphic.offset.x,
  22802. graphic.offset.y,
  22803. shape.radius * sizeMultiplier
  22804. );
  22805. break;
  22806. case CONSTANTS.SHAPES.RECT:
  22807. graphic.drawRect(
  22808. graphic.offset.x,
  22809. graphic.offset.y,
  22810. shape.width * sizeMultiplier,
  22811. shape.height * sizeMultiplier
  22812. );
  22813. break;
  22814. case CONSTANTS.SHAPES.ELIP:
  22815. graphic.drawEllipse(
  22816. graphic.offset.x,
  22817. graphic.offset.y,
  22818. shape.width * sizeMultiplier,
  22819. shape.height * sizeMultiplier
  22820. );
  22821. break;
  22822. case CONSTANTS.SHAPES.RREC:
  22823. graphic.drawRoundedRect(
  22824. graphic.offset.x,
  22825. graphic.offset.y,
  22826. shape.width * sizeMultiplier,
  22827. shape.height * sizeMultiplier,
  22828. shape.radius * sizeMultiplier
  22829. );
  22830. break;
  22831. case CONSTANTS.SHAPES.POLY:
  22832. graphic.drawPolygon(
  22833. shape.points.map((point) => {
  22834. return new PIXI.Point(
  22835. point[0] * sizeMultiplier,
  22836. point[1] * sizeMultiplier
  22837. );
  22838. })
  22839. );
  22840. break;
  22841. }
  22842. graphic.alpha = shape.alpha ?? 1;
  22843. graphic.endFill();
  22844. return graphic;
  22845. }
  22846. function calculate_missed_position(source, target, twister) {
  22847. const sourcePosition = get_object_position(source);
  22848. const sourceDimensions = get_object_dimensions(source, true);
  22849. if (!target) {
  22850. const angle2 = twister.random() * Math.PI * 2;
  22851. let x2 = Math.cos(angle2) * sourceDimensions.width;
  22852. let y2 = Math.sin(angle2) * sourceDimensions.height;
  22853. return {
  22854. x: random_float_between(x2 * 1.5, x2 * 2.5, twister),
  22855. y: random_float_between(y2 * 1.5, y2 * 2.5, twister)
  22856. };
  22857. }
  22858. const targetDimensions = get_object_dimensions(target, true);
  22859. const targetPosition = get_object_position(target);
  22860. const ray = new Ray(targetPosition, sourcePosition);
  22861. let startRadians = ray.angle + Math.PI / 2;
  22862. let endRadians = ray.angle - Math.PI / 2;
  22863. const sizeCompensation = Math.max(
  22864. 1,
  22865. Math.abs(sourceDimensions.width - targetDimensions.height)
  22866. );
  22867. let distance = ray.distance / canvas.grid.size - sizeCompensation;
  22868. if (distance <= 1) {
  22869. const angle2 = twister.random() > 0.5 ? ray.angle + Math.PI / 4 : ray.angle - Math.PI / 4;
  22870. const x2 = Math.cos(angle2) * targetDimensions.width;
  22871. const y2 = Math.sin(angle2) * targetDimensions.height;
  22872. return { x: x2, y: y2 };
  22873. }
  22874. distance = Math.max(Math.abs(distance - 15), 6);
  22875. endRadians -= Math.PI / distance;
  22876. startRadians += Math.PI / distance;
  22877. const angle = interpolate(startRadians, endRadians, twister.random());
  22878. const x = Math.cos(angle) * targetDimensions.width;
  22879. const y = Math.sin(angle) * targetDimensions.height;
  22880. return {
  22881. x: random_float_between(x * 1.5, x * 2.5, twister),
  22882. y: random_float_between(y * 1.5, y * 2.5, twister)
  22883. };
  22884. }
  22885. function get_object_position(obj, { measure = false, exact = false } = {}) {
  22886. if (obj instanceof CanvasEffect) {
  22887. return obj.worldPosition;
  22888. }
  22889. obj = obj?._object ?? obj.object ?? obj;
  22890. let pos = {};
  22891. if (obj instanceof MeasuredTemplate) {
  22892. if (measure) {
  22893. if (obj.document.t === "cone" || obj.document.t === "ray") {
  22894. pos.x = obj.ray.B.x;
  22895. pos.y = obj.ray.B.y;
  22896. }
  22897. }
  22898. if (obj.document.t === "rect") {
  22899. pos.x = obj.x;
  22900. pos.y = obj.y;
  22901. if (!exact) {
  22902. pos.x += Math.abs(obj.shape.width / 2) + obj.shape.x;
  22903. pos.y += Math.abs(obj.shape.height / 2) + obj.shape.y;
  22904. }
  22905. }
  22906. } else if (obj instanceof Tile) {
  22907. pos = {
  22908. x: obj.document.x,
  22909. y: obj.document.y
  22910. };
  22911. if (!exact) {
  22912. pos.x += Math.abs(obj.document.width / 2);
  22913. pos.y += Math.abs(obj.document.height / 2);
  22914. }
  22915. } else if (obj instanceof Token) {
  22916. const halfSize = get_object_dimensions(obj, true);
  22917. pos = {
  22918. x: obj.x + halfSize.width,
  22919. y: obj.y + halfSize.height
  22920. };
  22921. if (exact) {
  22922. pos.x -= halfSize.width;
  22923. pos.y -= halfSize.height;
  22924. }
  22925. } else if (obj instanceof Drawing) {
  22926. pos = {
  22927. x: obj.document.x,
  22928. y: obj.document.y
  22929. };
  22930. if (!exact) {
  22931. const halfSize = get_object_dimensions(obj, true);
  22932. pos.x += halfSize.width;
  22933. pos.y += halfSize.height;
  22934. }
  22935. }
  22936. pos = {
  22937. x: pos.x ?? obj?.x ?? obj?.position?.x ?? obj?.position?._x ?? obj?.document?.x ?? obj?.document?.position?.x ?? null,
  22938. y: pos.y ?? obj?.y ?? obj?.position?.y ?? obj?.position?._y ?? obj?.document?.y ?? obj?.document?.position?.y ?? null,
  22939. elevation: obj?.elevation ?? obj?.document?.elevation ?? null
  22940. };
  22941. if (pos.x === null)
  22942. delete pos["x"];
  22943. if (pos.y === null)
  22944. delete pos["y"];
  22945. if (pos.elevation === null)
  22946. delete pos["elevation"];
  22947. return pos;
  22948. }
  22949. function get_random_offset(target, randomOffset, twister = false) {
  22950. let { width: width2, height } = get_object_dimensions(target, true);
  22951. width2 *= randomOffset;
  22952. height *= randomOffset;
  22953. return {
  22954. x: random_float_between(width2 * -1, width2, twister),
  22955. y: random_float_between(height * -1, height, twister)
  22956. };
  22957. }
  22958. function get_object_dimensions(inObj, half = false) {
  22959. inObj = inObj?.object ?? inObj?._object ?? inObj;
  22960. let width2 = inObj?.hitArea?.width ?? inObj?.w ?? inObj?.shape?.width ?? (inObj?.shape?.radius ? inObj?.shape?.radius * 2 : void 0) ?? inObj?.width ?? canvas.grid.size;
  22961. let height = inObj?.hitArea?.height ?? inObj?.h ?? inObj?.shape?.height ?? (inObj?.shape?.radius ? inObj?.shape?.radius * 2 : void 0) ?? inObj?.height ?? canvas.grid.size;
  22962. return {
  22963. width: width2 / (half ? 2 : 1),
  22964. height: height / (half ? 2 : 1)
  22965. };
  22966. }
  22967. const alignments = {
  22968. "top-left": { x: 0.5, y: 0.5 },
  22969. top: { x: 0, y: 0.5 },
  22970. "top-right": { x: -0.5, y: 0.5 },
  22971. left: { x: 0.5, y: 0 },
  22972. center: { x: 0, y: 0 },
  22973. right: { x: -0.5, y: 0 },
  22974. "bottom-left": { x: 0.5, y: -0.5 },
  22975. bottom: { x: 0, y: -0.5 },
  22976. "bottom-right": { x: -0.5, y: -0.5 }
  22977. };
  22978. function align({
  22979. context,
  22980. spriteWidth,
  22981. spriteHeight,
  22982. align: align2,
  22983. edge
  22984. } = {}) {
  22985. let { width: width2, height } = get_object_dimensions(context);
  22986. const alignRatio = alignments[align2];
  22987. const offset2 = {
  22988. x: interpolate(width2 * -0.5, width2 * 0.5, alignRatio.x + 0.5),
  22989. y: interpolate(height * -0.5, height * 0.5, alignRatio.y + 0.5)
  22990. };
  22991. return {
  22992. x: offset2.x + (edge && edge !== "on" ? spriteWidth * alignRatio.x * (edge === "outer" ? 1 : -1) : 0),
  22993. y: offset2.y + (edge && edge !== "on" ? spriteHeight * alignRatio.y * (edge === "outer" ? 1 : -1) : 0)
  22994. };
  22995. }
  22996. function is_object_canvas_data(inObj) {
  22997. if (typeof inObj !== "object")
  22998. return false;
  22999. const keys = Object.keys(inObj);
  23000. keys.sort();
  23001. return keys.includes("x") && keys.includes("y") || keys.includes("height") && keys.includes("width") && keys.includes("x") && keys.includes("y");
  23002. }
  23003. function get_object_canvas_data(inObject, measure = false) {
  23004. inObject = inObject?.object ?? inObject;
  23005. return {
  23006. ...get_object_position(inObject, { measure }),
  23007. ...get_object_dimensions(inObject?.mesh ?? inObject?.tile ?? inObject),
  23008. elevation: get_object_elevation(inObject),
  23009. uuid: inObject?.document?.uuid ?? inObject?.uuid,
  23010. cachedLocation: true
  23011. };
  23012. }
  23013. function get_object_elevation(inObject) {
  23014. return inObject?.document?.elevation ?? inObject?.elevation ?? 0;
  23015. }
  23016. function get_mouse_position(snapToGrid = false, gridSnap = 2) {
  23017. const pos = getCanvasMouse().getLocalPosition(canvas.app.stage);
  23018. return !snapToGrid ? new PIXI.Point(pos.x, pos.y) : canvas.grid.getSnappedPosition(pos.x, pos.y, gridSnap);
  23019. }
  23020. function distance_between(p1, p2) {
  23021. return new Ray(p1, p2).distance;
  23022. }
  23023. function is_position_within_bounds(inPosition, inElement, relativeTo) {
  23024. const localPosition = inElement.toLocal(inPosition, relativeTo);
  23025. return inElement.getLocalBounds().contains(localPosition.x, localPosition.y);
  23026. }
  23027. function rotate_coordinate(p1, p2, radians) {
  23028. let cos = Math.cos(radians);
  23029. let sin = Math.sin(radians);
  23030. let nx = cos * (p2.x - p1.x) + sin * (p2.y - p1.y) + p1.x;
  23031. let ny = cos * (p2.y - p1.y) - sin * (p2.x - p1.x) + p1.y;
  23032. return [nx, ny];
  23033. }
  23034. function get_closest_token(inPosition, { minimumDistance = false } = {}) {
  23035. let tokens = Array.from(canvas.scene.tokens);
  23036. if (minimumDistance) {
  23037. tokens = tokens.filter(
  23038. (token) => distance_between(get_object_position(token), inPosition) <= minimumDistance
  23039. );
  23040. }
  23041. tokens.sort((a, b) => {
  23042. return distance_between(get_object_position(a), inPosition) - distance_between(get_object_position(b), inPosition);
  23043. });
  23044. return tokens?.[0] ?? false;
  23045. }
  23046. function rotateAroundPoint(cx, cy, x, y, radians) {
  23047. const cos = Math.cos(radians);
  23048. const sin = Math.sin(radians);
  23049. const nx = cos * (x - cx) + sin * (y - cy) + cx;
  23050. const ny = cos * (y - cy) - sin * (x - cx) + cy;
  23051. return { x: nx, y: ny };
  23052. }
  23053. function validateAnimation(inTarget, inPropertyName, inOptions) {
  23054. if (typeof inPropertyName !== "string") {
  23055. return `inPropertyName must be of type string`;
  23056. }
  23057. if (typeof inTarget !== "string") {
  23058. return `inTarget must be of type string`;
  23059. }
  23060. if (!is_real_number(inOptions.from)) {
  23061. return `inOptions.from must be of type number`;
  23062. }
  23063. if (!is_real_number(inOptions.to)) {
  23064. return `inOptions.to must be of type number`;
  23065. }
  23066. if (!is_real_number(inOptions.duration)) {
  23067. return `inOptions.duration must be of type number`;
  23068. }
  23069. if (inOptions?.delay !== void 0 && !is_real_number(inOptions.delay)) {
  23070. return `inOptions.delay must be of type number`;
  23071. }
  23072. if (inOptions?.ease !== void 0 && typeof inOptions.ease !== "string") {
  23073. return `inOptions.ease must be of type string`;
  23074. }
  23075. if (inOptions?.fromEnd !== void 0 && typeof inOptions.fromEnd !== "boolean") {
  23076. return `inOptions.fromEnd must be of type boolean`;
  23077. }
  23078. if (inOptions?.gridUnits !== void 0) {
  23079. if (typeof inOptions.gridUnits !== "boolean") {
  23080. return `inOptions.gridUnits must be of type boolean`;
  23081. }
  23082. if (inOptions.gridUnits && ![
  23083. "position.x",
  23084. "position.y",
  23085. "scale.x",
  23086. "scale.y",
  23087. "height",
  23088. "width"
  23089. ].includes(inPropertyName)) {
  23090. return `if inOptions.gridUnits is true, inPropertyName must be position.x, position.y, scale.x, scale.y, width, or height`;
  23091. }
  23092. }
  23093. return {
  23094. target: inTarget,
  23095. propertyName: inPropertyName,
  23096. from: inOptions?.from,
  23097. to: inOptions?.to,
  23098. duration: inOptions?.duration ?? 0,
  23099. delay: inOptions?.delay ?? 0,
  23100. ease: inOptions?.ease ?? "linear",
  23101. looping: false,
  23102. fromEnd: inOptions?.fromEnd ?? false,
  23103. gridUnits: inOptions?.gridUnits ?? false
  23104. };
  23105. }
  23106. function validateLoopingAnimation(inTarget, inPropertyName, inOptions) {
  23107. if (typeof inPropertyName !== "string") {
  23108. return `inPropertyName must be of type string`;
  23109. }
  23110. if (typeof inTarget !== "string") {
  23111. return `inTarget must be of type string`;
  23112. }
  23113. if (!inOptions?.values) {
  23114. if (!inOptions?.from === void 0 || !inOptions?.to === void 0) {
  23115. return `if inOptions.values is not set, you must provide inOptions.from and inOptions.to`;
  23116. }
  23117. if (!is_real_number(inOptions.from)) {
  23118. return `inOptions.from must be of type number`;
  23119. }
  23120. if (!is_real_number(inOptions.to)) {
  23121. return `inOptions.to must be of type number`;
  23122. }
  23123. inOptions.values = [inOptions?.from, inOptions?.to];
  23124. delete inOptions.from;
  23125. delete inOptions.to;
  23126. } else {
  23127. if (!Array.isArray(inOptions.values)) {
  23128. return `inOptions.values must be of type array`;
  23129. }
  23130. inOptions.values.forEach((value) => {
  23131. if (!is_real_number(value)) {
  23132. return `values in inOptions.keys must be of type number`;
  23133. }
  23134. });
  23135. }
  23136. if (!is_real_number(inOptions.duration)) {
  23137. return `inOptions.duration must be of type number`;
  23138. }
  23139. if (inOptions?.delay !== void 0 && !is_real_number(inOptions.delay)) {
  23140. return `inOptions.delay must be of type number`;
  23141. }
  23142. if (inOptions?.ease !== void 0 && typeof inOptions.ease !== "string") {
  23143. return `inOptions.ease must be of type string`;
  23144. }
  23145. if (inOptions?.loops !== void 0 && !is_real_number(inOptions.loops)) {
  23146. return `inOptions.loops must be of type number`;
  23147. }
  23148. if (inOptions?.pingPong !== void 0 && typeof inOptions.pingPong !== "boolean") {
  23149. return `inOptions.pingPong must be of type boolean`;
  23150. }
  23151. if (inOptions?.gridUnits !== void 0) {
  23152. if (typeof inOptions.gridUnits !== "boolean") {
  23153. return `inOptions.gridUnits must be of type boolean`;
  23154. }
  23155. if (inOptions.gridUnits && ![
  23156. "position.x",
  23157. "position.y",
  23158. "scale.x",
  23159. "scale.y",
  23160. "height",
  23161. "width"
  23162. ].includes(inPropertyName)) {
  23163. return `if inOptions.gridUnits is true, inPropertyName must be position.x, position.y, scale.x, scale.y, width, or height`;
  23164. }
  23165. }
  23166. return {
  23167. target: inTarget,
  23168. propertyName: inPropertyName,
  23169. values: inOptions?.values,
  23170. duration: inOptions?.duration ?? 0,
  23171. delay: inOptions?.delay ?? 0,
  23172. ease: inOptions?.ease ?? "linear",
  23173. looping: true,
  23174. loops: inOptions?.loops,
  23175. indefinite: inOptions?.loops === void 0 || !is_real_number(inOptions?.loops),
  23176. pingPong: inOptions?.pingPong ?? false,
  23177. gridUnits: inOptions?.gridUnits ?? false
  23178. };
  23179. }
  23180. const PlayerSettings = {
  23181. file: {
  23182. label: "SEQUENCER.Player.Option.File",
  23183. store: writable$1(""),
  23184. default: ""
  23185. },
  23186. scale: {
  23187. label: "SEQUENCER.Player.Option.Scale",
  23188. store: writable$1(1),
  23189. default: 1
  23190. },
  23191. users: {
  23192. label: "SEQUENCER.Player.Option.ForUsers",
  23193. store: writable$1([]),
  23194. default: []
  23195. },
  23196. belowTokens: {
  23197. label: "SEQUENCER.Player.Option.BelowTokens",
  23198. store: writable$1(false),
  23199. default: false
  23200. },
  23201. snapToGrid: {
  23202. label: "SEQUENCER.Player.Option.SnapToGrid",
  23203. store: writable$1(false),
  23204. default: false
  23205. },
  23206. rotation: {
  23207. label: "SEQUENCER.Player.Option.Rotation",
  23208. store: writable$1(0),
  23209. default: 0
  23210. },
  23211. randomRotation: {
  23212. label: "SEQUENCER.Player.Option.Randomize",
  23213. store: writable$1(false),
  23214. default: false
  23215. },
  23216. fadeIn: {
  23217. label: "SEQUENCER.Player.Option.FadeIn",
  23218. store: writable$1(0),
  23219. default: 0
  23220. },
  23221. fadeOut: {
  23222. label: "SEQUENCER.Player.Option.FadeOut",
  23223. store: writable$1(0),
  23224. default: 0
  23225. },
  23226. scaleIn: {
  23227. label: "SEQUENCER.Player.Option.ScaleIn",
  23228. store: writable$1(0),
  23229. default: 0
  23230. },
  23231. scaleOut: {
  23232. label: "SEQUENCER.Player.Option.ScaleOut",
  23233. store: writable$1(0),
  23234. default: 0
  23235. },
  23236. mirrorX: {
  23237. label: "SEQUENCER.Player.Option.MirrorX",
  23238. store: writable$1(false),
  23239. default: false
  23240. },
  23241. mirrorY: {
  23242. label: "SEQUENCER.Player.Option.MirrorY",
  23243. store: writable$1(false),
  23244. default: false
  23245. },
  23246. randomMirrorX: {
  23247. label: "SEQUENCER.Player.Option.Randomize",
  23248. store: writable$1(false),
  23249. default: false
  23250. },
  23251. randomMirrorY: {
  23252. label: "SEQUENCER.Player.Option.Randomize",
  23253. store: writable$1(false),
  23254. default: false
  23255. },
  23256. offsetX: {
  23257. label: "SEQUENCER.Player.Option.OffsetX",
  23258. store: writable$1(0),
  23259. default: 0
  23260. },
  23261. offsetY: {
  23262. label: "SEQUENCER.Player.Option.OffsetY",
  23263. store: writable$1(0),
  23264. default: 0
  23265. },
  23266. offsetGridUnits: {
  23267. label: "SEQUENCER.Player.Option.GridUnits",
  23268. store: writable$1(false),
  23269. default: false
  23270. },
  23271. randomOffsetAmount: {
  23272. label: "SEQUENCER.Player.Option.RandomOffset",
  23273. store: writable$1(0),
  23274. default: 0
  23275. },
  23276. randomOffset: {
  23277. label: "SEQUENCER.Player.Option.Randomize",
  23278. store: writable$1(false),
  23279. default: false
  23280. },
  23281. preload: {
  23282. label: "SEQUENCER.Player.Option.Preload",
  23283. store: writable$1(false),
  23284. default: false
  23285. },
  23286. moveTowards: {
  23287. label: "SEQUENCER.Player.Option.DragBehavior",
  23288. label_off: "SEQUENCER.Player.Option.DragStretch",
  23289. label_on: "SEQUENCER.Player.Option.DragMove",
  23290. store: writable$1(false),
  23291. default: false
  23292. },
  23293. moveSpeed: {
  23294. label: "SEQUENCER.Player.Option.MoveSpeed",
  23295. store: writable$1(0),
  23296. default: 0
  23297. },
  23298. attachTo: {
  23299. label: "SEQUENCER.Player.Option.AttachTo",
  23300. store: writable$1(false),
  23301. default: false,
  23302. callback: (e) => {
  23303. EffectPlayer.sourceAttach = e.target.checked;
  23304. }
  23305. },
  23306. stretchToAttach: {
  23307. label: "SEQUENCER.Player.Option.StretchToAttach",
  23308. store: writable$1(false),
  23309. default: false,
  23310. callback: (e) => {
  23311. EffectPlayer.targetAttach = e.target.checked;
  23312. }
  23313. },
  23314. persist: {
  23315. label: "SEQUENCER.Player.Option.Persist",
  23316. store: writable$1(false),
  23317. default: false
  23318. },
  23319. repeat: {
  23320. label: "SEQUENCER.Player.Option.Repeat",
  23321. store: writable$1(false),
  23322. default: false
  23323. },
  23324. repetitions: {
  23325. label: "SEQUENCER.Player.Option.Repetitions",
  23326. store: writable$1(1),
  23327. default: 1
  23328. },
  23329. repeatDelayMin: {
  23330. label: "SEQUENCER.Player.Option.DelayMin",
  23331. store: writable$1(200),
  23332. default: 200
  23333. },
  23334. repeatDelayMax: {
  23335. label: "SEQUENCER.Player.Option.DelayMax",
  23336. store: writable$1(400),
  23337. default: 400
  23338. }
  23339. };
  23340. PlayerSettings.export = () => {
  23341. return Object.fromEntries(
  23342. Object.entries(PlayerSettings).map((entry) => {
  23343. return [entry[0], get_store_value(entry[1].store)];
  23344. })
  23345. );
  23346. };
  23347. PlayerSettings.import = (settings) => {
  23348. Object.entries(PlayerSettings).forEach((entry) => {
  23349. if (settings?.[entry[0]] !== void 0) {
  23350. entry[1].store.set(settings?.[entry[0]]);
  23351. } else if (entry[1]?.default !== void 0) {
  23352. entry[1].store.set(entry[1].default);
  23353. }
  23354. });
  23355. };
  23356. PlayerSettings.getPresets = () => {
  23357. return Object.keys(game.settings.get(CONSTANTS.MODULE_NAME, "effectPresets"));
  23358. };
  23359. PlayerSettings.loadPreset = (name) => {
  23360. const effectPresets = game.settings.get(
  23361. CONSTANTS.MODULE_NAME,
  23362. "effectPresets"
  23363. );
  23364. return PlayerSettings.import(effectPresets[name]);
  23365. };
  23366. PlayerSettings.savePreset = async (name) => {
  23367. const newName = await promptNewPresetName(name);
  23368. if (!newName)
  23369. return;
  23370. const effectPresets = game.settings.get(
  23371. CONSTANTS.MODULE_NAME,
  23372. "effectPresets"
  23373. );
  23374. effectPresets[newName] = PlayerSettings.export();
  23375. return game.settings.set(
  23376. CONSTANTS.MODULE_NAME,
  23377. "effectPresets",
  23378. effectPresets
  23379. );
  23380. };
  23381. PlayerSettings.copyPreset = async (name) => {
  23382. const newName = await promptNewPresetName(name, true);
  23383. if (!newName)
  23384. return;
  23385. const effectPresets = game.settings.get(
  23386. CONSTANTS.MODULE_NAME,
  23387. "effectPresets"
  23388. );
  23389. effectPresets[newName] = PlayerSettings.export();
  23390. return game.settings.set(
  23391. CONSTANTS.MODULE_NAME,
  23392. "effectPresets",
  23393. effectPresets
  23394. );
  23395. };
  23396. PlayerSettings.deletePreset = (name) => {
  23397. const effectPresets = game.settings.get(
  23398. CONSTANTS.MODULE_NAME,
  23399. "effectPresets"
  23400. );
  23401. delete effectPresets[name];
  23402. return game.settings.set(
  23403. CONSTANTS.MODULE_NAME,
  23404. "effectPresets",
  23405. effectPresets
  23406. );
  23407. };
  23408. async function promptNewPresetName(inName, copy = false) {
  23409. const effectPresets = game.settings.get(
  23410. CONSTANTS.MODULE_NAME,
  23411. "effectPresets"
  23412. );
  23413. let title = copy ? game.i18n.localize("SEQUENCER.Player.CopyPresetTitle") : game.i18n.localize("SEQUENCER.Player.CreateNewPresetTitle");
  23414. let presetName = await new Promise((resolve) => {
  23415. new Dialog({
  23416. title,
  23417. content: `<p><input type="text" placeholder="${game.i18n.localize(
  23418. "SEQUENCER.Player.CreateNewPresetInputLabel"
  23419. )}" id="newPresetName" style="width:100%;"></p>`,
  23420. buttons: {
  23421. okay: {
  23422. icon: '<i class="fas fa-check"></i>',
  23423. label: game.i18n.localize("SEQUENCER.OK"),
  23424. callback: async (html) => {
  23425. let name = html.find("#newPresetName").val();
  23426. if (name === "" || !name) {
  23427. name = false;
  23428. }
  23429. resolve(name);
  23430. }
  23431. },
  23432. cancel: {
  23433. icon: '<i class="fas fa-times"></i>',
  23434. label: game.i18n.localize("SEQUENCER.Cancel"),
  23435. callback: () => {
  23436. resolve(false);
  23437. }
  23438. }
  23439. },
  23440. close: () => {
  23441. },
  23442. render: (html) => {
  23443. html.find("#newPresetName").val(inName).focus();
  23444. }
  23445. }).render(true);
  23446. });
  23447. if (presetName) {
  23448. if (presetName.toLowerCase() === "default") {
  23449. Dialog.prompt({
  23450. title: game.i18n.localize("SEQUENCER.Player.DefaultErrorTitle"),
  23451. content: `<p>${game.i18n.localize(
  23452. "SEQUENCER.Player.DefaultErrorContent"
  23453. )}</p>`,
  23454. label: game.i18n.localize("SEQUENCER.OK"),
  23455. callback: () => {
  23456. }
  23457. });
  23458. return false;
  23459. }
  23460. if (effectPresets[presetName]) {
  23461. const overwrite = await Dialog.confirm({
  23462. title: game.i18n.localize("SEQUENCER.Player.OverwritePresetTitle"),
  23463. content: `<p>${game.i18n.format(
  23464. "SEQUENCER.Player.OverwritePresetContent",
  23465. { preset_name: presetName }
  23466. )}</p>`
  23467. });
  23468. if (!overwrite) {
  23469. presetName = await promptPresetName(presetName);
  23470. }
  23471. }
  23472. }
  23473. return presetName;
  23474. }
  23475. PlayerSettings.migrateOldPresets = () => {
  23476. if (!game.user.isGM)
  23477. return;
  23478. const effectPresets = game.settings.get(
  23479. CONSTANTS.MODULE_NAME,
  23480. "effectPresets"
  23481. );
  23482. const presetsToUpdate = Object.values(effectPresets).filter((preset) => {
  23483. return !preset?.version;
  23484. });
  23485. if (!presetsToUpdate.length)
  23486. return;
  23487. const newEffectPresets = Object.fromEntries(
  23488. Object.entries(effectPresets).map(([name, preset]) => {
  23489. if (preset?.version)
  23490. return [name, preset];
  23491. preset.version = game.modules.get(CONSTANTS.MODULE_NAME).version;
  23492. if (preset.repetitions > 1) {
  23493. preset.repeat = true;
  23494. }
  23495. if (preset.randomMirrorY) {
  23496. preset.mirrorY = true;
  23497. }
  23498. if (preset.randomOffset) {
  23499. preset.randomOffsetAmount = 1;
  23500. preset.offsetGridUnits = true;
  23501. }
  23502. return [name, preset];
  23503. })
  23504. );
  23505. return game.settings.set(
  23506. CONSTANTS.MODULE_NAME,
  23507. "effectPresets",
  23508. newEffectPresets
  23509. );
  23510. };
  23511. const InteractionManager = {
  23512. startDragPosition: false,
  23513. state: {
  23514. LeftMouseDown: false,
  23515. RightMouseDown: false,
  23516. Dragging: false
  23517. },
  23518. get isLayerActive() {
  23519. return canvas.sequencerInterfaceLayer.active;
  23520. },
  23521. initialize() {
  23522. window.addEventListener("mousedown", (event) => {
  23523. if (!canvas.ready)
  23524. return;
  23525. if (!this.isLayerActive)
  23526. return;
  23527. const hover = document.elementFromPoint(event.clientX, event.clientY);
  23528. if (!hover || hover.id !== "board")
  23529. return;
  23530. const button = event.button;
  23531. if (!(button === 0 || button === 2))
  23532. return;
  23533. if (button === 0) {
  23534. this.state.LeftMouseDown = true;
  23535. this._propagateEvent("mouseLeftDown");
  23536. }
  23537. if (button === 2) {
  23538. this.state.RightMouseDown = true;
  23539. this._propagateEvent("mouseRightDown");
  23540. }
  23541. });
  23542. window.addEventListener("mouseup", (event) => {
  23543. if (!canvas.ready)
  23544. return;
  23545. if (!this.isLayerActive)
  23546. return;
  23547. const hover = document.elementFromPoint(event.clientX, event.clientY);
  23548. if (!hover || hover.id !== "board")
  23549. return;
  23550. if (document.activeElement.tagName !== "BODY")
  23551. return;
  23552. const button = event.button;
  23553. if (!(button === 0 || button === 2))
  23554. return;
  23555. if (button === 0) {
  23556. this.state.LeftMouseDown = false;
  23557. this._propagateEvent("mouseLeftUp");
  23558. this.state.Dragging = false;
  23559. this.startDragPosition = false;
  23560. }
  23561. if (button === 2) {
  23562. this.state.RightMouseDown = false;
  23563. this._propagateEvent("mouseRightUp");
  23564. this.state.Dragging = false;
  23565. this.startDragPosition = false;
  23566. }
  23567. });
  23568. window.addEventListener("mousemove", (event) => {
  23569. if (!canvas.ready)
  23570. return;
  23571. const hover = document.elementFromPoint(event.clientX, event.clientY);
  23572. if (!hover || hover.id !== "board")
  23573. return;
  23574. if (!this.isLayerActive)
  23575. return;
  23576. this._propagateEvent("mouseMove");
  23577. if (this.state.LeftMouseDown && !this.startDragPosition) {
  23578. this.startDragPosition = get_mouse_position();
  23579. }
  23580. if (this.state.LeftMouseDown && !this.state.Dragging) {
  23581. const distance = distance_between(
  23582. this.startDragPosition,
  23583. get_mouse_position()
  23584. );
  23585. this.state.Dragging = distance > 20;
  23586. }
  23587. });
  23588. EffectPlayer.initialize();
  23589. SelectionManager.initialize();
  23590. },
  23591. tearDown() {
  23592. EffectPlayer.tearDown();
  23593. SelectionManager.tearDown();
  23594. },
  23595. _propagateEvent(eventName) {
  23596. if (EffectPlayer.isActive && EffectPlayer[eventName]) {
  23597. EffectPlayer[eventName]();
  23598. }
  23599. if (SelectionManager.isActive && SelectionManager[eventName]) {
  23600. SelectionManager[eventName]();
  23601. }
  23602. }
  23603. };
  23604. const EffectPlayer = {
  23605. sequenceBuffer: [],
  23606. playMany: false,
  23607. playManySequenced: false,
  23608. cursorPos: false,
  23609. startPos: false,
  23610. endPos: false,
  23611. get snapLocationToGrid() {
  23612. return get_store_value(PlayerSettings.snapToGrid.store);
  23613. },
  23614. get sourceAttach() {
  23615. return get_store_value(PlayerSettings.attachTo.store);
  23616. },
  23617. get targetAttach() {
  23618. return get_store_value(PlayerSettings.stretchToAttach.store);
  23619. },
  23620. sourceAttachFound: false,
  23621. targetAttachFound: false,
  23622. get isActive() {
  23623. return InteractionManager.isLayerActive && game?.activeTool === "play-effect";
  23624. },
  23625. /**
  23626. * Opens the Sequencer Effects UI with the player tab open
  23627. */
  23628. show() {
  23629. return EffectsUIApp.show({ tab: "player" });
  23630. },
  23631. initialize() {
  23632. this.layer = canvas.sequencerInterfaceLayer;
  23633. },
  23634. tearDown() {
  23635. this._reset();
  23636. },
  23637. /**
  23638. * Mouse events
  23639. */
  23640. mouseLeftDown() {
  23641. this._evaluateStartPosition();
  23642. },
  23643. mouseLeftUp() {
  23644. if (!this.startPos)
  23645. return;
  23646. this._playEffect();
  23647. this.startPos = false;
  23648. this.endPos = false;
  23649. this.sourceAttachFound = false;
  23650. this.targetAttachFound = false;
  23651. },
  23652. mouseRightUp() {
  23653. this._reset();
  23654. },
  23655. mouseMove() {
  23656. this._evaluateCursorPosition();
  23657. if (InteractionManager.state.Dragging) {
  23658. this._evaluateEndPosition();
  23659. }
  23660. },
  23661. /**
  23662. * Hotkeys
  23663. */
  23664. playManyUp() {
  23665. this._playEffects();
  23666. this._reset();
  23667. },
  23668. /**
  23669. * Private methods
  23670. */
  23671. _evaluatePosition(attach = false) {
  23672. let position = get_mouse_position(this.snapLocationToGrid);
  23673. const attachToObject = attach ? get_closest_token(position, {
  23674. minimumDistance: canvas.grid.size
  23675. }) : false;
  23676. let attachFound = false;
  23677. if (attachToObject) {
  23678. attachFound = true;
  23679. position = get_object_position(attachToObject);
  23680. }
  23681. return [position, attachFound];
  23682. },
  23683. _evaluateCursorPosition() {
  23684. const attach = InteractionManager.state.Dragging ? this.targetAttach : this.sourceAttach;
  23685. [this.cursorPos] = this._evaluatePosition(attach);
  23686. },
  23687. _evaluateStartPosition() {
  23688. if (this.startPos)
  23689. return;
  23690. [this.startPos, this.sourceAttachFound] = this._evaluatePosition(
  23691. this.sourceAttach
  23692. );
  23693. },
  23694. _evaluateEndPosition() {
  23695. [this.endPos, this.targetAttachFound] = this._evaluatePosition(
  23696. this.targetAttach
  23697. );
  23698. },
  23699. _reset() {
  23700. if (!this.layer)
  23701. return;
  23702. this.startPos = false;
  23703. this.endPos = false;
  23704. this.sourceAttachFound = false;
  23705. this.targetAttachFound = false;
  23706. this.sequenceBuffer = [];
  23707. this._evaluateCursorPosition();
  23708. },
  23709. async _playEffect() {
  23710. const settings = foundry.utils.mergeObject(PlayerSettings.export(), {
  23711. ...InteractionManager.state,
  23712. startPos: this.startPos,
  23713. endPos: this.endPos
  23714. });
  23715. if (!settings.users.length || settings.users?.[0] === "all")
  23716. settings.users = [];
  23717. if (settings.file === "")
  23718. return;
  23719. if (!(Sequencer.Database.entryExists(settings.file) || await SequencerFileCache.srcExists(settings.file))) {
  23720. throw custom_error(
  23721. "Sequencer",
  23722. `Sequencer Player | Could not find file or database entry: ${settings.file}`
  23723. );
  23724. }
  23725. if (settings.preload) {
  23726. await Sequencer.Preloader.preloadForClients(settings.file);
  23727. }
  23728. const sequence = this.sequenceBuffer.length > 0 && this.playManySequenced ? this.sequenceBuffer[this.sequenceBuffer.length - 1] : new Sequence();
  23729. 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);
  23730. if (settings.repeat) {
  23731. effect.repeats(
  23732. settings.repetitions,
  23733. settings.repeatDelayMin,
  23734. settings.repeatDelayMax
  23735. );
  23736. }
  23737. if (settings.fadeIn > 0)
  23738. effect.fadeIn(settings.fadeIn);
  23739. if (settings.fadeOut > 0)
  23740. effect.fadeOut(settings.fadeOut);
  23741. const offsetData = settings.randomOffset ? {
  23742. offset: { x: settings.offsetX, y: settings.offsetY },
  23743. gridUnits: settings.offsetGridUnits
  23744. } : {};
  23745. const offsetSource = !settings.Dragging ? offsetData : {};
  23746. const attachToObject = settings.attachTo ? get_closest_token(settings.startPos, {
  23747. minimumDistance: canvas.grid.size
  23748. }) : false;
  23749. if (attachToObject) {
  23750. effect.attachTo(attachToObject, {
  23751. randomOffset: settings.randomOffset && !settings.Dragging ? settings.randomOffsetAmount : false,
  23752. ...offsetSource
  23753. });
  23754. } else {
  23755. effect.atLocation(settings.startPos, {
  23756. randomOffset: settings.randomOffset && !settings.Dragging ? settings.randomOffsetAmount : false,
  23757. ...offsetSource
  23758. });
  23759. }
  23760. if (settings.persist && settings.name && settings.name !== "" && settings.name !== "default" && settings.name !== "new") {
  23761. effect.name("Preset: " + settings.name);
  23762. }
  23763. if (settings.Dragging) {
  23764. if (settings.moveTowards) {
  23765. effect.moveTowards(settings.endPos, {
  23766. randomOffset: settings.randomOffset ? settings.randomOffsetAmount : false,
  23767. ...offsetData
  23768. });
  23769. if (settings.moveSpeed) {
  23770. effect.moveSpeed(settings.moveSpeed);
  23771. }
  23772. } else {
  23773. let target = settings.stretchToAttach ? get_closest_token(settings.endPos, {
  23774. minimumDistance: canvas.grid.size
  23775. }) : settings.endPos;
  23776. effect.stretchTo(target, {
  23777. attachTo: settings.stretchToAttach,
  23778. randomOffset: settings.randomOffset ? settings.randomOffsetAmount : false,
  23779. ...offsetData
  23780. });
  23781. }
  23782. }
  23783. if (!settings.Dragging || settings.Dragging && settings.moveTowards) {
  23784. effect.scale(settings.scale);
  23785. if (settings.scaleIn > 0)
  23786. effect.scaleIn(0, settings.scaleIn, { ease: "easeInOutSine" });
  23787. if (settings.scaleOut > 0)
  23788. effect.scaleOut(0, settings.scaleOut, { ease: "easeInOutSine" });
  23789. effect.randomRotation(settings.randomRotation);
  23790. }
  23791. if (this.playManySequenced) {
  23792. effect.waitUntilFinished();
  23793. }
  23794. if (!this.playManySequenced || this.sequenceBuffer.length === 0) {
  23795. this.sequenceBuffer.push(sequence);
  23796. }
  23797. if (!this.playMany && !this.playManySequenced)
  23798. this._playEffects();
  23799. },
  23800. _playEffects() {
  23801. this.sequenceBuffer.forEach((sequence) => sequence.play());
  23802. this.sequenceBuffer = [];
  23803. }
  23804. };
  23805. const SelectionManager = {
  23806. selectedEffect: false,
  23807. hoveredEffects: /* @__PURE__ */ new Set(),
  23808. suggestedProperties: false,
  23809. sourceOrTarget: false,
  23810. dragOffset: false,
  23811. hoveredEffectUI: false,
  23812. get snapToGrid() {
  23813. return get_store_value(PlayerSettings.snapToGrid.store);
  23814. },
  23815. set snapToGrid(bool) {
  23816. PlayerSettings.snapToGrid.store.set(bool);
  23817. },
  23818. set attachToTarget(bool) {
  23819. PlayerSettings.stretchToAttach.store.set(bool);
  23820. },
  23821. get attachToTarget() {
  23822. return get_store_value(PlayerSettings.stretchToAttach.store);
  23823. },
  23824. get isActive() {
  23825. return InteractionManager.isLayerActive && game?.activeTool === "select-effect";
  23826. },
  23827. get effects() {
  23828. return SequencerEffectManager.effects.filter(
  23829. (effect) => effect.userCanDelete
  23830. );
  23831. },
  23832. initialize() {
  23833. this.layer = canvas.sequencerInterfaceLayer;
  23834. },
  23835. tearDown() {
  23836. this._reset();
  23837. this.hoveredEffects = /* @__PURE__ */ new Set();
  23838. },
  23839. sourcePointSelected() {
  23840. this.sourceOrTarget = "source";
  23841. },
  23842. targetPointSelected() {
  23843. this.sourceOrTarget = "target";
  23844. },
  23845. /**
  23846. * Mouse Events
  23847. */
  23848. mouseLeftDown() {
  23849. if (!this.selectedEffect) {
  23850. return this._selectEffects();
  23851. }
  23852. if (!this.hoveredEffects.size) {
  23853. this._reset();
  23854. }
  23855. },
  23856. mouseRightDown() {
  23857. },
  23858. mouseLeftUp() {
  23859. if (!InteractionManager.state.Dragging) {
  23860. return this._selectEffects();
  23861. }
  23862. if (!InteractionManager.state.Dragging || !this.selectedEffect || !this.suggestedProperties)
  23863. return;
  23864. this._updateEffect();
  23865. },
  23866. mouseRightUp() {
  23867. InteractionManager.state.LeftMouseDown = false;
  23868. this.suggestedProperties = false;
  23869. this.sourceOrTarget = false;
  23870. this.dragOffset = false;
  23871. },
  23872. mouseMove() {
  23873. this._evaluateHoveredEffects();
  23874. if (InteractionManager.state.LeftMouseDown && !InteractionManager.state.RightMouseDown) {
  23875. this._evaluateEffectPositionUpdate();
  23876. }
  23877. },
  23878. /**
  23879. * Hotkeys
  23880. */
  23881. async delete() {
  23882. if (!this.selectedEffect)
  23883. return;
  23884. await SequencerEffectManager.endEffects({ effects: this.selectedEffect });
  23885. this.selectedEffect = false;
  23886. },
  23887. attachToTargetDown() {
  23888. if (InteractionManager.state.LeftMouseDown && !InteractionManager.state.RightMouseDown) {
  23889. this._evaluateEffectPositionUpdate();
  23890. }
  23891. },
  23892. /**
  23893. * Private methods
  23894. */
  23895. _selectEffects() {
  23896. this._reset();
  23897. if (!this.hoveredEffects.size)
  23898. return;
  23899. const firstElement = Array.from(this.hoveredEffects)[0];
  23900. this.selectedEffect = !firstElement.selected ? firstElement : false;
  23901. },
  23902. _evaluateHoveredEffects() {
  23903. const position = get_mouse_position();
  23904. this.hoveredEffects = this.effects.filter(
  23905. (effect) => effect.isPositionWithinBounds(position)
  23906. );
  23907. this.hoveredEffects.sort((a, b) => {
  23908. return a.data.layer !== b.data.zIndex ? a.data.zIndex - b.data.zIndex : a.data.layer - b.data.zIndex;
  23909. });
  23910. this.hoveredEffects = new Set(this.hoveredEffects);
  23911. },
  23912. _evaluateEffectPositionUpdate() {
  23913. if (!this.selectedEffect)
  23914. return;
  23915. if (this.selectedEffect.data.stretchTo && !this.sourceOrTarget) {
  23916. return;
  23917. }
  23918. let showCursor = false;
  23919. let showPoint = this.snapToGrid;
  23920. let position = get_mouse_position(this.snapToGrid);
  23921. if (!this.selectedEffect.data.stretchTo && !this.dragOffset) {
  23922. this.dragOffset = {
  23923. x: position.x - this.selectedEffect.position.x,
  23924. y: position.y - this.selectedEffect.position.y
  23925. };
  23926. }
  23927. if (this.attachToTarget) {
  23928. const obj = get_closest_token(position, {
  23929. minimumDistance: canvas.grid.size
  23930. });
  23931. if (obj) {
  23932. position = get_object_position(obj);
  23933. showCursor = true;
  23934. showPoint = false;
  23935. }
  23936. }
  23937. if (this.dragOffset && !showCursor && !this.snapToGrid) {
  23938. position.x -= this.dragOffset.x;
  23939. position.y -= this.dragOffset.y;
  23940. }
  23941. const color = (this.sourceOrTarget || "source") === "source" ? CONSTANTS.COLOR.PRIMARY : CONSTANTS.COLOR.SECONDARY;
  23942. this.suggestedProperties = {
  23943. position,
  23944. showCursor,
  23945. showPoint,
  23946. color
  23947. };
  23948. },
  23949. _updateEffect() {
  23950. if (!this.selectedEffect)
  23951. return;
  23952. const updates = {
  23953. attachTo: this.selectedEffect.data.attachTo,
  23954. stretchTo: this.selectedEffect.data.stretchTo
  23955. };
  23956. const obj = this.attachToTarget ? get_closest_token(this.suggestedProperties.position, {
  23957. minimumDistance: canvas.grid.size,
  23958. type: TokenDocument
  23959. }) : false;
  23960. let objUuid = obj ? get_object_identifier(obj) : false;
  23961. if (this.sourceOrTarget === "source") {
  23962. if (!updates.attachTo) {
  23963. updates.attachTo = {
  23964. active: false,
  23965. align: "center",
  23966. rotation: true,
  23967. bindVisibility: true,
  23968. bindAlpha: true
  23969. };
  23970. }
  23971. updates.attachTo = foundry.utils.mergeObject(updates.attachTo || {}, {
  23972. active: this.attachToTarget && !!objUuid
  23973. });
  23974. if (this.attachToTarget && objUuid) {
  23975. updates.source = objUuid;
  23976. } else {
  23977. updates["source"] = this.suggestedProperties.position;
  23978. }
  23979. } else if (this.sourceOrTarget === "target") {
  23980. updates.stretchTo.attachTo = this.attachToTarget && !!objUuid;
  23981. if (this.attachToTarget && objUuid) {
  23982. updates.target = objUuid;
  23983. } else {
  23984. updates["target"] = this.suggestedProperties.position;
  23985. }
  23986. } else {
  23987. updates["source"] = this.suggestedProperties.position;
  23988. }
  23989. this.selectedEffect.update(updates);
  23990. this.suggestedProperties = false;
  23991. this.sourceOrTarget = false;
  23992. this.dragOffset = false;
  23993. },
  23994. _reset() {
  23995. this.selectedEffect = false;
  23996. this.suggestedProperties = false;
  23997. this.sourceOrTarget = false;
  23998. this.dragOffset = false;
  23999. }
  24000. };
  24001. const EffectEntry_svelte_svelte_type_style_lang = "";
  24002. function create_fragment$c(ctx) {
  24003. let div1;
  24004. let button;
  24005. let t0;
  24006. let div0;
  24007. let t1_value = (
  24008. /*getEffectName*/
  24009. ctx[1](
  24010. /*effect*/
  24011. ctx[0]
  24012. ) + ""
  24013. );
  24014. let t1;
  24015. let mounted;
  24016. let dispose;
  24017. return {
  24018. c() {
  24019. div1 = element("div");
  24020. button = element("button");
  24021. button.innerHTML = `<i class="fas fa-times"></i>`;
  24022. t0 = space();
  24023. div0 = element("div");
  24024. t1 = text$1(t1_value);
  24025. attr(button, "class", "btn_end svelte-ese-1fyjvdk");
  24026. attr(button, "type", "button");
  24027. attr(div0, "class", "effect-text hover-text svelte-ese-1fyjvdk");
  24028. attr(div1, "class", "effect hover-highlight svelte-ese-1fyjvdk");
  24029. },
  24030. m(target, anchor) {
  24031. insert(target, div1, anchor);
  24032. append(div1, button);
  24033. append(div1, t0);
  24034. append(div1, div0);
  24035. append(div0, t1);
  24036. if (!mounted) {
  24037. dispose = [
  24038. listen(
  24039. button,
  24040. "click",
  24041. /*endEffect*/
  24042. ctx[4]
  24043. ),
  24044. listen(
  24045. div1,
  24046. "mouseleave",
  24047. /*mouseLeave*/
  24048. ctx[3]
  24049. ),
  24050. listen(
  24051. div1,
  24052. "mouseover",
  24053. /*mouseOver*/
  24054. ctx[2]
  24055. )
  24056. ];
  24057. mounted = true;
  24058. }
  24059. },
  24060. p(ctx2, [dirty]) {
  24061. if (dirty & /*effect*/
  24062. 1 && t1_value !== (t1_value = /*getEffectName*/
  24063. ctx2[1](
  24064. /*effect*/
  24065. ctx2[0]
  24066. ) + ""))
  24067. set_data(t1, t1_value);
  24068. },
  24069. i: noop,
  24070. o: noop,
  24071. d(detaching) {
  24072. if (detaching)
  24073. detach(div1);
  24074. mounted = false;
  24075. run_all(dispose);
  24076. }
  24077. };
  24078. }
  24079. function instance$c($$self, $$props, $$invalidate) {
  24080. let { effect } = $$props;
  24081. function getEffectName(effect2) {
  24082. let effectName = "Unknown effect";
  24083. if (effect2.data.file) {
  24084. effectName = effect2.data.file.split("\\").pop().split("/").pop();
  24085. } else if (effect2.data.text) {
  24086. effectName = "Text: " + effect2.data.text.text;
  24087. } else {
  24088. effectName = "Shape: " + effect2.data.shapes[0].type;
  24089. }
  24090. effectName = effect2.data.name ? `${effect2.data.name} (${effectName})` : effectName;
  24091. if (effect2.data.creatorUserId !== game.userId) {
  24092. let user_name = game.users.get(effect2.data.creatorUserId)?.name;
  24093. let formattedUsername = user_name ? localize("SEQUENCER.ManagerPlayersEffect", { user_name }) : localize("SEQUENCER.ManagerUnknownEffect");
  24094. effectName += ` (${formattedUsername})`;
  24095. }
  24096. return effectName;
  24097. }
  24098. function mouseOver() {
  24099. SelectionManager.hoveredEffectUI = effect;
  24100. }
  24101. function mouseLeave() {
  24102. SelectionManager.hoveredEffectUI = false;
  24103. }
  24104. function endEffect() {
  24105. SequencerEffectManager.endEffects({ effects: [effect] });
  24106. }
  24107. $$self.$$set = ($$props2) => {
  24108. if ("effect" in $$props2)
  24109. $$invalidate(0, effect = $$props2.effect);
  24110. };
  24111. return [effect, getEffectName, mouseOver, mouseLeave, endEffect];
  24112. }
  24113. class EffectEntry extends SvelteComponent {
  24114. constructor(options) {
  24115. super();
  24116. init(this, options, instance$c, create_fragment$c, safe_not_equal, { effect: 0 });
  24117. }
  24118. }
  24119. const SoundEntry_svelte_svelte_type_style_lang = "";
  24120. function create_fragment$b(ctx) {
  24121. let div1;
  24122. let button;
  24123. let t0;
  24124. let div0;
  24125. let t1_value = (
  24126. /*sound*/
  24127. ctx[0].src.split("/").slice(-1) + ""
  24128. );
  24129. let t1;
  24130. let mounted;
  24131. let dispose;
  24132. return {
  24133. c() {
  24134. div1 = element("div");
  24135. button = element("button");
  24136. button.innerHTML = `<i class="fas fa-times"></i>`;
  24137. t0 = space();
  24138. div0 = element("div");
  24139. t1 = text$1(t1_value);
  24140. attr(button, "class", "btn_end svelte-ese-1fyjvdk");
  24141. attr(button, "type", "button");
  24142. attr(div0, "class", "effect-text hover-text svelte-ese-1fyjvdk");
  24143. attr(div1, "class", "effect hover-highlight svelte-ese-1fyjvdk");
  24144. },
  24145. m(target, anchor) {
  24146. insert(target, div1, anchor);
  24147. append(div1, button);
  24148. append(div1, t0);
  24149. append(div1, div0);
  24150. append(div0, t1);
  24151. if (!mounted) {
  24152. dispose = listen(
  24153. button,
  24154. "click",
  24155. /*endSound*/
  24156. ctx[1]
  24157. );
  24158. mounted = true;
  24159. }
  24160. },
  24161. p(ctx2, [dirty]) {
  24162. if (dirty & /*sound*/
  24163. 1 && t1_value !== (t1_value = /*sound*/
  24164. ctx2[0].src.split("/").slice(-1) + ""))
  24165. set_data(t1, t1_value);
  24166. },
  24167. i: noop,
  24168. o: noop,
  24169. d(detaching) {
  24170. if (detaching)
  24171. detach(div1);
  24172. mounted = false;
  24173. dispose();
  24174. }
  24175. };
  24176. }
  24177. function instance$b($$self, $$props, $$invalidate) {
  24178. let { id } = $$props;
  24179. let { sound } = $$props;
  24180. function endSound() {
  24181. SequencerAudioHelper.stop([id], true);
  24182. }
  24183. $$self.$$set = ($$props2) => {
  24184. if ("id" in $$props2)
  24185. $$invalidate(2, id = $$props2.id);
  24186. if ("sound" in $$props2)
  24187. $$invalidate(0, sound = $$props2.sound);
  24188. };
  24189. return [sound, endSound, id];
  24190. }
  24191. class SoundEntry extends SvelteComponent {
  24192. constructor(options) {
  24193. super();
  24194. init(this, options, instance$b, create_fragment$b, safe_not_equal, { id: 2, sound: 0 });
  24195. }
  24196. }
  24197. const Manager_svelte_svelte_type_style_lang = "";
  24198. function get_each_context$3(ctx, list, i) {
  24199. const child_ctx = ctx.slice();
  24200. child_ctx[10] = list[i][0];
  24201. child_ctx[11] = list[i][1];
  24202. return child_ctx;
  24203. }
  24204. function get_each_context_1$1(ctx, list, i) {
  24205. const child_ctx = ctx.slice();
  24206. child_ctx[14] = list[i];
  24207. return child_ctx;
  24208. }
  24209. function get_each_context_2$1(ctx, list, i) {
  24210. const child_ctx = ctx.slice();
  24211. child_ctx[14] = list[i];
  24212. return child_ctx;
  24213. }
  24214. function create_if_block_5(ctx) {
  24215. let div;
  24216. let h2;
  24217. return {
  24218. c() {
  24219. div = element("div");
  24220. h2 = element("h2");
  24221. h2.textContent = `${localize("SEQUENCER.Manager.NothingPlaying")}`;
  24222. attr(div, "class", "no-effects");
  24223. },
  24224. m(target, anchor) {
  24225. insert(target, div, anchor);
  24226. append(div, h2);
  24227. },
  24228. p: noop,
  24229. d(detaching) {
  24230. if (detaching)
  24231. detach(div);
  24232. }
  24233. };
  24234. }
  24235. function create_if_block_1(ctx) {
  24236. let button;
  24237. let t1;
  24238. let div;
  24239. let t2;
  24240. let t3;
  24241. let current;
  24242. let mounted;
  24243. let dispose;
  24244. let if_block0 = (
  24245. /*persistentEffects*/
  24246. ctx[2].length && create_if_block_4(ctx)
  24247. );
  24248. let if_block1 = (
  24249. /*temporaryEffects*/
  24250. ctx[1].length && /*persistentEffects*/
  24251. ctx[2].length && create_if_block_3()
  24252. );
  24253. let if_block2 = (
  24254. /*temporaryEffects*/
  24255. ctx[1].length && create_if_block_2(ctx)
  24256. );
  24257. return {
  24258. c() {
  24259. button = element("button");
  24260. button.textContent = `${localize("SEQUENCER.Manager.EndAllEffects")}`;
  24261. t1 = space();
  24262. div = element("div");
  24263. if (if_block0)
  24264. if_block0.c();
  24265. t2 = space();
  24266. if (if_block1)
  24267. if_block1.c();
  24268. t3 = space();
  24269. if (if_block2)
  24270. if_block2.c();
  24271. attr(button, "class", "w-100 end-all-effects mb-2 svelte-ese-nc5j73");
  24272. attr(button, "type", "button");
  24273. attr(div, "class", "effects svelte-ese-nc5j73");
  24274. },
  24275. m(target, anchor) {
  24276. insert(target, button, anchor);
  24277. insert(target, t1, anchor);
  24278. insert(target, div, anchor);
  24279. if (if_block0)
  24280. if_block0.m(div, null);
  24281. append(div, t2);
  24282. if (if_block1)
  24283. if_block1.m(div, null);
  24284. append(div, t3);
  24285. if (if_block2)
  24286. if_block2.m(div, null);
  24287. current = true;
  24288. if (!mounted) {
  24289. dispose = listen(
  24290. button,
  24291. "click",
  24292. /*endAllEffects*/
  24293. ctx[6]
  24294. );
  24295. mounted = true;
  24296. }
  24297. },
  24298. p(ctx2, dirty) {
  24299. if (
  24300. /*persistentEffects*/
  24301. ctx2[2].length
  24302. ) {
  24303. if (if_block0) {
  24304. if_block0.p(ctx2, dirty);
  24305. if (dirty & /*persistentEffects*/
  24306. 4) {
  24307. transition_in(if_block0, 1);
  24308. }
  24309. } else {
  24310. if_block0 = create_if_block_4(ctx2);
  24311. if_block0.c();
  24312. transition_in(if_block0, 1);
  24313. if_block0.m(div, t2);
  24314. }
  24315. } else if (if_block0) {
  24316. group_outros();
  24317. transition_out(if_block0, 1, 1, () => {
  24318. if_block0 = null;
  24319. });
  24320. check_outros();
  24321. }
  24322. if (
  24323. /*temporaryEffects*/
  24324. ctx2[1].length && /*persistentEffects*/
  24325. ctx2[2].length
  24326. ) {
  24327. if (if_block1)
  24328. ;
  24329. else {
  24330. if_block1 = create_if_block_3();
  24331. if_block1.c();
  24332. if_block1.m(div, t3);
  24333. }
  24334. } else if (if_block1) {
  24335. if_block1.d(1);
  24336. if_block1 = null;
  24337. }
  24338. if (
  24339. /*temporaryEffects*/
  24340. ctx2[1].length
  24341. ) {
  24342. if (if_block2) {
  24343. if_block2.p(ctx2, dirty);
  24344. if (dirty & /*temporaryEffects*/
  24345. 2) {
  24346. transition_in(if_block2, 1);
  24347. }
  24348. } else {
  24349. if_block2 = create_if_block_2(ctx2);
  24350. if_block2.c();
  24351. transition_in(if_block2, 1);
  24352. if_block2.m(div, null);
  24353. }
  24354. } else if (if_block2) {
  24355. group_outros();
  24356. transition_out(if_block2, 1, 1, () => {
  24357. if_block2 = null;
  24358. });
  24359. check_outros();
  24360. }
  24361. },
  24362. i(local) {
  24363. if (current)
  24364. return;
  24365. transition_in(if_block0);
  24366. transition_in(if_block2);
  24367. current = true;
  24368. },
  24369. o(local) {
  24370. transition_out(if_block0);
  24371. transition_out(if_block2);
  24372. current = false;
  24373. },
  24374. d(detaching) {
  24375. if (detaching)
  24376. detach(button);
  24377. if (detaching)
  24378. detach(t1);
  24379. if (detaching)
  24380. detach(div);
  24381. if (if_block0)
  24382. if_block0.d();
  24383. if (if_block1)
  24384. if_block1.d();
  24385. if (if_block2)
  24386. if_block2.d();
  24387. mounted = false;
  24388. dispose();
  24389. }
  24390. };
  24391. }
  24392. function create_if_block_4(ctx) {
  24393. let h2;
  24394. let t1;
  24395. let div;
  24396. let each_blocks = [];
  24397. let each_1_lookup = /* @__PURE__ */ new Map();
  24398. let current;
  24399. let each_value_2 = (
  24400. /*persistentEffects*/
  24401. ctx[2]
  24402. );
  24403. const get_key = (ctx2) => (
  24404. /*effect*/
  24405. ctx2[14].id
  24406. );
  24407. for (let i = 0; i < each_value_2.length; i += 1) {
  24408. let child_ctx = get_each_context_2$1(ctx, each_value_2, i);
  24409. let key = get_key(child_ctx);
  24410. each_1_lookup.set(key, each_blocks[i] = create_each_block_2$1(key, child_ctx));
  24411. }
  24412. return {
  24413. c() {
  24414. h2 = element("h2");
  24415. h2.textContent = `${localize("SEQUENCER.Manager.PersistentEffects")}`;
  24416. t1 = space();
  24417. div = element("div");
  24418. for (let i = 0; i < each_blocks.length; i += 1) {
  24419. each_blocks[i].c();
  24420. }
  24421. },
  24422. m(target, anchor) {
  24423. insert(target, h2, anchor);
  24424. insert(target, t1, anchor);
  24425. insert(target, div, anchor);
  24426. for (let i = 0; i < each_blocks.length; i += 1) {
  24427. each_blocks[i].m(div, null);
  24428. }
  24429. current = true;
  24430. },
  24431. p(ctx2, dirty) {
  24432. if (dirty & /*persistentEffects*/
  24433. 4) {
  24434. each_value_2 = /*persistentEffects*/
  24435. ctx2[2];
  24436. group_outros();
  24437. 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);
  24438. check_outros();
  24439. }
  24440. },
  24441. i(local) {
  24442. if (current)
  24443. return;
  24444. for (let i = 0; i < each_value_2.length; i += 1) {
  24445. transition_in(each_blocks[i]);
  24446. }
  24447. current = true;
  24448. },
  24449. o(local) {
  24450. for (let i = 0; i < each_blocks.length; i += 1) {
  24451. transition_out(each_blocks[i]);
  24452. }
  24453. current = false;
  24454. },
  24455. d(detaching) {
  24456. if (detaching)
  24457. detach(h2);
  24458. if (detaching)
  24459. detach(t1);
  24460. if (detaching)
  24461. detach(div);
  24462. for (let i = 0; i < each_blocks.length; i += 1) {
  24463. each_blocks[i].d();
  24464. }
  24465. }
  24466. };
  24467. }
  24468. function create_each_block_2$1(key_1, ctx) {
  24469. let first;
  24470. let effectentry;
  24471. let current;
  24472. effectentry = new EffectEntry({ props: { effect: (
  24473. /*effect*/
  24474. ctx[14]
  24475. ) } });
  24476. return {
  24477. key: key_1,
  24478. first: null,
  24479. c() {
  24480. first = empty();
  24481. create_component(effectentry.$$.fragment);
  24482. this.first = first;
  24483. },
  24484. m(target, anchor) {
  24485. insert(target, first, anchor);
  24486. mount_component(effectentry, target, anchor);
  24487. current = true;
  24488. },
  24489. p(new_ctx, dirty) {
  24490. ctx = new_ctx;
  24491. const effectentry_changes = {};
  24492. if (dirty & /*persistentEffects*/
  24493. 4)
  24494. effectentry_changes.effect = /*effect*/
  24495. ctx[14];
  24496. effectentry.$set(effectentry_changes);
  24497. },
  24498. i(local) {
  24499. if (current)
  24500. return;
  24501. transition_in(effectentry.$$.fragment, local);
  24502. current = true;
  24503. },
  24504. o(local) {
  24505. transition_out(effectentry.$$.fragment, local);
  24506. current = false;
  24507. },
  24508. d(detaching) {
  24509. if (detaching)
  24510. detach(first);
  24511. destroy_component(effectentry, detaching);
  24512. }
  24513. };
  24514. }
  24515. function create_if_block_3(ctx) {
  24516. let hr;
  24517. return {
  24518. c() {
  24519. hr = element("hr");
  24520. },
  24521. m(target, anchor) {
  24522. insert(target, hr, anchor);
  24523. },
  24524. d(detaching) {
  24525. if (detaching)
  24526. detach(hr);
  24527. }
  24528. };
  24529. }
  24530. function create_if_block_2(ctx) {
  24531. let h2;
  24532. let t1;
  24533. let div;
  24534. let each_blocks = [];
  24535. let each_1_lookup = /* @__PURE__ */ new Map();
  24536. let current;
  24537. let each_value_1 = (
  24538. /*temporaryEffects*/
  24539. ctx[1]
  24540. );
  24541. const get_key = (ctx2) => (
  24542. /*effect*/
  24543. ctx2[14].id
  24544. );
  24545. for (let i = 0; i < each_value_1.length; i += 1) {
  24546. let child_ctx = get_each_context_1$1(ctx, each_value_1, i);
  24547. let key = get_key(child_ctx);
  24548. each_1_lookup.set(key, each_blocks[i] = create_each_block_1$1(key, child_ctx));
  24549. }
  24550. return {
  24551. c() {
  24552. h2 = element("h2");
  24553. h2.textContent = `${localize("SEQUENCER.Manager.TemporaryEffects")}`;
  24554. t1 = space();
  24555. div = element("div");
  24556. for (let i = 0; i < each_blocks.length; i += 1) {
  24557. each_blocks[i].c();
  24558. }
  24559. },
  24560. m(target, anchor) {
  24561. insert(target, h2, anchor);
  24562. insert(target, t1, anchor);
  24563. insert(target, div, anchor);
  24564. for (let i = 0; i < each_blocks.length; i += 1) {
  24565. each_blocks[i].m(div, null);
  24566. }
  24567. current = true;
  24568. },
  24569. p(ctx2, dirty) {
  24570. if (dirty & /*temporaryEffects*/
  24571. 2) {
  24572. each_value_1 = /*temporaryEffects*/
  24573. ctx2[1];
  24574. group_outros();
  24575. 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);
  24576. check_outros();
  24577. }
  24578. },
  24579. i(local) {
  24580. if (current)
  24581. return;
  24582. for (let i = 0; i < each_value_1.length; i += 1) {
  24583. transition_in(each_blocks[i]);
  24584. }
  24585. current = true;
  24586. },
  24587. o(local) {
  24588. for (let i = 0; i < each_blocks.length; i += 1) {
  24589. transition_out(each_blocks[i]);
  24590. }
  24591. current = false;
  24592. },
  24593. d(detaching) {
  24594. if (detaching)
  24595. detach(h2);
  24596. if (detaching)
  24597. detach(t1);
  24598. if (detaching)
  24599. detach(div);
  24600. for (let i = 0; i < each_blocks.length; i += 1) {
  24601. each_blocks[i].d();
  24602. }
  24603. }
  24604. };
  24605. }
  24606. function create_each_block_1$1(key_1, ctx) {
  24607. let first;
  24608. let effectentry;
  24609. let current;
  24610. effectentry = new EffectEntry({ props: { effect: (
  24611. /*effect*/
  24612. ctx[14]
  24613. ) } });
  24614. return {
  24615. key: key_1,
  24616. first: null,
  24617. c() {
  24618. first = empty();
  24619. create_component(effectentry.$$.fragment);
  24620. this.first = first;
  24621. },
  24622. m(target, anchor) {
  24623. insert(target, first, anchor);
  24624. mount_component(effectentry, target, anchor);
  24625. current = true;
  24626. },
  24627. p(new_ctx, dirty) {
  24628. ctx = new_ctx;
  24629. const effectentry_changes = {};
  24630. if (dirty & /*temporaryEffects*/
  24631. 2)
  24632. effectentry_changes.effect = /*effect*/
  24633. ctx[14];
  24634. effectentry.$set(effectentry_changes);
  24635. },
  24636. i(local) {
  24637. if (current)
  24638. return;
  24639. transition_in(effectentry.$$.fragment, local);
  24640. current = true;
  24641. },
  24642. o(local) {
  24643. transition_out(effectentry.$$.fragment, local);
  24644. current = false;
  24645. },
  24646. d(detaching) {
  24647. if (detaching)
  24648. detach(first);
  24649. destroy_component(effectentry, detaching);
  24650. }
  24651. };
  24652. }
  24653. function create_if_block$2(ctx) {
  24654. let button;
  24655. let t1;
  24656. let div1;
  24657. let h2;
  24658. let t3;
  24659. let div0;
  24660. let each_blocks = [];
  24661. let each_1_lookup = /* @__PURE__ */ new Map();
  24662. let current;
  24663. let mounted;
  24664. let dispose;
  24665. let each_value = (
  24666. /*sounds*/
  24667. ctx[3]
  24668. );
  24669. const get_key = (ctx2) => (
  24670. /*id*/
  24671. ctx2[10]
  24672. );
  24673. for (let i = 0; i < each_value.length; i += 1) {
  24674. let child_ctx = get_each_context$3(ctx, each_value, i);
  24675. let key = get_key(child_ctx);
  24676. each_1_lookup.set(key, each_blocks[i] = create_each_block$3(key, child_ctx));
  24677. }
  24678. return {
  24679. c() {
  24680. button = element("button");
  24681. button.textContent = `${localize("SEQUENCER.Manager.EndAllSounds")}`;
  24682. t1 = space();
  24683. div1 = element("div");
  24684. h2 = element("h2");
  24685. h2.textContent = `${localize("SEQUENCER.Manager.Sounds")}`;
  24686. t3 = space();
  24687. div0 = element("div");
  24688. for (let i = 0; i < each_blocks.length; i += 1) {
  24689. each_blocks[i].c();
  24690. }
  24691. attr(button, "class", "w-100 end-all-effects mb-2 svelte-ese-nc5j73");
  24692. attr(button, "type", "button");
  24693. },
  24694. m(target, anchor) {
  24695. insert(target, button, anchor);
  24696. insert(target, t1, anchor);
  24697. insert(target, div1, anchor);
  24698. append(div1, h2);
  24699. append(div1, t3);
  24700. append(div1, div0);
  24701. for (let i = 0; i < each_blocks.length; i += 1) {
  24702. each_blocks[i].m(div0, null);
  24703. }
  24704. current = true;
  24705. if (!mounted) {
  24706. dispose = listen(
  24707. button,
  24708. "click",
  24709. /*endAllSounds*/
  24710. ctx[7]
  24711. );
  24712. mounted = true;
  24713. }
  24714. },
  24715. p(ctx2, dirty) {
  24716. if (dirty & /*sounds*/
  24717. 8) {
  24718. each_value = /*sounds*/
  24719. ctx2[3];
  24720. group_outros();
  24721. 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);
  24722. check_outros();
  24723. }
  24724. },
  24725. i(local) {
  24726. if (current)
  24727. return;
  24728. for (let i = 0; i < each_value.length; i += 1) {
  24729. transition_in(each_blocks[i]);
  24730. }
  24731. current = true;
  24732. },
  24733. o(local) {
  24734. for (let i = 0; i < each_blocks.length; i += 1) {
  24735. transition_out(each_blocks[i]);
  24736. }
  24737. current = false;
  24738. },
  24739. d(detaching) {
  24740. if (detaching)
  24741. detach(button);
  24742. if (detaching)
  24743. detach(t1);
  24744. if (detaching)
  24745. detach(div1);
  24746. for (let i = 0; i < each_blocks.length; i += 1) {
  24747. each_blocks[i].d();
  24748. }
  24749. mounted = false;
  24750. dispose();
  24751. }
  24752. };
  24753. }
  24754. function create_each_block$3(key_1, ctx) {
  24755. let first;
  24756. let soundentry;
  24757. let current;
  24758. soundentry = new SoundEntry({
  24759. props: {
  24760. id: (
  24761. /*id*/
  24762. ctx[10]
  24763. ),
  24764. sound: (
  24765. /*sound*/
  24766. ctx[11]
  24767. )
  24768. }
  24769. });
  24770. return {
  24771. key: key_1,
  24772. first: null,
  24773. c() {
  24774. first = empty();
  24775. create_component(soundentry.$$.fragment);
  24776. this.first = first;
  24777. },
  24778. m(target, anchor) {
  24779. insert(target, first, anchor);
  24780. mount_component(soundentry, target, anchor);
  24781. current = true;
  24782. },
  24783. p(new_ctx, dirty) {
  24784. ctx = new_ctx;
  24785. const soundentry_changes = {};
  24786. if (dirty & /*sounds*/
  24787. 8)
  24788. soundentry_changes.id = /*id*/
  24789. ctx[10];
  24790. if (dirty & /*sounds*/
  24791. 8)
  24792. soundentry_changes.sound = /*sound*/
  24793. ctx[11];
  24794. soundentry.$set(soundentry_changes);
  24795. },
  24796. i(local) {
  24797. if (current)
  24798. return;
  24799. transition_in(soundentry.$$.fragment, local);
  24800. current = true;
  24801. },
  24802. o(local) {
  24803. transition_out(soundentry.$$.fragment, local);
  24804. current = false;
  24805. },
  24806. d(detaching) {
  24807. if (detaching)
  24808. detach(first);
  24809. destroy_component(soundentry, detaching);
  24810. }
  24811. };
  24812. }
  24813. function create_fragment$a(ctx) {
  24814. let div;
  24815. let t0;
  24816. let t1;
  24817. let current;
  24818. let if_block0 = !/*effects*/
  24819. ctx[0].length && !/*sounds*/
  24820. ctx[3].length && create_if_block_5();
  24821. let if_block1 = (
  24822. /*effects*/
  24823. ctx[0].length && create_if_block_1(ctx)
  24824. );
  24825. let if_block2 = (
  24826. /*sounds*/
  24827. ctx[3].length && create_if_block$2(ctx)
  24828. );
  24829. return {
  24830. c() {
  24831. div = element("div");
  24832. if (if_block0)
  24833. if_block0.c();
  24834. t0 = space();
  24835. if (if_block1)
  24836. if_block1.c();
  24837. t1 = space();
  24838. if (if_block2)
  24839. if_block2.c();
  24840. attr(div, "class", "effects-container svelte-ese-nc5j73");
  24841. },
  24842. m(target, anchor) {
  24843. insert(target, div, anchor);
  24844. if (if_block0)
  24845. if_block0.m(div, null);
  24846. append(div, t0);
  24847. if (if_block1)
  24848. if_block1.m(div, null);
  24849. append(div, t1);
  24850. if (if_block2)
  24851. if_block2.m(div, null);
  24852. current = true;
  24853. },
  24854. p(ctx2, [dirty]) {
  24855. if (!/*effects*/
  24856. ctx2[0].length && !/*sounds*/
  24857. ctx2[3].length) {
  24858. if (if_block0) {
  24859. if_block0.p(ctx2, dirty);
  24860. } else {
  24861. if_block0 = create_if_block_5();
  24862. if_block0.c();
  24863. if_block0.m(div, t0);
  24864. }
  24865. } else if (if_block0) {
  24866. if_block0.d(1);
  24867. if_block0 = null;
  24868. }
  24869. if (
  24870. /*effects*/
  24871. ctx2[0].length
  24872. ) {
  24873. if (if_block1) {
  24874. if_block1.p(ctx2, dirty);
  24875. if (dirty & /*effects*/
  24876. 1) {
  24877. transition_in(if_block1, 1);
  24878. }
  24879. } else {
  24880. if_block1 = create_if_block_1(ctx2);
  24881. if_block1.c();
  24882. transition_in(if_block1, 1);
  24883. if_block1.m(div, t1);
  24884. }
  24885. } else if (if_block1) {
  24886. group_outros();
  24887. transition_out(if_block1, 1, 1, () => {
  24888. if_block1 = null;
  24889. });
  24890. check_outros();
  24891. }
  24892. if (
  24893. /*sounds*/
  24894. ctx2[3].length
  24895. ) {
  24896. if (if_block2) {
  24897. if_block2.p(ctx2, dirty);
  24898. if (dirty & /*sounds*/
  24899. 8) {
  24900. transition_in(if_block2, 1);
  24901. }
  24902. } else {
  24903. if_block2 = create_if_block$2(ctx2);
  24904. if_block2.c();
  24905. transition_in(if_block2, 1);
  24906. if_block2.m(div, null);
  24907. }
  24908. } else if (if_block2) {
  24909. group_outros();
  24910. transition_out(if_block2, 1, 1, () => {
  24911. if_block2 = null;
  24912. });
  24913. check_outros();
  24914. }
  24915. },
  24916. i(local) {
  24917. if (current)
  24918. return;
  24919. transition_in(if_block1);
  24920. transition_in(if_block2);
  24921. current = true;
  24922. },
  24923. o(local) {
  24924. transition_out(if_block1);
  24925. transition_out(if_block2);
  24926. current = false;
  24927. },
  24928. d(detaching) {
  24929. if (detaching)
  24930. detach(div);
  24931. if (if_block0)
  24932. if_block0.d();
  24933. if (if_block1)
  24934. if_block1.d();
  24935. if (if_block2)
  24936. if_block2.d();
  24937. }
  24938. };
  24939. }
  24940. function instance$a($$self, $$props, $$invalidate) {
  24941. let effects;
  24942. let sounds;
  24943. let persistentEffects;
  24944. let temporaryEffects;
  24945. let $RunningSounds;
  24946. let $VisibleEffects;
  24947. const VisibleEffects = SequenceManager.VisibleEffects;
  24948. component_subscribe($$self, VisibleEffects, (value) => $$invalidate(9, $VisibleEffects = value));
  24949. const RunningSounds = SequenceManager.RunningSounds;
  24950. component_subscribe($$self, RunningSounds, (value) => $$invalidate(8, $RunningSounds = value));
  24951. function endAllEffects() {
  24952. Sequencer.EffectManager.endEffects({
  24953. effects: effects.filter((effect) => effect.userCanDelete && !effect.data.private)
  24954. });
  24955. }
  24956. function endAllSounds() {
  24957. SequencerAudioHelper.stop(RunningSounds.keys());
  24958. }
  24959. $$self.$$.update = () => {
  24960. if ($$self.$$.dirty & /*$VisibleEffects*/
  24961. 512) {
  24962. $$invalidate(0, effects = Object.values($VisibleEffects));
  24963. }
  24964. if ($$self.$$.dirty & /*$RunningSounds*/
  24965. 256) {
  24966. $$invalidate(3, sounds = Object.entries($RunningSounds));
  24967. }
  24968. if ($$self.$$.dirty & /*effects*/
  24969. 1) {
  24970. $$invalidate(2, persistentEffects = effects.filter((effect) => effect.data.persist));
  24971. }
  24972. if ($$self.$$.dirty & /*effects*/
  24973. 1) {
  24974. $$invalidate(1, temporaryEffects = effects.filter((effect) => !effect.data.persist));
  24975. }
  24976. };
  24977. return [
  24978. effects,
  24979. temporaryEffects,
  24980. persistentEffects,
  24981. sounds,
  24982. VisibleEffects,
  24983. RunningSounds,
  24984. endAllEffects,
  24985. endAllSounds,
  24986. $RunningSounds,
  24987. $VisibleEffects
  24988. ];
  24989. }
  24990. class Manager extends SvelteComponent {
  24991. constructor(options) {
  24992. super();
  24993. init(this, options, instance$a, create_fragment$a, safe_not_equal, {});
  24994. }
  24995. }
  24996. const SliderInput_svelte_svelte_type_style_lang = "";
  24997. function create_fragment$9(ctx) {
  24998. let div;
  24999. let label;
  25000. let t0_value = localize(
  25001. /*setting*/
  25002. ctx[0].label
  25003. ) + "";
  25004. let t0;
  25005. let t1;
  25006. let input0;
  25007. let t2;
  25008. let input1;
  25009. let applyStyles_action;
  25010. let mounted;
  25011. let dispose;
  25012. return {
  25013. c() {
  25014. div = element("div");
  25015. label = element("label");
  25016. t0 = text$1(t0_value);
  25017. t1 = space();
  25018. input0 = element("input");
  25019. t2 = space();
  25020. input1 = element("input");
  25021. attr(label, "class", "svelte-ese-x3192w");
  25022. input0.disabled = /*$isLocked*/
  25023. ctx[6];
  25024. attr(
  25025. input0,
  25026. "max",
  25027. /*max*/
  25028. ctx[2]
  25029. );
  25030. attr(
  25031. input0,
  25032. "min",
  25033. /*min*/
  25034. ctx[1]
  25035. );
  25036. attr(
  25037. input0,
  25038. "step",
  25039. /*step*/
  25040. ctx[4]
  25041. );
  25042. attr(input0, "type", "range");
  25043. attr(input0, "class", "svelte-ese-x3192w");
  25044. input1.disabled = /*$isLocked*/
  25045. ctx[6];
  25046. attr(
  25047. input1,
  25048. "max",
  25049. /*maxInput*/
  25050. ctx[3]
  25051. );
  25052. attr(
  25053. input1,
  25054. "min",
  25055. /*min*/
  25056. ctx[1]
  25057. );
  25058. input1.required = true;
  25059. attr(
  25060. input1,
  25061. "step",
  25062. /*step*/
  25063. ctx[4]
  25064. );
  25065. attr(input1, "type", "number");
  25066. attr(input1, "class", "svelte-ese-x3192w");
  25067. set_style(div, "display", "flex");
  25068. set_style(div, "align-items", "center");
  25069. },
  25070. m(target, anchor) {
  25071. insert(target, div, anchor);
  25072. append(div, label);
  25073. append(label, t0);
  25074. append(div, t1);
  25075. append(div, input0);
  25076. set_input_value(
  25077. input0,
  25078. /*$store*/
  25079. ctx[7]
  25080. );
  25081. append(div, t2);
  25082. append(div, input1);
  25083. set_input_value(
  25084. input1,
  25085. /*$store*/
  25086. ctx[7]
  25087. );
  25088. if (!mounted) {
  25089. dispose = [
  25090. listen(
  25091. input0,
  25092. "change",
  25093. /*input0_change_input_handler*/
  25094. ctx[11]
  25095. ),
  25096. listen(
  25097. input0,
  25098. "input",
  25099. /*input0_change_input_handler*/
  25100. ctx[11]
  25101. ),
  25102. listen(
  25103. input1,
  25104. "input",
  25105. /*input1_input_handler*/
  25106. ctx[12]
  25107. ),
  25108. action_destroyer(applyStyles_action = applyStyles.call(
  25109. null,
  25110. div,
  25111. /*styles*/
  25112. ctx[5]
  25113. ))
  25114. ];
  25115. mounted = true;
  25116. }
  25117. },
  25118. p(ctx2, [dirty]) {
  25119. if (dirty & /*setting*/
  25120. 1 && t0_value !== (t0_value = localize(
  25121. /*setting*/
  25122. ctx2[0].label
  25123. ) + ""))
  25124. set_data(t0, t0_value);
  25125. if (dirty & /*$isLocked*/
  25126. 64) {
  25127. input0.disabled = /*$isLocked*/
  25128. ctx2[6];
  25129. }
  25130. if (dirty & /*max*/
  25131. 4) {
  25132. attr(
  25133. input0,
  25134. "max",
  25135. /*max*/
  25136. ctx2[2]
  25137. );
  25138. }
  25139. if (dirty & /*min*/
  25140. 2) {
  25141. attr(
  25142. input0,
  25143. "min",
  25144. /*min*/
  25145. ctx2[1]
  25146. );
  25147. }
  25148. if (dirty & /*step*/
  25149. 16) {
  25150. attr(
  25151. input0,
  25152. "step",
  25153. /*step*/
  25154. ctx2[4]
  25155. );
  25156. }
  25157. if (dirty & /*$store*/
  25158. 128) {
  25159. set_input_value(
  25160. input0,
  25161. /*$store*/
  25162. ctx2[7]
  25163. );
  25164. }
  25165. if (dirty & /*$isLocked*/
  25166. 64) {
  25167. input1.disabled = /*$isLocked*/
  25168. ctx2[6];
  25169. }
  25170. if (dirty & /*maxInput*/
  25171. 8) {
  25172. attr(
  25173. input1,
  25174. "max",
  25175. /*maxInput*/
  25176. ctx2[3]
  25177. );
  25178. }
  25179. if (dirty & /*min*/
  25180. 2) {
  25181. attr(
  25182. input1,
  25183. "min",
  25184. /*min*/
  25185. ctx2[1]
  25186. );
  25187. }
  25188. if (dirty & /*step*/
  25189. 16) {
  25190. attr(
  25191. input1,
  25192. "step",
  25193. /*step*/
  25194. ctx2[4]
  25195. );
  25196. }
  25197. if (dirty & /*$store*/
  25198. 128 && to_number(input1.value) !== /*$store*/
  25199. ctx2[7]) {
  25200. set_input_value(
  25201. input1,
  25202. /*$store*/
  25203. ctx2[7]
  25204. );
  25205. }
  25206. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
  25207. 32)
  25208. applyStyles_action.update.call(
  25209. null,
  25210. /*styles*/
  25211. ctx2[5]
  25212. );
  25213. },
  25214. i: noop,
  25215. o: noop,
  25216. d(detaching) {
  25217. if (detaching)
  25218. detach(div);
  25219. mounted = false;
  25220. run_all(dispose);
  25221. }
  25222. };
  25223. }
  25224. function instance$9($$self, $$props, $$invalidate) {
  25225. let $isLocked;
  25226. let $store;
  25227. let { setting } = $$props;
  25228. let { lock = false } = $$props;
  25229. let { min = 0.1 } = $$props;
  25230. let { max = 2 } = $$props;
  25231. let { maxInput = Infinity } = $$props;
  25232. let { step = 0.01 } = $$props;
  25233. let { styles = {} } = $$props;
  25234. const store = setting.store;
  25235. component_subscribe($$self, store, (value) => $$invalidate(7, $store = value));
  25236. const isLocked = lock ? lock.store : writable$1(false);
  25237. component_subscribe($$self, isLocked, (value) => $$invalidate(6, $isLocked = value));
  25238. function input0_change_input_handler() {
  25239. $store = to_number(this.value);
  25240. store.set($store);
  25241. }
  25242. function input1_input_handler() {
  25243. $store = to_number(this.value);
  25244. store.set($store);
  25245. }
  25246. $$self.$$set = ($$props2) => {
  25247. if ("setting" in $$props2)
  25248. $$invalidate(0, setting = $$props2.setting);
  25249. if ("lock" in $$props2)
  25250. $$invalidate(10, lock = $$props2.lock);
  25251. if ("min" in $$props2)
  25252. $$invalidate(1, min = $$props2.min);
  25253. if ("max" in $$props2)
  25254. $$invalidate(2, max = $$props2.max);
  25255. if ("maxInput" in $$props2)
  25256. $$invalidate(3, maxInput = $$props2.maxInput);
  25257. if ("step" in $$props2)
  25258. $$invalidate(4, step = $$props2.step);
  25259. if ("styles" in $$props2)
  25260. $$invalidate(5, styles = $$props2.styles);
  25261. };
  25262. return [
  25263. setting,
  25264. min,
  25265. max,
  25266. maxInput,
  25267. step,
  25268. styles,
  25269. $isLocked,
  25270. $store,
  25271. store,
  25272. isLocked,
  25273. lock,
  25274. input0_change_input_handler,
  25275. input1_input_handler
  25276. ];
  25277. }
  25278. class SliderInput extends SvelteComponent {
  25279. constructor(options) {
  25280. super();
  25281. init(this, options, instance$9, create_fragment$9, safe_not_equal, {
  25282. setting: 0,
  25283. lock: 10,
  25284. min: 1,
  25285. max: 2,
  25286. maxInput: 3,
  25287. step: 4,
  25288. styles: 5
  25289. });
  25290. }
  25291. }
  25292. function create_fragment$8(ctx) {
  25293. let div;
  25294. let input;
  25295. let input_disabled_value;
  25296. let t0;
  25297. let label_1;
  25298. let t1_value = localize(
  25299. /*setting*/
  25300. ctx[0].label
  25301. ) + "";
  25302. let t1;
  25303. let applyStyles_action;
  25304. let mounted;
  25305. let dispose;
  25306. return {
  25307. c() {
  25308. div = element("div");
  25309. input = element("input");
  25310. t0 = space();
  25311. label_1 = element("label");
  25312. t1 = text$1(t1_value);
  25313. attr(
  25314. input,
  25315. "id",
  25316. /*id*/
  25317. ctx[5]
  25318. );
  25319. input.disabled = input_disabled_value = /*$isLocked*/
  25320. ctx[3] !== /*inverse*/
  25321. ctx[1];
  25322. set_style(input, "height", "15px");
  25323. attr(input, "type", "checkbox");
  25324. attr(
  25325. label_1,
  25326. "for",
  25327. /*id*/
  25328. ctx[5]
  25329. );
  25330. set_style(div, "display", "flex");
  25331. set_style(div, "align-items", "center");
  25332. },
  25333. m(target, anchor) {
  25334. insert(target, div, anchor);
  25335. append(div, input);
  25336. input.checked = /*$store*/
  25337. ctx[4];
  25338. append(div, t0);
  25339. append(div, label_1);
  25340. append(label_1, t1);
  25341. if (!mounted) {
  25342. dispose = [
  25343. listen(
  25344. input,
  25345. "change",
  25346. /*input_change_handler*/
  25347. ctx[10]
  25348. ),
  25349. action_destroyer(applyStyles_action = applyStyles.call(
  25350. null,
  25351. div,
  25352. /*styles*/
  25353. ctx[2]
  25354. ))
  25355. ];
  25356. mounted = true;
  25357. }
  25358. },
  25359. p(ctx2, [dirty]) {
  25360. if (dirty & /*$isLocked, inverse*/
  25361. 10 && input_disabled_value !== (input_disabled_value = /*$isLocked*/
  25362. ctx2[3] !== /*inverse*/
  25363. ctx2[1])) {
  25364. input.disabled = input_disabled_value;
  25365. }
  25366. if (dirty & /*$store*/
  25367. 16) {
  25368. input.checked = /*$store*/
  25369. ctx2[4];
  25370. }
  25371. if (dirty & /*setting*/
  25372. 1 && t1_value !== (t1_value = localize(
  25373. /*setting*/
  25374. ctx2[0].label
  25375. ) + ""))
  25376. set_data(t1, t1_value);
  25377. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
  25378. 4)
  25379. applyStyles_action.update.call(
  25380. null,
  25381. /*styles*/
  25382. ctx2[2]
  25383. );
  25384. },
  25385. i: noop,
  25386. o: noop,
  25387. d(detaching) {
  25388. if (detaching)
  25389. detach(div);
  25390. mounted = false;
  25391. run_all(dispose);
  25392. }
  25393. };
  25394. }
  25395. function instance$8($$self, $$props, $$invalidate) {
  25396. let $store;
  25397. let $isLocked;
  25398. let { setting } = $$props;
  25399. let { label = false } = $$props;
  25400. let { lock = false } = $$props;
  25401. let { inverse = false } = $$props;
  25402. let { styles = {} } = $$props;
  25403. const id = "sequencer-input-" + randomID();
  25404. const store = setting.store;
  25405. component_subscribe($$self, store, (value) => $$invalidate(4, $store = value));
  25406. const isLocked = lock ? lock.store : writable$1(inverse);
  25407. component_subscribe($$self, isLocked, (value) => $$invalidate(3, $isLocked = value));
  25408. function input_change_handler() {
  25409. $store = this.checked;
  25410. store.set($store);
  25411. }
  25412. $$self.$$set = ($$props2) => {
  25413. if ("setting" in $$props2)
  25414. $$invalidate(0, setting = $$props2.setting);
  25415. if ("label" in $$props2)
  25416. $$invalidate(8, label = $$props2.label);
  25417. if ("lock" in $$props2)
  25418. $$invalidate(9, lock = $$props2.lock);
  25419. if ("inverse" in $$props2)
  25420. $$invalidate(1, inverse = $$props2.inverse);
  25421. if ("styles" in $$props2)
  25422. $$invalidate(2, styles = $$props2.styles);
  25423. };
  25424. $$self.$$.update = () => {
  25425. if ($$self.$$.dirty & /*$isLocked, inverse*/
  25426. 10) {
  25427. {
  25428. if ($isLocked !== inverse) {
  25429. set_store_value(store, $store = false, $store);
  25430. }
  25431. }
  25432. }
  25433. };
  25434. return [
  25435. setting,
  25436. inverse,
  25437. styles,
  25438. $isLocked,
  25439. $store,
  25440. id,
  25441. store,
  25442. isLocked,
  25443. label,
  25444. lock,
  25445. input_change_handler
  25446. ];
  25447. }
  25448. class Checkbox extends SvelteComponent {
  25449. constructor(options) {
  25450. super();
  25451. init(this, options, instance$8, create_fragment$8, safe_not_equal, {
  25452. setting: 0,
  25453. label: 8,
  25454. lock: 9,
  25455. inverse: 1,
  25456. styles: 2
  25457. });
  25458. }
  25459. }
  25460. const NumberInput_svelte_svelte_type_style_lang = "";
  25461. function create_if_block$1(ctx) {
  25462. let label;
  25463. let t_value = localize(
  25464. /*setting*/
  25465. ctx[0].label
  25466. ) + "";
  25467. let t;
  25468. return {
  25469. c() {
  25470. label = element("label");
  25471. t = text$1(t_value);
  25472. attr(
  25473. label,
  25474. "for",
  25475. /*id*/
  25476. ctx[5]
  25477. );
  25478. attr(label, "class", "svelte-ese-ywsxq0");
  25479. },
  25480. m(target, anchor) {
  25481. insert(target, label, anchor);
  25482. append(label, t);
  25483. },
  25484. p(ctx2, dirty) {
  25485. if (dirty & /*setting*/
  25486. 1 && t_value !== (t_value = localize(
  25487. /*setting*/
  25488. ctx2[0].label
  25489. ) + ""))
  25490. set_data(t, t_value);
  25491. },
  25492. d(detaching) {
  25493. if (detaching)
  25494. detach(label);
  25495. }
  25496. };
  25497. }
  25498. function create_fragment$7(ctx) {
  25499. let div;
  25500. let show_if = localize(
  25501. /*setting*/
  25502. ctx[0].label
  25503. );
  25504. let t;
  25505. let input;
  25506. let input_disabled_value;
  25507. let applyStyles_action;
  25508. let mounted;
  25509. let dispose;
  25510. let if_block = show_if && create_if_block$1(ctx);
  25511. return {
  25512. c() {
  25513. div = element("div");
  25514. if (if_block)
  25515. if_block.c();
  25516. t = space();
  25517. input = element("input");
  25518. attr(
  25519. input,
  25520. "id",
  25521. /*id*/
  25522. ctx[5]
  25523. );
  25524. attr(input, "type", "number");
  25525. input.disabled = input_disabled_value = /*$isLocked*/
  25526. ctx[3] !== /*inverse*/
  25527. ctx[1];
  25528. attr(input, "class", "svelte-ese-ywsxq0");
  25529. set_style(div, "display", "flex");
  25530. set_style(div, "align-items", "center");
  25531. },
  25532. m(target, anchor) {
  25533. insert(target, div, anchor);
  25534. if (if_block)
  25535. if_block.m(div, null);
  25536. append(div, t);
  25537. append(div, input);
  25538. set_input_value(
  25539. input,
  25540. /*$store*/
  25541. ctx[4]
  25542. );
  25543. if (!mounted) {
  25544. dispose = [
  25545. listen(
  25546. input,
  25547. "input",
  25548. /*input_input_handler*/
  25549. ctx[9]
  25550. ),
  25551. listen(
  25552. input,
  25553. "change",
  25554. /*change_handler*/
  25555. ctx[10]
  25556. ),
  25557. action_destroyer(applyStyles_action = applyStyles.call(
  25558. null,
  25559. div,
  25560. /*styles*/
  25561. ctx[2]
  25562. ))
  25563. ];
  25564. mounted = true;
  25565. }
  25566. },
  25567. p(ctx2, [dirty]) {
  25568. if (dirty & /*setting*/
  25569. 1)
  25570. show_if = localize(
  25571. /*setting*/
  25572. ctx2[0].label
  25573. );
  25574. if (show_if) {
  25575. if (if_block) {
  25576. if_block.p(ctx2, dirty);
  25577. } else {
  25578. if_block = create_if_block$1(ctx2);
  25579. if_block.c();
  25580. if_block.m(div, t);
  25581. }
  25582. } else if (if_block) {
  25583. if_block.d(1);
  25584. if_block = null;
  25585. }
  25586. if (dirty & /*$isLocked, inverse*/
  25587. 10 && input_disabled_value !== (input_disabled_value = /*$isLocked*/
  25588. ctx2[3] !== /*inverse*/
  25589. ctx2[1])) {
  25590. input.disabled = input_disabled_value;
  25591. }
  25592. if (dirty & /*$store*/
  25593. 16 && to_number(input.value) !== /*$store*/
  25594. ctx2[4]) {
  25595. set_input_value(
  25596. input,
  25597. /*$store*/
  25598. ctx2[4]
  25599. );
  25600. }
  25601. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*styles*/
  25602. 4)
  25603. applyStyles_action.update.call(
  25604. null,
  25605. /*styles*/
  25606. ctx2[2]
  25607. );
  25608. },
  25609. i: noop,
  25610. o: noop,
  25611. d(detaching) {
  25612. if (detaching)
  25613. detach(div);
  25614. if (if_block)
  25615. if_block.d();
  25616. mounted = false;
  25617. run_all(dispose);
  25618. }
  25619. };
  25620. }
  25621. function instance$7($$self, $$props, $$invalidate) {
  25622. let $isLocked;
  25623. let $store;
  25624. let { setting } = $$props;
  25625. let { lock = false } = $$props;
  25626. let { inverse = false } = $$props;
  25627. let { styles = {} } = $$props;
  25628. const id = "sequencer-input-" + randomID();
  25629. const store = setting.store;
  25630. component_subscribe($$self, store, (value) => $$invalidate(4, $store = value));
  25631. const isLocked = lock ? lock.store : writable$1(inverse);
  25632. component_subscribe($$self, isLocked, (value) => $$invalidate(3, $isLocked = value));
  25633. function input_input_handler() {
  25634. $store = to_number(this.value);
  25635. store.set($store);
  25636. }
  25637. const change_handler = () => {
  25638. if ($store === null)
  25639. set_store_value(store, $store = 0, $store);
  25640. };
  25641. $$self.$$set = ($$props2) => {
  25642. if ("setting" in $$props2)
  25643. $$invalidate(0, setting = $$props2.setting);
  25644. if ("lock" in $$props2)
  25645. $$invalidate(8, lock = $$props2.lock);
  25646. if ("inverse" in $$props2)
  25647. $$invalidate(1, inverse = $$props2.inverse);
  25648. if ("styles" in $$props2)
  25649. $$invalidate(2, styles = $$props2.styles);
  25650. };
  25651. return [
  25652. setting,
  25653. inverse,
  25654. styles,
  25655. $isLocked,
  25656. $store,
  25657. id,
  25658. store,
  25659. isLocked,
  25660. lock,
  25661. input_input_handler,
  25662. change_handler
  25663. ];
  25664. }
  25665. class NumberInput extends SvelteComponent {
  25666. constructor(options) {
  25667. super();
  25668. init(this, options, instance$7, create_fragment$7, safe_not_equal, {
  25669. setting: 0,
  25670. lock: 8,
  25671. inverse: 1,
  25672. styles: 2
  25673. });
  25674. }
  25675. }
  25676. const SwitchToggle_svelte_svelte_type_style_lang = "";
  25677. function create_fragment$6(ctx) {
  25678. let div2;
  25679. let div0;
  25680. let span0;
  25681. let t0_value = localize(
  25682. /*setting*/
  25683. ctx[0].label_off
  25684. ) + "";
  25685. let t0;
  25686. let t1;
  25687. let div1;
  25688. let span1;
  25689. let t2_value = localize(
  25690. /*setting*/
  25691. ctx[0].label_on
  25692. ) + "";
  25693. let t2;
  25694. let applyStyles_action;
  25695. let mounted;
  25696. let dispose;
  25697. return {
  25698. c() {
  25699. div2 = element("div");
  25700. div0 = element("div");
  25701. span0 = element("span");
  25702. t0 = text$1(t0_value);
  25703. t1 = space();
  25704. div1 = element("div");
  25705. span1 = element("span");
  25706. t2 = text$1(t2_value);
  25707. attr(div0, "class", "first svelte-ese-o0yoxs");
  25708. toggle_class(div0, "active", !/*$store*/
  25709. ctx[2]);
  25710. attr(div1, "class", "second svelte-ese-o0yoxs");
  25711. toggle_class(
  25712. div1,
  25713. "active",
  25714. /*$store*/
  25715. ctx[2]
  25716. );
  25717. set_style(div2, "display", "flex");
  25718. set_style(div2, "align-items", "center");
  25719. attr(div2, "class", "svelte-ese-o0yoxs");
  25720. },
  25721. m(target, anchor) {
  25722. insert(target, div2, anchor);
  25723. append(div2, div0);
  25724. append(div0, span0);
  25725. append(span0, t0);
  25726. append(div2, t1);
  25727. append(div2, div1);
  25728. append(div1, span1);
  25729. append(span1, t2);
  25730. if (!mounted) {
  25731. dispose = [
  25732. listen(
  25733. div0,
  25734. "click",
  25735. /*click_handler*/
  25736. ctx[5]
  25737. ),
  25738. listen(
  25739. div1,
  25740. "click",
  25741. /*click_handler_1*/
  25742. ctx[6]
  25743. ),
  25744. action_destroyer(applyStyles_action = applyStyles.call(
  25745. null,
  25746. div2,
  25747. /*finalStyles*/
  25748. ctx[1]
  25749. ))
  25750. ];
  25751. mounted = true;
  25752. }
  25753. },
  25754. p(ctx2, [dirty]) {
  25755. if (dirty & /*setting*/
  25756. 1 && t0_value !== (t0_value = localize(
  25757. /*setting*/
  25758. ctx2[0].label_off
  25759. ) + ""))
  25760. set_data(t0, t0_value);
  25761. if (dirty & /*$store*/
  25762. 4) {
  25763. toggle_class(div0, "active", !/*$store*/
  25764. ctx2[2]);
  25765. }
  25766. if (dirty & /*setting*/
  25767. 1 && t2_value !== (t2_value = localize(
  25768. /*setting*/
  25769. ctx2[0].label_on
  25770. ) + ""))
  25771. set_data(t2, t2_value);
  25772. if (dirty & /*$store*/
  25773. 4) {
  25774. toggle_class(
  25775. div1,
  25776. "active",
  25777. /*$store*/
  25778. ctx2[2]
  25779. );
  25780. }
  25781. if (applyStyles_action && is_function(applyStyles_action.update) && dirty & /*finalStyles*/
  25782. 2)
  25783. applyStyles_action.update.call(
  25784. null,
  25785. /*finalStyles*/
  25786. ctx2[1]
  25787. );
  25788. },
  25789. i: noop,
  25790. o: noop,
  25791. d(detaching) {
  25792. if (detaching)
  25793. detach(div2);
  25794. mounted = false;
  25795. run_all(dispose);
  25796. }
  25797. };
  25798. }
  25799. const width = 5.75;
  25800. function instance$6($$self, $$props, $$invalidate) {
  25801. let finalStyles;
  25802. let $store;
  25803. let { setting } = $$props;
  25804. let { styles = {} } = $$props;
  25805. const store = setting.store;
  25806. component_subscribe($$self, store, (value) => $$invalidate(2, $store = value));
  25807. const click_handler = () => {
  25808. set_store_value(store, $store = false, $store);
  25809. };
  25810. const click_handler_1 = () => {
  25811. set_store_value(store, $store = true, $store);
  25812. };
  25813. $$self.$$set = ($$props2) => {
  25814. if ("setting" in $$props2)
  25815. $$invalidate(0, setting = $$props2.setting);
  25816. if ("styles" in $$props2)
  25817. $$invalidate(4, styles = $$props2.styles);
  25818. };
  25819. $$self.$$.update = () => {
  25820. if ($$self.$$.dirty & /*styles*/
  25821. 16) {
  25822. $$invalidate(1, finalStyles = {
  25823. ...styles,
  25824. "--switch-width": `${width}em`,
  25825. "--half-switch-width": `${width - 1.5}em`,
  25826. "--half-switch-width-on": `${width - 1.7}em`
  25827. });
  25828. }
  25829. };
  25830. return [setting, finalStyles, $store, store, styles, click_handler, click_handler_1];
  25831. }
  25832. class SwitchToggle extends SvelteComponent {
  25833. constructor(options) {
  25834. super();
  25835. init(this, options, instance$6, create_fragment$6, safe_not_equal, { setting: 0, styles: 4 });
  25836. }
  25837. }
  25838. const Player_svelte_svelte_type_style_lang = "";
  25839. function get_each_context$2(ctx, list, i) {
  25840. const child_ctx = ctx.slice();
  25841. child_ctx[20] = list[i];
  25842. return child_ctx;
  25843. }
  25844. function get_each_context_1(ctx, list, i) {
  25845. const child_ctx = ctx.slice();
  25846. child_ctx[23] = list[i];
  25847. return child_ctx;
  25848. }
  25849. function get_each_context_2(ctx, list, i) {
  25850. const child_ctx = ctx.slice();
  25851. child_ctx[26] = list[i];
  25852. return child_ctx;
  25853. }
  25854. function create_each_block_2(ctx) {
  25855. let option;
  25856. let t_value = (
  25857. /*suggestion*/
  25858. ctx[26] + ""
  25859. );
  25860. let t;
  25861. let option_value_value;
  25862. return {
  25863. c() {
  25864. option = element("option");
  25865. t = text$1(t_value);
  25866. option.__value = option_value_value = /*suggestion*/
  25867. ctx[26];
  25868. option.value = option.__value;
  25869. },
  25870. m(target, anchor) {
  25871. insert(target, option, anchor);
  25872. append(option, t);
  25873. },
  25874. p(ctx2, dirty) {
  25875. if (dirty & /*suggestions*/
  25876. 2 && t_value !== (t_value = /*suggestion*/
  25877. ctx2[26] + ""))
  25878. set_data(t, t_value);
  25879. if (dirty & /*suggestions*/
  25880. 2 && option_value_value !== (option_value_value = /*suggestion*/
  25881. ctx2[26])) {
  25882. option.__value = option_value_value;
  25883. option.value = option.__value;
  25884. }
  25885. },
  25886. d(detaching) {
  25887. if (detaching)
  25888. detach(option);
  25889. }
  25890. };
  25891. }
  25892. function create_each_block_1(key_1, ctx) {
  25893. let option;
  25894. let t_value = (
  25895. /*user*/
  25896. ctx[23].name + ""
  25897. );
  25898. let t;
  25899. return {
  25900. key: key_1,
  25901. first: null,
  25902. c() {
  25903. option = element("option");
  25904. t = text$1(t_value);
  25905. option.__value = /*user*/
  25906. ctx[23].id;
  25907. option.value = option.__value;
  25908. this.first = option;
  25909. },
  25910. m(target, anchor) {
  25911. insert(target, option, anchor);
  25912. append(option, t);
  25913. },
  25914. p(new_ctx, dirty) {
  25915. ctx = new_ctx;
  25916. },
  25917. d(detaching) {
  25918. if (detaching)
  25919. detach(option);
  25920. }
  25921. };
  25922. }
  25923. function create_each_block$2(ctx) {
  25924. let option;
  25925. let t_value = (
  25926. /*preset*/
  25927. ctx[20] + ""
  25928. );
  25929. let t;
  25930. return {
  25931. c() {
  25932. option = element("option");
  25933. t = text$1(t_value);
  25934. option.__value = /*preset*/
  25935. ctx[20];
  25936. option.value = option.__value;
  25937. },
  25938. m(target, anchor) {
  25939. insert(target, option, anchor);
  25940. append(option, t);
  25941. },
  25942. p: noop,
  25943. d(detaching) {
  25944. if (detaching)
  25945. detach(option);
  25946. }
  25947. };
  25948. }
  25949. function create_fragment$5(ctx) {
  25950. let div16;
  25951. let fieldset0;
  25952. let legend0;
  25953. let t1;
  25954. let button0;
  25955. let t3;
  25956. let div0;
  25957. let input;
  25958. let t4;
  25959. let button1;
  25960. let t5;
  25961. let datalist;
  25962. let t6;
  25963. let div1;
  25964. let label;
  25965. let t8;
  25966. let select0;
  25967. let option0;
  25968. let each_blocks_1 = [];
  25969. let each1_lookup = /* @__PURE__ */ new Map();
  25970. let t10;
  25971. let div4;
  25972. let div2;
  25973. let t12;
  25974. let div3;
  25975. let select1;
  25976. let option1;
  25977. let t14;
  25978. let button2;
  25979. let t15;
  25980. let button3;
  25981. let i2;
  25982. let button3_disabled_value;
  25983. let t16;
  25984. let button4;
  25985. let i3;
  25986. let button4_disabled_value;
  25987. let t17;
  25988. let fieldset1;
  25989. let legend1;
  25990. let t19;
  25991. let div11;
  25992. let sliderinput0;
  25993. let t20;
  25994. let numberinput0;
  25995. let t21;
  25996. let numberinput1;
  25997. let t22;
  25998. let div5;
  25999. let t23;
  26000. let sliderinput1;
  26001. let t24;
  26002. let div6;
  26003. let t25;
  26004. let checkbox0;
  26005. let t26;
  26006. let div7;
  26007. let t27;
  26008. let div8;
  26009. let t28;
  26010. let checkbox1;
  26011. let t29;
  26012. let checkbox2;
  26013. let t30;
  26014. let checkbox3;
  26015. let t31;
  26016. let checkbox4;
  26017. let t32;
  26018. let div9;
  26019. let t33;
  26020. let numberinput2;
  26021. let t34;
  26022. let numberinput3;
  26023. let t35;
  26024. let checkbox5;
  26025. let t36;
  26026. let numberinput4;
  26027. let t37;
  26028. let checkbox6;
  26029. let t38;
  26030. let div10;
  26031. let t39;
  26032. let fieldset2;
  26033. let legend2;
  26034. let t41;
  26035. let div15;
  26036. let numberinput5;
  26037. let t42;
  26038. let numberinput6;
  26039. let t43;
  26040. let div12;
  26041. let t44;
  26042. let checkbox7;
  26043. let t45;
  26044. let numberinput7;
  26045. let t46;
  26046. let numberinput8;
  26047. let t47;
  26048. let numberinput9;
  26049. let t48;
  26050. let div13;
  26051. let t49;
  26052. let switchtoggle;
  26053. let t50;
  26054. let numberinput10;
  26055. let t51;
  26056. let div14;
  26057. let t52;
  26058. let checkbox8;
  26059. let t53;
  26060. let checkbox9;
  26061. let t54;
  26062. let checkbox10;
  26063. let t55;
  26064. let checkbox11;
  26065. let t56;
  26066. let checkbox12;
  26067. let current;
  26068. let mounted;
  26069. let dispose;
  26070. let each_value_2 = (
  26071. /*suggestions*/
  26072. ctx[1]
  26073. );
  26074. let each_blocks_2 = [];
  26075. for (let i = 0; i < each_value_2.length; i += 1) {
  26076. each_blocks_2[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i));
  26077. }
  26078. let each_value_1 = (
  26079. /*users*/
  26080. ctx[5]
  26081. );
  26082. const get_key = (ctx2) => (
  26083. /*user*/
  26084. ctx2[23].id
  26085. );
  26086. for (let i = 0; i < each_value_1.length; i += 1) {
  26087. let child_ctx = get_each_context_1(ctx, each_value_1, i);
  26088. let key = get_key(child_ctx);
  26089. each1_lookup.set(key, each_blocks_1[i] = create_each_block_1(key, child_ctx));
  26090. }
  26091. let each_value = (
  26092. /*presets*/
  26093. ctx[8]
  26094. );
  26095. let each_blocks = [];
  26096. for (let i = 0; i < each_value.length; i += 1) {
  26097. each_blocks[i] = create_each_block$2(get_each_context$2(ctx, each_value, i));
  26098. }
  26099. sliderinput0 = new SliderInput({
  26100. props: {
  26101. setting: PlayerSettings.scale,
  26102. styles: { "grid-column": `1 / 5` }
  26103. }
  26104. });
  26105. numberinput0 = new NumberInput({
  26106. props: {
  26107. setting: PlayerSettings.scaleIn,
  26108. styles: { "grid-column": `1 / 3` }
  26109. }
  26110. });
  26111. numberinput1 = new NumberInput({
  26112. props: {
  26113. setting: PlayerSettings.scaleOut,
  26114. styles: { "grid-column": `3 / 5` }
  26115. }
  26116. });
  26117. sliderinput1 = new SliderInput({
  26118. props: {
  26119. setting: PlayerSettings.rotation,
  26120. lock: PlayerSettings.randomRotation,
  26121. min: "-180",
  26122. max: "180",
  26123. styles: { "grid-column": `1 / 5` }
  26124. }
  26125. });
  26126. checkbox0 = new Checkbox({
  26127. props: {
  26128. setting: PlayerSettings.randomRotation,
  26129. styles: { "grid-column": `2 / 4` }
  26130. }
  26131. });
  26132. checkbox1 = new Checkbox({
  26133. props: {
  26134. setting: PlayerSettings.mirrorX,
  26135. styles: { "grid-column": `1 / 3` }
  26136. }
  26137. });
  26138. checkbox2 = new Checkbox({
  26139. props: {
  26140. setting: PlayerSettings.mirrorY,
  26141. styles: { "grid-column": `3 / 5` }
  26142. }
  26143. });
  26144. checkbox3 = new Checkbox({
  26145. props: {
  26146. setting: PlayerSettings.randomMirrorX,
  26147. styles: { "grid-column": `1 / 3` },
  26148. lock: PlayerSettings.mirrorX,
  26149. inverse: true
  26150. }
  26151. });
  26152. checkbox4 = new Checkbox({
  26153. props: {
  26154. setting: PlayerSettings.randomMirrorY,
  26155. styles: { "grid-column": `3 / 5` },
  26156. lock: PlayerSettings.mirrorY,
  26157. inverse: true
  26158. }
  26159. });
  26160. numberinput2 = new NumberInput({
  26161. props: {
  26162. setting: PlayerSettings.offsetX,
  26163. lock: PlayerSettings.randomOffset,
  26164. styles: { "grid-column": `1 / 3` }
  26165. }
  26166. });
  26167. numberinput3 = new NumberInput({
  26168. props: {
  26169. setting: PlayerSettings.offsetY,
  26170. lock: PlayerSettings.randomOffset,
  26171. styles: { "grid-column": `3 / 5` }
  26172. }
  26173. });
  26174. checkbox5 = new Checkbox({
  26175. props: {
  26176. setting: PlayerSettings.randomOffset,
  26177. styles: { "grid-column": `1 / 3` }
  26178. }
  26179. });
  26180. numberinput4 = new NumberInput({
  26181. props: {
  26182. setting: PlayerSettings.randomOffsetAmount,
  26183. lock: PlayerSettings.randomOffset,
  26184. inverse: true,
  26185. styles: { "grid-column": `3 / 5` }
  26186. }
  26187. });
  26188. checkbox6 = new Checkbox({
  26189. props: {
  26190. setting: PlayerSettings.offsetGridUnits,
  26191. styles: { "grid-column": `2 / 5` }
  26192. }
  26193. });
  26194. numberinput5 = new NumberInput({
  26195. props: {
  26196. setting: PlayerSettings.fadeIn,
  26197. styles: { "grid-column": `1 / 3` }
  26198. }
  26199. });
  26200. numberinput6 = new NumberInput({
  26201. props: {
  26202. setting: PlayerSettings.fadeOut,
  26203. styles: { "grid-column": `3 / 5` }
  26204. }
  26205. });
  26206. checkbox7 = new Checkbox({
  26207. props: {
  26208. setting: PlayerSettings.repeat,
  26209. styles: { "grid-column": `1 / 3` }
  26210. }
  26211. });
  26212. numberinput7 = new NumberInput({
  26213. props: {
  26214. setting: PlayerSettings.repetitions,
  26215. lock: PlayerSettings.repeat,
  26216. inverse: true,
  26217. styles: { "grid-column": `3 / 5` }
  26218. }
  26219. });
  26220. numberinput8 = new NumberInput({
  26221. props: {
  26222. setting: PlayerSettings.repeatDelayMin,
  26223. lock: PlayerSettings.repeat,
  26224. inverse: true,
  26225. styles: { "grid-column": `3 / 4` }
  26226. }
  26227. });
  26228. numberinput9 = new NumberInput({
  26229. props: {
  26230. setting: PlayerSettings.repeatDelayMax,
  26231. lock: PlayerSettings.repeat,
  26232. inverse: true,
  26233. styles: { "grid-column": `4 / 5` }
  26234. }
  26235. });
  26236. switchtoggle = new SwitchToggle({
  26237. props: {
  26238. setting: PlayerSettings.moveTowards,
  26239. styles: { "grid-column": `1 / 3` }
  26240. }
  26241. });
  26242. numberinput10 = new NumberInput({
  26243. props: {
  26244. setting: PlayerSettings.moveSpeed,
  26245. lock: PlayerSettings.moveTowards,
  26246. inverse: true,
  26247. styles: { "grid-column": `3 / 5` }
  26248. }
  26249. });
  26250. checkbox8 = new Checkbox({
  26251. props: {
  26252. setting: PlayerSettings.attachTo,
  26253. styles: { "grid-column": `1 / 5` }
  26254. }
  26255. });
  26256. checkbox9 = new Checkbox({
  26257. props: {
  26258. setting: PlayerSettings.stretchToAttach,
  26259. styles: { "grid-column": `1 / 5` }
  26260. }
  26261. });
  26262. checkbox10 = new Checkbox({
  26263. props: {
  26264. setting: PlayerSettings.snapToGrid,
  26265. styles: { "grid-column": `1 / 5` }
  26266. }
  26267. });
  26268. checkbox11 = new Checkbox({
  26269. props: {
  26270. setting: PlayerSettings.persist,
  26271. styles: { "grid-column": `1 / 5` }
  26272. }
  26273. });
  26274. checkbox12 = new Checkbox({
  26275. props: {
  26276. setting: PlayerSettings.belowTokens,
  26277. styles: { "grid-column": `1 / 5` }
  26278. }
  26279. });
  26280. return {
  26281. c() {
  26282. div16 = element("div");
  26283. fieldset0 = element("fieldset");
  26284. legend0 = element("legend");
  26285. legend0.textContent = "Effect";
  26286. t1 = space();
  26287. button0 = element("button");
  26288. button0.textContent = `${localize("SEQUENCER.Player.SwitchToLayer")}`;
  26289. t3 = space();
  26290. div0 = element("div");
  26291. input = element("input");
  26292. t4 = space();
  26293. button1 = element("button");
  26294. button1.innerHTML = `<i class="fas fa-file-import svelte-ese-1ipnpu1"></i>`;
  26295. t5 = space();
  26296. datalist = element("datalist");
  26297. for (let i = 0; i < each_blocks_2.length; i += 1) {
  26298. each_blocks_2[i].c();
  26299. }
  26300. t6 = space();
  26301. div1 = element("div");
  26302. label = element("label");
  26303. label.textContent = "Play for users:";
  26304. t8 = space();
  26305. select0 = element("select");
  26306. option0 = element("option");
  26307. option0.textContent = `${localize("SEQUENCER.Player.AllUsers")}`;
  26308. for (let i = 0; i < each_blocks_1.length; i += 1) {
  26309. each_blocks_1[i].c();
  26310. }
  26311. t10 = space();
  26312. div4 = element("div");
  26313. div2 = element("div");
  26314. div2.textContent = `${localize("SEQUENCER.Player.Presets")}`;
  26315. t12 = space();
  26316. div3 = element("div");
  26317. select1 = element("select");
  26318. option1 = element("option");
  26319. option1.textContent = `${localize("SEQUENCER.Player.PresetsDefault")}`;
  26320. for (let i = 0; i < each_blocks.length; i += 1) {
  26321. each_blocks[i].c();
  26322. }
  26323. t14 = space();
  26324. button2 = element("button");
  26325. button2.innerHTML = `<i class="fas fa-download svelte-ese-1ipnpu1"></i>`;
  26326. t15 = space();
  26327. button3 = element("button");
  26328. i2 = element("i");
  26329. t16 = space();
  26330. button4 = element("button");
  26331. i3 = element("i");
  26332. t17 = space();
  26333. fieldset1 = element("fieldset");
  26334. legend1 = element("legend");
  26335. legend1.textContent = "Transform";
  26336. t19 = space();
  26337. div11 = element("div");
  26338. create_component(sliderinput0.$$.fragment);
  26339. t20 = space();
  26340. create_component(numberinput0.$$.fragment);
  26341. t21 = space();
  26342. create_component(numberinput1.$$.fragment);
  26343. t22 = space();
  26344. div5 = element("div");
  26345. t23 = space();
  26346. create_component(sliderinput1.$$.fragment);
  26347. t24 = space();
  26348. div6 = element("div");
  26349. t25 = space();
  26350. create_component(checkbox0.$$.fragment);
  26351. t26 = space();
  26352. div7 = element("div");
  26353. t27 = space();
  26354. div8 = element("div");
  26355. t28 = space();
  26356. create_component(checkbox1.$$.fragment);
  26357. t29 = space();
  26358. create_component(checkbox2.$$.fragment);
  26359. t30 = space();
  26360. create_component(checkbox3.$$.fragment);
  26361. t31 = space();
  26362. create_component(checkbox4.$$.fragment);
  26363. t32 = space();
  26364. div9 = element("div");
  26365. t33 = space();
  26366. create_component(numberinput2.$$.fragment);
  26367. t34 = space();
  26368. create_component(numberinput3.$$.fragment);
  26369. t35 = space();
  26370. create_component(checkbox5.$$.fragment);
  26371. t36 = space();
  26372. create_component(numberinput4.$$.fragment);
  26373. t37 = space();
  26374. create_component(checkbox6.$$.fragment);
  26375. t38 = space();
  26376. div10 = element("div");
  26377. t39 = space();
  26378. fieldset2 = element("fieldset");
  26379. legend2 = element("legend");
  26380. legend2.textContent = "Behavior";
  26381. t41 = space();
  26382. div15 = element("div");
  26383. create_component(numberinput5.$$.fragment);
  26384. t42 = space();
  26385. create_component(numberinput6.$$.fragment);
  26386. t43 = space();
  26387. div12 = element("div");
  26388. t44 = space();
  26389. create_component(checkbox7.$$.fragment);
  26390. t45 = space();
  26391. create_component(numberinput7.$$.fragment);
  26392. t46 = space();
  26393. create_component(numberinput8.$$.fragment);
  26394. t47 = space();
  26395. create_component(numberinput9.$$.fragment);
  26396. t48 = space();
  26397. div13 = element("div");
  26398. t49 = space();
  26399. create_component(switchtoggle.$$.fragment);
  26400. t50 = space();
  26401. create_component(numberinput10.$$.fragment);
  26402. t51 = space();
  26403. div14 = element("div");
  26404. t52 = space();
  26405. create_component(checkbox8.$$.fragment);
  26406. t53 = space();
  26407. create_component(checkbox9.$$.fragment);
  26408. t54 = space();
  26409. create_component(checkbox10.$$.fragment);
  26410. t55 = space();
  26411. create_component(checkbox11.$$.fragment);
  26412. t56 = space();
  26413. create_component(checkbox12.$$.fragment);
  26414. attr(button0, "class", "activate-layer svelte-ese-1ipnpu1");
  26415. attr(button0, "type", "button");
  26416. attr(input, "class", "file-input flex3");
  26417. attr(input, "list", "dblist");
  26418. attr(input, "placeholder", localize("SEQUENCER.Player.PathInput"));
  26419. attr(input, "type", "text");
  26420. attr(button1, "class", "custom-file-picker small-button svelte-ese-1ipnpu1");
  26421. attr(button1, "type", "button");
  26422. attr(datalist, "id", "dblist");
  26423. attr(div0, "class", "file-settings svelte-ese-1ipnpu1");
  26424. attr(label, "for", "user-select");
  26425. option0.selected = true;
  26426. option0.__value = "all";
  26427. option0.value = option0.__value;
  26428. attr(select0, "class", "user-select");
  26429. attr(select0, "id", "user-select");
  26430. select0.multiple = true;
  26431. if (
  26432. /*$userStore*/
  26433. ctx[3] === void 0
  26434. )
  26435. add_render_callback(() => (
  26436. /*select0_change_handler*/
  26437. ctx[11].call(select0)
  26438. ));
  26439. attr(div1, "class", "user-settings flexcol svelte-ese-1ipnpu1");
  26440. attr(div2, "class", "row w-100");
  26441. option1.__value = "default";
  26442. option1.value = option1.__value;
  26443. attr(select1, "class", "preset-select svelte-ese-1ipnpu1");
  26444. if (
  26445. /*selectedPreset*/
  26446. ctx[2] === void 0
  26447. )
  26448. add_render_callback(() => (
  26449. /*select1_change_handler*/
  26450. ctx[12].call(select1)
  26451. ));
  26452. attr(button2, "class", "save-preset small-button svelte-ese-1ipnpu1");
  26453. attr(button2, "data-tooltip", "Save Preset");
  26454. attr(button2, "type", "button");
  26455. attr(i2, "class", "fas fa-copy svelte-ese-1ipnpu1");
  26456. attr(button3, "class", "copy-preset small-button svelte-ese-1ipnpu1");
  26457. attr(button3, "data-tooltip", "Copy Preset");
  26458. button3.disabled = button3_disabled_value = /*selectedPreset*/
  26459. ctx[2] === "default";
  26460. attr(button3, "type", "button");
  26461. attr(i3, "class", "fas fa-times svelte-ese-1ipnpu1");
  26462. attr(button4, "class", "delete-preset small-button svelte-ese-1ipnpu1");
  26463. attr(button4, "data-tooltip", "Delete Preset");
  26464. button4.disabled = button4_disabled_value = /*selectedPreset*/
  26465. ctx[2] === "default";
  26466. attr(button4, "type", "button");
  26467. attr(div3, "class", "preset-container svelte-ese-1ipnpu1");
  26468. attr(div4, "class", "flexcol");
  26469. attr(div5, "class", "divider");
  26470. attr(div8, "class", "divider");
  26471. attr(div9, "class", "divider");
  26472. attr(div11, "class", "effect-transform-container");
  26473. attr(div12, "class", "divider");
  26474. attr(div13, "class", "divider");
  26475. attr(div14, "class", "divider");
  26476. attr(div15, "class", "effect-transform-container");
  26477. attr(div16, "class", "effect-player-container");
  26478. },
  26479. m(target, anchor) {
  26480. insert(target, div16, anchor);
  26481. append(div16, fieldset0);
  26482. append(fieldset0, legend0);
  26483. append(fieldset0, t1);
  26484. append(fieldset0, button0);
  26485. append(fieldset0, t3);
  26486. append(fieldset0, div0);
  26487. append(div0, input);
  26488. set_input_value(
  26489. input,
  26490. /*$fileStore*/
  26491. ctx[0]
  26492. );
  26493. append(div0, t4);
  26494. append(div0, button1);
  26495. append(div0, t5);
  26496. append(div0, datalist);
  26497. for (let i = 0; i < each_blocks_2.length; i += 1) {
  26498. each_blocks_2[i].m(datalist, null);
  26499. }
  26500. append(fieldset0, t6);
  26501. append(fieldset0, div1);
  26502. append(div1, label);
  26503. append(div1, t8);
  26504. append(div1, select0);
  26505. append(select0, option0);
  26506. for (let i = 0; i < each_blocks_1.length; i += 1) {
  26507. each_blocks_1[i].m(select0, null);
  26508. }
  26509. select_options(
  26510. select0,
  26511. /*$userStore*/
  26512. ctx[3]
  26513. );
  26514. append(fieldset0, t10);
  26515. append(fieldset0, div4);
  26516. append(div4, div2);
  26517. append(div4, t12);
  26518. append(div4, div3);
  26519. append(div3, select1);
  26520. append(select1, option1);
  26521. for (let i = 0; i < each_blocks.length; i += 1) {
  26522. each_blocks[i].m(select1, null);
  26523. }
  26524. select_option(
  26525. select1,
  26526. /*selectedPreset*/
  26527. ctx[2]
  26528. );
  26529. append(div3, t14);
  26530. append(div3, button2);
  26531. append(div3, t15);
  26532. append(div3, button3);
  26533. append(button3, i2);
  26534. append(div3, t16);
  26535. append(div3, button4);
  26536. append(button4, i3);
  26537. append(div16, t17);
  26538. append(div16, fieldset1);
  26539. append(fieldset1, legend1);
  26540. append(fieldset1, t19);
  26541. append(fieldset1, div11);
  26542. mount_component(sliderinput0, div11, null);
  26543. append(div11, t20);
  26544. mount_component(numberinput0, div11, null);
  26545. append(div11, t21);
  26546. mount_component(numberinput1, div11, null);
  26547. append(div11, t22);
  26548. append(div11, div5);
  26549. append(div11, t23);
  26550. mount_component(sliderinput1, div11, null);
  26551. append(div11, t24);
  26552. append(div11, div6);
  26553. append(div11, t25);
  26554. mount_component(checkbox0, div11, null);
  26555. append(div11, t26);
  26556. append(div11, div7);
  26557. append(div11, t27);
  26558. append(div11, div8);
  26559. append(div11, t28);
  26560. mount_component(checkbox1, div11, null);
  26561. append(div11, t29);
  26562. mount_component(checkbox2, div11, null);
  26563. append(div11, t30);
  26564. mount_component(checkbox3, div11, null);
  26565. append(div11, t31);
  26566. mount_component(checkbox4, div11, null);
  26567. append(div11, t32);
  26568. append(div11, div9);
  26569. append(div11, t33);
  26570. mount_component(numberinput2, div11, null);
  26571. append(div11, t34);
  26572. mount_component(numberinput3, div11, null);
  26573. append(div11, t35);
  26574. mount_component(checkbox5, div11, null);
  26575. append(div11, t36);
  26576. mount_component(numberinput4, div11, null);
  26577. append(div11, t37);
  26578. mount_component(checkbox6, div11, null);
  26579. append(div11, t38);
  26580. append(div11, div10);
  26581. append(div16, t39);
  26582. append(div16, fieldset2);
  26583. append(fieldset2, legend2);
  26584. append(fieldset2, t41);
  26585. append(fieldset2, div15);
  26586. mount_component(numberinput5, div15, null);
  26587. append(div15, t42);
  26588. mount_component(numberinput6, div15, null);
  26589. append(div15, t43);
  26590. append(div15, div12);
  26591. append(div15, t44);
  26592. mount_component(checkbox7, div15, null);
  26593. append(div15, t45);
  26594. mount_component(numberinput7, div15, null);
  26595. append(div15, t46);
  26596. mount_component(numberinput8, div15, null);
  26597. append(div15, t47);
  26598. mount_component(numberinput9, div15, null);
  26599. append(div15, t48);
  26600. append(div15, div13);
  26601. append(div15, t49);
  26602. mount_component(switchtoggle, div15, null);
  26603. append(div15, t50);
  26604. mount_component(numberinput10, div15, null);
  26605. append(div15, t51);
  26606. append(div15, div14);
  26607. append(div15, t52);
  26608. mount_component(checkbox8, div15, null);
  26609. append(div15, t53);
  26610. mount_component(checkbox9, div15, null);
  26611. append(div15, t54);
  26612. mount_component(checkbox10, div15, null);
  26613. append(div15, t55);
  26614. mount_component(checkbox11, div15, null);
  26615. append(div15, t56);
  26616. mount_component(checkbox12, div15, null);
  26617. current = true;
  26618. if (!mounted) {
  26619. dispose = [
  26620. listen(
  26621. button0,
  26622. "click",
  26623. /*click_handler*/
  26624. ctx[9]
  26625. ),
  26626. listen(
  26627. input,
  26628. "input",
  26629. /*input_input_handler*/
  26630. ctx[10]
  26631. ),
  26632. listen(
  26633. button1,
  26634. "click",
  26635. /*handleClick*/
  26636. ctx[7]
  26637. ),
  26638. listen(
  26639. select0,
  26640. "change",
  26641. /*select0_change_handler*/
  26642. ctx[11]
  26643. ),
  26644. listen(
  26645. select1,
  26646. "change",
  26647. /*select1_change_handler*/
  26648. ctx[12]
  26649. ),
  26650. listen(
  26651. select1,
  26652. "change",
  26653. /*change_handler*/
  26654. ctx[13]
  26655. ),
  26656. listen(
  26657. button2,
  26658. "click",
  26659. /*click_handler_1*/
  26660. ctx[14]
  26661. ),
  26662. listen(
  26663. button3,
  26664. "click",
  26665. /*click_handler_2*/
  26666. ctx[15]
  26667. ),
  26668. listen(
  26669. button4,
  26670. "click",
  26671. /*click_handler_3*/
  26672. ctx[16]
  26673. )
  26674. ];
  26675. mounted = true;
  26676. }
  26677. },
  26678. p(ctx2, [dirty]) {
  26679. if (dirty & /*$fileStore*/
  26680. 1 && input.value !== /*$fileStore*/
  26681. ctx2[0]) {
  26682. set_input_value(
  26683. input,
  26684. /*$fileStore*/
  26685. ctx2[0]
  26686. );
  26687. }
  26688. if (dirty & /*suggestions*/
  26689. 2) {
  26690. each_value_2 = /*suggestions*/
  26691. ctx2[1];
  26692. let i;
  26693. for (i = 0; i < each_value_2.length; i += 1) {
  26694. const child_ctx = get_each_context_2(ctx2, each_value_2, i);
  26695. if (each_blocks_2[i]) {
  26696. each_blocks_2[i].p(child_ctx, dirty);
  26697. } else {
  26698. each_blocks_2[i] = create_each_block_2(child_ctx);
  26699. each_blocks_2[i].c();
  26700. each_blocks_2[i].m(datalist, null);
  26701. }
  26702. }
  26703. for (; i < each_blocks_2.length; i += 1) {
  26704. each_blocks_2[i].d(1);
  26705. }
  26706. each_blocks_2.length = each_value_2.length;
  26707. }
  26708. if (dirty & /*users*/
  26709. 32) {
  26710. each_value_1 = /*users*/
  26711. ctx2[5];
  26712. 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);
  26713. }
  26714. if (dirty & /*$userStore, users*/
  26715. 40) {
  26716. select_options(
  26717. select0,
  26718. /*$userStore*/
  26719. ctx2[3]
  26720. );
  26721. }
  26722. if (dirty & /*presets*/
  26723. 256) {
  26724. each_value = /*presets*/
  26725. ctx2[8];
  26726. let i;
  26727. for (i = 0; i < each_value.length; i += 1) {
  26728. const child_ctx = get_each_context$2(ctx2, each_value, i);
  26729. if (each_blocks[i]) {
  26730. each_blocks[i].p(child_ctx, dirty);
  26731. } else {
  26732. each_blocks[i] = create_each_block$2(child_ctx);
  26733. each_blocks[i].c();
  26734. each_blocks[i].m(select1, null);
  26735. }
  26736. }
  26737. for (; i < each_blocks.length; i += 1) {
  26738. each_blocks[i].d(1);
  26739. }
  26740. each_blocks.length = each_value.length;
  26741. }
  26742. if (dirty & /*selectedPreset, presets*/
  26743. 260) {
  26744. select_option(
  26745. select1,
  26746. /*selectedPreset*/
  26747. ctx2[2]
  26748. );
  26749. }
  26750. if (!current || dirty & /*selectedPreset, presets*/
  26751. 260 && button3_disabled_value !== (button3_disabled_value = /*selectedPreset*/
  26752. ctx2[2] === "default")) {
  26753. button3.disabled = button3_disabled_value;
  26754. }
  26755. if (!current || dirty & /*selectedPreset, presets*/
  26756. 260 && button4_disabled_value !== (button4_disabled_value = /*selectedPreset*/
  26757. ctx2[2] === "default")) {
  26758. button4.disabled = button4_disabled_value;
  26759. }
  26760. },
  26761. i(local) {
  26762. if (current)
  26763. return;
  26764. transition_in(sliderinput0.$$.fragment, local);
  26765. transition_in(numberinput0.$$.fragment, local);
  26766. transition_in(numberinput1.$$.fragment, local);
  26767. transition_in(sliderinput1.$$.fragment, local);
  26768. transition_in(checkbox0.$$.fragment, local);
  26769. transition_in(checkbox1.$$.fragment, local);
  26770. transition_in(checkbox2.$$.fragment, local);
  26771. transition_in(checkbox3.$$.fragment, local);
  26772. transition_in(checkbox4.$$.fragment, local);
  26773. transition_in(numberinput2.$$.fragment, local);
  26774. transition_in(numberinput3.$$.fragment, local);
  26775. transition_in(checkbox5.$$.fragment, local);
  26776. transition_in(numberinput4.$$.fragment, local);
  26777. transition_in(checkbox6.$$.fragment, local);
  26778. transition_in(numberinput5.$$.fragment, local);
  26779. transition_in(numberinput6.$$.fragment, local);
  26780. transition_in(checkbox7.$$.fragment, local);
  26781. transition_in(numberinput7.$$.fragment, local);
  26782. transition_in(numberinput8.$$.fragment, local);
  26783. transition_in(numberinput9.$$.fragment, local);
  26784. transition_in(switchtoggle.$$.fragment, local);
  26785. transition_in(numberinput10.$$.fragment, local);
  26786. transition_in(checkbox8.$$.fragment, local);
  26787. transition_in(checkbox9.$$.fragment, local);
  26788. transition_in(checkbox10.$$.fragment, local);
  26789. transition_in(checkbox11.$$.fragment, local);
  26790. transition_in(checkbox12.$$.fragment, local);
  26791. current = true;
  26792. },
  26793. o(local) {
  26794. transition_out(sliderinput0.$$.fragment, local);
  26795. transition_out(numberinput0.$$.fragment, local);
  26796. transition_out(numberinput1.$$.fragment, local);
  26797. transition_out(sliderinput1.$$.fragment, local);
  26798. transition_out(checkbox0.$$.fragment, local);
  26799. transition_out(checkbox1.$$.fragment, local);
  26800. transition_out(checkbox2.$$.fragment, local);
  26801. transition_out(checkbox3.$$.fragment, local);
  26802. transition_out(checkbox4.$$.fragment, local);
  26803. transition_out(numberinput2.$$.fragment, local);
  26804. transition_out(numberinput3.$$.fragment, local);
  26805. transition_out(checkbox5.$$.fragment, local);
  26806. transition_out(numberinput4.$$.fragment, local);
  26807. transition_out(checkbox6.$$.fragment, local);
  26808. transition_out(numberinput5.$$.fragment, local);
  26809. transition_out(numberinput6.$$.fragment, local);
  26810. transition_out(checkbox7.$$.fragment, local);
  26811. transition_out(numberinput7.$$.fragment, local);
  26812. transition_out(numberinput8.$$.fragment, local);
  26813. transition_out(numberinput9.$$.fragment, local);
  26814. transition_out(switchtoggle.$$.fragment, local);
  26815. transition_out(numberinput10.$$.fragment, local);
  26816. transition_out(checkbox8.$$.fragment, local);
  26817. transition_out(checkbox9.$$.fragment, local);
  26818. transition_out(checkbox10.$$.fragment, local);
  26819. transition_out(checkbox11.$$.fragment, local);
  26820. transition_out(checkbox12.$$.fragment, local);
  26821. current = false;
  26822. },
  26823. d(detaching) {
  26824. if (detaching)
  26825. detach(div16);
  26826. destroy_each(each_blocks_2, detaching);
  26827. for (let i = 0; i < each_blocks_1.length; i += 1) {
  26828. each_blocks_1[i].d();
  26829. }
  26830. destroy_each(each_blocks, detaching);
  26831. destroy_component(sliderinput0);
  26832. destroy_component(numberinput0);
  26833. destroy_component(numberinput1);
  26834. destroy_component(sliderinput1);
  26835. destroy_component(checkbox0);
  26836. destroy_component(checkbox1);
  26837. destroy_component(checkbox2);
  26838. destroy_component(checkbox3);
  26839. destroy_component(checkbox4);
  26840. destroy_component(numberinput2);
  26841. destroy_component(numberinput3);
  26842. destroy_component(checkbox5);
  26843. destroy_component(numberinput4);
  26844. destroy_component(checkbox6);
  26845. destroy_component(numberinput5);
  26846. destroy_component(numberinput6);
  26847. destroy_component(checkbox7);
  26848. destroy_component(numberinput7);
  26849. destroy_component(numberinput8);
  26850. destroy_component(numberinput9);
  26851. destroy_component(switchtoggle);
  26852. destroy_component(numberinput10);
  26853. destroy_component(checkbox8);
  26854. destroy_component(checkbox9);
  26855. destroy_component(checkbox10);
  26856. destroy_component(checkbox11);
  26857. destroy_component(checkbox12);
  26858. mounted = false;
  26859. run_all(dispose);
  26860. }
  26861. };
  26862. }
  26863. let lastInput = "";
  26864. async function activateLayer() {
  26865. ui.controls.initialize({
  26866. control: "sequencer",
  26867. tool: "play-effect"
  26868. });
  26869. canvas.sequencerInterfaceLayer.activate({ tool: "play-effect" });
  26870. }
  26871. function instance$5($$self, $$props, $$invalidate) {
  26872. let $fileStore;
  26873. let $userStore;
  26874. const fileStore = PlayerSettings.file.store;
  26875. component_subscribe($$self, fileStore, (value) => $$invalidate(0, $fileStore = value));
  26876. const users2 = game.users.filter((user) => user.active);
  26877. const userStore = PlayerSettings.users.store;
  26878. component_subscribe($$self, userStore, (value) => $$invalidate(3, $userStore = value));
  26879. let lastResults = [];
  26880. let suggestions = [];
  26881. const searchDebounce = foundry.utils.debounce(
  26882. () => {
  26883. const fileInput = get_store_value(fileStore).toLowerCase();
  26884. if (!fileInput) {
  26885. $$invalidate(1, suggestions = SequencerDatabase.publicModules);
  26886. return;
  26887. }
  26888. if (lastInput === fileInput)
  26889. return;
  26890. let results = SequencerDatabase.searchFor(fileInput);
  26891. if (lastResults.equals(results))
  26892. return;
  26893. lastResults = foundry.utils.duplicate(results);
  26894. if (results.length === 1 && results[0].startsWith(fileInput))
  26895. return;
  26896. if (results.length > 100) {
  26897. results = results.slice(0, 100);
  26898. }
  26899. $$invalidate(1, suggestions = foundry.utils.duplicate(results));
  26900. },
  26901. 200
  26902. );
  26903. let filePicker = false;
  26904. function handleClick() {
  26905. if (!filePicker) {
  26906. const file = get_store_value(fileStore);
  26907. const current = SequencerDatabase.getEntry(file, { softFail: true }) ? file : "";
  26908. filePicker = new FilePicker({
  26909. type: "imageVideo",
  26910. current,
  26911. callback: (path) => {
  26912. fileStore.set(path);
  26913. filePicker = false;
  26914. }
  26915. });
  26916. }
  26917. filePicker.render(true, { focus: true });
  26918. }
  26919. const presets = PlayerSettings.getPresets();
  26920. let selectedPreset = "default";
  26921. const click_handler = () => activateLayer();
  26922. function input_input_handler() {
  26923. $fileStore = this.value;
  26924. fileStore.set($fileStore);
  26925. }
  26926. function select0_change_handler() {
  26927. $userStore = select_multiple_value(this);
  26928. userStore.set($userStore);
  26929. $$invalidate(5, users2);
  26930. }
  26931. function select1_change_handler() {
  26932. selectedPreset = select_value(this);
  26933. $$invalidate(2, selectedPreset);
  26934. $$invalidate(8, presets);
  26935. }
  26936. const change_handler = () => PlayerSettings.loadPreset(selectedPreset);
  26937. const click_handler_1 = () => PlayerSettings.savePreset(selectedPreset);
  26938. const click_handler_2 = () => PlayerSettings.copyPreset(selectedPreset);
  26939. const click_handler_3 = () => PlayerSettings.deletePreset(selectedPreset);
  26940. $$self.$$.update = () => {
  26941. if ($$self.$$.dirty & /*$fileStore*/
  26942. 1) {
  26943. searchDebounce($fileStore);
  26944. }
  26945. };
  26946. return [
  26947. $fileStore,
  26948. suggestions,
  26949. selectedPreset,
  26950. $userStore,
  26951. fileStore,
  26952. users2,
  26953. userStore,
  26954. handleClick,
  26955. presets,
  26956. click_handler,
  26957. input_input_handler,
  26958. select0_change_handler,
  26959. select1_change_handler,
  26960. change_handler,
  26961. click_handler_1,
  26962. click_handler_2,
  26963. click_handler_3
  26964. ];
  26965. }
  26966. class Player extends SvelteComponent {
  26967. constructor(options) {
  26968. super();
  26969. init(this, options, instance$5, create_fragment$5, safe_not_equal, {});
  26970. }
  26971. }
  26972. const SequenceStatus_svelte_svelte_type_style_lang = "";
  26973. function create_fragment$4(ctx) {
  26974. let i0;
  26975. let t;
  26976. let i1;
  26977. return {
  26978. c() {
  26979. i0 = element("i");
  26980. t = space();
  26981. i1 = element("i");
  26982. attr(i0, "class", "fa-solid background-circle svelte-ese-1wkm0y3");
  26983. toggle_class(
  26984. i0,
  26985. "invisible",
  26986. /*$status*/
  26987. ctx[1] === CONSTANTS.STATUS.READY
  26988. );
  26989. toggle_class(
  26990. i0,
  26991. "fa-arrow-right",
  26992. /*$status*/
  26993. ctx[1] === CONSTANTS.STATUS.RUNNING || /*$status*/
  26994. ctx[1] === CONSTANTS.STATUS.READY
  26995. );
  26996. toggle_class(
  26997. i0,
  26998. "fa-check",
  26999. /*$status*/
  27000. ctx[1] === CONSTANTS.STATUS.COMPLETE
  27001. );
  27002. toggle_class(
  27003. i0,
  27004. "fa-arrow-down",
  27005. /*$status*/
  27006. ctx[1] === CONSTANTS.STATUS.SKIPPED
  27007. );
  27008. toggle_class(
  27009. i0,
  27010. "fa-times",
  27011. /*$status*/
  27012. ctx[1] === CONSTANTS.STATUS.ABORTED
  27013. );
  27014. attr(i1, "class", "fa-solid ");
  27015. },
  27016. m(target, anchor) {
  27017. insert(target, i0, anchor);
  27018. insert(target, t, anchor);
  27019. insert(target, i1, anchor);
  27020. },
  27021. p(ctx2, [dirty]) {
  27022. if (dirty & /*$status, CONSTANTS*/
  27023. 2) {
  27024. toggle_class(
  27025. i0,
  27026. "invisible",
  27027. /*$status*/
  27028. ctx2[1] === CONSTANTS.STATUS.READY
  27029. );
  27030. }
  27031. if (dirty & /*$status, CONSTANTS*/
  27032. 2) {
  27033. toggle_class(
  27034. i0,
  27035. "fa-arrow-right",
  27036. /*$status*/
  27037. ctx2[1] === CONSTANTS.STATUS.RUNNING || /*$status*/
  27038. ctx2[1] === CONSTANTS.STATUS.READY
  27039. );
  27040. }
  27041. if (dirty & /*$status, CONSTANTS*/
  27042. 2) {
  27043. toggle_class(
  27044. i0,
  27045. "fa-check",
  27046. /*$status*/
  27047. ctx2[1] === CONSTANTS.STATUS.COMPLETE
  27048. );
  27049. }
  27050. if (dirty & /*$status, CONSTANTS*/
  27051. 2) {
  27052. toggle_class(
  27053. i0,
  27054. "fa-arrow-down",
  27055. /*$status*/
  27056. ctx2[1] === CONSTANTS.STATUS.SKIPPED
  27057. );
  27058. }
  27059. if (dirty & /*$status, CONSTANTS*/
  27060. 2) {
  27061. toggle_class(
  27062. i0,
  27063. "fa-times",
  27064. /*$status*/
  27065. ctx2[1] === CONSTANTS.STATUS.ABORTED
  27066. );
  27067. }
  27068. },
  27069. i: noop,
  27070. o: noop,
  27071. d(detaching) {
  27072. if (detaching)
  27073. detach(i0);
  27074. if (detaching)
  27075. detach(t);
  27076. if (detaching)
  27077. detach(i1);
  27078. }
  27079. };
  27080. }
  27081. function instance$4($$self, $$props, $$invalidate) {
  27082. let $status, $$unsubscribe_status = noop, $$subscribe_status = () => ($$unsubscribe_status(), $$unsubscribe_status = subscribe(status, ($$value) => $$invalidate(1, $status = $$value)), status);
  27083. $$self.$$.on_destroy.push(() => $$unsubscribe_status());
  27084. let { status } = $$props;
  27085. $$subscribe_status();
  27086. $$self.$$set = ($$props2) => {
  27087. if ("status" in $$props2)
  27088. $$subscribe_status($$invalidate(0, status = $$props2.status));
  27089. };
  27090. return [status, $status];
  27091. }
  27092. class SequenceStatus extends SvelteComponent {
  27093. constructor(options) {
  27094. super();
  27095. init(this, options, instance$4, create_fragment$4, safe_not_equal, { status: 0 });
  27096. }
  27097. }
  27098. const SequenceSection_svelte_svelte_type_style_lang = "";
  27099. function create_fragment$3(ctx) {
  27100. let div;
  27101. let span0;
  27102. let sequencestatus;
  27103. let t0;
  27104. let span1;
  27105. let t2;
  27106. let span2;
  27107. let a;
  27108. let current;
  27109. let mounted;
  27110. let dispose;
  27111. sequencestatus = new SequenceStatus({ props: { status: (
  27112. /*status*/
  27113. ctx[2]
  27114. ) } });
  27115. return {
  27116. c() {
  27117. div = element("div");
  27118. span0 = element("span");
  27119. create_component(sequencestatus.$$.fragment);
  27120. t0 = space();
  27121. span1 = element("span");
  27122. span1.textContent = `${/*sectionName*/
  27123. ctx[3]}`;
  27124. t2 = space();
  27125. span2 = element("span");
  27126. a = element("a");
  27127. a.innerHTML = `<i class="fas fa-stop"></i>`;
  27128. attr(span1, "class", "section-name svelte-ese-1ee9h3");
  27129. attr(a, "class", "svelte-ese-1ee9h3");
  27130. toggle_class(
  27131. a,
  27132. "section-done",
  27133. /*$status*/
  27134. ctx[1] > CONSTANTS.STATUS.READY
  27135. );
  27136. attr(span2, "class", "sequence-actions svelte-ese-1ee9h3");
  27137. attr(span2, "data-tooltip", localize("SEQUENCER.Sequences.AbortSection"));
  27138. attr(div, "class", "svelte-ese-1ee9h3");
  27139. },
  27140. m(target, anchor) {
  27141. insert(target, div, anchor);
  27142. append(div, span0);
  27143. mount_component(sequencestatus, span0, null);
  27144. append(div, t0);
  27145. append(div, span1);
  27146. append(div, t2);
  27147. append(div, span2);
  27148. append(span2, a);
  27149. current = true;
  27150. if (!mounted) {
  27151. dispose = listen(
  27152. a,
  27153. "click",
  27154. /*click_handler*/
  27155. ctx[4]
  27156. );
  27157. mounted = true;
  27158. }
  27159. },
  27160. p(ctx2, [dirty]) {
  27161. if (!current || dirty & /*$status, CONSTANTS*/
  27162. 2) {
  27163. toggle_class(
  27164. a,
  27165. "section-done",
  27166. /*$status*/
  27167. ctx2[1] > CONSTANTS.STATUS.READY
  27168. );
  27169. }
  27170. },
  27171. i(local) {
  27172. if (current)
  27173. return;
  27174. transition_in(sequencestatus.$$.fragment, local);
  27175. current = true;
  27176. },
  27177. o(local) {
  27178. transition_out(sequencestatus.$$.fragment, local);
  27179. current = false;
  27180. },
  27181. d(detaching) {
  27182. if (detaching)
  27183. detach(div);
  27184. destroy_component(sequencestatus);
  27185. mounted = false;
  27186. dispose();
  27187. }
  27188. };
  27189. }
  27190. function instance$3($$self, $$props, $$invalidate) {
  27191. let $status;
  27192. let { section } = $$props;
  27193. const status = section.sectionStatus;
  27194. component_subscribe($$self, status, (value) => $$invalidate(1, $status = value));
  27195. const sectionName = (section?.constructor?.niceName ? section?.constructor?.niceName : section.constructor.name) + (section?._name ? " - " + section._name : "");
  27196. const click_handler = () => {
  27197. section._abortSection();
  27198. };
  27199. $$self.$$set = ($$props2) => {
  27200. if ("section" in $$props2)
  27201. $$invalidate(0, section = $$props2.section);
  27202. };
  27203. return [section, $status, status, sectionName, click_handler];
  27204. }
  27205. class SequenceSection extends SvelteComponent {
  27206. constructor(options) {
  27207. super();
  27208. init(this, options, instance$3, create_fragment$3, safe_not_equal, { section: 0 });
  27209. }
  27210. }
  27211. const Sequence_svelte_svelte_type_style_lang = "";
  27212. function get_each_context$1(ctx, list, i) {
  27213. const child_ctx = ctx.slice();
  27214. child_ctx[6] = list[i];
  27215. return child_ctx;
  27216. }
  27217. function create_each_block$1(ctx) {
  27218. let sequencesection;
  27219. let current;
  27220. sequencesection = new SequenceSection({ props: { section: (
  27221. /*section*/
  27222. ctx[6]
  27223. ) } });
  27224. return {
  27225. c() {
  27226. create_component(sequencesection.$$.fragment);
  27227. },
  27228. m(target, anchor) {
  27229. mount_component(sequencesection, target, anchor);
  27230. current = true;
  27231. },
  27232. p(ctx2, dirty) {
  27233. const sequencesection_changes = {};
  27234. if (dirty & /*sequence*/
  27235. 1)
  27236. sequencesection_changes.section = /*section*/
  27237. ctx2[6];
  27238. sequencesection.$set(sequencesection_changes);
  27239. },
  27240. i(local) {
  27241. if (current)
  27242. return;
  27243. transition_in(sequencesection.$$.fragment, local);
  27244. current = true;
  27245. },
  27246. o(local) {
  27247. transition_out(sequencesection.$$.fragment, local);
  27248. current = false;
  27249. },
  27250. d(detaching) {
  27251. destroy_component(sequencesection, detaching);
  27252. }
  27253. };
  27254. }
  27255. function create_fragment$2(ctx) {
  27256. let div;
  27257. let span0;
  27258. let sequencestatus;
  27259. let t0;
  27260. let span1;
  27261. let t1;
  27262. let t2;
  27263. let t3_value = (
  27264. /*sequence*/
  27265. ctx[0].moduleName ? ` (${/*sequence*/
  27266. ctx[0].moduleName})` : ""
  27267. );
  27268. let t3;
  27269. let t4;
  27270. let span2;
  27271. let a0;
  27272. let i0;
  27273. let t5;
  27274. let a1;
  27275. let i1;
  27276. let t6;
  27277. let each_1_anchor;
  27278. let current;
  27279. let mounted;
  27280. let dispose;
  27281. sequencestatus = new SequenceStatus({ props: { status: (
  27282. /*status*/
  27283. ctx[3]
  27284. ) } });
  27285. let each_value = (
  27286. /*sequence*/
  27287. ctx[0].sections
  27288. );
  27289. let each_blocks = [];
  27290. for (let i = 0; i < each_value.length; i += 1) {
  27291. each_blocks[i] = create_each_block$1(get_each_context$1(ctx, each_value, i));
  27292. }
  27293. const out = (i) => transition_out(each_blocks[i], 1, 1, () => {
  27294. each_blocks[i] = null;
  27295. });
  27296. return {
  27297. c() {
  27298. div = element("div");
  27299. span0 = element("span");
  27300. create_component(sequencestatus.$$.fragment);
  27301. t0 = space();
  27302. span1 = element("span");
  27303. t1 = text$1("Sequence ");
  27304. t2 = text$1(
  27305. /*index*/
  27306. ctx[1]
  27307. );
  27308. t3 = text$1(t3_value);
  27309. t4 = space();
  27310. span2 = element("span");
  27311. a0 = element("a");
  27312. i0 = element("i");
  27313. t5 = space();
  27314. a1 = element("a");
  27315. i1 = element("i");
  27316. t6 = space();
  27317. for (let i = 0; i < each_blocks.length; i += 1) {
  27318. each_blocks[i].c();
  27319. }
  27320. each_1_anchor = empty();
  27321. attr(span1, "class", "sequence-name svelte-ese-1dcwqos");
  27322. attr(i0, "class", "fas fa-trash-can");
  27323. attr(a0, "class", "clear-sequence svelte-ese-1dcwqos");
  27324. attr(a0, "data-tooltip", localize("SEQUENCER.Sequences.Clear"));
  27325. toggle_class(
  27326. a0,
  27327. "sequence-done-show",
  27328. /*$status*/
  27329. ctx[2] > CONSTANTS.STATUS.RUNNING
  27330. );
  27331. attr(i1, "class", "fas fa-stop");
  27332. attr(a1, "data-tooltip", localize("SEQUENCER.Sequences.AbortSequence"));
  27333. attr(a1, "class", "svelte-ese-1dcwqos");
  27334. toggle_class(
  27335. a1,
  27336. "sequence-done-hide",
  27337. /*$status*/
  27338. ctx[2] > CONSTANTS.STATUS.RUNNING
  27339. );
  27340. attr(span2, "class", "sequence-actions svelte-ese-1dcwqos");
  27341. attr(div, "class", "sequence-name-container svelte-ese-1dcwqos");
  27342. },
  27343. m(target, anchor) {
  27344. insert(target, div, anchor);
  27345. append(div, span0);
  27346. mount_component(sequencestatus, span0, null);
  27347. append(div, t0);
  27348. append(div, span1);
  27349. append(span1, t1);
  27350. append(span1, t2);
  27351. append(span1, t3);
  27352. append(div, t4);
  27353. append(div, span2);
  27354. append(span2, a0);
  27355. append(a0, i0);
  27356. append(span2, t5);
  27357. append(span2, a1);
  27358. append(a1, i1);
  27359. insert(target, t6, anchor);
  27360. for (let i = 0; i < each_blocks.length; i += 1) {
  27361. each_blocks[i].m(target, anchor);
  27362. }
  27363. insert(target, each_1_anchor, anchor);
  27364. current = true;
  27365. if (!mounted) {
  27366. dispose = [
  27367. listen(
  27368. a0,
  27369. "click",
  27370. /*click_handler*/
  27371. ctx[4]
  27372. ),
  27373. listen(
  27374. a1,
  27375. "click",
  27376. /*click_handler_1*/
  27377. ctx[5]
  27378. )
  27379. ];
  27380. mounted = true;
  27381. }
  27382. },
  27383. p(ctx2, [dirty]) {
  27384. if (!current || dirty & /*index*/
  27385. 2)
  27386. set_data(
  27387. t2,
  27388. /*index*/
  27389. ctx2[1]
  27390. );
  27391. if ((!current || dirty & /*sequence*/
  27392. 1) && t3_value !== (t3_value = /*sequence*/
  27393. ctx2[0].moduleName ? ` (${/*sequence*/
  27394. ctx2[0].moduleName})` : ""))
  27395. set_data(t3, t3_value);
  27396. if (!current || dirty & /*$status, CONSTANTS*/
  27397. 4) {
  27398. toggle_class(
  27399. a0,
  27400. "sequence-done-show",
  27401. /*$status*/
  27402. ctx2[2] > CONSTANTS.STATUS.RUNNING
  27403. );
  27404. }
  27405. if (!current || dirty & /*$status, CONSTANTS*/
  27406. 4) {
  27407. toggle_class(
  27408. a1,
  27409. "sequence-done-hide",
  27410. /*$status*/
  27411. ctx2[2] > CONSTANTS.STATUS.RUNNING
  27412. );
  27413. }
  27414. if (dirty & /*sequence*/
  27415. 1) {
  27416. each_value = /*sequence*/
  27417. ctx2[0].sections;
  27418. let i;
  27419. for (i = 0; i < each_value.length; i += 1) {
  27420. const child_ctx = get_each_context$1(ctx2, each_value, i);
  27421. if (each_blocks[i]) {
  27422. each_blocks[i].p(child_ctx, dirty);
  27423. transition_in(each_blocks[i], 1);
  27424. } else {
  27425. each_blocks[i] = create_each_block$1(child_ctx);
  27426. each_blocks[i].c();
  27427. transition_in(each_blocks[i], 1);
  27428. each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor);
  27429. }
  27430. }
  27431. group_outros();
  27432. for (i = each_value.length; i < each_blocks.length; i += 1) {
  27433. out(i);
  27434. }
  27435. check_outros();
  27436. }
  27437. },
  27438. i(local) {
  27439. if (current)
  27440. return;
  27441. transition_in(sequencestatus.$$.fragment, local);
  27442. for (let i = 0; i < each_value.length; i += 1) {
  27443. transition_in(each_blocks[i]);
  27444. }
  27445. current = true;
  27446. },
  27447. o(local) {
  27448. transition_out(sequencestatus.$$.fragment, local);
  27449. each_blocks = each_blocks.filter(Boolean);
  27450. for (let i = 0; i < each_blocks.length; i += 1) {
  27451. transition_out(each_blocks[i]);
  27452. }
  27453. current = false;
  27454. },
  27455. d(detaching) {
  27456. if (detaching)
  27457. detach(div);
  27458. destroy_component(sequencestatus);
  27459. if (detaching)
  27460. detach(t6);
  27461. destroy_each(each_blocks, detaching);
  27462. if (detaching)
  27463. detach(each_1_anchor);
  27464. mounted = false;
  27465. run_all(dispose);
  27466. }
  27467. };
  27468. }
  27469. function instance$2($$self, $$props, $$invalidate) {
  27470. let $status;
  27471. let { sequence } = $$props;
  27472. let { index } = $$props;
  27473. const status = sequence.status;
  27474. component_subscribe($$self, status, (value) => $$invalidate(2, $status = value));
  27475. const click_handler = () => {
  27476. SequenceManager.RunningSequences.delete(sequence.id);
  27477. };
  27478. const click_handler_1 = () => {
  27479. sequence._abort();
  27480. };
  27481. $$self.$$set = ($$props2) => {
  27482. if ("sequence" in $$props2)
  27483. $$invalidate(0, sequence = $$props2.sequence);
  27484. if ("index" in $$props2)
  27485. $$invalidate(1, index = $$props2.index);
  27486. };
  27487. return [sequence, index, $status, status, click_handler, click_handler_1];
  27488. }
  27489. let Sequence$2 = class Sequence2 extends SvelteComponent {
  27490. constructor(options) {
  27491. super();
  27492. init(this, options, instance$2, create_fragment$2, safe_not_equal, { sequence: 0, index: 1 });
  27493. }
  27494. };
  27495. const Sequences_svelte_svelte_type_style_lang = "";
  27496. function get_each_context(ctx, list, i) {
  27497. const child_ctx = ctx.slice();
  27498. child_ctx[7] = list[i][0];
  27499. child_ctx[8] = list[i][1];
  27500. child_ctx[10] = i;
  27501. return child_ctx;
  27502. }
  27503. function create_else_block(ctx) {
  27504. let div0;
  27505. let button0;
  27506. let t1;
  27507. let button1;
  27508. let t3;
  27509. let div1;
  27510. let each_blocks = [];
  27511. let each_1_lookup = /* @__PURE__ */ new Map();
  27512. let current;
  27513. let mounted;
  27514. let dispose;
  27515. let each_value = (
  27516. /*runningSequences*/
  27517. ctx[0]
  27518. );
  27519. const get_key = (ctx2) => (
  27520. /*id*/
  27521. ctx2[7]
  27522. );
  27523. for (let i = 0; i < each_value.length; i += 1) {
  27524. let child_ctx = get_each_context(ctx, each_value, i);
  27525. let key = get_key(child_ctx);
  27526. each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx));
  27527. }
  27528. return {
  27529. c() {
  27530. div0 = element("div");
  27531. button0 = element("button");
  27532. button0.textContent = `${localize("SEQUENCER.Sequences.ClearFinished")}`;
  27533. t1 = space();
  27534. button1 = element("button");
  27535. button1.textContent = `${localize("SEQUENCER.Sequences.StopAll")}`;
  27536. t3 = space();
  27537. div1 = element("div");
  27538. for (let i = 0; i < each_blocks.length; i += 1) {
  27539. each_blocks[i].c();
  27540. }
  27541. attr(button0, "type", "button");
  27542. attr(button0, "class", "svelte-ese-1ismzan");
  27543. attr(button1, "type", "button");
  27544. attr(button1, "class", "svelte-ese-1ismzan");
  27545. attr(div0, "class", "sequence-button-header svelte-ese-1ismzan");
  27546. attr(div1, "class", "running-sequences svelte-ese-1ismzan");
  27547. },
  27548. m(target, anchor) {
  27549. insert(target, div0, anchor);
  27550. append(div0, button0);
  27551. append(div0, t1);
  27552. append(div0, button1);
  27553. insert(target, t3, anchor);
  27554. insert(target, div1, anchor);
  27555. for (let i = 0; i < each_blocks.length; i += 1) {
  27556. each_blocks[i].m(div1, null);
  27557. }
  27558. current = true;
  27559. if (!mounted) {
  27560. dispose = [
  27561. listen(
  27562. button0,
  27563. "click",
  27564. /*click_handler*/
  27565. ctx[3]
  27566. ),
  27567. listen(
  27568. button1,
  27569. "click",
  27570. /*click_handler_1*/
  27571. ctx[4]
  27572. )
  27573. ];
  27574. mounted = true;
  27575. }
  27576. },
  27577. p(ctx2, dirty) {
  27578. if (dirty & /*runningSequences*/
  27579. 1) {
  27580. each_value = /*runningSequences*/
  27581. ctx2[0];
  27582. group_outros();
  27583. 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);
  27584. check_outros();
  27585. }
  27586. },
  27587. i(local) {
  27588. if (current)
  27589. return;
  27590. for (let i = 0; i < each_value.length; i += 1) {
  27591. transition_in(each_blocks[i]);
  27592. }
  27593. current = true;
  27594. },
  27595. o(local) {
  27596. for (let i = 0; i < each_blocks.length; i += 1) {
  27597. transition_out(each_blocks[i]);
  27598. }
  27599. current = false;
  27600. },
  27601. d(detaching) {
  27602. if (detaching)
  27603. detach(div0);
  27604. if (detaching)
  27605. detach(t3);
  27606. if (detaching)
  27607. detach(div1);
  27608. for (let i = 0; i < each_blocks.length; i += 1) {
  27609. each_blocks[i].d();
  27610. }
  27611. mounted = false;
  27612. run_all(dispose);
  27613. }
  27614. };
  27615. }
  27616. function create_if_block(ctx) {
  27617. let div;
  27618. let h2;
  27619. return {
  27620. c() {
  27621. div = element("div");
  27622. h2 = element("h2");
  27623. h2.textContent = `${localize("SEQUENCER.Sequences.NoSequences")}`;
  27624. attr(div, "class", "no-sequences");
  27625. },
  27626. m(target, anchor) {
  27627. insert(target, div, anchor);
  27628. append(div, h2);
  27629. },
  27630. p: noop,
  27631. i: noop,
  27632. o: noop,
  27633. d(detaching) {
  27634. if (detaching)
  27635. detach(div);
  27636. }
  27637. };
  27638. }
  27639. function create_each_block(key_1, ctx) {
  27640. let first;
  27641. let sequence;
  27642. let current;
  27643. sequence = new Sequence$2({
  27644. props: {
  27645. sequence: (
  27646. /*sequence*/
  27647. ctx[8]
  27648. ),
  27649. index: (
  27650. /*index*/
  27651. ctx[10] + 1
  27652. )
  27653. }
  27654. });
  27655. return {
  27656. key: key_1,
  27657. first: null,
  27658. c() {
  27659. first = empty();
  27660. create_component(sequence.$$.fragment);
  27661. this.first = first;
  27662. },
  27663. m(target, anchor) {
  27664. insert(target, first, anchor);
  27665. mount_component(sequence, target, anchor);
  27666. current = true;
  27667. },
  27668. p(new_ctx, dirty) {
  27669. ctx = new_ctx;
  27670. const sequence_changes = {};
  27671. if (dirty & /*runningSequences*/
  27672. 1)
  27673. sequence_changes.sequence = /*sequence*/
  27674. ctx[8];
  27675. if (dirty & /*runningSequences*/
  27676. 1)
  27677. sequence_changes.index = /*index*/
  27678. ctx[10] + 1;
  27679. sequence.$set(sequence_changes);
  27680. },
  27681. i(local) {
  27682. if (current)
  27683. return;
  27684. transition_in(sequence.$$.fragment, local);
  27685. current = true;
  27686. },
  27687. o(local) {
  27688. transition_out(sequence.$$.fragment, local);
  27689. current = false;
  27690. },
  27691. d(detaching) {
  27692. if (detaching)
  27693. detach(first);
  27694. destroy_component(sequence, detaching);
  27695. }
  27696. };
  27697. }
  27698. function create_fragment$1(ctx) {
  27699. let div;
  27700. let current_block_type_index;
  27701. let if_block;
  27702. let current;
  27703. const if_block_creators = [create_if_block, create_else_block];
  27704. const if_blocks = [];
  27705. function select_block_type(ctx2, dirty) {
  27706. if (!/*runningSequences*/
  27707. ctx2[0].length)
  27708. return 0;
  27709. return 1;
  27710. }
  27711. current_block_type_index = select_block_type(ctx);
  27712. if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
  27713. return {
  27714. c() {
  27715. div = element("div");
  27716. if_block.c();
  27717. attr(div, "class", "sequence-container svelte-ese-1ismzan");
  27718. },
  27719. m(target, anchor) {
  27720. insert(target, div, anchor);
  27721. if_blocks[current_block_type_index].m(div, null);
  27722. current = true;
  27723. },
  27724. p(ctx2, [dirty]) {
  27725. let previous_block_index = current_block_type_index;
  27726. current_block_type_index = select_block_type(ctx2);
  27727. if (current_block_type_index === previous_block_index) {
  27728. if_blocks[current_block_type_index].p(ctx2, dirty);
  27729. } else {
  27730. group_outros();
  27731. transition_out(if_blocks[previous_block_index], 1, 1, () => {
  27732. if_blocks[previous_block_index] = null;
  27733. });
  27734. check_outros();
  27735. if_block = if_blocks[current_block_type_index];
  27736. if (!if_block) {
  27737. if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx2);
  27738. if_block.c();
  27739. } else {
  27740. if_block.p(ctx2, dirty);
  27741. }
  27742. transition_in(if_block, 1);
  27743. if_block.m(div, null);
  27744. }
  27745. },
  27746. i(local) {
  27747. if (current)
  27748. return;
  27749. transition_in(if_block);
  27750. current = true;
  27751. },
  27752. o(local) {
  27753. transition_out(if_block);
  27754. current = false;
  27755. },
  27756. d(detaching) {
  27757. if (detaching)
  27758. detach(div);
  27759. if_blocks[current_block_type_index].d();
  27760. }
  27761. };
  27762. }
  27763. function instance$1($$self, $$props, $$invalidate) {
  27764. let runningSequences;
  27765. let $RunningSequences;
  27766. const RunningSequences = SequenceManager.RunningSequences;
  27767. component_subscribe($$self, RunningSequences, (value) => $$invalidate(2, $RunningSequences = value));
  27768. onDestroy(() => {
  27769. SequenceManager.RunningSequences.clearFinishedSequences();
  27770. });
  27771. const click_handler = () => {
  27772. SequenceManager.RunningSequences.clearFinishedSequences();
  27773. };
  27774. const click_handler_1 = () => {
  27775. SequenceManager.RunningSequences.stopAll();
  27776. };
  27777. $$self.$$.update = () => {
  27778. if ($$self.$$.dirty & /*$RunningSequences*/
  27779. 4) {
  27780. Object.values($RunningSequences);
  27781. }
  27782. if ($$self.$$.dirty & /*$RunningSequences*/
  27783. 4) {
  27784. $$invalidate(0, runningSequences = Object.entries($RunningSequences));
  27785. }
  27786. };
  27787. return [
  27788. runningSequences,
  27789. RunningSequences,
  27790. $RunningSequences,
  27791. click_handler,
  27792. click_handler_1
  27793. ];
  27794. }
  27795. class Sequences extends SvelteComponent {
  27796. constructor(options) {
  27797. super();
  27798. init(this, options, instance$1, create_fragment$1, safe_not_equal, {});
  27799. }
  27800. }
  27801. function create_default_slot(ctx) {
  27802. let tabs_1;
  27803. let updating_activeTab;
  27804. let t;
  27805. let switch_instance;
  27806. let switch_instance_anchor;
  27807. let current;
  27808. function tabs_1_activeTab_binding(value) {
  27809. ctx[4](value);
  27810. }
  27811. let tabs_1_props = { tabs: (
  27812. /*tabs*/
  27813. ctx[3]
  27814. ) };
  27815. if (
  27816. /*activeTab*/
  27817. ctx[1] !== void 0
  27818. ) {
  27819. tabs_1_props.activeTab = /*activeTab*/
  27820. ctx[1];
  27821. }
  27822. tabs_1 = new Tabs({ props: tabs_1_props });
  27823. binding_callbacks.push(() => bind(tabs_1, "activeTab", tabs_1_activeTab_binding));
  27824. var switch_value = (
  27825. /*component*/
  27826. ctx[2]
  27827. );
  27828. function switch_props(ctx2) {
  27829. return {};
  27830. }
  27831. if (switch_value) {
  27832. switch_instance = construct_svelte_component(switch_value, switch_props());
  27833. }
  27834. return {
  27835. c() {
  27836. create_component(tabs_1.$$.fragment);
  27837. t = space();
  27838. if (switch_instance)
  27839. create_component(switch_instance.$$.fragment);
  27840. switch_instance_anchor = empty();
  27841. },
  27842. m(target, anchor) {
  27843. mount_component(tabs_1, target, anchor);
  27844. insert(target, t, anchor);
  27845. if (switch_instance)
  27846. mount_component(switch_instance, target, anchor);
  27847. insert(target, switch_instance_anchor, anchor);
  27848. current = true;
  27849. },
  27850. p(ctx2, dirty) {
  27851. const tabs_1_changes = {};
  27852. if (!updating_activeTab && dirty & /*activeTab*/
  27853. 2) {
  27854. updating_activeTab = true;
  27855. tabs_1_changes.activeTab = /*activeTab*/
  27856. ctx2[1];
  27857. add_flush_callback(() => updating_activeTab = false);
  27858. }
  27859. tabs_1.$set(tabs_1_changes);
  27860. if (switch_value !== (switch_value = /*component*/
  27861. ctx2[2])) {
  27862. if (switch_instance) {
  27863. group_outros();
  27864. const old_component = switch_instance;
  27865. transition_out(old_component.$$.fragment, 1, 0, () => {
  27866. destroy_component(old_component, 1);
  27867. });
  27868. check_outros();
  27869. }
  27870. if (switch_value) {
  27871. switch_instance = construct_svelte_component(switch_value, switch_props());
  27872. create_component(switch_instance.$$.fragment);
  27873. transition_in(switch_instance.$$.fragment, 1);
  27874. mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
  27875. } else {
  27876. switch_instance = null;
  27877. }
  27878. }
  27879. },
  27880. i(local) {
  27881. if (current)
  27882. return;
  27883. transition_in(tabs_1.$$.fragment, local);
  27884. if (switch_instance)
  27885. transition_in(switch_instance.$$.fragment, local);
  27886. current = true;
  27887. },
  27888. o(local) {
  27889. transition_out(tabs_1.$$.fragment, local);
  27890. if (switch_instance)
  27891. transition_out(switch_instance.$$.fragment, local);
  27892. current = false;
  27893. },
  27894. d(detaching) {
  27895. destroy_component(tabs_1, detaching);
  27896. if (detaching)
  27897. detach(t);
  27898. if (detaching)
  27899. detach(switch_instance_anchor);
  27900. if (switch_instance)
  27901. destroy_component(switch_instance, detaching);
  27902. }
  27903. };
  27904. }
  27905. function create_fragment(ctx) {
  27906. let applicationshell;
  27907. let updating_elementRoot;
  27908. let current;
  27909. function applicationshell_elementRoot_binding(value) {
  27910. ctx[5](value);
  27911. }
  27912. let applicationshell_props = {
  27913. $$slots: { default: [create_default_slot] },
  27914. $$scope: { ctx }
  27915. };
  27916. if (
  27917. /*elementRoot*/
  27918. ctx[0] !== void 0
  27919. ) {
  27920. applicationshell_props.elementRoot = /*elementRoot*/
  27921. ctx[0];
  27922. }
  27923. applicationshell = new ApplicationShell({ props: applicationshell_props });
  27924. binding_callbacks.push(() => bind(applicationshell, "elementRoot", applicationshell_elementRoot_binding));
  27925. return {
  27926. c() {
  27927. create_component(applicationshell.$$.fragment);
  27928. },
  27929. m(target, anchor) {
  27930. mount_component(applicationshell, target, anchor);
  27931. current = true;
  27932. },
  27933. p(ctx2, [dirty]) {
  27934. const applicationshell_changes = {};
  27935. if (dirty & /*$$scope, component, activeTab*/
  27936. 134) {
  27937. applicationshell_changes.$$scope = { dirty, ctx: ctx2 };
  27938. }
  27939. if (!updating_elementRoot && dirty & /*elementRoot*/
  27940. 1) {
  27941. updating_elementRoot = true;
  27942. applicationshell_changes.elementRoot = /*elementRoot*/
  27943. ctx2[0];
  27944. add_flush_callback(() => updating_elementRoot = false);
  27945. }
  27946. applicationshell.$set(applicationshell_changes);
  27947. },
  27948. i(local) {
  27949. if (current)
  27950. return;
  27951. transition_in(applicationshell.$$.fragment, local);
  27952. current = true;
  27953. },
  27954. o(local) {
  27955. transition_out(applicationshell.$$.fragment, local);
  27956. current = false;
  27957. },
  27958. d(detaching) {
  27959. destroy_component(applicationshell, detaching);
  27960. }
  27961. };
  27962. }
  27963. function instance($$self, $$props, $$invalidate) {
  27964. let component;
  27965. const { application } = getContext("#external");
  27966. let { elementRoot } = $$props;
  27967. let tabs = [
  27968. {
  27969. value: "player",
  27970. label: localize("SEQUENCER.Player.Title"),
  27971. icon: "fas fa-play-circle",
  27972. component: Player
  27973. },
  27974. {
  27975. value: "manager",
  27976. label: localize("SEQUENCER.Manager.Title"),
  27977. icon: "fas fa-film",
  27978. component: Manager
  27979. },
  27980. {
  27981. value: "sequences",
  27982. label: localize("SEQUENCER.Sequences.Title"),
  27983. icon: "fas fa-play",
  27984. component: Sequences
  27985. },
  27986. {
  27987. value: "howto",
  27988. label: localize("SEQUENCER.HowTo.Title"),
  27989. icon: "fas fa-chalkboard-teacher",
  27990. component: HowTo
  27991. }
  27992. ];
  27993. let activeTab = application.options.tab ?? "manager";
  27994. function tabs_1_activeTab_binding(value) {
  27995. activeTab = value;
  27996. $$invalidate(1, activeTab);
  27997. }
  27998. function applicationshell_elementRoot_binding(value) {
  27999. elementRoot = value;
  28000. $$invalidate(0, elementRoot);
  28001. }
  28002. $$self.$$set = ($$props2) => {
  28003. if ("elementRoot" in $$props2)
  28004. $$invalidate(0, elementRoot = $$props2.elementRoot);
  28005. };
  28006. $$self.$$.update = () => {
  28007. if ($$self.$$.dirty & /*activeTab*/
  28008. 2) {
  28009. $$invalidate(2, component = tabs.find((tab) => tab.value === activeTab).component);
  28010. }
  28011. };
  28012. return [
  28013. elementRoot,
  28014. activeTab,
  28015. component,
  28016. tabs,
  28017. tabs_1_activeTab_binding,
  28018. applicationshell_elementRoot_binding
  28019. ];
  28020. }
  28021. class Effects_ui_shell extends SvelteComponent {
  28022. constructor(options) {
  28023. super();
  28024. init(this, options, instance, create_fragment, safe_not_equal, { elementRoot: 0 });
  28025. }
  28026. get elementRoot() {
  28027. return this.$$.ctx[0];
  28028. }
  28029. set elementRoot(elementRoot) {
  28030. this.$$set({ elementRoot });
  28031. flush();
  28032. }
  28033. }
  28034. class EffectsUIApp extends SvelteApplication {
  28035. static get defaultOptions() {
  28036. return foundry.utils.mergeObject(super.defaultOptions, {
  28037. title: game.i18n.localize("SEQUENCER.ManagerUI"),
  28038. classes: ["dialog"],
  28039. width: "auto",
  28040. height: "auto",
  28041. top: 65,
  28042. left: 120,
  28043. resizable: false,
  28044. svelte: {
  28045. class: Effects_ui_shell,
  28046. target: document.body
  28047. }
  28048. });
  28049. }
  28050. static getActiveApp() {
  28051. return Object.values(ui.windows).find((app) => {
  28052. return app instanceof this && app._state > Application.RENDER_STATES.CLOSED;
  28053. });
  28054. }
  28055. static async show(options = {}) {
  28056. const existingApp = this.getActiveApp();
  28057. if (existingApp)
  28058. return existingApp.render(false, { focus: true });
  28059. return new Promise((resolve) => {
  28060. options.resolve = resolve;
  28061. new this(options).render(true, { focus: true });
  28062. });
  28063. }
  28064. }
  28065. function registerSettings() {
  28066. game.settings.register(CONSTANTS.MODULE_NAME, "enable-fix-pixi", {
  28067. name: "SEQUENCER.Setting.EnablePixiFix.Title",
  28068. hint: "SEQUENCER.Setting.EnablePixiFix.Label",
  28069. scope: "client",
  28070. config: true,
  28071. default: false,
  28072. requiresReload: true,
  28073. type: Boolean
  28074. });
  28075. game.settings.register(CONSTANTS.MODULE_NAME, "enable-global-fix-pixi", {
  28076. name: "SEQUENCER.Setting.EnableGlobalPixiFix.Title",
  28077. hint: "SEQUENCER.Setting.EnableGlobalPixiFix.Label",
  28078. scope: "client",
  28079. config: true,
  28080. default: false,
  28081. requiresReload: true,
  28082. type: Boolean
  28083. });
  28084. game.settings.register(CONSTANTS.MODULE_NAME, "enable-above-ui-screenspace", {
  28085. name: "SEQUENCER.Setting.EnableAboveUIScreenspace.Title",
  28086. hint: "SEQUENCER.Setting.EnableAboveUIScreenspace.Label",
  28087. scope: "client",
  28088. config: true,
  28089. default: true,
  28090. requiresReload: true,
  28091. type: Boolean
  28092. });
  28093. game.settings.register(CONSTANTS.MODULE_NAME, "debug", {
  28094. name: "SEQUENCER.Setting.Debug.Title",
  28095. hint: "SEQUENCER.Setting.Debug.Label",
  28096. scope: "client",
  28097. config: true,
  28098. default: false,
  28099. type: Boolean
  28100. });
  28101. game.settings.register(CONSTANTS.MODULE_NAME, "showSidebarTools", {
  28102. name: "SEQUENCER.Setting.ShowTools.Title",
  28103. hint: "SEQUENCER.Setting.ShowTools.Label",
  28104. scope: "client",
  28105. config: true,
  28106. default: true,
  28107. requiresReload: true,
  28108. type: Boolean
  28109. });
  28110. game.settings.register(CONSTANTS.MODULE_NAME, "showTokenSidebarTools", {
  28111. name: "SEQUENCER.Setting.ShowTokenTools.Title",
  28112. hint: "SEQUENCER.Setting.ShowTokenTools.Label",
  28113. scope: "client",
  28114. config: true,
  28115. default: true,
  28116. requiresReload: true,
  28117. type: Boolean
  28118. });
  28119. game.settings.register(CONSTANTS.MODULE_NAME, "effectsEnabled", {
  28120. name: "SEQUENCER.Setting.EnableEffects.Title",
  28121. hint: "SEQUENCER.Setting.EnableEffects.Label",
  28122. scope: "client",
  28123. config: true,
  28124. default: true,
  28125. requiresReload: true,
  28126. type: Boolean
  28127. });
  28128. game.settings.register(CONSTANTS.MODULE_NAME, "soundsEnabled", {
  28129. name: "SEQUENCER.Setting.EnableSounds.Title",
  28130. hint: "SEQUENCER.Setting.EnableSounds.Label",
  28131. scope: "client",
  28132. config: true,
  28133. default: true,
  28134. requiresReload: true,
  28135. type: Boolean
  28136. });
  28137. game.settings.register(CONSTANTS.MODULE_NAME, "user-effect-opacity", {
  28138. name: "SEQUENCER.Setting.ExternalEffectOpacity.Title",
  28139. hint: "SEQUENCER.Setting.ExternalEffectOpacity.Label",
  28140. scope: "client",
  28141. config: true,
  28142. default: 50,
  28143. type: Number,
  28144. range: {
  28145. min: 0,
  28146. max: 100,
  28147. step: 1
  28148. }
  28149. });
  28150. game.settings.register(CONSTANTS.MODULE_NAME, "db-list-view", {
  28151. scope: "client",
  28152. config: false,
  28153. default: false,
  28154. type: Boolean
  28155. });
  28156. const permissionLevels = [
  28157. game.i18n.localize("SEQUENCER.Permission.Player"),
  28158. game.i18n.localize("SEQUENCER.Permission.Trusted"),
  28159. game.i18n.localize("SEQUENCER.Permission.Assistant"),
  28160. game.i18n.localize("SEQUENCER.Permission.GM")
  28161. ];
  28162. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-effect-create", {
  28163. name: "SEQUENCER.Setting.Permission.EffectCreate.Title",
  28164. hint: "SEQUENCER.Setting.Permission.EffectCreate.Label",
  28165. scope: "world",
  28166. config: true,
  28167. default: 0,
  28168. type: Number,
  28169. choices: permissionLevels,
  28170. requiresReload: true
  28171. });
  28172. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-effect-delete", {
  28173. name: "SEQUENCER.Setting.Permission.EffectDelete.Title",
  28174. hint: "SEQUENCER.Setting.Permission.EffectDelete.Label",
  28175. scope: "world",
  28176. config: true,
  28177. default: 2,
  28178. type: Number,
  28179. choices: permissionLevels,
  28180. requiresReload: true
  28181. });
  28182. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-sound-create", {
  28183. name: "SEQUENCER.Setting.Permission.SoundCreate.Title",
  28184. hint: "SEQUENCER.Setting.Permission.SoundCreate.Label",
  28185. scope: "world",
  28186. config: true,
  28187. default: 0,
  28188. type: Number,
  28189. choices: permissionLevels,
  28190. requiresReload: true
  28191. });
  28192. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-preload", {
  28193. name: "SEQUENCER.Setting.Permission.PreloadClients.Title",
  28194. hint: "SEQUENCER.Setting.Permission.PreloadClients.Label",
  28195. scope: "world",
  28196. config: true,
  28197. default: 1,
  28198. type: Number,
  28199. choices: permissionLevels,
  28200. requiresReload: true
  28201. });
  28202. game.settings.register(CONSTANTS.MODULE_NAME, "permissions-sidebar-tools", {
  28203. name: "SEQUENCER.Setting.Permission.UseSidebarTools.Title",
  28204. hint: "SEQUENCER.Setting.Permission.UseSidebarTools.Label",
  28205. scope: "world",
  28206. config: true,
  28207. default: 0,
  28208. type: Number,
  28209. choices: permissionLevels,
  28210. requiresReload: true
  28211. });
  28212. game.settings.register(CONSTANTS.MODULE_NAME, "effectPresets", {
  28213. scope: "client",
  28214. default: {},
  28215. type: Object
  28216. });
  28217. Hooks.on("getSceneControlButtons", (controls) => {
  28218. if (!game.settings.get(CONSTANTS.MODULE_NAME, "showSidebarTools"))
  28219. return;
  28220. const selectTool = {
  28221. icon: "fas fa-expand",
  28222. name: "select-effect",
  28223. title: "SEQUENCER.SidebarButtons.Select",
  28224. visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools")
  28225. };
  28226. const playTool = {
  28227. icon: "fas fa-play",
  28228. name: "play-effect",
  28229. title: "SEQUENCER.SidebarButtons.Play",
  28230. visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
  28231. onClick: () => {
  28232. EffectsUIApp.show({ inFocus: true, tab: "player" });
  28233. }
  28234. };
  28235. const viewer = {
  28236. icon: "fas fa-film",
  28237. name: "effectviewer",
  28238. title: "SEQUENCER.SidebarButtons.Manager",
  28239. button: true,
  28240. visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
  28241. onClick: () => {
  28242. EffectsUIApp.show({ inFocus: true, tab: "manager" });
  28243. }
  28244. };
  28245. const database = {
  28246. icon: "fas fa-database",
  28247. name: "effectdatabase",
  28248. title: "SEQUENCER.SidebarButtons.Database",
  28249. button: true,
  28250. visible: user_can_do("permissions-sidebar-tools"),
  28251. onClick: () => {
  28252. DatabaseViewerApp.show();
  28253. }
  28254. };
  28255. controls.push({
  28256. name: CONSTANTS.MODULE_NAME,
  28257. title: "Sequencer Layer",
  28258. icon: "fas fa-list-ol",
  28259. layer: "sequencerInterfaceLayer",
  28260. visible: user_can_do("permissions-effect-create") && user_can_do("permissions-sidebar-tools"),
  28261. activeTool: "select-effect",
  28262. tools: [selectTool, playTool, database, viewer]
  28263. });
  28264. if (!game.settings.get(CONSTANTS.MODULE_NAME, "showTokenSidebarTools"))
  28265. return;
  28266. const bar = controls.find((c) => c.name === "token");
  28267. bar.tools.push(database);
  28268. bar.tools.push(viewer);
  28269. });
  28270. debug("Sequencer | Registered settings");
  28271. }
  28272. async function migrateSettings() {
  28273. const oldScreenspaceSetting = game.settings.storage.get("client").getItem("sequencer.disable-above-ui-screenspace");
  28274. if (oldScreenspaceSetting) {
  28275. const value = oldScreenspaceSetting === "true";
  28276. game.settings.storage.get("client").removeItem("sequencer.disable-above-ui-screenspace");
  28277. await game.settings.set(
  28278. CONSTANTS.MODULE_NAME,
  28279. "enable-above-ui-screenspace",
  28280. !value
  28281. );
  28282. }
  28283. }
  28284. function registerLayers() {
  28285. CONFIG.Canvas.layers = foundry.utils.mergeObject(Canvas.layers, {
  28286. sequencerEffects: {
  28287. layerClass: BaseEffectsLayer,
  28288. group: "primary"
  28289. },
  28290. sequencerInterfaceLayer: {
  28291. layerClass: SequencerInterfaceLayer,
  28292. group: "interface"
  28293. },
  28294. sequencerEffectsUILayer: {
  28295. layerClass: UIEffectsLayer,
  28296. group: "interface"
  28297. }
  28298. });
  28299. if (!Object.is(Canvas.layers, CONFIG.Canvas.layers)) {
  28300. const layers = Canvas.layers;
  28301. Object.defineProperty(Canvas, "layers", {
  28302. get: function() {
  28303. return foundry.utils.mergeObject(layers, CONFIG.Canvas.layers);
  28304. }
  28305. });
  28306. }
  28307. debug("Registered Layers");
  28308. }
  28309. const hotkeys = {
  28310. get _ready() {
  28311. return canvas.ready && canvas.sequencerInterfaceLayer.active;
  28312. },
  28313. playTool: {
  28314. playManySequencedDown: () => {
  28315. if (!hotkeys._ready)
  28316. return;
  28317. EffectPlayer.playManySequenced = true;
  28318. },
  28319. playManySequencedUp: () => {
  28320. if (!hotkeys._ready)
  28321. return;
  28322. EffectPlayer.playManySequenced = false;
  28323. if (!EffectPlayer.isActive)
  28324. return;
  28325. EffectPlayer.playManyUp();
  28326. },
  28327. playManyDown: () => {
  28328. if (!hotkeys._ready)
  28329. return;
  28330. EffectPlayer.playMany = true;
  28331. },
  28332. playManyUp: () => {
  28333. if (!hotkeys._ready)
  28334. return;
  28335. EffectPlayer.playMany = false;
  28336. if (!EffectPlayer.isActive)
  28337. return;
  28338. EffectPlayer.playManyUp();
  28339. },
  28340. attachToDown: () => {
  28341. if (!hotkeys._ready)
  28342. return;
  28343. PlayerSettings.attachTo.store.set(true);
  28344. PlayerSettings.stretchToAttach.store.set(true);
  28345. },
  28346. attachToUp: () => {
  28347. if (!hotkeys._ready)
  28348. return;
  28349. PlayerSettings.attachTo.store.set(false);
  28350. PlayerSettings.stretchToAttach.store.set(false);
  28351. }
  28352. },
  28353. selectTool: {
  28354. snapToGridDown: () => {
  28355. if (!hotkeys._ready)
  28356. return;
  28357. SelectionManager.snapToGrid = true;
  28358. },
  28359. snapToGridUp: () => {
  28360. if (!hotkeys._ready)
  28361. return;
  28362. SelectionManager.snapToGrid = false;
  28363. },
  28364. attachToTargetDown: () => {
  28365. if (!hotkeys._ready)
  28366. return;
  28367. if (!SelectionManager.isActive)
  28368. return;
  28369. PlayerSettings.attachTo.store.set(true);
  28370. PlayerSettings.stretchToAttach.store.set(true);
  28371. },
  28372. attachToTargetUp: () => {
  28373. if (!hotkeys._ready)
  28374. return;
  28375. PlayerSettings.attachTo.store.set(false);
  28376. PlayerSettings.stretchToAttach.store.set(false);
  28377. },
  28378. deleteDown: () => {
  28379. if (!hotkeys._ready)
  28380. return;
  28381. SelectionManager.delete();
  28382. }
  28383. }
  28384. };
  28385. function registerHotkeys() {
  28386. game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-control", {
  28387. name: "SEQUENCER.Hotkeys.PlayTool.Control",
  28388. editable: [{ key: "ControlLeft" }],
  28389. onDown: hotkeys.playTool.playManySequencedDown,
  28390. onUp: hotkeys.playTool.playManySequencedUp
  28391. });
  28392. game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-shift", {
  28393. name: "SEQUENCER.Hotkeys.PlayTool.Shift",
  28394. editable: [{ key: "ShiftLeft" }],
  28395. onDown: hotkeys.playTool.playManyDown,
  28396. onUp: hotkeys.playTool.playManyUp
  28397. });
  28398. game.keybindings.register(CONSTANTS.MODULE_NAME, "play-tool-hotkey-alt", {
  28399. name: "SEQUENCER.Hotkeys.PlayTool.Alt",
  28400. editable: [{ key: "AltLeft" }],
  28401. onDown: hotkeys.playTool.attachToDown,
  28402. onUp: hotkeys.playTool.attachToDown
  28403. });
  28404. game.keybindings.register(
  28405. CONSTANTS.MODULE_NAME,
  28406. "select-tool-hotkey-control",
  28407. {
  28408. name: "SEQUENCER.Hotkeys.SelectTool.Control",
  28409. editable: [{ key: "ControlLeft" }],
  28410. onDown: hotkeys.selectTool.snapToGridDown,
  28411. onUp: hotkeys.selectTool.snapToGridUp
  28412. }
  28413. );
  28414. game.keybindings.register(CONSTANTS.MODULE_NAME, "select-tool-hotkey-alt", {
  28415. name: "SEQUENCER.Hotkeys.SelectTool.Alt",
  28416. editable: [{ key: "AltLeft" }],
  28417. onDown: hotkeys.selectTool.attachToTargetDown,
  28418. onUp: hotkeys.selectTool.attachToTargetUp
  28419. });
  28420. game.keybindings.register(
  28421. CONSTANTS.MODULE_NAME,
  28422. "select-tool-hotkey-delete",
  28423. {
  28424. name: "SEQUENCER.Hotkeys.SelectTool.Delete",
  28425. editable: [{ key: "Delete" }],
  28426. onDown: hotkeys.selectTool.deleteDown
  28427. }
  28428. );
  28429. }
  28430. async function registerTypes(register) {
  28431. fetch("modules/sequencer/typings/types.d.ts").then((response) => response.text()).then((content) => register("sequencer/types.d.ts", content));
  28432. }
  28433. const presetMap = /* @__PURE__ */ new Map();
  28434. class SequencerPresets {
  28435. /**
  28436. * Adds a preset that can then be used in sequences
  28437. *
  28438. * @param {string} inName
  28439. * @param {Function} inFunction
  28440. * @param {boolean} [overwrite=false] overwrite
  28441. * @returns {Map<string, Function>}
  28442. */
  28443. static add(inName, inFunction, overwrite = false) {
  28444. if (typeof inName !== "string") {
  28445. throw custom_error(
  28446. "Sequencer",
  28447. `SequencerPresets | inName must be of type string`
  28448. );
  28449. }
  28450. if (!is_function$1(inFunction)) {
  28451. throw custom_error(
  28452. "Sequencer",
  28453. `SequencerPresets | inFunction must be of type function`
  28454. );
  28455. }
  28456. if (presetMap.get(inName) && !overwrite) {
  28457. throw custom_error(
  28458. "Sequencer",
  28459. `SequencerPresets | Preset "${inName}" already exists`
  28460. );
  28461. }
  28462. presetMap.set(inName, inFunction);
  28463. debug(`Sequencer | Presets | Added "${inName}" preset`);
  28464. return presetMap;
  28465. }
  28466. /**
  28467. * Retrieves all presets
  28468. *
  28469. * @returns {Map<string, Function>}
  28470. */
  28471. static getAll() {
  28472. return presetMap;
  28473. }
  28474. /**
  28475. * Retrieves preset based on its name
  28476. *
  28477. * @param {string} name
  28478. * @returns {Function}
  28479. */
  28480. static get(name) {
  28481. return presetMap.get(name);
  28482. }
  28483. }
  28484. class Section {
  28485. constructor(inSequence) {
  28486. this.sequence = inSequence;
  28487. this._applyTraits();
  28488. this._sectionStatus = writable$1(CONSTANTS.STATUS.READY);
  28489. this._playIf = true;
  28490. this._waitUntilFinished = false;
  28491. this._async = false;
  28492. this._waitUntilFinishedDelay = [0, 0];
  28493. this._repetitions = 1;
  28494. this._currentRepetition = 0;
  28495. this._repeatDelayMin = 0;
  28496. this._repeatDelayMax = 0;
  28497. this._repeatDelay = 0;
  28498. this._delayMin = 0;
  28499. this._delayMax = 0;
  28500. this._basicDelay = 0;
  28501. this._duration = false;
  28502. }
  28503. static niceName = "Section";
  28504. /**
  28505. * @protected
  28506. */
  28507. get _shouldAsync() {
  28508. return this._async || this._waitAnyway;
  28509. }
  28510. /**
  28511. * @protected
  28512. */
  28513. get shouldWaitUntilFinished() {
  28514. return this._waitUntilFinished || this._waitAnyway;
  28515. }
  28516. /**
  28517. * @protected
  28518. */
  28519. get _waitAnyway() {
  28520. return (this._async || this._waitUntilFinished) && this._isLastRepetition || this._isLastRepetition && this._isLastSection;
  28521. }
  28522. /**
  28523. * @protected
  28524. */
  28525. get _isLastSection() {
  28526. return this.sequence.sections.length - 1 === this.sequence.sections.indexOf(this);
  28527. }
  28528. /** ------------------------------------------------------------------------------------------------------------------------------ *
  28529. * Methods below this point should NOT be overridden by child instances of the class, they are integral to the sequence functioning
  28530. * ------------------------------------------------------------------------------------------------------------------------------- */
  28531. /**
  28532. * @protected
  28533. */
  28534. get _isLastRepetition() {
  28535. return this._repetitions === 1 || this._repetitions === this._currentRepetition + 1;
  28536. }
  28537. /**
  28538. * @protected
  28539. */
  28540. get _currentWaitTime() {
  28541. let waitUntilFinishedDelay = this._waitAnyway ? random_int_between(...this._waitUntilFinishedDelay) : 0;
  28542. return waitUntilFinishedDelay + this._repeatDelay;
  28543. }
  28544. /**
  28545. * Method overwritten by inheriting classes, which is called just before the "run" method is called (see below)
  28546. *
  28547. * @returns {Promise<void>}
  28548. * @protected
  28549. */
  28550. async preRun() {
  28551. }
  28552. /**
  28553. * Method overwritten by inheriting classes, which is called when this section is executed by the Sequence
  28554. *
  28555. * @returns {Promise<void>}
  28556. * @protected
  28557. */
  28558. async run() {
  28559. }
  28560. /**
  28561. * Method overwritten by inheriting classes, which is called when this section is serialized by the Sequence
  28562. *
  28563. * @returns {object}
  28564. * @private
  28565. */
  28566. async _serialize() {
  28567. return {
  28568. async: this._async,
  28569. delay: [this._delayMin, this._delayMax],
  28570. waitUntilFinished: this._waitUntilFinished,
  28571. waitUntilFinishedDelay: this._waitUntilFinishedDelay,
  28572. repetitions: this._repetitions,
  28573. repetitionsDelay: [this._repeatDelayMin, this._repeatDelayMax]
  28574. };
  28575. }
  28576. _deserialize(data) {
  28577. this._async = data.async;
  28578. this._waitUntilFinished = data.waitUntilFinished;
  28579. this._waitUntilFinishedDelay = data.waitUntilFinishedDelay;
  28580. this._repetitions = data.repetitions;
  28581. this._repeatDelayMin = data.repetitionsDelay[0];
  28582. this._repeatDelayMax = data.repetitionsDelay[1];
  28583. return this;
  28584. }
  28585. /**
  28586. * Method overwritten by inheriting classes, which stores data or prepares data before the Sequence executes it (see EffectsSection)
  28587. *
  28588. * @protected
  28589. */
  28590. async _initialize() {
  28591. }
  28592. /**
  28593. * Method overwritten by inheriting classes. Inheriting classes uses the following to apply traits to themselves:
  28594. * - Object.assign(this.constructor.prototype, trait)
  28595. *
  28596. * @protected
  28597. */
  28598. _applyTraits() {
  28599. }
  28600. /**
  28601. * Causes the section to be repeated n amount of times, with an optional delay. If given inRepeatDelayMin
  28602. * and inRepeatDelayMax, a random repetition delay will be picked for every repetition
  28603. *
  28604. * @param {number} inRepetitions
  28605. * @param {number} inRepeatDelayMin
  28606. * @param {number} inRepeatDelayMax
  28607. * @returns {Section} this
  28608. */
  28609. repeats(inRepetitions, inRepeatDelayMin = 0, inRepeatDelayMax) {
  28610. if (!is_real_number(inRepetitions))
  28611. throw this.sequence._customError(
  28612. this,
  28613. "repeats",
  28614. "inRepetitions must be of type number"
  28615. );
  28616. if (!is_real_number(inRepeatDelayMin))
  28617. throw this.sequence._customError(
  28618. this,
  28619. "repeats",
  28620. "repeatDelayMin must be of type number"
  28621. );
  28622. if (inRepeatDelayMax && !is_real_number(inRepeatDelayMax)) {
  28623. throw this.sequence._customError(
  28624. this,
  28625. "repeats",
  28626. "repeatDelayMax must be of type number"
  28627. );
  28628. }
  28629. this._repetitions = inRepetitions;
  28630. this._repeatDelayMin = Math.min(
  28631. inRepeatDelayMin,
  28632. inRepeatDelayMax ?? inRepeatDelayMin
  28633. );
  28634. this._repeatDelayMax = Math.max(
  28635. inRepeatDelayMin,
  28636. inRepeatDelayMax ?? inRepeatDelayMin
  28637. );
  28638. return this;
  28639. }
  28640. /**
  28641. * Causes the effect or sound to not play, and skip all delays, repetitions, waits, etc. If you pass a function,
  28642. * the function should return something false-y if you do not want the effect or sound to play.
  28643. *
  28644. * @param {boolean|function} inCondition
  28645. * @returns {Section} this
  28646. */
  28647. playIf(inCondition) {
  28648. this._playIf = inCondition;
  28649. return this;
  28650. }
  28651. /**
  28652. * Causes the section to finish running before starting the next section.
  28653. *
  28654. * @param {number|boolean} [minDelay=0] minDelay
  28655. * @param {number/null} [maxDelay=null] maxDelay
  28656. * @returns {Section} this
  28657. */
  28658. waitUntilFinished(minDelay = 0, maxDelay = null) {
  28659. if (minDelay === false)
  28660. return this;
  28661. if (!is_real_number(minDelay))
  28662. throw this.sequence._customError(
  28663. this,
  28664. "waitUntilFinished",
  28665. "minDelay must be of type number"
  28666. );
  28667. if (maxDelay !== null && !is_real_number(maxDelay))
  28668. throw this.sequence._customError(
  28669. this,
  28670. "waitUntilFinished",
  28671. "maxDelay must be of type number"
  28672. );
  28673. this._waitUntilFinished = true;
  28674. this._waitUntilFinishedDelay = [
  28675. Math.min(minDelay, maxDelay ?? minDelay),
  28676. Math.max(minDelay, maxDelay ?? minDelay)
  28677. ];
  28678. return this;
  28679. }
  28680. /**
  28681. * Causes each effect or sound to finish playing before the next one starts playing. This differs from
  28682. * .waitUntilFinished() in the sense that this is for each repetition, whilst .waitUntilFinished() is
  28683. * for the entire section.
  28684. *
  28685. * @returns {Section} this
  28686. */
  28687. async() {
  28688. this._async = true;
  28689. return this;
  28690. }
  28691. /**
  28692. * Delays the effect or sound from being played for a set amount of milliseconds. If given a second number, a
  28693. * random delay between the two numbers will be generated.
  28694. *
  28695. * @param {number} [msMin=1] minMs
  28696. * @param {number} [msMax=1] maxMs
  28697. * @returns {Section} this
  28698. */
  28699. delay(msMin, msMax) {
  28700. if (!is_real_number(msMin))
  28701. throw this.sequence._customError(
  28702. this,
  28703. "delay",
  28704. "msMin must be of type number"
  28705. );
  28706. if (msMax && !is_real_number(msMax))
  28707. throw this.sequence._customError(
  28708. this,
  28709. "delay",
  28710. "msMax must be of type number"
  28711. );
  28712. this._delayMin = Math.min(msMin, msMax ?? msMin);
  28713. this._delayMax = Math.max(msMin, msMax ?? msMin);
  28714. return this;
  28715. }
  28716. /**
  28717. * Overrides the duration of this section
  28718. *
  28719. * @param {number} inDuration
  28720. * @returns {Section} this
  28721. */
  28722. duration(inDuration) {
  28723. if (!is_real_number(inDuration))
  28724. throw this.sequence._customError(
  28725. this,
  28726. "duration",
  28727. "inDuration must be of type number"
  28728. );
  28729. this._duration = inDuration;
  28730. return this;
  28731. }
  28732. /**
  28733. * Applies a preset to the current section
  28734. *
  28735. * @param {string} presetName
  28736. * @param {*} args
  28737. * @returns {Section|FunctionSection|EffectSection|AnimationSection|SoundSection}
  28738. */
  28739. preset(presetName, ...args) {
  28740. if (typeof presetName !== "string") {
  28741. throw this.sequence._customError(
  28742. this,
  28743. "name",
  28744. `inName must be of type string`
  28745. );
  28746. }
  28747. const preset = SequencerPresets.get(presetName);
  28748. if (!preset) {
  28749. custom_warning(
  28750. "Sequencer",
  28751. `preset | Could not find preset with name "${presetName}"`
  28752. );
  28753. return this;
  28754. }
  28755. return preset(this, ...args);
  28756. }
  28757. /**
  28758. * @protected
  28759. */
  28760. async _shouldPlay() {
  28761. return is_function$1(this._playIf) ? await this._playIf() : this._playIf;
  28762. }
  28763. /**
  28764. * @protected
  28765. */
  28766. _validateLocation(inLocation) {
  28767. inLocation = validate_document(inLocation);
  28768. if (typeof inLocation === "string") {
  28769. inLocation = get_object_from_scene(inLocation) ?? inLocation;
  28770. }
  28771. if (typeof inLocation === "string") {
  28772. inLocation = safe_str(inLocation);
  28773. }
  28774. return inLocation;
  28775. }
  28776. /**
  28777. * @protected
  28778. */
  28779. async _execute() {
  28780. if (!await this._shouldPlay()) {
  28781. this.sectionStatus = CONSTANTS.STATUS.SKIPPED;
  28782. return;
  28783. }
  28784. this._basicDelay = random_float_between(this._delayMin, this._delayMax);
  28785. return new Promise(async (resolve) => {
  28786. setTimeout(async () => {
  28787. this.sectionStatus = CONSTANTS.STATUS.RUNNING;
  28788. for (let i = 0; i < this._repetitions; i++) {
  28789. if (get_store_value(this.sectionStatus) === CONSTANTS.STATUS.ABORTED) {
  28790. resolve();
  28791. return;
  28792. }
  28793. this._currentRepetition = i;
  28794. this._repeatDelay = i !== this._repetitions - 1 ? random_float_between(
  28795. this._repeatDelayMin,
  28796. this._repeatDelayMax
  28797. ) : 0;
  28798. await this.preRun();
  28799. if (this._shouldAsync) {
  28800. await this.run();
  28801. } else {
  28802. this.run();
  28803. }
  28804. if (this._repetitions > 1 && i !== this._repetitions - 1) {
  28805. await this._delayBetweenRepetitions();
  28806. }
  28807. }
  28808. resolve();
  28809. }, this._basicDelay);
  28810. }).then(() => {
  28811. this.sectionStatus = CONSTANTS.STATUS.COMPLETE;
  28812. });
  28813. }
  28814. set sectionStatus(inStatus) {
  28815. this._sectionStatus.update((currentStatus) => {
  28816. if (currentStatus === CONSTANTS.STATUS.READY || currentStatus === CONSTANTS.STATUS.RUNNING && inStatus !== CONSTANTS.STATUS.ABORTED) {
  28817. return inStatus;
  28818. }
  28819. return currentStatus;
  28820. });
  28821. }
  28822. get sectionStatus() {
  28823. return this._sectionStatus;
  28824. }
  28825. _abortSection() {
  28826. this.sectionStatus = CONSTANTS.STATUS.ABORTED;
  28827. }
  28828. /**
  28829. * @protected
  28830. */
  28831. async _delayBetweenRepetitions() {
  28832. let self = this;
  28833. return new Promise((resolve) => {
  28834. setTimeout(resolve, self._repeatDelay);
  28835. });
  28836. }
  28837. }
  28838. class FunctionSection extends Section {
  28839. constructor(inSequence, inFunc) {
  28840. super(inSequence);
  28841. if (!is_function$1(inFunc))
  28842. this._customError(
  28843. "create",
  28844. "The given function needs to be an actual function"
  28845. );
  28846. this._func = inFunc;
  28847. this._waitUntilFinished = inFunc.constructor.name === "AsyncFunction";
  28848. }
  28849. static niceName = "Function";
  28850. /**
  28851. * @returns {Promise<void>}
  28852. */
  28853. async run() {
  28854. debug("Running function");
  28855. await this._func();
  28856. }
  28857. /**
  28858. * @returns {Promise}
  28859. * @private
  28860. */
  28861. async _execute() {
  28862. await this.run();
  28863. }
  28864. }
  28865. const animation = {
  28866. /**
  28867. * Base properties
  28868. */
  28869. _animations: null,
  28870. /**
  28871. * Animates a property on the target of the animation.
  28872. *
  28873. * @param {string} inTarget
  28874. * @param {string} inPropertyName
  28875. * @param {object} inOptions
  28876. * @param {Number} inOptions.from - a single number from which to animate
  28877. * @param {Number} inOptions.to - a single number to which to animate
  28878. * @param {Number} inOptions.duration - how long in ms the animation should take
  28879. * @param {Number} inOptions.delay - inserts a delay in ms before the animation starts
  28880. * @param {String} inOptions.ease - what type of easing the animation should use
  28881. * @param {Boolean} inOptions.gridUnits - if animating width or height, this will set it to work in the scene's grid units
  28882. * @param {Boolean} inOptions.fromEnd - makes this animation play from the end, like fadeOut, scaleOut, etc
  28883. *
  28884. * @returns this
  28885. */
  28886. animateProperty(inTarget, inPropertyName, inOptions = {}) {
  28887. if (!this._animations)
  28888. this._animations = [];
  28889. const result = validateAnimation(
  28890. inTarget,
  28891. inPropertyName,
  28892. inOptions
  28893. );
  28894. if (typeof result === "string") {
  28895. throw this.sequence._customError(this, "animateProperty", result);
  28896. }
  28897. this._animations.push(result);
  28898. return this;
  28899. },
  28900. /**
  28901. * Loops a property between a set of values on the target
  28902. *
  28903. * @param {string} inTarget
  28904. * @param {string} inPropertyName
  28905. * @param {object} inOptions
  28906. * @param {Number} inOptions.from - a single number from which to loop
  28907. * @param {Number} inOptions.to - a single number to which to loop
  28908. * @param {Number} inOptions.values - an array of values to loop between
  28909. * @param {Number} inOptions.duration - how long in ms the loop should take
  28910. * @param {Number} inOptions.loops - how many loops in total this animation should go through - if none are specified, the loop is indefinite
  28911. * @param {Number} inOptions.delay - inserts a delay in ms before the animation starts
  28912. * @param {String} inOptions.ease - what type of easing the animation should use
  28913. * @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
  28914. * @param {Boolean} inOptions.gridUnits - if animating width or height, this will set it to work in the scene's grid units
  28915. *
  28916. * @returns this
  28917. */
  28918. loopProperty(inTarget, inPropertyName, inOptions = {}) {
  28919. if (!this._animations)
  28920. this._animations = [];
  28921. const result = validateLoopingAnimation(
  28922. inTarget,
  28923. inPropertyName,
  28924. inOptions
  28925. );
  28926. if (typeof result === "string") {
  28927. throw this.sequence._customError(this, "loopProperty", result);
  28928. }
  28929. this._animations.push(result);
  28930. return this;
  28931. }
  28932. };
  28933. const audio = {
  28934. /**
  28935. * Base properties
  28936. */
  28937. _volume: null,
  28938. _fadeInAudio: null,
  28939. _fadeOutAudio: null,
  28940. /**
  28941. * Sets the volume of the sound.
  28942. *
  28943. * @param {number} inVolume
  28944. * @returns this
  28945. */
  28946. volume(inVolume) {
  28947. if (!is_real_number(inVolume))
  28948. throw this.sequence._customError(
  28949. this,
  28950. "volume",
  28951. "inVolume must be of type number"
  28952. );
  28953. this._volume = Math.max(0, Math.min(1, inVolume));
  28954. return this;
  28955. },
  28956. /**
  28957. * Causes the animated section to fade in its audio (if any) when played
  28958. *
  28959. * @param {number} duration How long the fade should be
  28960. * @param {object} [options] Additional options, such as easing and delay
  28961. * @returns this
  28962. */
  28963. fadeInAudio(duration, options = {}) {
  28964. if (typeof options !== "object")
  28965. throw this.sequence._customError(
  28966. this,
  28967. "fadeInAudio",
  28968. "options must be of type object"
  28969. );
  28970. options = foundry.utils.mergeObject(
  28971. {
  28972. ease: "linear",
  28973. delay: 0
  28974. },
  28975. options
  28976. );
  28977. if (!is_real_number(duration))
  28978. throw this.sequence._customError(
  28979. this,
  28980. "fadeInAudio",
  28981. "duration must be of type number"
  28982. );
  28983. if (typeof options.ease !== "string")
  28984. throw this.sequence._customError(
  28985. this,
  28986. "fadeInAudio",
  28987. "options.ease must be of type string"
  28988. );
  28989. if (!is_real_number(options.delay))
  28990. throw this.sequence._customError(
  28991. this,
  28992. "fadeInAudio",
  28993. "options.delay must be of type number"
  28994. );
  28995. this._fadeInAudio = {
  28996. duration,
  28997. ease: options.ease,
  28998. delay: options.delay
  28999. };
  29000. return this;
  29001. },
  29002. /**
  29003. * Causes the audio to fade out at the end of the animated section's duration
  29004. *
  29005. * @param {number} duration How long the fade should be
  29006. * @param {object} [options] Additional options, such as easing and delay
  29007. * @returns this
  29008. */
  29009. fadeOutAudio(duration, options = {}) {
  29010. if (typeof options !== "object")
  29011. throw this.sequence._customError(
  29012. this,
  29013. "fadeOutAudio",
  29014. "options must be of type object"
  29015. );
  29016. options = foundry.utils.mergeObject(
  29017. {
  29018. ease: "linear",
  29019. delay: 0
  29020. },
  29021. options
  29022. );
  29023. if (!is_real_number(duration))
  29024. throw this.sequence._customError(
  29025. this,
  29026. "fadeOutAudio",
  29027. "duration must be of type number"
  29028. );
  29029. if (typeof options.ease !== "string")
  29030. throw this.sequence._customError(
  29031. this,
  29032. "fadeOutAudio",
  29033. "ease must be of type string"
  29034. );
  29035. if (!is_real_number(options.delay))
  29036. throw this.sequence._customError(
  29037. this,
  29038. "fadeOutAudio",
  29039. "delay must be of type number"
  29040. );
  29041. this._fadeOutAudio = {
  29042. duration,
  29043. ease: options.ease,
  29044. delay: options.delay
  29045. };
  29046. return this;
  29047. }
  29048. };
  29049. const files = {
  29050. /**
  29051. * Base properties
  29052. */
  29053. _file: "",
  29054. _fileOptions: false,
  29055. _baseFolder: "",
  29056. _mustache: null,
  29057. /**
  29058. * Declares which file to be played. This may also be an array of paths, which will be randomly picked from each
  29059. * time the section is played.
  29060. *
  29061. * @param {string|array} inFile
  29062. * @returns this
  29063. */
  29064. file(inFile) {
  29065. this._file = inFile;
  29066. return this;
  29067. },
  29068. /**
  29069. * Defines the base folder that will prepend to the file path. This is mainly just useful to make the file
  29070. * path easier to manage.
  29071. *
  29072. * @param {string} inBaseFolder
  29073. * @returns this
  29074. */
  29075. baseFolder(inBaseFolder) {
  29076. if (typeof inBaseFolder !== "string")
  29077. throw this.sequence._customError(
  29078. this,
  29079. "baseFolder",
  29080. "inBaseFolder must be of type string"
  29081. );
  29082. this._baseFolder = inBaseFolder + (inBaseFolder.endsWith("/") ? "" : "/");
  29083. return this;
  29084. },
  29085. /**
  29086. * Sets the Mustache of the filepath. This is applied after the randomization of the filepath, if available.
  29087. *
  29088. * @param {object} inMustache
  29089. * @returns this
  29090. */
  29091. setMustache(inMustache) {
  29092. if (typeof inMustache !== "object")
  29093. throw this.sequence._customError(
  29094. this,
  29095. "setMustache",
  29096. "inMustache must be of type object"
  29097. );
  29098. this._mustache = inMustache;
  29099. return this;
  29100. },
  29101. async _determineFile(inFile) {
  29102. if (!Array.isArray(inFile) && typeof inFile === "object") {
  29103. return this._validateCustomRange(inFile);
  29104. }
  29105. if (Array.isArray(inFile))
  29106. inFile = random_array_element(inFile, { recurse: true });
  29107. inFile = this._applyMustache(inFile);
  29108. if (Sequencer.Database.entryExists(inFile)) {
  29109. return this._determineDatabaseFile(inFile);
  29110. }
  29111. const determinedFile = await this._processFile(inFile);
  29112. return { file: determinedFile, forcedIndex: false, customRange: false };
  29113. },
  29114. async _processFile(inFile) {
  29115. inFile = this._applyMustache(inFile);
  29116. inFile = this._applyBaseFolder(inFile);
  29117. inFile = await this._applyWildcard(inFile);
  29118. if (Array.isArray(inFile))
  29119. inFile = random_array_element(inFile, { recurse: true });
  29120. return inFile;
  29121. },
  29122. async _validateCustomRange(inFile) {
  29123. const finalFiles = {};
  29124. const validRanges = Object.keys(SequencerFileRangeFind.ftToDistanceMap);
  29125. for (const [range, rangeFile] of Object.entries(inFile)) {
  29126. if (!validRanges.includes(range)) {
  29127. throw this.sequence._customError(
  29128. this,
  29129. "file",
  29130. `a file-distance key map must only contain the following keys: ${validRanges.join(
  29131. ", "
  29132. )}`
  29133. );
  29134. }
  29135. finalFiles[range] = await this._processFile(rangeFile);
  29136. }
  29137. return { file: finalFiles, forcedIndex: false, customRange: true };
  29138. },
  29139. _determineDatabaseFile(inFile) {
  29140. const entries = Sequencer.Database.getEntry(inFile);
  29141. const entry = Array.isArray(entries) ? random_array_element(entries) : entries;
  29142. const match = inFile.match(/(\d)+$/);
  29143. return {
  29144. file: entry,
  29145. forcedIndex: match ? Number(match[1]) : false,
  29146. customRange: false
  29147. };
  29148. },
  29149. _applyBaseFolder(inFile) {
  29150. if (Array.isArray(inFile))
  29151. return inFile.map((file) => this._applyBaseFolder(file));
  29152. return inFile.startsWith(this._baseFolder) ? inFile : this._baseFolder + inFile;
  29153. },
  29154. _applyMustache(inFile) {
  29155. if (!this._mustache)
  29156. return inFile;
  29157. let template = Handlebars.compile(inFile);
  29158. return template(this._mustache);
  29159. },
  29160. async _applyWildcard(inFile) {
  29161. if (!inFile.includes("*"))
  29162. return inFile;
  29163. if (Array.isArray(inFile))
  29164. return inFile.map(async (file) => await this._applyWildcard(file));
  29165. inFile = this._applyBaseFolder(inFile);
  29166. return getFiles(inFile, {
  29167. applyWildCard: true,
  29168. softFail: this.sequence.softFail
  29169. });
  29170. }
  29171. };
  29172. const moves = {
  29173. /**
  29174. * Base properties
  29175. */
  29176. _moveTowards: null,
  29177. _moveSpeed: null,
  29178. /**
  29179. * Sets the location to move the target object to
  29180. *
  29181. * @param {object|string} inTarget
  29182. * @param {object} options
  29183. * @returns this
  29184. */
  29185. moveTowards(inTarget, options = {}) {
  29186. options = foundry.utils.mergeObject(
  29187. {
  29188. ease: "linear",
  29189. delay: 0,
  29190. rotate: true,
  29191. cacheLocation: false
  29192. },
  29193. options
  29194. );
  29195. if (typeof options.ease !== "string")
  29196. throw this.sequence._customError(
  29197. this,
  29198. "moveTowards",
  29199. "options.ease must be of type string"
  29200. );
  29201. if (!is_real_number(options.delay))
  29202. throw this.sequence._customError(
  29203. this,
  29204. "moveTowards",
  29205. "options.delay must be of type number"
  29206. );
  29207. if (typeof options.rotate !== "boolean")
  29208. throw this.sequence._customError(
  29209. this,
  29210. "moveTowards",
  29211. "options.rotate must be of type boolean"
  29212. );
  29213. if (typeof options.cacheLocation !== "boolean")
  29214. throw this.sequence._customError(
  29215. this,
  29216. "moveTowards",
  29217. "options.cacheLocation must be of type boolean"
  29218. );
  29219. options.target = this._validateLocation(inTarget);
  29220. if (!options.target)
  29221. throw this.sequence._customError(
  29222. this,
  29223. "moveTowards",
  29224. "could not find position of given object"
  29225. );
  29226. options.target = options.cacheLocation ? get_object_position(options.cacheLocation, { measure: true }) : options.target;
  29227. this._moveTowards = options;
  29228. return this;
  29229. },
  29230. /**
  29231. * Sets the speed (pixels per frame) to move the target object
  29232. *
  29233. * @param {number} inSpeed
  29234. * @returns this
  29235. */
  29236. moveSpeed(inSpeed) {
  29237. if (!is_real_number(inSpeed))
  29238. throw this.sequence._customError(
  29239. this,
  29240. "moveSpeed",
  29241. "inSpeed must be of type number"
  29242. );
  29243. this._moveSpeed = inSpeed;
  29244. return this;
  29245. }
  29246. };
  29247. const opacity = {
  29248. /**
  29249. * Base properties
  29250. */
  29251. _opacity: null,
  29252. _fadeIn: null,
  29253. _fadeOut: null,
  29254. /**
  29255. * Sets the opacity of the effect. If used with ._fadeIn() and/or ._fadeOut(), this defines what the effect will fade to/from
  29256. *
  29257. * @param {number} inOpacity
  29258. * @returns this
  29259. */
  29260. opacity(inOpacity) {
  29261. if (!is_real_number(inOpacity))
  29262. throw this.sequence._customError(
  29263. this,
  29264. "opacity",
  29265. "inOpacity must be of type number"
  29266. );
  29267. this._opacity = inOpacity;
  29268. return this;
  29269. },
  29270. /**
  29271. * Causes the effect to fade in when played
  29272. *
  29273. * @param {number} duration How long the fade should be
  29274. * @param {object} [options] Additional options, such as easing and delay
  29275. * @returns this
  29276. */
  29277. fadeIn(duration, options = {}) {
  29278. if (typeof options !== "object")
  29279. throw this.sequence._customError(
  29280. this,
  29281. "fadeIn",
  29282. "options must be of type object"
  29283. );
  29284. options = foundry.utils.mergeObject(
  29285. {
  29286. ease: "linear",
  29287. delay: 0
  29288. },
  29289. options
  29290. );
  29291. if (!is_real_number(duration))
  29292. throw this.sequence._customError(
  29293. this,
  29294. "fadeIn",
  29295. "duration must be of type number"
  29296. );
  29297. if (typeof options.ease !== "string")
  29298. throw this.sequence._customError(
  29299. this,
  29300. "fadeIn",
  29301. "options.ease must be of type string"
  29302. );
  29303. if (!is_real_number(options.delay))
  29304. throw this.sequence._customError(
  29305. this,
  29306. "fadeIn",
  29307. "options.delay must be of type number"
  29308. );
  29309. this._fadeIn = {
  29310. duration,
  29311. ease: options.ease,
  29312. delay: options.delay
  29313. };
  29314. return this;
  29315. },
  29316. /**
  29317. * Causes the effect to fade out at the end of the effect's duration
  29318. *
  29319. * @param {number} duration How long the fade should be
  29320. * @param {object} [options] Additional options, such as easing and delay
  29321. * @returns this
  29322. */
  29323. fadeOut(duration, options = {}) {
  29324. if (typeof options !== "object")
  29325. throw this.sequence._customError(
  29326. this,
  29327. "fadeOut",
  29328. "options must be of type object"
  29329. );
  29330. options = foundry.utils.mergeObject(
  29331. {
  29332. ease: "linear",
  29333. delay: 0
  29334. },
  29335. options
  29336. );
  29337. if (!is_real_number(duration))
  29338. throw this.sequence._customError(
  29339. this,
  29340. "fadeOut",
  29341. "duration must be of type number"
  29342. );
  29343. if (typeof options.ease !== "string")
  29344. throw this.sequence._customError(
  29345. this,
  29346. "fadeOut",
  29347. "ease must be of type string"
  29348. );
  29349. if (!is_real_number(options.delay))
  29350. throw this.sequence._customError(
  29351. this,
  29352. "fadeOut",
  29353. "delay must be of type number"
  29354. );
  29355. this._fadeOut = {
  29356. duration,
  29357. ease: options.ease,
  29358. delay: options.delay
  29359. };
  29360. return this;
  29361. }
  29362. };
  29363. const rotation = {
  29364. /**
  29365. * Base properties
  29366. */
  29367. _angle: null,
  29368. _rotateIn: null,
  29369. _rotateOut: null,
  29370. _randomRotation: null,
  29371. _rotateTowards: null,
  29372. /**
  29373. * The object gets a random rotation, which means it should not be used with .stretchTo()
  29374. *
  29375. * @param {boolean} [inBool=true] inBool
  29376. * @returns this
  29377. */
  29378. randomRotation(inBool = true) {
  29379. if (typeof inBool !== "boolean")
  29380. throw this.sequence._customError(
  29381. this,
  29382. "randomRotation",
  29383. "inBool must be of type boolean"
  29384. );
  29385. this._randomRotation = inBool;
  29386. return this;
  29387. },
  29388. /**
  29389. * Sets the rotation of the object, which is added on top of the calculated rotation after .rotateTowards() or .randomRotation()
  29390. *
  29391. * @param {number} inRotation
  29392. * @returns this
  29393. */
  29394. rotate(inRotation) {
  29395. if (!is_real_number(inRotation))
  29396. throw this.sequence._customError(
  29397. this,
  29398. "opacity",
  29399. "inRotation must be of type number"
  29400. );
  29401. this._angle = inRotation;
  29402. return this;
  29403. },
  29404. /**
  29405. * Causes the object to rotate when it starts playing
  29406. *
  29407. * @param {number} degrees
  29408. * @param {number} duration
  29409. * @param {object} [options] options
  29410. * @returns this
  29411. */
  29412. rotateIn(degrees, duration, options = {}) {
  29413. if (typeof options !== "object")
  29414. throw this.sequence._customError(
  29415. this,
  29416. "rotateIn",
  29417. "options must be of type object"
  29418. );
  29419. options = foundry.utils.mergeObject(
  29420. {
  29421. ease: "linear",
  29422. delay: 0
  29423. },
  29424. options
  29425. );
  29426. if (!is_real_number(degrees))
  29427. throw this.sequence._customError(
  29428. this,
  29429. "rotateOut",
  29430. "degrees must be of type number"
  29431. );
  29432. if (!is_real_number(duration))
  29433. throw this.sequence._customError(
  29434. this,
  29435. "rotateOut",
  29436. "duration must be of type number"
  29437. );
  29438. if (typeof options.ease !== "string")
  29439. throw this.sequence._customError(
  29440. this,
  29441. "rotateIn",
  29442. "options.ease must be of type string"
  29443. );
  29444. if (!is_real_number(options.delay))
  29445. throw this.sequence._customError(
  29446. this,
  29447. "rotateIn",
  29448. "options.delay must be of type number"
  29449. );
  29450. this._rotateIn = {
  29451. value: degrees,
  29452. duration,
  29453. ease: options.ease,
  29454. delay: options.delay
  29455. };
  29456. return this;
  29457. },
  29458. /**
  29459. * Causes the object to rotate at the end of the effect's duration
  29460. *
  29461. * @param {number} degrees
  29462. * @param {number} duration
  29463. * @param {object} [options] options
  29464. * @returns this
  29465. */
  29466. rotateOut(degrees, duration, options = {}) {
  29467. if (typeof options !== "object")
  29468. throw this.sequence._customError(
  29469. this,
  29470. "rotateOut",
  29471. "options must be of type object"
  29472. );
  29473. options = foundry.utils.mergeObject(
  29474. {
  29475. ease: "linear",
  29476. delay: 0
  29477. },
  29478. options
  29479. );
  29480. if (!is_real_number(degrees))
  29481. throw this.sequence._customError(
  29482. this,
  29483. "rotateOut",
  29484. "degrees must be of type number"
  29485. );
  29486. if (!is_real_number(duration))
  29487. throw this.sequence._customError(
  29488. this,
  29489. "rotateOut",
  29490. "duration must be of type number"
  29491. );
  29492. if (typeof options.ease !== "string")
  29493. throw this.sequence._customError(
  29494. this,
  29495. "rotateOut",
  29496. "options.ease must be of type string"
  29497. );
  29498. if (!is_real_number(options.delay))
  29499. throw this.sequence._customError(
  29500. this,
  29501. "rotateOut",
  29502. "options.delay must be of type number"
  29503. );
  29504. this._rotateOut = {
  29505. value: degrees,
  29506. duration,
  29507. ease: options.ease,
  29508. delay: options.delay
  29509. };
  29510. return this;
  29511. }
  29512. };
  29513. const scale = {
  29514. _scaleMin: null,
  29515. _scaleMax: null,
  29516. _scaleIn: null,
  29517. _scaleOut: null,
  29518. /**
  29519. * A method that can take the following:
  29520. * - A number to set the scale uniformly
  29521. * - An object with x and y for non-uniform scaling
  29522. * - Two numbers which the Sequencer will randomly pick a uniform scale between
  29523. *
  29524. * @param {number|object} inScaleMin
  29525. * @param {number} [inScaleMax] inScaleMax
  29526. * @returns this
  29527. */
  29528. scale(inScaleMin, inScaleMax) {
  29529. if (!is_real_number(inScaleMin) && typeof inScaleMin !== "object")
  29530. throw this.sequence._customError(
  29531. this,
  29532. "scale",
  29533. "inScale must be of type number or object"
  29534. );
  29535. if (is_real_number(inScaleMin)) {
  29536. if (inScaleMax && !is_real_number(inScaleMax)) {
  29537. throw this.sequence._customError(
  29538. this,
  29539. "scale",
  29540. "if inScaleMin is a number, inScaleMax must also be of type number"
  29541. );
  29542. }
  29543. }
  29544. this._scaleMin = inScaleMin;
  29545. this._scaleMax = inScaleMax ?? false;
  29546. return this;
  29547. },
  29548. /**
  29549. * Causes the effect to scale when it starts playing
  29550. *
  29551. * @param {number|object} scale
  29552. * @param {number} duration
  29553. * @param {object} [options] options
  29554. * @returns this
  29555. */
  29556. scaleIn(scale2, duration, options = {}) {
  29557. if (typeof options !== "object")
  29558. throw this.sequence._customError(
  29559. this,
  29560. "scaleIn",
  29561. "options must be of type object"
  29562. );
  29563. options = foundry.utils.mergeObject(
  29564. {
  29565. ease: "linear",
  29566. delay: 0
  29567. },
  29568. options
  29569. );
  29570. if (!is_real_number(duration))
  29571. throw this.sequence._customError(
  29572. this,
  29573. "scaleIn",
  29574. "duration must be of type number"
  29575. );
  29576. if (!is_real_number(scale2) && typeof scale2 !== "object")
  29577. throw this.sequence._customError(
  29578. this,
  29579. "scaleIn",
  29580. "scale must be of type number or object"
  29581. );
  29582. if (typeof options.ease !== "string")
  29583. throw this.sequence._customError(
  29584. this,
  29585. "scaleIn",
  29586. "options.ease must be of type string"
  29587. );
  29588. if (!is_real_number(options.delay))
  29589. throw this.sequence._customError(
  29590. this,
  29591. "scaleIn",
  29592. "options.delay must be of type number"
  29593. );
  29594. this._scaleIn = {
  29595. value: scale2,
  29596. duration,
  29597. ease: options.ease,
  29598. delay: options.delay
  29599. };
  29600. return this;
  29601. },
  29602. /**
  29603. * Causes the effect to scale at the end of the effect's duration
  29604. *
  29605. * @param {number|object} scale
  29606. * @param {number} duration
  29607. * @param {object} [options] options
  29608. * @returns this
  29609. */
  29610. scaleOut(scale2, duration, options = {}) {
  29611. if (typeof options !== "object")
  29612. throw this.sequence._customError(
  29613. this,
  29614. "scaleOut",
  29615. "options must be of type object"
  29616. );
  29617. options = foundry.utils.mergeObject(
  29618. {
  29619. ease: "linear",
  29620. delay: 0
  29621. },
  29622. options
  29623. );
  29624. if (!is_real_number(duration))
  29625. throw this.sequence._customError(
  29626. this,
  29627. "scaleOut",
  29628. "duration must be of type number"
  29629. );
  29630. if (!is_real_number(scale2) && typeof scale2 !== "object")
  29631. throw this.sequence._customError(
  29632. this,
  29633. "scaleOut",
  29634. "scale must be of type number or object"
  29635. );
  29636. if (typeof options.ease !== "string")
  29637. throw this.sequence._customError(
  29638. this,
  29639. "scaleOut",
  29640. "options.ease must be of type string"
  29641. );
  29642. if (!is_real_number(options.delay))
  29643. throw this.sequence._customError(
  29644. this,
  29645. "scaleOut",
  29646. "options.delay must be of type number"
  29647. );
  29648. this._scaleOut = {
  29649. value: scale2,
  29650. duration,
  29651. ease: options.ease,
  29652. delay: options.delay
  29653. };
  29654. return this;
  29655. }
  29656. };
  29657. const time = {
  29658. _hasTime: true,
  29659. _isRange: false,
  29660. _startTime: null,
  29661. _startPerc: null,
  29662. _endTime: null,
  29663. _endPerc: null,
  29664. /**
  29665. * Sets the start and end time of the section, playing only that range
  29666. *
  29667. * @param {number} inMsStart
  29668. * @param {number} inMsEnd
  29669. * @returns this
  29670. */
  29671. timeRange(inMsStart, inMsEnd) {
  29672. if (!is_real_number(inMsStart))
  29673. throw this.sequence._customError(
  29674. this,
  29675. "timeRange",
  29676. "inMsStart must be of type number"
  29677. );
  29678. if (!is_real_number(inMsEnd))
  29679. throw this.sequence._customError(
  29680. this,
  29681. "timeRange",
  29682. "inMsEnd must be of type number"
  29683. );
  29684. this._startTime = inMsStart;
  29685. this._endTime = inMsEnd;
  29686. this._isRange = true;
  29687. return this;
  29688. },
  29689. /**
  29690. * Sets the start time of the section.
  29691. *
  29692. * @param {number} inMs
  29693. * @returns this
  29694. */
  29695. startTime(inMs) {
  29696. if (!is_real_number(inMs))
  29697. throw this.sequence._customError(
  29698. this,
  29699. "startTime",
  29700. "inMs must be of type number"
  29701. );
  29702. this._startTime = inMs;
  29703. this._startPerc = false;
  29704. this._isRange = false;
  29705. return this;
  29706. },
  29707. /**
  29708. * Sets the start time of the section based on a percentage from its total duration.
  29709. *
  29710. * @param {number} inPercentage
  29711. * @returns this
  29712. */
  29713. startTimePerc(inPercentage) {
  29714. if (!is_real_number(inPercentage))
  29715. throw this.sequence._customError(
  29716. this,
  29717. "startTimePerc",
  29718. "inPercentage must be of type number"
  29719. );
  29720. this._startTime = inPercentage;
  29721. this._startPerc = true;
  29722. this._isRange = false;
  29723. return this;
  29724. },
  29725. /**
  29726. * Sets the ending time of the section (from the end).
  29727. *
  29728. * @param {number} inMs
  29729. * @returns this
  29730. */
  29731. endTime(inMs) {
  29732. if (!is_real_number(inMs))
  29733. throw this.sequence._customError(
  29734. this,
  29735. "endTime",
  29736. "inMs must be of type number"
  29737. );
  29738. this._endTime = inMs;
  29739. this._endPerc = false;
  29740. this._isRange = false;
  29741. return this;
  29742. },
  29743. /**
  29744. * Sets the ending time of the section based on a percentage from the total duration.
  29745. *
  29746. * @param {number} inPercentage
  29747. * @returns this
  29748. */
  29749. endTimePerc(inPercentage) {
  29750. if (!is_real_number(inPercentage))
  29751. throw this.sequence._customError(
  29752. this,
  29753. "endTimePerc",
  29754. "inPercentage must be of type number"
  29755. );
  29756. this._endTime = inPercentage;
  29757. this._endPerc = true;
  29758. this._isRange = false;
  29759. return this;
  29760. }
  29761. };
  29762. const users = {
  29763. _users: null,
  29764. _addUser(inUser) {
  29765. if (!this._users)
  29766. this._users = [];
  29767. if (typeof inUser !== "string")
  29768. throw this.sequence._customError(
  29769. this,
  29770. "_addUser",
  29771. "inUser must be of type string"
  29772. );
  29773. if (!game.users.has(inUser)) {
  29774. if (game.users.getName(inUser)) {
  29775. inUser = game.users.getName(inUser).id;
  29776. } else {
  29777. throw this.sequence._customError(
  29778. this,
  29779. "_addUser",
  29780. `user with id or name "${inUser}" does not exist!`
  29781. );
  29782. }
  29783. }
  29784. if (!this._users.includes(inUser))
  29785. this._users.push(inUser);
  29786. },
  29787. _deleteUser(inUser) {
  29788. if (!this._users)
  29789. this._users = [];
  29790. if (this._users.includes(inUser)) {
  29791. let index = this._users.indexOf(inUser);
  29792. this._users.splice(index, 1);
  29793. }
  29794. },
  29795. /**
  29796. * Causes section to be executed only locally, and not push to other connected clients.
  29797. *
  29798. * @param {boolean} inLocally
  29799. * @returns this
  29800. */
  29801. locally(inLocally = true) {
  29802. if (inLocally)
  29803. this._addUser(game.userId);
  29804. else
  29805. this._deleteUser(game.userId);
  29806. return this;
  29807. },
  29808. /**
  29809. * Causes the section to be executed for only a set of users.
  29810. *
  29811. * @param {string|User|array<string|User>} inUsers
  29812. * @returns this
  29813. */
  29814. forUsers(inUsers) {
  29815. if (!Array.isArray(inUsers)) {
  29816. if (typeof inUsers !== "string")
  29817. throw this.sequence._customError(
  29818. this,
  29819. "forUsers",
  29820. "inUser must be of type string"
  29821. );
  29822. inUsers = [inUsers];
  29823. }
  29824. inUsers.forEach((u) => this._addUser(u));
  29825. return this;
  29826. }
  29827. };
  29828. const filter = {
  29829. _filters: null,
  29830. _addFilter(inFilterName, inData, inName = false) {
  29831. if (!this._filters)
  29832. this._filters = [];
  29833. this._filters.push({
  29834. className: inFilterName,
  29835. name: inName,
  29836. data: inData
  29837. });
  29838. },
  29839. _testFilter(inFilterName, inData) {
  29840. let filter2 = new filters[inFilterName](inData);
  29841. if (!filter2.isValid)
  29842. throw this.sequence._customError(
  29843. this,
  29844. "filter",
  29845. `Could not create ${inFilterName} filter - data is malformed!`
  29846. );
  29847. },
  29848. filter(inFilterName, inData = {}, inName = "") {
  29849. if (typeof inFilterName !== "string")
  29850. throw this.sequence._customError(
  29851. this,
  29852. "filter",
  29853. `inFilterName must be of type string`
  29854. );
  29855. if (!Object.keys(filters).includes(inFilterName))
  29856. throw this.sequence._customError(
  29857. this,
  29858. "filter",
  29859. `"${inFilterName}" does not exist`
  29860. );
  29861. this._testFilter(inFilterName, inData);
  29862. this._addFilter(inFilterName, inData, inName);
  29863. return this;
  29864. }
  29865. };
  29866. const tint = {
  29867. _tint: null,
  29868. /**
  29869. * Tints the target of this section by the color given to the
  29870. *
  29871. * @param {number|string} inColor
  29872. * @returns this
  29873. */
  29874. tint(inColor) {
  29875. if (!is_real_number(inColor) && typeof inColor !== "string")
  29876. throw this.sequence._customError(
  29877. this,
  29878. "tint",
  29879. `inColor must be of type string (hexadecimal) or number (decimal)!`
  29880. );
  29881. this._tint = parseColor(inColor);
  29882. return this;
  29883. }
  29884. };
  29885. const location = {
  29886. /**
  29887. * Base properties
  29888. */
  29889. _source: null,
  29890. /**
  29891. * A smart method that can take a reference to an object, or a direct on the canvas to play the effect at,
  29892. * or a string reference (see .name())
  29893. *
  29894. * @param {Object|String} inLocation
  29895. * @param {Object} inOptions
  29896. * @returns {EffectSection}
  29897. */
  29898. atLocation(inLocation, inOptions = {}) {
  29899. if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
  29900. throw this.sequence._customError(
  29901. this,
  29902. "atLocation",
  29903. `inLocation is invalid, and must be of type of object, string, placeable object, or document`
  29904. );
  29905. }
  29906. if (typeof inOptions !== "object")
  29907. throw this.sequence._customError(
  29908. this,
  29909. "atLocation",
  29910. `inOptions must be of type object`
  29911. );
  29912. inOptions = foundry.utils.mergeObject(
  29913. {
  29914. cacheLocation: false,
  29915. offset: false,
  29916. randomOffset: false,
  29917. gridUnits: false,
  29918. local: false
  29919. },
  29920. inOptions
  29921. );
  29922. inLocation = this._validateLocation(inLocation);
  29923. if (inLocation === void 0)
  29924. throw this.sequence._customError(
  29925. this,
  29926. "atLocation",
  29927. "could not find position of given object"
  29928. );
  29929. if (typeof inOptions.cacheLocation !== "boolean")
  29930. throw this.sequence._customError(
  29931. this,
  29932. "atLocation",
  29933. "inOptions.cacheLocation must be of type boolean"
  29934. );
  29935. if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
  29936. throw this.sequence._customError(
  29937. this,
  29938. "atLocation",
  29939. "inOptions.randomOffset must be of type boolean or number"
  29940. );
  29941. this._temporaryEffect = this._temporaryEffect || (inLocation instanceof foundry.abstract.Document ? !is_UUID(inLocation?.uuid) : false);
  29942. if (inOptions.offset) {
  29943. const offsetData = this._validateOffset(
  29944. "atLocation",
  29945. inOptions.offset,
  29946. inOptions
  29947. );
  29948. this._offset = {
  29949. source: offsetData,
  29950. target: this._offset?.target ?? false
  29951. };
  29952. }
  29953. this._randomOffset = {
  29954. source: inOptions.randomOffset,
  29955. target: this._randomOffset?.target ?? false
  29956. };
  29957. this._source = inOptions.cacheLocation && typeof inLocation !== "string" ? get_object_canvas_data(inLocation) : inLocation;
  29958. return this;
  29959. }
  29960. };
  29961. const offset = {
  29962. _offset: null,
  29963. _randomOffset: null,
  29964. _validateOffset(functionName, inOffset, inOptions = {}) {
  29965. inOffset = foundry.utils.mergeObject(
  29966. {
  29967. x: 0,
  29968. y: 0
  29969. },
  29970. inOffset
  29971. );
  29972. inOptions = foundry.utils.mergeObject(
  29973. {
  29974. gridUnits: false,
  29975. local: false
  29976. },
  29977. inOptions
  29978. );
  29979. if (typeof inOptions.gridUnits !== "boolean")
  29980. throw this.sequence._customError(
  29981. this,
  29982. functionName,
  29983. "inOptions.gridUnits must be of type boolean"
  29984. );
  29985. if (typeof inOptions.local !== "boolean")
  29986. throw this.sequence._customError(
  29987. this,
  29988. functionName,
  29989. "inOptions.local must be of type boolean"
  29990. );
  29991. if (!is_real_number(inOffset.x))
  29992. throw this.sequence._customError(
  29993. this,
  29994. functionName,
  29995. `inOffset.x must be of type number!`
  29996. );
  29997. if (!is_real_number(inOffset.y))
  29998. throw this.sequence._customError(
  29999. this,
  30000. functionName,
  30001. `inOffset.y must be of type number!`
  30002. );
  30003. return {
  30004. ...inOffset,
  30005. ...inOptions
  30006. };
  30007. }
  30008. };
  30009. const text = {
  30010. _text: null,
  30011. /**
  30012. * Creates a text element, attached to the sprite. The options for the text are available here:
  30013. * https://pixijs.io/pixi-text-style/
  30014. *
  30015. * @param {String} inText
  30016. * @param {Object} inOptions
  30017. * @returns {EffectSection}
  30018. */
  30019. text(inText, inOptions = {}) {
  30020. if (typeof inText !== "string")
  30021. throw this.sequence._customError(
  30022. this,
  30023. "text",
  30024. "inText must be of type string"
  30025. );
  30026. this._text = foundry.utils.mergeObject(
  30027. {
  30028. text: inText
  30029. },
  30030. inOptions
  30031. );
  30032. return this;
  30033. }
  30034. };
  30035. const traits = {
  30036. animation,
  30037. audio,
  30038. files,
  30039. moves,
  30040. opacity,
  30041. rotation,
  30042. scale,
  30043. time,
  30044. users,
  30045. filter,
  30046. tint,
  30047. location,
  30048. offset,
  30049. text
  30050. };
  30051. class EffectSection extends Section {
  30052. constructor(inSequence, inFile = "") {
  30053. super(inSequence);
  30054. this._deserializedData = null;
  30055. this._file = inFile;
  30056. this._text = null;
  30057. this._source = null;
  30058. this._stretchTo = null;
  30059. this._attachTo = null;
  30060. this._from = null;
  30061. this._origin = null;
  30062. this._anchor = null;
  30063. this._spriteAnchor = null;
  30064. this._randomOffset = null;
  30065. this._missed = null;
  30066. this._private = null;
  30067. this._randomMirrorX = null;
  30068. this._randomMirrorY = null;
  30069. this._mirrorX = null;
  30070. this._mirrorY = null;
  30071. this._playbackRate = null;
  30072. this._template = null;
  30073. this._overrides = [];
  30074. this._name = null;
  30075. this._zIndex = null;
  30076. this._offset = null;
  30077. this._spriteOffset = null;
  30078. this._size = null;
  30079. this._persist = null;
  30080. this._persistOptions = null;
  30081. this._zeroSpriteRotation = null;
  30082. this._extraEndDuration = null;
  30083. this._noLoop = null;
  30084. this._tilingTexture = null;
  30085. this._snapToGrid = null;
  30086. this._scaleToObject = null;
  30087. this._screenSpace = null;
  30088. this._screenSpaceAboveUI = null;
  30089. this._screenSpaceAnchor = null;
  30090. this._screenSpacePosition = null;
  30091. this._screenSpaceScale = null;
  30092. this._elevation = null;
  30093. this._masks = [];
  30094. this._tiedDocuments = [];
  30095. this._selfMask = false;
  30096. this._temporaryEffect = false;
  30097. this._spriteRotation = 0;
  30098. this._randomSpriteRotation = false;
  30099. this._isRangedEffect = null;
  30100. this._offsetLegacy = null;
  30101. this._randomOffsetLegacy = null;
  30102. this._aboveLighting = null;
  30103. this._aboveInterface = null;
  30104. this._spriteScaleMin = 1;
  30105. this._spriteScaleMax = null;
  30106. this._isometric = null;
  30107. this._shapes = [];
  30108. this._xray = null;
  30109. this._playEffect = true;
  30110. }
  30111. static niceName = "Effect";
  30112. /**
  30113. * @private
  30114. */
  30115. get _target() {
  30116. return this._stretchTo || this._rotateTowards || this._moveTowards || false;
  30117. }
  30118. static debounceWarning() {
  30119. custom_warning(
  30120. "Sequencer",
  30121. "Effect | This user does not have permissions to play effects. This can be configured in Sequencer's module settings."
  30122. );
  30123. }
  30124. /**
  30125. * Causes the effect's position to be stored and can then be used with .atLocation(), .stretchTowards(),
  30126. * and .rotateTowards() to refer to previous effects' locations
  30127. *
  30128. * @param {String} inName
  30129. * @returns {EffectSection}
  30130. */
  30131. name(inName) {
  30132. if (typeof inName !== "string")
  30133. throw this.sequence._customError(
  30134. this,
  30135. "name",
  30136. "inName must be of type string"
  30137. );
  30138. this._name = safe_str(inName);
  30139. return this;
  30140. }
  30141. /**
  30142. * Causes the effect to persist indefinitely on the canvas until _ended via SequencerEffectManager.endAllEffects() or
  30143. * name the effect with .name() and then end it through SequencerEffectManager.endEffect()
  30144. *
  30145. * @param {Boolean} [inBool=true] inBool
  30146. * @param {Object} [inOptions={}] inOptions
  30147. * @returns {EffectSection}
  30148. */
  30149. persist(inBool = true, inOptions = {}) {
  30150. if (typeof inBool !== "boolean")
  30151. throw this.sequence._customError(
  30152. this,
  30153. "persist",
  30154. "inBool must be of type boolean"
  30155. );
  30156. if (typeof inOptions !== "object")
  30157. throw this.sequence._customError(
  30158. this,
  30159. "persist",
  30160. `inOptions must be of type object`
  30161. );
  30162. inOptions = foundry.utils.mergeObject(
  30163. {
  30164. id: randomID(),
  30165. persistTokenPrototype: false
  30166. },
  30167. inOptions
  30168. );
  30169. if (typeof inOptions.persistTokenPrototype !== "boolean")
  30170. throw this.sequence._customError(
  30171. this,
  30172. "persist",
  30173. "inOptions.persistTokenPrototype must be of type boolean"
  30174. );
  30175. this._persist = inBool;
  30176. this._persistOptions = inOptions;
  30177. return this;
  30178. }
  30179. /**
  30180. * Causes the effect to become temporary, which means it will not be stored in the flags of any object,
  30181. * even if it .persist() is called
  30182. *
  30183. * @param {Boolean} inBool
  30184. * @returns {EffectSection}
  30185. */
  30186. temporary(inBool = true) {
  30187. if (typeof inBool !== "boolean")
  30188. throw this.sequence._customError(
  30189. this,
  30190. "temporary",
  30191. "inBool must be of type boolean"
  30192. );
  30193. this._temporaryEffect = inBool || this._temporaryEffect;
  30194. return this;
  30195. }
  30196. /**
  30197. * Sets the effect's playback rate. A playback rate of 2.0 would make it play 2x as fast, 0.5 would make
  30198. * it play half as fast.
  30199. *
  30200. * @param {Number} inNumber
  30201. * @returns {EffectSection}
  30202. */
  30203. playbackRate(inNumber = 1) {
  30204. if (!is_real_number(inNumber))
  30205. throw this.sequence._customError(
  30206. this,
  30207. "playbackRate",
  30208. "inNumber must be of type number"
  30209. );
  30210. this._playbackRate = inNumber;
  30211. return this;
  30212. }
  30213. /**
  30214. * Causes the effect to target a location close to the .stretchTowards() location, but not on it.
  30215. *
  30216. * @param {Boolean} [inBool=true] inBool
  30217. * @returns {EffectSection}
  30218. */
  30219. missed(inBool = true) {
  30220. if (typeof inBool !== "boolean")
  30221. throw this.sequence._customError(
  30222. this,
  30223. "missed",
  30224. "inBool must be of type boolean"
  30225. );
  30226. this._missed = inBool;
  30227. return this;
  30228. }
  30229. /**
  30230. * Adds a function that will run at the end of the effect serialization step, but before it is played. Allows direct
  30231. * modifications of effect's data. For example, it could be manipulated to change which file will be used based
  30232. * on the distance to the target.
  30233. *
  30234. * @param {Function} inFunc
  30235. * @returns {EffectSection}
  30236. */
  30237. addOverride(inFunc) {
  30238. if (!is_function$1(inFunc))
  30239. throw this.sequence._customError(
  30240. this,
  30241. "addOverride",
  30242. "The given function needs to be an actual function."
  30243. );
  30244. this._overrides.push(inFunc);
  30245. return this;
  30246. }
  30247. /**
  30248. * A smart method that can take a reference to an object, or a direct on the canvas to attach an effect to,
  30249. * or a string reference (see .name())
  30250. *
  30251. * @param {Object|String} inObject
  30252. * @param {Object} inOptions
  30253. * @returns {EffectSection}
  30254. */
  30255. attachTo(inObject, inOptions = {}) {
  30256. if (!(typeof inObject === "object" || typeof inObject === "string")) {
  30257. throw this.sequence._customError(
  30258. this,
  30259. "attachTo",
  30260. `inObject is invalid, and must be of type of object, string, placeable object, or document`
  30261. );
  30262. }
  30263. if (typeof inOptions !== "object")
  30264. throw this.sequence._customError(
  30265. this,
  30266. "attachTo",
  30267. `inOptions must be of type object`
  30268. );
  30269. inOptions = foundry.utils.mergeObject(
  30270. {
  30271. align: "center",
  30272. edge: "on",
  30273. bindVisibility: true,
  30274. bindAlpha: true,
  30275. bindElevation: true,
  30276. followRotation: true,
  30277. offset: false,
  30278. randomOffset: false,
  30279. gridUnits: false,
  30280. local: false
  30281. },
  30282. inOptions
  30283. );
  30284. const validatedObject = this._validateLocation(inObject);
  30285. if (validatedObject === void 0)
  30286. throw this.sequence._customError(
  30287. this,
  30288. "attachTo",
  30289. "could not find given object"
  30290. );
  30291. let isValidObject = true;
  30292. if (typeof inObject === "string") {
  30293. 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;
  30294. if (!isValidObject) {
  30295. this.sequence._showWarning(
  30296. this,
  30297. "attachTo",
  30298. "Only Tokens, Tiles, Drawings, and MeasuredTemplates may have attached effects - will play effect on target's location"
  30299. );
  30300. }
  30301. }
  30302. const aligns = Object.keys(alignments);
  30303. if (typeof inOptions.align !== "string" || !aligns.includes(inOptions.align)) {
  30304. throw this.sequence._customError(
  30305. this,
  30306. "attachTo",
  30307. `inOptions.align must be of type string, one of: ${aligns.join(", ")}`
  30308. );
  30309. }
  30310. if (typeof inOptions.edge !== "string" || !(inOptions.edge === "on" || inOptions.edge === "inner" || inOptions.edge === "outer")) {
  30311. throw this.sequence._customError(
  30312. this,
  30313. "attachTo",
  30314. `inOptions.edge must of type string with the value of either "on", "inner", or "outer"`
  30315. );
  30316. }
  30317. if (typeof inOptions.bindVisibility !== "boolean")
  30318. throw this.sequence._customError(
  30319. this,
  30320. "attachTo",
  30321. `inOptions.bindVisibility must be of type boolean`
  30322. );
  30323. if (typeof inOptions.followRotation !== "boolean")
  30324. throw this.sequence._customError(
  30325. this,
  30326. "attachTo",
  30327. `inOptions.followRotation must be of type boolean`
  30328. );
  30329. if (typeof inOptions.bindAlpha !== "boolean")
  30330. throw this.sequence._customError(
  30331. this,
  30332. "attachTo",
  30333. "inOptions.bindAlpha must be of type boolean"
  30334. );
  30335. if (typeof inOptions.bindElevation !== "boolean")
  30336. throw this.sequence._customError(
  30337. this,
  30338. "attachTo",
  30339. "inOptions.bindElevation must be of type boolean"
  30340. );
  30341. if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
  30342. throw this.sequence._customError(
  30343. this,
  30344. "attachTo",
  30345. "inOptions.randomOffset must be of type boolean or number"
  30346. );
  30347. this._source = validatedObject;
  30348. this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document || validatedObject instanceof MeasuredTemplate ? !is_UUID(validatedObject?.uuid) : this._temporaryEffect || false);
  30349. if (inOptions.offset) {
  30350. const offsetData = this._validateOffset(
  30351. "attachTo",
  30352. inOptions.offset,
  30353. inOptions
  30354. );
  30355. this._offset = {
  30356. source: offsetData,
  30357. target: this._offset?.target ?? false
  30358. };
  30359. }
  30360. this._randomOffset = {
  30361. source: inOptions.randomOffset,
  30362. target: this._randomOffset?.target ?? false
  30363. };
  30364. this._attachTo = {
  30365. active: isValidObject,
  30366. align: inOptions.align,
  30367. edge: inOptions.edge,
  30368. bindVisibility: inOptions.bindVisibility,
  30369. bindAlpha: inOptions.bindAlpha,
  30370. bindElevation: inOptions.bindElevation,
  30371. followRotation: inOptions.followRotation
  30372. };
  30373. return this;
  30374. }
  30375. /**
  30376. * 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())
  30377. * This effectively calculates the proper X scale for the effect to reach the target
  30378. *
  30379. * @param {Object|String} inLocation
  30380. * @param {Object} inOptions
  30381. * @returns {EffectSection}
  30382. */
  30383. stretchTo(inLocation, inOptions = {}) {
  30384. if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
  30385. throw this.sequence._customError(
  30386. this,
  30387. "stretchTo",
  30388. `inLocation is invalid, and must be of type of object, string, placeable object, or document`
  30389. );
  30390. }
  30391. if (typeof inOptions !== "object")
  30392. throw this.sequence._customError(
  30393. this,
  30394. "stretchTo",
  30395. `inOptions must be of type object`
  30396. );
  30397. inOptions = foundry.utils.mergeObject(
  30398. {
  30399. cacheLocation: false,
  30400. attachTo: false,
  30401. onlyX: false,
  30402. tiling: false,
  30403. offset: false,
  30404. randomOffset: false,
  30405. gridUnits: false,
  30406. local: false,
  30407. requiresLineOfSight: false,
  30408. hideLineOfSight: false
  30409. },
  30410. inOptions
  30411. );
  30412. const validatedObject = this._validateLocation(inLocation);
  30413. if (validatedObject === void 0)
  30414. throw this.sequence._customError(
  30415. this,
  30416. "stretchTo",
  30417. "could not find position of given object"
  30418. );
  30419. if (typeof inOptions.cacheLocation !== "boolean")
  30420. throw this.sequence._customError(
  30421. this,
  30422. "stretchTo",
  30423. "inOptions.cacheLocation must be of type boolean"
  30424. );
  30425. if (typeof inOptions.attachTo !== "boolean")
  30426. throw this.sequence._customError(
  30427. this,
  30428. "stretchTo",
  30429. "inOptions.attachTo must be of type boolean"
  30430. );
  30431. if (typeof inOptions.onlyX !== "boolean")
  30432. throw this.sequence._customError(
  30433. this,
  30434. "stretchTo",
  30435. "inOptions.onlyX must be of type boolean"
  30436. );
  30437. if (typeof inOptions.tiling !== "boolean")
  30438. throw this.sequence._customError(
  30439. this,
  30440. "stretchTo",
  30441. "inOptions.tiling must be of type boolean"
  30442. );
  30443. if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
  30444. throw this.sequence._customError(
  30445. this,
  30446. "stretchTo",
  30447. "inOptions.randomOffset must be of type boolean or number"
  30448. );
  30449. if (inOptions.cacheLocation && inOptions.attachTo) {
  30450. throw this.sequence._customError(
  30451. this,
  30452. "stretchTo",
  30453. "cacheLocation and attachTo cannot both be true - pick one or the other"
  30454. );
  30455. }
  30456. if (typeof inOptions.requiresLineOfSight !== "boolean") {
  30457. throw this.sequence._customError(
  30458. this,
  30459. "stretchTo",
  30460. "requiresLineOfSight must be of type boolean"
  30461. );
  30462. }
  30463. if (!inOptions.attachTo && inOptions.requiresLineOfSight) {
  30464. throw this.sequence._customError(
  30465. this,
  30466. "stretchTo",
  30467. "requiresLineOfSight requires that attachTo is true"
  30468. );
  30469. }
  30470. if (typeof inOptions.hideLineOfSight !== "boolean") {
  30471. throw this.sequence._customError(
  30472. this,
  30473. "stretchTo",
  30474. "hideLineOfSight must be of type boolean"
  30475. );
  30476. }
  30477. if (!inOptions.requiresLineOfSight && inOptions.hideLineOfSight) {
  30478. throw this.sequence._customError(
  30479. this,
  30480. "stretchTo",
  30481. "hideLineOfSight requires that requiresLineOfSight is true"
  30482. );
  30483. }
  30484. if (inOptions.tiling)
  30485. this.tilingTexture();
  30486. this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document ? !is_UUID(validatedObject?.uuid) : this._temporaryEffect || false);
  30487. if (inOptions.offset) {
  30488. const offsetData = this._validateOffset(
  30489. "stretchTo",
  30490. inOptions.offset,
  30491. inOptions
  30492. );
  30493. this._offset = {
  30494. source: this._offset?.source ?? false,
  30495. target: offsetData
  30496. };
  30497. }
  30498. this._randomOffset = {
  30499. source: this._randomOffset?.source ?? false,
  30500. target: inOptions.randomOffset
  30501. };
  30502. this._stretchTo = {
  30503. target: inOptions.cacheLocation ? get_object_canvas_data(validatedObject, { measure: true }) : validatedObject,
  30504. attachTo: inOptions.attachTo,
  30505. onlyX: inOptions.onlyX,
  30506. requiresLineOfSight: inOptions.requiresLineOfSight,
  30507. hideLineOfSight: inOptions.hideLineOfSight
  30508. };
  30509. return this;
  30510. }
  30511. /**
  30512. * Sets the location to rotate the object to
  30513. *
  30514. * @param {object|string} inLocation
  30515. * @param {object} inOptions
  30516. * @returns this
  30517. */
  30518. rotateTowards(inLocation, inOptions = {}) {
  30519. if (!(typeof inLocation === "object" || typeof inLocation === "string")) {
  30520. throw this.sequence._customError(
  30521. this,
  30522. "inLocation",
  30523. `inLocation is invalid, and must be of type of object, string, placeable object, or document`
  30524. );
  30525. }
  30526. inOptions = foundry.utils.mergeObject(
  30527. {
  30528. rotationOffset: 0,
  30529. cacheLocation: false,
  30530. attachTo: false,
  30531. offset: false,
  30532. randomOffset: false,
  30533. local: false,
  30534. gridUnits: false
  30535. },
  30536. inOptions
  30537. );
  30538. if (!is_real_number(inOptions.rotationOffset))
  30539. throw this.sequence._customError(
  30540. this,
  30541. "rotateTowards",
  30542. "inOptions.rotationOffset must be of type number"
  30543. );
  30544. if (typeof inOptions.attachTo !== "boolean")
  30545. throw this.sequence._customError(
  30546. this,
  30547. "rotateTowards",
  30548. "inOptions.attachTo must be of type boolean"
  30549. );
  30550. if (typeof inOptions.cacheLocation !== "boolean")
  30551. throw this.sequence._customError(
  30552. this,
  30553. "rotateTowards",
  30554. "inOptions.cacheLocation must be of type boolean"
  30555. );
  30556. const validatedObject = this._validateLocation(inLocation);
  30557. if (!validatedObject)
  30558. throw this.sequence._customError(
  30559. this,
  30560. "rotateTowards",
  30561. "could not find position of given object"
  30562. );
  30563. this._temporaryEffect = this._temporaryEffect || (validatedObject instanceof foundry.abstract.Document ? !is_UUID(validatedObject?.uuid) : this._temporaryEffect || false);
  30564. if (inOptions.offset) {
  30565. const offsetData = this._validateOffset(
  30566. "attachTo",
  30567. inOptions.offset,
  30568. inOptions
  30569. );
  30570. this._offset = {
  30571. source: offsetData,
  30572. target: this._offset?.target ?? false
  30573. };
  30574. }
  30575. this._randomOffset = {
  30576. source: inOptions.randomOffset,
  30577. target: this._randomOffset?.target ?? false
  30578. };
  30579. this._rotateTowards = {
  30580. target: inOptions.cacheLocation ? get_object_canvas_data(validatedObject, { measure: true }) : validatedObject,
  30581. rotationOffset: inOptions.rotationOffset,
  30582. cacheLocation: inOptions.cacheLocation,
  30583. attachTo: inOptions.attachTo
  30584. };
  30585. return this;
  30586. }
  30587. /**
  30588. * 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.
  30589. *
  30590. * @param {Object} inObject
  30591. * @param {Object} inOptions
  30592. * @returns {EffectSection}
  30593. */
  30594. from(inObject, inOptions = {}) {
  30595. if (!(inObject instanceof Token || inObject instanceof Tile || inObject instanceof TokenDocument || inObject instanceof TileDocument)) {
  30596. throw this.sequence._customError(
  30597. this,
  30598. "from",
  30599. "inObject must be of type Token, Tile, TokenDocument, or TileDocument"
  30600. );
  30601. }
  30602. if (typeof inOptions !== "object")
  30603. throw this.sequence._customError(
  30604. this,
  30605. "from",
  30606. `inOptions must be of type object`
  30607. );
  30608. inObject = inObject.document ?? inObject;
  30609. if (!inObject?.texture?.src)
  30610. throw this.sequence._customError(
  30611. this,
  30612. "from",
  30613. "could not find the image for the given object"
  30614. );
  30615. inOptions = foundry.utils.mergeObject(
  30616. {
  30617. cacheLocation: false,
  30618. offset: false,
  30619. randomOffset: false,
  30620. local: false,
  30621. gridUnits: false
  30622. },
  30623. inOptions
  30624. );
  30625. if (typeof inOptions.cacheLocation !== "boolean")
  30626. throw this.sequence._customError(
  30627. this,
  30628. "from",
  30629. "inOptions.cacheLocation must be of type boolean"
  30630. );
  30631. if (!(typeof inOptions.randomOffset === "boolean" || is_real_number(inOptions.randomOffset)))
  30632. throw this.sequence._customError(
  30633. this,
  30634. "from",
  30635. "inOptions.randomOffset must be of type boolean or number"
  30636. );
  30637. this._temporaryEffect = this._temporaryEffect || (inObject instanceof foundry.abstract.Document ? !is_UUID(inObject?.uuid) : this._temporaryEffect || false);
  30638. if (inOptions.offset) {
  30639. const offsetData = this._validateOffset(
  30640. "attachTo",
  30641. inOptions.offset,
  30642. inOptions
  30643. );
  30644. this._offset = {
  30645. source: offsetData,
  30646. target: this._offset?.target ?? false
  30647. };
  30648. }
  30649. this._randomOffset = {
  30650. source: inOptions.randomOffset,
  30651. target: this._randomOffset?.target ?? false
  30652. };
  30653. this._from = {
  30654. object: inObject,
  30655. options: inOptions
  30656. };
  30657. return this;
  30658. }
  30659. shape(inType, inOptions = {}) {
  30660. if (typeof inType !== "string")
  30661. throw this.sequence._customError(
  30662. this,
  30663. "shape",
  30664. "type must be of type string"
  30665. );
  30666. if (!Object.values(CONSTANTS.SHAPES).includes(inType)) {
  30667. throw this.sequence._customError(
  30668. this,
  30669. "shape",
  30670. "type must be one of: " + Object.values(CONSTANTS.SHAPES).join(", ")
  30671. );
  30672. }
  30673. if (inType === CONSTANTS.SHAPES.POLY) {
  30674. if (!Array.isArray(inOptions.points)) {
  30675. throw this.sequence._customError(
  30676. this,
  30677. "shape",
  30678. "if creating polygon, inOptions.points must be of type array"
  30679. );
  30680. }
  30681. inOptions.points = inOptions.points.map((point) => {
  30682. if (Array.isArray(point)) {
  30683. if (!is_real_number(point[0]) || !is_real_number(point[1])) {
  30684. throw this.sequence._customError(
  30685. this,
  30686. "shape",
  30687. "inOptions.points must be an array, containing an array of two numbers or objects with x and y number properties"
  30688. );
  30689. }
  30690. return point;
  30691. }
  30692. if (typeof point === "object") {
  30693. if (!is_real_number(point?.x) || !is_real_number(point?.y)) {
  30694. throw this.sequence._customError(
  30695. this,
  30696. "shape",
  30697. "inOptions.points must be an array, containing an array of two numbers or objects with x and y number properties"
  30698. );
  30699. }
  30700. return [point.x, point.y];
  30701. }
  30702. });
  30703. } else if (inType === CONSTANTS.SHAPES.CIRC) {
  30704. if (typeof inOptions.radius !== "number") {
  30705. throw this.sequence._customError(
  30706. this,
  30707. "shape",
  30708. "if creating circle, inOptions.radius must be of type number"
  30709. );
  30710. }
  30711. } else if (inType === CONSTANTS.SHAPES.RECT || inType === CONSTANTS.SHAPES.RREC || inType === CONSTANTS.SHAPES.ELIP) {
  30712. if (inOptions.width ^ inOptions.height) {
  30713. inOptions.width = inOptions.width ?? inOptions.height;
  30714. inOptions.height = inOptions.height ?? inOptions.width;
  30715. }
  30716. if (typeof inOptions.width !== "number") {
  30717. throw this.sequence._customError(
  30718. this,
  30719. "shape",
  30720. `if creating rectangle, rounded rectangle, or an ellipse, inOptions.width must be of type number`
  30721. );
  30722. }
  30723. if (typeof inOptions.height !== "number") {
  30724. throw this.sequence._customError(
  30725. this,
  30726. "shape",
  30727. "if creating rectangle, rounded rectangle, or an ellipse, inOptions.height must be of type number"
  30728. );
  30729. }
  30730. if (inType === CONSTANTS.SHAPES.RREC && typeof inOptions.radius !== "number") {
  30731. throw this.sequence._customError(
  30732. this,
  30733. "shape",
  30734. "if creating rounded border rectangle, inOptions.radius must be of type number"
  30735. );
  30736. }
  30737. }
  30738. if (inOptions.gridUnits !== void 0 && typeof inOptions.gridUnits !== "boolean") {
  30739. throw this.sequence._customError(
  30740. this,
  30741. "shape",
  30742. "inOptions.gridUnits must be of type boolean"
  30743. );
  30744. }
  30745. if (inOptions.name && typeof inOptions.name !== "string") {
  30746. throw this.sequence._customError(
  30747. this,
  30748. "shape",
  30749. "inOptions.name must be of type string"
  30750. );
  30751. }
  30752. if (inOptions.fillColor && !is_real_number(inOptions.fillColor) && typeof inOptions.fillColor !== "string") {
  30753. throw this.sequence._customError(
  30754. this,
  30755. "shape",
  30756. "inOptions.fillColor must be of type string (hexadecimal) or number (decimal)"
  30757. );
  30758. } else {
  30759. inOptions.fillColor = parseColor(inOptions.fillColor).decimal;
  30760. }
  30761. if (inOptions.fillAlpha && !is_real_number(inOptions.fillAlpha)) {
  30762. throw this.sequence._customError(
  30763. this,
  30764. "shape",
  30765. "inOptions.fillAlpha must be of type number"
  30766. );
  30767. }
  30768. if (inOptions.alpha && !is_real_number(inOptions.alpha)) {
  30769. throw this.sequence._customError(
  30770. this,
  30771. "shape",
  30772. "inOptions.alpha must be of type number"
  30773. );
  30774. }
  30775. if (inOptions.lineSize && !is_real_number(inOptions.lineSize)) {
  30776. throw this.sequence._customError(
  30777. this,
  30778. "shape",
  30779. "inOptions.lineSize must be of type number"
  30780. );
  30781. }
  30782. if (inOptions.lineColor && !is_real_number(inOptions.lineColor) && typeof inOptions.lineColor !== "string") {
  30783. throw this.sequence._customError(
  30784. this,
  30785. "shape",
  30786. "inOptions.lineColor must be of type string (hexadecimal) or number (decimal)"
  30787. );
  30788. } else {
  30789. inOptions.lineColor = parseColor(inOptions.lineColor).decimal;
  30790. }
  30791. if (inOptions.offset) {
  30792. inOptions.offset = this._validateOffset(
  30793. "shape",
  30794. inOptions.offset,
  30795. inOptions.offset
  30796. );
  30797. }
  30798. if (inOptions.texture !== void 0 && typeof inOptions.texture !== "string") {
  30799. throw this.sequence._customError(
  30800. this,
  30801. "shape",
  30802. "inOptions.texture must be of type string"
  30803. );
  30804. }
  30805. if (inOptions.isMask !== void 0 && typeof inOptions.isMask !== "boolean") {
  30806. throw this.sequence._customError(
  30807. this,
  30808. "shape",
  30809. "inOptions.isMask must be of type boolean"
  30810. );
  30811. }
  30812. this._shapes.push({
  30813. ...inOptions,
  30814. type: inType
  30815. });
  30816. return this;
  30817. }
  30818. /**
  30819. * Causes the effect to be offset relative to its location based on a given vector
  30820. *
  30821. * @param {Object} inOffset
  30822. * @param {Object} inOptions
  30823. * @returns {EffectSection}
  30824. */
  30825. offset(inOffset, inOptions = {}) {
  30826. this.sequence._showWarning(
  30827. this,
  30828. "offset",
  30829. "This method is becoming deprecated, please use the secondary offset option in atLocation, attachTo, stretchTo instead.",
  30830. true
  30831. );
  30832. if (inOffset === void 0)
  30833. throw this.sequence._customError(
  30834. this,
  30835. "offset",
  30836. "inOffset must not be undefined"
  30837. );
  30838. if (typeof inOptions !== "object")
  30839. throw this.sequence._customError(
  30840. this,
  30841. "offset",
  30842. "options must be of type object"
  30843. );
  30844. this._offsetLegacy = this._validateOffset("offset", inOffset, inOptions);
  30845. return this;
  30846. }
  30847. /**
  30848. * Causes the effect's sprite to be offset relative to its location based on a given vector
  30849. *
  30850. * @param {Object} inOffset
  30851. * @param {Object} inOptions
  30852. * @returns {EffectSection}
  30853. */
  30854. spriteOffset(inOffset, inOptions = {}) {
  30855. if (inOffset === void 0)
  30856. throw this.sequence._customError(
  30857. this,
  30858. "spriteOffset",
  30859. "inOffset must not be undefined"
  30860. );
  30861. if (typeof inOptions !== "object")
  30862. throw this.sequence._customError(
  30863. this,
  30864. "spriteOffset",
  30865. "options must be of type object"
  30866. );
  30867. this._spriteOffset = this._validateOffset(
  30868. "spriteOffset",
  30869. inOffset,
  30870. inOptions
  30871. );
  30872. return this;
  30873. }
  30874. /**
  30875. * Causes the final effect location to be snapped to the grid
  30876. *
  30877. * @param {Boolean} inBool
  30878. * @returns {EffectSection}
  30879. */
  30880. snapToGrid(inBool = true) {
  30881. if (typeof inBool !== "boolean")
  30882. throw this.sequence._customError(
  30883. this,
  30884. "snapToGrid",
  30885. "inBool must be of type boolean"
  30886. );
  30887. this._snapToGrid = inBool;
  30888. return this;
  30889. }
  30890. /**
  30891. * Causes the effect to be scaled to the target object's width
  30892. *
  30893. * @param {Number} inScale
  30894. * @param {Object} inOptions
  30895. * @returns {EffectSection}
  30896. */
  30897. scaleToObject(inScale = 1, inOptions = {}) {
  30898. if (!is_real_number(inScale))
  30899. throw this.sequence._customError(
  30900. this,
  30901. "scaleToObject",
  30902. `inScale must be of type number!`
  30903. );
  30904. if (typeof inOptions !== "object")
  30905. throw this.sequence._customError(
  30906. this,
  30907. "scaleToObject",
  30908. "inOptions must be of type object"
  30909. );
  30910. inOptions = foundry.utils.mergeObject(
  30911. {
  30912. scale: inScale,
  30913. considerTokenScale: false,
  30914. uniform: false
  30915. },
  30916. inOptions
  30917. );
  30918. if (typeof inOptions.uniform !== "boolean")
  30919. throw this.sequence._customError(
  30920. this,
  30921. "scaleToObject",
  30922. "inBool must be of type boolean"
  30923. );
  30924. this._scaleToObject = inOptions;
  30925. return this;
  30926. }
  30927. /**
  30928. * Sets the width and the height of the effect in pixels, this size is set before any scaling
  30929. *
  30930. * @param {Number|Object<{width: {Number}, height: {Number}}>} inSize
  30931. * @param {Object} inOptions
  30932. * @returns {EffectSection}
  30933. */
  30934. size(inSize, inOptions = {}) {
  30935. if (!is_real_number(inSize) && typeof inSize !== "object")
  30936. throw this.sequence._customError(
  30937. this,
  30938. "size",
  30939. "inSize must be of type number or object"
  30940. );
  30941. if (typeof inOptions !== "object")
  30942. throw this.sequence._customError(
  30943. this,
  30944. "size",
  30945. "inOptions must be of type object"
  30946. );
  30947. if (is_real_number(inSize)) {
  30948. inSize = {
  30949. width: inSize,
  30950. height: inSize
  30951. };
  30952. }
  30953. if (inSize.width === void 0 ^ inSize.height === void 0) {
  30954. if (inSize.width) {
  30955. if (!is_real_number(inSize.width))
  30956. throw this.sequence._customError(
  30957. this,
  30958. "size",
  30959. "inSize.width must be of type number or string 'auto'"
  30960. );
  30961. inSize["height"] = "auto";
  30962. } else {
  30963. if (!is_real_number(inSize.height))
  30964. throw this.sequence._customError(
  30965. this,
  30966. "size",
  30967. "inSize.height must be of type number or string 'auto'"
  30968. );
  30969. inSize["width"] = "auto";
  30970. }
  30971. }
  30972. inOptions = foundry.utils.mergeObject(
  30973. {
  30974. gridUnits: false
  30975. },
  30976. inOptions
  30977. );
  30978. if (!is_real_number(inSize.width) && inSize.width !== "auto")
  30979. throw this.sequence._customError(
  30980. this,
  30981. "size",
  30982. "inSize.width must be of type number or string 'auto'"
  30983. );
  30984. if (!is_real_number(inSize.height) && inSize.height !== "auto")
  30985. throw this.sequence._customError(
  30986. this,
  30987. "size",
  30988. "inSize.height must be of type number or string 'auto'"
  30989. );
  30990. if (typeof inOptions.gridUnits !== "boolean")
  30991. throw this.sequence._customError(
  30992. this,
  30993. "size",
  30994. "inOptions.gridUnits must be of type boolean"
  30995. );
  30996. this._size = {
  30997. width: inSize.width ?? canvas.grid.size,
  30998. height: inSize.height ?? canvas.grid.size,
  30999. ...inOptions
  31000. };
  31001. return this;
  31002. }
  31003. /**
  31004. * This scales the sprite of the effect, and this method can take the following:
  31005. * - A number to set the scale uniformly
  31006. * - An object with x and y for non-uniform scaling
  31007. * - Two numbers which the Sequencer will randomly pick a uniform scale between
  31008. *
  31009. * @param {number|object} inScaleMin
  31010. * @param {number} [inScaleMax] inScaleMax
  31011. * @returns this
  31012. */
  31013. spriteScale(inScaleMin, inScaleMax) {
  31014. if (!is_real_number(inScaleMin) && typeof inScaleMin !== "object")
  31015. throw this.sequence._customError(
  31016. this,
  31017. "spriteScale",
  31018. "inScale must be of type number or object"
  31019. );
  31020. if (is_real_number(inScaleMin)) {
  31021. if (inScaleMax && !is_real_number(inScaleMax)) {
  31022. throw this.sequence._customError(
  31023. this,
  31024. "spriteScale",
  31025. "if inScaleMin is a number, inScaleMax must also be of type number"
  31026. );
  31027. }
  31028. }
  31029. this._spriteScaleMin = inScaleMin;
  31030. this._spriteScaleMax = inScaleMax ?? false;
  31031. return this;
  31032. }
  31033. /**
  31034. * 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
  31035. * relative to the canvas's grid size. Start and end point defines padding at the left and right of the effect
  31036. *
  31037. * @param {Number} gridSize
  31038. * @param {Number} startPoint
  31039. * @param {Number} endPoint
  31040. * @returns {EffectSection}
  31041. */
  31042. template({ gridSize, startPoint, endPoint } = {}) {
  31043. if (gridSize && !is_real_number(gridSize))
  31044. throw this.sequence._customError(
  31045. this,
  31046. "template",
  31047. "gridSize must be of type number"
  31048. );
  31049. if (startPoint && !is_real_number(startPoint))
  31050. throw this.sequence._customError(
  31051. this,
  31052. "template",
  31053. "startPoint must be of type number"
  31054. );
  31055. if (endPoint && !is_real_number(endPoint))
  31056. throw this.sequence._customError(
  31057. this,
  31058. "template",
  31059. "endPoint must be of type number"
  31060. );
  31061. if (!gridSize && !startPoint && !endPoint)
  31062. throw this.sequence._customError(
  31063. this,
  31064. "template",
  31065. "You need to define at least one parameter!"
  31066. );
  31067. if (!this._template)
  31068. this._template = {};
  31069. if (gridSize)
  31070. this._template["gridSize"] = gridSize;
  31071. if (startPoint)
  31072. this._template["startPoint"] = startPoint;
  31073. if (endPoint)
  31074. this._template["endPoint"] = endPoint;
  31075. return this;
  31076. }
  31077. /**
  31078. * This makes the texture of the effect tile, effectively repeat itself within the sprite's dimensions
  31079. *
  31080. * @param {Object|Number} scale
  31081. * @param {Object} position
  31082. * @returns {EffectSection}
  31083. */
  31084. tilingTexture(scale2 = { x: 1, y: 1 }, position = { x: 0, y: 0 }) {
  31085. if (is_real_number(scale2)) {
  31086. scale2 = { x: scale2, y: scale2 };
  31087. }
  31088. scale2 = { x: scale2?.x ?? 1, y: scale2?.y ?? 1 };
  31089. if (!is_real_number(scale2.x))
  31090. throw this.sequence._customError(
  31091. this,
  31092. "tilingTexture",
  31093. `scale.x must be of type number!`
  31094. );
  31095. if (!is_real_number(scale2.y))
  31096. throw this.sequence._customError(
  31097. this,
  31098. "tilingTexture",
  31099. `scale.y must be of type number!`
  31100. );
  31101. position = { x: position?.x ?? 0, y: position?.y ?? 0 };
  31102. if (!is_real_number(position.x))
  31103. throw this.sequence._customError(
  31104. this,
  31105. "tilingTexture",
  31106. `position.x must be of type number!`
  31107. );
  31108. if (!is_real_number(position.y))
  31109. throw this.sequence._customError(
  31110. this,
  31111. "tilingTexture",
  31112. `position.y must be of type number!`
  31113. );
  31114. this._tilingTexture = {
  31115. scale: scale2,
  31116. position
  31117. };
  31118. return this;
  31119. }
  31120. /**
  31121. * Anchors the sprite's container according to the given x and y coordinates, or uniformly based on a single number
  31122. *
  31123. * @param {Number|Object} inAnchor
  31124. * @returns {EffectSection}
  31125. */
  31126. anchor(inAnchor) {
  31127. if (is_real_number(inAnchor)) {
  31128. inAnchor = {
  31129. x: inAnchor,
  31130. y: inAnchor
  31131. };
  31132. }
  31133. inAnchor = {
  31134. x: inAnchor?.x ?? 0.5,
  31135. y: inAnchor?.y ?? 0.5
  31136. };
  31137. if (!is_real_number(inAnchor.x))
  31138. throw this.sequence._customError(
  31139. this,
  31140. "anchor",
  31141. `inAnchor.x must be of type number!`
  31142. );
  31143. if (!is_real_number(inAnchor.y))
  31144. throw this.sequence._customError(
  31145. this,
  31146. "anchor",
  31147. `inAnchor.y must be of type number!`
  31148. );
  31149. this._anchor = inAnchor;
  31150. return this;
  31151. }
  31152. /**
  31153. * Anchors the sprite according to the given x and y coordinates, or uniformly based on a single number
  31154. *
  31155. * @param {Number|Object} inAnchor
  31156. * @returns {EffectSection}
  31157. */
  31158. spriteAnchor(inAnchor) {
  31159. if (is_real_number(inAnchor)) {
  31160. inAnchor = {
  31161. x: inAnchor,
  31162. y: inAnchor
  31163. };
  31164. }
  31165. inAnchor = {
  31166. x: inAnchor?.x ?? 0.5,
  31167. y: inAnchor?.y ?? 0.5
  31168. };
  31169. if (!is_real_number(inAnchor.x))
  31170. throw this.sequence._customError(
  31171. this,
  31172. "anchor",
  31173. `inAnchor.x must be of type number!`
  31174. );
  31175. if (!is_real_number(inAnchor.y))
  31176. throw this.sequence._customError(
  31177. this,
  31178. "anchor",
  31179. `inAnchor.y must be of type number!`
  31180. );
  31181. this._spriteAnchor = inAnchor;
  31182. return this;
  31183. }
  31184. /**
  31185. * Centers the sprite, effectively giving it an anchor of {x: 0.5, y: 0.5}
  31186. *
  31187. * Note: If this is used, it will override the anchor set by Aim Towards, which sets the sprite's anchor to the
  31188. * outermost edge of the location the sprite is played at
  31189. *
  31190. * @returns {EffectSection}
  31191. */
  31192. center() {
  31193. this.anchor(0.5);
  31194. return this;
  31195. }
  31196. /**
  31197. * The sprite gets a random offset on its target location, usually within the object's bounds. The optional parameter
  31198. * scales how much offset should be added. Defaults to 1.0, which covers the entire target position, 0.5 would cover half.
  31199. *
  31200. * @param {Number} inOffsetScale
  31201. * @returns {EffectSection}
  31202. */
  31203. randomOffset(inOffsetScale = 1) {
  31204. this.sequence._showWarning(
  31205. this,
  31206. "randomOffset",
  31207. "This method has been deprecated, please use randomOffset as a second parameter on atLocation, stretchTo, etc.",
  31208. true
  31209. );
  31210. if (!is_real_number(inOffsetScale))
  31211. throw this.sequence._customError(
  31212. this,
  31213. "randomOffset",
  31214. "inBool must be of type number"
  31215. );
  31216. this._randomOffsetLegacy = inOffsetScale;
  31217. return this;
  31218. }
  31219. /**
  31220. * The sprite gets a randomized flipped X scale. If the scale on that axis was 1, it can
  31221. * become 1 or -1, effectively mirroring the sprite on its horizontal axis
  31222. *
  31223. * @param {Boolean} inBool
  31224. * @returns {EffectSection}
  31225. */
  31226. randomizeMirrorX(inBool = true) {
  31227. if (typeof inBool !== "boolean")
  31228. throw this.sequence._customError(
  31229. this,
  31230. "randomizeMirrorX",
  31231. "inBool must be of type boolean"
  31232. );
  31233. this._randomMirrorX = inBool;
  31234. return this;
  31235. }
  31236. /**
  31237. * The sprite gets a randomized flipped Y scale. If the scale on that axis was 1, it can
  31238. * become 1 or -1, effectively mirroring the sprite on its vertical axis
  31239. *
  31240. * @param {Boolean} inBool
  31241. * @returns {EffectSection}
  31242. */
  31243. randomizeMirrorY(inBool = true) {
  31244. if (typeof inBool !== "boolean")
  31245. throw this.sequence._customError(
  31246. this,
  31247. "randomizeMirrorY",
  31248. "inBool must be of type boolean"
  31249. );
  31250. this._randomMirrorY = inBool;
  31251. return this;
  31252. }
  31253. /**
  31254. * The sprite gets a flipped X scale. If the scale on that axis was 1, it will become 1 or -1, effectively
  31255. * mirroring the sprite on its horizontal axis
  31256. *
  31257. * @param {Boolean} inBool
  31258. * @returns {EffectSection}
  31259. */
  31260. mirrorX(inBool = true) {
  31261. if (typeof inBool !== "boolean")
  31262. throw this.sequence._customError(
  31263. this,
  31264. "mirrorX",
  31265. "inBool must be of type boolean"
  31266. );
  31267. this._mirrorX = inBool;
  31268. return this;
  31269. }
  31270. /**
  31271. * The sprite gets a flipped Y scale. If the scale on that axis was 1, it will become 1 or -1, effectively
  31272. * mirroring the sprite on its vertical axis
  31273. *
  31274. * @param {Boolean} inBool
  31275. * @returns {EffectSection}
  31276. */
  31277. mirrorY(inBool = true) {
  31278. if (typeof inBool !== "boolean")
  31279. throw this.sequence._customError(
  31280. this,
  31281. "mirrorY",
  31282. "inBool must be of type boolean"
  31283. );
  31284. this._mirrorY = inBool;
  31285. return this;
  31286. }
  31287. /**
  31288. * Causes the effect to play beneath most tokens
  31289. *
  31290. * @param {Boolean} inBool
  31291. * @returns {EffectSection}
  31292. */
  31293. belowTokens(inBool = true) {
  31294. if (typeof inBool !== "boolean")
  31295. throw this.sequence._customError(
  31296. this,
  31297. "belowTokens",
  31298. "inBool must be of type boolean"
  31299. );
  31300. if (!inBool)
  31301. return this;
  31302. return this.elevation(0, { absolute: true });
  31303. }
  31304. /**
  31305. * Causes the effect to play beneath most tiles
  31306. *
  31307. * @param {Boolean} inBool
  31308. * @returns {EffectSection}
  31309. */
  31310. belowTiles(inBool = true) {
  31311. if (typeof inBool !== "boolean")
  31312. throw this.sequence._customError(
  31313. this,
  31314. "belowTokens",
  31315. "inBool must be of type boolean"
  31316. );
  31317. if (!inBool)
  31318. return this;
  31319. return this.elevation(-1, { absolute: true });
  31320. }
  31321. /**
  31322. * Causes the effect to be played on top of the vision mask
  31323. *
  31324. * @param {Boolean} inBool
  31325. * @returns {EffectSection}
  31326. */
  31327. aboveLighting(inBool = true) {
  31328. if (typeof inBool !== "boolean")
  31329. throw this.sequence._customError(
  31330. this,
  31331. "aboveLighting",
  31332. "inBool must be of type boolean"
  31333. );
  31334. this._aboveLighting = inBool;
  31335. return this;
  31336. }
  31337. /**
  31338. * Causes the effect to be played on top of interface
  31339. *
  31340. * @param {Boolean} inBool
  31341. * @returns {EffectSection}
  31342. */
  31343. aboveInterface(inBool = true) {
  31344. if (typeof inBool !== "boolean")
  31345. throw this.sequence._customError(
  31346. this,
  31347. "aboveInterface",
  31348. "inBool must be of type boolean"
  31349. );
  31350. this._aboveInterface = inBool;
  31351. return this;
  31352. }
  31353. /**
  31354. * Changes the effect's elevation
  31355. *
  31356. * @param {Number} inElevation
  31357. * @param {Object} inOptions
  31358. * @returns {EffectSection}
  31359. */
  31360. elevation(inElevation, inOptions = {}) {
  31361. if (typeof inElevation !== "number")
  31362. throw this.sequence._customError(
  31363. this,
  31364. "elevation",
  31365. "inElevation must be of type number"
  31366. );
  31367. if (typeof inOptions !== "object")
  31368. throw this.sequence._customError(
  31369. this,
  31370. "elevation",
  31371. `inOptions must be of type object`
  31372. );
  31373. inOptions = foundry.utils.mergeObject(
  31374. {
  31375. elevation: 1,
  31376. absolute: false
  31377. },
  31378. inOptions
  31379. );
  31380. if (typeof inOptions.absolute !== "boolean")
  31381. throw this.sequence._customError(
  31382. this,
  31383. "elevation",
  31384. "inOptions.absolute must be of type boolean"
  31385. );
  31386. this._elevation = {
  31387. elevation: inElevation,
  31388. absolute: inOptions.absolute
  31389. };
  31390. return this;
  31391. }
  31392. /**
  31393. * Sets the zIndex of the effect, potentially displaying it on top of other effects the same elevation
  31394. *
  31395. * @param {Number} inZIndex
  31396. * @returns {EffectSection}
  31397. */
  31398. zIndex(inZIndex) {
  31399. if (!is_real_number(inZIndex))
  31400. throw this.sequence._customError(
  31401. this,
  31402. "zIndex",
  31403. "inZIndex must be of type number"
  31404. );
  31405. this._zIndex = inZIndex;
  31406. return this;
  31407. }
  31408. /**
  31409. * 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.
  31410. *
  31411. * @param {Number} inExtraDuration
  31412. * @returns {EffectSection}
  31413. */
  31414. extraEndDuration(inExtraDuration) {
  31415. if (!is_real_number(inExtraDuration))
  31416. throw this.sequence._customError(
  31417. this,
  31418. "extraEndDuration",
  31419. "inExtraDuration must be of type number"
  31420. );
  31421. this._extraEndDuration = inExtraDuration;
  31422. return this;
  31423. }
  31424. /**
  31425. * Rotates the sprite
  31426. *
  31427. * @param {Number} inAngle
  31428. * @returns {EffectSection}
  31429. */
  31430. spriteRotation(inAngle) {
  31431. if (!is_real_number(inAngle))
  31432. throw this.sequence._customError(
  31433. this,
  31434. "spriteRotation",
  31435. "inAngle must be of type number"
  31436. );
  31437. this._spriteRotation = inAngle;
  31438. return this;
  31439. }
  31440. /**
  31441. * Rotates the sprite
  31442. *
  31443. * @param {Boolean} inBool
  31444. * @returns {EffectSection}
  31445. */
  31446. randomSpriteRotation(inBool = true) {
  31447. if (typeof inBool !== "boolean")
  31448. throw this.sequence._customError(
  31449. this,
  31450. "randomSpriteRotation",
  31451. "inBool must be of type boolean"
  31452. );
  31453. this._randomSpriteRotation = inBool;
  31454. return this;
  31455. }
  31456. /**
  31457. * Causes the effect to not rotate should its container rotate
  31458. *
  31459. * @param {Boolean} [inBool=true] inBool
  31460. * @returns {EffectSection}
  31461. */
  31462. zeroSpriteRotation(inBool = true) {
  31463. if (typeof inBool !== "boolean")
  31464. throw this.sequence._customError(
  31465. this,
  31466. "zeroSpriteRotation",
  31467. "inBool must be of type boolean"
  31468. );
  31469. this._zeroSpriteRotation = inBool;
  31470. return this;
  31471. }
  31472. /**
  31473. * If the effect would loop due to its duration or persistence, this causes it not to
  31474. *
  31475. * @param {Boolean} [inBool=true] inBool
  31476. * @returns {EffectSection}
  31477. */
  31478. noLoop(inBool = true) {
  31479. if (typeof inBool !== "boolean")
  31480. throw this.sequence._customError(
  31481. this,
  31482. "noLoop",
  31483. "inBool must be of type boolean"
  31484. );
  31485. this._noLoop = inBool;
  31486. return this;
  31487. }
  31488. /**
  31489. * Causes the effect to not show up in the Effect Manager UI - DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING
  31490. *
  31491. * @param inBool
  31492. * @returns {EffectSection}
  31493. */
  31494. private(inBool = true) {
  31495. if (typeof inBool !== "boolean")
  31496. throw this.sequence._customError(
  31497. this,
  31498. "private",
  31499. "inBool must be of type boolean"
  31500. );
  31501. this._private = inBool;
  31502. return this;
  31503. }
  31504. /**
  31505. * Causes the effect to be played in screen space instead of world space (where tokens are)
  31506. *
  31507. * @param {Boolean} [inBool=true] inBool
  31508. * @returns {EffectSection}
  31509. */
  31510. screenSpace(inBool = true) {
  31511. if (typeof inBool !== "boolean")
  31512. throw this.sequence._customError(
  31513. this,
  31514. "screenSpace",
  31515. "inBool must be of type boolean"
  31516. );
  31517. this._screenSpace = inBool;
  31518. this._screenSpaceAnchor = this._screenSpaceAnchor ?? { x: 0.5, y: 0.5 };
  31519. return this;
  31520. }
  31521. /**
  31522. * Causes the effect to be played above all of the UI elements
  31523. *
  31524. * @param {Boolean} [inBool=true] inBool
  31525. * @returns {EffectSection}
  31526. */
  31527. screenSpaceAboveUI(inBool = true) {
  31528. if (typeof inBool !== "boolean")
  31529. throw this.sequence._customError(
  31530. this,
  31531. "screenSpaceAboveUI",
  31532. "inBool must be of type boolean"
  31533. );
  31534. this._screenSpaceAboveUI = inBool;
  31535. return this;
  31536. }
  31537. /**
  31538. * Positions the effect in a screen space position, offset from its .screenSpaceAnchor()
  31539. *
  31540. * @param {Object} inPosition
  31541. * @returns {EffectSection}
  31542. */
  31543. screenSpacePosition(inPosition) {
  31544. inPosition = {
  31545. x: inPosition?.x ?? 0,
  31546. y: inPosition?.y ?? 0
  31547. };
  31548. if (!is_real_number(inPosition.x))
  31549. throw this.sequence._customError(
  31550. this,
  31551. "screenSpacePosition",
  31552. `inPosition.x must be of type number!`
  31553. );
  31554. if (!is_real_number(inPosition.y))
  31555. throw this.sequence._customError(
  31556. this,
  31557. "screenSpacePosition",
  31558. `inPosition.y must be of type number!`
  31559. );
  31560. this._screenSpacePosition = inPosition;
  31561. return this;
  31562. }
  31563. /**
  31564. * Anchors the sprite according to the given x and y coordinates, or uniformly based on a single number in screen space
  31565. *
  31566. * @param {Number|Object} inAnchor
  31567. * @returns {EffectSection}
  31568. */
  31569. screenSpaceAnchor(inAnchor) {
  31570. if (is_real_number(inAnchor)) {
  31571. inAnchor = {
  31572. x: inAnchor,
  31573. y: inAnchor
  31574. };
  31575. }
  31576. inAnchor = {
  31577. x: inAnchor?.x ?? 0.5,
  31578. y: inAnchor?.y ?? 0.5
  31579. };
  31580. if (!is_real_number(inAnchor.x))
  31581. throw this.sequence._customError(
  31582. this,
  31583. "screenSpaceAnchor",
  31584. `inAnchor.x must be of type number!`
  31585. );
  31586. if (!is_real_number(inAnchor.y))
  31587. throw this.sequence._customError(
  31588. this,
  31589. "screenSpaceAnchor",
  31590. `inAnchor.y must be of type number!`
  31591. );
  31592. this._screenSpaceAnchor = inAnchor;
  31593. return this;
  31594. }
  31595. /**
  31596. * Sets up various properties relating to scale of the effect on the screen
  31597. *
  31598. * @param {Object} inOptions
  31599. * @returns {EffectSection}
  31600. */
  31601. screenSpaceScale(inOptions) {
  31602. if (typeof inOptions !== "object")
  31603. throw this.sequence._customError(
  31604. this,
  31605. "screenSpaceScale",
  31606. `inOptions must be of type object`
  31607. );
  31608. inOptions = foundry.utils.mergeObject(
  31609. {
  31610. x: 1,
  31611. y: 1,
  31612. fitX: false,
  31613. fitY: false,
  31614. ratioX: false,
  31615. ratioY: false
  31616. },
  31617. inOptions
  31618. );
  31619. if (!is_real_number(inOptions.x))
  31620. throw this.sequence._customError(
  31621. this,
  31622. "screenSpaceScale",
  31623. `inOptions.x must be of type number!`
  31624. );
  31625. if (!is_real_number(inOptions.y))
  31626. throw this.sequence._customError(
  31627. this,
  31628. "screenSpaceScale",
  31629. `inOptions.y must be of type number!`
  31630. );
  31631. if (typeof inOptions.fitX !== "boolean")
  31632. throw this.sequence._customError(
  31633. this,
  31634. "screenSpaceScale",
  31635. "inOptions.fitX must be of type boolean"
  31636. );
  31637. if (typeof inOptions.fitY !== "boolean")
  31638. throw this.sequence._customError(
  31639. this,
  31640. "screenSpaceScale",
  31641. "inOptions.fitY must be of type boolean"
  31642. );
  31643. if (typeof inOptions.ratioX !== "boolean")
  31644. throw this.sequence._customError(
  31645. this,
  31646. "screenSpaceScale",
  31647. "inOptions.ratioX must be of type boolean"
  31648. );
  31649. if (typeof inOptions.ratioY !== "boolean")
  31650. throw this.sequence._customError(
  31651. this,
  31652. "screenSpaceScale",
  31653. "inOptions.ratioY must be of type boolean"
  31654. );
  31655. if (inOptions.ratioX && inOptions.ratioY)
  31656. throw this.sequence._customError(
  31657. this,
  31658. "screenSpaceScale",
  31659. "both ratioX and ratioY cannot be true, one axis must fit or be set directly"
  31660. );
  31661. this._screenSpaceScale = inOptions;
  31662. return this;
  31663. }
  31664. /**
  31665. * This is for adding extra information to an effect, like the origin of the effect in the form of the item's uuid.
  31666. * The method accepts a string or a Document that has an UUID.
  31667. *
  31668. * @param {string|document} inOrigin
  31669. * @returns {Section}
  31670. */
  31671. origin(inOrigin) {
  31672. inOrigin = validate_document(inOrigin);
  31673. if (inOrigin instanceof foundry.abstract.Document) {
  31674. inOrigin = inOrigin?.uuid;
  31675. if (!inOrigin)
  31676. throw this.sequence._customError(
  31677. this,
  31678. "origin",
  31679. "could not find the UUID for the given Document"
  31680. );
  31681. }
  31682. if (typeof inOrigin !== "string")
  31683. throw this.sequence._customError(
  31684. this,
  31685. "origin",
  31686. "inOrigin must be of type string"
  31687. );
  31688. this._origin = inOrigin;
  31689. return this;
  31690. }
  31691. /**
  31692. * Ties the effect to any number of documents in Foundry - if those get deleted, the effect is ended.
  31693. *
  31694. * @param {String|PlaceableObject|foundry.abstract.Document|Array<String|PlaceableObject|foundry.abstract.Document>} inDocuments
  31695. * @returns {EffectSection}
  31696. */
  31697. tieToDocuments(inDocuments) {
  31698. if (!Array.isArray(inDocuments)) {
  31699. inDocuments = [inDocuments];
  31700. }
  31701. for (let doc of inDocuments) {
  31702. if (typeof doc !== "string" && !(doc instanceof PlaceableObject) && !(doc instanceof foundry.abstract.Document)) {
  31703. throw this.sequence._customError(
  31704. this,
  31705. "tieToDocument",
  31706. "inOrigin must be of type string, PlaceableObject, or Document, or an array thereof"
  31707. );
  31708. }
  31709. if (typeof doc === "string") {
  31710. const obj = fromUuidSync(doc);
  31711. if (!obj)
  31712. throw this.sequence._customError(
  31713. this,
  31714. "tieToDocument",
  31715. `could not find document with UUID "${doc}"`
  31716. );
  31717. } else {
  31718. doc = validate_document(doc);
  31719. if (doc instanceof foundry.abstract.Document) {
  31720. doc = doc?.uuid;
  31721. if (!doc)
  31722. throw this.sequence._customError(
  31723. this,
  31724. "tieToDocument",
  31725. "could not find the UUID for the given object"
  31726. );
  31727. }
  31728. }
  31729. this._tiedDocuments.push(doc);
  31730. }
  31731. return this;
  31732. }
  31733. /**
  31734. * Masks the effect to the given object or objects. If no object is given, the effect will be masked to the source
  31735. * of the effect.
  31736. *
  31737. * @param {Token/TokenDocument/Tile/TileDocument/Drawing/DrawingDocument/MeasuredTemplate/MeasuredTemplateDocument/Array} inObject
  31738. * @returns {Section}
  31739. */
  31740. mask(inObject) {
  31741. if (!inObject) {
  31742. this._selfMask = true;
  31743. return this;
  31744. }
  31745. if (Array.isArray(inObject)) {
  31746. for (let obj of inObject) {
  31747. this.mask(obj);
  31748. }
  31749. return this;
  31750. }
  31751. const validatedObject = this._validateLocation(inObject);
  31752. const isValidObject = validatedObject instanceof TokenDocument || validatedObject instanceof TileDocument || validatedObject instanceof DrawingDocument || validatedObject instanceof MeasuredTemplateDocument;
  31753. if (!isValidObject) {
  31754. throw this.sequence._customError(
  31755. this,
  31756. "mask",
  31757. "A foundry object was provided, but only Tokens, Tiles, Drawings, and MeasuredTemplates may be used to create effect masks"
  31758. );
  31759. }
  31760. this._masks.push(get_object_identifier(validatedObject));
  31761. return this;
  31762. }
  31763. /**
  31764. * Causes the effect to be visible through walls
  31765. *
  31766. * @param inBool
  31767. * @returns {EffectSection}
  31768. */
  31769. xray(inBool = true) {
  31770. if (typeof inBool !== "boolean")
  31771. throw this.sequence._customError(
  31772. this,
  31773. "xray",
  31774. "inBool must be of type boolean"
  31775. );
  31776. this._xray = inBool;
  31777. return this;
  31778. }
  31779. /**
  31780. * Configures the isometric configuration of this effect
  31781. *
  31782. * @param inOptions
  31783. * @returns {EffectSection}
  31784. */
  31785. isometric(inOptions = {}) {
  31786. if (typeof inOptions !== "object")
  31787. throw this.sequence._customError(
  31788. this,
  31789. "isometric",
  31790. `inOptions must be of type object`
  31791. );
  31792. inOptions = foundry.utils.mergeObject(
  31793. {
  31794. overlay: false
  31795. },
  31796. inOptions
  31797. );
  31798. if (typeof inOptions?.overlay !== "boolean")
  31799. throw this.sequence._customError(
  31800. this,
  31801. "isometric",
  31802. "inOptions.overlay must be of type boolean"
  31803. );
  31804. this._isometric = inOptions;
  31805. return this;
  31806. }
  31807. /**
  31808. * @private
  31809. */
  31810. _expressWarnings() {
  31811. if (this._stretchTo && this._anchor?.x) {
  31812. this.sequence._showWarning(
  31813. this,
  31814. "stretchTo",
  31815. "you have called .stretchTo() and .anchor() - stretchTo will manually set the X axis of the anchor and may not behave like you expect.",
  31816. true
  31817. );
  31818. }
  31819. if (this._stretchTo && this._scaleToObject) {
  31820. throw this.sequence._customError(
  31821. this,
  31822. "stretchTo",
  31823. "You're trying to stretch towards an object, while scaling to fit another??? Make up your mind!"
  31824. );
  31825. }
  31826. if (this._stretchTo && this._randomRotation) {
  31827. throw this.sequence._customError(
  31828. this,
  31829. "stretchTo",
  31830. "You're trying to stretch towards an object, while trying to randomly rotate the effect? What?"
  31831. );
  31832. }
  31833. if (this._stretchTo && this._moveTowards) {
  31834. throw this.sequence._customError(
  31835. this,
  31836. "stretchTo",
  31837. "You're trying to stretch towards an object, while moving towards it? You're insane."
  31838. );
  31839. }
  31840. if (this._attachTo && this._stretchTo?.attachTo && (this._startTime || this._endTime) && this._isRangedEffect) {
  31841. throw this.sequence._customError(
  31842. this,
  31843. "stretchTo",
  31844. "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."
  31845. );
  31846. }
  31847. const source = this._getSourceObject();
  31848. const target = this._getTargetObject();
  31849. if (!this._screenSpace && this._persistOptions?.persistTokenPrototype && this._masks.filter((uuid) => uuid !== source).length > 0) {
  31850. this.sequence._showWarning(
  31851. this,
  31852. "persist",
  31853. "You have applied persistTokenPrototype with multiple masks from objects in the scene - these will not be persisted across scenes!",
  31854. true
  31855. );
  31856. }
  31857. if (!source && !target && !this._screenSpace) {
  31858. throw this.sequence._customError(
  31859. this,
  31860. "play",
  31861. "Could not determine where to play the effect!"
  31862. );
  31863. }
  31864. }
  31865. /**
  31866. * @OVERRIDE
  31867. */
  31868. async preRun() {
  31869. if (this._from) {
  31870. this._file = this._file || this._from.object?.texture?.src;
  31871. if (this._source === null) {
  31872. this._source = this._validateLocation(this._from.object);
  31873. }
  31874. if (this._size === null) {
  31875. const size = get_object_dimensions(this._from.object);
  31876. this._size = {
  31877. width: size?.width ?? canvas.grid.size,
  31878. height: size?.height ?? canvas.grid.size,
  31879. gridUnits: false
  31880. };
  31881. }
  31882. if (this._mirrorX === null && (this._from.object.mirrorX || this._from.object?.tile && this._from.object?.tile.scale.x < 0)) {
  31883. this._mirrorX = true;
  31884. }
  31885. if (this._mirrorY === null && (this._from.object.mirrorY || this._from.object?.tile && this._from.object?.tile.scale.y < 0)) {
  31886. this._mirrorY = true;
  31887. }
  31888. if (this._angle === null && this._from.object?.rotation) {
  31889. this._angle = -this._from.object.rotation;
  31890. }
  31891. this._randomOffset = {
  31892. source: this._randomOffset?.source ?? this._from.options.randomOffset,
  31893. target: this._randomOffset?.target ?? false
  31894. };
  31895. }
  31896. }
  31897. /**
  31898. * @OVERRIDE
  31899. * @returns {Promise<void>}
  31900. */
  31901. async run() {
  31902. if (!user_can_do("permissions-effect-create") || !this._playEffect) {
  31903. if (!user_can_do("permissions-effect-create")) {
  31904. debounce(EffectSection.debounceWarning, 1e3);
  31905. }
  31906. return new Promise((resolve) => {
  31907. resolve();
  31908. });
  31909. }
  31910. if (!this._deserializedData)
  31911. this._expressWarnings();
  31912. const data = await this._sanitizeEffectData();
  31913. if (Hooks.call("preCreateSequencerEffect", data) === false)
  31914. return;
  31915. let push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
  31916. let canvasEffectData = await Sequencer.EffectManager.play(data, push);
  31917. let totalDuration = this._currentWaitTime;
  31918. if (this._persist) {
  31919. totalDuration += await canvasEffectData.promise;
  31920. } else {
  31921. totalDuration += await canvasEffectData.duration;
  31922. }
  31923. await new Promise((resolve) => setTimeout(resolve, totalDuration));
  31924. }
  31925. /**
  31926. * @private
  31927. */
  31928. _applyTraits() {
  31929. Object.assign(this.constructor.prototype, traits.files);
  31930. Object.assign(this.constructor.prototype, traits.audio);
  31931. Object.assign(this.constructor.prototype, traits.moves);
  31932. Object.assign(this.constructor.prototype, traits.opacity);
  31933. Object.assign(this.constructor.prototype, traits.rotation);
  31934. Object.assign(this.constructor.prototype, traits.scale);
  31935. Object.assign(this.constructor.prototype, traits.time);
  31936. Object.assign(this.constructor.prototype, traits.users);
  31937. Object.assign(this.constructor.prototype, traits.animation);
  31938. Object.assign(this.constructor.prototype, traits.filter);
  31939. Object.assign(this.constructor.prototype, traits.tint);
  31940. Object.assign(this.constructor.prototype, traits.location);
  31941. Object.assign(this.constructor.prototype, traits.offset);
  31942. Object.assign(this.constructor.prototype, traits.text);
  31943. }
  31944. /**
  31945. * @private
  31946. */
  31947. async _initialize() {
  31948. if (this._name) {
  31949. if (!this.sequence.nameOffsetMap) {
  31950. this.sequence.nameOffsetMap = {};
  31951. }
  31952. if (!this.sequence.nameOffsetMap[this._name]) {
  31953. const source = this._getSourceObject();
  31954. const target = this._getTargetObject();
  31955. if (this._offsetLegacy && !this._offset) {
  31956. this._offset = {
  31957. source: !target ? this._offsetLegacy : false,
  31958. target: !!target ? this._offsetLegacy : false
  31959. };
  31960. }
  31961. if (this._randomOffsetLegacy && !this._randomOffset) {
  31962. this._randomOffset = {
  31963. source: !target ? this._randomOffsetLegacy : false,
  31964. target: !!target ? this._randomOffsetLegacy : false
  31965. };
  31966. }
  31967. this.sequence.nameOffsetMap[this._name] = {
  31968. seed: `${this._name}-${randomID()}`,
  31969. source,
  31970. target,
  31971. randomOffset: this._randomOffset,
  31972. missed: this._missed,
  31973. offset: this._offset,
  31974. repetitions: this._repetitions,
  31975. twister: {}
  31976. };
  31977. }
  31978. }
  31979. if (!this._file && !this._from && !this._text && !this._shapes.length && this.sequence.softFail) {
  31980. this._playEffect = false;
  31981. return;
  31982. }
  31983. let fileData = this._file ? await this._determineFile(this._file) : {
  31984. file: this._file,
  31985. forcedIndex: false,
  31986. customRange: false
  31987. };
  31988. this._isRangedEffect = fileData?.file?.rangeFind;
  31989. if (fileData.customRange || fileData.file?.dbPath)
  31990. return;
  31991. let exists = false;
  31992. try {
  31993. exists = await SequencerFileCache.srcExists(fileData.file);
  31994. } catch (err) {
  31995. }
  31996. if (!exists) {
  31997. if (this.sequence.softFail) {
  31998. this._playEffect = false;
  31999. return;
  32000. }
  32001. throw this.sequence._customError(
  32002. this,
  32003. "Play",
  32004. `Could not find file:<br>${fileData.file}`
  32005. );
  32006. }
  32007. }
  32008. /**
  32009. * @private
  32010. */
  32011. _getSourceObject() {
  32012. if (!this._source || typeof this._source !== "object")
  32013. return this._source;
  32014. if (this._source?.cachedLocation || !this._attachTo) {
  32015. return get_object_canvas_data(this._source);
  32016. }
  32017. return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
  32018. }
  32019. /**
  32020. * @private
  32021. */
  32022. _getTargetObject() {
  32023. if (!this._target?.target)
  32024. return this._target;
  32025. if (typeof this._target.target !== "object")
  32026. return this._target.target;
  32027. if (this._target?.target?.cachedLocation || !(this._stretchTo?.attachTo || this._rotateTowards?.attachTo)) {
  32028. return get_object_canvas_data(this._target.target, true);
  32029. }
  32030. return get_object_identifier(this._target.target) ?? get_object_canvas_data(this._target.target, true);
  32031. }
  32032. /**
  32033. * @private
  32034. */
  32035. async _sanitizeEffectData() {
  32036. if (this._deserializedData) {
  32037. this._deserializedData.creationTimestamp = +new Date();
  32038. this._deserializedData.remote = true;
  32039. return this._deserializedData;
  32040. }
  32041. const { file, forcedIndex, customRange } = this._file && this._playEffect ? await this._determineFile(this._file) : {
  32042. file: this._file,
  32043. forcedIndex: false,
  32044. customRange: false
  32045. };
  32046. const source = this._getSourceObject();
  32047. const target = this._getTargetObject();
  32048. if (this._offsetLegacy) {
  32049. this._offset = {
  32050. source: !target && this._offset?.source ? this._offsetLegacy : this._offset?.source,
  32051. target: !!target && this._offset?.target ? this._offsetLegacy : this._offset?.target
  32052. };
  32053. }
  32054. if (this._randomOffsetLegacy) {
  32055. this._randomOffset = {
  32056. source: !target && this._randomOffset?.source ? this._randomOffsetLegacy : this._randomOffset?.source,
  32057. target: !!target && this._randomOffset?.target ? this._randomOffsetLegacy : this._randomOffset?.target
  32058. };
  32059. }
  32060. if (this._selfMask) {
  32061. this._masks.push(
  32062. get_object_identifier(this._source) ?? get_object_canvas_data(this._source)
  32063. );
  32064. }
  32065. let sceneId = game.user.viewedScene;
  32066. if (is_UUID(source)) {
  32067. const potentialSceneId = source.split(".")[1];
  32068. if (game.scenes.get(potentialSceneId)) {
  32069. sceneId = potentialSceneId;
  32070. }
  32071. } else if (is_UUID(target)) {
  32072. const potentialSceneId = target.split(".")[1];
  32073. if (game.scenes.get(potentialSceneId)) {
  32074. sceneId = potentialSceneId;
  32075. }
  32076. }
  32077. let data = foundry.utils.duplicate({
  32078. /**
  32079. * Core properties
  32080. */
  32081. _id: randomID(),
  32082. flagVersion: flagManager.latestFlagVersion,
  32083. sequenceId: this.sequence.id,
  32084. creationTimestamp: +new Date(),
  32085. sceneId,
  32086. creatorUserId: game.userId,
  32087. moduleName: this.sequence.moduleName,
  32088. users: this._users ? Array.from(this._users) : false,
  32089. name: this._name,
  32090. origin: this._origin,
  32091. index: this.sequence.effectIndex,
  32092. repetition: this._currentRepetition,
  32093. private: this._private,
  32094. temporary: this._temporaryEffect,
  32095. tiedDocuments: Array.from(new Set(this._tiedDocuments)),
  32096. /**
  32097. * Source/target properties
  32098. */
  32099. source,
  32100. target,
  32101. rotateTowards: this._rotateTowards,
  32102. stretchTo: this._stretchTo ? {
  32103. attachTo: this._stretchTo.attachTo,
  32104. onlyX: this._stretchTo.onlyX,
  32105. requiresLineOfSight: this._stretchTo.requiresLineOfSight,
  32106. hideLineOfSight: this._stretchTo.hideLineOfSight
  32107. } : false,
  32108. moveTowards: this._moveTowards ? {
  32109. ease: this._moveTowards.ease,
  32110. rotate: this._moveTowards.rotate
  32111. } : false,
  32112. attachTo: this._attachTo,
  32113. missed: this._missed,
  32114. /**
  32115. * Sprite properties
  32116. */
  32117. file: file?.dbPath ?? file,
  32118. customRange,
  32119. forcedIndex,
  32120. text: this._text,
  32121. tilingTexture: this._tilingTexture,
  32122. masks: Array.from(new Set(this._masks)),
  32123. shapes: this._shapes,
  32124. volume: this._volume,
  32125. isometric: this._isometric,
  32126. // Transforms
  32127. scale: this._getCalculatedScale("scale"),
  32128. spriteScale: this._getCalculatedScale("spriteScale"),
  32129. angle: this._angle,
  32130. size: this._size,
  32131. offset: this._offset,
  32132. anchor: this._anchor,
  32133. spriteOffset: this._spriteOffset,
  32134. spriteAnchor: this._spriteAnchor,
  32135. template: this._template,
  32136. zeroSpriteRotation: this._zeroSpriteRotation,
  32137. randomOffset: this._randomOffset,
  32138. randomRotation: this._randomRotation,
  32139. scaleToObject: this._scaleToObject,
  32140. elevation: this._elevation,
  32141. aboveLighting: this._aboveLighting,
  32142. aboveInterface: this._aboveInterface,
  32143. xray: this._xray,
  32144. // Appearance
  32145. zIndex: this._zIndex,
  32146. opacity: is_real_number(this._opacity) ? this._opacity : 1,
  32147. filters: this._filters,
  32148. noLoop: this._noLoop,
  32149. spriteRotation: this._spriteRotation,
  32150. randomSpriteRotation: this._randomSpriteRotation,
  32151. tint: this._tint?.decimal,
  32152. flipX: this._mirrorX || this._randomMirrorX && Math.random() < 0.5,
  32153. flipY: this._mirrorY || this._randomMirrorY && Math.random() < 0.5,
  32154. /**
  32155. * Time properties
  32156. */
  32157. duration: this._duration,
  32158. persist: this._persist,
  32159. persistOptions: this._persistOptions,
  32160. playbackRate: this._playbackRate,
  32161. extraEndDuration: this._extraEndDuration,
  32162. time: this._startTime || this._endTime ? {
  32163. start: is_real_number(this._startTime) ? {
  32164. value: this._startTime,
  32165. isPerc: this._startPerc
  32166. } : false,
  32167. end: is_real_number(this._endTime) ? {
  32168. value: this._endTime,
  32169. isPerc: this._endPerc
  32170. } : false,
  32171. isRange: this._isRange
  32172. } : false,
  32173. /**
  32174. * Animation properties
  32175. */
  32176. moves: this._moveTowards,
  32177. moveSpeed: this._moveSpeed,
  32178. fadeIn: this._fadeIn,
  32179. fadeOut: this._fadeOut,
  32180. scaleIn: this._scaleIn,
  32181. scaleOut: this._scaleOut,
  32182. rotateIn: this._rotateIn,
  32183. rotateOut: this._rotateOut,
  32184. fadeInAudio: this._fadeInAudio,
  32185. fadeOutAudio: this._fadeOutAudio,
  32186. animations: this._animations,
  32187. /**
  32188. * Screenspace properties
  32189. */
  32190. screenSpace: this._screenSpace,
  32191. screenSpaceAboveUI: this._screenSpaceAboveUI,
  32192. screenSpaceAnchor: this._screenSpaceAnchor,
  32193. screenSpacePosition: this._screenSpacePosition,
  32194. screenSpaceScale: this._screenSpaceScale,
  32195. nameOffsetMap: this.sequence.nameOffsetMap
  32196. });
  32197. for (let override of this._overrides) {
  32198. data = await override(this, data);
  32199. }
  32200. if ((typeof data.file !== "string" || data.file === "") && !data.text && !data.shapes && !data.customRange) {
  32201. throw this.sequence._customError(
  32202. this,
  32203. "file",
  32204. "an effect must have a file, text, or have a shape!"
  32205. );
  32206. }
  32207. return data;
  32208. }
  32209. async _serialize() {
  32210. const data = await super._serialize();
  32211. await this.preRun();
  32212. const sectionData = await this._sanitizeEffectData();
  32213. return {
  32214. ...data,
  32215. type: "effect",
  32216. sectionData
  32217. };
  32218. }
  32219. async _deserialize(data) {
  32220. this._deserializedData = data.sectionData;
  32221. return super._deserialize(data);
  32222. }
  32223. /**
  32224. * @private
  32225. */
  32226. _getCalculatedScale(type) {
  32227. const min = this["_" + type + "Min"];
  32228. const max = this["_" + type + "Max"];
  32229. let scale2 = min;
  32230. if (is_real_number(min)) {
  32231. if (max && is_real_number(max)) {
  32232. scale2 = random_float_between(min, max);
  32233. }
  32234. scale2 = {
  32235. x: scale2,
  32236. y: scale2
  32237. };
  32238. }
  32239. return {
  32240. x: scale2?.x ?? 1,
  32241. y: scale2?.y ?? 1
  32242. };
  32243. }
  32244. }
  32245. class SoundSection extends Section {
  32246. constructor(inSequence, inFile = "") {
  32247. super(inSequence);
  32248. this._file = inFile;
  32249. this._volume = 0.8;
  32250. this._overrides = [];
  32251. }
  32252. static niceName = "Sound";
  32253. /**
  32254. * Adds a function that will run at the end of the sound serialization step, but before it is played. Allows direct
  32255. * modifications of sound's data.
  32256. *
  32257. * @param {function} inFunc
  32258. * @returns {SoundSection}
  32259. */
  32260. addOverride(inFunc) {
  32261. if (!is_function$1(inFunc))
  32262. throw this.sequence._customError(
  32263. this,
  32264. "addOverride",
  32265. "The given function needs to be an actual function."
  32266. );
  32267. this._overrides.push(inFunc);
  32268. return this;
  32269. }
  32270. /**
  32271. * @private
  32272. */
  32273. _applyTraits() {
  32274. Object.assign(this.constructor.prototype, traits.files);
  32275. Object.assign(this.constructor.prototype, traits.audio);
  32276. Object.assign(this.constructor.prototype, traits.time);
  32277. Object.assign(this.constructor.prototype, traits.users);
  32278. }
  32279. /**
  32280. * @OVERRIDE
  32281. * @returns {Promise}
  32282. */
  32283. async run() {
  32284. const playData = await this._sanitizeSoundData();
  32285. if (!playData.play && this.sequence.softFail) {
  32286. return new Promise((reject2) => {
  32287. reject2();
  32288. });
  32289. }
  32290. if (!playData?.play) {
  32291. this.sequence._customError(
  32292. this,
  32293. "Play",
  32294. `File not found: ${playData.src}`
  32295. );
  32296. return new Promise((reject2) => reject2());
  32297. }
  32298. if (Hooks.call("preCreateSequencerSound", playData) === false)
  32299. return;
  32300. let push = !(playData?.users?.length === 1 && playData?.users?.includes(game.userId)) && !this.sequence.localOnly;
  32301. SequencerAudioHelper.play(playData, push);
  32302. await new Promise(
  32303. (resolve) => setTimeout(resolve, this._currentWaitTime + playData.duration)
  32304. );
  32305. }
  32306. /**
  32307. * @returns {Promise}
  32308. * @private
  32309. */
  32310. async _sanitizeSoundData() {
  32311. if (this._deserializedData) {
  32312. return this._deserializedData;
  32313. }
  32314. if (!this._file) {
  32315. return {
  32316. play: false,
  32317. src: false
  32318. };
  32319. }
  32320. let { file, forcedIndex } = await this._determineFile(this._file);
  32321. if (!file) {
  32322. return {
  32323. play: false,
  32324. src: false
  32325. };
  32326. }
  32327. if (file instanceof SequencerFileBase) {
  32328. file.forcedIndex = forcedIndex;
  32329. if (file.timeRange) {
  32330. [this._startTime, this._endTime] = file.timeRange;
  32331. this._isRange = true;
  32332. }
  32333. file = file.getFile();
  32334. }
  32335. let soundFile = await AudioHelper.preloadSound(file);
  32336. if (!soundFile || soundFile.failed) {
  32337. return {
  32338. play: false,
  32339. src: this._file
  32340. };
  32341. }
  32342. let duration = soundFile.duration * 1e3;
  32343. let startTime = (this._startTime ? !this._startPerc ? this._startTime : this._startTime * duration : 0) / 1e3;
  32344. if (this._endTime) {
  32345. duration = !this._endPerc ? Number(
  32346. this._isRange ? this._endTime - this._startTime : duration - this._endTime
  32347. ) : this._endTime * duration;
  32348. }
  32349. let data = {
  32350. id: randomID(),
  32351. play: true,
  32352. src: file,
  32353. loop: this._duration > duration,
  32354. volume: this._volume,
  32355. fadeIn: this._fadeInAudio,
  32356. fadeOut: this._fadeOutAudio,
  32357. startTime,
  32358. duration: this._duration || duration,
  32359. sceneId: game.user.viewedScene,
  32360. users: this._users ? Array.from(this._users) : null
  32361. };
  32362. for (let override of this._overrides) {
  32363. data = await override(this, data);
  32364. }
  32365. if (typeof data.src !== "string" || data.src === "") {
  32366. throw this.sequence._customError(
  32367. this,
  32368. "file",
  32369. "a sound must have a src of type string!"
  32370. );
  32371. }
  32372. return data;
  32373. }
  32374. async _serialize() {
  32375. const data = await super._serialize();
  32376. const sectionData = await this._sanitizeSoundData();
  32377. return {
  32378. ...data,
  32379. type: "sound",
  32380. sectionData
  32381. };
  32382. }
  32383. async _deserialize(data) {
  32384. this._deserializedData = data.sectionData;
  32385. return super._deserialize(data);
  32386. }
  32387. }
  32388. class AnimationSection extends Section {
  32389. constructor(inSequence, inTarget) {
  32390. super(inSequence);
  32391. this._teleportTo = false;
  32392. this._originObject = false;
  32393. this._moveSpeed = 23;
  32394. this._offset = { x: 0, y: 0 };
  32395. this._closestSquare = false;
  32396. this._snapToGrid = false;
  32397. this._hide = void 0;
  32398. if (inTarget)
  32399. this.on(inTarget);
  32400. }
  32401. static niceName = "Animation";
  32402. /**
  32403. * Sets the target object to be animated
  32404. *
  32405. * @param {object|string} inTarget
  32406. * @returns {AnimationSection}
  32407. */
  32408. on(inTarget) {
  32409. inTarget = this._validateLocation(inTarget);
  32410. if (!inTarget)
  32411. throw this.sequence._customError(
  32412. this,
  32413. "on",
  32414. "could not find position of given object"
  32415. );
  32416. this._originObject = this._validateLocation(inTarget);
  32417. return this;
  32418. }
  32419. /**
  32420. * Sets the location to teleport the target object to
  32421. *
  32422. * @param {object|string} inTarget
  32423. * @param {object} options
  32424. * @returns {AnimationSection}
  32425. */
  32426. teleportTo(inTarget, options = {}) {
  32427. options = foundry.utils.mergeObject(
  32428. {
  32429. delay: 0,
  32430. relativeToCenter: false
  32431. },
  32432. options
  32433. );
  32434. if (!is_real_number(options.delay))
  32435. throw this.sequence._customError(
  32436. this,
  32437. "teleportTo",
  32438. "options.delay must be of type number"
  32439. );
  32440. inTarget = this._validateLocation(inTarget);
  32441. if (!inTarget)
  32442. throw this.sequence._customError(
  32443. this,
  32444. "teleportTo",
  32445. "could not find position of given object"
  32446. );
  32447. options.target = this._validateLocation(inTarget);
  32448. this._teleportTo = options;
  32449. return this;
  32450. }
  32451. /**
  32452. * Sets the location to rotate the object to
  32453. *
  32454. * @param {object|string} inLocation
  32455. * @param {object} options
  32456. * @returns this
  32457. */
  32458. rotateTowards(inLocation, options = {}) {
  32459. options = foundry.utils.mergeObject(
  32460. {
  32461. duration: 0,
  32462. ease: "linear",
  32463. delay: 0,
  32464. rotationOffset: 0,
  32465. towardsCenter: true,
  32466. cacheLocation: false
  32467. },
  32468. options
  32469. );
  32470. if (!is_real_number(options.duration))
  32471. throw this.sequence._customError(
  32472. this,
  32473. "rotateTowards",
  32474. "options.duration must be of type number"
  32475. );
  32476. if (typeof options.ease !== "string")
  32477. throw this.sequence._customError(
  32478. this,
  32479. "rotateTowards",
  32480. "options.ease must be of type string"
  32481. );
  32482. if (!is_real_number(options.delay))
  32483. throw this.sequence._customError(
  32484. this,
  32485. "rotateTowards",
  32486. "options.delay must be of type number"
  32487. );
  32488. if (!is_real_number(options.rotationOffset))
  32489. throw this.sequence._customError(
  32490. this,
  32491. "rotateTowards",
  32492. "options.rotationOffset must be of type number"
  32493. );
  32494. if (typeof options.towardsCenter !== "boolean")
  32495. throw this.sequence._customError(
  32496. this,
  32497. "rotateTowards",
  32498. "options.towardsCenter must be of type boolean"
  32499. );
  32500. if (typeof options.cacheLocation !== "boolean")
  32501. throw this.sequence._customError(
  32502. this,
  32503. "rotateTowards",
  32504. "options.cacheLocation must be of type boolean"
  32505. );
  32506. options.target = this._validateLocation(inLocation);
  32507. if (!options.target)
  32508. throw this.sequence._customError(
  32509. this,
  32510. "rotateTowards",
  32511. "could not find position of given object"
  32512. );
  32513. options.target = options.cacheLocation ? get_object_position(options.target, { measure: true }) : options.target;
  32514. this._rotateTowards = options;
  32515. return this;
  32516. }
  32517. /**
  32518. * Causes the movement or teleportation to be offset in the X and/or Y axis
  32519. *
  32520. * @param {object} inOffset
  32521. * @returns {AnimationSection}
  32522. */
  32523. offset(inOffset) {
  32524. inOffset = foundry.utils.mergeObject({ x: 0, y: 0 }, inOffset);
  32525. this._offset = this._validateLocation(inOffset);
  32526. return this;
  32527. }
  32528. /**
  32529. * Causes the movement or teleportation to pick the closest non-intersecting square, if the target is a token or tile
  32530. *
  32531. * @param {boolean} inBool
  32532. * @returns {AnimationSection}
  32533. */
  32534. closestSquare(inBool = true) {
  32535. if (typeof inBool !== "boolean")
  32536. throw this.sequence._customError(
  32537. this,
  32538. "closestSquare",
  32539. "inBool must be of type boolean"
  32540. );
  32541. this._closestSquare = inBool;
  32542. return this;
  32543. }
  32544. /**
  32545. * Causes the final location to be snapped to the grid
  32546. *
  32547. * @param {boolean} inBool
  32548. * @returns {AnimationSection}
  32549. */
  32550. snapToGrid(inBool = true) {
  32551. if (typeof inBool !== "boolean")
  32552. throw this.sequence._customError(
  32553. this,
  32554. "snapToGrid",
  32555. "inBool must be of type boolean"
  32556. );
  32557. this._snapToGrid = inBool;
  32558. return this;
  32559. }
  32560. /**
  32561. * Causes the object to become hidden
  32562. *
  32563. * @param {boolean} inBool
  32564. * @returns {AnimationSection}
  32565. */
  32566. hide(inBool = true) {
  32567. if (typeof inBool !== "boolean")
  32568. throw this.sequence._customError(
  32569. this,
  32570. "hide",
  32571. "inBool must be of type boolean"
  32572. );
  32573. this._hide = inBool;
  32574. return this;
  32575. }
  32576. /**
  32577. * Causes the object to become visible
  32578. *
  32579. * @param {boolean} inBool
  32580. * @returns {AnimationSection}
  32581. */
  32582. show(inBool = true) {
  32583. if (typeof inBool !== "boolean")
  32584. throw this.sequence._customError(
  32585. this,
  32586. "show",
  32587. "inBool must be of type boolean"
  32588. );
  32589. this._hide = !inBool;
  32590. return this;
  32591. }
  32592. /**
  32593. * @private
  32594. */
  32595. async run() {
  32596. return this._runAnimate();
  32597. }
  32598. /**
  32599. * @private
  32600. */
  32601. _applyTraits() {
  32602. Object.assign(this.constructor.prototype, traits.moves);
  32603. Object.assign(this.constructor.prototype, traits.opacity);
  32604. Object.assign(this.constructor.prototype, traits.rotation);
  32605. Object.assign(this.constructor.prototype, traits.audio);
  32606. Object.assign(this.constructor.prototype, traits.tint);
  32607. }
  32608. /**
  32609. * @private
  32610. */
  32611. async _updateObject(obj, updates, animate = false, animation2 = {}) {
  32612. const uuid = obj?.uuid ?? obj?.document?.uuid;
  32613. await sequencerSocket.executeAsGM(
  32614. SOCKET_HANDLERS.UPDATE_DOCUMENT,
  32615. uuid,
  32616. updates,
  32617. { animate, animation: animation2 }
  32618. );
  32619. }
  32620. /**
  32621. * @private
  32622. */
  32623. async _waitForTokenRefresh(obj) {
  32624. let token;
  32625. if (obj instanceof Token) {
  32626. token = obj;
  32627. }
  32628. if (obj instanceof TokenDocument) {
  32629. token = obj.object;
  32630. }
  32631. if (token == null || token.mesh != null) {
  32632. return;
  32633. }
  32634. let refreshTokenID;
  32635. return await new Promise((resolve) => {
  32636. refreshTokenID = Hooks.on("refreshToken", async (tokenDoc) => {
  32637. if (tokenDoc.document.actorId !== token.document.actorId || !token.mesh)
  32638. return;
  32639. Hooks.off("refreshToken", refreshTokenID);
  32640. resolve();
  32641. });
  32642. });
  32643. }
  32644. /**
  32645. * @private
  32646. */
  32647. async _execute() {
  32648. if (!await this._shouldPlay())
  32649. return;
  32650. let self = this;
  32651. this._basicDelay = random_float_between(this._delayMin, this._delayMax);
  32652. return new Promise(async (resolve) => {
  32653. setTimeout(async () => {
  32654. await this._waitForTokenRefresh(this._originObject);
  32655. if (this._shouldAsync) {
  32656. await self.run();
  32657. } else {
  32658. self.run();
  32659. }
  32660. resolve();
  32661. }, this._basicDelay);
  32662. });
  32663. }
  32664. /**
  32665. * @private
  32666. */
  32667. _getClosestSquare(origin, target) {
  32668. let originLoc = get_object_position(origin, { exact: true });
  32669. let targetLoc = get_object_position(target, { exact: true });
  32670. let originDimensions = get_object_dimensions(origin);
  32671. let targetDimensions = get_object_dimensions(target);
  32672. let originBottom = Math.max(
  32673. originDimensions.width - canvas.grid.size,
  32674. canvas.grid.size
  32675. );
  32676. let originRight = Math.max(
  32677. originDimensions.height - canvas.grid.size,
  32678. canvas.grid.size
  32679. );
  32680. let ray = new Ray(originLoc, targetLoc);
  32681. let dx = ray.dx;
  32682. let dy = ray.dy;
  32683. if (dx > 0 && Math.abs(dx) > originRight) {
  32684. dx -= originDimensions.width;
  32685. } else if (dx < 0 && Math.abs(dx) > targetDimensions.width) {
  32686. dx += targetDimensions.height;
  32687. } else {
  32688. dx = 0;
  32689. }
  32690. if (dy > 0 && Math.abs(dy) > originBottom) {
  32691. dy -= originDimensions.height;
  32692. } else if (dy < 0 && Math.abs(dy) > targetDimensions.height) {
  32693. dy += targetDimensions.height;
  32694. } else {
  32695. dy = 0;
  32696. }
  32697. const pos = {
  32698. x: originLoc.x + dx,
  32699. y: originLoc.y + dy,
  32700. elevation: targetLoc?.elevation
  32701. };
  32702. if (!pos.elevation) {
  32703. delete pos["elevation"];
  32704. }
  32705. return pos;
  32706. }
  32707. /**
  32708. * @private
  32709. */
  32710. _snapLocationToGrid(inLocation) {
  32711. return canvas.grid.getSnappedPosition(inLocation.x, inLocation.y);
  32712. }
  32713. /**
  32714. * This needs a rewrite, jeesus.
  32715. */
  32716. async _runAnimate() {
  32717. let animData = {
  32718. attributes: [],
  32719. maxFPS: 1e3 / game.settings.get("core", "maxFPS"),
  32720. lastTimespan: performance.now(),
  32721. totalDt: 0
  32722. };
  32723. let overallDuration = this._duration ? this._duration : 0;
  32724. const originLocation = get_object_position(this._originObject, {
  32725. exact: true
  32726. });
  32727. if (this._rotateTowards) {
  32728. let offset2 = (this._angle ? this._angle : 0) + this._rotateTowards.rotationOffset;
  32729. let targetLoc = this._moveTowards?.target || this._teleportTo?.target || this._originObject;
  32730. targetLoc = this._closestSquare ? this._getClosestSquare(this._originObject, targetLoc) : get_object_position(targetLoc, { exact: true });
  32731. targetLoc.x += this._offset.x;
  32732. targetLoc.y += this._offset.y;
  32733. if (this._snapToGrid) {
  32734. targetLoc = this._snapLocationToGrid(targetLoc);
  32735. }
  32736. if (this._originObject instanceof TokenDocument) {
  32737. setTimeout(async () => {
  32738. let startLocation = this._originObject.object?.center ?? targetLoc;
  32739. let targetLocation = this._rotateTowards.target?.object ?? this._rotateTowards.target;
  32740. if (this._rotateTowards.towardsCenter) {
  32741. targetLocation = targetLocation?.center ?? targetLocation;
  32742. }
  32743. let ray = new Ray(startLocation, targetLocation);
  32744. let angle = Math.normalizeDegrees(ray.angle * 180 / Math.PI - 90);
  32745. angle += offset2;
  32746. await this._updateObject(
  32747. this._originObject,
  32748. { rotation: angle },
  32749. this._rotateTowards.duration > 0,
  32750. {
  32751. duration: this._rotateTowards.duration,
  32752. easing: this._rotateTowards.ease
  32753. }
  32754. );
  32755. }, this._rotateTowards.delay ?? 0);
  32756. } else {
  32757. animData.attributes.push({
  32758. name: "rotationTowards",
  32759. offset: offset2,
  32760. origin: this._originObject,
  32761. originLocation: targetLoc,
  32762. target: this._rotateTowards.target?.object ?? this._rotateTowards.target,
  32763. towardsCenter: this._rotateTowards.towardsCenter,
  32764. from: false,
  32765. to: false,
  32766. progress: 0,
  32767. done: false,
  32768. duration: this._rotateTowards.duration,
  32769. durationDone: 0,
  32770. delay: this._rotateTowards.delay,
  32771. ease: this._rotateTowards.ease
  32772. });
  32773. }
  32774. let rotateDuration = this._rotateTowards.duration + this._rotateTowards.delay;
  32775. overallDuration = overallDuration > rotateDuration ? overallDuration : rotateDuration;
  32776. }
  32777. if (this._fadeIn) {
  32778. let to = is_real_number(this._opacity) ? this._opacity : 1;
  32779. if (this._originObject instanceof TokenDocument) {
  32780. setTimeout(async () => {
  32781. await this._updateObject(this._originObject, { alpha: to }, true, {
  32782. duration: this._fadeIn.duration,
  32783. easing: this._fadeIn.ease
  32784. });
  32785. }, this._fadeIn.delay ?? 0);
  32786. } else {
  32787. animData.attributes.push({
  32788. name: "alpha",
  32789. from: 0,
  32790. to,
  32791. progress: 0,
  32792. done: false,
  32793. duration: this._fadeIn.duration,
  32794. durationDone: 0,
  32795. delay: this._fadeIn.delay,
  32796. ease: this._fadeIn.ease
  32797. });
  32798. }
  32799. let fadeDuration = this._fadeIn.duration + this._fadeIn.delay;
  32800. overallDuration = overallDuration > fadeDuration ? overallDuration : fadeDuration;
  32801. }
  32802. if (this._fadeInAudio && this._originObject?.video?.volume !== void 0) {
  32803. let to = is_real_number(this._volume) ? this._volume : 1;
  32804. animData.attributes.push({
  32805. name: "video.volume",
  32806. from: 0,
  32807. to,
  32808. progress: 0,
  32809. done: false,
  32810. duration: this._fadeInAudio.duration,
  32811. durationDone: 0,
  32812. delay: this._fadeInAudio.delay,
  32813. ease: this._fadeInAudio.ease
  32814. });
  32815. let fadeDuration = this._fadeInAudio.duration + this._fadeInAudio.delay;
  32816. overallDuration = overallDuration > fadeDuration ? overallDuration : fadeDuration;
  32817. }
  32818. if (this._rotateIn) {
  32819. let from = this._angle ? this._angle : this._originObject.rotation;
  32820. let to = this._rotateIn.value;
  32821. if (Math.abs(from - to) > 180) {
  32822. if (to < 0) {
  32823. to += 360;
  32824. } else if (from > to) {
  32825. from -= 360;
  32826. }
  32827. }
  32828. if (this._originObject instanceof TokenDocument) {
  32829. setTimeout(async () => {
  32830. if (this._originObject.rotation !== from) {
  32831. await this._updateObject(
  32832. this._originObject,
  32833. { rotation: from },
  32834. false
  32835. );
  32836. }
  32837. await this._updateObject(this._originObject, { rotation: to }, true, {
  32838. duration: this._rotateIn.duration,
  32839. easing: this._rotateIn.ease
  32840. });
  32841. }, this._rotateIn.delay ?? 0);
  32842. } else {
  32843. animData.attributes.push({
  32844. name: "rotation",
  32845. from,
  32846. to,
  32847. progress: 0,
  32848. done: false,
  32849. duration: this._rotateIn.duration,
  32850. durationDone: 0,
  32851. delay: this._rotateIn.delay,
  32852. ease: this._rotateIn.ease
  32853. });
  32854. }
  32855. let rotateDuration = this._rotateIn.duration + this._rotateIn.delay;
  32856. overallDuration = overallDuration > rotateDuration ? overallDuration : rotateDuration;
  32857. }
  32858. if (this._moveTowards) {
  32859. let originLocation2 = get_object_position(this._originObject, {
  32860. exact: true
  32861. });
  32862. let targetLocation = this._closestSquare ? this._getClosestSquare(this._originObject, this._moveTowards.target) : get_object_position(this._moveTowards.target, {
  32863. exact: true
  32864. });
  32865. targetLocation.x += this._offset.x;
  32866. targetLocation.y += this._offset.y;
  32867. targetLocation.elevation = targetLocation?.elevation ?? this._originObject?.elevation;
  32868. if (this._moveTowards.relativeToCenter) {
  32869. const dimensions = get_object_dimensions(this._originObject);
  32870. targetLocation.x -= dimensions.width / 2;
  32871. targetLocation.y -= dimensions.height / 2;
  32872. if (this._snapToGrid) {
  32873. targetLocation.x -= 0.01;
  32874. targetLocation.y -= 0.01;
  32875. }
  32876. }
  32877. if (this._snapToGrid) {
  32878. targetLocation = this._snapLocationToGrid(targetLocation);
  32879. }
  32880. let originalDx = targetLocation.x - originLocation2.x;
  32881. let originalDy = targetLocation.y - originLocation2.y;
  32882. let originalDistance = Math.sqrt(
  32883. originalDx * originalDx + originalDy * originalDy
  32884. );
  32885. let duration = this._duration ? this._duration : originalDistance / this._moveSpeed * animData.maxFPS;
  32886. let moveDuration = duration + this._moveTowards.delay;
  32887. overallDuration = overallDuration > moveDuration ? overallDuration : moveDuration;
  32888. if (this._originObject instanceof TokenDocument) {
  32889. setTimeout(async () => {
  32890. await this._updateObject(this._originObject, targetLocation, true, {
  32891. movementSpeed: this._moveSpeed,
  32892. duration,
  32893. easing: this._moveTowards.ease
  32894. });
  32895. }, this._moveTowards.delay ?? 0);
  32896. } else {
  32897. if (!(duration === 0 || originalDistance === 0)) {
  32898. animData.attributes.push({
  32899. name: "position",
  32900. origin: originLocation2,
  32901. target: targetLocation,
  32902. originalDistance,
  32903. currentDistance: 0,
  32904. progress: 0,
  32905. speed: 0,
  32906. duration,
  32907. done: false,
  32908. ease: this._moveTowards.ease,
  32909. delay: this._moveTowards.delay
  32910. });
  32911. }
  32912. }
  32913. }
  32914. if (this._fadeOut) {
  32915. let from = is_real_number(this._opacity) ? this._opacity : this._originObject.alpha ?? 1;
  32916. if (this._originObject instanceof TokenDocument) {
  32917. setTimeout(async () => {
  32918. await this._updateObject(this._originObject, { alpha: 0 }, true, {
  32919. duration: this._fadeOut.duration,
  32920. easing: this._fadeOut.ease
  32921. });
  32922. }, this._fadeOut.delay ?? 0);
  32923. } else {
  32924. animData.attributes.push({
  32925. name: "alpha",
  32926. from,
  32927. to: 0,
  32928. progress: 0,
  32929. done: false,
  32930. duration: this._fadeOut.duration,
  32931. durationDone: 0,
  32932. delay: overallDuration - this._fadeOut.duration,
  32933. ease: this._fadeOut.ease
  32934. });
  32935. }
  32936. let fadeOutDuration = this._fadeOut.duration + this._fadeOut.delay;
  32937. overallDuration = overallDuration > fadeOutDuration ? overallDuration : fadeOutDuration;
  32938. }
  32939. if (this._fadeOutAudio && this._originObject?.video?.volume !== void 0) {
  32940. let from = is_real_number(this._volume) ? this._volume : this._originObject.video.volume;
  32941. animData.attributes.push({
  32942. name: "video.volume",
  32943. from,
  32944. to: 0,
  32945. progress: 0,
  32946. done: false,
  32947. duration: this._fadeOutAudio.duration,
  32948. durationDone: 0,
  32949. delay: overallDuration - this._fadeOutAudio.duration,
  32950. ease: this._fadeOutAudio.ease
  32951. });
  32952. let fadeOutAudioDuration = this._fadeOutAudio.duration + this._fadeOutAudio.delay;
  32953. overallDuration = overallDuration > fadeOutAudioDuration ? overallDuration : fadeOutAudioDuration;
  32954. }
  32955. if (this._rotateOut) {
  32956. let from = this._rotateOut.value;
  32957. let to = this._angle ? this._angle : this._originObject.rotation;
  32958. if (this._rotateIn)
  32959. from += this._rotateIn.value;
  32960. if (Math.abs(from - to) > 180) {
  32961. if (to < 0) {
  32962. to += 360;
  32963. } else if (from > to) {
  32964. from -= 360;
  32965. }
  32966. }
  32967. if (this._originObject instanceof TokenDocument) {
  32968. setTimeout(async () => {
  32969. if (this._originObject.rotation !== from) {
  32970. await this._updateObject(
  32971. this._originObject,
  32972. { rotation: from },
  32973. false
  32974. );
  32975. }
  32976. await this._updateObject(this._originObject, { rotation: to }, true, {
  32977. duration: this._rotateOut.duration,
  32978. easing: this._rotateOut.ease
  32979. });
  32980. }, this._rotateOut.delay ?? 0);
  32981. } else {
  32982. animData.attributes.push({
  32983. name: "rotation",
  32984. from,
  32985. to,
  32986. progress: 0,
  32987. done: false,
  32988. duration: this._rotateOut.duration,
  32989. durationDone: 0,
  32990. delay: overallDuration - this._rotateOut.duration,
  32991. ease: this._rotateOut.ease
  32992. });
  32993. }
  32994. let rotateOutDuration = this._rotateOut.duration + this._rotateOut.delay;
  32995. overallDuration = overallDuration > rotateOutDuration ? overallDuration : rotateOutDuration;
  32996. }
  32997. if (this._teleportTo) {
  32998. setTimeout(async () => {
  32999. let targetLocation = this._closestSquare ? this._getClosestSquare(this._originObject, this._teleportTo.target) : get_object_position(this._teleportTo.target, {
  33000. exact: true
  33001. });
  33002. if (targetLocation.x === void 0)
  33003. targetLocation.x = originLocation.x;
  33004. if (targetLocation.y === void 0)
  33005. targetLocation.y = originLocation.y;
  33006. if (targetLocation.elevation === void 0)
  33007. targetLocation.elevation = originLocation.elevation;
  33008. targetLocation.x += this._offset.x;
  33009. targetLocation.y += this._offset.y;
  33010. targetLocation.elevation = targetLocation?.elevation ?? this._originObject?.elevation;
  33011. const dimensions = get_object_dimensions(this._originObject);
  33012. if (this._teleportTo.relativeToCenter) {
  33013. targetLocation.x -= dimensions.width / 2;
  33014. targetLocation.y -= dimensions.height / 2;
  33015. if (this._snapToGrid) {
  33016. targetLocation.x -= 0.01;
  33017. targetLocation.y -= 0.01;
  33018. }
  33019. }
  33020. if (this._snapToGrid) {
  33021. targetLocation.x -= dimensions.width / 2;
  33022. targetLocation.y -= dimensions.height / 2;
  33023. targetLocation = this._snapLocationToGrid(targetLocation);
  33024. }
  33025. await this._updateObject(this._originObject, targetLocation, false);
  33026. }, this._teleportTo.delay);
  33027. if (overallDuration <= this._teleportTo.delay) {
  33028. this._waitUntilFinished = true;
  33029. }
  33030. overallDuration = overallDuration > this._teleportTo.delay ? overallDuration : this._teleportTo.delay;
  33031. }
  33032. let updateAttributes = {};
  33033. if (is_real_number(this._angle) && !this._rotateIn && !this._rotateOut) {
  33034. updateAttributes["rotation"] = this._angle;
  33035. }
  33036. if (is_real_number(this._opacity) && !this._fadeIn && !this._fadeOut) {
  33037. updateAttributes["alpha"] = this._opacity;
  33038. }
  33039. if (is_real_number(this._volume) && !this._fadeInAudio && !this._fadeOutAudio && this._originObject?.video?.volume !== void 0) {
  33040. updateAttributes["video.volume"] = this._volume;
  33041. }
  33042. if (this._tint) {
  33043. updateAttributes["tint"] = this._tint.hexadecimal;
  33044. }
  33045. if (this._hide !== void 0) {
  33046. updateAttributes["hidden"] = this._hide;
  33047. }
  33048. if (Object.keys(updateAttributes).length) {
  33049. await wait$1(1);
  33050. await this._updateObject(this._originObject, updateAttributes);
  33051. await wait$1(1);
  33052. }
  33053. return new Promise(async (resolve) => {
  33054. this._animate(animData, resolve);
  33055. setTimeout(
  33056. resolve,
  33057. Math.max(0, overallDuration + this._currentWaitTime + animData.maxFPS)
  33058. );
  33059. });
  33060. }
  33061. /**
  33062. * @private
  33063. */
  33064. async _animate(animData, resolve, timespan) {
  33065. if (timespan) {
  33066. let animatedAttributes = {};
  33067. let dt = timespan - animData.lastTimespan;
  33068. if (dt >= animData.maxFPS) {
  33069. animData.totalDt += dt;
  33070. for (let attribute of animData.attributes) {
  33071. if (attribute.done)
  33072. continue;
  33073. if (animData.totalDt < attribute.delay)
  33074. continue;
  33075. if (attribute.name === "position") {
  33076. attribute.speed = attribute.originalDistance / (attribute.duration / dt);
  33077. attribute.currentDistance += attribute.speed;
  33078. attribute.progress = attribute.currentDistance / attribute.originalDistance;
  33079. let x = interpolate(
  33080. attribute.origin.x,
  33081. attribute.target.x,
  33082. attribute.progress,
  33083. attribute.ease
  33084. );
  33085. let y = interpolate(
  33086. attribute.origin.y,
  33087. attribute.target.y,
  33088. attribute.progress,
  33089. attribute.ease
  33090. );
  33091. if (attribute.currentDistance >= attribute.originalDistance) {
  33092. x = attribute.target.x;
  33093. y = attribute.target.y;
  33094. attribute.done = true;
  33095. }
  33096. animatedAttributes["x"] = x;
  33097. animatedAttributes["y"] = y;
  33098. } else {
  33099. if (attribute.name === "rotationTowards" && !attribute.from && !attribute.to) {
  33100. let target = attribute.target;
  33101. if (this._rotateTowards.towardsCenter)
  33102. target = target?.center ?? target;
  33103. let ray = new Ray(attribute.originLocation, target);
  33104. let angle = ray.angle * 180 / Math.PI - 90;
  33105. angle += attribute.offset;
  33106. attribute.from = attribute.origin.rotation;
  33107. attribute.to = angle;
  33108. if (Math.abs(attribute.from - attribute.to) > 180) {
  33109. if (attribute.to < 0) {
  33110. attribute.to += 360;
  33111. } else if (attribute.from > attribute.to) {
  33112. attribute.from -= 360;
  33113. }
  33114. }
  33115. attribute.name = "rotation";
  33116. }
  33117. attribute.durationDone += dt;
  33118. attribute.progress = attribute.durationDone / attribute.duration;
  33119. let val = interpolate(
  33120. attribute.from,
  33121. attribute.to,
  33122. attribute.progress,
  33123. attribute.ease
  33124. );
  33125. if (attribute.progress >= 1) {
  33126. val = attribute.to;
  33127. attribute.done = true;
  33128. }
  33129. animatedAttributes[attribute.name] = val;
  33130. }
  33131. }
  33132. if (Object.keys(animatedAttributes).length > 0) {
  33133. await this._updateObject(this._originObject, animatedAttributes);
  33134. }
  33135. animData.attributes = animData.attributes.filter((a) => !a.done);
  33136. if (animData.attributes.length === 0)
  33137. return;
  33138. animData.lastTimespan = timespan;
  33139. }
  33140. }
  33141. let self = this;
  33142. requestAnimationFrame(function(timespan2) {
  33143. self._animate(animData, resolve, timespan2);
  33144. });
  33145. }
  33146. }
  33147. class ScrollingTextSection extends Section {
  33148. constructor(inSequence, target, text2, textOptions) {
  33149. super(inSequence);
  33150. this._deserializedData = null;
  33151. this._source = null;
  33152. this._text = "";
  33153. this._duration = 2e3;
  33154. this._distance = null;
  33155. this._jitter = 0;
  33156. this._anchor = null;
  33157. this._direction = null;
  33158. this._seed = get_hash(randomID());
  33159. if (target) {
  33160. this.atLocation(target);
  33161. }
  33162. if (text2) {
  33163. this.text(text2, textOptions);
  33164. }
  33165. }
  33166. static niceName = "Scrolling Text";
  33167. /**
  33168. * @private
  33169. */
  33170. _applyTraits() {
  33171. Object.assign(this.constructor.prototype, traits.users);
  33172. Object.assign(this.constructor.prototype, traits.location);
  33173. Object.assign(this.constructor.prototype, traits.offset);
  33174. Object.assign(this.constructor.prototype, traits.text);
  33175. }
  33176. direction(inDirection) {
  33177. if (!(typeof inDirection === "string" || is_real_number(inDirection))) {
  33178. throw this.sequence._customError(
  33179. this,
  33180. "direction",
  33181. "inDirection must be of type string (CONST.TEXT_ANCHOR_POINTS) or number"
  33182. );
  33183. }
  33184. if (typeof inDirection === "string" && !CONST.TEXT_ANCHOR_POINTS[inDirection]) {
  33185. throw this.sequence._customError(
  33186. this,
  33187. "direction",
  33188. `${inDirection} does not exist in CONST.TEXT_ANCHOR_POINTS!`
  33189. );
  33190. } else if (typeof inDirection === "string") {
  33191. this._direction = CONST.TEXT_ANCHOR_POINTS[inDirection];
  33192. } else {
  33193. this._direction = inDirection;
  33194. }
  33195. return this;
  33196. }
  33197. anchor(inAnchor) {
  33198. if (!(typeof inAnchor === "string" || is_real_number(inAnchor))) {
  33199. throw this.sequence._customError(
  33200. this,
  33201. "direction",
  33202. "inAnchor must be of type string (CONST.TEXT_ANCHOR_POINTS) or number"
  33203. );
  33204. }
  33205. if (typeof inAnchor === "string" && !CONST.TEXT_ANCHOR_POINTS[inAnchor] || is_real_number(inAnchor) && !Object.values(CONST.TEXT_ANCHOR_POINTS).includes(inAnchor)) {
  33206. throw this.sequence._customError(
  33207. this,
  33208. "direction",
  33209. `${inAnchor} does not exist in CONST.TEXT_ANCHOR_POINTS!`
  33210. );
  33211. } else if (typeof inAnchor === "string") {
  33212. this._anchor = CONST.TEXT_ANCHOR_POINTS[inAnchor];
  33213. } else {
  33214. this._anchor = inAnchor;
  33215. }
  33216. return this;
  33217. }
  33218. jitter(inJitter) {
  33219. if (!(is_real_number(inJitter) && inJitter >= 0 && inJitter <= 1)) {
  33220. throw this.sequence._customError(
  33221. this,
  33222. "jitter",
  33223. "inJitter must be of type number between 0 and 1"
  33224. );
  33225. }
  33226. this._jitter = inJitter;
  33227. return this;
  33228. }
  33229. async run() {
  33230. const data = await this._sanitizeTextData();
  33231. if (Hooks.call("preCreateScrollingText", data) === false)
  33232. return;
  33233. const push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
  33234. const duration = SequencerFoundryReplicator.playScrollingText(data, push);
  33235. await new Promise(
  33236. (resolve) => setTimeout(resolve, this._currentWaitTime + duration)
  33237. );
  33238. }
  33239. _getSourceObject() {
  33240. if (!this._source || typeof this._source !== "object")
  33241. return this._source;
  33242. return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
  33243. }
  33244. async _sanitizeTextData() {
  33245. if (this._deserializedData) {
  33246. return this._deserializedData;
  33247. }
  33248. return {
  33249. sceneId: game.user.viewedScene,
  33250. seed: this._seed,
  33251. sequenceId: this.sequence.id,
  33252. creatorUserId: game.userId,
  33253. users: this._users ? Array.from(this._users) : false,
  33254. moduleName: this.sequence.moduleName,
  33255. source: this._getSourceObject(),
  33256. offset: this._offset?.source ?? false,
  33257. randomOffset: this._randomOffset?.source ?? false,
  33258. content: this._text?.text ?? "",
  33259. options: {
  33260. anchor: this._anchor,
  33261. direction: this._direction,
  33262. duration: this._duration,
  33263. distance: this._distance,
  33264. jitter: this._jitter,
  33265. ...this._text
  33266. }
  33267. };
  33268. }
  33269. async _serialize() {
  33270. const data = await super._serialize();
  33271. const sectionData = await this._sanitizeTextData();
  33272. return {
  33273. ...data,
  33274. type: "scrollingText",
  33275. sectionData
  33276. };
  33277. }
  33278. async _deserialize(data) {
  33279. this._deserializedData = data.sectionData;
  33280. return super._deserialize(data);
  33281. }
  33282. }
  33283. class CanvasPanSection extends Section {
  33284. constructor(inSequence, target, duration, scale2) {
  33285. super(inSequence);
  33286. this._deserializedData = null;
  33287. this._source = null;
  33288. this._duration = duration ?? 250;
  33289. this._speed = null;
  33290. this._scale = scale2 ?? 1;
  33291. this._lockView = null;
  33292. this._shake = null;
  33293. this._seed = get_hash(randomID());
  33294. if (target) {
  33295. this.atLocation(target);
  33296. }
  33297. }
  33298. static niceName = "Canvas Pan";
  33299. /**
  33300. * @private
  33301. */
  33302. _applyTraits() {
  33303. Object.assign(this.constructor.prototype, traits.users);
  33304. Object.assign(this.constructor.prototype, traits.location);
  33305. Object.assign(this.constructor.prototype, traits.offset);
  33306. }
  33307. /**
  33308. * Sets the speed (pixels per frame) of the canvas pan
  33309. *
  33310. * @param {number} inSpeed
  33311. * @returns this
  33312. */
  33313. speed(inSpeed) {
  33314. if (!is_real_number(inSpeed))
  33315. throw this.sequence._customError(
  33316. this,
  33317. "speed",
  33318. "inSpeed must be of type number"
  33319. );
  33320. if (inSpeed < 0) {
  33321. throw this.sequence._customError(
  33322. this,
  33323. "speed",
  33324. "inSpeed must be greater or equal to 0"
  33325. );
  33326. }
  33327. this._speed = inSpeed;
  33328. return this;
  33329. }
  33330. /**
  33331. * Sets the zoom level of the canvas pan
  33332. *
  33333. * @param {number} inScale
  33334. * @returns this
  33335. */
  33336. scale(inScale) {
  33337. if (!is_real_number(inScale))
  33338. throw this.sequence._customError(
  33339. this,
  33340. "scale",
  33341. "inScale must be of type number"
  33342. );
  33343. if (inScale <= 0) {
  33344. throw this.sequence._customError(
  33345. this,
  33346. "scale",
  33347. "inScale must be greater than 0"
  33348. );
  33349. }
  33350. this._scale = inScale;
  33351. return this;
  33352. }
  33353. /**
  33354. * Locks the canvas at the given location for the given duration
  33355. *
  33356. * @param {number} inDuration
  33357. * @returns this
  33358. */
  33359. lockView(inDuration) {
  33360. if (!is_real_number(inDuration))
  33361. throw this.sequence._customError(
  33362. this,
  33363. "lockView",
  33364. "inDuration must be of type number"
  33365. );
  33366. if (inDuration < 0) {
  33367. throw this.sequence._customError(
  33368. this,
  33369. "lockView",
  33370. "inDuration must be greater or equal to 0"
  33371. );
  33372. }
  33373. this._lockView = inDuration;
  33374. return this;
  33375. }
  33376. /**
  33377. * Shakes the canvas
  33378. *
  33379. * @param {number} duration
  33380. * @param {number} strength
  33381. * @param {number} frequency
  33382. * @param {number} fadeInDuration
  33383. * @param {number} fadeOutDuration
  33384. * @param {boolean} rotation
  33385. * @returns this
  33386. */
  33387. shake({
  33388. duration = 250,
  33389. strength = 20,
  33390. frequency = 10,
  33391. fadeInDuration = 0,
  33392. fadeOutDuration = 200,
  33393. rotation: rotation2 = true
  33394. } = {}) {
  33395. if (!is_real_number(duration)) {
  33396. throw this.sequence._customError(
  33397. this,
  33398. "shake",
  33399. "duration must be of type number"
  33400. );
  33401. }
  33402. if (!is_real_number(strength)) {
  33403. throw this.sequence._customError(
  33404. this,
  33405. "shake",
  33406. "strength must be of type number"
  33407. );
  33408. }
  33409. if (!is_real_number(strength)) {
  33410. throw this.sequence._customError(
  33411. this,
  33412. "shake",
  33413. "frequency must be of type number"
  33414. );
  33415. }
  33416. if (!is_real_number(fadeInDuration)) {
  33417. throw this.sequence._customError(
  33418. this,
  33419. "shake",
  33420. "fadeInDuration must be of type number"
  33421. );
  33422. }
  33423. if (!is_real_number(fadeOutDuration)) {
  33424. throw this.sequence._customError(
  33425. this,
  33426. "shake",
  33427. "fadeOutDuration must be of type number"
  33428. );
  33429. }
  33430. if (typeof rotation2 !== "boolean") {
  33431. throw this.sequence._customError(
  33432. this,
  33433. "shake",
  33434. "rotation must be of type boolean"
  33435. );
  33436. }
  33437. this._shake = {
  33438. duration,
  33439. strength,
  33440. frequency,
  33441. fadeInDuration,
  33442. fadeOutDuration,
  33443. rotation: rotation2
  33444. };
  33445. return this;
  33446. }
  33447. async run() {
  33448. const data = await this._sanitizeData();
  33449. if (Hooks.call("preCanvasPan", data) === false)
  33450. return;
  33451. const push = !(data?.users?.length === 1 && data?.users?.includes(game.userId)) && !this.sequence.localOnly;
  33452. const duration = SequencerFoundryReplicator.panCanvas(data, push);
  33453. await new Promise(
  33454. (resolve) => setTimeout(resolve, this._currentWaitTime + duration)
  33455. );
  33456. }
  33457. _getSourceObject() {
  33458. if (!this._source || typeof this._source !== "object")
  33459. return this._source;
  33460. return get_object_identifier(this._source) ?? get_object_canvas_data(this._source);
  33461. }
  33462. async _sanitizeData() {
  33463. if (this._deserializedData) {
  33464. return this._deserializedData;
  33465. }
  33466. return {
  33467. sceneId: game.user.viewedScene,
  33468. seed: this._seed,
  33469. sequenceId: this.sequence.id,
  33470. creatorUserId: game.userId,
  33471. users: this._users ? Array.from(this._users) : false,
  33472. moduleName: this.sequence.moduleName,
  33473. source: this._getSourceObject(),
  33474. offset: this._offset?.source ?? false,
  33475. randomOffset: this._randomOffset?.source ?? false,
  33476. duration: this._duration,
  33477. speed: this._speed,
  33478. scale: this._scale,
  33479. lockView: this._lockView,
  33480. shake: this._shake
  33481. };
  33482. }
  33483. async _serialize() {
  33484. const data = await super._serialize();
  33485. const sectionData = await this._sanitizeData();
  33486. return {
  33487. ...data,
  33488. type: "canvasPan",
  33489. sectionData
  33490. };
  33491. }
  33492. async _deserialize(data) {
  33493. this._deserializedData = data.sectionData;
  33494. return super._deserialize(data);
  33495. }
  33496. }
  33497. class WaitSection extends Section {
  33498. constructor(inSequence, msMin, msMax) {
  33499. super(inSequence);
  33500. this._waitUntilFinished = true;
  33501. this._waitDuration = random_int_between(msMin, Math.max(msMin, msMax));
  33502. }
  33503. static niceName = "Wait";
  33504. /**
  33505. * @returns {Promise<void>}
  33506. */
  33507. async run() {
  33508. debug("Running wait");
  33509. await new Promise(async (resolve) => {
  33510. setTimeout(resolve, this._waitDuration);
  33511. });
  33512. }
  33513. /**
  33514. * @returns {Promise}
  33515. * @private
  33516. */
  33517. async _execute() {
  33518. await this.run();
  33519. }
  33520. async _serialize() {
  33521. const data = await super._serialize();
  33522. return {
  33523. ...data,
  33524. type: "wait",
  33525. sectionData: {
  33526. waitDuration: this._waitDuration
  33527. }
  33528. };
  33529. }
  33530. async _deserialize(data) {
  33531. this._waitDuration = data.sectionData.waitDuration;
  33532. return super._deserialize(data);
  33533. }
  33534. }
  33535. let Sequence$1 = class Sequence3 {
  33536. constructor(options = {
  33537. moduleName: "Sequencer",
  33538. softFail: false
  33539. }, softFail = false) {
  33540. this.id = randomID();
  33541. this.moduleName = typeof options === "string" ? options : options?.moduleName ?? "Sequencer";
  33542. this.softFail = options?.softFail ?? softFail;
  33543. this.sections = [];
  33544. this.nameOffsetMap = false;
  33545. this.effectIndex = 0;
  33546. this.sectionToCreate = void 0;
  33547. this.localOnly = false;
  33548. this._status = writable$1(CONSTANTS.STATUS.READY);
  33549. return sequence_proxy_wrap(this);
  33550. }
  33551. /**
  33552. * Plays all of this sequence's sections
  33553. *
  33554. * @returns {Promise}
  33555. */
  33556. async play({ remote = false } = {}) {
  33557. if (remote) {
  33558. this.localOnly = true;
  33559. const data = await this.toJSON();
  33560. sequencerSocket.executeForOthers(
  33561. SOCKET_HANDLERS.RUN_SEQUENCE_LOCALLY,
  33562. data
  33563. );
  33564. return new Sequence3().fromJSON(data).play();
  33565. }
  33566. Hooks.callAll("createSequencerSequence", this);
  33567. debug("Initializing sections");
  33568. for (let section of this.sections) {
  33569. await section._initialize();
  33570. }
  33571. SequenceManager.RunningSequences.add(this.id, this);
  33572. this.effectIndex = 0;
  33573. debug("Playing sections");
  33574. this.status = CONSTANTS.STATUS.RUNNING;
  33575. const promises = [];
  33576. for (let section of this.sections) {
  33577. if (section instanceof EffectSection)
  33578. this.effectIndex++;
  33579. if (section.shouldWaitUntilFinished) {
  33580. promises.push(await section._execute());
  33581. } else {
  33582. promises.push(section._execute());
  33583. }
  33584. if (get_store_value(this.status) === CONSTANTS.STATUS.ABORTED) {
  33585. continue;
  33586. }
  33587. if (!section._isLastSection) {
  33588. await new Promise((resolve) => setTimeout(resolve, 1));
  33589. }
  33590. }
  33591. return Promise.allSettled(promises).then(() => {
  33592. Hooks.callAll("endedSequencerSequence");
  33593. debug("Finished playing sections");
  33594. this.status = CONSTANTS.STATUS.COMPLETE;
  33595. });
  33596. }
  33597. /**
  33598. * Creates a section that will run a function.
  33599. *
  33600. * @param {function} inFunc
  33601. * @returns {Sequence} this
  33602. */
  33603. thenDo(inFunc) {
  33604. const func2 = section_proxy_wrap(new FunctionSection(this, inFunc));
  33605. this.sections.push(func2);
  33606. return func2;
  33607. }
  33608. /**
  33609. * Creates a section that will run a macro based on a name or a direct reference to a macro.
  33610. *
  33611. * @param {string|Macro} inMacro
  33612. * @param {object} args
  33613. * @returns {Sequence} this
  33614. */
  33615. macro(inMacro, ...args) {
  33616. let macro;
  33617. let compendium = false;
  33618. if (typeof inMacro === "string") {
  33619. if (inMacro.startsWith("Compendium")) {
  33620. let packArray = inMacro.split(".");
  33621. let pack = game.packs.get(`${packArray[1]}.${packArray[2]}`);
  33622. if (!pack) {
  33623. if (this.softFail) {
  33624. return this;
  33625. }
  33626. throw custom_error(
  33627. this.moduleName,
  33628. `macro - Compendium '${packArray[1]}.${packArray[2]}' was not found`
  33629. );
  33630. }
  33631. macro = packArray;
  33632. compendium = pack;
  33633. } else {
  33634. macro = game.macros.getName(inMacro);
  33635. if (!macro) {
  33636. if (this.softFail) {
  33637. return this;
  33638. }
  33639. throw custom_error(
  33640. this.moduleName,
  33641. `macro - Macro '${inMacro}' was not found`
  33642. );
  33643. }
  33644. }
  33645. } else if (inMacro instanceof Macro) {
  33646. macro = inMacro;
  33647. } else {
  33648. throw custom_error(
  33649. this.moduleName,
  33650. `macro - inMacro must be of instance string or Macro`
  33651. );
  33652. }
  33653. if (isNewerVersion(game.version, "11")) {
  33654. args = args.length ? args?.[0] : {};
  33655. if (typeof args !== "object") {
  33656. throw custom_error(
  33657. this.moduleName,
  33658. `macro - Secondary argument must be an object`
  33659. );
  33660. }
  33661. } else if (args && args.length && !game.modules.get("advanced-macros")?.active) {
  33662. custom_warning(
  33663. this.moduleName,
  33664. `macro - Supplying macros with arguments require the advanced-macros module to be active`,
  33665. true
  33666. );
  33667. }
  33668. const func2 = section_proxy_wrap(
  33669. new FunctionSection(
  33670. this,
  33671. async () => {
  33672. if (compendium) {
  33673. const macroData = (await compendium.getDocuments()).find((i) => i.name === macro[3])?.toObject();
  33674. if (!macroData) {
  33675. if (this.softFail) {
  33676. return;
  33677. }
  33678. throw custom_error(
  33679. this.moduleName,
  33680. `macro - Macro '${macro[3]}' was not found in compendium '${macro[1]}.${macro[2]}'`
  33681. );
  33682. }
  33683. macro = new Macro(macroData);
  33684. macro.ownership.default = CONST.DOCUMENT_PERMISSION_LEVELS.OWNER;
  33685. }
  33686. if (isNewerVersion(game.version, "11")) {
  33687. await macro.execute(args);
  33688. } else {
  33689. const version = game.modules.get("advanced-macros")?.version;
  33690. const bugAdvancedMacros = game.modules.get("advanced-macros")?.active && isNewerVersion(
  33691. version.startsWith("v") ? version.slice(1) : version,
  33692. "1.18.2"
  33693. ) && !isNewerVersion(
  33694. version.startsWith("v") ? version.slice(1) : version,
  33695. "1.19.1"
  33696. );
  33697. if (bugAdvancedMacros) {
  33698. await macro.execute([...args]);
  33699. } else {
  33700. await macro.execute(...args);
  33701. }
  33702. }
  33703. },
  33704. true
  33705. )
  33706. );
  33707. this.sections.push(func2);
  33708. return this;
  33709. }
  33710. /**
  33711. * Creates an effect section. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Effect section.
  33712. *
  33713. * @param {string} [inFile] inFile
  33714. * @returns {Section}
  33715. */
  33716. effect(inFile = "") {
  33717. const effect = section_proxy_wrap(new EffectSection(this, inFile));
  33718. this.sections.push(effect);
  33719. return effect;
  33720. }
  33721. /**
  33722. * Creates a sound section. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Sound section.
  33723. *
  33724. * @param {string} [inFile] inFile
  33725. * @returns {Section}
  33726. */
  33727. sound(inFile = "") {
  33728. const sound = section_proxy_wrap(new SoundSection(this, inFile));
  33729. this.sections.push(sound);
  33730. return sound;
  33731. }
  33732. /**
  33733. * Creates an animation. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Animation section.
  33734. *
  33735. * @param {Token|Tile} [inTarget=false] inTarget
  33736. * @returns {AnimationSection}
  33737. */
  33738. animation(inTarget) {
  33739. const animation2 = section_proxy_wrap(
  33740. new AnimationSection(this, inTarget)
  33741. );
  33742. this.sections.push(animation2);
  33743. return animation2;
  33744. }
  33745. /**
  33746. * Creates a scrolling text. Until you call .then(), .effect(), .sound(), or .wait(), you'll be working on the Scrolling Text section.
  33747. *
  33748. * @param {Object|String|Boolean} [inTarget=false] inTarget
  33749. * @param {String|Boolean} [inText=false] inText
  33750. * @param {Object} [inTextOptions={}] inTextOptions
  33751. * @returns {AnimationSection}
  33752. */
  33753. scrollingText(inTarget = false, inText = false, inTextOptions = {}) {
  33754. const scrolling = section_proxy_wrap(
  33755. new ScrollingTextSection(this, inTarget, inText, inTextOptions)
  33756. );
  33757. this.sections.push(scrolling);
  33758. return scrolling;
  33759. }
  33760. /**
  33761. * Pans the canvas text. Until you call any other sections you'll be working on the Canvas Pan section.
  33762. *
  33763. * @param {Object|String|Boolean} [inTarget=false] inTarget
  33764. * @param {Boolean|Number} inDuration
  33765. * @param {Boolean|Number} inSpeed
  33766. * @returns {AnimationSection}
  33767. */
  33768. canvasPan(inTarget = false, inDuration = null, inSpeed = null) {
  33769. const panning = section_proxy_wrap(
  33770. new CanvasPanSection(this, inTarget)
  33771. );
  33772. this.sections.push(panning);
  33773. return panning;
  33774. }
  33775. /**
  33776. * Causes the sequence to wait after the last section for as many milliseconds as you pass to this method. If given
  33777. * a second number, a random wait time between the two given numbers will be generated.
  33778. *
  33779. * @param {number} [msMin=1] minMs
  33780. * @param {number} [msMax=1] maxMs
  33781. * @returns {Sequence} this
  33782. */
  33783. wait(msMin = 1, msMax = 1) {
  33784. if (msMin < 1)
  33785. throw custom_error(
  33786. this.moduleName,
  33787. `wait - Wait ms cannot be less than 1`
  33788. );
  33789. if (msMax < 1)
  33790. throw custom_error(
  33791. this.moduleName,
  33792. `wait - Max wait ms cannot be less than 1`
  33793. );
  33794. const section = section_proxy_wrap(new WaitSection(this, msMin, msMax));
  33795. this.sections.push(section);
  33796. return this;
  33797. }
  33798. /**
  33799. * Applies a preset to the sequence
  33800. *
  33801. * @param {string} presetName
  33802. * @param {*} args
  33803. * @returns {Sequence|FunctionSection|EffectSection|AnimationSection|SoundSection}
  33804. */
  33805. preset(presetName, ...args) {
  33806. if (typeof presetName !== "string") {
  33807. throw this._customError(this, "name", `inName must be of type string`);
  33808. }
  33809. const preset = SequencerPresets.get(presetName);
  33810. if (!preset) {
  33811. custom_warning(
  33812. "Sequencer",
  33813. `preset | Could not find preset with name "${presetName}"`
  33814. );
  33815. return this;
  33816. }
  33817. const lastSection = this.sections[this.sections.length - 1];
  33818. return preset(lastSection, ...args);
  33819. }
  33820. /**
  33821. * Adds the sections from a given Sequence to this Sequence
  33822. *
  33823. * @param {Sequence|FunctionSection|EffectSection|AnimationSection|SoundSection} inSequence
  33824. * @returns {Sequence} this
  33825. */
  33826. addSequence(inSequence) {
  33827. if (inSequence instanceof Section)
  33828. inSequence = inSequence.sequence;
  33829. if (!(inSequence instanceof Sequence3)) {
  33830. throw custom_error(
  33831. this.moduleName,
  33832. `addSequence - could not find the sequence from the given parameter`
  33833. );
  33834. }
  33835. const newSections = inSequence.sections.map((section) => {
  33836. const newSection = Object.assign(
  33837. Object.create(Object.getPrototypeOf(section)),
  33838. section
  33839. );
  33840. newSection.sequence = this;
  33841. return newSection;
  33842. });
  33843. this.sections = this.sections.concat(newSections);
  33844. return this;
  33845. }
  33846. async toJSON() {
  33847. const data = {
  33848. options: { moduleName: this.moduleName, softFail: this.softFail },
  33849. sections: []
  33850. };
  33851. for (const section of this.sections) {
  33852. const sectionData = await section._serialize();
  33853. if (!sectionData.type) {
  33854. throw new Error(
  33855. `Sequencer | toJson | ${section.constructor.name} does not support serialization!`
  33856. );
  33857. }
  33858. data.sections.push(sectionData);
  33859. }
  33860. return data;
  33861. }
  33862. fromJSON(data) {
  33863. this.moduleName = data.options.moduleName;
  33864. this.softFail = data.options.softFail;
  33865. this.localOnly = true;
  33866. for (const section of data.sections) {
  33867. this[section.type]()._deserialize(section);
  33868. }
  33869. return this;
  33870. }
  33871. _createCustomSection(...args) {
  33872. const func2 = section_proxy_wrap(
  33873. new this.sectionToCreate(this, ...args)
  33874. );
  33875. this.sectionToCreate = void 0;
  33876. this.sections.push(func2);
  33877. return func2;
  33878. }
  33879. _showWarning(self, func2, warning, notify) {
  33880. custom_warning(
  33881. this.moduleName,
  33882. `${self.constructor.name.replace("Section", "")} | ${func2} - ${warning}`,
  33883. notify
  33884. );
  33885. }
  33886. _customError(self, func2, error) {
  33887. return custom_error(
  33888. this.moduleName,
  33889. `${self.constructor.name.replace("Section", "")} | ${func2} - ${error}`
  33890. );
  33891. }
  33892. set status(inStatus) {
  33893. this._status.update((currentStatus) => {
  33894. if (currentStatus === CONSTANTS.STATUS.READY || currentStatus === CONSTANTS.STATUS.RUNNING) {
  33895. return inStatus;
  33896. }
  33897. return currentStatus;
  33898. });
  33899. }
  33900. get status() {
  33901. return this._status;
  33902. }
  33903. _abort() {
  33904. this.status = CONSTANTS.STATUS.ABORTED;
  33905. for (const section of this.sections) {
  33906. section._abortSection();
  33907. }
  33908. }
  33909. };
  33910. const SequencerPreloader = {
  33911. _usersToRespond: /* @__PURE__ */ new Set(),
  33912. _clientsDone: [],
  33913. _resolve: () => {
  33914. },
  33915. /**
  33916. * Caches provided file(s) locally, vastly improving loading speed of those files.
  33917. *
  33918. * @param {Array|String} inSrcs
  33919. * @param {Boolean} showProgressBar
  33920. * @returns {Promise<void>}
  33921. */
  33922. preload(inSrcs, showProgressBar = false) {
  33923. if (Array.isArray(inSrcs)) {
  33924. inSrcs.forEach((str) => {
  33925. if (typeof str !== "string") {
  33926. throw custom_error(
  33927. "Sequencer",
  33928. "preload | each entry in inSrcs must be of type string"
  33929. );
  33930. }
  33931. });
  33932. } else if (typeof inSrcs !== "string") {
  33933. throw custom_error(
  33934. "Sequencer",
  33935. "preload | inSrcs must be of type string or array of strings"
  33936. );
  33937. }
  33938. if (typeof showProgressBar !== "boolean") {
  33939. throw custom_error(
  33940. "Sequencer",
  33941. "preload | showProgressBar must be of type of boolean"
  33942. );
  33943. }
  33944. const srcs = this._cleanSrcs(inSrcs);
  33945. if (srcs.length === 0)
  33946. return;
  33947. return this._preloadLocal(srcs, showProgressBar);
  33948. },
  33949. /**
  33950. * Causes each connected client (including the caller) to fetch and cache the provided file(s) locally, vastly
  33951. * improving loading speed of those files.
  33952. *
  33953. * @param {Array|String} inSrcs
  33954. * @param {Boolean} showProgressBar
  33955. * @returns {Promise<void>}
  33956. */
  33957. preloadForClients(inSrcs, showProgressBar = false) {
  33958. if (Array.isArray(inSrcs)) {
  33959. inSrcs.forEach((str) => {
  33960. if (typeof str !== "string") {
  33961. throw custom_error(
  33962. "Sequencer",
  33963. "preloadForClients | each entry in inSrcs must be of type string"
  33964. );
  33965. }
  33966. });
  33967. } else if (typeof inSrcs !== "string") {
  33968. throw custom_error(
  33969. "Sequencer",
  33970. "preloadForClients | inSrcs must be of type string or array of strings"
  33971. );
  33972. }
  33973. if (typeof showProgressBar !== "boolean") {
  33974. throw custom_error(
  33975. "Sequencer",
  33976. "preloadForClients | showProgressBar must be of type of boolean"
  33977. );
  33978. }
  33979. const srcs = this._cleanSrcs(inSrcs);
  33980. if (srcs.length === 0)
  33981. return;
  33982. if (!user_can_do("permissions-preload")) {
  33983. custom_warning(
  33984. "Sequencer",
  33985. "preloadForClients - You do not have permission to force other clients to preload. Preloading locally instead."
  33986. );
  33987. return this._preloadLocal(srcs, showProgressBar);
  33988. }
  33989. const promise2 = new Promise((resolve) => {
  33990. this._resolve = resolve;
  33991. });
  33992. this._usersToRespond = new Set(
  33993. game.users.filter((user) => user.active).map((user) => user.id)
  33994. );
  33995. sequencerSocket.executeForEveryone(
  33996. SOCKET_HANDLERS.PRELOAD,
  33997. game.user.id,
  33998. srcs,
  33999. showProgressBar
  34000. );
  34001. return promise2;
  34002. },
  34003. async respond(inSenderId, inSrcs, showProgressBar) {
  34004. const numFilesFailedToLoad = await this._preloadLocal(
  34005. inSrcs,
  34006. showProgressBar
  34007. );
  34008. return sequencerSocket.executeAsUser(
  34009. SOCKET_HANDLERS.PRELOAD_RESPONSE,
  34010. inSenderId,
  34011. game.user.id,
  34012. numFilesFailedToLoad
  34013. );
  34014. },
  34015. handleResponse(inUserId, numFilesFailedToLoad) {
  34016. this._usersToRespond.delete(inUserId);
  34017. this._clientsDone.push({
  34018. userId: inUserId,
  34019. numFilesFailedToLoad
  34020. });
  34021. if (this._usersToRespond.size > 0)
  34022. return;
  34023. this._clientsDone.forEach((user) => {
  34024. if (user.numFilesFailedToLoad > 0) {
  34025. debug(
  34026. `${game.users.get(user.userId).name} preloaded files, failed to preload ${user.numFilesFailedToLoad} files`
  34027. );
  34028. } else {
  34029. debug(
  34030. `${game.users.get(user.userId).name} preloaded files successfully`
  34031. );
  34032. }
  34033. });
  34034. debug(`All clients responded to file preloads`);
  34035. this._resolve();
  34036. this._usersToRespond = /* @__PURE__ */ new Set();
  34037. this._clientsDone = [];
  34038. this._resolve = () => {
  34039. };
  34040. },
  34041. /**
  34042. * Filters and cleans up file paths given to the preload methods
  34043. *
  34044. * @private
  34045. */
  34046. _cleanSrcs(inSrcs) {
  34047. if (!Array.isArray(inSrcs)) {
  34048. inSrcs = [inSrcs];
  34049. }
  34050. if (inSrcs.length === 0) {
  34051. custom_warning("Sequencer", "You need to provide files to preload");
  34052. return [];
  34053. }
  34054. inSrcs = make_array_unique(
  34055. inSrcs.filter(Boolean).map((src) => {
  34056. if (Sequencer.Database.entryExists(src)) {
  34057. return Sequencer.Database.getAllFileEntries(src);
  34058. }
  34059. return src;
  34060. })
  34061. ).deepFlatten();
  34062. if (inSrcs.length >= 750) {
  34063. custom_warning(
  34064. "Sequencer",
  34065. "You are preloading over 750 files, you are most likely preloading more files than the system can cache.",
  34066. true
  34067. );
  34068. }
  34069. return inSrcs;
  34070. },
  34071. /**
  34072. * The method that actually preloads files locally, with an optional progress bar
  34073. *
  34074. * @private
  34075. */
  34076. _preloadLocal(inSrcs, showProgressBar) {
  34077. let startTime = performance.now();
  34078. let numFilesToLoad = inSrcs.length;
  34079. debug(`Preloading ${numFilesToLoad} files...`);
  34080. if (showProgressBar)
  34081. LoadingBar.init(
  34082. `Sequencer - Preloading ${numFilesToLoad} files`,
  34083. numFilesToLoad
  34084. );
  34085. return new Promise(async (resolve) => {
  34086. let numFilesFailedToLoad = 0;
  34087. for (let src of inSrcs) {
  34088. const blob = await SequencerFileCache.loadFile(src, true);
  34089. if (showProgressBar)
  34090. LoadingBar.incrementProgress();
  34091. if (!blob) {
  34092. numFilesFailedToLoad++;
  34093. }
  34094. }
  34095. const timeTaken = (performance.now() - startTime) / 1e3;
  34096. let failedToLoad = ` (${numFilesFailedToLoad} failed to load)`;
  34097. debug(
  34098. `Preloading ${numFilesToLoad} files took ${timeTaken}s` + failedToLoad
  34099. );
  34100. resolve(numFilesFailedToLoad);
  34101. });
  34102. }
  34103. };
  34104. class SequencerSectionManager {
  34105. constructor() {
  34106. this.externalSections = {};
  34107. }
  34108. /**
  34109. * Registers a class by a name that will then be available through the Sequencer
  34110. *
  34111. * @param {String} inModuleName
  34112. * @param {String} inMethodName
  34113. * @param {Class} inClass
  34114. * @param {Boolean} overwrite
  34115. * @returns {Boolean} Whether the registration succeeded
  34116. */
  34117. registerSection(inModuleName, inMethodName, inClass, overwrite = false) {
  34118. if (!(inClass.prototype instanceof Section)) {
  34119. throw custom_error(
  34120. inModuleName,
  34121. `inClass must be instance of Sequencer.BaseSection`
  34122. );
  34123. }
  34124. let coreMethods = Object.getOwnPropertyNames(Sequence.prototype).filter(
  34125. (method) => {
  34126. return !method.startsWith("_") && method !== "constructor";
  34127. }
  34128. );
  34129. if (coreMethods.includes(inMethodName)) {
  34130. throw custom_error(
  34131. inModuleName,
  34132. `${inMethodName} is an existing protected method of the Sequence class - please register with another method name!`
  34133. );
  34134. }
  34135. if (this.externalSections[inMethodName] && !overwrite) {
  34136. throw custom_error(
  34137. inModuleName,
  34138. `${inMethodName} is already a registered Section with the class ${this.externalSections[inMethodName].constructor.name}`
  34139. );
  34140. }
  34141. coreMethods = coreMethods.concat(Object.keys(this.externalSections));
  34142. const clashingMethods = Object.getOwnPropertyNames(
  34143. inClass.prototype
  34144. ).filter((method) => coreMethods.includes(method));
  34145. if (clashingMethods.length) {
  34146. let errors = clashingMethods.join(", ");
  34147. throw custom_error(
  34148. inModuleName,
  34149. `${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.`
  34150. );
  34151. }
  34152. debug(
  34153. `SectionManager | Successfully registered ${inMethodName} with Sequencer!`
  34154. );
  34155. this.externalSections[inMethodName] = inClass;
  34156. return true;
  34157. }
  34158. }
  34159. let libWrapper = void 0;
  34160. const TGT_SPLIT_RE = new RegExp(
  34161. `([^.[]+|\\[('([^'\\\\]|\\\\.)+?'|"([^"\\\\]|\\\\.)+?")\\])`,
  34162. "g"
  34163. );
  34164. const TGT_CLEANUP_RE = new RegExp(`(^\\['|'\\]$|^\\["|"\\]$)`, "g");
  34165. Hooks.once("init", () => {
  34166. if (globalThis.libWrapper && !(globalThis.libWrapper.is_fallback ?? true)) {
  34167. libWrapper = globalThis.libWrapper;
  34168. return;
  34169. }
  34170. libWrapper = class {
  34171. static get is_fallback() {
  34172. return true;
  34173. }
  34174. static get WRAPPER() {
  34175. return "WRAPPER";
  34176. }
  34177. static get MIXED() {
  34178. return "MIXED";
  34179. }
  34180. static get OVERRIDE() {
  34181. return "OVERRIDE";
  34182. }
  34183. static register(package_id, target, fn, type = "MIXED", { chain = void 0, bind: bind2 = [] } = {}) {
  34184. const is_setter = target.endsWith("#set");
  34185. target = !is_setter ? target : target.slice(0, -4);
  34186. const split = target.match(TGT_SPLIT_RE).map((x) => x.replace(/\\(.)/g, "$1").replace(TGT_CLEANUP_RE, ""));
  34187. const root_nm = split.splice(0, 1)[0];
  34188. let obj, fn_name;
  34189. if (split.length == 0) {
  34190. obj = globalThis;
  34191. fn_name = root_nm;
  34192. } else {
  34193. const _eval = eval;
  34194. fn_name = split.pop();
  34195. obj = split.reduce(
  34196. (x, y) => x[y],
  34197. globalThis[root_nm] ?? _eval(root_nm)
  34198. );
  34199. }
  34200. let iObj = obj;
  34201. let descriptor = null;
  34202. while (iObj) {
  34203. descriptor = Object.getOwnPropertyDescriptor(iObj, fn_name);
  34204. if (descriptor)
  34205. break;
  34206. iObj = Object.getPrototypeOf(iObj);
  34207. }
  34208. if (!descriptor || descriptor?.configurable === false)
  34209. throw new Error(
  34210. `libWrapper Shim: '${target}' does not exist, could not be found, or has a non-configurable descriptor.`
  34211. );
  34212. let original = null;
  34213. const wrapper = chain ?? (type.toUpperCase?.() != "OVERRIDE" && type != 3) ? function(...args) {
  34214. return fn.call(this, original.bind(this), ...bind2, ...args);
  34215. } : function(...args) {
  34216. return fn.call(this, ...bind2, ...args);
  34217. };
  34218. if (!is_setter) {
  34219. if (descriptor.value) {
  34220. original = descriptor.value;
  34221. descriptor.value = wrapper;
  34222. } else {
  34223. original = descriptor.get;
  34224. descriptor.get = wrapper;
  34225. }
  34226. } else {
  34227. if (!descriptor.set)
  34228. throw new Error(
  34229. `libWrapper Shim: '${target}' does not have a setter`
  34230. );
  34231. original = descriptor.set;
  34232. descriptor.set = wrapper;
  34233. }
  34234. descriptor.configurable = true;
  34235. Object.defineProperty(obj, fn_name, descriptor);
  34236. }
  34237. };
  34238. });
  34239. function registerLibwrappers() {
  34240. const override = isNewerVersion(game.version, "11") ? "PIXI.BaseImageResource.prototype.upload" : "PIXI.resources.BaseImageResource.prototype.upload";
  34241. libWrapper.register(CONSTANTS.MODULE_NAME, override, PIXIUPLOAD);
  34242. }
  34243. function PIXIUPLOAD(wrapped, ...args) {
  34244. let baseTexture = args[1];
  34245. if (baseTexture.sequencer_patched || !game.settings.get(CONSTANTS.MODULE_NAME, "enable-global-fix-pixi")) {
  34246. return wrapped(...args);
  34247. }
  34248. let source = args[3];
  34249. source = source || this.source;
  34250. const isVideo = !!source.videoWidth;
  34251. if (isVideo) {
  34252. baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
  34253. baseTexture.sequencer_patched = true;
  34254. }
  34255. return wrapped(...args);
  34256. }
  34257. async function runMigrations() {
  34258. const sortedMigrations = Object.entries(migrations).sort((a, b) => {
  34259. return isNewerVersion(b[0], a[0]) ? -1 : 1;
  34260. });
  34261. for (const [version, migration] of sortedMigrations) {
  34262. try {
  34263. await migration(version);
  34264. } catch (err) {
  34265. custom_warning(
  34266. "Sequencer",
  34267. `Something went wrong when migrating to version ${version}. Please check the console for the error!`,
  34268. true
  34269. );
  34270. console.error(err);
  34271. }
  34272. }
  34273. }
  34274. function getSequencerEffectTokens(version, tokenFilter = false) {
  34275. return Array.from(game.scenes).map((scene) => [
  34276. scene,
  34277. Array.from(scene.tokens).filter((token) => token.isOwner).filter((token, index) => {
  34278. if (tokenFilter) {
  34279. return tokenFilter(token, index);
  34280. }
  34281. const effects = getProperty(token, CONSTANTS.EFFECTS_FLAG) ?? [];
  34282. const effectsOutOfDate = effects.filter(
  34283. (e) => isNewerVersion(version, e[1].flagVersion)
  34284. );
  34285. return effectsOutOfDate.length;
  34286. })
  34287. ]).filter(([_, tokens]) => tokens.length);
  34288. }
  34289. function getSequencerEffectActors(version, actorFilter = false) {
  34290. return Array.from(game.actors).filter((actor) => actor.isOwner).filter((actor, index) => {
  34291. if (actorFilter) {
  34292. return actorFilter(actor, index);
  34293. }
  34294. const effects = getProperty(actor, CONSTANTS.EFFECTS_FLAG) ?? [];
  34295. const effectsOutOfDate = effects.filter(
  34296. (e) => isNewerVersion(version, e[1].flagVersion)
  34297. );
  34298. return effectsOutOfDate.length;
  34299. });
  34300. }
  34301. const migrations = {
  34302. "3.0.0": async (version) => {
  34303. const actorsToUpdate = getSequencerEffectActors(version, (actor) => {
  34304. const effects = getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
  34305. const effectsOutOfDate = effects.filter(
  34306. (e) => isNewerVersion(version, e[1].flagVersion)
  34307. );
  34308. return effectsOutOfDate.length;
  34309. });
  34310. const actorUpdateArray = actorsToUpdate.map((actor) => {
  34311. const effectsToMoveFromTokenPrototype = getProperty(
  34312. actor.prototypeToken,
  34313. CONSTANTS.EFFECTS_FLAG
  34314. ).filter(([_, effect]) => {
  34315. return effect.persistOptions?.persistTokenPrototype;
  34316. }).map(([id, effect]) => {
  34317. effect.flagVersion = version;
  34318. return [id, effect];
  34319. });
  34320. const effectsToKeepOnTokenPrototype = getProperty(
  34321. actor.prototypeToken,
  34322. CONSTANTS.EFFECTS_FLAG
  34323. ).filter(([_, effect]) => {
  34324. return !effect.persistOptions?.persistTokenPrototype;
  34325. }).map(([id, effect]) => {
  34326. effect.flagVersion = version;
  34327. return [id, effect];
  34328. });
  34329. return {
  34330. _id: actor.id,
  34331. [CONSTANTS.EFFECTS_FLAG]: effectsToMoveFromTokenPrototype,
  34332. ["prototypeToken." + CONSTANTS.EFFECTS_FLAG]: effectsToKeepOnTokenPrototype
  34333. };
  34334. });
  34335. const tokensOnScenes = getSequencerEffectTokens(version, (t) => {
  34336. let actor;
  34337. try {
  34338. actor = t.actor;
  34339. } catch (err) {
  34340. return false;
  34341. }
  34342. const effects = getProperty(t, CONSTANTS.EFFECTS_FLAG) ?? [];
  34343. const prototypeTokenEffects = getProperty(actor, "prototypeToken." + CONSTANTS.EFFECTS_FLAG) ?? [];
  34344. const effectsOutOfDate = effects.filter(
  34345. (e) => isNewerVersion(version, e[1].flagVersion)
  34346. );
  34347. const prototypeEffectsOutOfDate = prototypeTokenEffects.filter(
  34348. (e) => isNewerVersion(version, e[1].flagVersion)
  34349. );
  34350. return t.actorLink && (effectsOutOfDate.length || prototypeEffectsOutOfDate.length);
  34351. });
  34352. for (const [scene, tokens] of tokensOnScenes) {
  34353. const updates = [];
  34354. for (const token of tokens) {
  34355. const effectsToKeepOnToken = getProperty(token, CONSTANTS.EFFECTS_FLAG).filter(([_, effect]) => {
  34356. return !effect.persistOptions?.persistTokenPrototype;
  34357. }).map(([id, effect]) => {
  34358. effect.flagVersion = version;
  34359. return [id, effect];
  34360. });
  34361. updates.push({
  34362. _id: token.id,
  34363. [CONSTANTS.EFFECTS_FLAG]: effectsToKeepOnToken
  34364. });
  34365. }
  34366. if (updates.length) {
  34367. debug(
  34368. `Sequencer | Updated ${updates.length} tokens' effects on scene ${scene.id} to version ${version}`
  34369. );
  34370. await scene.updateEmbeddedDocuments("Token", updates);
  34371. }
  34372. }
  34373. if (actorUpdateArray.length) {
  34374. debug(
  34375. `Sequencer | Updated ${actorUpdateArray.length} actors' effects to version ${version}`
  34376. );
  34377. await Actor.updateDocuments(actorUpdateArray);
  34378. }
  34379. }
  34380. };
  34381. let moduleValid = false;
  34382. let moduleReady = false;
  34383. let canvasReady = false;
  34384. Hooks.once("init", async function() {
  34385. if (!game.modules.get("socketlib")?.active)
  34386. return;
  34387. moduleValid = true;
  34388. CONSTANTS.INTEGRATIONS.ISOMETRIC.ACTIVE = false;
  34389. initializeModule();
  34390. registerSocket();
  34391. });
  34392. Hooks.once("socketlib.ready", registerSocket);
  34393. Hooks.once("ready", async function() {
  34394. if (!game.modules.get("socketlib")?.active) {
  34395. ui.notifications.error(
  34396. "Sequencer requires the SocketLib module to be active and will not work without it!",
  34397. { console: true }
  34398. );
  34399. return;
  34400. }
  34401. for (const [name, func2] of Object.entries(easeFunctions)) {
  34402. if (!CanvasAnimation[name]) {
  34403. CanvasAnimation[name] = func2;
  34404. }
  34405. }
  34406. if (game.user.isGM) {
  34407. await runMigrations();
  34408. await migrateSettings();
  34409. await PlayerSettings.migrateOldPresets();
  34410. }
  34411. SequencerFoundryReplicator.registerHooks();
  34412. InteractionManager.initialize();
  34413. });
  34414. Hooks.on("canvasTearDown", () => {
  34415. canvasReady = false;
  34416. SequencerEffectManager.tearDownPersists();
  34417. });
  34418. const setupModule = debounce(() => {
  34419. if (!moduleValid)
  34420. return;
  34421. if (!moduleReady) {
  34422. moduleReady = true;
  34423. debug("Ready to go!");
  34424. Hooks.callAll("sequencer.ready");
  34425. Hooks.callAll("sequencerReady");
  34426. }
  34427. if (!canvasReady) {
  34428. canvasReady = true;
  34429. SequencerEffectManager.initializePersistentEffects();
  34430. }
  34431. }, 25);
  34432. Hooks.on("canvasReady", () => {
  34433. setTimeout(() => {
  34434. setupModule();
  34435. }, 450);
  34436. });
  34437. Hooks.on("refreshToken", setupModule);
  34438. Hooks.on("refreshDrawing", setupModule);
  34439. Hooks.on("refreshTile", setupModule);
  34440. Hooks.on("refreshMeasuredTemplate", setupModule);
  34441. function initializeModule() {
  34442. window.Sequence = Sequence$1;
  34443. window.Sequencer = {
  34444. Player: EffectPlayer,
  34445. Presets: SequencerPresets,
  34446. Database: SequencerDatabase,
  34447. DatabaseViewer: DatabaseViewerApp,
  34448. Preloader: SequencerPreloader,
  34449. EffectManager: SequencerEffectManager,
  34450. SectionManager: new SequencerSectionManager(),
  34451. registerEase,
  34452. BaseSection: Section,
  34453. CONSTANTS: {
  34454. EASE
  34455. },
  34456. Helpers: {
  34457. wait: wait$1,
  34458. clamp,
  34459. interpolate,
  34460. random_float_between,
  34461. random_int_between,
  34462. shuffle_array,
  34463. random_array_element,
  34464. random_object_element,
  34465. make_array_unique
  34466. }
  34467. };
  34468. registerSettings();
  34469. registerLayers();
  34470. registerHotkeys();
  34471. registerLibwrappers();
  34472. SequencerAboveUILayer.setup();
  34473. SequencerEffectManager.setup();
  34474. }
  34475. Hooks.once("monaco-editor.ready", registerTypes);
  34476. //# sourceMappingURL=module.js.map