SCALE FILMS
work /
← back

Shadow in the Dark

(function(){ const ORIGIN = location.origin; // Patch origin param for YouTube embeds (it helps when enablejsapi=1) document.querySelectorAll('.js-player[data-type="youtube"]').forEach(el=>{ try{ const u = new URL(el.dataset.src); u.searchParams.set('origin', ORIGIN); el.dataset.src = u.toString(); }catch(e){} }); function postToYouTube(iframe, func, args){ if(!iframe || !iframe.contentWindow) return; iframe.contentWindow.postMessage(JSON.stringify({event:'command',func:func,args:args||[]}), '*'); } function postToVimeo(iframe, method, value){ if(!iframe || !iframe.contentWindow) return; iframe.contentWindow.postMessage({method, value}, '*'); } function ensureIframe(el){ const frame = el.querySelector('.frame'); if(!frame) return null; let iframe = frame.querySelector('iframe'); if(iframe) return iframe; iframe = document.createElement('iframe'); iframe.allow = 'autoplay; fullscreen; picture-in-picture'; iframe.setAttribute('allowfullscreen',''); iframe.setAttribute('playsinline',''); iframe.src = el.dataset.src || ''; iframe.id = 'scalePlayer'; frame.appendChild(iframe); const poster = frame.querySelector('.poster'); if(poster) poster.style.display='none'; const ctp = frame.querySelector('.clickToPlay'); if(ctp) ctp.style.display='none'; // Make 16:9 iframe cover 2.35 container (uniform scale, then crop). function cover(){ const r = frame.getBoundingClientRect(); // target a 16:9 box inside frame for scaling reference const iw = r.width; const ih = r.width * 9/16; const scale = Math.max(1, r.height / ih); iframe.style.width = iw + 'px'; iframe.style.height = ih + 'px'; iframe.style.transform = 'translate(-50%,-50%) scale(' + scale + ')'; } cover(); window.addEventListener('resize', cover, {passive:true}); setTimeout(cover, 250); iframe.addEventListener('load', ()=>{ iframe.classList.add('ready'); setTimeout(cover, 50); }); return iframe; } function currentType(el){ return (el && el.dataset && el.dataset.type) || 'none'; } document.addEventListener('click', (e)=>{ const playClick = e.target.closest('.clickToPlay'); if(playClick){ const el = playClick.closest('.js-player'); if(!el) return; const iframe = ensureIframe(el); const t = currentType(el); if(t === 'youtube') postToYouTube(iframe, 'playVideo'); if(t === 'vimeo') postToVimeo(iframe, 'play'); } const btn = e.target.closest('.ctl'); if(!btn) return; const el = btn.closest('.js-player'); if(!el) return; const act = btn.getAttribute('data-act'); const iframe = ensureIframe(el); const t = currentType(el); if(act === 'toggle'){ const playing = el.getAttribute('data-playing') === '1'; if(t === 'youtube'){ postToYouTube(iframe, playing ? 'pauseVideo' : 'playVideo'); } if(t === 'vimeo'){ postToVimeo(iframe, playing ? 'pause' : 'play'); } el.setAttribute('data-playing', playing ? '0' : '1'); } if(act === 'rew'){ if(t === 'youtube'){ const cur = (el.__yt && typeof el.__yt.currentTime === 'number') ? el.__yt.currentTime : 0; postToYouTube(iframe,'seekTo',[Math.max(0, cur - 10), true]); } if(t === 'vimeo'){ postToVimeo(iframe,'getCurrentTime'); el.__vmSeekDir = -1; } } if(act === 'ff'){ if(t === 'youtube'){ const cur = (el.__yt && typeof el.__yt.currentTime === 'number') ? el.__yt.currentTime : 0; postToYouTube(iframe,'seekTo',[Math.max(0, cur + 10), true]); } if(t === 'vimeo'){ postToVimeo(iframe,'getCurrentTime'); el.__vmSeekDir = +1; } } if(act === 'fs'){ if(act === 'fs'){ const f = el.querySelector('iframe'); if(f && f.requestFullscreen) f.requestFullscreen(); } }); // Seek helpers: we request current time, then set. window.addEventListener('message', (ev)=>{ const data = ev.data; // YouTube infoDelivery messages arrive as strings (JSON) if(typeof data === 'string' && data.indexOf('infoDelivery') !== -1){ try{ const obj = JSON.parse(data); if(obj && obj.event === 'infoDelivery' && obj.info){ const iframe = document.getElementById('scalePlayer'); // best-effort: attach to the nearest player const el = iframe ? iframe.closest('.js-player') : null; if(el){ el.__yt = el.__yt || {}; if(typeof obj.info.currentTime === 'number') el.__yt.currentTime = obj.info.currentTime; if(typeof obj.info.playerState === 'number') el.__yt.playerState = obj.info.playerState; // 1=playing el.setAttribute('data-playing', (el.__yt.playerState === 1) ? '1' : (el.getAttribute('data-playing')||'0')); } } }catch(e){} } // Vimeo responses are objects if(data && typeof data === 'object' && data.method === 'getCurrentTime' && typeof data.value === 'number'){ const iframe = document.getElementById('scalePlayer'); const el = iframe ? iframe.closest('.js-player') : null; if(el){ const dir = el.__vmSeekDir || 0; const next = Math.max(0, data.value + dir * 10); postToVimeo(iframe, 'setCurrentTime', next); el.__vmSeekDir = 0; el.setAttribute('data-playing','1'); } } }); // Volume document.querySelectorAll('.volR').forEach(r=>{ r.addEventListener('input', ()=>{ const el = r.closest('.js-player'); const iframe = ensureIframe(el); const t = currentType(el); const v = parseInt(r.value || '0',10); if(t === 'youtube') postToYouTube(iframe,'setVolume',[v]); if(t === 'vimeo') postToVimeo(iframe,'setVolume', Math.max(0, Math.min(1, v/100))); }, {passive:true}); }); // Share link (copy) document.querySelectorAll('[data-act="share"]').forEach(a=>{ a.addEventListener('click', async (e)=>{ e.preventDefault(); try{ await navigator.clipboard.writeText(location.href); a.style.opacity = '1'; }catch(err){ // fallback prompt('Copy link:', location.href); } }); }); // Photo gallery lightbox (if present) const masonry = document.getElementById('masonry'); const lb = document.getElementById('lb'); const lbImg = document.getElementById('lbImg'); const lbX = document.getElementById('lbX'); const lbPrev = document.getElementById('lbPrev'); const lbNext = document.getElementById('lbNext'); if (masonry && lb && lbImg) { const buttons = Array.from(masonry.querySelectorAll('[data-idx]')); const urls = buttons.map(b => (b.querySelector('img') ? b.querySelector('img').getAttribute('src') : '')).filter(Boolean); let idx = 0; function openAt(i){ idx = Math.max(0, Math.min(urls.length - 1, i)); lbImg.src = urls[idx] || ''; lb.classList.add('open'); lb.setAttribute('aria-hidden', 'false'); } function closeLb(){ lb.classList.remove('open'); lb.setAttribute('aria-hidden', 'true'); } function next(){ openAt((idx + 1) % urls.length); } function prev(){ openAt((idx - 1 + urls.length) % urls.length); } masonry.addEventListener('click', (e) => { const b = e.target && e.target.closest ? e.target.closest('[data-idx]') : null; if (!b) return; const i = parseInt(b.getAttribute('data-idx') || '0', 10) || 0; openAt(i); }); lbX && (lbX.onclick = closeLb); lbPrev && (lbPrev.onclick = prev); lbNext && (lbNext.onclick = next); lb.addEventListener('click', (e) => { if (e.target === lb) closeLb(); }); document.addEventListener('keydown', (e) => { if (!urls.length) return; const isOpen = lb.classList.contains('open'); if (e.key === ' ' || e.code === 'Space') { e.preventDefault(); if (isOpen) closeLb(); else openAt(0); return; } if (!isOpen) return; if (e.key === 'Escape') { closeLb(); return; } if (e.key === 'ArrowRight') { next(); return; } if (e.key === 'ArrowLeft') { prev(); return; } }); } })();