PageSourceSearch

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

js trioeclipses.es collected 2026-10-04 00:26:03 UTC 4,905 bytes, 193 lines download raw bytes

1document.addEventListener("DOMContentLoaded", () => {
2
3  /* =========================
4     ELEMENTOS BASE
5  ========================= */
6
7  const header = document.getElementById("site-header");
8  const main = document.querySelector("main");
9  const body = document.body;
10
11  const collapseEl = document.querySelector("#mainNavbar");
12  const toggler = document.querySelector(".navbar-toggler");
13  const navLinks = document.querySelectorAll(".nav-link");
14  const hashLinks = document.querySelectorAll(".navbar-nav a[href^='/#']");
15
16  if (!header || !main || !collapseEl || !toggler) return;
17
18  /* =========================
19     TOOLBAR + HEADER OFFSET
20  ========================= */
21
22  function getToolbarHeight() {
23    const toolbar = document.querySelector("#toolbar-bar");
24    const tray = document.querySelector(".toolbar-tray.is-active");
25
26    return (toolbar?.offsetHeight || 0) + (tray?.offsetHeight || 0);
27  }
28
29  function getHeaderOffset() {
30    return getToolbarHeight() + header.offsetHeight;
31  }
32
33  function adjustLayout() {
34    const offset = getHeaderOffset();
35
36    header.style.top = getToolbarHeight() + "px";
37    main.style.paddingTop = offset + "px";
38  }
39
40  adjustLayout();
41  window.addEventListener("resize", adjustLayout);
42
43  /* =========================
44     SCROLL A HASH CON OFFSET
45  ========================= */
46
47  function normalizeHash(hash) {
48    const clean = hash.replace("#", "");
49
50    if (/^\d/.test(clean)) {
51      return {
52        legacy: clean,
53        normalized: "y" + clean
54      };
55    }
56
57    return {
58      legacy: clean,
59      normalized: clean
60    };
61  }
62
63  function scrollToHash(hash, smooth = true) {
64    if (!hash) return;
65
66    const { legacy, normalized } = normalizeHash(hash);
67
68    const target =
69      document.getElementById(normalized) ||
70      document.getElementById(legacy);
71
72    if (!target) return;
73
74    requestAnimationFrame(() => {
75      const offset = getHeaderOffset();
76      const y = target.getBoundingClientRect().top + window.scrollY - offset;
77
78      window.scrollTo({
79        top: y,
80        behavior: smooth ? "smooth" : "auto"
81      });
82
83      if (!target.hasAttribute("tabindex")) {
84        target.setAttribute("tabindex", "-1");
85      }
86
87      target.focus({ preventScroll: true });
88    });
89  }
90
91  /* =========================
92     ESTADO ACTIVO POR HASH
93  ========================= */
94
95  function updateActiveByHash() {
96    const currentHash = window.location.hash;
97
98    hashLinks.forEach(link => {
99      const linkHash = link.getAttribute("href").replace("/", "");
100
101      const isActive = linkHash === currentHash;
102
103      link.classList.toggle("is-active", isActive);
104      link.classList.toggle("active", isActive);
105
106      if (isActive) {
107        link.setAttribute("aria-current", "page");
108      } else {
109        link.removeAttribute("aria-current");
110      }
111    });
112  }
113
114  /* =========================
115     LINKS HASH (FRONTPAGE)
116  ========================= */
117
118  if (body.classList.contains("path-frontpage")) {
119
120    hashLinks.forEach(link => {
121      link.addEventListener("click", (e) => {
122        const hash = link.getAttribute("href").replace("/", "");
123
124        e.preventDefault();
125
126        history.pushState(null, "", hash);
127
128        // Actualizar estado activo inmediatamente
129        updateActiveByHash();
130
131        // Esperar a que Bootstrap cierre menú
132        setTimeout(() => {
133          scrollToHash(hash);
134        }, 50);
135      });
136    });
137
138    // Carga inicial
139    if (window.location.hash) {
140      scrollToHash(window.location.hash, false);
141    }
142
143    updateActiveByHash();
144
145    // Navegación atrás / adelante
146    window.addEventListener("popstate", () => {
147      updateActiveByHash();
148      scrollToHash(window.location.hash, false);
149    });
150  }
151
152  /* =========================
153     BLOQUEO SCROLL (MÓVIL)
154  ========================= */
155
156  function lockScroll() {
157    body.classList.add("menu-open");
158  }
159
160  function unlockScroll() {
161    body.classList.remove("menu-open");
162  }
163
164  /* =========================
165     EVENTOS BOOTSTRAP
166  ========================= */
167
168  collapseEl.addEventListener("shown.bs.collapse", () => {
169    toggler.setAttribute("aria-expanded", "true");
170    lockScroll();
171  });
172
173  collapseEl.addEventListener("hidden.bs.collapse", () => {
174    toggler.setAttribute("aria-expanded", "false");
175    unlockScroll();
176  });
177
178  /* =========================
179     CIERRE MENÚ
180  ========================= */
181
182  navLinks.forEach(link => {
183    link.addEventListener("click", () => {
184
185      if (link.classList.contains("dropdown-toggle")) return;
186      if (link.closest(".dropdown-menu")) return;
187
188      const bsCollapse = bootstrap.Collapse.getOrCreateInstance(collapseEl);
189      bsCollapse.hide();
190    });
191  });
192
193});

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.