PageSourceSearch

https://www.vizionplus.tv/wp-content/themes/vizionplus/assets/js/modules/hero-block.js

js vizionplus.tv collected 2026-10-02 06:32:10 UTC 5,574 bytes, 120 lines download raw bytes

1/**
2 * The first block's "Programi i sotëm" (home/live.php):
3 * · the button unfolds the rest of today's schedule under the two columns (.stage.is-open —
4 *   the panel is a 0fr→1fr grid row, the rows reveal with a stagger set here as --i, capped);
5 *   the chevron turns, the label flips to "Mbyll", Esc closes, focus lands on the first row and
6 *   comes back to the button. On phones the button is a different one that opens the bottom
7 *   sheet (modules/sheet.js), so nothing here runs there.
8 * · the rows read the clock: a cached page may be an hour old, so which programme is on and
9 *   which are over is decided here (is-now / is-past) and once a minute after that. Earlier rows
10 *   hide behind "Shfaq … programet e mëparshme" (.is-hide-past, the guide's own pattern).
11 * · ?epg=open opens the panel on load — a harmless way to look at the open state without a click.
12 */
13import { toMin, nowMin, nowSec } from './data.js';
14
15export default function heroBlock() {
16  const stage = document.querySelector('[data-stage]');
17  if (!stage) return;
18  const btn = stage.querySelector('[data-stage-toggle]');
19  const panel = stage.querySelector('[data-stage-panel]');
20  const label = btn && btn.querySelector('[data-stage-label]');
21  const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
22  const visible = (el) => getComputedStyle(el).display !== 'none';
23
24  /* --- the rows, from the clock --- */
25  const paintRows = () => {
26    const n = nowMin();
27    document.querySelectorAll('[data-epg-row]').forEach((row) => {
28      if (!row.dataset.start) return;
29      const s = toMin(row.dataset.start);
30      let e = toMin(row.dataset.end);
31      if (e <= s) e = 24 * 60;
32      row.classList.toggle('is-past', e <= n);
33      row.classList.toggle('is-now', s <= n && n < e);
34    });
35    document.querySelectorAll('[data-epg-list]').forEach((list) => {
36      const toggle = list.querySelector('[data-epg-past]');
37      if (!toggle) return;
38      const count = list.querySelectorAll('.epgl__row.is-past').length;
39      toggle.hidden = !count;
40      toggle.textContent = list.classList.contains('is-hide-past')
41        ? (toggle.dataset.i18nShow || 'Show the %d earlier programmes').replace('%d', count)
42        : (toggle.dataset.i18nHide || 'Hide the earlier programmes');
43    });
44  };
45
46  /* stagger indexes over the rows that will actually show (past ones may be hidden) */
47  const stagger = () => {
48    if (!panel) return;
49    let i = 0;
50    panel.querySelectorAll('.epgl__row').forEach((row) => {
51      row.style.setProperty('--i', reduced ? 0 : Math.min(i, 12));
52      if (visible(row)) i += 1;
53    });
54  };
55
56  document.addEventListener('click', (e) => {
57    const toggle = e.target.closest('[data-epg-past]');
58    if (!toggle) return;
59    const list = toggle.closest('[data-epg-list]');
60    if (!list) return;
61    list.classList.toggle('is-hide-past');
62    paintRows();
63    stagger();
64  });
65
66  /* --- open / close --- */
67  const settle = reduced ? 0 : 460; // matches the panel's grid-row transition in live.css
68  let timer = 0;
69  const isOpen = () => stage.classList.contains('is-open');
70
71  const open = (focus = true) => {
72    if (!btn || !panel || isOpen()) return;
73    clearTimeout(timer);
74    paintRows();
75    stagger();
76    panel.removeAttribute('inert');
77    stage.classList.add('is-open');
78    btn.setAttribute('aria-expanded', 'true');
79    if (label) label.textContent = btn.dataset.i18nClose || label.textContent;
80    if (!focus) return; // opened on page load: no focus jump, no scroll
81    timer = setTimeout(() => {
82      const first = panel.querySelector('.epgl__row.is-now') || [...panel.querySelectorAll('.epgl__row')].find(visible);
83      if (first) first.focus({ preventScroll: true });
84      if (first) first.scrollIntoView({ block: 'nearest', behavior: reduced ? 'auto' : 'smooth' });
85    }, settle);
86  };
87  const close = (refocus = true) => {
88    if (!btn || !panel || !isOpen()) return;
89    clearTimeout(timer);
90    stage.classList.remove('is-open');
91    btn.setAttribute('aria-expanded', 'false');
92    if (label) label.textContent = btn.dataset.i18nOpen || label.textContent;
93    timer = setTimeout(() => { if (!isOpen()) panel.setAttribute('inert', ''); }, settle);
94    if (refocus) btn.focus({ preventScroll: true });
95  };
96
97  if (btn && panel) {
98    panel.setAttribute('inert', '');
99    btn.addEventListener('click', () => (isOpen() ? close() : open()));
100    document.addEventListener('keydown', (e) => {
101      if (e.key === 'Escape' && isOpen()) { e.preventDefault(); close(); }
102    });
103    /* the phone layout swaps the panel for the sheet — an open panel left over from a resize
104       must not keep the toggle claiming it is expanded */
105    window.matchMedia('(max-width: 767.98px)').addEventListener('change', (e) => { if (e.matches && !stage.hasAttribute('data-stage-open')) close(false); });
106    /* the Live TV page ships the panel open (data-stage-open); ?epg=open does the same anywhere */
107    if ((stage.hasAttribute('data-stage-open') || new URLSearchParams(window.location.search).get('epg') === 'open') && visible(btn)) {
108      stage.classList.remove('is-open'); // the server-rendered open state, so no-JS readers see the schedule
109      open(false);
110    } else {
111      stage.classList.remove('is-open');
112    }
113  }
114
115  /* --- keep the rows current, on the minute --- */
116  paintRows();
117  const loop = () => setTimeout(() => { paintRows(); loop(); }, Math.max(2000, (60 - nowSec()) * 1000 + 250));
118  loop();
119  document.addEventListener('visibilitychange', () => { if (!document.hidden) paintRows(); });
120}

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.