@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.cjs CHANGED
@@ -77,671 +77,874 @@ __export(index_exports, {
77
77
  });
78
78
  module.exports = __toCommonJS(index_exports);
79
79
 
80
- // src/PxAnimatorUtil.ts
81
- function bezierToSvgPath(path) {
82
- var _a, _b, _c, _d;
83
- const v = path.v;
84
- const i = path.i;
85
- const o = path.o;
86
- const c = path.c;
87
- if (!v.length) return "";
88
- const d = [];
89
- const len = v.length;
90
- d.push("M" + v[0][0] + "," + v[0][1]);
91
- for (let idx = 1; idx < len; idx++) {
92
- const prevV = v[idx - 1];
93
- const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
94
- const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
95
- const currV = v[idx];
96
- const isLine = prevO[0] === prevV[0] && prevO[1] === prevV[1] && (currI[0] === currV[0] && currI[1] === currV[1]);
97
- if (isLine) {
98
- d.push("L" + currV[0] + "," + currV[1]);
80
+ // src/PxAnimatorTypes.ts
81
+ var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
82
+ var PX_ANIM_ATTR_NAME = "_px_animator";
83
+ var ANIMATE_ATTR = "animate";
84
+ var TEXT_ATTR = "text";
85
+ var TEXT_CONTENT_ATTR = "textContent";
86
+ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
87
+ "type",
88
+ "children",
89
+ ANIMATE_ATTR,
90
+ "animator",
91
+ "meta",
92
+ "defs",
93
+ "bindings",
94
+ TEXT_ATTR,
95
+ TEXT_CONTENT_ATTR
96
+ ]);
97
+ function isPxElementFileFormat(fileJson) {
98
+ if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
99
+ return false;
100
+ }
101
+ return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
102
+ }
103
+ function isObject(v) {
104
+ return v && typeof v === "object" && !Array.isArray(v);
105
+ }
106
+ function validateFillMode(v, path, errors) {
107
+ if (v === "forwards" || v === "backwards" || v === "both" || v === "none") return true;
108
+ errors.push(path + ': invalid FillMode "' + v + `", expected 'forwards'|'backwards'|'both'|'none'`);
109
+ return false;
110
+ }
111
+ function validatePlaybackDirection(v, path, errors) {
112
+ if (v === "normal" || v === "reverse" || v === "alternate" || v === "alternate-reverse") return true;
113
+ errors.push(path + ': invalid PlaybackDirection "' + v + `", expected 'normal'|'reverse'|'alternate'|'alternate-reverse'`);
114
+ return false;
115
+ }
116
+ function validatePxEasingOrRef(v, path, errors) {
117
+ if (typeof v === "string") return true;
118
+ if (Array.isArray(v) && v.length === 4 && v.every((n) => typeof n === "number")) return true;
119
+ errors.push(path + ": invalid easing, expected string or [number, number, number, number]");
120
+ return false;
121
+ }
122
+ function validatePxKeyframe(v, path, errors) {
123
+ if (!isObject(v)) {
124
+ errors.push(path + ": expected object");
125
+ return false;
126
+ }
127
+ let valid = true;
128
+ if (v.time !== void 0 && typeof v.time !== "number") {
129
+ errors.push(path + ".time: expected number, got " + typeof v.time);
130
+ valid = false;
131
+ }
132
+ if (v.t !== void 0 && typeof v.t !== "number") {
133
+ errors.push(path + ".t: expected number, got " + typeof v.t);
134
+ valid = false;
135
+ }
136
+ if (v.easing !== void 0 && !validatePxEasingOrRef(v.easing, path + ".easing", errors)) valid = false;
137
+ if (v.e !== void 0 && !validatePxEasingOrRef(v.e, path + ".e", errors)) valid = false;
138
+ return valid;
139
+ }
140
+ function validatePxPropertyAnimation(v, path, errors) {
141
+ if (!isObject(v)) {
142
+ errors.push(path + ": expected object");
143
+ return false;
144
+ }
145
+ let valid = true;
146
+ if (v.keyframes !== void 0) {
147
+ if (!Array.isArray(v.keyframes)) {
148
+ errors.push(path + ".keyframes: expected array");
149
+ valid = false;
99
150
  } else {
100
- d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
151
+ v.keyframes.forEach((kf, i) => {
152
+ if (!validatePxKeyframe(kf, path + ".keyframes[" + i + "]", errors)) valid = false;
153
+ });
101
154
  }
102
155
  }
103
- if (c && len > 0) {
104
- const lastV = v[len - 1];
105
- const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
106
- const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
107
- const firstV = v[0];
108
- const isLine = lastO[0] === lastV[0] && lastO[1] === lastV[1] && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
109
- if (!isLine) {
110
- d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
156
+ if (v.kfs !== void 0) {
157
+ if (!Array.isArray(v.kfs)) {
158
+ errors.push(path + ".kfs: expected array");
159
+ valid = false;
160
+ } else {
161
+ v.kfs.forEach((kf, i) => {
162
+ if (!validatePxKeyframe(kf, path + ".kfs[" + i + "]", errors)) valid = false;
163
+ });
111
164
  }
112
- d.push("z");
113
165
  }
114
- return d.join("");
115
- }
116
- function interpolateNum(a, b, t) {
117
- return a + (b - a) * t;
166
+ return valid;
118
167
  }
119
- function interpolateVec(a, b, t) {
120
- const res = [];
121
- const count = Math.max(a.length, b.length);
122
- for (let i = 0; i < count; i++) {
123
- res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
168
+ function validatePxAnimationDefinition(v, path, errors) {
169
+ if (!isObject(v)) {
170
+ errors.push(path + ": expected object");
171
+ return false;
124
172
  }
125
- return res;
126
- }
127
- function interpolateColor(a, b, t) {
128
- return [
129
- interpolateNum(a[0] || 0, b[0] || 0, t),
130
- interpolateNum(a[1] || 0, b[1] || 0, t),
131
- interpolateNum(a[2] || 0, b[2] || 0, t),
132
- interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
133
- ];
134
- }
135
- function interpolateBeziers(paths1, paths2, progress) {
136
- const count = Math.max(paths1.length, paths2.length);
137
- const res = [];
138
- for (let i = 0; i < count; i++) {
139
- res.push(interpolateBezier(paths1[i], paths2[i], progress));
173
+ let valid = true;
174
+ for (const key of Object.keys(v)) {
175
+ if (!validatePxPropertyAnimation(v[key], path + "." + key, errors)) valid = false;
140
176
  }
141
- return res;
177
+ return valid;
142
178
  }
143
- function interpolateBezier(path1, path2, progress) {
144
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
145
- if (!path1 || !path2) return path1 || path2 || { v: [] };
146
- const t = Math.min(Math.max(progress, 0), 1);
147
- const len = Math.min(path1.v.length, path2.v.length);
148
- const v = [];
149
- const i = [];
150
- const o = [];
151
- for (let idx = 0; idx < len; idx++) {
152
- const v1 = path1.v[idx];
153
- const v2 = path2.v[idx];
154
- v.push(interpolateVec(v1, v2, t));
155
- const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
156
- const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
157
- i.push(interpolateVec(i1, i2, t));
158
- const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
159
- const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
160
- o.push(interpolateVec(o1, o2, t));
179
+ function validatePxElementAnimation(v, path, errors) {
180
+ if (typeof v === "string") return true;
181
+ if (Array.isArray(v)) {
182
+ let valid = true;
183
+ v.forEach((item, i) => {
184
+ if (typeof item !== "string" && !validatePxAnimationDefinition(item, path + "[" + i + "]", errors)) {
185
+ valid = false;
186
+ }
187
+ });
188
+ return valid;
161
189
  }
162
- return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
163
- }
164
- function remap(value, inMin, inMax, outMin, outMax) {
165
- if (inMax === inMin) return outMin;
166
- const t = (value - inMin) / (inMax - inMin);
167
- return outMin + t * (outMax - outMin);
190
+ if (isObject(v)) return validatePxAnimationDefinition(v, path, errors);
191
+ errors.push(path + ": expected string, array, or PxAnimationDefinition object");
192
+ return false;
168
193
  }
169
- function cubicBezier(easing) {
170
- const [p1x, p1y, p2x, p2y] = easing;
171
- const cx = 3 * p1x;
172
- const bx = 3 * (p2x - p1x) - cx;
173
- const ax = 1 - cx - bx;
174
- const cy = 3 * p1y;
175
- const by = 3 * (p2y - p1y) - cy;
176
- const ay = 1 - cy - by;
177
- function sampleCurveX(t) {
178
- return ((ax * t + bx) * t + cx) * t;
194
+ function validatePxTrigger(v, path, errors) {
195
+ if (!isObject(v)) {
196
+ errors.push(path + ": expected object");
197
+ return false;
179
198
  }
180
- function sampleCurveY(t) {
181
- return ((ay * t + by) * t + cy) * t;
199
+ let valid = true;
200
+ const validStartOn = ["load", "mouseOver", "click", "scrollIntoView", "programmatic"];
201
+ if (!validStartOn.includes(v.startOn)) {
202
+ errors.push(path + '.startOn: invalid value "' + v.startOn + '", expected ' + validStartOn.join("|"));
203
+ valid = false;
182
204
  }
183
- function sampleCurveDerivativeX(t) {
184
- return (3 * ax * t + 2 * bx) * t + cx;
205
+ if (v.outAction !== void 0) {
206
+ const validOutAction = ["continue", "pause", "reset", "reverse"];
207
+ if (!validOutAction.includes(v.outAction)) {
208
+ errors.push(path + '.outAction: invalid value "' + v.outAction + '", expected ' + validOutAction.join("|"));
209
+ valid = false;
210
+ }
185
211
  }
186
- function solveCurveX(x) {
187
- if (x <= 0) return 0;
188
- if (x >= 1) return 1;
189
- let t2 = x;
190
- let t0 = 0;
191
- let t1 = 1;
192
- for (let i = 0; i < 8; i++) {
193
- const x2 = sampleCurveX(t2) - x;
194
- if (Math.abs(x2) < 1e-6) return t2;
195
- const d2 = sampleCurveDerivativeX(t2);
196
- if (Math.abs(d2) < 1e-6) break;
197
- t2 -= x2 / d2;
198
- }
199
- t2 = x;
200
- while (t0 < t1) {
201
- const x2 = sampleCurveX(t2);
202
- if (Math.abs(x2 - x) < 1e-6) return t2;
203
- if (x > x2) t0 = t2;
204
- else t1 = t2;
205
- t2 = (t1 + t0) / 2;
206
- }
207
- return t2;
212
+ if (v.scrollIntoViewThreshold !== void 0 && typeof v.scrollIntoViewThreshold !== "number") {
213
+ errors.push(path + ".scrollIntoViewThreshold: expected number, got " + typeof v.scrollIntoViewThreshold);
214
+ valid = false;
208
215
  }
209
- return function(x) {
210
- return sampleCurveY(solveCurveX(x));
211
- };
212
- }
213
- function toRGBA(color) {
214
- const r = Math.round(color[0] * 255);
215
- const g = Math.round(color[1] * 255);
216
- const b = Math.round(color[2] * 255);
217
- return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
218
- }
219
- function parseRgba(s) {
220
- var _a;
221
- const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
222
- if (!inner) throw new Error("Invalid rgb/rgba format");
223
- const parts = inner.split(",").map((v) => +v.trim());
224
- return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
216
+ return valid;
225
217
  }
226
- function parseHex(s) {
227
- const hex = s.slice(1);
228
- const isShort = hex.length <= 4;
229
- const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
230
- const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
231
- const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
232
- const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
233
- const result = [
234
- parseInt(r, 16) / 255,
235
- parseInt(g, 16) / 255,
236
- parseInt(b, 16) / 255
237
- ];
238
- if (a !== null) {
239
- result.push(parseInt(a, 16) / 255);
218
+ function validatePxAnimatorConfig(v, path, errors) {
219
+ if (!isObject(v)) {
220
+ errors.push(path + ": expected object");
221
+ return false;
240
222
  }
241
- return result;
242
- }
243
- function parseColor(s) {
244
- if (!s) return void 0;
245
- if (Array.isArray(s)) return s;
246
- if (typeof s !== "string") return void 0;
247
- if (s.startsWith("#")) {
248
- return parseHex(s);
249
- } else if (s.startsWith("rgb")) {
250
- return parseRgba(s);
251
- } else {
252
- console.warn("Unsupported color format: " + s);
223
+ let valid = true;
224
+ if (v.mode !== void 0 && !["auto", "webapi", "frames"].includes(v.mode)) {
225
+ errors.push(path + '.mode: invalid value "' + v.mode + `", expected 'auto'|'webapi'|'frames'`);
226
+ valid = false;
253
227
  }
254
- return void 0;
255
- }
256
- var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
257
- var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
258
- var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
259
- var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
260
- var DEFAULT_DURATION_MS = 1e3;
261
- function kebabToCamelCaseWord(kebab) {
262
- return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
263
- }
264
- function isCamelCaseWord(word) {
265
- return !word.includes("-") && /[a-z][A-Z]/.test(word);
266
- }
267
- var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
268
- // Transform/positioning
269
- "viewBox",
270
- "preserveAspectRatio",
271
- // Gradient
272
- "gradientUnits",
273
- "gradientTransform",
274
- "spreadMethod",
275
- // Pattern
276
- "patternUnits",
277
- "patternContentUnits",
278
- "patternTransform",
279
- // Clipping/masking
280
- "clipPathUnits",
281
- "maskUnits",
282
- "maskContentUnits",
283
- // Text
284
- "textLength",
285
- "lengthAdjust",
286
- "startOffset",
287
- // Filter
288
- "filterUnits",
289
- "primitiveUnits",
290
- "stdDeviation",
291
- "baseFrequency",
292
- "numOctaves",
293
- "surfaceScale",
294
- "diffuseConstant",
295
- "specularConstant",
296
- "specularExponent",
297
- "kernelMatrix",
298
- "kernelUnitLength",
299
- "edgeMode",
300
- "preserveAlpha",
301
- "targetX",
302
- "targetY"
303
- // // Animation
304
- // 'attributeName',
305
- // 'attributeType',
306
- // 'calcMode',
307
- // 'keyTimes',
308
- // 'keySplines',
309
- // 'repeatCount',
310
- // 'repeatDur'
311
- ]);
312
- function camelCaseToKebabWordIfNeeded(camel) {
313
- return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
314
- }
315
- function clamp(value, min, max) {
316
- return Math.max(min, Math.min(value, max));
317
- }
318
-
319
- // src/PxAnimatorDOM.ts
320
- var SVG_NS = "http://www.w3.org/2000/svg";
321
- var INTERNAL_ATTRS = /* @__PURE__ */ new Set(["type", "children", "animate", "style", "animator", "defs", "bindings"]);
322
- function createElement(tagName, props, style, children) {
323
- const element = document.createElementNS(SVG_NS, tagName);
324
- for (const propName in props) {
325
- element.setAttribute(camelCaseToKebabWordIfNeeded(propName), props[propName]);
228
+ if (v.duration !== void 0 && typeof v.duration !== "number") {
229
+ errors.push(path + ".duration: expected number, got " + typeof v.duration);
230
+ valid = false;
326
231
  }
327
- if (style) {
328
- for (const styleProp in style) {
329
- element.style[styleProp] = String(style[styleProp]);
330
- }
232
+ if (v.delay !== void 0 && typeof v.delay !== "number") {
233
+ errors.push(path + ".delay: expected number, got " + typeof v.delay);
234
+ valid = false;
331
235
  }
332
- if (children) {
333
- for (const child of children) {
334
- element.appendChild(child);
335
- }
236
+ if (v.iterations !== void 0 && typeof v.iterations !== "number" && v.iterations !== "infinite") {
237
+ errors.push(path + ".iterations: expected number or 'infinite', got " + typeof v.iterations);
238
+ valid = false;
336
239
  }
337
- return element;
338
- }
339
- function resolveStyle(style, defs) {
340
- var _a;
341
- if (!style) return void 0;
342
- if (typeof style === "string") {
343
- return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
240
+ if (v.fill !== void 0 && !validateFillMode(v.fill, path + ".fill", errors)) valid = false;
241
+ if (v.direction !== void 0 && !validatePlaybackDirection(v.direction, path + ".direction", errors)) valid = false;
242
+ if (v.frameRate !== void 0 && typeof v.frameRate !== "number") {
243
+ errors.push(path + ".frameRate: expected number, got " + typeof v.frameRate);
244
+ valid = false;
344
245
  }
345
- return style;
246
+ if (v.trigger !== void 0 && !validatePxTrigger(v.trigger, path + ".trigger", errors)) valid = false;
247
+ return valid;
346
248
  }
347
- function getNormalizedProps(props) {
348
- const propsCopy = {};
349
- for (const key of Object.keys(props)) {
350
- if (INTERNAL_ATTRS.has(key)) continue;
351
- let value = props[key];
352
- if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
353
- propsCopy[key] = toRGBA(value);
354
- } else if (TRANSFORM_FN_NAMES.has(key)) {
355
- if (Array.isArray(value)) {
356
- if (key === "translate") value = value.map((v) => v + "px");
357
- value = value.join(",");
358
- }
359
- if (key === "rotate") value = value + "deg";
360
- propsCopy["transform"] = key + "(" + value + ")";
361
- } else if (value !== void 0 && value !== null) {
362
- propsCopy[key] = String(value);
363
- }
249
+ function validatePxDefs(v, path, errors) {
250
+ if (!isObject(v)) {
251
+ errors.push(path + ": expected object");
252
+ return false;
364
253
  }
365
- return propsCopy;
366
- }
367
- function renderNode(node, defs) {
368
- if (!node) return null;
369
- const _a = node, { type, children, animate, style } = _a, props = __objRest(_a, ["type", "children", "animate", "style"]);
370
- const nodeDefs = node.defs || defs;
371
- const resolvedStyle = resolveStyle(style, nodeDefs);
372
- let childElements;
373
- if (children) {
374
- for (const ch of children) {
375
- const child = renderNode(ch, nodeDefs);
376
- if (child) {
377
- if (!childElements) childElements = [];
378
- childElements.push(child);
254
+ let valid = true;
255
+ if (v.easings !== void 0) {
256
+ if (!isObject(v.easings)) {
257
+ errors.push(path + ".easings: expected object");
258
+ valid = false;
259
+ } else {
260
+ for (const key of Object.keys(v.easings)) {
261
+ if (!validatePxEasingOrRef(v.easings[key], path + ".easings." + key, errors)) valid = false;
379
262
  }
380
263
  }
381
264
  }
382
- return createElement(
383
- type || "g",
384
- getNormalizedProps(props),
385
- resolvedStyle,
386
- childElements
387
- );
388
- }
389
-
390
- // src/PxAnimatorTriggers.ts
391
- function setupAnimationTriggers(api, config) {
392
- const { startOn, outAction = "continue", scrollIntoViewThreshold = 0.5 } = config;
393
- const root = api.getRootElement();
394
- if (!root) {
395
- console.warn("setupAnimationTriggers: No root element found for animation.");
396
- return api;
397
- }
398
- const start = () => {
399
- api.play();
400
- };
401
- const handleEndAction = () => {
402
- switch (outAction) {
403
- case "pause":
404
- api.pause();
405
- break;
406
- case "reset":
407
- api.cancel();
408
- break;
409
- case "reverse":
410
- api.play();
411
- break;
412
- case "continue":
413
- default:
414
- break;
415
- }
416
- };
417
- switch (startOn) {
418
- case "load": {
419
- const startHandler = () => start();
420
- if (document.readyState === "complete") {
421
- startHandler();
422
- } else {
423
- window.addEventListener("load", startHandler, { once: true });
265
+ if (v.animations !== void 0) {
266
+ if (!isObject(v.animations)) {
267
+ errors.push(path + ".animations: expected object");
268
+ valid = false;
269
+ } else {
270
+ for (const key of Object.keys(v.animations)) {
271
+ if (!validatePxAnimationDefinition(v.animations[key], path + ".animations." + key, errors)) valid = false;
424
272
  }
425
- break;
426
- }
427
- case "mouseOver": {
428
- const mouseOverHandler = () => start();
429
- const mouseOutHandler = () => handleEndAction();
430
- root.addEventListener("mouseenter", mouseOverHandler);
431
- root.addEventListener("mouseleave", mouseOutHandler);
432
- break;
433
- }
434
- case "click": {
435
- const clickHandler = () => {
436
- if (api.isPlaying()) {
437
- handleEndAction();
438
- } else {
439
- start();
440
- }
441
- };
442
- root.addEventListener("click", clickHandler);
443
- break;
444
- }
445
- case "scrollIntoView": {
446
- const observer = new IntersectionObserver(
447
- (entries) => {
448
- entries.forEach((entry) => {
449
- if (entry.isIntersecting && entry.intersectionRatio >= scrollIntoViewThreshold) {
450
- start();
451
- } else {
452
- handleEndAction();
453
- }
454
- });
455
- },
456
- { threshold: scrollIntoViewThreshold }
457
- );
458
- observer.observe(root);
459
- break;
460
273
  }
461
- case "programmatic":
462
- break;
463
274
  }
464
- return api;
465
- }
466
-
467
- // src/PxAnimatorTypes.ts
468
- var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
469
- var PX_ANIM_ATTR_NAME = "_px_animator";
470
- function isPxElementFileFormat(fileJson) {
471
- if (!(fileJson && typeof fileJson === "object" && !Array.isArray(fileJson))) {
472
- return false;
275
+ if (v.styles !== void 0 && !isObject(v.styles)) {
276
+ errors.push(path + ".styles: expected object");
277
+ valid = false;
473
278
  }
474
- return fileJson["type"] === "svg" || fileJson["tagName"] === "svg";
475
- }
476
- function isObject(v) {
477
- return v && typeof v === "object" && !Array.isArray(v);
478
- }
479
- function validateFillMode(v, path, errors) {
480
- if (v === "forwards" || v === "backwards" || v === "both" || v === "none") return true;
481
- errors.push(path + ': invalid FillMode "' + v + `", expected 'forwards'|'backwards'|'both'|'none'`);
482
- return false;
483
- }
484
- function validatePlaybackDirection(v, path, errors) {
485
- if (v === "normal" || v === "reverse" || v === "alternate" || v === "alternate-reverse") return true;
486
- errors.push(path + ': invalid PlaybackDirection "' + v + `", expected 'normal'|'reverse'|'alternate'|'alternate-reverse'`);
487
- return false;
488
- }
489
- function validatePxEasingOrRef(v, path, errors) {
490
- if (typeof v === "string") return true;
491
- if (Array.isArray(v) && v.length === 4 && v.every((n) => typeof n === "number")) return true;
492
- errors.push(path + ": invalid easing, expected string or [number, number, number, number]");
493
- return false;
279
+ return valid;
494
280
  }
495
- function validatePxKeyframe(v, path, errors) {
281
+ function validatePxBinding(v, path, errors) {
496
282
  if (!isObject(v)) {
497
283
  errors.push(path + ": expected object");
498
284
  return false;
499
285
  }
500
286
  let valid = true;
501
- if (v.time !== void 0 && typeof v.time !== "number") {
502
- errors.push(path + ".time: expected number, got " + typeof v.time);
503
- valid = false;
504
- }
505
- if (v.t !== void 0 && typeof v.t !== "number") {
506
- errors.push(path + ".t: expected number, got " + typeof v.t);
287
+ if (typeof v.id !== "string") {
288
+ errors.push(path + ".id: expected string, got " + typeof v.id);
507
289
  valid = false;
508
290
  }
509
- if (v.easing !== void 0 && !validatePxEasingOrRef(v.easing, path + ".easing", errors)) valid = false;
510
- if (v.e !== void 0 && !validatePxEasingOrRef(v.e, path + ".e", errors)) valid = false;
291
+ if (!validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
511
292
  return valid;
512
293
  }
513
- function validatePxPropertyAnimation(v, path, errors) {
294
+ function validatePxNode(v, path, errors) {
514
295
  if (!isObject(v)) {
515
296
  errors.push(path + ": expected object");
516
297
  return false;
517
298
  }
518
299
  let valid = true;
519
- if (v.keyframes !== void 0) {
520
- if (!Array.isArray(v.keyframes)) {
521
- errors.push(path + ".keyframes: expected array");
522
- valid = false;
523
- } else {
524
- v.keyframes.forEach((kf, i) => {
525
- if (!validatePxKeyframe(kf, path + ".keyframes[" + i + "]", errors)) valid = false;
526
- });
527
- }
300
+ if (typeof v.type !== "string") {
301
+ errors.push(path + ".type: expected string, got " + typeof v.type);
302
+ valid = false;
528
303
  }
529
- if (v.kfs !== void 0) {
530
- if (!Array.isArray(v.kfs)) {
531
- errors.push(path + ".kfs: expected array");
304
+ if (v.children !== void 0) {
305
+ if (!Array.isArray(v.children)) {
306
+ errors.push(path + ".children: expected array");
532
307
  valid = false;
533
308
  } else {
534
- v.kfs.forEach((kf, i) => {
535
- if (!validatePxKeyframe(kf, path + ".kfs[" + i + "]", errors)) valid = false;
309
+ v.children.forEach((child, i) => {
310
+ if (!validatePxNode(child, path + ".children[" + i + "]", errors)) valid = false;
536
311
  });
537
312
  }
538
313
  }
314
+ if (v.animate !== void 0 && !validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
539
315
  return valid;
540
316
  }
541
- function validatePxAnimationDefinition(v, path, errors) {
542
- if (!isObject(v)) {
543
- errors.push(path + ": expected object");
544
- return false;
545
- }
317
+ function validatePxSvgNode(v, path, errors) {
318
+ if (!validatePxNode(v, path, errors)) return false;
546
319
  let valid = true;
547
- for (const key of Object.keys(v)) {
548
- if (!validatePxPropertyAnimation(v[key], path + "." + key, errors)) valid = false;
320
+ if (v.type !== "svg") {
321
+ errors.push(path + ".type: expected 'svg', got '" + v.type + "'");
322
+ valid = false;
549
323
  }
550
- return valid;
551
- }
552
- function validatePxElementAnimation(v, path, errors) {
553
- if (typeof v === "string") return true;
554
- if (Array.isArray(v)) {
555
- let valid = true;
556
- v.forEach((item, i) => {
557
- if (typeof item !== "string" && !validatePxAnimationDefinition(item, path + "[" + i + "]", errors)) {
558
- valid = false;
559
- }
560
- });
561
- return valid;
324
+ if (v.width !== void 0 && typeof v.width !== "number") {
325
+ errors.push(path + ".width: expected number, got " + typeof v.width);
326
+ valid = false;
562
327
  }
563
- if (isObject(v)) return validatePxAnimationDefinition(v, path, errors);
564
- errors.push(path + ": expected string, array, or PxAnimationDefinition object");
565
- return false;
566
- }
567
- function validatePxTrigger(v, path, errors) {
568
- if (!isObject(v)) {
569
- errors.push(path + ": expected object");
570
- return false;
328
+ if (v.height !== void 0 && typeof v.height !== "number") {
329
+ errors.push(path + ".height: expected number, got " + typeof v.height);
330
+ valid = false;
571
331
  }
572
- let valid = true;
573
- const validStartOn = ["load", "mouseOver", "click", "scrollIntoView", "programmatic"];
574
- if (!validStartOn.includes(v.startOn)) {
575
- errors.push(path + '.startOn: invalid value "' + v.startOn + '", expected ' + validStartOn.join("|"));
332
+ if (v.viewBox !== void 0 && typeof v.viewBox !== "string") {
333
+ errors.push(path + ".viewBox: expected string, got " + typeof v.viewBox);
576
334
  valid = false;
577
335
  }
578
- if (v.outAction !== void 0) {
579
- const validOutAction = ["continue", "pause", "reset", "reverse"];
580
- if (!validOutAction.includes(v.outAction)) {
581
- errors.push(path + '.outAction: invalid value "' + v.outAction + '", expected ' + validOutAction.join("|"));
336
+ if (v.animator !== void 0 && !validatePxAnimatorConfig(v.animator, path + ".animator", errors)) valid = false;
337
+ if (v.defs !== void 0 && !validatePxDefs(v.defs, path + ".defs", errors)) valid = false;
338
+ if (v.bindings !== void 0) {
339
+ if (!Array.isArray(v.bindings)) {
340
+ errors.push(path + ".bindings: expected array");
582
341
  valid = false;
342
+ } else {
343
+ v.bindings.forEach((binding, i) => {
344
+ if (!validatePxBinding(binding, path + ".bindings[" + i + "]", errors)) valid = false;
345
+ });
583
346
  }
584
347
  }
585
- if (v.scrollIntoViewThreshold !== void 0 && typeof v.scrollIntoViewThreshold !== "number") {
586
- errors.push(path + ".scrollIntoViewThreshold: expected number, got " + typeof v.scrollIntoViewThreshold);
587
- valid = false;
588
- }
348
+ if (v.design !== void 0 && !validatePxNode(v.design, path + ".design", errors)) valid = false;
589
349
  return valid;
590
350
  }
591
- function validatePxAnimatorConfig(v, path, errors) {
592
- if (!isObject(v)) {
593
- errors.push(path + ": expected object");
594
- return false;
595
- }
596
- let valid = true;
597
- if (v.mode !== void 0 && !["auto", "webapi", "frames"].includes(v.mode)) {
598
- errors.push(path + '.mode: invalid value "' + v.mode + `", expected 'auto'|'webapi'|'frames'`);
599
- valid = false;
351
+ function isPxElementFileFormatDeep(fileJson) {
352
+ const errors = [];
353
+ const valid = validatePxSvgNode(fileJson, "root", errors);
354
+ return { valid, errors };
355
+ }
356
+ function getAnimatorConfig(doc) {
357
+ var _a, _b;
358
+ 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);
359
+ }
360
+ function getDefs(doc) {
361
+ var _a;
362
+ if (!doc) return void 0;
363
+ return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
364
+ }
365
+ function getBindings(doc) {
366
+ var _a;
367
+ if (!doc) return void 0;
368
+ return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
369
+ }
370
+ function getChildren(doc) {
371
+ return doc == null ? void 0 : doc.children;
372
+ }
373
+
374
+ // src/PxAnimatorUtil.ts
375
+ function bezierToSvgPath(path) {
376
+ var _a, _b, _c, _d;
377
+ const v = path.v;
378
+ const i = path.i;
379
+ const o = path.o;
380
+ const c = path.c;
381
+ if (!v.length) return "";
382
+ const d = [];
383
+ const len = v.length;
384
+ d.push("M" + v[0][0] + "," + v[0][1]);
385
+ for (let idx = 1; idx < len; idx++) {
386
+ const prevV = v[idx - 1];
387
+ const prevO = (_a = o == null ? void 0 : o[idx - 1]) != null ? _a : prevV;
388
+ const currI = (_b = i == null ? void 0 : i[idx]) != null ? _b : v[idx];
389
+ const currV = v[idx];
390
+ const isLine = prevO[0] === prevV[0] && prevO[1] === prevV[1] && (currI[0] === currV[0] && currI[1] === currV[1]);
391
+ if (isLine) {
392
+ d.push("L" + currV[0] + "," + currV[1]);
393
+ } else {
394
+ d.push("C" + prevO[0] + "," + prevO[1] + "," + currI[0] + "," + currI[1] + "," + currV[0] + "," + currV[1]);
395
+ }
600
396
  }
601
- if (v.duration !== void 0 && typeof v.duration !== "number") {
602
- errors.push(path + ".duration: expected number, got " + typeof v.duration);
603
- valid = false;
397
+ if (c && len > 0) {
398
+ const lastV = v[len - 1];
399
+ const lastO = (_c = o == null ? void 0 : o[len - 1]) != null ? _c : lastV;
400
+ const firstI = (_d = i == null ? void 0 : i[0]) != null ? _d : v[0];
401
+ const firstV = v[0];
402
+ const isLine = lastO[0] === lastV[0] && lastO[1] === lastV[1] && (firstI[0] === firstV[0] && firstI[1] === firstV[1]);
403
+ if (!isLine) {
404
+ d.push("C" + lastO[0] + "," + lastO[1] + "," + firstI[0] + "," + firstI[1] + "," + firstV[0] + "," + firstV[1]);
405
+ }
406
+ d.push("z");
604
407
  }
605
- if (v.delay !== void 0 && typeof v.delay !== "number") {
606
- errors.push(path + ".delay: expected number, got " + typeof v.delay);
607
- valid = false;
408
+ return d.join("");
409
+ }
410
+ function interpolateNum(a, b, t) {
411
+ return a + (b - a) * t;
412
+ }
413
+ function interpolateVec(a, b, t) {
414
+ const res = [];
415
+ const count = Math.max(a.length, b.length);
416
+ for (let i = 0; i < count; i++) {
417
+ res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);
608
418
  }
609
- if (v.iterations !== void 0 && typeof v.iterations !== "number" && v.iterations !== "infinite") {
610
- errors.push(path + ".iterations: expected number or 'infinite', got " + typeof v.iterations);
611
- valid = false;
419
+ return res;
420
+ }
421
+ function interpolateColor(a, b, t) {
422
+ return [
423
+ interpolateNum(a[0] || 0, b[0] || 0, t),
424
+ interpolateNum(a[1] || 0, b[1] || 0, t),
425
+ interpolateNum(a[2] || 0, b[2] || 0, t),
426
+ interpolateNum(a[3] === void 0 ? 1 : a[3], b[3] === void 0 ? 1 : b[3], t)
427
+ ];
428
+ }
429
+ function interpolateBeziers(paths1, paths2, progress) {
430
+ const count = Math.max(paths1.length, paths2.length);
431
+ const res = [];
432
+ for (let i = 0; i < count; i++) {
433
+ res.push(interpolateBezier(paths1[i], paths2[i], progress));
612
434
  }
613
- if (v.fill !== void 0 && !validateFillMode(v.fill, path + ".fill", errors)) valid = false;
614
- if (v.direction !== void 0 && !validatePlaybackDirection(v.direction, path + ".direction", errors)) valid = false;
615
- if (v.frameRate !== void 0 && typeof v.frameRate !== "number") {
616
- errors.push(path + ".frameRate: expected number, got " + typeof v.frameRate);
617
- valid = false;
435
+ return res;
436
+ }
437
+ function interpolateBezier(path1, path2, progress) {
438
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
439
+ if (!path1 || !path2) return path1 || path2 || { v: [] };
440
+ const t = Math.min(Math.max(progress, 0), 1);
441
+ const len = Math.min(path1.v.length, path2.v.length);
442
+ const v = [];
443
+ const i = [];
444
+ const o = [];
445
+ for (let idx = 0; idx < len; idx++) {
446
+ const v1 = path1.v[idx];
447
+ const v2 = path2.v[idx];
448
+ v.push(interpolateVec(v1, v2, t));
449
+ const i1 = (_b = (_a = path1.i) == null ? void 0 : _a[idx]) != null ? _b : v1;
450
+ const i2 = (_d = (_c = path2.i) == null ? void 0 : _c[idx]) != null ? _d : v2;
451
+ i.push(interpolateVec(i1, i2, t));
452
+ const o1 = (_f = (_e = path1.o) == null ? void 0 : _e[idx]) != null ? _f : v1;
453
+ const o2 = (_h = (_g = path2.o) == null ? void 0 : _g[idx]) != null ? _h : v2;
454
+ o.push(interpolateVec(o1, o2, t));
618
455
  }
619
- if (v.trigger !== void 0 && !validatePxTrigger(v.trigger, path + ".trigger", errors)) valid = false;
620
- return valid;
456
+ return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
621
457
  }
622
- function validatePxDefs(v, path, errors) {
623
- if (!isObject(v)) {
624
- errors.push(path + ": expected object");
625
- return false;
458
+ function remap(value, inMin, inMax, outMin, outMax) {
459
+ if (inMax === inMin) return outMin;
460
+ const t = (value - inMin) / (inMax - inMin);
461
+ return outMin + t * (outMax - outMin);
462
+ }
463
+ function solveCubicBezierX(p1x, p2x, x) {
464
+ if (x <= 0) return 0;
465
+ if (x >= 1) return 1;
466
+ const cx = 3 * p1x;
467
+ const bx = 3 * (p2x - p1x) - cx;
468
+ const ax = 1 - cx - bx;
469
+ function sampleX(t) {
470
+ return ((ax * t + bx) * t + cx) * t;
626
471
  }
627
- let valid = true;
628
- if (v.easings !== void 0) {
629
- if (!isObject(v.easings)) {
630
- errors.push(path + ".easings: expected object");
631
- valid = false;
632
- } else {
633
- for (const key of Object.keys(v.easings)) {
634
- if (!validatePxEasingOrRef(v.easings[key], path + ".easings." + key, errors)) valid = false;
635
- }
636
- }
472
+ function sampleDX(t) {
473
+ return (3 * ax * t + 2 * bx) * t + cx;
637
474
  }
638
- if (v.animations !== void 0) {
639
- if (!isObject(v.animations)) {
640
- errors.push(path + ".animations: expected object");
641
- valid = false;
642
- } else {
643
- for (const key of Object.keys(v.animations)) {
644
- if (!validatePxAnimationDefinition(v.animations[key], path + ".animations." + key, errors)) valid = false;
645
- }
646
- }
475
+ let t2 = x;
476
+ let t0 = 0;
477
+ let t1 = 1;
478
+ for (let i = 0; i < 8; i++) {
479
+ const x2 = sampleX(t2) - x;
480
+ if (Math.abs(x2) < 1e-6) return t2;
481
+ const d2 = sampleDX(t2);
482
+ if (Math.abs(d2) < 1e-6) break;
483
+ t2 -= x2 / d2;
484
+ }
485
+ t2 = x;
486
+ while (t0 < t1) {
487
+ const x2 = sampleX(t2);
488
+ if (Math.abs(x2 - x) < 1e-6) return t2;
489
+ if (x > x2) t0 = t2;
490
+ else t1 = t2;
491
+ t2 = (t1 + t0) / 2;
492
+ }
493
+ return t2;
494
+ }
495
+ function cubicBezier(easing) {
496
+ const [p1x, p1y, p2x, p2y] = easing;
497
+ const cy = 3 * p1y;
498
+ const by = 3 * (p2y - p1y) - cy;
499
+ const ay = 1 - cy - by;
500
+ function sampleCurveY(t) {
501
+ return ((ay * t + by) * t + cy) * t;
647
502
  }
648
- if (v.styles !== void 0 && !isObject(v.styles)) {
649
- errors.push(path + ".styles: expected object");
650
- valid = false;
503
+ return function(x) {
504
+ return sampleCurveY(solveCubicBezierX(p1x, p2x, x));
505
+ };
506
+ }
507
+ function lerp2(a, b, t) {
508
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
509
+ }
510
+ function subdivideCubicBezier(p0, p1, p2, p3, t) {
511
+ const q0 = lerp2(p0, p1, t);
512
+ const q1 = lerp2(p1, p2, t);
513
+ const q2 = lerp2(p2, p3, t);
514
+ const r0 = lerp2(q0, q1, t);
515
+ const r1 = lerp2(q1, q2, t);
516
+ const s = lerp2(r0, r1, t);
517
+ return {
518
+ left: [p0, q0, r0, s],
519
+ right: [s, r1, q2, p3]
520
+ };
521
+ }
522
+ function splitEasing(easing, xFraction) {
523
+ if (!easing) return { left: void 0, right: void 0 };
524
+ if (xFraction <= 0) return { left: void 0, right: easing };
525
+ if (xFraction >= 1) return { left: easing, right: void 0 };
526
+ const [x1, y1, x2, y2] = easing;
527
+ const t = solveCubicBezierX(x1, x2, xFraction);
528
+ const p0 = [0, 0];
529
+ const p1 = [x1, y1];
530
+ const p2 = [x2, y2];
531
+ const p3 = [1, 1];
532
+ const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);
533
+ const sx = left[3][0];
534
+ const sy = left[3][1];
535
+ let leftEasing;
536
+ if (sx > 1e-9 && Math.abs(sy) > 1e-9) {
537
+ leftEasing = [
538
+ left[1][0] / sx,
539
+ left[1][1] / sy,
540
+ left[2][0] / sx,
541
+ left[2][1] / sy
542
+ ];
543
+ }
544
+ let rightEasing;
545
+ const rx = 1 - sx;
546
+ const ry = 1 - sy;
547
+ if (rx > 1e-9 && Math.abs(ry) > 1e-9) {
548
+ rightEasing = [
549
+ (right[1][0] - sx) / rx,
550
+ (right[1][1] - sy) / ry,
551
+ (right[2][0] - sx) / rx,
552
+ (right[2][1] - sy) / ry
553
+ ];
554
+ }
555
+ return { left: leftEasing, right: rightEasing };
556
+ }
557
+ function reverseEasing(easing) {
558
+ if (!easing) return void 0;
559
+ return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];
560
+ }
561
+ function toRGBA(color) {
562
+ const r = Math.round(color[0] * 255);
563
+ const g = Math.round(color[1] * 255);
564
+ const b = Math.round(color[2] * 255);
565
+ return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
566
+ }
567
+ function parseRgba(s) {
568
+ var _a;
569
+ const inner = (_a = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a[1];
570
+ if (!inner) throw new Error("Invalid rgb/rgba format");
571
+ const parts = inner.split(",").map((v) => +v.trim());
572
+ return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
573
+ }
574
+ function parseHex(s) {
575
+ const hex = s.slice(1);
576
+ const isShort = hex.length <= 4;
577
+ const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
578
+ const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
579
+ const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
580
+ const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
581
+ const result = [
582
+ parseInt(r, 16) / 255,
583
+ parseInt(g, 16) / 255,
584
+ parseInt(b, 16) / 255
585
+ ];
586
+ if (a !== null) {
587
+ result.push(parseInt(a, 16) / 255);
651
588
  }
652
- return valid;
589
+ return result;
653
590
  }
654
- function validatePxBinding(v, path, errors) {
655
- if (!isObject(v)) {
656
- errors.push(path + ": expected object");
657
- return false;
591
+ function parseColor(s) {
592
+ if (!s) return void 0;
593
+ if (Array.isArray(s)) return s;
594
+ if (typeof s !== "string") return void 0;
595
+ if (s.startsWith("#")) {
596
+ return parseHex(s);
597
+ } else if (s.startsWith("rgb")) {
598
+ return parseRgba(s);
599
+ } else {
600
+ console.warn("Unsupported color format: " + s);
601
+ }
602
+ return void 0;
603
+ }
604
+ var COLOUR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
605
+ var TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
606
+ var PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
607
+ var STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
608
+ var DEFAULT_DURATION_MS = 1e3;
609
+ function kebabToCamelCaseWord(kebab) {
610
+ return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
611
+ }
612
+ function isCamelCaseWord(word) {
613
+ return !word.includes("-") && /[a-z][A-Z]/.test(word);
614
+ }
615
+ var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
616
+ // Transform/positioning
617
+ "viewBox",
618
+ "preserveAspectRatio",
619
+ // Gradient
620
+ "gradientUnits",
621
+ "gradientTransform",
622
+ "spreadMethod",
623
+ // Pattern
624
+ "patternUnits",
625
+ "patternContentUnits",
626
+ "patternTransform",
627
+ // Clipping/masking
628
+ "clipPathUnits",
629
+ "maskUnits",
630
+ "maskContentUnits",
631
+ // Text
632
+ "textLength",
633
+ "lengthAdjust",
634
+ "startOffset",
635
+ // Filter
636
+ "filterUnits",
637
+ "primitiveUnits",
638
+ "stdDeviation",
639
+ "baseFrequency",
640
+ "numOctaves",
641
+ "surfaceScale",
642
+ "diffuseConstant",
643
+ "specularConstant",
644
+ "specularExponent",
645
+ "kernelMatrix",
646
+ "kernelUnitLength",
647
+ "edgeMode",
648
+ "preserveAlpha",
649
+ "targetX",
650
+ "targetY"
651
+ // // Animation
652
+ // 'attributeName',
653
+ // 'attributeType',
654
+ // 'calcMode',
655
+ // 'keyTimes',
656
+ // 'keySplines',
657
+ // 'repeatCount',
658
+ // 'repeatDur'
659
+ ]);
660
+ function camelCaseToKebabWordIfNeeded(camel) {
661
+ return SVG_CAMEL_CASE_ATTRS.has(camel) ? camel : camel.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
662
+ }
663
+ function clamp(value, min, max) {
664
+ return Math.max(min, Math.min(value, max));
665
+ }
666
+
667
+ // src/PxAnimatorDOM.ts
668
+ var SVG_NS = "http://www.w3.org/2000/svg";
669
+ var ALLOWED_SVG_TAGS_LOWER_CASE = new Set([
670
+ "svg",
671
+ "g",
672
+ "path",
673
+ "circle",
674
+ "ellipse",
675
+ "rect",
676
+ "line",
677
+ "polyline",
678
+ "polygon",
679
+ "text",
680
+ "tspan",
681
+ "defs",
682
+ "clipPath",
683
+ "mask",
684
+ "pattern",
685
+ "linearGradient",
686
+ "radialGradient",
687
+ "stop",
688
+ "use",
689
+ "symbol",
690
+ "marker",
691
+ "filter",
692
+ "feGaussianBlur",
693
+ "feOffset",
694
+ "feBlend",
695
+ "feColorMatrix",
696
+ "feMerge",
697
+ "feMergeNode"
698
+ ].map((tagName) => tagName.toLowerCase()));
699
+ var ALLOWED_RESOURCE_ATTRIBUTES = [
700
+ "href",
701
+ // <use>
702
+ "src",
703
+ // <image>
704
+ "filter",
705
+ // url(#filterId)
706
+ "clipPath",
707
+ // clip-path="url(#clipPathId)"
708
+ "mask",
709
+ // url(#maskId)
710
+ "markerStart",
711
+ // marker-start="url(#markerId)"
712
+ "markerMid",
713
+ // marker-mid="url(#markerId)"
714
+ "markerEnd"
715
+ // marker-end="url(#markerId)"
716
+ // 'fill', // url(#gradientId) or url(#patternId)
717
+ // 'stroke', // url(#gradientId) or url(#patternId)
718
+ // Don't allow 'cursor', can use external SVG, // url(cursor.svg)
719
+ ];
720
+ var ALLOWED_RESOURCE_ATTRIBUTES_SET = new Set(ALLOWED_RESOURCE_ATTRIBUTES);
721
+ var ALLOWED_ATTRIBUTES_SET = /* @__PURE__ */ new Set([
722
+ "href",
723
+ "src",
724
+ // Presentation
725
+ "fill",
726
+ "stroke",
727
+ "strokeWidth",
728
+ "opacity",
729
+ "transform",
730
+ // Geometry
731
+ "x",
732
+ "y",
733
+ "cx",
734
+ "cy",
735
+ "r",
736
+ "rx",
737
+ "ry",
738
+ "width",
739
+ "height",
740
+ "d",
741
+ "x1",
742
+ "y1",
743
+ "x2",
744
+ "y2",
745
+ "points",
746
+ // Text
747
+ "fontSize",
748
+ "fontFamily",
749
+ "textAnchor",
750
+ // Structure
751
+ "id",
752
+ "class",
753
+ "viewBox",
754
+ "preserveAspectRatio",
755
+ // Gradient/Pattern
756
+ "offset",
757
+ "stopColor",
758
+ "stopOpacity",
759
+ "gradientTransform",
760
+ // Clippath/Mask
761
+ "clipPath",
762
+ "mask",
763
+ // Filter
764
+ "filter",
765
+ "stdDeviation",
766
+ "in",
767
+ "in2",
768
+ "result",
769
+ "mode",
770
+ ...ALLOWED_RESOURCE_ATTRIBUTES
771
+ ]);
772
+ function sanitiseAttributeValue(name, value) {
773
+ const nameLower = name.toLowerCase();
774
+ if (!ALLOWED_ATTRIBUTES_SET.has(nameLower)) {
775
+ console.warn("Attribute not in whitelist: ", nameLower);
776
+ return void 0;
777
+ }
778
+ if (nameLower === "fill" || nameLower === "stroke" || nameLower === "stopColor") {
779
+ const str = String(value);
780
+ if (str.includes("url(") && !/^url\(#[^)]+\)$/.test(str)) {
781
+ console.warn('Attribute "' + nameLower + '" blocked: url() references must be internal url(#id), got:', value);
782
+ return void 0;
783
+ }
784
+ return value;
658
785
  }
659
- let valid = true;
660
- if (typeof v.id !== "string") {
661
- errors.push(path + ".id: expected string, got " + typeof v.id);
662
- valid = false;
786
+ if (ALLOWED_RESOURCE_ATTRIBUTES_SET.has(nameLower)) {
787
+ const str = String(value);
788
+ if (str.startsWith("#")) {
789
+ return value;
790
+ }
791
+ if (/^url\(#[^)]+\)$/.test(str)) {
792
+ return value;
793
+ }
794
+ return void 0;
663
795
  }
664
- if (!validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
665
- return valid;
796
+ return value;
666
797
  }
667
- function validatePxNode(v, path, errors) {
668
- if (!isObject(v)) {
669
- errors.push(path + ": expected object");
670
- return false;
798
+ function createElement(tagName, normalisedProps, style, children, textContent) {
799
+ if (!ALLOWED_SVG_TAGS_LOWER_CASE.has(tagName.toLowerCase())) return null;
800
+ const element = document.createElementNS(SVG_NS, tagName);
801
+ for (const propName in normalisedProps) {
802
+ element.setAttribute(
803
+ camelCaseToKebabWordIfNeeded(propName),
804
+ sanitiseAttributeValue(propName, normalisedProps[propName])
805
+ );
671
806
  }
672
- let valid = true;
673
- if (typeof v.type !== "string") {
674
- errors.push(path + ".type: expected string, got " + typeof v.type);
675
- valid = false;
807
+ if (style) {
808
+ for (const styleProp in style) {
809
+ element.style[styleProp] = String(style[styleProp]);
810
+ }
676
811
  }
677
- if (v.children !== void 0) {
678
- if (!Array.isArray(v.children)) {
679
- errors.push(path + ".children: expected array");
680
- valid = false;
681
- } else {
682
- v.children.forEach((child, i) => {
683
- if (!validatePxNode(child, path + ".children[" + i + "]", errors)) valid = false;
684
- });
812
+ if (children) {
813
+ for (const child of children) {
814
+ element.appendChild(child);
685
815
  }
686
816
  }
687
- if (v.animate !== void 0 && !validatePxElementAnimation(v.animate, path + ".animate", errors)) valid = false;
688
- return valid;
817
+ if (textContent) element.textContent = textContent;
818
+ return element;
689
819
  }
690
- function validatePxSvgNode(v, path, errors) {
691
- if (!validatePxNode(v, path, errors)) return false;
692
- let valid = true;
693
- if (v.type !== "svg") {
694
- errors.push(path + ".type: expected 'svg', got '" + v.type + "'");
695
- valid = false;
820
+ function resolveStyle(style, defs) {
821
+ var _a;
822
+ if (!style) return void 0;
823
+ if (typeof style === "string") {
824
+ return (_a = defs == null ? void 0 : defs.styles) == null ? void 0 : _a[style];
696
825
  }
697
- if (v.width !== void 0 && typeof v.width !== "number") {
698
- errors.push(path + ".width: expected number, got " + typeof v.width);
699
- valid = false;
826
+ return style;
827
+ }
828
+ function getNormalizedProps(props) {
829
+ const propsCopy = {};
830
+ for (const key of Object.keys(props)) {
831
+ if (INTERNAL_ATTRS.has(key)) continue;
832
+ if (key === "style") continue;
833
+ let value = props[key];
834
+ if (COLOUR_ATTR_NAMES.has(key) && Array.isArray(value)) {
835
+ propsCopy[key] = toRGBA(value);
836
+ } else if (TRANSFORM_FN_NAMES.has(key)) {
837
+ if (Array.isArray(value)) {
838
+ if (key === "translate") value = value.map((v) => v + "px");
839
+ value = value.join(",");
840
+ }
841
+ if (key === "rotate") value = value + "deg";
842
+ propsCopy["transform"] = key + "(" + value + ")";
843
+ } else if (value !== void 0 && value !== null) {
844
+ propsCopy[key] = String(value);
845
+ }
700
846
  }
701
- if (v.height !== void 0 && typeof v.height !== "number") {
702
- errors.push(path + ".height: expected number, got " + typeof v.height);
703
- valid = false;
847
+ return propsCopy;
848
+ }
849
+ function renderNode(node, defs) {
850
+ if (!node) return null;
851
+ const _a = node, { type, children, animate, style } = _a, props = __objRest(_a, ["type", "children", "animate", "style"]);
852
+ const nodeDefs = node.defs || defs;
853
+ const resolvedStyle = resolveStyle(style, nodeDefs);
854
+ let childElements;
855
+ if (children) {
856
+ for (const ch of children) {
857
+ const child = renderNode(ch, nodeDefs);
858
+ if (child) {
859
+ if (!childElements) childElements = [];
860
+ childElements.push(child);
861
+ }
862
+ }
704
863
  }
705
- if (v.viewBox !== void 0 && typeof v.viewBox !== "string") {
706
- errors.push(path + ".viewBox: expected string, got " + typeof v.viewBox);
707
- valid = false;
864
+ return createElement(
865
+ type || "g",
866
+ getNormalizedProps(props),
867
+ resolvedStyle,
868
+ childElements,
869
+ props[TEXT_ATTR] || props[TEXT_CONTENT_ATTR]
870
+ );
871
+ }
872
+
873
+ // src/PxAnimatorTriggers.ts
874
+ function setupAnimationTriggers(api, config) {
875
+ const { startOn, outAction = "continue", scrollIntoViewThreshold = 0.5 } = config;
876
+ const root = api.getRootElement();
877
+ if (!root) {
878
+ console.warn("setupAnimationTriggers: No root element found for animation.");
879
+ return api;
708
880
  }
709
- if (v.animator !== void 0 && !validatePxAnimatorConfig(v.animator, path + ".animator", errors)) valid = false;
710
- if (v.defs !== void 0 && !validatePxDefs(v.defs, path + ".defs", errors)) valid = false;
711
- if (v.bindings !== void 0) {
712
- if (!Array.isArray(v.bindings)) {
713
- errors.push(path + ".bindings: expected array");
714
- valid = false;
715
- } else {
716
- v.bindings.forEach((binding, i) => {
717
- if (!validatePxBinding(binding, path + ".bindings[" + i + "]", errors)) valid = false;
718
- });
881
+ const start = () => {
882
+ api.play();
883
+ };
884
+ const handleEndAction = () => {
885
+ switch (outAction) {
886
+ case "pause":
887
+ api.pause();
888
+ break;
889
+ case "reset":
890
+ api.cancel();
891
+ break;
892
+ case "reverse":
893
+ api.play();
894
+ break;
895
+ case "continue":
896
+ default:
897
+ break;
898
+ }
899
+ };
900
+ switch (startOn) {
901
+ case "load": {
902
+ const startHandler = () => start();
903
+ if (document.readyState === "complete") {
904
+ startHandler();
905
+ } else {
906
+ window.addEventListener("load", startHandler, { once: true });
907
+ }
908
+ break;
909
+ }
910
+ case "mouseOver": {
911
+ const mouseOverHandler = () => start();
912
+ const mouseOutHandler = () => handleEndAction();
913
+ root.addEventListener("mouseenter", mouseOverHandler);
914
+ root.addEventListener("mouseleave", mouseOutHandler);
915
+ break;
916
+ }
917
+ case "click": {
918
+ const clickHandler = () => {
919
+ if (api.isPlaying()) {
920
+ handleEndAction();
921
+ } else {
922
+ start();
923
+ }
924
+ };
925
+ root.addEventListener("click", clickHandler);
926
+ break;
927
+ }
928
+ case "scrollIntoView": {
929
+ const observer = new IntersectionObserver(
930
+ (entries) => {
931
+ entries.forEach((entry) => {
932
+ if (entry.isIntersecting && entry.intersectionRatio >= scrollIntoViewThreshold) {
933
+ start();
934
+ } else {
935
+ handleEndAction();
936
+ }
937
+ });
938
+ },
939
+ { threshold: scrollIntoViewThreshold }
940
+ );
941
+ observer.observe(root);
942
+ break;
719
943
  }
944
+ case "programmatic":
945
+ break;
720
946
  }
721
- if (v.design !== void 0 && !validatePxNode(v.design, path + ".design", errors)) valid = false;
722
- return valid;
723
- }
724
- function isPxElementFileFormatDeep(fileJson) {
725
- const errors = [];
726
- const valid = validatePxSvgNode(fileJson, "root", errors);
727
- return { valid, errors };
728
- }
729
- function getAnimatorConfig(doc) {
730
- var _a, _b;
731
- 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);
732
- }
733
- function getDefs(doc) {
734
- var _a;
735
- if (!doc) return void 0;
736
- return doc.defs || ((_a = doc.meta) == null ? void 0 : _a.defs);
737
- }
738
- function getBindings(doc) {
739
- var _a;
740
- if (!doc) return void 0;
741
- return doc.bindings || ((_a = doc.meta) == null ? void 0 : _a.bindings);
742
- }
743
- function getChildren(doc) {
744
- return doc == null ? void 0 : doc.children;
947
+ return api;
745
948
  }
746
949
 
747
950
  // src/PxDefinitions.ts
@@ -898,6 +1101,111 @@ function resolveElementAnimation(animate, defs) {
898
1101
  }
899
1102
  return results;
900
1103
  }
1104
+ function interpolateValue(propName, a, b, t) {
1105
+ var _a, _b;
1106
+ if (propName === "d") {
1107
+ const aPaths = (_a = a == null ? void 0 : a.paths) != null ? _a : Array.isArray(a) ? a : [];
1108
+ const bPaths = (_b = b == null ? void 0 : b.paths) != null ? _b : Array.isArray(b) ? b : [];
1109
+ return { paths: interpolateBeziers(aPaths, bPaths, t) };
1110
+ }
1111
+ if (COLOUR_ATTR_NAMES.has(propName)) {
1112
+ return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);
1113
+ }
1114
+ if (TRANSFORM_FN_NAMES.has(propName) || propName === "stroke-dasharray" || propName === "strokeDasharray") {
1115
+ return interpolateVec(a || [], b || [], t);
1116
+ }
1117
+ return interpolateNum(+(a || 0), +(b || 0), t);
1118
+ }
1119
+ function expandLoopKeyframes(propName, keyframes, loop, duration) {
1120
+ var _a, _b, _c, _d, _e;
1121
+ const totalIntervals = keyframes.length - 1;
1122
+ const segCount = clamp((_a = loop.segmentCount) != null ? _a : totalIntervals, 1, totalIntervals);
1123
+ let segKfs;
1124
+ if (loop.before) {
1125
+ segKfs = keyframes.slice(0, segCount + 1);
1126
+ } else {
1127
+ segKfs = keyframes.slice(totalIntervals - segCount);
1128
+ }
1129
+ const firstT = (_b = keyframes[0].t) != null ? _b : 0;
1130
+ const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
1131
+ let fillStart, fillEnd;
1132
+ if (loop.before) {
1133
+ fillStart = 0;
1134
+ fillEnd = firstT;
1135
+ } else {
1136
+ fillStart = lastT;
1137
+ fillEnd = duration;
1138
+ }
1139
+ const fillDuration = fillEnd - fillStart;
1140
+ if (fillDuration <= 0) return keyframes;
1141
+ const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
1142
+ const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
1143
+ const segDuration = segEndT - segStartT;
1144
+ if (segDuration <= 0) return keyframes;
1145
+ const template = segKfs.map((kf) => ({
1146
+ relT: (kf.t - segStartT) / segDuration,
1147
+ v: kf.v,
1148
+ e: kf.e
1149
+ }));
1150
+ const fullReps = Math.floor(fillDuration / segDuration);
1151
+ const remainder = fillDuration - fullReps * segDuration;
1152
+ const partialFraction = remainder / segDuration;
1153
+ const looped = [];
1154
+ function appendRep(repStart, isReversed, partial) {
1155
+ let entries;
1156
+ if (isReversed) {
1157
+ entries = [];
1158
+ for (let i = template.length - 1; i >= 0; i--) {
1159
+ entries.push({
1160
+ relT: 1 - template[i].relT,
1161
+ v: template[i].v,
1162
+ // Easing for reversed transition: use reversed easing from the forward "from" keyframe
1163
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0
1164
+ });
1165
+ }
1166
+ } else {
1167
+ entries = template;
1168
+ }
1169
+ const cutRelT = partial !== void 0 ? partial : 1;
1170
+ for (let i = 0; i < entries.length; i++) {
1171
+ const entry = entries[i];
1172
+ if (entry.relT > cutRelT + 1e-9) {
1173
+ const prev = entries[i - 1];
1174
+ const intervalSpan = entry.relT - prev.relT;
1175
+ const localFrac = (cutRelT - prev.relT) / intervalSpan;
1176
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
1177
+ const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
1178
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
1179
+ if (looped.length > 0 && prev.relT <= cutRelT) {
1180
+ looped[looped.length - 1].e = leftEasing;
1181
+ }
1182
+ looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
1183
+ return;
1184
+ }
1185
+ looped.push({
1186
+ t: repStart + entry.relT * segDuration,
1187
+ v: entry.v,
1188
+ e: i < entries.length - 1 ? entry.e : void 0
1189
+ });
1190
+ }
1191
+ }
1192
+ for (let rep = 0; rep < fullReps; rep++) {
1193
+ const distFromBoundary = loop.before ? fullReps - 1 - rep : rep;
1194
+ const isReversed = !!loop.alternate && distFromBoundary % 2 === 0;
1195
+ const repStart = fillStart + rep * segDuration;
1196
+ appendRep(repStart, isReversed);
1197
+ }
1198
+ if (partialFraction > 1e-9) {
1199
+ const isReversed = !!loop.alternate && fullReps % 2 === 0;
1200
+ const repStart = fillStart + fullReps * segDuration;
1201
+ appendRep(repStart, isReversed, partialFraction);
1202
+ }
1203
+ if (loop.before) {
1204
+ return [...looped, ...keyframes];
1205
+ } else {
1206
+ return [...keyframes, ...looped];
1207
+ }
1208
+ }
901
1209
  function normalizeKeyframes(propName, propAnim, duration, defs) {
902
1210
  var _a, _b, _c, _d, _e;
903
1211
  const keyframes = propAnim.keyframes || propAnim.kfs || [];
@@ -922,6 +1230,11 @@ function normalizeKeyframes(propName, propAnim, duration, defs) {
922
1230
  var _a2, _b2;
923
1231
  return ((_a2 = a.t) != null ? _a2 : 0) - ((_b2 = b.t) != null ? _b2 : 0);
924
1232
  });
1233
+ const loopRaw = propAnim.loop;
1234
+ const loop = loopRaw === true ? {} : loopRaw || void 0;
1235
+ if (loop && normalized.length >= 2) {
1236
+ return expandLoopKeyframes(propName, normalized, loop, duration);
1237
+ }
925
1238
  return normalized;
926
1239
  }
927
1240
  function mergeAnimationDefinitions(animations) {
@@ -1356,40 +1669,56 @@ function createDomAdapter(rootElement) {
1356
1669
  }
1357
1670
 
1358
1671
  // src/PxAnimatorWebApi.ts
1672
+ function createCssKf(kf, t, propName, unsupportedSet) {
1673
+ var _a, _b;
1674
+ let value = (_a = kf.v) != null ? _a : kf.value;
1675
+ const e = (_b = kf.e) != null ? _b : kf.easing;
1676
+ const cssKf = {
1677
+ offset: t,
1678
+ easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
1679
+ };
1680
+ let cssValue;
1681
+ let cssKey = propName;
1682
+ if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
1683
+ cssValue = toRGBA(value);
1684
+ } else if (TRANSFORM_FN_NAMES.has(propName)) {
1685
+ if (Array.isArray(value)) {
1686
+ if (propName === "translate") value = value.map((v) => v + "px");
1687
+ value = value.join(",");
1688
+ }
1689
+ if (propName === "rotate") value = value + "deg";
1690
+ cssValue = propName + "(" + value + ")";
1691
+ cssKey = "transform";
1692
+ } else {
1693
+ cssValue = "" + value;
1694
+ }
1695
+ if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
1696
+ cssKey = kebabToCamelCaseWord(cssKey);
1697
+ cssKf[cssKey] = cssValue;
1698
+ return cssKf;
1699
+ }
1359
1700
  function convertToWebApiKeyframes(animDef, unsupportedSet, config) {
1360
- var _a, _b, _c, _d;
1701
+ var _a, _b;
1361
1702
  const result = /* @__PURE__ */ new Map();
1362
1703
  for (const [propName, propAnim] of Object.entries(animDef)) {
1363
1704
  const keyframes = propAnim.kfs || propAnim.keyframes || [];
1364
1705
  const cssKeyframes = [];
1365
- for (const kf of keyframes) {
1706
+ for (let i = 0; i < keyframes.length; i++) {
1707
+ const kf = keyframes[i];
1366
1708
  let t = (_b = (_a = kf.t) != null ? _a : kf.time) != null ? _b : 0;
1367
1709
  t = clamp(t / (config.duration || 1), 0, 1);
1368
- let value = (_c = kf.v) != null ? _c : kf.value;
1369
- const e = (_d = kf.e) != null ? _d : kf.easing;
1370
- const cssKf = {
1371
- offset: t,
1372
- easing: e && Array.isArray(e) ? "cubic-bezier(" + e.join(",") + ")" : void 0
1373
- };
1374
- let cssValue;
1375
- let cssKey = propName;
1376
- if (COLOUR_ATTR_NAMES.has(propName) && Array.isArray(value)) {
1377
- cssValue = toRGBA(value);
1378
- } else if (TRANSFORM_FN_NAMES.has(propName)) {
1379
- if (Array.isArray(value)) {
1380
- if (propName === "translate") value = value.map((v) => v + "px");
1381
- value = value.join(",");
1382
- }
1383
- if (propName === "rotate") value = value + "deg";
1384
- cssValue = propName + "(" + value + ")";
1385
- cssKey = "transform";
1386
- } else {
1387
- cssValue = "" + value;
1710
+ const cssKf = createCssKf(kf, t, propName, unsupportedSet);
1711
+ if (i === 0 && (cssKf.offset || 0) > 0) {
1712
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
1713
+ offset: 0
1714
+ }));
1388
1715
  }
1389
- if (!CSS.supports(cssKey, cssValue)) unsupportedSet.add(cssKey);
1390
- cssKey = kebabToCamelCaseWord(cssKey);
1391
- cssKf[cssKey] = cssValue;
1392
1716
  cssKeyframes.push(cssKf);
1717
+ if (i === keyframes.length - 1 && (cssKf.offset || 0) < 1) {
1718
+ cssKeyframes.push(__spreadProps(__spreadValues({}, cssKf), {
1719
+ offset: 1
1720
+ }));
1721
+ }
1393
1722
  }
1394
1723
  if (cssKeyframes.length > 0) {
1395
1724
  result.set(propName, cssKeyframes);
@@ -1430,17 +1759,17 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1430
1759
  console.warn('createWebApiAnimator: No elements found for selector "' + selector + '"');
1431
1760
  }
1432
1761
  const keyframesMap = convertToWebApiKeyframes(animDef, unsupportedSet, config);
1762
+ const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
1763
+ const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
1764
+ const effectOptions = {
1765
+ duration: config.duration,
1766
+ delay: positiveDelay,
1767
+ fill: config.fill,
1768
+ direction: config.direction,
1769
+ iterations
1770
+ };
1433
1771
  for (let i = 0; i < elements.length; i++) {
1434
1772
  const element = elements[i];
1435
- const positiveDelay = config.delay && config.delay > 0 ? config.delay : void 0;
1436
- const seekPosition = config.delay && config.delay < 0 && config.duration ? -config.delay % config.duration : void 0;
1437
- const effectOptions = {
1438
- duration: config.duration,
1439
- delay: positiveDelay,
1440
- fill: config.fill,
1441
- direction: config.direction,
1442
- iterations
1443
- };
1444
1773
  for (const [, keyframes] of keyframesMap) {
1445
1774
  if (keyframes.length > 0) {
1446
1775
  try {
@@ -1492,7 +1821,20 @@ function createWebApiAnimator(doc, callbacks, rootElement, forceEvenIfHasUnsuppo
1492
1821
  (_a = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a.call(callbacks);
1493
1822
  },
1494
1823
  "finish": () => {
1495
- animations.forEach((a) => a.finish());
1824
+ var _a;
1825
+ for (const a of animations) {
1826
+ try {
1827
+ if (((_a = a.effect) == null ? void 0 : _a.getTiming().iterations) === Infinity) {
1828
+ a.effect.updateTiming({ iterations: 1 });
1829
+ a.finish();
1830
+ a.effect.updateTiming({ iterations: Infinity });
1831
+ } else {
1832
+ a.finish();
1833
+ }
1834
+ } catch (e) {
1835
+ a.cancel();
1836
+ }
1837
+ }
1496
1838
  },
1497
1839
  "setPlaybackRate": (rate) => {
1498
1840
  animations.forEach((a) => a.playbackRate = rate);
@@ -1623,7 +1965,7 @@ function generateNewIds(doc) {
1623
1965
  value[styleProp] = replaceUrlRefs(styleValue, idMap);
1624
1966
  }
1625
1967
  }
1626
- } else if (typeof value === "object" && value !== null && key !== "animate") {
1968
+ } else if (typeof value === "object" && value !== null && key !== ANIMATE_ATTR) {
1627
1969
  updateRefs(value);
1628
1970
  }
1629
1971
  }