PageSourceSearch

https://welcometoibiza.com/wp-content/plugins/wti-google-places/assets/js/wti-gp-front.js

js welcometoibiza.com collected 2026-09-24 23:41:23 UTC 19,058 bytes, 449 lines download raw bytes

1/**
2 * =============================================================================
3 * Frontend JS — WTI Google Places (Carousel + Modal Multi-paso)
4 * =============================================================================
5 * Archivo: wti-google-places/assets/js/wti-gp-front.js
6 * Ubicación: /wp-content/plugins/wti-google-places/assets/js/wti-gp-front.js
7 * Creado:  2026-02-24
8 * Actualizado: 2026-02-24 v1.4.0 — Modal multi-paso con reseñas locales
9 */
10
11(function() {
12    'use strict';
13
14    document.addEventListener('DOMContentLoaded', function() {
15        initCarousels();
16        initReviewModal();
17    });
18
19    // ═══════════════════════════════════════════════════════════════════════════
20    // CAROUSEL: dots + autoplay + drag/swipe
21    // ═══════════════════════════════════════════════════════════════════════════
22
23    function initCarousels() {
24        var carousels = document.querySelectorAll('.wti-gp-reviews__carousel');
25
26        carousels.forEach(function(carousel) {
27            var track = carousel.querySelector('.wti-gp-reviews__carousel-track');
28            var dots  = carousel.querySelectorAll('.wti-gp-reviews__carousel-dot');
29            if (!track) return;
30
31            var cards       = track.querySelectorAll('.wti-gp-reviews__card');
32            var current     = 0;
33            var total       = cards.length;
34            var autoplayMs  = parseInt(carousel.getAttribute('data-autoplay')) || 5000;
35            var autoTimer   = null;
36            var isPaused    = false;
37
38            // ── Ir a una card por indice ──
39            function goTo(index) {
40                if (index < 0) index = total - 1;
41                if (index >= total) index = 0;
42                current = index;
43
44                var card = cards[current];
45                if (card) {
46                    track.scrollTo({
47                        left: card.offsetLeft - track.offsetLeft,
48                        behavior: 'smooth'
49                    });
50                }
51                updateDots();
52            }
53
54            // ── Actualizar dots ──
55            function updateDots() {
56                dots.forEach(function(dot, i) {
57                    if (i === current) {
58                        dot.classList.add('wti-gp-reviews__carousel-dot--active');
59                    } else {
60                        dot.classList.remove('wti-gp-reviews__carousel-dot--active');
61                    }
62                });
63            }
64
65            // ── Click en dots ──
66            dots.forEach(function(dot) {
67                dot.addEventListener('click', function() {
68                    var idx = parseInt(dot.getAttribute('data-index'));
69                    goTo(idx);
70                    resetAutoplay();
71                });
72            });
73
74            // ── Autoplay ──
75            function startAutoplay() {
76                stopAutoplay();
77                autoTimer = setInterval(function() {
78                    if (!isPaused) {
79                        goTo(current + 1);
80                    }
81                }, autoplayMs);
82            }
83
84            function stopAutoplay() {
85                if (autoTimer) {
86                    clearInterval(autoTimer);
87                    autoTimer = null;
88                }
89            }
90
91            function resetAutoplay() {
92                stopAutoplay();
93                startAutoplay();
94            }
95
96            carousel.addEventListener('mouseenter', function() { isPaused = true; });
97            carousel.addEventListener('mouseleave', function() { isPaused = false; });
98            carousel.addEventListener('touchstart', function() { isPaused = true; }, {passive: true});
99            carousel.addEventListener('touchend', function() {
100                isPaused = false;
101                resetAutoplay();
102            }, {passive: true});
103
104            // ── Detectar scroll manual y actualizar dots ──
105            var scrollTimeout;
106            track.addEventListener('scroll', function() {
107                clearTimeout(scrollTimeout);
108                scrollTimeout = setTimeout(function() {
109                    var trackLeft = track.scrollLeft;
110                    var closest = 0;
111                    var closestDist = Infinity;
112                    cards.forEach(function(card, i) {
113                        var dist = Math.abs(card.offsetLeft - track.offsetLeft - trackLeft);
114                        if (dist < closestDist) {
115                            closestDist = dist;
116                            closest = i;
117                        }
118                    });
119                    if (closest !== current) {
120                        current = closest;
121                        updateDots();
122                    }
123                }, 80);
124            });
125
126            // ── Drag/swipe ──
127            var isDown = false, startX, scrollLeft;
128
129            track.addEventListener('mousedown', function(e) {
130                isDown = true;
131                track.classList.add('wti-gp-grabbing');
132                startX = e.pageX - track.offsetLeft;
133                scrollLeft = track.scrollLeft;
134            });
135
136            track.addEventListener('mouseleave', function() {
137                isDown = false;
138                track.classList.remove('wti-gp-grabbing');
139            });
140
141            track.addEventListener('mouseup', function() {
142                isDown = false;
143                track.classList.remove('wti-gp-grabbing');
144                resetAutoplay();
145            });
146
147            track.addEventListener('mousemove', function(e) {
148                if (!isDown) return;
149                e.preventDefault();
150                var x = e.pageX - track.offsetLeft;
151                var walk = (x - startX) * 1.5;
152                track.scrollLeft = scrollLeft - walk;
153            });
154
155            updateDots();
156            startAutoplay();
157        });
158    }
159
160    // ═══════════════════════════════════════════════════════════════════════════
161    // MODAL MULTI-PASO: Escribir reseña (3 slides)
162    // ═══════════════════════════════════════════════════════════════════════════
163
164    function initReviewModal() {
165        var modal = document.getElementById('wti-gp-review-modal');
166        if (!modal) return;
167
168        var backdrop    = modal.querySelector('.wti-gp-modal__backdrop');
169        var closeBtn    = modal.querySelector('.wti-gp-modal__close');
170        var steps       = modal.querySelectorAll('.wti-gp-modal__step');
171        var progressBar = document.getElementById('wti-gp-progress-bar');
172        var titleText   = document.getElementById('wti-gp-modal-title-text');
173        var stars       = modal.querySelectorAll('.wti-gp-modal__star');
174        var ratingLabel = document.getElementById('wti-gp-rating-label');
175
176        // Botones navegacion
177        var step1Next   = document.getElementById('wti-gp-step1-next');
178        var step2Back   = document.getElementById('wti-gp-step2-back');
179        var submitBtn   = document.getElementById('wti-gp-submit-review');
180        var closeThanks = document.getElementById('wti-gp-close-thanks');
181
182        // Campos paso 2
183        var nameInput   = document.getElementById('wti-gp-review-name');
184        var textArea    = document.getElementById('wti-gp-review-text');
185        var charCount   = document.getElementById('wti-gp-char-count');
186        var selectedStarsDiv = document.getElementById('wti-gp-selected-stars');
187
188        // Estado
189        var currentStep    = 1;
190        var selectedRating = 0;
191        var currentPostId  = 0;
192
193        var ratingLabels = ['', 'Malo', 'Regular', 'Bien', 'Muy bien', 'Excelente'];
194        var stepTitles   = ['', 'Tu opinion importa', 'Cuentanos mas', 'Gracias'];
195
196        // ── Abrir modal ──
197        document.addEventListener('click', function(e) {
198            var btn = e.target.closest('.wti-gp-reviews__write-btn, .wti-gp-summary__opinar');
199            if (btn) {
200                e.preventDefault();
201                // Obtener post_id del boton o del wrapper mas cercano
202                currentPostId = parseInt(btn.getAttribute('data-post-id')) || 0;
203                if (!currentPostId) {
204                    var wrapper = btn.closest('.wti-gp-reviews, [data-post-id]');
205                    if (wrapper) {
206                        currentPostId = parseInt(wrapper.getAttribute('data-post-id')) || 0;
207                    }
208                }
209                openModal();
210            }
211        });
212
213        // ── Cerrar modal ──
214        if (backdrop) backdrop.addEventListener('click', closeModal);
215        if (closeBtn) closeBtn.addEventListener('click', closeModal);
216        if (closeThanks) closeThanks.addEventListener('click', closeModal);
217
218        // Cancelar en paso 1
219        var cancelBtns = modal.querySelectorAll('.wti-gp-modal__cancel');
220        cancelBtns.forEach(function(btn) {
221            btn.addEventListener('click', closeModal);
222        });
223
224        // Escape key
225        document.addEventListener('keydown', function(e) {
226            if (e.key === 'Escape' && modal.classList.contains('wti-gp-modal--open')) {
227                closeModal();
228            }
229        });
230
231        // ── Paso 1: Estrellas ──
232        stars.forEach(function(star, index) {
233            star.addEventListener('mouseenter', function() {
234                highlightStars(index + 1);
235                if (ratingLabel) ratingLabel.textContent = ratingLabels[index + 1] || '';
236            });
237
238            star.addEventListener('click', function() {
239                selectedRating = index + 1;
240                var sc = modal.querySelector('.wti-gp-modal__star-select');
241                if (sc) sc.dataset.selected = selectedRating;
242                highlightStars(selectedRating);
243                if (ratingLabel) {
244                    ratingLabel.textContent = ratingLabels[selectedRating] || '';
245                    ratingLabel.classList.add('wti-gp-modal__rating-label--active');
246                }
247                // Habilitar boton siguiente
248                if (step1Next) step1Next.disabled = false;
249            });
250        });
251
252        var starContainer = modal.querySelector('.wti-gp-modal__star-select');
253        if (starContainer) {
254            starContainer.addEventListener('mouseleave', function() {
255                var selected = parseInt(starContainer.dataset.selected) || 0;
256                highlightStars(selected);
257                if (ratingLabel) {
258                    ratingLabel.textContent = selected ? ratingLabels[selected] : '';
259                }
260            });
261        }
262
263        // ── Navegacion entre pasos ──
264        if (step1Next) {
265            step1Next.addEventListener('click', function() {
266                if (selectedRating < 1) return;
267                goToStep(2);
268                // Mostrar estrellas seleccionadas en paso 2
269                if (selectedStarsDiv) {
270                    var starsHtml = '';
271                    for (var i = 1; i <= 5; i++) {
272                        var fill = i <= selectedRating ? '#F57C00' : '#E0E0E0';
273                        starsHtml += '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" fill="' + fill + '"/></svg>';
274                    }
275                    starsHtml += '<span class="wti-gp-modal__rating-text">' + ratingLabels[selectedRating] + '</span>';
276                    selectedStarsDiv.innerHTML = starsHtml;
277                }
278                // Focus en nombre
279                if (nameInput) {
280                    setTimeout(function() { nameInput.focus(); }, 300);
281                }
282            });
283        }
284
285        if (step2Back) {
286            step2Back.addEventListener('click', function() {
287                goToStep(1);
288            });
289        }
290
291        // ── Contador de caracteres ──
292        if (textArea && charCount) {
293            textArea.addEventListener('input', function() {
294                charCount.textContent = textArea.value.length;
295            });
296        }
297
298        // ── Enviar reseña (AJAX) ──
299        if (submitBtn) {
300            submitBtn.addEventListener('click', function(e) {
301                e.preventDefault();
302                if (selectedRating < 1 || !currentPostId) return;
303
304                var name = nameInput ? nameInput.value.trim() : '';
305                var text = textArea  ? textArea.value.trim()  : '';
306
307                // Deshabilitar boton
308                submitBtn.disabled = true;
309                submitBtn.innerHTML = '<span class="wti-gp-modal__spinner"></span> Enviando...';
310
311                // AJAX POST
312                var formData = new FormData();
313                formData.append('action',  'wti_gp_save_review');
314                formData.append('nonce',   wtiGPFront.nonce);
315                formData.append('post_id', currentPostId);
316                formData.append('rating',  selectedRating);
317                formData.append('name',    name);
318                formData.append('text',    text);
319
320                fetch(wtiGPFront.ajaxUrl, {
321                    method: 'POST',
322                    body: formData,
323                    credentials: 'same-origin'
324                })
325                .then(function(response) { return response.json(); })
326                .then(function(result) {
327                    submitBtn.disabled = false;
328                    submitBtn.innerHTML = 'Publicar <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/></svg>';
329
330                    if (result.success) {
331                        goToStep(3);
332                    } else {
333                        // Mostrar error inline
334                        var errorDiv = modal.querySelector('.wti-gp-modal__error');
335                        if (!errorDiv) {
336                            errorDiv = document.createElement('div');
337                            errorDiv.className = 'wti-gp-modal__error';
338                            submitBtn.parentNode.insertBefore(errorDiv, submitBtn);
339                        }
340                        errorDiv.textContent = result.data || 'Error al enviar. Intentalo de nuevo.';
341                        errorDiv.style.display = 'block';
342                        setTimeout(function() { errorDiv.style.display = 'none'; }, 4000);
343                    }
344                })
345                .catch(function() {
346                    submitBtn.disabled = false;
347                    submitBtn.innerHTML = 'Publicar <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/></svg>';
348                    alert('Error de conexion. Comprueba tu internet e intentalo de nuevo.');
349                });
350            });
351        }
352
353        // ── Funciones helpers ──
354
355        function openModal() {
356            // Reset todo
357            selectedRating = 0;
358            currentStep = 1;
359            var sc = modal.querySelector('.wti-gp-modal__star-select');
360            if (sc) sc.dataset.selected = 0;
361            highlightStars(0);
362            if (step1Next) step1Next.disabled = true;
363            if (nameInput) nameInput.value = '';
364            if (textArea) textArea.value = '';
365            if (charCount) charCount.textContent = '0';
366            if (ratingLabel) {
367                ratingLabel.textContent = '';
368                ratingLabel.classList.remove('wti-gp-modal__rating-label--active');
369            }
370
371            // Ocultar errores
372            var errors = modal.querySelectorAll('.wti-gp-modal__error');
373            errors.forEach(function(el) { el.style.display = 'none'; });
374
375            // Mostrar paso 1
376            showStep(1);
377            updateProgress(1);
378
379            modal.classList.add('wti-gp-modal--open');
380            document.body.style.overflow = 'hidden';
381        }
382
383        function closeModal() {
384            modal.classList.remove('wti-gp-modal--open');
385            document.body.style.overflow = '';
386
387            // Si estabamos en paso 3 (gracias), recargar la pagina para mostrar la nueva resena
388            if (currentStep === 3) {
389                window.location.reload();
390            }
391        }
392
393        function goToStep(step) {
394            var currentEl = modal.querySelector('.wti-gp-modal__step--active');
395            var nextEl    = modal.querySelector('.wti-gp-modal__step[data-step="' + step + '"]');
396            if (!currentEl || !nextEl) return;
397
398            var direction = step > currentStep ? 'left' : 'right';
399
400            // Animar salida
401            currentEl.classList.add('wti-gp-modal__step--exit-' + direction);
402
403            setTimeout(function() {
404                currentEl.classList.remove('wti-gp-modal__step--active', 'wti-gp-modal__step--exit-left', 'wti-gp-modal__step--exit-right');
405
406                // Animar entrada
407                nextEl.classList.add('wti-gp-modal__step--enter-' + direction);
408
409                // Forzar reflow
410                void nextEl.offsetWidth;
411
412                nextEl.classList.add('wti-gp-modal__step--active');
413                nextEl.classList.remove('wti-gp-modal__step--enter-left', 'wti-gp-modal__step--enter-right');
414
415                currentStep = step;
416                updateProgress(step);
417
418                // Actualizar titulo
419                if (titleText) titleText.textContent = stepTitles[step] || '';
420            }, 200);
421        }
422
423        function showStep(step) {
424            steps.forEach(function(s) {
425                s.classList.remove('wti-gp-modal__step--active', 'wti-gp-modal__step--exit-left', 'wti-gp-modal__step--exit-right', 'wti-gp-modal__step--enter-left', 'wti-gp-modal__step--enter-right');
426            });
427            var target = modal.querySelector('.wti-gp-modal__step[data-step="' + step + '"]');
428            if (target) target.classList.add('wti-gp-modal__step--active');
429            if (titleText) titleText.textContent = stepTitles[step] || '';
430        }
431
432        function updateProgress(step) {
433            if (progressBar) {
434                var pct = (step / 3) * 100;
435                progressBar.style.width = pct + '%';
436            }
437        }
438
439        function highlightStars(count) {
440            stars.forEach(function(s, i) {
441                var path = s.querySelector('path');
442                if (path) {
443                    path.setAttribute('fill', i < count ? '#F57C00' : '#E0E0E0');
444                }
445            });
446        }
447    }
448
449})();

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.