(function () {
'use strict';
var VERSION = 13;
if (window.__portfolioVideoDecoderVersion === VERSION) return;
window.__portfolioVideoDecoderVersion = VERSION;
var HLS_CDNS = [
'https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js',
'https://unpkg.com/hls.js@1.5.7/dist/hls.min.js'
];
var MOBILE_MQ = '(max-width: 991px)';
// Same width gate as the immersive carousel (canShowHoldUi): only there every tile needs a first frame.
var CAROUSEL_MQ = (window.HOME_IMMERSIVE_CONFIG && window.HOME_IMMERSIVE_CONFIG.widthMq) || '(min-width: 992px)';
var PRELOAD_GATE_TIMEOUT_MS = 3000;
var PRELOAD_JOB_TIMEOUT_MS = 8000;
var MAX_FATAL_RETRIES = 3;
var mobileMq = window.matchMedia(MOBILE_MQ);
var carouselMq = window.matchMedia(CAROUSEL_MQ);
function hlsOpts() {
var mobile = mobileMq.matches;
return {
enableWorker: true,
autoStartLoad: false,
startFragPrefetch: true,
maxBufferLength: mobile ? 12 : 20,
maxMaxBufferLength: mobile ? 24 : 40,
startLevel: 0,
capLevelToPlayerSize: true
};
}
function loadHls() {
if (window.Hls) return Promise.resolve();
if (window.__portfolioHlsLoadPromise) return window.__portfolioHlsLoadPromise;
window.__portfolioHlsLoadPromise = new Promise(function (resolve, reject) {
var i = 0;
function attempt() {
var s = document.createElement('script');
s.src = HLS_CDNS[i];
s.async = true;
// Must match the in
, otherwise the preload is wasted and hls.js downloads twice.
s.crossOrigin = 'anonymous';
s.onload = function () {
if (window.Hls) resolve();
else reject(new Error('hls.js loaded without global'));
};
s.onerror = function () {
s.remove();
i += 1;
if (i < HLS_CDNS.length) attempt();
else reject(new Error('hls.js failed to load'));
};
document.head.appendChild(s);
}
attempt();
});
return window.__portfolioHlsLoadPromise;
}
loadHls().catch(function () {});
function ready(fn) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fn, { once: true });
} else {
fn();
}
}
function isArchiveGridVideo(video) {
return !!(video && video.closest('.custom_grid .archive_item'));
}
function isCmsHeroVideo(video) {
return !!(video && video.closest('.cms_background .cms_item'));
}
function liveOverlayOpen() {
return !!document.querySelector('.surf_live_overlay.is-open');
}
function getCmsItem(video) {
return video ? video.closest('.cms_background .cms_item') : null;
}
function getHeroItems() {
return Array.prototype.slice.call(document.querySelectorAll('.background_cases .cms_background .cms_item'));
}
function getHlsUrl(video) {
return video.getAttribute('data-hls-src') || video.getAttribute('data-hsl-src') || '';
}
function getVideoUrl(video) {
return (
video.getAttribute('data-mp4-src') ||
video.getAttribute('data-src') ||
getHlsUrl(video) ||
video.getAttribute('src') ||
''
);
}
function getHeroPlaybackUrl(video) {
var mp4 = video.getAttribute('data-mp4-src') || video.getAttribute('data-src') || '';
if (/\.mp4(\?|#|$)/i.test(mp4)) return { type: 'mp4', url: mp4, fallback: getHlsUrl(video) };
var hls = getHlsUrl(video);
if (hls) return { type: 'hls', url: hls };
return { type: 'unknown', url: video.getAttribute('src') || '' };
}
// Each CMS item carries a desktop and a mobile ; one of them is display:none for the current breakpoint.
function isHiddenBreakpointTwin(video) {
var item = getCmsItem(video);
if (!item) return false;
if (!item.querySelector('video.desktop') || !item.querySelector('video.mobile')) return false;
return getComputedStyle(video).display === 'none';
}
function getVisibleHeroVideo(item) {
if (!item) return null;
var desktop = item.querySelector('video.desktop');
var mobile = item.querySelector('video.mobile');
if (desktop && mobile) return getComputedStyle(desktop).display !== 'none' ? desktop : mobile;
return item.querySelector('video');
}
function shouldInit(video) {
if (video.closest('.surf_live_overlay') || video.getAttribute('data-surf-live-player') === '1') {
return false;
}
if (window.__workVisualVideoVersion && video.closest('.visual_item')) return false;
if (isArchiveGridVideo(video)) {
if (window.__archiveMobileVideoActive) return false;
if (mobileMq.matches) return false;
}
if (isCmsHeroVideo(video) && isHiddenBreakpointTwin(video)) return false;
return true;
}
// Managed page videos only play while shown; a late canplay must not restart one that was hidden meanwhile.
function wantsPlay(video, shouldPlay) {
if (video._pvManaged) return !!video._pvWantPlay;
return shouldPlay || video._pvWantPlay;
}
function requestPlay(video) {
if (!video) return;
function tryPlay() {
if (!video.paused) return;
if (video._pvManaged && !video._pvWantPlay) return;
video.play().catch(function () {});
}
tryPlay();
if (video.readyState < 3) {
video.addEventListener('loadeddata', tryPlay, { once: true });
video.addEventListener('canplay', tryPlay, { once: true });
}
}
function clearPosterLayers(video, item) {
item = item || getCmsItem(video);
if (!video) return;
if (video.dataset.carouselPoster == null) video.dataset.carouselPoster = video.getAttribute('poster') || '';
video.removeAttribute('poster');
if (item && item.dataset.posterBg === '1') {
item.style.backgroundImage = '';
item.style.backgroundSize = '';
item.style.backgroundPosition = '';
delete item.dataset.posterBg;
}
}
function markVideoReady(video) {
var item = getCmsItem(video);
if (item) item.classList.add('is-video-ready');
}
function revealHeroVideo(video) {
if (!video || !isCmsHeroVideo(video)) return;
if (video.dataset.heroRevealed === '1' || video._pvRevealBound) return;
video._pvRevealBound = true;
function tryReveal() {
if (video.dataset.heroRevealed === '1') return;
if (video.readyState < 2) return;
if (video.paused && video.currentTime <= 0) return;
video.dataset.heroRevealed = '1';
markVideoReady(video);
var item = getCmsItem(video);
if (item && item.classList.contains('is-active')) {
clearPosterLayers(video, item);
}
}
video.addEventListener('loadeddata', tryReveal);
video.addEventListener('canplay', tryReveal);
video.addEventListener('playing', tryReveal);
video.addEventListener('timeupdate', function onTU() {
if (video.currentTime > 0.01) {
video.removeEventListener('timeupdate', onTU);
tryReveal();
}
});
tryReveal();
}
function ensureCmsPosterFallback(video) {
var item = getCmsItem(video);
if (!item || item.dataset.posterBg === '1') return;
var poster = video.getAttribute('poster');
if (!poster) return;
item.dataset.posterBg = '1';
item.style.backgroundImage = 'url("' + poster.replace(/"/g, '%22') + '")';
item.style.backgroundSize = 'cover';
item.style.backgroundPosition = 'center';
}
function addDecorativeCaptionsTrack(video) {
if (video._pvTrack || video.querySelector('track[data-ps-track="1"]')) {
video._pvTrack = true;
return;
}
video._pvTrack = true;
var track = document.createElement('track');
track.setAttribute('data-ps-track', '1');
track.kind = 'captions';
track.label = 'No speech';
track.srclang = 'en';
track.src =
'data:text/vtt;charset=utf-8,' +
encodeURIComponent('WEBVTT\n\n1\n00:00:00.000 --> 00:00:01.000\n.\n');
video.appendChild(track);
if (video.textTracks && video.textTracks.length) {
try {
video.textTracks[0].mode = 'hidden';
} catch (e) {}
}
}
function applyVideoFlags(video, isHero, wantPlay) {
video.muted = true;
video.defaultMuted = true;
video.loop = true;
video.playsInline = true;
video.setAttribute('muted', '');
video.setAttribute('loop', '');
video.setAttribute('playsinline', '');
video.removeAttribute('data-lazy-load');
if (!video.crossOrigin) video.crossOrigin = 'anonymous';
if (isHero) {
// Webflow renders autoplay on every CMS video, which made every buffered background video start playing and decoding.
video.autoplay = false;
video.removeAttribute('autoplay');
video.setAttribute('preload', wantPlay ? 'auto' : 'metadata');
ensureCmsPosterFallback(video);
revealHeroVideo(video);
bindLoadControl(video);
} else if (video._pvManaged) {
video.autoplay = false;
video.removeAttribute('autoplay');
video.setAttribute('preload', wantPlay ? 'auto' : 'metadata');
bindLoadControl(video);
} else {
video.setAttribute('preload', 'auto');
}
}
// hls.js keeps downloading while paused; buffered-but-idle videos only need their first fragment.
function bindLoadControl(video) {
if (video._pvLoadControl) return;
video._pvLoadControl = true;
video.addEventListener('play', function () {
video._pvWantPlay = true;
resumeLoading(video);
});
video.addEventListener('pause', function () {
video._pvWantPlay = false;
suspendLoading(video);
});
}
function resumeLoading(video) {
var hls = video._portfolioHls;
if (!hls || !video._pvLoadStopped) return;
video._pvLoadStopped = false;
try {
hls.startLoad(-1);
} catch (e) {}
}
function suspendLoading(video) {
var hls = video._portfolioHls;
if (!hls || video._pvLoadStopped || !video._pvHeadBuffered) return;
video._pvLoadStopped = true;
try {
hls.stopLoad();
} catch (e) {}
}
function attachHls(video, url, shouldPlay) {
loadHls()
.then(function () {
if (!window.Hls || !Hls.isSupported()) {
video.src = url;
if (wantsPlay(video, shouldPlay)) requestPlay(video);
else settlePreload(video);
return;
}
if (video._portfolioHls) {
video._portfolioHls.destroy();
video._portfolioHls = null;
}
var hls = new Hls(hlsOpts());
var retries = 0;
video._portfolioHls = hls;
video._pvLoadStopped = false;
video._pvHeadBuffered = false;
hls.attachMedia(video);
hls.on(Hls.Events.MEDIA_ATTACHED, function () {
hls.loadSource(url);
});
hls.on(Hls.Events.MANIFEST_PARSED, function () {
hls.startLoad(-1);
if (wantsPlay(video, shouldPlay)) requestPlay(video);
});
hls.on(Hls.Events.FRAG_BUFFERED, function () {
if (video._pvHeadBuffered) return;
video._pvHeadBuffered = true;
if (wantsPlay(video, shouldPlay)) requestPlay(video);
else if (video.paused) suspendLoading(video);
settlePreload(video);
});
hls.on(Hls.Events.ERROR, function (_, data) {
if (!data || !data.fatal) return;
console.warn('[portfolio-video-decoder] HLS fatal:', data.type, data.details);
if (retries >= MAX_FATAL_RETRIES) {
settlePreload(video);
return;
}
retries += 1;
if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
hls.recoverMediaError();
} else if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
setTimeout(function () {
if (video._portfolioHls === hls) hls.startLoad(-1);
}, 1000 * retries);
} else {
settlePreload(video);
}
});
})
.catch(function () {
video.src = url;
if (wantsPlay(video, shouldPlay)) requestPlay(video);
else settlePreload(video);
});
}
function loadNativeVideo(video, url, shouldPlay) {
if (video.getAttribute('src') !== url) video.src = url;
if (shouldPlay) {
video.addEventListener('canplay', function () { requestPlay(video); }, { once: true });
requestPlay(video);
} else {
video.addEventListener('loadeddata', function () { settlePreload(video); }, { once: true });
video.addEventListener('error', function () { settlePreload(video); }, { once: true });
}
}
function loadSource(video, spec, shouldPlay) {
if (!spec || !spec.url) return;
if (spec.type === 'hls' || /\.m3u8(\?|#|$)/i.test(spec.url)) {
// Prefer hls.js wherever MSE exists: native HLS cannot be told to stop buffering a paused video.
if (!window.MediaSource && video.canPlayType('application/vnd.apple.mpegurl')) {
loadNativeVideo(video, spec.url, shouldPlay);
return;
}
attachHls(video, spec.url, shouldPlay);
return;
}
if (spec.fallback) {
video.addEventListener('error', function onErr() {
if (video._pvFellBack) return;
video._pvFellBack = true;
try {
video.removeAttribute('src');
} catch (e) {}
loadSource(video, { type: 'hls', url: spec.fallback }, shouldPlay || video._pvWantPlay);
}, { once: true });
}
loadNativeVideo(video, spec.url, shouldPlay);
}
function startVideo(video, options) {
if (!video || !shouldInit(video)) return;
options = options || {};
var isHero = !!options.isHero || isCmsHeroVideo(video);
var shouldPlay = options.shouldPlay !== false && !options.bufferOnly;
addDecorativeCaptionsTrack(video);
if (video.dataset.hlsInit === '1') {
if (shouldPlay) {
video._pvWantPlay = true;
resumeLoading(video);
requestPlay(video);
}
return;
}
var spec = isHero ? getHeroPlaybackUrl(video) : { type: 'auto', url: getVideoUrl(video) };
if (!spec.url) return;
video.dataset.hlsInit = '1';
if (shouldPlay) video._pvWantPlay = true;
applyVideoFlags(video, isHero, shouldPlay);
if (isHero) {
loadSource(video, spec, shouldPlay);
return;
}
if (/\.mp4(\?|#|$)/i.test(spec.url)) {
loadNativeVideo(video, spec.url, shouldPlay);
return;
}
if (/\.m3u8(\?|#|$)/i.test(spec.url)) {
loadSource(video, { type: 'hls', url: spec.url }, shouldPlay);
return;
}
if (shouldPlay) requestPlay(video);
}
/* ---------- Idle preload queue: one video at a time, only after the active one plays ---------- */
/* The carousel switches it to urgent: gate open, several first fragments in parallel, nearest first. */
var queue = [];
var running = [];
var runningTimers = [];
var concurrency = 1;
var URGENT_CONCURRENCY = 3;
var gateOpen = false;
var batching = false;
var gateTimer = 0;
function openGate() {
if (gateOpen) return;
gateOpen = true;
clearTimeout(gateTimer);
pump();
}
function armGate(activeVideo) {
if (gateOpen || gateTimer) return;
gateTimer = setTimeout(openGate, PRELOAD_GATE_TIMEOUT_MS);
if (activeVideo) {
if (!activeVideo.paused && activeVideo.readyState >= 3) openGate();
else activeVideo.addEventListener('playing', openGate, { once: true });
}
}
function saveData() {
var c = navigator.connection;
return !!(c && (c.saveData || /(^|-)2g$/.test(c.effectiveType || '')));
}
function itemDistanceFromActive(item) {
var items = getHeroItems();
var n = items.length;
var idx = items.indexOf(item);
var active = -1;
for (var i = 0; i < n; i++) {
if (items[i].classList.contains('is-active')) {
active = i;
break;
}
}
if (idx < 0 || active < 0) return 0;
var d = Math.abs(idx - active);
return Math.min(d, n - d);
}
function inPreloadWindow(video) {
if (carouselMq.matches && !saveData()) return true;
return itemDistanceFromActive(getCmsItem(video)) <= 1;
}
function settlePreload(video) {
var i = running.indexOf(video);
if (i < 0) return;
clearTimeout(runningTimers[i]);
running.splice(i, 1);
runningTimers.splice(i, 1);
pump();
}
function startPreloadJob(video) {
running.push(video);
runningTimers.push(
setTimeout(function () {
settlePreload(video);
}, PRELOAD_JOB_TIMEOUT_MS)
);
startVideo(video, { isHero: isCmsHeroVideo(video), bufferOnly: true, shouldPlay: false });
if (video.dataset.hlsInit !== '1') settlePreload(video);
}
function preloadDistance(video) {
var item = getCmsItem(video);
return item ? itemDistanceFromActive(item) : stackDistance(video);
}
function pump() {
// The live preview needs the whole connection; idle first fragments wait until it closes.
if (batching || !gateOpen || running.length >= concurrency || liveOverlayOpen()) return;
queue.sort(function (a, b) {
return preloadDistance(a) - preloadDistance(b);
});
while (queue.length && running.length < concurrency) {
var video = queue.shift();
video._pvQueued = false;
if (video.dataset.hlsInit === '1' || !shouldInit(video) || !inPreloadWindow(video)) continue;
startPreloadJob(video);
}
}
// Called by the carousel when the user is about to switch (hover / hold / enter): every tile needs a frame now.
function urgentPreload() {
concurrency = URGENT_CONCURRENCY;
bufferAllHeroVideos();
openGate();
pump();
}
function bufferHeroVideo(video) {
if (!video || !shouldInit(video)) return;
if (video.dataset.hlsInit === '1' || video._pvQueued) return;
if (!inPreloadWindow(video)) return;
video._pvQueued = true;
queue.push(video);
pump();
}
function playHeroVideo(video) {
if (!video || !shouldInit(video)) return;
if (video._pvQueued) {
video._pvQueued = false;
queue = queue.filter(function (v) { return v !== video; });
}
revealHeroVideo(video);
startVideo(video, { isHero: true, shouldPlay: true });
requestPlay(video);
armGate(video);
var item = getCmsItem(video);
if (item && item.classList.contains('is-active') && video.readyState >= 2 && video.currentTime > 0) {
clearPosterLayers(video, item);
}
}
function bufferAllHeroVideos() {
var active = null;
// Enqueue everything first so pump() starts the nearest neighbours, not the first items in DOM order.
batching = true;
getHeroItems().forEach(function (item) {
var video = getVisibleHeroVideo(item);
if (!video) return;
if (item.classList.contains('is-active')) active = video;
else bufferHeroVideo(video);
});
batching = false;
pump();
if (active) playHeroVideo(active);
else armGate(null);
}
/* ---------- Page videos outside the hero: only the shown ones play ---------- */
/* Case pages stack whole CMS lists of videos on top of each other (visibility:hidden until their item is switched
on). Playing every one of them used the whole connection (17 streams, ~200 MB in 8 s) and starved the live
preview. A managed video plays only while it is visible and near the viewport and no preview is open; the rest
keep their first fragment, paused, and are fetched one at a time, nearest to the shown item first. */
var managedVideos = [];
var managedObserver = null;
var managedUpdatePending = false;
function isManagedVideo(video) {
return !isCmsHeroVideo(video) && !isArchiveGridVideo(video) && !!getVideoUrl(video);
}
function isShownVideo(video) {
if (!video.isConnected || !video._pvInView || liveOverlayOpen()) return false;
return getComputedStyle(video).visibility !== 'hidden';
}
function stackDistance(video) {
var item = video.closest('.w-dyn-item');
var list = item && item.parentElement;
if (!list) return 0;
var items = Array.prototype.slice.call(list.children);
var shown = -1;
for (var i = 0; i < items.length; i++) {
var v = items[i].querySelector('video');
if (v && v._pvShown) {
shown = i;
break;
}
}
return shown < 0 ? 0 : Math.abs(items.indexOf(item) - shown);
}
function updateManagedVideos() {
managedUpdatePending = false;
managedVideos = managedVideos.filter(function (video) {
return video.isConnected;
});
managedVideos.forEach(function (video) {
video._pvShown = isShownVideo(video);
});
managedVideos.forEach(function (video) {
if (video._pvShown) {
video._pvWantPlay = true;
if (video.dataset.hlsInit !== '1') {
if (video._pvQueued) {
video._pvQueued = false;
queue = queue.filter(function (v) { return v !== video; });
}
video.addEventListener('playing', openGate, { once: true });
startVideo(video, { shouldPlay: true });
} else {
resumeLoading(video);
requestPlay(video);
}
} else {
video._pvWantPlay = false;
if (!video.paused) video.pause();
if (video.dataset.hlsInit !== '1' && !video._pvQueued && shouldInit(video)) {
video._pvQueued = true;
queue.push(video);
}
}
});
pump();
}
function scheduleManagedUpdate() {
if (managedUpdatePending || !managedVideos.length) return;
managedUpdatePending = true;
requestAnimationFrame(updateManagedVideos);
}
function manageVideo(video) {
if (video._pvManaged) return;
video._pvManaged = true;
video._pvInView = !managedObserver;
// Webflow's autoplay attribute would start the video as soon as data arrives, whether it is shown or not.
video.autoplay = false;
video.removeAttribute('autoplay');
// Page scripts call load() on videos without metadata yet; on one hls.js is attaching to, that drops its MediaSource.
video.load = function () {
if (video._portfolioHls && /^blob:/.test(video.getAttribute('src') || '')) return;
HTMLMediaElement.prototype.load.call(video);
};
managedVideos.push(video);
if (managedObserver) managedObserver.observe(video);
scheduleManagedUpdate();
}
if (typeof IntersectionObserver === 'function') {
managedObserver = new IntersectionObserver(
function (entries) {
entries.forEach(function (e) {
e.target._pvInView = e.isIntersecting;
});
scheduleManagedUpdate();
},
{ rootMargin: '50% 0px' }
);
}
function disconnectArchiveVideos() {
document.querySelectorAll('.custom_grid .archive_item video').forEach(function (video) {
video.pause();
if (video._portfolioHls) {
try {
video._portfolioHls.destroy();
} catch (e) {}
video._portfolioHls = null;
}
video.removeAttribute('src');
try {
video.load();
} catch (e) {}
delete video.dataset.hlsInit;
});
}
function initVideo(video) {
if (!shouldInit(video)) return;
if (isCmsHeroVideo(video)) {
var item = getCmsItem(video);
if (item && item.classList.contains('is-active')) playHeroVideo(video);
else bufferHeroVideo(video);
return;
}
if (isManagedVideo(video)) {
manageVideo(video);
return;
}
startVideo(video);
}
function initPending() {
document.querySelectorAll('video:not([data-hls-init="1"])').forEach(initVideo);
}
function warmVideo(video, keepPlaying) {
if (!video || !shouldInit(video)) return;
if (isCmsHeroVideo(video)) {
if (keepPlaying) playHeroVideo(video);
else bufferHeroVideo(video);
return;
}
startVideo(video, { bufferOnly: true, shouldPlay: false });
if (!keepPlaying) video.pause();
}
function onBreakpointChange() {
getHeroItems().forEach(function (item) {
item.querySelectorAll('video').forEach(function (video) {
if (isHiddenBreakpointTwin(video) && !video.paused) video.pause();
});
if (item.classList.contains('is-active')) playHeroVideo(getVisibleHeroVideo(item));
});
bufferAllHeroVideos();
}
window.startPortfolioVideo = startVideo;
window.playPortfolioHeroVideo = playHeroVideo;
window.bufferPortfolioHeroVideo = bufferHeroVideo;
window.bufferAllPortfolioHeroVideos = bufferAllHeroVideos;
window.warmPortfolioVideo = warmVideo;
window.prioritizePortfolioHeroVideos = urgentPreload;
window.disconnectPortfolioArchiveVideos = disconnectArchiveVideos;
ready(function () {
bufferAllHeroVideos();
initPending();
if (typeof MutationObserver === 'function') {
var pending = false;
var activated = [];
var observer = new MutationObserver(function (records) {
var relevant = false;
for (var i = 0; i < records.length; i++) {
var r = records[i];
var t = r.target;
if (r.type === 'childList') {
for (var j = 0; j < r.addedNodes.length; j++) {
var n = r.addedNodes[j];
if (n.nodeType === 1 && (n.tagName === 'VIDEO' || n.querySelector('video'))) relevant = true;
}
} else if (r.attributeName === 'class') {
scheduleManagedUpdate();
if (t.classList && t.classList.contains('cms_item') && t.classList.contains('is-active')) {
activated.push(t);
relevant = true;
}
} else if (t.tagName === 'VIDEO' && t.dataset.hlsInit !== '1') {
relevant = true;
}
}
if (!relevant || pending) return;
pending = true;
requestAnimationFrame(function () {
pending = false;
var items = activated;
activated = [];
items.forEach(function (item) {
if (item.classList.contains('is-active')) playHeroVideo(getVisibleHeroVideo(item));
});
initPending();
});
});
observer.observe(document, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['data-hls-src', 'data-src', 'data-mp4-src', 'class']
});
}
// Items fade out with a visibility transition, and some scripts reveal videos through inline styles.
document.addEventListener('transitionend', scheduleManagedUpdate, true);
setInterval(function () {
if (!document.hidden) scheduleManagedUpdate();
}, 400);
if (mobileMq.addEventListener) mobileMq.addEventListener('change', onBreakpointChange);
else if (mobileMq.addListener) mobileMq.addListener(onBreakpointChange);
window.addEventListener('pageshow', function (e) {
document.querySelectorAll('.cms_background .cms_item.is-active').forEach(function (item) {
playHeroVideo(getVisibleHeroVideo(item));
});
if (e.persisted) bufferAllHeroVideos();
});
console.info('[portfolio-video-decoder v' + VERSION + '] ready');
});
})();