PageSourceSearch

https://www.avaibook.com/wp-content/themes/avaibook/assets/js/home.js

js avaibook.com collected 2026-10-01 12:31:49 UTC 25,840 bytes, 599 lines download raw bytes

1/**
2 * HOME.JS - Scripts para la nueva Landing de AvaiBook
3 * Versión: 3.0
4 * Versión landing home: 22.7
5 * Autor: Equipo AvaiBook
6 * Fecha: noviembre 2025
7 * Última actualización: 2025-11-26 [REFACTOR: Detección de idioma simplificada]
8 * Descripción: Archivo de animaciones e interactividad para la landing home.
9 * 
10 * CAMBIOS v3.0:
11 * - Carrusel de beneficios ahora recibe idioma SOLO desde evento avaibook:languageReady de script.js
12 * - Eliminado localStorage completamente
13 * - Eliminado fallback a localStorage (solo español si el evento no se recibe)
14 * - Sistema de detección de idioma más limpio y centralizado
15 *
16 * Funcionalidades incluidas:
17 * 1. Animaciones al scroll - Elementos principales (fade-in)
18 * 2. Smooth scroll para enlaces internos
19 * 3. Animación de beneficios clave (AB-reveal)
20 * 4. Animación de tarjetas escalonadas (delayed card animations)
21 * 5. Efecto flip card para tarjetas coloridas
22 * 6. Animación de logos de partners con delays incrementales
23 * 7. Carrusel de beneficios multiidioma (ES/PT/IT) - Idioma desde evento avaibook:languageReady
24 */
25
26// LOG INMEDIATO - Se ejecuta apenas se carga el archivo
27console.log('🔍 [HOME.JS v3.0] Archivo cargado - Detección de idioma simplificada');
28console.log('📍 [HOME.JS] URL:', window.location.pathname);
29
30
31document.addEventListener('DOMContentLoaded', function () {
32    console.log('✅ [HOME.JS] DOMContentLoaded disparado - Inicializando...');
33
34    // ==================================================
35    // 1. ANIMACIONES AL SCROLL - Elementos principales
36    // ==================================================
37    function initScrollAnimations() {
38        const observerOptions = {
39            threshold: 0.25,
40            rootMargin: '0px 0px -50px 0px'
41        };
42
43        const observer = new IntersectionObserver(function (entries) {
44            entries.forEach(entry => {
45                if (entry.isIntersecting) {
46                    entry.target.classList.add('AB-visible');
47                }
48            });
49        }, observerOptions);
50
51        // Aplicar animaciones a las tarjetas de productos y elementos
52        const fadeElements = document.querySelectorAll('.AB-fade-in');
53        fadeElements.forEach(element => {
54            observer.observe(element);
55        });
56    }
57
58    // ==========================================
59    // 2. SMOOTH SCROLL PARA ENLACES INTERNOS
60    // ==========================================
61    function initSmoothScroll() {
62        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
63            anchor.addEventListener('click', function (e) {
64                e.preventDefault();
65                const target = document.querySelector(this.getAttribute('href'));
66                if (target) {
67                    target.scrollIntoView({
68                        behavior: 'smooth',
69                        block: 'start'
70                    });
71                }
72            });
73        });
74    }
75
76    // ==========================================
77    // 3. ANIMACIÓN DE BENEFICIOS CLAVE (AB-reveal)
78    // ==========================================
79    function initRevealAnimations() {
80        const revealElements = document.querySelectorAll('.AB-reveal');
81
82        if ("IntersectionObserver" in window) {
83            const observer = new IntersectionObserver((entries, observer) => {
84                entries.forEach(entry => {
85                    if (entry.isIntersecting) {
86                        entry.target.classList.add('visible');
87                        // Opcional: dejar de observar el elemento una vez que es visible
88                        // observer.unobserve(entry.target); 
89                    }
90                });
91            }, {
92                threshold: 0.1 // El elemento se revela cuando el 10% es visible
93            });
94
95            revealElements.forEach(el => {
96                observer.observe(el);
97            });
98        } else {
99            // Fallback para navegadores antiguos sin IntersectionObserver
100            revealElements.forEach(el => el.classList.add('visible'));
101        }
102    }
103
104    
105
106    // ==========================================
107    // 4. ANIMACIÓN DE TARJETAS ESCALONADAS
108    // ==========================================
109    function initCardAnimations() {
110        const animatedCards = document.querySelectorAll('.AB-animated-card');
111
112        const observerOptions = {
113            root: null,
114            rootMargin: '0px',
115            threshold: 0.2 // El 20% del elemento debe ser visible
116        };
117
118        const cardObserver = new IntersectionObserver((entries, observer) => {
119            entries.forEach((entry, index) => {
120                if (entry.isIntersecting) {
121                    // Retraso de 600ms entre cada carta
122                    setTimeout(() => {
123                        entry.target.classList.add('is-in-view');
124                    }, index * 600);
125
126                    // OPTIMIZACIÓN: Dejamos de observar este elemento una vez animado
127                    observer.unobserve(entry.target);
128                }
129            });
130        }, observerOptions);
131
132        animatedCards.forEach(card => {
133            cardObserver.observe(card);
134        });
135    }
136
137    // ==========================================
138    // 5. EFECTO FLIP CARD
139    // ==========================================
140    function initFlipCards() {
141        const flipCards = document.querySelectorAll('.AB-colorful-item');
142        let currentFlipped = null;
143
144        flipCards.forEach(card => {
145            card.addEventListener('click', function () {
146                // Si hay una carta volteada y no es la actual, la regresamos
147                if (currentFlipped && currentFlipped !== this) {
148                    currentFlipped.classList.remove('flipped');
149                }
150
151                // Alternar el estado de la carta actual
152                this.classList.toggle('flipped');
153
154                // Actualizar la referencia de la carta volteada
155                if (this.classList.contains('flipped')) {
156                    currentFlipped = this;
157                } else {
158                    currentFlipped = null;
159                }
160            });
161        });
162    }
163
164    // ==========================================
165    // 6. ANIMACIÓN DE LOGOS DE PARTNERS
166    // ==========================================
167    function initLogoAnimations() {
168        const logos = document.querySelectorAll('.AB-home-logo-item');
169       
170
171        if (logos.length === 0) {
172            console.warn('⚠️ No se encontraron elementos .AB-home-logo-item');
173            return;
174        }
175
176        let delay = 0;
177
178        // Asigna delays incrementales para aparecer uno por uno
179        logos.forEach((logo, index) => {
180            logo.style.transitionDelay = delay + 's';
181            delay += 0.2; // Ajusta el retraso entre apariciones (0.2s por logo)
182            
183        });
184
185        // Función para verificar si están en viewport y activar
186        function checkIfInView() {
187            logos.forEach((logo, index) => {
188                const rect = logo.getBoundingClientRect();
189                // Aparecen cuando están al 90% del viewport (más tarde en el scroll)
190                const inView = rect.top <= (window.innerHeight * 0.9) && rect.bottom >= 0;
191                
192                if (inView) {
193                    logo.classList.add('AB-visible');
194                }
195            });
196        }
197
198        // Inicializa al cargar y al scroll
199        
200        checkIfInView();
201        window.addEventListener('scroll', checkIfInView);
202    }
203
204    // ==========================================
205    // 7. CARRUSEL DE BENEFICIOS 
206    // ==========================================
207
208    function initCarruselBeneficios(forcedLanguage) {
209        // ==========================================
210        // SISTEMA DE DETECCIÓN DE IDIOMA (v3.0 - Simplificado)
211        // ==========================================
212        // El idioma se recibe desde evento avaibook:languageReady de script.js
213        // Si no se recibe el evento (race condition), fallback a español
214        
215        // PREVENIR DOBLE INICIALIZACIÓN
216        if (window.avaiBookCarruselInitialized) {
217            console.log('🔄 [HOME.JS] Carrusel ya inicializado, saltando...');
218            return;
219        }
220        
221        // Función auxiliar: obtener idioma desde parámetro del evento
222        function detectLanguage() {
223            /* const lang = forcedLanguage || 'es';
224            console.log(`🌐 [HOME.JS] Idioma para carrusel: ${lang.toUpperCase()} ${forcedLanguage ? '(desde evento avaibook:languageReady)' : '(fallback a español)'}`);
225            return lang; */
226
227
228            const lang = localStorage.getItem("userLang");
229            console.log(`🌐 [HOME.JS] Idioma para carrusel: ${lang.toUpperCase()} ${forcedLanguage ? '(desde evento avaibook:languageReady)' : '(fallback a español)'}`);
230            return lang;
231        }
232        
233        const currentLanguage = detectLanguage();
234        console.log(`🎠 [HOME.JS v3.0] Inicializando carrusel en idioma: ${currentLanguage.toUpperCase()}`);
235        
236        // ==========================================
237        // DATOS MULTIIDIOMA DEL CARRUSEL
238        // ==========================================
239        
240        const CAROUSEL_DATA = {
241            es: [
242                {
243                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/configuracion-rapida.png",
244                    titulo: "Configuración rápida y gratuita",
245                    texto: "Sincroniza o crea anuncios de forma rápida y sencilla. Optimiza su visibilidad gracias a nuestro onboarding ",
246                    enlace: "#configuracion"
247                },
248                {
249                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/formaciones-avaibook-1.png",
250                    titulo: "Formaciones y recursos online",
251                    texto: "Compartimos contigo nuestra experiencia con sesiones que contemplan cambios en normativa, integraciones, nuevas funcionalidades…",
252                    enlace: "#formaciones"
253                },
254                {
255                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/producto-adaptado.png",
256                    titulo: "Producto adaptado a ti",
257                    texto: "AvaiBook está pensado por y para propietarios y gestores de alquileres turísticos, ¡cubrimos todas tus necesidades!",
258                    enlace: "#personalizacion"
259                },
260                {
261                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/integraciones-partners.png",
262                    titulo: "Integraciones con partners clave del sector",
263                    texto: "Conéctate con los proveedores líderes del sector y complementa todavía más el rendimiento de tus alojamientos",
264                    enlace: "#integraciones"
265                }
266            ],
267            pt: [
268                {
269                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/configuracion-rapida.png",
270                    titulo: "Configuração de conta rápida e gratuita",
271                    texto: "Cria e sincroniza anúncios facilmente. Melhora a visibilidade do teu alojamento com o nosso onboarding personalizado e gratuito.",
272                    enlace: "#configuracion"
273                },
274                {
275                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/formaciones-avaibook-1.png",
276                    titulo: "Formação online e conteúdos exclusivos",
277                    texto: "Fica a par das atualizações sobre legislação nacional, integrações e novidades através de sessões de formação online e gratuitas.",
278                    enlace: "#formaciones"
279                },
280                {
281                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/producto-adaptado.png",
282                    titulo: "Solução pensada para ti",
283                    texto: "O software AvaiBook by idealista foi criado especialmente para o alojamento local e feito a pensar nas necessidades de proprietários e gestores.",
284                    enlace: "#personalizacion"
285                },
286                {
287                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/integraciones-partners.png",
288                    titulo: "Integrações com os principais parceiros do setor",
289                    texto: "Integramos com os fornecedores líderes do mercado para complementar ainda mais o desempenho dos teus alojamentos.",
290                    enlace: "#integraciones"
291                }
292            ],
293            it: [
294                {
295                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/configuracion-rapida.png",
296                    titulo: "Configurazione rapida e gratuita",
297                    texto: "Sincronizza o crea annunci in modo rapido e semplice. Ottimizza la loro visibilità grazie al nostro on-boarding",
298                    enlace: "#configuracion"
299                },
300                {
301                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/formaciones-avaibook-1.png",
302                    titulo: "Formazioni e risorse online",
303                    texto: "Condividiamo con te la nostra esperienza con sessioni in cui parliamo di cambi normativi, integrazioni, nuove funzionalità…",
304                    enlace: "#formaciones"
305                },
306                {
307                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/producto-adaptado.png",
308                    titulo: "Prodotto adattato a te",
309                    texto: "AvaiBook è pensato da e per proprietari e gestori di affitti turistici, copriamo tutte le tue esigenze!",
310                    enlace: "#personalizacion"
311                },
312                {
313                    imagen: "https://www.avaibook.com/wp-content/uploads/2021/02/integraciones-partners.png",
314                    titulo: "Integrazioni con partner chiave del settore",
315                    texto: "Connettiti con i fornitori leader del settore e migliora ancora di più le performance dei tuoi alloggi",
316                    enlace: "#integraciones"
317                }
318            ]
319        };
320        
321        // Obtener datos del idioma actual (con fallback a español)
322        const contenidoData = CAROUSEL_DATA[currentLanguage] || CAROUSEL_DATA['es'];
323        
324        if (!CAROUSEL_DATA[currentLanguage]) {
325            console.warn(`⚠️ [HOME.JS] No hay datos para idioma "${currentLanguage}", usando español`);
326        }
327        
328        console.log(`📊 [HOME.JS] Contenido del carrusel:`, contenidoData.length, 'elementos en', currentLanguage.toUpperCase());
329
330            // Configuración del carrusel
331            const carrusel = document.getElementById('carrusel');
332            const prevBtn = document.getElementById('prevBtn');
333            const nextBtn = document.getElementById('nextBtn');
334            const indicators = document.querySelectorAll('.AB-home-indicator');
335            const totalItems = contenidoData.length;
336            let currentIndex = 0;
337
338            if (!carrusel) {
339                console.error('❌ [HOME.JS] No se encontró el elemento #carrusel');
340                return;
341            }
342
343            // Obtener las tarjetas por posición (solo 3 posiciones)
344            const cards = {
345                'left': carrusel.querySelector('[data-position="left"]'),
346                'center': carrusel.querySelector('[data-position="center"]'),
347                'right': carrusel.querySelector('[data-position="right"]')
348            };
349
350            // Obtener contenedores de posiciones
351            const leftPosition = carrusel.querySelector('.AB-home-left-position');
352            const rightPosition = carrusel.querySelector('.AB-home-right-position');
353
354            // Función para actualizar el contenido de una tarjeta
355            function updateCard(card, data) {
356                if (!card || !data) return;
357
358                const img = card.querySelector('.AB-home-card-image');
359                const title = card.querySelector('.AB-home-card-title');
360                const text = card.querySelector('.AB-home-card-text');
361
362                img.src = data.imagen;
363                img.alt = data.titulo;
364                title.textContent = data.titulo;
365                if (text) text.textContent = data.texto;
366            }
367
368            // Handlers separados para evitar problemas de closure
369            function handleLeftCardClick(e) {
370                e.preventDefault();
371                e.stopPropagation();
372                if (currentIndex > 0) {
373                    currentIndex--;
374                    updateCarrusel();
375                }
376            }
377
378            function handleRightCardClick(e) {
379                e.preventDefault();
380                e.stopPropagation();
381                if (currentIndex < totalItems - 1) {
382                    currentIndex++;
383                    updateCarrusel();
384                }
385            }
386
387            // Función para re-establecer event listeners después de cada actualización
388            function attachCardListeners() {
389                // Obtener elementos actuales
390                const leftCardElement = carrusel.querySelector('[data-position="left"]');
391                const rightCardElement = carrusel.querySelector('[data-position="right"]');
392
393                if (leftCardElement && !leftCardElement.classList.contains('AB-home-card-hidden')) {
394                    leftCardElement.removeEventListener('click', handleLeftCardClick);
395                    leftCardElement.addEventListener('click', handleLeftCardClick);
396                }
397
398                if (rightCardElement && !rightCardElement.classList.contains('AB-home-card-hidden')) {
399                    rightCardElement.removeEventListener('click', handleRightCardClick);
400                    rightCardElement.addEventListener('click', handleRightCardClick);
401                }
402            }
403
404            // Función para actualizar el carrusel
405            function updateCarrusel() {
406                const isMobile = window.innerWidth <= 768;
407
408                if (isMobile) {
409                    // En móvil, solo mostrar la tarjeta central
410                    const centerCard = cards['center'];
411                    updateCard(centerCard, contenidoData[currentIndex]);
412
413                    // Ocultar posiciones laterales
414                    leftPosition.style.display = 'none';
415                    rightPosition.style.display = 'none';
416                } else {
417                    // Mostrar posiciones laterales
418                    leftPosition.style.display = 'flex';
419                    rightPosition.style.display = 'flex';
420
421                    // La tarjeta central SIEMPRE está fija y muestra el contenido actual
422                    const centerCard = cards['center'];
423                    updateCard(centerCard, contenidoData[currentIndex]);
424
425                    // Posición izquierda: muestra la tarjeta anterior (si existe)
426                    const leftCard = cards['left'];
427                    const leftIndex = currentIndex - 1;
428
429                    leftCard.classList.remove('AB-home-card-featured', 'AB-home-card-side', 'AB-home-card-hidden');
430
431                    if (leftIndex < 0) {
432                        // No hay tarjeta anterior, ocultar
433                        leftCard.classList.add('AB-home-card-hidden');
434                    } else {
435                        // Mostrar tarjeta anterior
436                        leftCard.classList.add('AB-home-card-side');
437                        updateCard(leftCard, contenidoData[leftIndex]);
438                    }
439
440                    // Posición derecha: muestra la tarjeta siguiente (si existe)
441                    const rightCard = cards['right'];
442                    const rightIndex = currentIndex + 1;
443
444                    rightCard.classList.remove('AB-home-card-featured', 'AB-home-card-side', 'AB-home-card-hidden');
445
446                    if (rightIndex >= totalItems) {
447                        // No hay tarjeta siguiente, ocultar
448                        rightCard.classList.add('AB-home-card-hidden');
449                    } else {
450                        // Mostrar tarjeta siguiente
451                        rightCard.classList.add('AB-home-card-side');
452                        updateCard(rightCard, contenidoData[rightIndex]);
453                    }
454                }
455
456                // Actualizar indicadores
457                indicators.forEach((indicator, index) => {
458                    indicator.classList.toggle('active', index === currentIndex);
459                });
460
461                // Actualizar estado de los botones
462                prevBtn.disabled = currentIndex === 0;
463                nextBtn.disabled = currentIndex === totalItems - 1;
464                
465                // Re-establecer listeners de tarjetas laterales
466                setTimeout(attachCardListeners, 100);
467            }
468
469            // Event listeners para botones de navegación
470            if (nextBtn) {
471                nextBtn.addEventListener('click', function () {
472                    if (currentIndex < totalItems - 1) {
473                        currentIndex++;
474                        updateCarrusel();
475                    }
476                });
477            }
478
479            if (prevBtn) {
480                prevBtn.addEventListener('click', function () {
481                    if (currentIndex > 0) {
482                        currentIndex--;
483                        updateCarrusel();
484                    }
485                });
486            }
487
488            // Event listeners para indicadores
489            indicators.forEach((indicator, index) => {
490                indicator.addEventListener('click', function () {
491                    currentIndex = index;
492                    updateCarrusel();
493                });
494            });
495
496
497
498            // Navegación con teclado
499            document.addEventListener('keydown', function (e) {
500                if (e.key === 'ArrowLeft' && currentIndex > 0) {
501                    currentIndex--;
502                    updateCarrusel();
503                } else if (e.key === 'ArrowRight' && currentIndex < totalItems - 1) {
504                    currentIndex++;
505                    updateCarrusel();
506                }
507            });
508
509            // Actualizar en cambio de tamaño de ventana
510            window.addEventListener('resize', function () {
511                updateCarrusel();
512            });
513
514            // Inicializar carrusel
515            updateCarrusel();
516            
517            // Establecer listeners de tarjetas laterales
518            attachCardListeners();
519            
520            // Marcar como inicializado globalmente
521            window.avaiBookCarruselInitialized = true;
522            
523            // Log de confirmación con idioma correcto
524            console.log(`✅ [HOME.JS] Carrusel inicializado correctamente en ${currentLanguage.toUpperCase()}`);
525
526            // Opcional: Autoplay (descomentado por defecto)
527            /*
528            let autoplayInterval = setInterval(() => {
529                if (currentIndex < totalItems - 1) {
530                    currentIndex++;
531                } else {
532                    currentIndex = 0;
533                }
534                updateCarrusel();
535            }, 4000);
536
537            // Pausar autoplay al hacer hover
538            carrusel.addEventListener('mouseenter', () => clearInterval(autoplayInterval));
539            carrusel.addEventListener('mouseleave', () => {
540                autoplayInterval = setInterval(() => {
541                    if (currentIndex < totalItems - 1) {
542                        currentIndex++;
543                    } else {
544                        currentIndex = 0;
545                    }
546                    updateCarrusel();
547                }, 4000);
548            });
549            */
550    }
551
552
553
554    // ==========================================
555    // INICIALIZACIÓN DE TODAS LAS FUNCIONES
556    // ==========================================
557
558    // Ejecutar funciones que NO dependen del idioma
559    initScrollAnimations();
560    initSmoothScroll();
561    initRevealAnimations();
562    initCardAnimations();
563    initFlipCards();
564    initLogoAnimations();
565
566    // ==========================================
567    // INICIALIZACIÓN DEL CARRUSEL (v3.0 - Evento de idioma centralizado)
568    // ==========================================
569    // El carrusel espera el evento avaibook:languageReady desde script.js
570    // que contiene el idioma detectado (por URL, WPML, etc.)
571
572    //  Detección de idioma y guardado en variable userLang (ORP) esto hay que centralizarlo
573	
574    var detectedLang = localStorage.getItem("userLang");
575    setTimeout(function() {
576        initCarruselBeneficios(detectedLang);
577    }, 300);
578    
579    // Escuchar el evento de idioma listo desde script.js
580    /* document.addEventListener('avaibook:languageReady', function(event) {
581        //const detectedLang = event.detail.language;
582        console.log('entra');
583        console.log(`🎯 [HOME.JS v3.0] Evento 'avaibook:languageReady' recibido - Idioma: ${detectedLang.toUpperCase()}`);
584        initCarruselBeneficios(detectedLang);
585        console.log('Alberto arriba: ' + detectedLang);
586    }); */
587
588    // Fallback (race condition): si el evento no se dispara en 500ms, usar español
589    /* setTimeout(function() {
590        if (!window.avaiBookCarruselInitialized) {
591            console.log('Alberto: ' + detectedLang);
592            console.warn('⚠️ [HOME.JS v3.0] Evento no recibido en 500ms, inicializando carrusel con español (fallback)');
593            initCarruselBeneficios(detectedLang);
594        }
595    }, 500); */
596
597    // Log para debugging
598    console.log(`✅ [HOME.JS] Script cargado completamente - Esperando idioma desde script.js`);
599});

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.