PageSourceSearch

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

js vizionplus.tv collected 2026-10-02 06:32:21 UTC 2,864 bytes, 48 lines download raw bytes

1/**
2 * Live blog: while the event is live, poll the theme REST route every 45 s; when the update count grows,
3 * hold the new HTML behind a "Show N new updates" button (so the reader's scroll position is never hijacked),
4 * then swap it in with a highlight. Stops polling when the newsroom marks the coverage as ended.
5 */
6import { t } from './data.js';
7
8const INTERVAL = 45000;
9
10export default function init() {
11  const root = document.querySelector('[data-liveblog]');
12  if (!root || root.dataset.liveblogStatus !== 'live') return;
13  const feed = root.querySelector('[data-liveblog-feed]');
14  const bar = root.querySelector('[data-liveblog-new]');
15  const countEl = root.querySelector('[data-liveblog-new-count]');
16  const updatedEl = root.querySelector('[data-liveblog-updated]');
17  const key = document.querySelector('[data-liveblog-key] ol');
18  let known = Number(root.dataset.liveblogCount || 0);
19  let pending = null; let timer = 0;
20
21  const apply = () => {
22    if (!pending) return;
23    const before = new Set([...feed.querySelectorAll('[data-lb-update]')].map((n) => n.dataset.lbUpdate));
24    feed.innerHTML = pending.html;
25    feed.querySelectorAll('[data-lb-update]').forEach((n) => { if (!before.has(n.dataset.lbUpdate)) n.classList.add('is-new'); });
26    if (key) key.innerHTML = [...feed.querySelectorAll('[data-lb-update]')].slice(0, 8).map((n) => `<li><a href="#${n.id}"><time>${n.querySelector('time')?.textContent || ''}</time><span>${n.querySelector('.lb__t')?.textContent || ''}</span></a></li>`).join('');
27    if (updatedEl) updatedEl.textContent = t('updatedAt', 'updated %s').replace('%s', pending.updated);
28    known = pending.count; pending = null; bar.hidden = true;
29    document.title = document.title.replace(/^\(\d+\) /, '');
30  };
31  const poll = async () => {
32    try {
33      const res = await fetch(root.dataset.liveblogRest, { headers: { Accept: 'application/json' } });
34      if (!res.ok) return;
35      const d = await res.json();
36      if (d.status !== 'live') { root.dataset.liveblogStatus = 'ended'; root.classList.replace('lb--live', 'lb--ended'); clearInterval(timer); }
37      if (d.count > known) {
38        pending = d;
39        // reader is at the top of the feed → swap immediately, otherwise offer a button
40        if (feed.getBoundingClientRect().top > -40) apply();
41        else { countEl.textContent = `(${d.count - known})`; bar.hidden = false; document.title = `(${d.count - known}) ${document.title.replace(/^\(\d+\) /, '')}`; }
42      }
43    } catch { /* offline — try again next tick */ }
44  };
45  root.querySelector('[data-liveblog-show]')?.addEventListener('click', () => { apply(); feed.scrollIntoView({ behavior: 'smooth', block: 'start' }); });
46  timer = setInterval(() => { if (!document.hidden) poll(); }, INTERVAL);
47  document.addEventListener('visibilitychange', () => { if (!document.hidden) poll(); });
48}

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.