1 2 3 4$(window).on('load resize', function() { 5 6 //var esMovil = window.matchMedia('(max-width: 767.98px)').matches; 7 //var altoFooter = $('.footer-info').outerHeight() - 100 ; 8 //$('.footer-top').css('margin-top', altoFooter + 'px'); 9 $(':root').css('--footer-h', $('.footer-info').outerHeight() + 'px'); 10 11 12 $("#buscar_tracking").on('click', function(){ 13 var guia = $("#buscador_tracking").val(); 14 15 console.log( 'buscar:' + guia ); 16 window.location.href = "rastreo.html?guia=" + encodeURIComponent(guia); 17 }); 18 19 $("#buscar_tracking_m").on('click', function(){ 20 var guia = $("#buscador_tracking_m").val(); 21 22 console.log( 'buscar:' + guia ); 23 window.location.href = "rastreo.html?guia=" + encodeURIComponent(guia); 24 }); 25 26 /* Correccion para footer solo en pagina rastreo.html */ 27 const pagina = window.location.pathname.split("/").pop(); 28 const ancho = $(window).width(); 29 30 const paginas = ["rastreo.html", "cotizar.html", "puntosdeatencion.html"]; 31 32 if ( paginas.includes(pagina) && ancho <= 768 ) { 33 setTimeout(function() { 34 $(".footer-derecha").css({ 35 "padding-bottom": "20px" 36 }); 37 }, 2000); 38 } 39}); 40 41 42function carga_dinamica(archivo, contenedorId, callback) { 43 $("#" + contenedorId).load(archivo, function(response, status, xhr) { 44 if (status === "error") { 45 console.error("Error al cargar el archivo:", xhr.status, xhr.statusText); 46 } else { 47 if (typeof callback === "function") { 48 callback(response, status, xhr); 49 } 50 } 51 }); 52} 53 54 55function toggleMenu() { 56 const $sidebar = $('#sidebarMenu'); 57 const $overlay = $('#sidebarOverlay'); 58 const $menuButton = $('#menuButton'); 59 const $closeButton = $('.close-menu-button'); 60 61 const isMenuOpen = !$sidebar.hasClass('is-active'); // estado antes de toggle 62 63 $sidebar.toggleClass('is-active'); 64 $overlay.toggleClass('is-active'); 65 66 if (isMenuOpen) { 67 $menuButton.addClass('is-hidden'); 68 $closeButton.css('display', 'flex'); 69 } else { 70 $menuButton.removeClass('is-hidden'); 71 $closeButton.css('display', 'none'); 72 } 73} 74 75 76function inicializa_formulario_rastreo(){ 77 78 const $stickyMenu = $('#stickyTrackingMenu'); 79 80 if ($stickyMenu.length) { 81 // Guardar la posición inicial del menú 82 const initialMenuTopPosition = $stickyMenu.offset().top; 83 84 function handleScroll() { 85 if ($(window).scrollTop() > initialMenuTopPosition) { 86 $stickyMenu.addClass('sticky'); // Añade la clase 87 } else { 88 $stickyMenu.removeClass('sticky'); // Quita la clase 89 } 90 } 91 92 // Ejecutar al cargar por si ya hay scroll 93 handleScroll(); 94 95 // Escuchar scroll 96 $(window).on('scroll', handleScroll); 97 98 // Funcionalidad de cambio de pestañas 99 const $tabButtons = $('.tab-button'); 100 $tabButtons.on('click', function() { 101 $tabButtons.removeClass('active'); 102 $(this).addClass('active'); 103 }); 104 105 } else { 106 console.warn("Elemento con ID 'stickyTrackingMenu' no encontrado. Revisa tu HTML."); 107 } 108 109 110} 111 112// Funcionalidad para el carrusel de testimonios "Inicio" 113$(function () { 114 const $carousel = $('.testimonial-carousel'); 115 const $track = $carousel.find('.carousel-track'); 116 const $slides = $track.find('.carousel-item'); 117 const $prevBtn = $carousel.find('.carousel-nav-button1.prev'); 118 const $nextBtn = $carousel.find('.carousel-nav-button1.next'); 119 const $pagination = $carousel.find('.carousel-pagination'); 120 121 let currentIndex = 0; 122 const totalSlides = $slides.length; 123 124 // Crear paginación dinámica 125 for (let i = 0; i < totalSlides; i++) { 126 const $dot = $('<span>').attr('data-index', i); 127 if (i === 0) $dot.addClass('active'); 128 $pagination.append($dot); 129 } 130 const $dots = $pagination.find('span'); 131 132 // --- Función: actualizar carrusel con efecto translateX --- 133 function updateCarousel() { 134 const offset = -currentIndex * 100; // cada slide = 100% 135 $track.css('transform', `translateX(${offset}%)`); 136 137 $dots.removeClass('active').eq(currentIndex).addClass('active'); 138 } 139 140 // --- Navegación --- 141 $nextBtn.on('click', function () { 142 currentIndex = (currentIndex + 1) % totalSlides; 143 updateCarousel(); 144 }); 145 146 $prevBtn.on('click', function () { 147 currentIndex = (currentIndex - 1 + totalSlides) % totalSlides; 148 updateCarousel(); 149 }); 150 151 $dots.on('click', function () { 152 currentIndex = $(this).data('index'); 153 updateCarousel(); 154 }); 155
156 // --- Soporte táctil en móvil (swipe) --- 157 let startX = 0; 158 let endX = 0; 159 160 $track.on('touchstart', function (e) { 161 startX = e.originalEvent.touches[0].clientX; 162 }); 163 164 $track.on('touchend', function (e) { 165 endX = e.originalEvent.changedTouches[0].clientX; 166 let diff = startX - endX; 167 168 if (diff > 50) { 169 // swipe izquierda â siguiente 170 currentIndex = (currentIndex + 1) % totalSlides; 171 updateCarousel(); 172 } else if (diff < -50) { 173 // swipe derecha â anterior 174 currentIndex = (currentIndex - 1 + totalSlides) % totalSlides; 175 updateCarousel(); 176 } 177 }); 178 179 // Inicial 180 updateCarousel(); 181}); 182 183 184// Funcionalidad para el carrusel de videos "Inicio" 185$(function () { 186 const $track = $("#carouselTrack"); 187 let $items = $(".video-carousel-item"); 188 let index = 1; 189 190 // Duplicar extremos para scroll infinito visual 191 function duplicateItems() { 192 const $first = $items.eq(0).clone(); 193 const $last = $items.eq($items.length - 1).clone(); 194 195 $track.prepend($last); 196 $track.append($first); 197 198 $items = $(".video-carousel-item"); 199 } 200 201 function updateCarousel(animate = true) { 202 $items.each(function () { 203 const $item = $(this); 204 $item.removeClass("active"); 205 206 const video = $item.find("video").get(0); 207 if (video) { 208 video.pause(); 209 video.currentTime = 0; 210 } 211 }); 212 213 const $activeItem = $items.eq(index); 214 $activeItem.addClass("active"); 215 216 const video = $activeItem.find("video").get(0); 217 if (video) video.play(); 218 219 const offset = index * 890; // ancho video + margen 220 const centerOffset = (1850 - 890) / 2; 221 222 if (!animate) { 223 $track.css("transition", "none"); 224 } else { 225 $track.css("transition", "transform 0.5s ease"); 226 } 227 228 $track.css("transform", `translateX(${centerOffset - offset}px)`); 229 } 230 231 function nextVideo() { 232 index++; 233 updateCarousel(); 234 235 if (index === $items.length - 1) { 236 setTimeout(() => { 237 $track.css("transition", "none"); 238 index = 1; 239 updateCarousel(false); 240 }, 500); 241 } 242 } 243 244 function prevVideo() { 245 index--; 246 updateCarousel(); 247 248 if (index === 0) { 249 setTimeout(() => { 250 $track.css("transition", "none"); 251 index = $items.length - 2; 252 updateCarousel(false); 253 }, 500); 254 } 255 } 256 257 // Inicializar al cargar 258 duplicateItems(); 259 updateCarousel(false); 260 261 // Exportar funciones si quieres usarlas en botones 262 window.nextVideo = nextVideo; 263 window.prevVideo = prevVideo; 264}); 265 266 267 268// Funcionalidad para la lÃnea de tiempo interactiva "Conocenos" 269 270 271$(function () { 272 const $timelineWrapper = $('.timeline-wrapper'); 273 const $timelineFillLine = $('.timeline-fill-line'); 274 const $timelineEventsContainer = $('.timeline-events-container'); 275 const $timelineEvents = $('.timeline-event'); 276 277 if ($timelineWrapper.length === 0) return; 278 279 const updateTimelineProgress = () => { 280 const viewportHeight = $(window).height(); 281 const isDesktop = $(window).width() > 768; 282 283 const timelineRect = $timelineWrapper[0].getBoundingClientRect(); 284 const scrollY = $(window).scrollTop(); 285 286 // --- Detectar si la sección es visible --- 287 let isSectionVisible; 288 if (isDesktop) { 289 isSectionVisible = timelineRect.top < viewportHeight && timelineRect.bottom > 2; 290 } else { 291 const timelineOffsetTop = $timelineWrapper.offset().top; 292 const timelineBottom = timelineOffsetTop + $timelineWrapper.outerHeight(); 293 const windowBottom = scrollY + viewportHeight; 294 isSectionVisible = windowBottom > timelineOffsetTop + 50 && scrollY < timelineBottom; 295 console.log( isSectionVisible ); 296 $timelineFillLine.css('height', '100%'); 297 return; 298 } 299 300 // --- Si no es visible, resetear --- 301 if (!isSectionVisible) { 302 $timelineFillLine.css(isDesktop ? 'width' : 'height', '0%'); 303 $timelineEvents.removeClass('active'); 304 return; 305 } 306 307 // --- Cálculo de progreso --- 308 let progress = 0; 309 310 if (isDesktop) { 311 // Horizontal 312 const timelineContentWidth = $timelineEventsContainer[0].scrollWidth; 313 const wrapperScrollLeft = $timelineWrapper.scrollLeft(); 314 progress = (wrapperScrollLeft + $timelineWrapper.width()) / timelineContentWidth; 315 progress = Math.min(1, Math.max(0, progress)); 316 $timelineFillLine.css('width', (progress * 100) + '%'); 317 318 $timelineEvents.each(function () { 319 const eventRect = this.getBoundingClientRect(); 320 const eventCenterX = eventRect.left + (eventRect.width / 2);
321 if (eventCenterX < viewportHeight) { 322 $(this).addClass('active'); 323 } else { 324 $(this).removeClass('active'); 325 } 326 }); 327 328 } else { 329 // Vertical 330 const timelineContentHeight = $timelineEventsContainer[0].scrollHeight; 331 const timelineOffsetTop = $timelineWrapper.offset().top; 332 const visibleHeight = $(window).height(); 333 334 progress = Math.min( 335 1, 336 Math.max(0, (scrollY + visibleHeight * 0.8 - timelineOffsetTop) / timelineContentHeight) 337 ); 338 $timelineFillLine.css('height', (progress * 100) + '%'); 339 340 $timelineEvents.each(function () { 341 const eventRect = this.getBoundingClientRect(); 342 const eventCenterY = eventRect.top + (eventRect.height / 2); 343 if (eventCenterY < (viewportHeight * 0.8) && eventCenterY > 0) { 344 $(this).addClass('active'); 345 } else { 346 $(this).removeClass('active'); 347 } 348 }); 349 } 350 }; 351 352 // --- Intersection Observer --- 353 const observerOptions = { root: null, rootMargin: '0px', threshold: 0.1 }; 354 355 const observer = new IntersectionObserver((entries) => { 356 entries.forEach(entry => { 357 if (entry.isIntersecting) { 358 $(window).on('scroll', updateTimelineProgress); 359 if ($(window).width() > 768) { 360 $timelineWrapper.on('scroll', updateTimelineProgress); 361 } 362 updateTimelineProgress(); 363 } else { 364 $(window).off('scroll', updateTimelineProgress); 365 $timelineWrapper.off('scroll', updateTimelineProgress); 366 } 367 }); 368 }, observerOptions); 369 370 observer.observe($timelineWrapper[0]); 371 372 // --- En resize, resetear y volver a observar --- 373 $(window).on('resize', () => { 374 $(window).off('scroll', updateTimelineProgress); 375 $timelineWrapper.off('scroll', updateTimelineProgress); 376 observer.unobserve($timelineWrapper[0]); 377 observer.observe($timelineWrapper[0]); 378 updateTimelineProgress(); 379 }); 380 381 updateTimelineProgress(); 382}); 383 384 385 386// Funcionalidad para el carrusel "Inicio"
387// Funcionalidad para animar los contadores cuando entran en vista "Conocenos" 388$(document).ready(function() { 389 const speed = 200; // The animation speed (lower is faster) 390 391 const animateCounter = (counter) => { 392 const $counter = $(counter); // Convert the native element to a jQuery object 393 const target = +$counter.data('target'); // Get the final value from the data attribute 394 395 $({ 396 countNum: +$counter.text().replace(/\./g, '') // Get the starting value 397 }).animate({ 398 countNum: target 399 }, { 400 duration: 1500, // Duration of the animation in milliseconds 401 easing: 'swing', 402 step: function() { 403 // This function runs for each step of the animation 404 $counter.text(Math.floor(this.countNum).toLocaleString('es-ES')); 405 }, 406 complete: function() { 407 // Ensure the final value is exactly the target and properly formatted 408 $counter.text(target.toLocaleString('es-ES')); 409 } 410 }); 411 }; 412 413 // The Intersection Observer remains in native JavaScript as there's no direct jQuery equivalent 414 const observerOptions = { 415 root: null, // The viewport 416 rootMargin: '0px', 417 threshold: 0.5 // When 50% of the element is visible 418 }; 419 420 const observer = new IntersectionObserver((entries, observer) => { 421 entries.forEach(entry => { 422 if (entry.isIntersecting) { 423 animateCounter(entry.target); 424 observer.unobserve(entry.target); // Stop observing after animation 425 } 426 }); 427 }, observerOptions); 428 429 // Observe all counter elements 430 $('.counter-stat').each(function() { 431 observer.observe(this); 432 }); 433}); 434 435// Funcionalidad para secciones expandibles de "Servicios" 436$(document).ready(function() { 437 $('.expandable-header').on('click', function() { 438 // 1. Encuentra el contenedor padre más cercano con la clase 'expandable-section' 439 const section = $(this).closest('.expandable-section'); 440 441 // 2. Cierra (quita 'is-active') todas las demás secciones 442 $('.expandable-section').not(section).removeClass('is-active'); 443 444 // 3. Abre o cierra (toggle 'is-active') la sección actual 445 section.toggleClass('is-active'); 446 }); 447}); 448 449// Funcionalidad para el FAQ "Contacto" 450$(function () { 451 const $questionItems = $('.faq-question-item'); 452 const $answerBoxes = $('.faq-answer'); 453 454 $questionItems.on('click', function () { 455 // Quitar 'is-active' de todas las preguntas y respuestas 456 $questionItems.removeClass('is-active'); 457 $answerBoxes.removeClass('is-active'); 458 459 // Agregar 'is-active' a la pregunta clicada 460 $(this).addClass('is-active'); 461 462 // Mostrar la respuesta correspondiente 463 const targetAnswerId = $(this).data('answer'); 464 $('#' + targetAnswerId).addClass('is-active'); 465 }); 466}); 467 468 469// $(function() { ... }); es la versión corta de $(document).ready(function() { ... }); 470 // Espera a que el DOM esté cargado, igual que 'DOMContentLoaded' 471 $(function () { 472 // Obtenemos los elementos (usamos $ al inicio del nombre variable por convención en jQuery) 473 const $checkbox = $('#autorizo-check'); 474 const $button = $('#btn-operador'); 475 476 // Escuchamos el evento 'change' 477 $checkbox.on('change', function () { 478 /* 479 Dentro del evento, '$(this)' se refiere al checkbox que fue cambiado. 480 .is(':checked') devuelve true si está marcado. 481 .prop('disabled', valor) establece la propiedad disabled. 482 */ 483 484 // Si está marcado (true), queremos disabled=false. Usamos el operador de negación (!). 485 $button.prop('disabled', !$(this).is(':checked')); 486 }); 487 }); 488 489 490 491// trabaja con nosotros // 492$(document).ready(function() { 493 494 // Detectamos el clic en cualquier botón con la clase .toggle-info 495 $('.toggle-info').click(function(e) { 496 e.preventDefault(); // Evita que la página salte hacia arriba al hacer clic 497 498 // 1. Identificar el contenedor de información asociado a ESTE botón 499 // Buscamos el padre (div centered), luego el hermano anterior (.more-info) 500 var contenidoOculto = $(this).parent().prev('.more-info'); 501 502 // 2. Animación de despliegue (Slide) 503 contenidoOculto.slideToggle(300); // 300ms de velocidad 504
505 // 3. (Opcional) Cambiar el texto del botón 506 var textoActual = $(this).text().trim(); 507 if (textoActual === "Ver más información") { 508 $(this).text("Ver menos información"); 509 } else { 510 $(this).text("Ver más información"); 511 } 512 }); 513 514}); 515 516 517 518// ========================== 519// VARIABLES GLOBALES 520// ========================== 521window.__QUILL_CARGADO = false; 522window.__EDITOR_ACTIVO = null; 523 524// ========================== 525// MODO EDITOR VIA ?edit=1 526// ========================== 527(function () { 528 529 const params = new URLSearchParams(window.location.search); 530 531 if (params.get('edit') === '1') { 532 window.EDIT_MODE = true; 533 $('body').addClass('modo-editor'); 534 console.warn('MODO EDITOR ACTIVADO'); 535 } else { 536 window.EDIT_MODE = false; 537 } 538 539})(); 540 541 542// ========================== 543// AGREGAR BOTÃN âï¸ EDITAR 544// ========================== 545$(function () { 546 547 if (!window.EDIT_MODE) return; 548 549 $('[data-edit]').each(function () { 550 551 const $el = $(this); 552 553 if ($el.data('editor-ready')) return; 554 $el.data('editor-ready', true); 555 556 const $btn = $('<button type="button">âï¸</button>') 557 .addClass('button is-warning is-small') 558 .css({ 559 position: 'absolute', 560 top: '6px', 561 right: '6px', 562 zIndex: 1000, 563 padding: '2px 6px' 564 }); 565 566 if ($el.css('position') === 'static') { 567 $el.css('position', 'relative'); 568 } 569 570 $el.append($btn); 571 572 $btn.on('click', function (e) { 573 e.stopPropagation(); 574 activarEditorInline($el[0]); 575 }); 576 }); 577 578}); 579 580 581// ========================== 582// BOTÃN ð¾ GUARDAR 583// ========================== 584$(function () { 585 586 if (!window.EDIT_MODE) return; 587 if ($('#btnGuardarContenido').length) return; 588 589 const $btn = $(` 590 <button id="btnGuardarContenido" 591 class="button is-success" 592 style="position:fixed;top:20px;right:20px;z-index:9999"> 593 Guardar cambios 594 </button> 595 `); 596 597 $('body').append($btn); 598}); 599 600 601// ========================== 602// GUARDAR CONTENIDO 603// ========================== 604$(document).on('click', '#btnGuardarContenido', function () { 605 606 // cerrar editor activo antes de guardar 607 if (window.__EDITOR_ACTIVO) { 608 window.__EDITOR_ACTIVO.disable(); 609 window.__EDITOR_ACTIVO = null; 610 } 611 612 let bloques = []; 613 614 $('[data-edit]').each(function () { 615 bloques.push({ 616 key: $(this).data('edit'), 617 html: $(this).html() 618 }); 619 }); 620 621 const archivo = window.location.pathname.split('/').pop(); 622 623 $.ajax({ 624 url: '/editor/guardar.php', 625 type: 'POST', 626 contentType: 'application/json', 627 dataType: 'json', 628 data: JSON.stringify({ 629 archivo: archivo, 630 bloques: bloques 631 }), 632 success: function (r) { 633 if (r.ok) { 634 location.reload(); 635 } else { 636 alert(r.msg || 'Error al guardar'); 637 } 638 }, 639 error: function () { 640 alert('Error de comunicación con el servidor'); 641 } 642 }); 643}); 644 645 646// ========================== 647// CARGAR QUILL DINÃMICAMENTE 648// ========================== 649function cargarQuill(callback) { 650 651 if (window.__QUILL_CARGADO) { 652 callback(); 653 return; 654 } 655 656 const css = document.createElement('link'); 657 css.rel = 'stylesheet'; 658 css.href = 'https://cdn.quilljs.com/1.3.7/quill.snow.css'; 659 660 const js = document.createElement('script'); 661 js.src = 'https://cdn.quilljs.com/1.3.7/quill.min.js'; 662 663 js.onload = function () { 664 window.__QUILL_CARGADO = true; 665 console.warn('Quill cargado dinámicamente'); 666 callback(); 667 }; 668 669 js.onerror = function () { 670 alert('No se pudo cargar Quill'); 671 }; 672 673 document.head.appendChild(css); 674 document.head.appendChild(js); 675} 676 677 678// ========================== 679// ACTIVAR EDITOR INLINE (QUILL) 680// ========================== 681function activarEditorInline(el) { 682 683 cargarQuill(function () { 684 685 // cerrar editor anterior 686 if (window.__EDITOR_ACTIVO) { 687 window.__EDITOR_ACTIVO.disable(); 688 window.__EDITOR_ACTIVO = null; 689 } 690 691 // crear toolbar flotante 692 let toolbar = document.createElement('div'); 693 toolbar.innerHTML = `
694 <span class="ql-formats"> 695 <button class="ql-bold"></button> 696 <button class="ql-italic"></button> 697 <button class="ql-underline"></button> 698 <button class="ql-link"></button> 699 <button class="ql-clean"></button> 700 </span> 701 `; 702 toolbar.style.position = 'absolute'; 703 toolbar.style.background = '#fff'; 704 toolbar.style.border = '1px solid #ddd'; 705 toolbar.style.padding = '4px'; 706 toolbar.style.zIndex = 2000; 707 708 document.body.appendChild(toolbar); 709 710 const rect = el.getBoundingClientRect(); 711 toolbar.style.top = (window.scrollY + rect.top - 40) + 'px'; 712 toolbar.style.left = (window.scrollX + rect.left) + 'px'; 713 714 const quill = new Quill(el, { 715 theme: 'snow', 716 modules: { 717 toolbar: toolbar 718 } 719 }); 720 721 window.__EDITOR_ACTIVO = quill; 722 723 quill.focus(); 724 725 // ESC para cerrar editor 726 el.addEventListener('keydown', function (e) { 727 if (e.key === 'Escape') { 728 quill.disable(); 729 toolbar.remove(); 730 window.__EDITOR_ACTIVO = null; 731 } 732 }, { once: true }); 733 734 }); 735}
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.