1/** 2 * ESSMA Cinematic Hero (HE-06) â play-once intro controller. Dependency-free. 3 * 4 * Progressive enhancement: the hero content is visible by default. This script 5 * hides it (adds `.is-intro` to the .essma-he6 root) ONLY when there is a real 6 * <video> to play on the live front end, then reveals it (removes `.is-intro`, 7 * adds `.is-revealed` + `.is-ended`) when the video ends. 8 * 9 * Bails (reveals immediately, no gating) when: 10 * ⢠running inside an iframe â i.e. the Breakdance builder canvas, so editors 11 * always see the content; 12 * ⢠the user prefers reduced motion; 13 * ⢠there is no playable <video> element; 14 * ⢠autoplay is blocked / the video errors / never finishes (safety backstop). 15 * 16 * The first play is MUTED (the only way browsers permit autoplay); an unmute 17 * button can add sound mid-play. Once the intro ends, a "Replay intro" button 18 * appears â clicking it restarts the video WITH SOUND (a click is a user 19 * gesture, so audio is now allowed) and re-hides the content until it ends 20 * again. Replays on every page load (no storage). 21 */ 22(function () { 23 function inIframe() { 24 try { return window.self !== window.top; } catch (e) { return true; } 25 } 26 27 function initHero(root) { 28 if (root.__essmaHe6) return; // guard against double-init 29 root.__essmaHe6 = true; 30 31 var video = root.querySelector('video.essma-he6__video'); 32 var mute = root.querySelector('.essma-he6__mute'); 33 var replay = root.querySelector('.essma-he6__replay'); 34 var revealed = false; 35 var backstop = null; 36 37 function clearBackstop() { if (backstop) { clearTimeout(backstop); backstop = null; } } 38 39 // Never trap the content: if autoplay is blocked or the video stalls, reveal 40 // anyway. 12s until metadata is known, then (duration + 2.5s). 41 function armBackstop() { 42 clearBackstop(); 43 var d = video.duration; 44 backstop = setTimeout(reveal, (d && isFinite(d)) ? d * 1000 + 2500 : 12000); 45 } 46 47 function reveal() { 48 if (revealed) return; 49 revealed = true; 50 clearBackstop(); 51 try { video.pause(); } catch (e) {} // hold the last frame (no loop) 52 root.classList.remove('is-intro'); 53 root.classList.add('is-revealed', 'is-ended'); 54 } 55 56 function setSound(on) { 57 video.muted = !on; 58 root.classList.toggle('is-unmuted', on); 59 if (mute) { 60 mute.setAttribute('aria-pressed', String(on)); 61 mute.setAttribute('aria-label', on ? 'Turn sound off' : 'Turn sound on'); 62 } 63 } 64 65 // Restart the intro: hide content, rewind, play. `withSound` is true for the 66 // Replay button (its click is the gesture that lets audio play). 67 function startIntro(withSound) { 68 revealed = false; 69 root.classList.remove('is-revealed', 'is-ended'); 70 root.classList.add('is-intro'); 71 if (withSound) setSound(true);
72 try { video.currentTime = 0; } catch (e) {} 73 var p = video.play(); 74 if (p && p.catch) p.catch(function () { reveal(); }); 75 armBackstop(); 76 } 77 78 // Unmute button â toggles sound while the intro plays. 79 if (mute && video) { 80 mute.addEventListener('click', function () { 81 setSound(video.muted); // currently muted â turn on, and vice-versa 82 if (!video.muted) { var p = video.play(); if (p && p.catch) p.catch(function () {}); } 83 }); 84 } 85 86 // Replay button â restarts the intro WITH sound. 87 if (replay && video) { 88 replay.addEventListener('click', function (e) { 89 e.preventDefault(); 90 startIntro(true); 91 }); 92 } 93 94 var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 95 96 // No video, embedded (builder), or reduced motion â show content now. 97 if (!video || inIframe() || reduce) { 98 if (video) { try { video.pause(); } catch (e) {} } 99 reveal(); 100 return; 101 } 102 103 video.addEventListener('ended', reveal); 104 video.addEventListener('error', reveal); 105 video.addEventListener('loadedmetadata', function () { if (!revealed) armBackstop(); }); 106 107 startIntro(false); // first play: muted autoplay 108 } 109 110 function init() { 111 var heroes = document.querySelectorAll('.essma-he6'); 112 Array.prototype.forEach.call(heroes, initHero); 113 } 114 115 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); 116 else init(); 117})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.