// Collect unique webpage URLs from the slide list. function getWebpageUrls(sourceSlides) { const urls = []; (Array.isArray(sourceSlides) ? sourceSlides : []).forEach(function (slide) { const content = slide && slide.content ? slide.content : {}; const regions = slide && slide.template && Array.isArray(slide.template.regions) ? slide.template.regions : []; regions.forEach(function (region) { if (region.region_type !== 'webpage') { return; } const regionContent = content[region.region_key] || {}; const url = String(regionContent.value || '').trim(); if (url && urls.indexOf(url) === -1) { urls.push(url); } }); }); return urls; } // Filter the slides down to those that are active right now. function getActiveSlidesFrom(sourceSlides) { const now = new Date(); return (Array.isArray(sourceSlides) ? sourceSlides : []).filter(function (slide) { return isSlideActive(slide, now); }); } // Build a cache key for the active slide set. function getActiveSlidesCacheKey() { const now = new Date(); return [ currentPlaylistSignature || '', now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds() ].join('|'); } // Return the cached active slide set for the current playlist and second. function getCurrentActiveSlides() { const cacheKey = getActiveSlidesCacheKey(); if (cacheKey !== activeSlidesCacheKey) { activeSlidesCacheValue = getActiveSlidesFrom(slides); activeSlidesCacheKey = cacheKey; } return activeSlidesCacheValue; } // Clear the cached active slide set. function clearActiveSlidesCache() { activeSlidesCacheKey = ''; activeSlidesCacheValue = []; } // Reset render caches when the viewport changes. function syncRenderCacheViewport() { var viewportKey = window.innerWidth + 'x' + window.innerHeight; if (renderCacheViewportKey === viewportKey) { return; } renderCacheViewportKey = viewportKey; slideMarkupCache = Object.create(null); templateLayoutCache = Object.create(null); templateRenderPlanCache = Object.create(null); } // Build a signature for the currently rendered view. function getCurrentRenderKey(activeSlides) { const viewportKey = window.innerWidth + 'x' + window.innerHeight; const availableSlides = Array.isArray(activeSlides) ? activeSlides : []; if (!availableSlides.length) { return [currentPlaylistSignature || '', viewportKey, 'empty', slides.length ? 'scheduled' : 'assigned'].join('|'); } let normalizedIndex = Number(index || 0); if (!Number.isFinite(normalizedIndex) || normalizedIndex < 0 || normalizedIndex >= availableSlides.length) { normalizedIndex = 0; } const slide = availableSlides[normalizedIndex]; return [currentPlaylistSignature || '', viewportKey, 'slide', slide && slide.id ? slide.id : ''].join('|'); } // Pick the current slide and the next slide for webpage preloading. function getWebpagePreloadSlides(sourceSlides, targetIndex) { const availableSlides = Array.isArray(sourceSlides) ? sourceSlides : []; if (!availableSlides.length) { return []; } let normalizedIndex = Number(targetIndex || 0); if (!Number.isFinite(normalizedIndex) || normalizedIndex < 0 || normalizedIndex >= availableSlides.length) { normalizedIndex = 0; } const preloadSlides = []; const currentSlide = availableSlides[normalizedIndex]; const nextSlide = availableSlides[normalizedIndex + 1]; if (currentSlide) { preloadSlides.push(currentSlide); } if (nextSlide && nextSlide !== currentSlide) { preloadSlides.push(nextSlide); } return preloadSlides; } // Mount hidden iframe preloads for the chosen webpage URLs. function syncWebpagePreloads(sourceSlides, targetIndex) { const urls = getWebpageUrls(getWebpagePreloadSlides(sourceSlides, targetIndex)); const signature = urls.join('\n'); if (signature === preloadSignature) { return; } if (!urls.length) { preloadSignature = ''; if (preloadContainer) { preloadContainer.innerHTML = ''; } return; } if (!preloadContainer) { preloadContainer = document.createElement('div'); preloadContainer.className = 'webpage-preloads'; preloadContainer.setAttribute('aria-hidden', 'true'); document.body.appendChild(preloadContainer); } preloadContainer.innerHTML = urls.map(function (url) { return ''; }).join(''); preloadSignature = signature; } // Return a stable client id for this browser session. function getCommandClientId() { if (commandClientId) { return commandClientId; } try { var storedClientId = window.localStorage.getItem(commandClientStorageKey); if (storedClientId) { commandClientId = storedClientId; return commandClientId; } } catch (_error) { // fall through to ephemeral ID generation } commandClientId = (window.crypto && window.crypto.randomUUID ? window.crypto.randomUUID() : 'client-' + Date.now() + '-' + Math.random().toString(16).slice(2)); try { window.localStorage.setItem(commandClientStorageKey, commandClientId); } catch (_error2) { // ignore storage errors } return commandClientId; } // Load the most recent playlist snapshot from browser storage. function loadPlaylistSnapshot() { try { var raw = window.localStorage.getItem(playlistSnapshotStorageKey); if (!raw) { return null; } var parsed = JSON.parse(raw); if (!parsed || !Array.isArray(parsed.slides)) { return null; } return { slides: parsed.slides.map(normalizeSlide), signature: String(parsed.signature || ''), fadeBetweenSlides: Boolean(parsed.fadeBetweenSlides), etag: String(parsed.etag || '') }; } catch (_error) { return null; } } // Save the latest playlist snapshot for offline recovery. function savePlaylistSnapshot(data) { try { window.localStorage.setItem(playlistSnapshotStorageKey, JSON.stringify({ slides: Array.isArray(data && data.slides) ? data.slides : [], signature: String(data && data.signature || ''), fadeBetweenSlides: Boolean(data && data.fadeBetweenSlides), etag: String(data && data.etag || ''), savedAt: new Date().toISOString() })); } catch (_error) { // ignore storage errors } } // Apply a playlist snapshot to the current in-memory state. function applyPlaylistSnapshot(data) { if (!data || !Array.isArray(data.slides)) { return false; } slides = data.slides.map(normalizeSlide); currentPlaylistSignature = String(data.signature || ''); currentPlaylistFadeBetweenSlides = Boolean(data.fadeBetweenSlides); currentPlaylistEtag = String(data.etag || ''); pendingPlaylistUpdate = null; clearActiveSlidesCache(); slideMarkupCache = Object.create(null); templateLayoutCache = Object.create(null); templateRenderPlanCache = Object.create(null); renderCacheViewportKey = ''; index = 0; return true; }