PageSourceSearch

https://rqstradio.pages.dev/js/schedule.js?v=71.1-a22

js rqstradio.pages.dev collected 2026-10-03 05:54:39 UTC 5,994 bytes, 167 lines download raw bytes

1(function () {
2  const cfg = () => window.RQST_CONFIG;
3  const programRefreshTimer = { 1: null, 2: null };
4  let initialLoad = true;
5
6  function chileToGMT(hhmm) {
7    const [h, m] = hhmm.trim().split(':').map(Number);
8    const base = new Date();
9    base.setHours(h, m, 0, 0);
10    const clStr = base.toLocaleString('en-US', { timeZone: cfg().schedule.timezone });
11    const clDate = new Date(clStr);
12    return {
13      txt: clDate.toUTCString().slice(17, 22),
14      mins: clDate.getUTCHours() * 60 + clDate.getUTCMinutes()
15    };
16  }
17
18  function isCurrentBlock(start, end, nowMin) {
19    if (start.mins < end.mins) return nowMin >= start.mins && nowMin < end.mins;
20    if (start.mins > end.mins) return nowMin >= start.mins || nowMin < end.mins;
21    return true;
22  }
23
24  function normalizeCover(value) {
25    const raw = String(value || '').trim();
26    if (!raw) return cfg().fallbackCover;
27
28    // La planilla guarda siempre el nombre lógico, sin extensión.
29    // Se normaliza por seguridad y se fuerza el formato PNG local.
30    const fileName = raw.replace(/\.png$/i, '') + '.png';
31    const base = `${cfg().programCoverPath || 'assets/img/covers/'}${fileName}`;
32    const version = String(cfg().programCoverVersion || '').trim();
33    return version ? `${base}?v=${encodeURIComponent(version)}` : base;
34  }
35
36  function createCell(className, text) {
37    const cell = document.createElement('span');
38    cell.className = className;
39    cell.textContent = text;
40    cell.title = text;
41    return cell;
42  }
43
44  function render(items) {
45    const roller = document.getElementById('program-roller');
46    if (!roller) return;
47    roller.replaceChildren();
48
49    const list = document.createElement('div');
50    list.className = 'schedule-list';
51
52    items.forEach(it => {
53      const row = document.createElement('div');
54      row.className = `roll-item${it.current ? ' current' : ''}`;
55      if (it.current) row.setAttribute('aria-current', 'true');
56
57      const time = document.createElement('span');
58      time.className = 'schedule-time';
59      time.title = `${it.startTxt}–${it.endTxt} GMT`;
60      const range = document.createElement('strong');
61      range.textContent = `${it.startTxt}`;
62      time.appendChild(range);
63      if (it.current) {
64        const badge = document.createElement('small');
65        badge.textContent = 'NOW';
66        time.appendChild(badge);
67      }
68
69      row.append(
70        time,
71        createCell('schedule-program', it.ch1),
72        createCell('schedule-program', it.ch2)
73      );
74      list.appendChild(row);
75    });
76
77    roller.appendChild(list);
78    requestAnimationFrame(() => {
79      roller.querySelector('.roll-item.current')?.scrollIntoView({ block: 'center', behavior: 'smooth' });
80    });
81  }
82
83  function applyProgramCover(signalId, coverValue, forceDisplay = false) {
84    const covers = window.RQST_COVERS;
85    if (!covers) return;
86
87    const changed = covers.setProgramCover(signalId, normalizeCover(coverValue), 'RQST Program');
88    if (!changed && !forceDisplay) return false;
89
90    // Una carátula editorial nueva —al iniciar o al cambiar de bloque— se
91    // protege exactamente por el tiempo configurado. Al terminar, forzamos
92    // metadata para evitar esperar al ciclo general de 15 segundos.
93    const durationMs = Math.max(0, Number(cfg().temporaryCoverMs || 10000));
94    const program = covers.getProgramCover(signalId);
95    covers.showTemporaryCover(signalId, program.src, {
96      durationMs,
97      sourceText: 'RQST Program'
98    });
99
100    if (programRefreshTimer[signalId]) clearTimeout(programRefreshTimer[signalId]);
101    programRefreshTimer[signalId] = setTimeout(() => {
102      programRefreshTimer[signalId] = null;
103      window.RQST_METADATA?.update(signalId);
104    }, durationMs + 100);
105    return true;
106  }
107
108  function applyCurrent(items) {
109    const current = items.find(it => it.current);
110    if (!current) return;
111    window.RQST_UI?.setProgram(1, current.ch1 || cfg().signals[1].defaultProgram);
112    window.RQST_UI?.setProgram(2, current.ch2 || cfg().signals[2].defaultProgram);
113    const forceDisplay = initialLoad;
114    applyProgramCover(1, current.cover1, forceDisplay);
115    applyProgramCover(2, current.cover2, forceDisplay);
116    initialLoad = false;
117  }
118
119  function load() {
120    const url = `https://opensheet.elk.sh/${cfg().schedule.sheetId}/${cfg().schedule.sheetName}`;
121    return window.RQST_NET.fetchJson(url, { timeoutMs: cfg().network?.timeoutMs || 7000, cache: 'no-store' })
122      .then(data => {
123        const now = new Date();
124        const nowMin = now.getUTCHours() * 60 + now.getUTCMinutes();
125        const items = [];
126
127        for (const row of data) {
128          if (row.Begin?.trim() === '<EOT>') break;
129          if (!row.Begin || !row.End || !row['RQST 1'] || !row['RQST 2']) continue;
130          const start = chileToGMT(row.Begin);
131          const end = chileToGMT(row.End);
132          items.push({
133            startTxt: start.txt,
134            endTxt: end.txt,
135            ch1: row['RQST 1'].trim(),
136            ch2: row['RQST 2'].trim(),
137            cover1: String(row['COVER 1'] || '').trim(),
138            cover2: String(row['COVER 2'] || '').trim(),
139            current: isCurrentBlock(start, end, nowMin)
140          });
141        }
142        render(items);
143        applyCurrent(items);
144      })
145      .catch(err => {
146        console.warn('Scheduler load error:', err);
147        if (initialLoad) {
148          initialLoad = false;
149          [1, 2].forEach(signalId => {
150            const covers = window.RQST_COVERS;
151            const program = covers.getProgramCover(signalId);
152            const durationMs = Math.max(0, Number(cfg().temporaryCoverMs || 10000));
153            covers.showTemporaryCover(signalId, program.src, { durationMs, sourceText: 'RQST Program' });
154            setTimeout(() => window.RQST_METADATA?.update(signalId), durationMs + 100);
155          });
156        }
157      });
158  }
159
160  window.RQST_SCHEDULE = {
161    init() {
162      load();
163      setInterval(load, 60000);
164    },
165    load
166  };
167})();

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.