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.