PageSourceSearch

https://www.comfenalcoantioquia.com.co/contenthandler/!ut/p/digest…/Comfenalco2025-v3/js/theme-comfenalco.js

js comfenalcoantioquia.com.co collected 2026-10-03 17:34:57 UTC 16,638 bytes, 534 lines download raw bytes

1// ============================================================
2// MENÚ MÓVIL + ACCORDIONS (INIT GENERAL)
3// ============================================================
4function initMobileMenu({
5  menuToggleId = "menu-toggle",
6  menuId = "menu-movil",
7  animationDuration = 200,
8  breakpoint = 1280,
9} = {}) {
10  const toggle = document.getElementById(menuToggleId);
11  const mobileMenu = document.getElementById(menuId);
12
13  if (!toggle || !mobileMenu) return;
14
15  let isAnimating = false;
16
17  const closeAllAccordions = () => {
18    document.querySelectorAll("[data-accordion-trigger]").forEach((btn) => {
19      btn.setAttribute("aria-expanded", "false");
20
21      const content = btn.nextElementSibling;
22      content?.setAttribute("hidden", "");
23
24      content
25        ?.querySelectorAll("[data-subaccordion-trigger]")
26        .forEach((subBtn) => {
27          subBtn.setAttribute("aria-expanded", "false");
28          subBtn.nextElementSibling?.setAttribute("hidden", "");
29        });
30    });
31  };
32
33  const closeMenu = (withAnimation = true) => {
34    if (mobileMenu.classList.contains("hidden")) return;
35
36    closeAllAccordions();
37
38    if (withAnimation) {
39      mobileMenu.classList.remove("is-open");
40
41      setTimeout(() => {
42        mobileMenu.classList.add("hidden");
43      }, animationDuration);
44    } else {
45      mobileMenu.classList.remove("is-open");
46      mobileMenu.classList.add("hidden");
47    }
48  };
49
50  mobileMenu.classList.add("hidden");
51  mobileMenu.classList.remove("is-open");
52
53  document
54    .querySelectorAll("[data-accordion-content], [data-subaccordion-content]")
55    .forEach((el) => el.setAttribute("hidden", ""));
56
57  toggle.addEventListener("click", () => {
58    if (isAnimating) return;
59
60    const isHidden = mobileMenu.classList.contains("hidden");
61    isAnimating = true;
62
63    if (isHidden) {
64      mobileMenu.classList.remove("hidden");
65
66      requestAnimationFrame(() => {
67        mobileMenu.classList.add("is-open");
68      });
69    } else {
70      closeMenu(true);
71    }
72
73    setTimeout(() => {
74      isAnimating = false;
75    }, animationDuration);
76  });
77
78  document.querySelectorAll("[data-accordion-trigger]").forEach((btn) => {
79    btn.addEventListener("click", () => {
80      const isExpanded = btn.getAttribute("aria-expanded") === "true";
81      const content = btn.nextElementSibling;
82
83      document.querySelectorAll("[data-accordion-trigger]").forEach((other) => {
84        if (other !== btn) {
85          other.setAttribute("aria-expanded", "false");
86
87          const otherContent = other.nextElementSibling;
88          otherContent?.setAttribute("hidden", "");
89
90          otherContent
91            ?.querySelectorAll("[data-subaccordion-trigger]")
92            .forEach((subBtn) => {
93              subBtn.setAttribute("aria-expanded", "false");
94              subBtn.nextElementSibling?.setAttribute("hidden", "");
95            });
96        }
97      });
98
99      btn.setAttribute("aria-expanded", String(!isExpanded));
100
101      if (isExpanded) {
102        content?.setAttribute("hidden", "");
103
104        content
105          ?.querySelectorAll("[data-subaccordion-trigger]")
106          .forEach((subBtn) => {
107            subBtn.setAttribute("aria-expanded", "false");
108            subBtn.nextElementSibling?.setAttribute("hidden", "");
109          });
110      } else {
111        content?.removeAttribute("hidden");
112      }
113    });
114  });
115
116  document.querySelectorAll("[data-subaccordion-trigger]").forEach((btn) => {
117    btn.addEventListener("click", () => {
118      const isExpanded = btn.getAttribute("aria-expanded") === "true";
119      const content = btn.nextElementSibling;
120      const parent = btn.closest("[data-accordion-content]");
121
122      parent
123        ?.querySelectorAll("[data-subaccordion-trigger]")
124        .forEach((other) => {
125          if (other !== btn) {
126            other.setAttribute("aria-expanded", "false");
127            other.nextElementSibling?.setAttribute("hidden", "");
128          }
129        });
130
131      btn.setAttribute("aria-expanded", String(!isExpanded));
132
133      if (isExpanded) {
134        content?.setAttribute("hidden", "");
135      } else {
136        content?.removeAttribute("hidden");
137      }
138    });
139  });
140
141  const mediaQuery = window.matchMedia(`(min-width: ${breakpoint}px)`);
142
143  mediaQuery.addEventListener("change", (e) => {
144    if (e.matches) {
145      closeMenu(false);
146    }
147  });
148}
149
150// ============================================================
151//  BUSCADOR (ESCRITORIO)
152// ============================================================
153function initSearchDesktop() {
154  const btnSearch = document.getElementById("boton-buscar");
155  const btnClear = document.getElementById("boton-limpiar");
156  const inputSearch = document.getElementById("input-busqueda");
157  const navContainer = document.getElementById("nav-container");
158  const formSearch = document.getElementById("form-busqueda");
159
160  if (!btnSearch || !inputSearch || !navContainer || !formSearch) return;
161
162  let isOpen = false;
163
164  btnSearch.addEventListener("click", (e) => {
165    e.preventDefault();
166
167    const query = inputSearch.value.trim();
168
169    if (!isOpen) {
170      openSearch();
171    } else if (query) {
172      handleSearch(query);
173    } else {
174      closeSearch();
175    }
176  });
177
178  btnClear.addEventListener("click", () => {
179    inputSearch.value = "";
180    btnClear.classList.add("hidden");
181    inputSearch.focus();
182  });
183
184  inputSearch.addEventListener("input", () => {
185    const hasText = inputSearch.value.trim() !== "";
186    btnClear.classList.toggle("hidden", !hasText);
187  });
188
189  inputSearch.addEventListener("keydown", (e) => {
190    if (e.key === "Escape") closeSearch();
191  });
192
193  function openSearch() {
194    isOpen = true;
195    navContainer?.classList.add("hidden");
196    inputSearch.classList.remove("hidden");
197
198    // Forzar reflow
199    void inputSearch.offsetWidth;
200
201    formSearch.classList.add("w-full");
202    inputSearch.classList.remove("scale-x-0", "opacity-0");
203    inputSearch.classList.add("scale-x-100", "opacity-100");
204
205    setTimeout(() => inputSearch.focus({ preventScroll: true }), 200);
206  }
207
208  function closeSearch() {
209    isOpen = false;
210    inputSearch.classList.remove("scale-x-100", "opacity-100");
211    inputSearch.classList.add("scale-x-0", "opacity-0");
212
213    setTimeout(() => {
214      navContainer?.classList.remove("hidden");
215      inputSearch.classList.add("hidden");
216      formSearch.classList.remove("w-full");
217      inputSearch.value = "";
218      btnClear.classList.add("hidden");
219    }, 250);
220  }
221
222  function handleSearch(query) {
223    console.log("Buscando:", query);
224    // Ejemplo:
225    // window.location.href = `/buscar?q=${encodeURIComponent(query)}`;
226  }
227}
228
229// ============================================================
230//  BUSCADOR (MÓVIL)
231// ============================================================
232function initSearchMobile() {
233  const btnSearch = document.getElementById("boton-buscar-movil");
234  const container = document.getElementById("contenedor-busqueda-movil");
235  const btnClear = document.getElementById("boton-limpiar-movil");
236  const input = document.getElementById("input-busqueda-movil");
237  const menuToggle = document.getElementById("menu-toggle");
238  const logoComfenalco = document.getElementById("logo-comfenalco");
239  const mobileMenu = document.getElementById("menu-movil");
240
241  if (!btnSearch || !container || !input) return;
242
243  let isExpanded = false;
244  const MOBILE_BREAKPOINT = 1536;
245
246  input.style.transformOrigin = "right center";
247
248  // Botón principal (toggle abrir/cerrar)
249  btnSearch.addEventListener("click", (e) => {
250    e.preventDefault();
251    if (!isExpanded) expand();
252    else collapse();
253  });
254
255  // Limpia el campo al hacer clic en la X
256  if (btnClear) {
257    btnClear.addEventListener("click", (e) => {
258      e.preventDefault();
259      input.value = "";
260      btnClear.classList.add("hidden");
261      input.focus();
262    });
263  }
264
265  // Mostrar/ocultar botón limpiar según haya texto
266  input.addEventListener("input", () => {
267    const hasText = input.value.trim() !== "";
268    if (btnClear) btnClear.classList.toggle("hidden", !hasText);
269  });
270
271  // Permitir cerrar con Escape
272  input.addEventListener("keydown", (e) => {
273    if (e.key === "Escape") collapse();
274  });
275
276  function expand() {
277    if (isExpanded) return;
278    isExpanded = true;
279
280    // Ocultar elementos del header
281    logoComfenalco?.classList.add("hidden");
282    menuToggle?.classList.add("hidden");
283    mobileMenu?.classList.add("hidden");
284
285    // Expandir contenedor y mostrar input
286    container.classList.replace("w-32", "w-full");
287    container.style.overflow = "visible";
288
289    // Forzar reflow antes de la animación
290    void input.offsetWidth;
291
292    input.classList.remove("scale-x-0", "opacity-0");
293    input.classList.add("scale-x-100", "opacity-100");
294
295    setTimeout(() => input.focus({ preventScroll: true }), 200);
296  }
297
298  function collapse() {
299    if (!isExpanded) return;
300    isExpanded = false;
301
302    // Animar cierre del input
303    input.classList.remove("scale-x-100", "opacity-100");
304    input.classList.add("scale-x-0", "opacity-0");
305
306    setTimeout(() => {
307      container.classList.replace("w-full", "w-32");
308      container.style.overflow = "hidden";
309      input.value = "";
310      btnClear?.classList.add("hidden");
311
312      // Mostrar nuevamente logo y menú
313      logoComfenalco?.classList.remove("hidden");
314      menuToggle?.classList.remove("hidden");
315    }, 250);
316  }
317
318  // Cierra el buscador si cambia el ancho de pantalla a escritorio
319  window.addEventListener("resize", () => {
320    if (window.innerWidth >= MOBILE_BREAKPOINT && isExpanded) collapse();
321  });
322}
323
324// ============================================================
325//  ACCESIBILIDAD: TAMAÑO DE TEXTO
326// ============================================================
327function initTextAccessibility() {
328  const root = document.documentElement;
329  let fontSize = 100;
330
331  const getMaxFontSize = () => {
332    const width = window.innerWidth;
333    if (width >= 2000) return 130;
334    if (width >= 1800) return 120;
335    if (width >= 1600) return 115;
336    if (width >= 1440) return 110;
337    return 105;
338  };
339
340  const applyFontSize = () => {
341    const max = getMaxFontSize();
342    if (fontSize > max) fontSize = max;
343    root.style.fontSize = `${fontSize}%`;
344  };
345
346  const increase = () => {
347    const max = getMaxFontSize();
348    if (fontSize < max) {
349      fontSize += 5;
350      applyFontSize();
351    }
352  };
353
354  const decrease = () => {
355    if (fontSize > 90) {
356      fontSize -= 5;
357      applyFontSize();
358    }
359  };
360
361  ["aumentar-texto", "aumentar-texto-mobile"].forEach((id) =>
362    document.getElementById(id)?.addEventListener("click", increase),
363  );
364
365  ["disminuir-texto", "disminuir-texto-mobile"].forEach((id) =>
366    document.getElementById(id)?.addEventListener("click", decrease),
367  );
368
369  window.addEventListener("resize", applyFontSize);
370}
371
372// ============================================================
373//  ACCESIBILIDAD: MODO NORMAL / ESCALA DE GRISES
374// ============================================================
375function initThemeAccessibility() {
376  const html = document.documentElement;
377
378  const toggleGray = (enable) =>
379    enable
380      ? html.classList.add("grayscale")
381      : html.classList.remove("grayscale");
382
383  ["tema-normal", "tema-normal-mobile"].forEach((id) =>
384    document
385      .getElementById(id)
386      ?.addEventListener("click", () => toggleGray(false)),
387  );
388
389  ["tema-grises", "tema-grises-mobile"].forEach((id) =>
390    document
391      .getElementById(id)
392      ?.addEventListener("click", () => toggleGray(true)),
393  );
394}
395
396// ============================================================
397//  MEGA MENÚS (PERSONAS / EMPRESAS)
398// ============================================================
399function initMegaMenus() {
400  const btnPersonas = document.getElementById("btn-personas");
401  const btnEmpresas = document.getElementById("btn-empresas");
402  const menuPersonas = document.getElementById("menu-personas");
403  const menuEmpresas = document.getElementById("menu-empresas");
404
405  if (!btnPersonas || !btnEmpresas || !menuPersonas || !menuEmpresas) return;
406
407  const allButtons = [btnPersonas, btnEmpresas];
408  const allMenus = [menuPersonas, menuEmpresas];
409  let hideTimer = null;
410  const HIDE_DELAY = 150;
411
412  const showMenu = (menu) => {
413    clearTimeout(hideTimer);
414    allMenus.forEach((m) => (m !== menu ? m.classList.add("hidden") : null));
415    menu.classList.remove("hidden");
416  };
417
418  const scheduleHide = (menu) => {
419    clearTimeout(hideTimer);
420    hideTimer = setTimeout(() => menu.classList.add("hidden"), HIDE_DELAY);
421  };
422
423  const hideAll = () => {
424    clearTimeout(hideTimer);
425    allMenus.forEach((m) => m.classList.add("hidden"));
426  };
427
428  [
429    { btn: btnPersonas, menu: menuPersonas },
430    { btn: btnEmpresas, menu: menuEmpresas },
431  ].forEach(({ btn, menu }) => {
432    btn.addEventListener("mouseenter", () => showMenu(menu));
433    btn.addEventListener("mouseleave", () => scheduleHide(menu));
434
435    menu.addEventListener("mouseenter", () => {
436      clearTimeout(hideTimer);
437      showMenu(menu);
438    });
439
440    menu.addEventListener("mouseleave", () => scheduleHide(menu));
441  });
442
443  document.addEventListener("click", (e) => {
444    const insideBtn = allButtons.some((b) => b.contains(e.target));
445    const insideMenu = allMenus.some((m) => m.contains(e.target));
446    if (!insideBtn && !insideMenu) hideAll();
447  });
448
449  document.addEventListener("keydown", (e) => e.key === "Escape" && hideAll());
450  window.addEventListener("resize", hideAll);
451}
452
453// ============================================================
454//  ENLACE "SERVICIOS EN LÍNEA" (PERSONAS / EMPRESAS)
455// ============================================================
456function handleServiceLink() {
457  const path = window.location.pathname.toLowerCase();
458
459  const personasURL =
460    "https://serviciosenlineapersonas.comfenalcoantioquia.com.co/";
461  const empresasURL =
462    "https://serviciosenlineaempresas.comfenalcoantioquia.com.co/";
463
464  const linkDesktop = document.getElementById("servicios-link-desktop");
465  const linkMobile = document.getElementById("servicios-link-mobile");
466  if (!linkDesktop) return;
467  if (!linkMobile) return;
468
469  const destino = path.includes("empresas") ? empresasURL : personasURL;
470
471  linkDesktop.setAttribute("href", destino);
472  linkMobile.setAttribute("href", destino);
473}
474
475// ============================================================
476//  ESTADO ACTIVO (aria-current)
477// ============================================================
478function handleAriaCurrent() {
479  const path = window.location.pathname.toLowerCase();
480  const links = [
481    { id: "btn-personas", keyword: "personas" },
482    { id: "btn-empresas", keyword: "empresas" },
483  ];
484
485  links.forEach((link) => {
486    const el = document.getElementById(link.id);
487    if (el) el.removeAttribute("aria-current");
488  });
489
490  const active = links.find((l) => path.includes(l.keyword));
491  const target = active
492    ? document.getElementById(active.id)
493    : document.getElementById("btn-personas");
494
495  if (target) target.setAttribute("aria-current", "page");
496}
497
498// ============================================================
499//  FUNCIÓN PARA MEJORAR ENLACES EN TEXTOS ENRIQUECIDOS
500// ============================================================
501function enhanceRichArticleLinks() {
502  const containers = document.querySelectorAll(".rich-article");
503
504  containers.forEach((container) => {
505    const paragraphs = container.querySelectorAll("p");
506
507    paragraphs.forEach((p) => {
508      const link = p.querySelector(":scope > a");
509      if (!link) return;
510
511      const paragraphText = p.textContent.replace(/\s+/g, " ").trim();
512      const linkText = link.textContent.replace(/\s+/g, " ").trim();
513
514      if (paragraphText === linkText) {
515        p.classList.add("is-button");
516      }
517    });
518  });
519}
520
521// ============================================================
522//  INICIALIZACIÓN GLOBAL
523// ============================================================
524document.addEventListener("DOMContentLoaded", () => {
525  initMobileMenu();
526  initSearchDesktop();
527  initSearchMobile();
528  initTextAccessibility();
529  initThemeAccessibility();
530  initMegaMenus();
531  handleServiceLink();
532  handleAriaCurrent();
533  enhanceRichArticleLinks();
534});

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.