// Region animation configuration and lifecycle helpers. function normalizePlayerAnimationConfig(value) { var raw = value; if (typeof raw === 'string') { var text = String(raw || '').trim(); if (!text) { raw = null; } else { try { raw = JSON.parse(text); } catch (_error) { raw = null; } } } if (!raw || typeof raw !== 'object' || Array.isArray(raw)) { raw = {}; } return { intro: normalizePlayerAnimationStep(raw.intro, 'none'), outro: normalizePlayerAnimationStep(raw.outro !== undefined ? raw.outro : raw.out, 'none'), loop: normalizePlayerAnimationStep(raw.loop, 'none') }; } function normalizePlayerAnimationStep(value, fallbackPreset) { if (value && typeof value === 'object' && !Array.isArray(value)) { return { preset: String(value.preset || fallbackPreset || 'none').trim(), duration_ms: Number.isFinite(Number(value.duration_ms)) && Number(value.duration_ms) > 0 ? Math.round(Number(value.duration_ms)) : null, delay_ms: Number.isFinite(Number(value.delay_ms)) && Number(value.delay_ms) >= 0 ? Math.round(Number(value.delay_ms)) : null, iterations: Number.isFinite(Number(value.iterations)) && Number(value.iterations) > 0 ? Math.round(Number(value.iterations)) : null }; } return { preset: String(typeof value === 'string' ? value : fallbackPreset || 'none').trim(), duration_ms: null, delay_ms: null, iterations: null }; } function getAnimationStepTimingMs(step) { if (!step) { return 0; } var preset = String(step.preset || 'none').trim(); if (!preset || preset === 'none') { return 0; } var durationMs = Number(step.duration_ms); if (!Number.isFinite(durationMs) || durationMs <= 0) { durationMs = 1000; } var delayMs = Number(step.delay_ms); if (!Number.isFinite(delayMs) || delayMs < 0) { delayMs = 0; } var iterations = Number(step.iterations); if (!Number.isFinite(iterations) || iterations < 1) { iterations = 1; } return delayMs + (durationMs * iterations); } function getRegionAnimationPhaseTimings(root, phase) { if (!root) { return []; } var normalizedPhase = String(phase || 'intro').trim().toLowerCase(); if (normalizedPhase !== 'intro' && normalizedPhase !== 'outro') { normalizedPhase = 'intro'; } var timings = []; Array.prototype.forEach.call(root.querySelectorAll('[data-animation-json]'), function (element) { if (!element) { return; } var config; try { config = normalizePlayerAnimationConfig(element.getAttribute('data-animation-json') || '{}'); } catch (_error) { config = null; } if (!config) { return; } var timingMs = getAnimationStepTimingMs(normalizedPhase === 'outro' ? config.outro : config.intro); if (timingMs > 0) { timings.push({ element: element, timingMs: timingMs }); } }); return timings; } function getRegionAnimationPhaseTimingMs(root, phase) { return getRegionAnimationPhaseTimings(root, phase).reduce(function (maxTimingMs, entry) { return Math.max(maxTimingMs, Number(entry && entry.timingMs || 0)); }, 0); } function clearRegionAnimationClasses(root) { if (!root) { return; } var elements = []; if (typeof root.matches === 'function' && root.matches('[data-animation-json]')) { elements.push(root); } Array.prototype.forEach.call(root.querySelectorAll('[data-animation-json]'), function (element) { elements.push(element); }); elements.forEach(function (element) { if (!element) { return; } element.classList.remove('animate__animated', 'animate__infinite'); element.classList.remove('animate__repeat-1', 'animate__repeat-2', 'animate__repeat-3'); Array.prototype.slice.call(element.classList || []).forEach(function (className) { if (String(className || '').indexOf('animate__') === 0) { element.classList.remove(className); } }); element.style.removeProperty('--animate-duration'); element.style.removeProperty('--animate-delay'); element.style.removeProperty('--animate-repeat'); element.style.removeProperty('animation-delay'); }); } function isAttentionSeekerAnimation(preset) { return ['bounce', 'flash', 'pulse', 'rubberBand', 'shakeX', 'shakeY', 'headShake', 'swing', 'tada', 'wobble', 'jello', 'heartBeat'].indexOf(String(preset || '').trim()) !== -1; } function applyAnimationStep(element, step, phase) { if (!element || !step) { return; } var preset = String(step.preset || 'none').trim(); if (!preset || preset === 'none') { return; } element.style.setProperty('--animate-duration', String(Math.max(1, Number(step.duration_ms || 0) || 1000)) + 'ms'); var delayMs = phase === 'outro' ? 0 : Math.max(0, Number(step.delay_ms || 0)); if (delayMs > 0) { element.style.setProperty('--animate-delay', String(delayMs) + 'ms'); element.style.setProperty('animation-delay', String(delayMs) + 'ms'); } else { element.style.removeProperty('--animate-delay'); element.style.removeProperty('animation-delay'); } element.classList.add('animate__animated', 'animate__' + preset); if (phase === 'loop') { var repeatCount = Number(step.iterations); if (!Number.isFinite(repeatCount) || repeatCount < 1) { repeatCount = 1; } if (repeatCount > 1) { element.classList.add('animate__repeat-1'); } element.style.setProperty('--animate-repeat', String(repeatCount)); return; } if (isAttentionSeekerAnimation(preset) && Number(step.iterations || 0) > 1) { element.style.setProperty('--animate-repeat', String(Math.max(1, Number(step.iterations || 1)))); } } function playRegionAnimation(element, phase) { if (!element) { return; } var normalizedPhase = String(phase || 'intro').trim().toLowerCase(); if (normalizedPhase !== 'intro' && normalizedPhase !== 'outro') { normalizedPhase = 'intro'; } var config; try { config = normalizePlayerAnimationConfig(element.getAttribute('data-animation-json') || '{}'); } catch (_error) { config = null; } if (!config) { return; } element.dataset.animationPhase = normalizedPhase; clearRegionAnimationClasses(element); if (normalizedPhase === 'outro') { applyAnimationStep(element, config.outro, 'outro'); return; } if (config.intro && String(config.intro.preset || '').trim() && String(config.intro.preset || '').trim() !== 'none') { applyAnimationStep(element, config.intro, 'intro'); if (config.loop && String(config.loop.preset || '').trim() && String(config.loop.preset || '').trim() !== 'none') { element.addEventListener('animationend', function handleAnimationEnd(event) { if (event.target !== element) { return; } if (String(element.dataset.animationPhase || '').trim() !== 'intro') { return; } element.removeEventListener('animationend', handleAnimationEnd); clearRegionAnimationClasses(element); applyAnimationStep(element, config.loop, 'loop'); }); } return; } applyAnimationStep(element, config.loop, 'loop'); } function playRegionAnimations(root, phase) { if (!root) { return; } var normalizedPhase = String(phase || 'intro').trim().toLowerCase(); if (normalizedPhase !== 'intro' && normalizedPhase !== 'outro') { normalizedPhase = 'intro'; } var elements = Array.prototype.slice.call(root.querySelectorAll('[data-animation-json]')); elements.forEach(function (element) { playRegionAnimation(element, normalizedPhase); }); }