PageSourceSearch

https://essma.eu/wp-content/plugins/essma-elements/assets/js/cinematic.js?ver=1.0.200

js essma.eu collected 2026-10-02 17:38:11 UTC 4,386 bytes, 117 lines download raw bytes

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.