(function () { 'use strict'; var VERSION = 10; if (window.__portfolioVideoDecoderVersion === VERSION) return; window.__portfolioVideoDecoderVersion = VERSION; var HLS_CDN = 'https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js'; var MOBILE_MQ = '(max-width: 991px)'; var HERO_HLS_OPTS = { enableWorker: true, autoStartLoad: true, startFragPrefetch: true, maxBufferLength: 30, maxMaxBufferLength: 60, 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 s = document.createElement('script'); s.src = HLS_CDN; s.async = true; s.onload = resolve; s.onerror = reject; document.head.appendChild(s); }); 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 getCmsItem(video) { return video ? video.closest('.cms_background .cms_item') : null; } function getHlsUrl(video) { return ( video.getAttribute('data-hls-src') || video.getAttribute('data-hsl-src') || '' ); } function getMuxPlaybackId(ref) { var m = (ref || '').match(/stream\.mux\.com\/([^/?#]+)/i); if (!m) return ''; return m[1].replace(/\.m3u8$/i, ''); } function getMuxMp4Url(video) { var id = getMuxPlaybackId(getHlsUrl(video) || video.getAttribute('src') || ''); if (!id) return ''; return 'https://stream.mux.com/' + id + '/capped-1080p.mp4'; } 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 }; var muxMp4 = getMuxMp4Url(video); if (muxMp4) return { type: 'mp4', url: muxMp4, fallback: getHlsUrl(video) }; var hls = getHlsUrl(video); if (hls) return { type: 'hls', url: hls }; return { type: 'unknown', url: video.getAttribute('src') || '' }; } 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 (window.matchMedia(MOBILE_MQ).matches) return false; } return true; } function requestPlay(video) { if (!video) return; function tryPlay() { if (!video.paused) 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; 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') return; 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.querySelector('track[data-ps-track="1"]')) return; 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) { video.muted = true; video.defaultMuted = true; video.loop = true; video.playsInline = true; video.setAttribute('muted', ''); video.setAttribute('loop', ''); video.setAttribute('playsinline', ''); video.setAttribute('preload', 'auto'); video.removeAttribute('data-lazy-load'); if (!video.crossOrigin) video.crossOrigin = 'anonymous'; if (isHero) { ensureCmsPosterFallback(video); revealHeroVideo(video); } } function attachHls(video, url, shouldPlay) { loadHls() .then(function () { if (!window.Hls || !Hls.isSupported()) { video.src = url; if (shouldPlay) requestPlay(video); return; } if (video._portfolioHls) { video._portfolioHls.destroy(); video._portfolioHls = null; } var hls = new Hls(HERO_HLS_OPTS); hls.loadSource(url); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, function () { if (shouldPlay) requestPlay(video); }); hls.on(Hls.Events.FRAG_BUFFERED, function (_, data) { if (shouldPlay && data && data.frag && data.frag.sn === 0) { requestPlay(video); } }); hls.on(Hls.Events.ERROR, function (_, data) { if (data && data.fatal) { console.warn('[portfolio-video-decoder] HLS fatal:', data.type, data.details); } }); video._portfolioHls = hls; }) .catch(function () { video.src = url; if (shouldPlay) requestPlay(video); }); } function loadNativeVideo(video, url, shouldPlay) { if (video.src !== url) video.src = url; if (shouldPlay) { video.addEventListener('canplay', function () { requestPlay(video); }, { once: true }); requestPlay(video); } } function loadSource(video, spec, shouldPlay) { if (!spec || !spec.url) return; if (spec.type === 'hls' || /\.m3u8(\?|#|$)/i.test(spec.url)) { if (video.canPlayType('application/vnd.apple.mpegurl')) { loadNativeVideo(video, spec.url, shouldPlay); return; } attachHls(video, spec.url, shouldPlay); return; } function useHlsFallback() { if (!spec.fallback) return; try { video.removeAttribute('src'); } catch (e) {} attachHls(video, spec.fallback, shouldPlay); } video.addEventListener( 'error', function onErr() { video.removeEventListener('error', onErr); useHlsFallback(); }, { once: true } ); loadNativeVideo(video, spec.url, shouldPlay); } function resetVideoInit(video) { if (!video) return; if (video._portfolioHls) { try { video._portfolioHls.destroy(); } catch (e) {} video._portfolioHls = null; } delete video.dataset.hlsInit; delete video.dataset.carouselPrimed; try { video.removeAttribute('src'); video.load(); } catch (e) {} } 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 (video.error) resetVideoInit(video); else { if (shouldPlay) requestPlay(video); return; } } var spec = isHero ? getHeroPlaybackUrl(video) : { type: 'auto', url: getVideoUrl(video) }; if (!spec.url) return; video.dataset.hlsInit = '1'; applyVideoFlags(video, isHero); 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); } function bufferHeroVideo(video) { if (!video || !shouldInit(video)) return; startVideo(video, { isHero: true, bufferOnly: true, shouldPlay: false }); } function playHeroVideo(video) { if (!video) return; revealHeroVideo(video); startVideo(video, { isHero: true, shouldPlay: true }); requestPlay(video); var item = getCmsItem(video); if (item && item.classList.contains('is-active') && video.readyState >= 2 && video.currentTime > 0) { clearPosterLayers(video, item); } } function bufferAllHeroVideos() { var items = document.querySelectorAll('.background_cases .cms_background .cms_item'); var activeVideos = []; var idleVideos = []; items.forEach(function (item) { item.querySelectorAll('video').forEach(function (video) { if (item.classList.contains('is-active')) activeVideos.push(video); else idleVideos.push(video); }); }); activeVideos.forEach(playHeroVideo); idleVideos.forEach(function (video, i) { setTimeout(function () { bufferHeroVideo(video); }, 120 + i * 80); }); } 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; } startVideo(video); } function initAll() { document.querySelectorAll('video').forEach(initVideo); } function warmVideo(video, keepPlaying) { if (!video || !shouldInit(video)) return; if (isCmsHeroVideo(video)) { bufferHeroVideo(video); if (keepPlaying) playHeroVideo(video); return; } startVideo(video, { bufferOnly: true, shouldPlay: false }); if (!keepPlaying) video.pause(); } window.startPortfolioVideo = startVideo; window.playPortfolioHeroVideo = playHeroVideo; window.bufferPortfolioHeroVideo = bufferHeroVideo; window.bufferAllPortfolioHeroVideos = bufferAllHeroVideos; window.warmPortfolioVideo = warmVideo; window.disconnectPortfolioArchiveVideos = disconnectArchiveVideos; ready(function () { bufferAllHeroVideos(); initAll(); if (typeof MutationObserver === 'function') { var pending = false; var observer = new MutationObserver(function () { if (pending) return; pending = true; requestAnimationFrame(function () { pending = false; initAll(); }); }); observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-hls-src', 'data-src', 'src', 'class'] }); } window.addEventListener('pageshow', function (e) { document.querySelectorAll('.cms_background .cms_item.is-active video').forEach(playHeroVideo); if (e.persisted) bufferAllHeroVideos(); }); console.info('[portfolio-video-decoder v' + VERSION + '] ready'); }); })();