PageSourceSearch

https://trioeclipses.es/themes/custom/b5subtheme/js/app.js?tlensb

js trioeclipses.es collected 2026-10-04 00:26:08 UTC 8,991 bytes, 363 lines download raw bytes

1/* =========================
2   FECHAS
3Para cambiar el contador de portada, cambiar la constante hero (linea 7)   
4   
5========================= */
6const dates = {
7  hero: new Date("August 2, 2027 10:50:00").getTime(),
8  y26: new Date("August 12, 2026 19:30:00").getTime(),
9  y27: new Date("August 2, 2027 10:50:00").getTime(),
10  y28: new Date("January 26, 2028 16:50:00").getTime()
11};
12
13/* =========================
14   I18N CENTRALIZADO
15========================= */
16const i18n = {
17  es: {
18    explore: "Explorar",
19    explore_details: "Explorar detalles"
20  },
21  en: {
22    explore: "Explore",
23    explore_details: "Explore details"
24  },
25  ca: {
26    explore: "Explorar",
27    explore_details: "Explorar detalls"
28  },
29  gl: {
30    explore: "Explorar",
31    explore_details: "Explorar detalles"
32  },
33  eu: {
34    explore: "Arakatu",
35    explore_details: "Xehetasunak arakatu"
36  }
37};
38
39function getLang() {
40  const select = document.getElementById("edit-lang-dropdown-select");
41  if (select) return select.options[select.selectedIndex].value;
42
43  // Fallback: el atributo lang del <html> lo establece Drupal en servidor
44  // con el idioma activo de la página (ej. "ca", "en", "eu", "gl", "es")
45  return (document.documentElement.lang || "es").split("-")[0];
46}
47
48function t(key) {
49  const lang = getLang();
50  return i18n[lang]?.[key] || i18n["en"]?.[key] || key;
51}
52
53/* =========================
54   TRADUCCIÓN AUTOMÁTICA HTML
55========================= */
56function translatePage() {
57  document.querySelectorAll("[data-i18n]").forEach(el => {
58    const key = el.getAttribute("data-i18n");
59    el.innerText = t(key);
60  });
61}
62
63/* =========================
64   UTILIDADES TIEMPO
65========================= */
66function calcTime(target, now) {
67  const diff = target - now;
68
69  if (diff <= 0) {
70    return { d: "00", h: "00", m: "00", s: "00" };
71  }
72
73  return {
74    d: String(Math.floor(diff / (1000 * 60 * 60 * 24))).padStart(2, "0"),
75    h: String(Math.floor((diff / (1000 * 60 * 60)) % 24)).padStart(2, "0"),
76    m: String(Math.floor((diff / (1000 * 60)) % 60)).padStart(2, "0"),
77    s: String(Math.floor((diff / 1000) % 60)).padStart(2, "0")
78  };
79}
80
81/* =========================
82   UPDATE CON ANIMACIÓN
83========================= */
84function updateElAnimated(id, value) {
85  const el = document.getElementById(id);
86  if (!el || el.textContent === value) return;
87
88  el.classList.add("num-change");
89  setTimeout(() => {
90    el.textContent = value;
91    el.classList.remove("num-change");
92  }, 150);
93}
94
95/* =========================
96   FLIP CLOCK
97========================= */
98function updateFlip(el, value) {
99  if (!el) return;
100
101  const top = el.querySelector(".flip-top");
102  const bottom = el.querySelector(".flip-bottom");
103  if (!top || !bottom || top.textContent === value) return;
104
105  bottom.textContent = value;
106  el.classList.remove("flip-animate");
107  void el.offsetWidth;
108  el.classList.add("flip-animate");
109
110  setTimeout(() => {
111    top.textContent = value;
112    el.classList.remove("flip-animate");
113  }, 400);
114}
115
116/* =========================
117   TIMERS
118========================= */
119function updateTimers(now) {
120  const tHero = calcTime(dates.hero, now);
121
122  updateElAnimated("days-h", tHero.d);
123  updateElAnimated("hours-h", tHero.h);
124  updateElAnimated("minutes-h", tHero.m);
125
126  const flipSeconds = document.getElementById("flip-seconds-h");
127  if (flipSeconds) {
128    updateFlip(flipSeconds, tHero.s);
129  } else {
130    updateElAnimated("seconds-h", tHero.s);
131  }
132
133  const t26 = calcTime(dates.y26, now);
134  updateElAnimated("d-26", t26.d);
135  updateElAnimated("h-26", t26.h);
136  updateElAnimated("m-26", t26.m);
137
138  const t27 = calcTime(dates.y27, now);
139  updateElAnimated("d-27", t27.d);
140  updateElAnimated("h-27", t27.h);
141  updateElAnimated("m-27", t27.m);
142
143  const t28 = calcTime(dates.y28, now);
144  updateElAnimated("d-28", t28.d);
145  updateElAnimated("h-28", t28.h);
146  updateElAnimated("m-28", t28.m);
147}
148
149/* =========================
150   LOOP
151========================= */
152let lastSecond = null;
153
154function rafLoop() {
155  const now = new Date();
156  const sec = now.getSeconds();
157
158  if (sec !== lastSecond) {
159    updateTimers(now.getTime());
160    lastSecond = sec;
161  }
162
163  requestAnimationFrame(rafLoop);
164}
165
166requestAnimationFrame(rafLoop);
167
168/* =========================
169   MAPAS / CAPAS
170========================= */
171let activeYearMap = null;
172
173function toggleLayer(y) {
174  const l = document.getElementById('layer-' + y);
175  const b = document.getElementById('btn-' + y);
176  const detailsBtn = document.getElementById('mapDetailsBtn');
177
178  ['26', '27', '28'].forEach(code => {
179
180    const layer = document.getElementById('layer-' + code);
181    const btn = document.getElementById('btn-' + code);
182
183    if (layer) {
184      layer.classList.remove('visible');
185      layer.hidden = true;
186    }
187
188    if (btn) {
189      btn.classList.remove('active-' + code);
190      btn.setAttribute('aria-selected', 'false');
191    }
192  });
193
194  if (l) {
195    l.classList.add('visible');
196    l.hidden = false;
197  }
198
199  if (b) {
200    b.classList.add('active-' + y);
201    b.setAttribute('aria-selected', 'true');
202  }
203
204  activeYearMap = '20' + y;
205
206  if (detailsBtn) {
207    const lang = getLang();
208    detailsBtn.classList.add('active');
209    detailsBtn.innerText =
210      t("explore").toLocaleUpperCase(lang) + " 20" + y;
211  }
212}
213
214
215
216function activateMap(y) {
217  toggleLayer(y.slice(-2));
218}
219
220function openCurrentIgn() {
221  if (!activeYearMap) return;
222  window.open(`https://visualizadores.ign.es/eclipses/${activeYearMap}`, '_blank');
223}
224
225/* =========================
226   EVENTOS INICIALES
227========================= */
228document.addEventListener("DOMContentLoaded", () => {
229
230  translatePage();
231
232  const langSelect = document.getElementById("edit-lang-dropdown-select");
233
234  if (langSelect) {
235    langSelect.addEventListener("change", () => {
236      translatePage();
237
238      if (activeYearMap) {
239        const y = activeYearMap.slice(-2);
240        toggleLayer(y);
241      }
242    });
243  }
244
245  toggleLayer('26');
246
247  const toggler = document.querySelector('.navbar-toggler');
248  if (toggler) {
249    toggler.addEventListener('click', () => {
250      toggler.setAttribute(
251        'aria-label',
252        toggler.getAttribute('aria-expanded') === 'true'
253          ? 'Cerrar menú'
254          : 'Abrir menú'
255      );
256    });
257  }
258});
259
260
261document.addEventListener("change", (e) => {
262  if (e.target && e.target.id === "edit-lang-dropdown-select") {
263
264    translatePage();
265
266    if (activeYearMap) {
267      const y = activeYearMap.slice(-2);
268      toggleLayer(y);
269    }
270  }
271});
272
273/* =========================
274   MODAL IGN
275========================= */
276function openIgnMap(year) {
277  const modal = document.getElementById('ignModal');
278  const name = document.getElementById('ignEventName');
279  const dur = document.getElementById('ignDuration');
280  const link = document.getElementById('ignLink');
281
282  const data = {
283    "2026": ["ECLIPSE TOTAL 2026", "1m 50s"],
284    "2027": ["ECLIPSE TOTAL 2027", "4m 40s"],
285    "2028": ["ECLIPSE ANULAR 2028", "7m 10s"]
286  };
287
288  name.innerText = data[year][0];
289  dur.innerText = data[year][1];
290  link.href = `https://visualizadores.ign.es/eclipses/${year}`;
291  modal.classList.add('active');
292}
293
294function closeIgnMap() {
295  document.getElementById('ignModal').classList.remove('active');
296}
297
298/* =========================
299   SIMULADOR
300========================= */
301const phasesTotal = [
302  { title: "C1 Parcial", desc: "Inicio" },
303  { title: "C2 Totalidad", desc: "Corona Visible" },
304  { title: "C3 Fin Total", desc: "Anillo Diamantes" }
305];
306
307const phasesAnnular = [
308  { title: "C1 Parcial", desc: "Inicio" },
309  { title: "Max Anular", desc: "Anillo de Fuego" },
310  { title: "C3 Salida", desc: "Fin Anillo" }
311];
312
313let curSimType = '', curStep = 0;
314
315function openSim(t) {
316  curSimType = t;
317  curStep = 0;
318  document.getElementById('simStage').className =
319    'sim-stage ' + (t === 'annular' ? 'sim-type-annular' : '');
320  document.getElementById('simOverlay').classList.add('active');
321  updSim();
322}
323
324function closeSim(e) {
325  if (e) e.stopPropagation();
326  document.getElementById('simOverlay').classList.remove('active');
327}
328
329function nextPhase() {
330  curStep = (curStep + 1) % 3;
331  updSim();
332}
333
334function updSim() {
335  const d = (curSimType === 'total' ? phasesTotal : phasesAnnular)[curStep];
336  document.getElementById('simTitle').innerText = d.title;
337  document.getElementById('simDesc').innerText = d.desc;
338  const s = document.getElementById('simStage');
339  s.className = 'sim-stage sim-state-' + (curStep + 1);
340}
341
342/* =========================
343   ANIMACIÓN CCAA
344========================= */
345(function () {
346  const items = document.querySelectorAll('.ccaa-item');
347
348  if (!('IntersectionObserver' in window)) {
349    items.forEach(el => el.classList.add('is-visible'));
350    return;
351  }
352
353  const observer = new IntersectionObserver((entries, obs) => {
354    entries.forEach(entry => {
355      if (entry.isIntersecting) {
356        entry.target.classList.add('is-visible');
357        obs.unobserve(entry.target);
358      }
359    });
360  }, { threshold: 0.15 });
361
362  items.forEach(el => observer.observe(el));
363})();

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.