@pixodesk/svg-animator-web 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30,671 +30,874 @@ var __objRest = (source, exclude) => {
30
30
  return target;
31
31
  };
32
32
 
33
- // src/PxAnimatorUtil.ts
34
- function bezierToSvgPath(path) {
35
- var _a, _b, _c, _d;
36
- const v = path.v;
37
- const i = path.i;
38
- const o = path.o;
39
- const c = path.c;
40
- if (!v.length) return "";
41
- const d = [];
42
- const len = v.length;
43
- d.push("M" + v[0][0] + "," + v[0][1]);
44
- for (let idx = 1; idx < len; idx++) {
45
- const prevV = v[idx - 1];
46
- const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
47
- const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
48
- const currV = v[idx];
49
- const isLine = prevO[0] === prevV[0] && prevO[1] === prevV[1] && (currI[0] === currV[0] && currI[1] === currV[1]);
50
- if (isLine) {
51
- d.push("L" + currV[0] + "," + currV[1]);
33
+ // src/PxAnimatorTypes.ts
34
+ var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
35
+ var PX_ANIM_ATTR_NAME = "_px_animator";
36
+ var ANIMATE_ATTR = "animate";
37
+ var TEXT_ATTR = "text";
38
+ var TEXT_CONTENT_ATTR = "textContent";
39
+ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
40
+ "type",
41
+ "children",
42
+ ANIMATE_ATTR,
43
+ "animator",
44
+ "meta",
45
+ "defs",
46
+ "bindings",
47
+ TEXT_ATTR,
48
+ TEXT_CONTENT_ATTR
49
+ ]);
50
+ function isPxElementFileFormat(fileJson) {
51
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
52
+ return false;
53
+ }
54
+ return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
55
+ }
56
+ function isObject(v) {
57
+ return v && typeof v === "object" && !Array.isArray(v);
58
+ }
59
+ function validateFillMode(v, path, errors) {
60
+ if (v === "forwards" || v === "backwards" || v === "both" || v === "none") return true;
61
+ errors.push(path + ': invalid FillMode "' + v + `", expected 'forwards'|'backwards'|'both'|'none'`);
62
+ return false;
63
+ }
64
+ function validatePlaybackDirection(v, path, errors) {
65
+ if (v === "normal" || v === "reverse" || v === "alternate" || v === "alternate-reverse") return true;
66
+ errors.push(path + ': invalid PlaybackDirection "' + v + `", expected 'normal'|'reverse'|'alternate'|'alternate-reverse'`);
67
+ return false;
68
+ }
69
+ function validatePxEasingOrRef(v, path, errors) {
70
+ if (typeof v === "string") return true;
71
+ if (Array.isArray(v) && v.length === 4 && v.every((n) => typeof n === "number")) return true;
72
+ errors.push(path + ": invalid easing, expected string or [number, number, number, number]");
73
+ return false;
74
+ }
75
+ function validatePxKeyframe(v, path, errors) {
76
+ if (!isObject(v)) {
77
+ errors.push(path + ": expected object");
78
+ return false;
79
+ }
80
+ let valid = true;
81
+ if (v.time !== void 0 && typeof v.time !== "number") {
82
+ errors.push(path + ".time: expected number, got " + typeof v.time);
83
+ valid = false;
84
+ }
85
+ if (v.t !== void 0 && typeof v.t !== "number") {
86
+ errors.push(path + ".t: expected number, got " + typeof v.t);
87
+ valid = false;
88
+ }
89
+ if (v.easing !== void 0 && !validatePxEasingOrRef(v.easing, path + ".easing", errors)) valid = false;
90
+ if (v.e !== void 0 && !validatePxEasingOrRef(v.e, path + ".e", errors)) valid = false;
91
+ return valid;
92
+ }
93
+ function validatePxPropertyAnimation(v, path, errors) {
94
+ if (!isObject(v)) {
95
+ errors.push(path + ": expected object");
96
+ return false;
97
+ }
98
+ let valid = true;
99
+ if (v.keyframes !== void 0) {
100
+ if (!Array.isArray(v.keyframes)) {
101
+ errors.push(path + ".keyframes: expected array");
102
+ valid = false;
52
103
  } else {
53
- d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
104
+ v.keyframes.forEach((kf, i) => {
105
+ if (!validatePxKeyframe(kf, path + ".keyframes[" + i + "]", errors)) valid = false;
106
+ });
54
107
  }
55
108
  }
56
- if (c && len > 0) {
57
- const lastV = v[len - 1];
58
- const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
59
- const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
60
- const firstV = v[0];
61
- const isLine = lastO[0] === lastV[0] && lastO[1] === lastV[1] && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
62
- if (!isLine) {
63
- d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
109
+ if (v.kfs !== void 0) {
110
+ if (!Array.isArray(v.kfs)) {
111
+ errors.push(path + ".kfs: expected array");
112
+ valid = false;
113
+ } else {
114
+ v.kfs.forEach((kf, i) => {
115
+ if (!validatePxKeyframe(kf, path + ".kfs[" + i + "]", errors)) valid = false;
116
+ });
64
117
  }
65
- d.push("z");
66
118
  }
67
- return d.join("");
68
- }
69
- function interpolateNum(a, b, t) {
70
- return a + (b - a) * t;
119
+ return valid;
71
120
  }
72
- function interpolateVec(a, b, t) {
73
- const res = [];
74
- const count = Math.max(a.length, b.length);
75
- for (let i = 0; i < count; i++) {
76
- res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
121
+ function validatePxAnimationDefinition(v, path, errors) {
122
+ if (!isObject(v)) {
123
+ errors.push(path + ": expected object");
124
+ return false;
77
125
  }
78
- return res;
79
- }
80
- function interpolateColor(a, b, t) {
81
- return [
82
- interpolateNum(a[0] || 0, b[0] || 0, t),
83
- interpolateNum(a[1] || 0, b[1] || 0, t),
84
- interpolateNum(a[2] || 0, b[2] || 0, t),
85
- interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
86
- ];
87
- }
88
- function interpolateBeziers(paths1, paths2, progress) {
89
- const count = Math.max(paths1.length, paths2.length);
90
- const res = [];
91
- for (let i = 0; i < count; i++) {
92
- res.push(interpolateBezier(paths1[i], paths2[i], progress));
126
+ let valid = true;
127
+ for (const key of Object.keys(v)) {
128
+ if (!validatePxPropertyAnimation(v[key], path + "." + key, errors)) valid = false;
93
129
  }
94
- return res;
130
+ return valid;
95
131
  }
96
- function interpolateBezier(path1, path2, progress) {
97
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
98
- if (!path1 || !path2) return path1 || path2 || { v: [] };
99
- const t = Math.min(Math.max(progress, 0), 1);
100
- const len = Math.min(path1.v.length, path2.v.length);
101
- const v = [];
102
- const i = [];
103
- const o = [];
104
- for (let idx = 0; idx < len; idx++) {
105
- const v1 = path1.v[idx];
106
- const v2 = path2.v[idx];
107
- v.push(interpolateVec(v1, v2, t));
108
- const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
109
- const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
110
- i.push(interpolateVec(i1, i2, t));
111
- const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
112
- const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
113
- o.push(interpolateVec(o1, o2, t));
132
+ function validatePxElementAnimation(v, path, errors) {
133
+ if (typeof v === "string") return true;
134
+ if (Array.isArray(v)) {
135
+ let valid = true;
136
+ v.forEach((item, i) => {
137
+ if (typeof item !== "string" && !validatePxAnimationDefinition(item, path + "[" + i + "]", errors)) {
138
+ valid = false;
139
+ }
140
+ });
141
+ return valid;
114
142
  }
115
- return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
116
- }
117
- function remap(value, inMin, inMax, outMin, outMax) {
118
- if (inMax === inMin) return outMin;
119
- const t = (value - inMin) / (inMax - inMin);
120
- return outMin + t * (outMax - outMin);
143
+ if (isObject(v)) return validatePxAnimationDefinition(v, path, errors);
144
+ errors.push(path + ": expected string, array, or PxAnimationDefinition object");
145
+ return false;
121
146
  }
122
- function cubicBezier(easing) {
123
- const [p1x, p1y, p2x, p2y] = easing;
124
- const cx = 3 * p1x;
125
- const bx = 3 * (p2x - p1x) - cx;
126
- const ax = 1 - cx - bx;
127
- const cy = 3 * p1y;
128
- const by = 3 * (p2y - p1y) - cy;
129
- const ay = 1 - cy - by;
130
- function sampleCurveX(t) {
131
- return ((ax * t + bx) * t + cx) * t;
147
+ function validatePxTrigger(v, path, errors) {
148
+ if (!isObject(v)) {
149
+ errors.push(path + ": expected object");
150
+ return false;
132
151
  }
133
- function sampleCurveY(t) {
134
- return ((ay * t + by) * t + cy) * t;
152
+ let valid = true;
153
+ const validStartOn = ["load", "mouseOver", "click", "scrollIntoView", "programmatic"];
154
+ if (!validStartOn.includes(v.startOn)) {
155
+ errors.push(path + '.startOn: invalid value "' + v.startOn + '", expected ' + validStartOn.join("|"));
156
+ valid = false;
135
157
  }
136
- function sampleCurveDerivativeX(t) {
137
- return (3 * ax * t + 2 * bx) * t + cx;
158
+ if (v.outAction !== void 0) {
159
+ const validOutAction = ["continue", "pause", "reset", "reverse"];
160
+ if (!validOutAction.includes(v.outAction)) {
161
+ errors.push(path + '.outAction: invalid value "' + v.outAction + '", expected ' + validOutAction.join("|"));
162
+ valid = false;
163
+ }
138
164
  }
139
- function solveCurveX(x) {
140
- if (x <= 0) return 0;
141
- if (x >= 1) return 1;
142
- let t2 = x;
143
- let t0 = 0;
144
- let t1 = 1;
145
- for (let i = 0; i < 8; i++) {
146
- const x2 = sampleCurveX(t2) - x;
147
- if (Math.abs(x2) < 1e-6) return t2;
148
- const d2 = sampleCurveDerivativeX(t2);
149
- if (Math.abs(d2) < 1e-6) break;
150
- t2 -= x2 / d2;
151
- }
152
- t2 = x;
153
- while (t0 < t1) {
154
- const x2 = sampleCurveX(t2);
155
- if (Math.abs(x2 - x) < 1e-6) return t2;
156
- if (x > x2) t0 = t2;
157
- else t1 = t2;
158
- t2 = (t1 + t0) / 2;
159
- }
160
- return t2;
165
+ if (v.scrollIntoViewThreshold !== void 0 && typeof v.scrollIntoViewThreshold !== "number") {
166
+ errors.push(path + ".scrollIntoViewThreshold: expected number, got " + typeof v.scrollIntoViewThreshold);
167
+ valid = false;
161
168
  }
162
- return function(x) {
163
- return sampleCurveY(solveCurveX(x));
164
- };
165
- }
166
- function toRGBA(color) {
167
- const r = Math.round(color[0] * 255);
168
- const g = Math.round(color[1] * 255);
169
- const b = Math.round(color[2] * 255);
170
- return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
171
- }
172
- function parseRgba(s) {
173
- var _a;
174
- const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
175
- if (!inner) throw new Error("Invalid rgb/rgba format");
176
- const parts = inner.split(",").map((v) => +v.trim());
177
- return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
169
+ return valid;
178
170
  }
179
- function parseHex(s) {
180
- const hex = s.slice(1);
181
- const isShort = hex.length <= 4;
182
- const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
183
- const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
184
- const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
185
- const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
186
- const result = [
187
- parseInt(r, 16) / 255,
188
- parseInt(g, 16) / 255,
189
- parseInt(b, 16) / 255
190
- ];
191
- if (a !== null) {
192
- result.push(parseInt(a, 16) / 255);
171
+ function validatePxAnimatorConfig(v, path, errors) {
172
+ if (!isObject(v)) {
173
+ errors.push(path + ": expected object");
174
+ return false;
193
175
  }
194
- return result;
195
- }
196
- function parseColor(s) {
197
- if (!s) return void 0;
198
- if (Array.isArray(s)) return s;
199
- if (typeof s !== "string") return void 0;
200
- if (s.startsWith("#")) {
201
- return parseHex(s);
202
- } else if (s.startsWith("rgb")) {
203
- return parseRgba(s);
204
- } else {
205
- console.warn("Unsupported color format: " + s);
176
+ let valid = true;
177
+ if (v.mode !== void 0 && !["auto", "webapi", "frames"].includes(v.mode)) {
178
+ errors.push(path + '.mode: invalid value "' + v.mode + `", expected 'auto'|'webapi'|'frames'`);
179
+ valid = false;
206
180
  }
207
- return void 0;
208
- }
209
- var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
210
- var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
211
- var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
212
- var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
213
- var DEFAULT_DURATION_MS = 1e3;
214
- function kebabToCamelCaseWord(kebab) {
215
- return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
216
- }
217
- function isCamelCaseWord(word) {
218
- return !word.includes("-") && /[a-z][A-Z]/.test(word);
219
- }
220
- var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
221
- // Transform/positioning
222
- "viewBox",
223
- "preserveAspectRatio",
224
- // Gradient
225
- "gradientUnits",
226
- "gradientTransform",
227
- "spreadMethod",
228
- // Pattern
229
- "patternUnits",
230
- "patternContentUnits",
231
- "patternTransform",
232
- // Clipping/masking
233
- "clipPathUnits",
234
- "maskUnits",
235
- "maskContentUnits",
236
- // Text
237
- "textLength",
238
- "lengthAdjust",
239
- "startOffset",
240
- // Filter
241
- "filterUnits",
242
- "primitiveUnits",
243
- "stdDeviation",
244
- "baseFrequency",
245
- "numOctaves",
246
- "surfaceScale",
247
- "diffuseConstant",
248
- "specularConstant",
249
- "specularExponent",
250
- "kernelMatrix",
251
- "kernelUnitLength",
252
- "edgeMode",
253
- "preserveAlpha",
254
- "targetX",
255
- "targetY"
256
- // // Animation
257
- // 'attributeName',
258
- // 'attributeType',
259
- // 'calcMode',
260
- // 'keyTimes',
261
- // 'keySplines',
262
- // 'repeatCount',
263
- // 'repeatDur'
264
- ]);
265
- function camelCaseToKebabWordIfNeeded(camel) {
266
- return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
267
- }
268
- function clamp(value, min, max) {
269
- return Math.max(min, Math.min(value, max));
270
- }
271
-
272
- // src/PxAnimatorDOM.ts
273
- var SVG_NS = "http://www.w3.org/2000/svg";
274
- var INTERNAL_ATTRS = /* @__PURE__ */ new Set(["type", "children", "animate", "style", "animator", "defs", "bindings"]);
275
- function createElement(tagName, props, style, children) {
276
- const element = document.createElementNS(SVG_NS, tagName);
277
- for (const propName in props) {
278
- element.setAttribute(camelCaseToKebabWordIfNeeded(propName), props[propName]);
181
+ if (v.duration !== void 0 && typeof v.duration !== "number") {
182
+ errors.push(path + ".duration: expected number, got " + typeof v.duration);
183
+ valid = false;
279
184
  }
280
- if (style) {
281
- for (const styleProp in style) {
282
- element.style[styleProp] = String(style[styleProp]);
283
- }
185
+ if (v.delay !== void 0 && typeof v.delay !== "number") {
186
+ errors.push(path + ".delay: expected number, got " + typeof v.delay);
187
+ valid = false;
284
188
  }
285
- if (children) {
286
- for (const child of children) {
287
- element.appendChild(child);
288
- }
189
+ if (v.iterations !== void 0 && typeof v.iterations !== "number" && v.iterations !== "infinite") {
190
+ errors.push(path + ".iterations: expected number or 'infinite', got " + typeof v.iterations);
191
+ valid = false;
289
192
  }
290
- return element;
291
- }
292
- function resolveStyle(style, defs) {
293
- var _a;
294
- if (!style) return void 0;
295
- if (typeof style === "string") {
296
- return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
193
+ if (v.fill !== void 0 && !validateFillMode(v.fill, path + ".fill", errors)) valid = false;
194
+ if (v.direction !== void 0 && !validatePlaybackDirection(v.direction, path + ".direction", errors)) valid = false;
195
+ if (v.frameRate !== void 0 && typeof v.frameRate !== "number") {
196
+ errors.push(path + ".frameRate: expected number, got " + typeof v.frameRate);
197
+ valid = false;
297
198
  }
298
- return style;
199
+ if (v.trigger !== void 0 && !validatePxTrigger(v.trigger, path + ".trigger", errors)) valid = false;
200
+ return valid;
299
201
  }
300
- function getNormalizedProps(props) {
301
- const propsCopy = {};
302
- for (const key of Object.keys(props)) {
303
- if (INTERNAL_ATTRS.has(key)) continue;
304
- let value = props[key];
305
- if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
306
- propsCopy[key] = toRGBA(value);
307
- } else if (TRANSFORM_FN_NAMES.has(key)) {
308
- if (Array.isArray(value)) {
309
- if (key === "translate") value = value.map((v) => v + "px");
310
- value = value.join(",");
311
- }
312
- if (key === "rotate") value = value + "deg";
313
- propsCopy["transform"] = key + "(" + value + ")";
314
- } else if (value !== void 0 && value !== null) {
315
- propsCopy[key] = String(value);
316
- }
202
+ function validatePxDefs(v, path, errors) {
203
+ if (!isObject(v)) {
204
+ errors.push(path + ": expected object");
205
+ return false;
317
206
  }
318
- return propsCopy;
319
- }
320
- function renderNode(node, defs) {
321
- if (!node) return null;
322
- const _a = node, { type, children, animate, style } = _a, props = __objRest(_a, ["type", "children", "animate", "style"]);
323
- const nodeDefs = node.defs || defs;
324
- const resolvedStyle = resolveStyle(style, nodeDefs);
325
- let childElements;
326
- if (children) {
327
- for (const ch of children) {
328
- const child = renderNode(ch, nodeDefs);
329
- if (child) {
330
- if (!childElements) childElements = [];
331
- childElements.push(child);
207
+ let valid = true;
208
+ if (v.easings !== void 0) {
209
+ if (!isObject(v.easings)) {
210
+ errors.push(path + ".easings: expected object");
211
+ valid = false;
212
+ } else {
213
+ for (const key of Object.keys(v.easings)) {
214
+ if (!validatePxEasingOrRef(v.easings[key], path + ".easings." + key, errors)) valid = false;
332
215
  }
333
216
  }
334
217
  }
335
- return createElement(
336
- type || "g",
337
- getNormalizedProps(props),
338
- resolvedStyle,
339
- childElements
340
- );
341
- }
342
-
343
- // src/PxAnimatorTriggers.ts
344
- function setupAnimationTriggers(api, config) {
345
- const { startOn, outAction = "continue", scrollIntoViewThreshold = 0.5 } = config;
346
- const root = api.getRootElement();
347
- if (!root) {
348
- console.warn("setupAnimationTriggers: No root element found for animation.");
349
- return api;
350
- }
351
- const start = () => {
352
- api.play();
353
- };
354
- const handleEndAction = () => {
355
- switch (outAction) {
356
- case "pause":
357
- api.pause();
358
- break;
359
- case "reset":
360
- api.cancel();
361
- break;
362
- case "reverse":
363
- api.play();
364
- break;
365
- case "continue":
366
- default:
367
- break;
368
- }
369
- };
370
- switch (startOn) {
371
- case "load": {
372
- const startHandler = () => start();
373
- if (document.readyState === "complete") {
374
- startHandler();
375
- } else {
376
- window.addEventListener("load", startHandler, { once: true });
218
+ if (v.animations !== void 0) {
219
+ if (!isObject(v.animations)) {
220
+ errors.push(path + ".animations: expected object");
221
+ valid = false;
222
+ } else {
223
+ for (const key of Object.keys(v.animations)) {
224
+ if (!validatePxAnimationDefinition(v.animations[key], path + ".animations." + key, errors)) valid = false;
377
225
  }
378
- break;
379
- }
380
- case "mouseOver": {
381
- const mouseOverHandler = () => start();
382
- const mouseOutHandler = () => handleEndAction();
383
- root.addEventListener("mouseenter", mouseOverHandler);
384
- root.addEventListener("mouseleave", mouseOutHandler);
385
- break;
386
- }
387
- case "click": {
388
- const clickHandler = () => {
389
- if (api.isPlaying()) {
390
- handleEndAction();
391
- } else {
392
- start();
393
- }
394
- };
395
- root.addEventListener("click", clickHandler);
396
- break;
397
- }
398
- case "scrollIntoView": {
399
- const observer = new IntersectionObserver(
400
- (entries) => {
401
- entries.forEach((entry) => {
402
- if (entry.isIntersecting && entry.intersectionRatio >= scrollIntoViewThreshold) {
403
- start();
404
- } else {
405
- handleEndAction();
406
- }
407
- });
408
- },
409
- { threshold: scrollIntoViewThreshold }
410
- );
411
- observer.observe(root);
412
- break;
413
226
  }
414
- case "programmatic":
415
- break;
416
227
  }
417
- return api;
418
- }
419
-
420
- // src/PxAnimatorTypes.ts
421
- var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
422
- var PX_ANIM_ATTR_NAME = "_px_animator";
423
- function isPxElementFileFormat(fileJson) {
424
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
425
- return false;
228
+ if (v.styles !== void 0 && !isObject(v.styles)) {
229
+ errors.push(path + ".styles: expected object");
230
+ valid = false;
426
231
  }
427
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
428
- }
429
- function isObject(v) {
430
- return v && typeof v === "object" && !Array.isArray(v);
431
- }
432
- function validateFillMode(v, path, errors) {
433
- if (v === "forwards" || v === "backwards" || v === "both" || v === "none") return true;
434
- errors.push(path + ': invalid FillMode "' + v + `", expected 'forwards'|'backwards'|'both'|'none'`);
435
- return false;
436
- }
437
- function validatePlaybackDirection(v, path, errors) {
438
- if (v === "normal" || v === "reverse" || v === "alternate" || v === "alternate-reverse") return true;
439
- errors.push(path + ': invalid PlaybackDirection "' + v + `", expected 'normal'|'reverse'|'alternate'|'alternate-reverse'`);
440
- return false;
441
- }
442
- function validatePxEasingOrRef(v, path, errors) {
443
- if (typeof v === "string") return true;
444
- if (Array.isArray(v) && v.length === 4 && v.every((n) => typeof n === "number")) return true;
445
- errors.push(path + ": invalid easing, expected string or [number, number, number, number]");
446
- return false;
232
+ return valid;
447
233
  }
448
- function validatePxKeyframe(v, path, errors) {
234
+ function validatePxBinding(v, path, errors) {
449
235
  if (!isObject(v)) {
450
236
  errors.push(path + ": expected object");
451
237
  return false;
452
238
  }
453
239
  let valid = true;
454
- if (v.time !== void 0 && typeof v.time !== "number") {
455
- errors.push(path + ".time: expected number, got " + typeof v.time);
456
- valid = false;
457
- }
458
- if (v.t !== void 0 && typeof v.t !== "number") {
459
- errors.push(path + ".t: expected number, got " + typeof v.t);
240
+ if (typeof v.id !== "string") {
241
+ errors.push(path + ".id: expected string, got " + typeof v.id);
460
242
  valid = false;
461
243
  }
462
- if (v.easing !== void 0 && !validatePxEasingOrRef(v.easing, path + ".easing", errors)) valid = false;
463
- if (v.e !== void 0 && !validatePxEasingOrRef(v.e, path + ".e", errors)) valid = false;
244
+ if (!validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
464
245
  return valid;
465
246
  }
466
- function validatePxPropertyAnimation(v, path, errors) {
247
+ function validatePxNode(v, path, errors) {
467
248
  if (!isObject(v)) {
468
249
  errors.push(path + ": expected object");
469
250
  return false;
470
251
  }
471
252
  let valid = true;
472
- if (v.keyframes !== void 0) {
473
- if (!Array.isArray(v.keyframes)) {
474
- errors.push(path + ".keyframes: expected array");
475
- valid = false;
476
- } else {
477
- v.keyframes.forEach((kf, i) => {
478
- if (!validatePxKeyframe(kf, path + ".keyframes[" + i + "]", errors)) valid = false;
479
- });
480
- }
253
+ if (typeof v.type !== "string") {
254
+ errors.push(path + ".type: expected string, got " + typeof v.type);
255
+ valid = false;
481
256
  }
482
- if (v.kfs !== void 0) {
483
- if (!Array.isArray(v.kfs)) {
484
- errors.push(path + ".kfs: expected array");
257
+ if (v.children !== void 0) {
258
+ if (!Array.isArray(v.children)) {
259
+ errors.push(path + ".children: expected array");
485
260
  valid = false;
486
261
  } else {
487
- v.kfs.forEach((kf, i) => {
488
- if (!validatePxKeyframe(kf, path + ".kfs[" + i + "]", errors)) valid = false;
262
+ v.children.forEach((child, i) => {
263
+ if (!validatePxNode(child, path + ".children[" + i + "]", errors)) valid = false;
489
264
  });
490
265
  }
491
266
  }
267
+ if (v.animate !== void 0 && !validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
492
268
  return valid;
493
269
  }
494
- function validatePxAnimationDefinition(v, path, errors) {
495
- if (!isObject(v)) {
496
- errors.push(path + ": expected object");
497
- return false;
498
- }
270
+ function validatePxSvgNode(v, path, errors) {
271
+ if (!validatePxNode(v, path, errors)) return false;
499
272
  let valid = true;
500
- for (const key of Object.keys(v)) {
501
- if (!validatePxPropertyAnimation(v[key], path + "." + key, errors)) valid = false;
273
+ if (v.type !== "svg") {
274
+ errors.push(path + ".type: expected 'svg', got '" + v.type + "'");
275
+ valid = false;
502
276
  }
503
- return valid;
504
- }
505
- function validatePxElementAnimation(v, path, errors) {
506
- if (typeof v === "string") return true;
507
- if (Array.isArray(v)) {
508
- let valid = true;
509
- v.forEach((item, i) => {
510
- if (typeof item !== "string" && !validatePxAnimationDefinition(item, path + "[" + i + "]", errors)) {
511
- valid = false;
512
- }
513
- });
514
- return valid;
277
+ if (v.width !== void 0 && typeof v.width !== "number") {
278
+ errors.push(path + ".width: expected number, got " + typeof v.width);
279
+ valid = false;
515
280
  }
516
- if (isObject(v)) return validatePxAnimationDefinition(v, path, errors);
517
- errors.push(path + ": expected string, array, or PxAnimationDefinition object");
518
- return false;
519
- }
520
- function validatePxTrigger(v, path, errors) {
521
- if (!isObject(v)) {
522
- errors.push(path + ": expected object");
523
- return false;
281
+ if (v.height !== void 0 && typeof v.height !== "number") {
282
+ errors.push(path + ".height: expected number, got " + typeof v.height);
283
+ valid = false;
524
284
  }
525
- let valid = true;
526
- const validStartOn = ["load", "mouseOver", "click", "scrollIntoView", "programmatic"];
527
- if (!validStartOn.includes(v.startOn)) {
528
- errors.push(path + '.startOn: invalid value "' + v.startOn + '", expected ' + validStartOn.join("|"));
285
+ if (v.viewBox !== void 0 && typeof v.viewBox !== "string") {
286
+ errors.push(path + ".viewBox: expected string, got " + typeof v.viewBox);
529
287
  valid = false;
530
288
  }
531
- if (v.outAction !== void 0) {
532
- const validOutAction = ["continue", "pause", "reset", "reverse"];
533
- if (!validOutAction.includes(v.outAction)) {
534
- errors.push(path + '.outAction: invalid value "' + v.outAction + '", expected ' + validOutAction.join("|"));
289
+ if (v.animator !== void 0 && !validatePxAnimatorConfig(v.animator, path + ".animator", errors)) valid = false;
290
+ if (v.defs !== void 0 && !validatePxDefs(v.defs, path + ".defs", errors)) valid = false;
291
+ if (v.bindings !== void 0) {
292
+ if (!Array.isArray(v.bindings)) {
293
+ errors.push(path + ".bindings: expected array");
535
294
  valid = false;
295
+ } else {
296
+ v.bindings.forEach((binding, i) => {
297
+ if (!validatePxBinding(binding, path + ".bindings[" + i + "]", errors)) valid = false;
298
+ });
536
299
  }
537
300
  }
538
- if (v.scrollIntoViewThreshold !== void 0 && typeof v.scrollIntoViewThreshold !== "number") {
539
- errors.push(path + ".scrollIntoViewThreshold: expected number, got " + typeof v.scrollIntoViewThreshold);
540
- valid = false;
541
- }
301
+ if (v.design !== void 0 && !validatePxNode(v.design, path + ".design", errors)) valid = false;
542
302
  return valid;
543
303
  }
544
- function validatePxAnimatorConfig(v, path, errors) {
545
- if (!isObject(v)) {
546
- errors.push(path + ": expected object");
547
- return false;
548
- }
549
- let valid = true;
550
- if (v.mode !== void 0 && !["auto", "webapi", "frames"].includes(v.mode)) {
551
- errors.push(path + '.mode: invalid value "' + v.mode + `", expected 'auto'|'webapi'|'frames'`);
552
- valid = false;
304
+ function isPxElementFileFormatDeep(fileJson) {
305
+ const errors = [];
306
+ const valid = validatePxSvgNode(fileJson, "root", errors);
307
+ return { valid, errors };
308
+ }
309
+ function getAnimatorConfig(doc) {
310
+ var _a, _b;
311
+ return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
312
+ }
313
+ function getDefs(doc) {
314
+ var _a;
315
+ if (!doc) return void 0;
316
+ return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
317
+ }
318
+ function getBindings(doc) {
319
+ var _a;
320
+ if (!doc) return void 0;
321
+ return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
322
+ }
323
+ function getChildren(doc) {
324
+ return doc == null ? void 0 : doc.children;
325
+ }
326
+
327
+ // src/PxAnimatorUtil.ts
328
+ function bezierToSvgPath(path) {
329
+ var _a, _b, _c, _d;
330
+ const v = path.v;
331
+ const i = path.i;
332
+ const o = path.o;
333
+ const c = path.c;
334
+ if (!v.length) return "";
335
+ const d = [];
336
+ const len = v.length;
337
+ d.push("M" + v[0][0] + "," + v[0][1]);
338
+ for (let idx = 1; idx < len; idx++) {
339
+ const prevV = v[idx - 1];
340
+ const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
341
+ const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
342
+ const currV = v[idx];
343
+ const isLine = prevO[0] === prevV[0] && prevO[1] === prevV[1] && (currI[0] === currV[0] && currI[1] === currV[1]);
344
+ if (isLine) {
345
+ d.push("L" + currV[0] + "," + currV[1]);
346
+ } else {
347
+ d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
348
+ }
553
349
  }
554
- if (v.duration !== void 0 && typeof v.duration !== "number") {
555
- errors.push(path + ".duration: expected number, got " + typeof v.duration);
556
- valid = false;
350
+ if (c && len > 0) {
351
+ const lastV = v[len - 1];
352
+ const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
353
+ const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
354
+ const firstV = v[0];
355
+ const isLine = lastO[0] === lastV[0] && lastO[1] === lastV[1] && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
356
+ if (!isLine) {
357
+ d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
358
+ }
359
+ d.push("z");
557
360
  }
558
- if (v.delay !== void 0 && typeof v.delay !== "number") {
559
- errors.push(path + ".delay: expected number, got " + typeof v.delay);
560
- valid = false;
361
+ return d.join("");
362
+ }
363
+ function interpolateNum(a, b, t) {
364
+ return a + (b - a) * t;
365
+ }
366
+ function interpolateVec(a, b, t) {
367
+ const res = [];
368
+ const count = Math.max(a.length, b.length);
369
+ for (let i = 0; i < count; i++) {
370
+ res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
561
371
  }
562
- if (v.iterations !== void 0 && typeof v.iterations !== "number" && v.iterations !== "infinite") {
563
- errors.push(path + ".iterations: expected number or 'infinite', got " + typeof v.iterations);
564
- valid = false;
372
+ return res;
373
+ }
374
+ function interpolateColor(a, b, t) {
375
+ return [
376
+ interpolateNum(a[0] || 0, b[0] || 0, t),
377
+ interpolateNum(a[1] || 0, b[1] || 0, t),
378
+ interpolateNum(a[2] || 0, b[2] || 0, t),
379
+ interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
380
+ ];
381
+ }
382
+ function interpolateBeziers(paths1, paths2, progress) {
383
+ const count = Math.max(paths1.length, paths2.length);
384
+ const res = [];
385
+ for (let i = 0; i < count; i++) {
386
+ res.push(interpolateBezier(paths1[i], paths2[i], progress));
565
387
  }
566
- if (v.fill !== void 0 && !validateFillMode(v.fill, path + ".fill", errors)) valid = false;
567
- if (v.direction !== void 0 && !validatePlaybackDirection(v.direction, path + ".direction", errors)) valid = false;
568
- if (v.frameRate !== void 0 && typeof v.frameRate !== "number") {
569
- errors.push(path + ".frameRate: expected number, got " + typeof v.frameRate);
570
- valid = false;
388
+ return res;
389
+ }
390
+ function interpolateBezier(path1, path2, progress) {
391
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
392
+ if (!path1 || !path2) return path1 || path2 || { v: [] };
393
+ const t = Math.min(Math.max(progress, 0), 1);
394
+ const len = Math.min(path1.v.length, path2.v.length);
395
+ const v = [];
396
+ const i = [];
397
+ const o = [];
398
+ for (let idx = 0; idx < len; idx++) {
399
+ const v1 = path1.v[idx];
400
+ const v2 = path2.v[idx];
401
+ v.push(interpolateVec(v1, v2, t));
402
+ const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
403
+ const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
404
+ i.push(interpolateVec(i1, i2, t));
405
+ const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
406
+ const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
407
+ o.push(interpolateVec(o1, o2, t));
571
408
  }
572
- if (v.trigger !== void 0 && !validatePxTrigger(v.trigger, path + ".trigger", errors)) valid = false;
573
- return valid;
409
+ return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
574
410
  }
575
- function validatePxDefs(v, path, errors) {
576
- if (!isObject(v)) {
577
- errors.push(path + ": expected object");
578
- return false;
411
+ function remap(value, inMin, inMax, outMin, outMax) {
412
+ if (inMax === inMin) return outMin;
413
+ const t = (value - inMin) / (inMax - inMin);
414
+ return outMin + t * (outMax - outMin);
415
+ }
416
+ function solveCubicBezierX(p1x, p2x, x) {
417
+ if (x <= 0) return 0;
418
+ if (x >= 1) return 1;
419
+ const cx = 3 * p1x;
420
+ const bx = 3 * (p2x - p1x) - cx;
421
+ const ax = 1 - cx - bx;
422
+ function sampleX(t) {
423
+ return ((ax * t + bx) * t + cx) * t;
579
424
  }
580
- let valid = true;
581
- if (v.easings !== void 0) {
582
- if (!isObject(v.easings)) {
583
- errors.push(path + ".easings: expected object");
584
- valid = false;
585
- } else {
586
- for (const key of Object.keys(v.easings)) {
587
- if (!validatePxEasingOrRef(v.easings[key], path + ".easings." + key, errors)) valid = false;
588
- }
589
- }
425
+ function sampleDX(t) {
426
+ return (3 * ax * t + 2 * bx) * t + cx;
590
427
  }
591
- if (v.animations !== void 0) {
592
- if (!isObject(v.animations)) {
593
- errors.push(path + ".animations: expected object");
594
- valid = false;
595
- } else {
596
- for (const key of Object.keys(v.animations)) {
597
- if (!validatePxAnimationDefinition(v.animations[key], path + ".animations." + key, errors)) valid = false;
598
- }
599
- }
428
+ let t2 = x;
429
+ let t0 = 0;
430
+ let t1 = 1;
431
+ for (let i = 0; i < 8; i++) {
432
+ const x2 = sampleX(t2) - x;
433
+ if (Math.abs(x2) < 1e-6) return t2;
434
+ const d2 = sampleDX(t2);
435
+ if (Math.abs(d2) < 1e-6) break;
436
+ t2 -= x2 / d2;
437
+ }
438
+ t2 = x;
439
+ while (t0 < t1) {
440
+ const x2 = sampleX(t2);
441
+ if (Math.abs(x2 - x) < 1e-6) return t2;
442
+ if (x > x2) t0 = t2;
443
+ else t1 = t2;
444
+ t2 = (t1 + t0) / 2;
445
+ }
446
+ return t2;
447
+ }
448
+ function cubicBezier(easing) {
449
+ const [p1x, p1y, p2x, p2y] = easing;
450
+ const cy = 3 * p1y;
451
+ const by = 3 * (p2y - p1y) - cy;
452
+ const ay = 1 - cy - by;
453
+ function sampleCurveY(t) {
454
+ return ((ay * t + by) * t + cy) * t;
600
455
  }
601
- if (v.styles !== void 0 && !isObject(v.styles)) {
602
- errors.push(path + ".styles: expected object");
603
- valid = false;
456
+ return function(x) {
457
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
458
+ };
459
+ }
460
+ function lerp2(a, b, t) {
461
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
462
+ }
463
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
464
+ const q0 = lerp2(p0, p1, t);
465
+ const q1 = lerp2(p1, p2, t);
466
+ const q2 = lerp2(p2, p3, t);
467
+ const r0 = lerp2(q0, q1, t);
468
+ const r1 = lerp2(q1, q2, t);
469
+ const s = lerp2(r0, r1, t);
470
+ return {
471
+ left: [p0, q0, r0, s],
472
+ right: [s, r1, q2, p3]
473
+ };
474
+ }
475
+ function splitEasing(easing, xFraction) {
476
+ if (!easing) return { left: void 0, right: void 0 };
477
+ if (xFraction <= 0) return { left: void 0, right: easing };
478
+ if (xFraction >= 1) return { left: easing, right: void 0 };
479
+ const [x1, y1, x2, y2] = easing;
480
+ const t = solveCubicBezierX(x1, x2, xFraction);
481
+ const p0 = [0, 0];
482
+ const p1 = [x1, y1];
483
+ const p2 = [x2, y2];
484
+ const p3 = [1, 1];
485
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
486
+ const sx = left[3][0];
487
+ const sy = left[3][1];
488
+ let leftEasing;
489
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
490
+ leftEasing = [
491
+ left[1][0] / sx,
492
+ left[1][1] / sy,
493
+ left[2][0] / sx,
494
+ left[2][1] / sy
495
+ ];
496
+ }
497
+ let rightEasing;
498
+ const rx = 1 - sx;
499
+ const ry = 1 - sy;
500
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
501
+ rightEasing = [
502
+ (right[1][0] - sx) / rx,
503
+ (right[1][1] - sy) / ry,
504
+ (right[2][0] - sx) / rx,
505
+ (right[2][1] - sy) / ry
506
+ ];
507
+ }
508
+ return { left: leftEasing, right: rightEasing };
509
+ }
510
+ function reverseEasing(easing) {
511
+ if (!easing) return void 0;
512
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
513
+ }
514
+ function toRGBA(color) {
515
+ const r = Math.round(color[0] * 255);
516
+ const g = Math.round(color[1] * 255);
517
+ const b = Math.round(color[2] * 255);
518
+ return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
519
+ }
520
+ function parseRgba(s) {
521
+ var _a;
522
+ const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
523
+ if (!inner) throw new Error("Invalid rgb/rgba format");
524
+ const parts = inner.split(",").map((v) => +v.trim());
525
+ return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
526
+ }
527
+ function parseHex(s) {
528
+ const hex = s.slice(1);
529
+ const isShort = hex.length <= 4;
530
+ const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
531
+ const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
532
+ const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
533
+ const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
534
+ const result = [
535
+ parseInt(r, 16) / 255,
536
+ parseInt(g, 16) / 255,
537
+ parseInt(b, 16) / 255
538
+ ];
539
+ if (a !== null) {
540
+ result.push(parseInt(a, 16) / 255);
604
541
  }
605
- return valid;
542
+ return result;
606
543
  }
607
- function validatePxBinding(v, path, errors) {
608
- if (!isObject(v)) {
609
- errors.push(path + ": expected object");
610
- return false;
544
+ function parseColor(s) {
545
+ if (!s) return void 0;
546
+ if (Array.isArray(s)) return s;
547
+ if (typeof s !== "string") return void 0;
548
+ if (s.startsWith("#")) {
549
+ return parseHex(s);
550
+ } else if (s.startsWith("rgb")) {
551
+ return parseRgba(s);
552
+ } else {
553
+ console.warn("Unsupported color format: " + s);
554
+ }
555
+ return void 0;
556
+ }
557
+ var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
558
+ var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
559
+ var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
560
+ var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
561
+ var DEFAULT_DURATION_MS = 1e3;
562
+ function kebabToCamelCaseWord(kebab) {
563
+ return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
564
+ }
565
+ function isCamelCaseWord(word) {
566
+ return !word.includes("-") && /[a-z][A-Z]/.test(word);
567
+ }
568
+ var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
569
+ // Transform/positioning
570
+ "viewBox",
571
+ "preserveAspectRatio",
572
+ // Gradient
573
+ "gradientUnits",
574
+ "gradientTransform",
575
+ "spreadMethod",
576
+ // Pattern
577
+ "patternUnits",
578
+ "patternContentUnits",
579
+ "patternTransform",
580
+ // Clipping/masking
581
+ "clipPathUnits",
582
+ "maskUnits",
583
+ "maskContentUnits",
584
+ // Text
585
+ "textLength",
586
+ "lengthAdjust",
587
+ "startOffset",
588
+ // Filter
589
+ "filterUnits",
590
+ "primitiveUnits",
591
+ "stdDeviation",
592
+ "baseFrequency",
593
+ "numOctaves",
594
+ "surfaceScale",
595
+ "diffuseConstant",
596
+ "specularConstant",
597
+ "specularExponent",
598
+ "kernelMatrix",
599
+ "kernelUnitLength",
600
+ "edgeMode",
601
+ "preserveAlpha",
602
+ "targetX",
603
+ "targetY"
604
+ // // Animation
605
+ // 'attributeName',
606
+ // 'attributeType',
607
+ // 'calcMode',
608
+ // 'keyTimes',
609
+ // 'keySplines',
610
+ // 'repeatCount',
611
+ // 'repeatDur'
612
+ ]);
613
+ function camelCaseToKebabWordIfNeeded(camel) {
614
+ return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
615
+ }
616
+ function clamp(value, min, max) {
617
+ return Math.max(min, Math.min(value, max));
618
+ }
619
+
620
+ // src/PxAnimatorDOM.ts
621
+ var SVG_NS = "http://www.w3.org/2000/svg";
622
+ var ALLOWED_SVG_TAGS_LOWER_CASE = new Set([
623
+ "svg",
624
+ "g",
625
+ "path",
626
+ "circle",
627
+ "ellipse",
628
+ "rect",
629
+ "line",
630
+ "polyline",
631
+ "polygon",
632
+ "text",
633
+ "tspan",
634
+ "defs",
635
+ "clipPath",
636
+ "mask",
637
+ "pattern",
638
+ "linearGradient",
639
+ "radialGradient",
640
+ "stop",
641
+ "use",
642
+ "symbol",
643
+ "marker",
644
+ "filter",
645
+ "feGaussianBlur",
646
+ "feOffset",
647
+ "feBlend",
648
+ "feColorMatrix",
649
+ "feMerge",
650
+ "feMergeNode"
651
+ ].map((tagName) => tagName.toLowerCase()));
652
+ var ALLOWED_RESOURCE_ATTRIBUTES = [
653
+ "href",
654
+ // <use>
655
+ "src",
656
+ // <image>
657
+ "filter",
658
+ // url(#filterId)
659
+ "clipPath",
660
+ // clip-path="url(#clipPathId)"
661
+ "mask",
662
+ // url(#maskId)
663
+ "markerStart",
664
+ // marker-start="url(#markerId)"
665
+ "markerMid",
666
+ // marker-mid="url(#markerId)"
667
+ "markerEnd"
668
+ // marker-end="url(#markerId)"
669
+ // 'fill', // url(#gradientId) or url(#patternId)
670
+ // 'stroke', // url(#gradientId) or url(#patternId)
671
+ // Don't allow 'cursor', can use external SVG, // url(cursor.svg)
672
+ ];
673
+ var ALLOWED_RESOURCE_ATTRIBUTES_SET = new Set(ALLOWED_RESOURCE_ATTRIBUTES);
674
+ var ALLOWED_ATTRIBUTES_SET = /* @__PURE__ */ new Set([
675
+ "href",
676
+ "src",
677
+ // Presentation
678
+ "fill",
679
+ "stroke",
680
+ "strokeWidth",
681
+ "opacity",
682
+ "transform",
683
+ // Geometry
684
+ "x",
685
+ "y",
686
+ "cx",
687
+ "cy",
688
+ "r",
689
+ "rx",
690
+ "ry",
691
+ "width",
692
+ "height",
693
+ "d",
694
+ "x1",
695
+ "y1",
696
+ "x2",
697
+ "y2",
698
+ "points",
699
+ // Text
700
+ "fontSize",
701
+ "fontFamily",
702
+ "textAnchor",
703
+ // Structure
704
+ "id",
705
+ "class",
706
+ "viewBox",
707
+ "preserveAspectRatio",
708
+ // Gradient/Pattern
709
+ "offset",
710
+ "stopColor",
711
+ "stopOpacity",
712
+ "gradientTransform",
713
+ // Clippath/Mask
714
+ "clipPath",
715
+ "mask",
716
+ // Filter
717
+ "filter",
718
+ "stdDeviation",
719
+ "in",
720
+ "in2",
721
+ "result",
722
+ "mode",
723
+ ...ALLOWED_RESOURCE_ATTRIBUTES
724
+ ]);
725
+ function sanitiseAttributeValue(name, value) {
726
+ const nameLower = name.toLowerCase();
727
+ if (!ALLOWED_ATTRIBUTES_SET.has(nameLower)) {
728
+ console.warn("Attribute not in whitelist: ", nameLower);
729
+ return void 0;
730
+ }
731
+ if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopColor") {
732
+ const str = String(value);
733
+ if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
734
+ console.warn('Attribute "' + nameLower + '" blocked: url() references must be internal url(#id), got:', value);
735
+ return void 0;
736
+ }
737
+ return value;
611
738
  }
612
- let valid = true;
613
- if (typeof v.id !== "string") {
614
- errors.push(path + ".id: expected string, got " + typeof v.id);
615
- valid = false;
739
+ if (ALLOWED_RESOURCE_ATTRIBUTES_SET.has(nameLower)) {
740
+ const str = String(value);
741
+ if (str.startsWith("#")) {
742
+ return value;
743
+ }
744
+ if (/^url\(#[^)]+\)$/.test(str)) {
745
+ return value;
746
+ }
747
+ return void 0;
616
748
  }
617
- if (!validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
618
- return valid;
749
+ return value;
619
750
  }
620
- function validatePxNode(v, path, errors) {
621
- if (!isObject(v)) {
622
- errors.push(path + ": expected object");
623
- return false;
751
+ function createElement(tagName, normalisedProps, style, children, textContent) {
752
+ if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) return null;
753
+ const element = document.createElementNS(SVG_NS, tagName);
754
+ for (const propName in normalisedProps) {
755
+ element.setAttribute(
756
+ camelCaseToKebabWordIfNeeded(propName),
757
+ sanitiseAttributeValue(propName, normalisedProps[propName])
758
+ );
624
759
  }
625
- let valid = true;
626
- if (typeof v.type !== "string") {
627
- errors.push(path + ".type: expected string, got " + typeof v.type);
628
- valid = false;
760
+ if (style) {
761
+ for (const styleProp in style) {
762
+ element.style[styleProp] = String(style[styleProp]);
763
+ }
629
764
  }
630
- if (v.children !== void 0) {
631
- if (!Array.isArray(v.children)) {
632
- errors.push(path + ".children: expected array");
633
- valid = false;
634
- } else {
635
- v.children.forEach((child, i) => {
636
- if (!validatePxNode(child, path + ".children[" + i + "]", errors)) valid = false;
637
- });
765
+ if (children) {
766
+ for (const child of children) {
767
+ element.appendChild(child);
638
768
  }
639
769
  }
640
- if (v.animate !== void 0 && !validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
641
- return valid;
770
+ if (textContent) element.textContent = textContent;
771
+ return element;
642
772
  }
643
- function validatePxSvgNode(v, path, errors) {
644
- if (!validatePxNode(v, path, errors)) return false;
645
- let valid = true;
646
- if (v.type !== "svg") {
647
- errors.push(path + ".type: expected 'svg', got '" + v.type + "'");
648
- valid = false;
773
+ function resolveStyle(style, defs) {
774
+ var _a;
775
+ if (!style) return void 0;
776
+ if (typeof style === "string") {
777
+ return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
649
778
  }
650
- if (v.width !== void 0 && typeof v.width !== "number") {
651
- errors.push(path + ".width: expected number, got " + typeof v.width);
652
- valid = false;
779
+ return style;
780
+ }
781
+ function getNormalizedProps(props) {
782
+ const propsCopy = {};
783
+ for (const key of Object.keys(props)) {
784
+ if (INTERNAL_ATTRS.has(key)) continue;
785
+ if (key === "style") continue;
786
+ let value = props[key];
787
+ if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
788
+ propsCopy[key] = toRGBA(value);
789
+ } else if (TRANSFORM_FN_NAMES.has(key)) {
790
+ if (Array.isArray(value)) {
791
+ if (key === "translate") value = value.map((v) => v + "px");
792
+ value = value.join(",");
793
+ }
794
+ if (key === "rotate") value = value + "deg";
795
+ propsCopy["transform"] = key + "(" + value + ")";
796
+ } else if (value !== void 0 && value !== null) {
797
+ propsCopy[key] = String(value);
798
+ }
653
799
  }
654
- if (v.height !== void 0 && typeof v.height !== "number") {
655
- errors.push(path + ".height: expected number, got " + typeof v.height);
656
- valid = false;
800
+ return propsCopy;
801
+ }
802
+ function renderNode(node, defs) {
803
+ if (!node) return null;
804
+ const _a = node, { type, children, animate, style } = _a, props = __objRest(_a, ["type", "children", "animate", "style"]);
805
+ const nodeDefs = node.defs || defs;
806
+ const resolvedStyle = resolveStyle(style, nodeDefs);
807
+ let childElements;
808
+ if (children) {
809
+ for (const ch of children) {
810
+ const child = renderNode(ch, nodeDefs);
811
+ if (child) {
812
+ if (!childElements) childElements = [];
813
+ childElements.push(child);
814
+ }
815
+ }
657
816
  }
658
- if (v.viewBox !== void 0 && typeof v.viewBox !== "string") {
659
- errors.push(path + ".viewBox: expected string, got " + typeof v.viewBox);
660
- valid = false;
817
+ return createElement(
818
+ type || "g",
819
+ getNormalizedProps(props),
820
+ resolvedStyle,
821
+ childElements,
822
+ props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
823
+ );
824
+ }
825
+
826
+ // src/PxAnimatorTriggers.ts
827
+ function setupAnimationTriggers(api, config) {
828
+ const { startOn, outAction = "continue", scrollIntoViewThreshold = 0.5 } = config;
829
+ const root = api.getRootElement();
830
+ if (!root) {
831
+ console.warn("setupAnimationTriggers: No root element found for animation.");
832
+ return api;
661
833
  }
662
- if (v.animator !== void 0 && !validatePxAnimatorConfig(v.animator, path + ".animator", errors)) valid = false;
663
- if (v.defs !== void 0 && !validatePxDefs(v.defs, path + ".defs", errors)) valid = false;
664
- if (v.bindings !== void 0) {
665
- if (!Array.isArray(v.bindings)) {
666
- errors.push(path + ".bindings: expected array");
667
- valid = false;
668
- } else {
669
- v.bindings.forEach((binding, i) => {
670
- if (!validatePxBinding(binding, path + ".bindings[" + i + "]", errors)) valid = false;
671
- });
834
+ const start = () => {
835
+ api.play();
836
+ };
837
+ const handleEndAction = () => {
838
+ switch (outAction) {
839
+ case "pause":
840
+ api.pause();
841
+ break;
842
+ case "reset":
843
+ api.cancel();
844
+ break;
845
+ case "reverse":
846
+ api.play();
847
+ break;
848
+ case "continue":
849
+ default:
850
+ break;
851
+ }
852
+ };
853
+ switch (startOn) {
854
+ case "load": {
855
+ const startHandler = () => start();
856
+ if (document.readyState === "complete") {
857
+ startHandler();
858
+ } else {
859
+ window.addEventListener("load", startHandler, { once: true });
860
+ }
861
+ break;
862
+ }
863
+ case "mouseOver": {
864
+ const mouseOverHandler = () => start();
865
+ const mouseOutHandler = () => handleEndAction();
866
+ root.addEventListener("mouseenter", mouseOverHandler);
867
+ root.addEventListener("mouseleave", mouseOutHandler);
868
+ break;
869
+ }
870
+ case "click": {
871
+ const clickHandler = () => {
872
+ if (api.isPlaying()) {
873
+ handleEndAction();
874
+ } else {
875
+ start();
876
+ }
877
+ };
878
+ root.addEventListener("click", clickHandler);
879
+ break;
880
+ }
881
+ case "scrollIntoView": {
882
+ const observer = new IntersectionObserver(
883
+ (entries) => {
884
+ entries.forEach((entry) => {
885
+ if (entry.isIntersecting && entry.intersectionRatio >= scrollIntoViewThreshold) {
886
+ start();
887
+ } else {
888
+ handleEndAction();
889
+ }
890
+ });
891
+ },
892
+ { threshold: scrollIntoViewThreshold }
893
+ );
894
+ observer.observe(root);
895
+ break;
672
896
  }
897
+ case "programmatic":
898
+ break;
673
899
  }
674
- if (v.design !== void 0 && !validatePxNode(v.design, path + ".design", errors)) valid = false;
675
- return valid;
676
- }
677
- function isPxElementFileFormatDeep(fileJson) {
678
- const errors = [];
679
- const valid = validatePxSvgNode(fileJson, "root", errors);
680
- return { valid, errors };
681
- }
682
- function getAnimatorConfig(doc) {
683
- var _a, _b;
684
- return (doc == null ? void 0 : doc.animator) || ((_a = doc == null ? void 0 : doc.meta) == null ? void 0 : _a.animator) || (doc == null ? void 0 : doc.animation) || ((_b = doc == null ? void 0 : doc.meta) == null ? void 0 : _b.animation);
685
- }
686
- function getDefs(doc) {
687
- var _a;
688
- if (!doc) return void 0;
689
- return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
690
- }
691
- function getBindings(doc) {
692
- var _a;
693
- if (!doc) return void 0;
694
- return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
695
- }
696
- function getChildren(doc) {
697
- return doc == null ? void 0 : doc.children;
900
+ return api;
698
901
  }
699
902
 
700
903
  // src/PxDefinitions.ts
@@ -851,6 +1054,111 @@ function resolveElementAnimation(animate, defs) {
851
1054
  }
852
1055
  return results;
853
1056
  }
1057
+ function interpolateValue(propName, a, b, t) {
1058
+ var _a, _b;
1059
+ if (propName === "d") {
1060
+ const aPaths = (_a = a == null ? void 0 : a.paths) != null ? _a : Array.isArray(a) ? a : [];
1061
+ const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
1062
+ return { paths: interpolateBeziers(aPaths, bPaths, t) };
1063
+ }
1064
+ if (COLOUR_ATTR_NAMES.has(propName)) {
1065
+ return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
1066
+ }
1067
+ if (TRANSFORM_FN_NAMES.has(propName) || propName === "stroke-dasharray" || propName === "strokeDasharray") {
1068
+ return interpolateVec(a || [], b || [], t);
1069
+ }
1070
+ return interpolateNum(+(a || 0), +(b || 0), t);
1071
+ }
1072
+ function expandLoopKeyframes(propName, keyframes, loop, duration) {
1073
+ var _a, _b, _c, _d, _e;
1074
+ const totalIntervals = keyframes.length - 1;
1075
+ const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
1076
+ let segKfs;
1077
+ if (loop.before) {
1078
+ segKfs = keyframes.slice(0, segCount + 1);
1079
+ } else {
1080
+ segKfs = keyframes.slice(totalIntervals - segCount);
1081
+ }
1082
+ const firstT = (_b = keyframes[0].t) != null ? _b : 0;
1083
+ const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
1084
+ let fillStart, fillEnd;
1085
+ if (loop.before) {
1086
+ fillStart = 0;
1087
+ fillEnd = firstT;
1088
+ } else {
1089
+ fillStart = lastT;
1090
+ fillEnd = duration;
1091
+ }
1092
+ const fillDuration = fillEnd - fillStart;
1093
+ if (fillDuration <= 0) return keyframes;
1094
+ const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
1095
+ const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
1096
+ const segDuration = segEndT - segStartT;
1097
+ if (segDuration <= 0) return keyframes;
1098
+ const template = segKfs.map((kf) => ({
1099
+ relT: (kf.t - segStartT) / segDuration,
1100
+ v: kf.v,
1101
+ e: kf.e
1102
+ }));
1103
+ const fullReps = Math.floor(fillDuration / segDuration);
1104
+ const remainder = fillDuration - fullReps * segDuration;
1105
+ const partialFraction = remainder / segDuration;
1106
+ const looped = [];
1107
+ function appendRep(repStart, isReversed, partial) {
1108
+ let entries;
1109
+ if (isReversed) {
1110
+ entries = [];
1111
+ for (let i = template.length - 1; i >= 0; i--) {
1112
+ entries.push({
1113
+ relT: 1 - template[i].relT,
1114
+ v: template[i].v,
1115
+ // Easing for reversed transition: use reversed easing from the forward "from" keyframe
1116
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0
1117
+ });
1118
+ }
1119
+ } else {
1120
+ entries = template;
1121
+ }
1122
+ const cutRelT = partial !== void 0 ? partial : 1;
1123
+ for (let i = 0; i < entries.length; i++) {
1124
+ const entry = entries[i];
1125
+ if (entry.relT > cutRelT + 1e-9) {
1126
+ const prev = entries[i - 1];
1127
+ const intervalSpan = entry.relT - prev.relT;
1128
+ const localFrac = (cutRelT - prev.relT) / intervalSpan;
1129
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
1130
+ const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
1131
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
1132
+ if (looped.length > 0 && prev.relT <= cutRelT) {
1133
+ looped[looped.length - 1].e = leftEasing;
1134
+ }
1135
+ looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
1136
+ return;
1137
+ }
1138
+ looped.push({
1139
+ t: repStart + entry.relT * segDuration,
1140
+ v: entry.v,
1141
+ e: i < entries.length - 1 ? entry.e : void 0
1142
+ });
1143
+ }
1144
+ }
1145
+ for (let rep = 0; rep < fullReps; rep++) {
1146
+ const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
1147
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
1148
+ const repStart = fillStart + rep * segDuration;
1149
+ appendRep(repStart, isReversed);
1150
+ }
1151
+ if (partialFraction > 1e-9) {
1152
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
1153
+ const repStart = fillStart + fullReps * segDuration;
1154
+ appendRep(repStart, isReversed, partialFraction);
1155
+ }
1156
+ if (loop.before) {
1157
+ return [...looped, ...keyframes];
1158
+ } else {
1159
+ return [...keyframes, ...looped];
1160
+ }
1161
+ }
854
1162
  function normalizeKeyframes(propName, propAnim, duration, defs) {
855
1163
  var _a, _b, _c, _d, _e;
856
1164
  const keyframes = propAnim.keyframes || propAnim.kfs || [];
@@ -875,6 +1183,11 @@ function normalizeKeyframes(propName, propAnim, duration, defs) {
875
1183
  var _a2, _b2;
876
1184
  return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
877
1185
  });
1186
+ const loopRaw = propAnim.loop;
1187
+ const loop = loopRaw === true ? {} : loopRaw || void 0;
1188
+ if (loop && normalized.length >= 2) {
1189
+ return expandLoopKeyframes(propName, normalized, loop, duration);
1190
+ }
878
1191
  return normalized;
879
1192
  }
880
1193
  function mergeAnimationDefinitions(animations) {
@@ -1309,40 +1622,56 @@ function createDomAdapter(rootElement) {
1309
1622
  }
1310
1623
 
1311
1624
  // src/PxAnimatorWebApi.ts
1625
+ function createCssKf(kf, t, propName, unsupportedSet) {
1626
+ var _a, _b;
1627
+ let value = (_a = kf.v) != null ? _a : kf.value;
1628
+ const e = (_b = kf.e) != null ? _b : kf.easing;
1629
+ const cssKf = {
1630
+ offset: t,
1631
+ easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
1632
+ };
1633
+ let cssValue;
1634
+ let cssKey = propName;
1635
+ if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
1636
+ cssValue = toRGBA(value);
1637
+ } else if (TRANSFORM_FN_NAMES.has(propName)) {
1638
+ if (Array.isArray(value)) {
1639
+ if (propName === "translate") value = value.map((v) => v + "px");
1640
+ value = value.join(",");
1641
+ }
1642
+ if (propName === "rotate") value = value + "deg";
1643
+ cssValue = propName + "(" + value + ")";
1644
+ cssKey = "transform";
1645
+ } else {
1646
+ cssValue = "" + value;
1647
+ }
1648
+ if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
1649
+ cssKey = kebabToCamelCaseWord(cssKey);
1650
+ cssKf[cssKey] = cssValue;
1651
+ return cssKf;
1652
+ }
1312
1653
  function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
1313
- var _a, _b, _c, _d;
1654
+ var _a, _b;
1314
1655
  const result = /* @__PURE__ */ new Map();
1315
1656
  for (const [propName, propAnim] of Object.entries(animDef)) {
1316
1657
  const keyframes = propAnim.kfs || propAnim.keyframes || [];
1317
1658
  const cssKeyframes = [];
1318
- for (const kf of keyframes) {
1659
+ for (let i = 0; i < keyframes.length; i++) {
1660
+ const kf = keyframes[i];
1319
1661
  let t = (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
1320
1662
  t = clamp(t / (config.duration || 1), 0, 1);
1321
- let value = (_c = kf.v) != null ? _c : kf.value;
1322
- const e = (_d = kf.e) != null ? _d : kf.easing;
1323
- const cssKf = {
1324
- offset: t,
1325
- easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
1326
- };
1327
- let cssValue;
1328
- let cssKey = propName;
1329
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
1330
- cssValue = toRGBA(value);
1331
- } else if (TRANSFORM_FN_NAMES.has(propName)) {
1332
- if (Array.isArray(value)) {
1333
- if (propName === "translate") value = value.map((v) => v + "px");
1334
- value = value.join(",");
1335
- }
1336
- if (propName === "rotate") value = value + "deg";
1337
- cssValue = propName + "(" + value + ")";
1338
- cssKey = "transform";
1339
- } else {
1340
- cssValue = "" + value;
1663
+ const cssKf = createCssKf(kf, t, propName, unsupportedSet);
1664
+ if (i === 0 && (cssKf.offset || 0) > 0) {
1665
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
1666
+ offset: 0
1667
+ }));
1341
1668
  }
1342
- if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
1343
- cssKey = kebabToCamelCaseWord(cssKey);
1344
- cssKf[cssKey] = cssValue;
1345
1669
  cssKeyframes.push(cssKf);
1670
+ if (i === keyframes.length - 1 && (cssKf.offset || 0) < 1) {
1671
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
1672
+ offset: 1
1673
+ }));
1674
+ }
1346
1675
  }
1347
1676
  if (cssKeyframes.length > 0) {
1348
1677
  result.set(propName, cssKeyframes);
@@ -1383,17 +1712,17 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1383
1712
  console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
1384
1713
  }
1385
1714
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
1715
+ const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
1716
+ const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
1717
+ const effectOptions = {
1718
+ duration: config.duration,
1719
+ delay: positiveDelay,
1720
+ fill: config.fill,
1721
+ direction: config.direction,
1722
+ iterations
1723
+ };
1386
1724
  for (let i = 0; i < elements.length; i++) {
1387
1725
  const element = elements[i];
1388
- const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
1389
- const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
1390
- const effectOptions = {
1391
- duration: config.duration,
1392
- delay: positiveDelay,
1393
- fill: config.fill,
1394
- direction: config.direction,
1395
- iterations
1396
- };
1397
1726
  for (const [, keyframes] of keyframesMap) {
1398
1727
  if (keyframes.length > 0) {
1399
1728
  try {
@@ -1445,7 +1774,20 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1445
1774
  (_a = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a.call(callbacks);
1446
1775
  },
1447
1776
  "finish": () => {
1448
- animations.forEach((a) => a.finish());
1777
+ var _a;
1778
+ for (const a of animations) {
1779
+ try {
1780
+ if (((_a = a.effect) == null ? void 0 : _a.getTiming().iterations) === Infinity) {
1781
+ a.effect.updateTiming({ iterations: 1 });
1782
+ a.finish();
1783
+ a.effect.updateTiming({ iterations: Infinity });
1784
+ } else {
1785
+ a.finish();
1786
+ }
1787
+ } catch (e) {
1788
+ a.cancel();
1789
+ }
1790
+ }
1449
1791
  },
1450
1792
  "setPlaybackRate": (rate) => {
1451
1793
  animations.forEach((a) => a.playbackRate = rate);
@@ -1576,7 +1918,7 @@ function generateNewIds(doc) {
1576
1918
  value[styleProp] = replaceUrlRefs(styleValue, idMap);
1577
1919
  }
1578
1920
  }
1579
- } else if (typeof value === "object" && value !== null && key !== "animate") {
1921
+ } else if (typeof value === "object" && value !== null && key !== ANIMATE_ATTR) {
1580
1922
  updateRefs(value);
1581
1923
  }
1582
1924
  }