PageSourceSearch

https://tramaco.com.ec/js/utiles.js

js tramaco.com.ec collected 2026-10-08 21:13:59 UTC 21,791 bytes, 735 lines download raw bytes

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.