(doc: Document)
| 450 | } |
| 451 | |
| 452 | function parseStageZoomKeyframes(doc: Document): StageZoomKeyframe[] { |
| 453 | const zoomContainer = doc.getElementById("stage-zoom-container"); |
| 454 | if (!zoomContainer) { |
| 455 | return []; |
| 456 | } |
| 457 | |
| 458 | const zoomKeyframesAttr = zoomContainer.getAttribute("data-zoom-keyframes"); |
| 459 | if (!zoomKeyframesAttr) { |
| 460 | return []; |
| 461 | } |
| 462 | |
| 463 | try { |
| 464 | const parsed = JSON.parse(zoomKeyframesAttr); |
| 465 | if (Array.isArray(parsed)) { |
| 466 | return parsed.filter( |
| 467 | (kf): kf is StageZoomKeyframe => |
| 468 | typeof kf === "object" && |
| 469 | kf !== null && |
| 470 | typeof kf.id === "string" && |
| 471 | typeof kf.time === "number" && |
| 472 | typeof kf.zoom === "object" && |
| 473 | kf.zoom !== null && |
| 474 | typeof kf.zoom.scale === "number" && |
| 475 | typeof kf.zoom.focusX === "number" && |
| 476 | typeof kf.zoom.focusY === "number", |
| 477 | ); |
| 478 | } |
| 479 | } catch { |
| 480 | // skip invalid zoom keyframes |
| 481 | } |
| 482 | |
| 483 | return []; |
| 484 | } |
| 485 | |
| 486 | function normalizeKeyframes( |
| 487 | keyframes: Keyframe[], |
no test coverage detected