PageSourceSearch

https://www.robinsonpetshop.it/app/mu-plugins/jump-waiting-list/assets/js/waiting-list-popup.js?ver=1.0.0

js robinsonpetshop.it collected 2026-09-26 19:19:49 UTC 34,545 bytes, 880 lines download raw bytes

1/**
2 * Jump Waiting List Popup JavaScript
3 * Handles the waiting list popup functionality with robust product detection
4 */
5(function() {
6    'use strict';
7
8    // Stato globale del popup
9    let popupState = {
10        isOpen: false,
11        currentProductData: null
12    };
13
14    // Selettori DOM
15    const selectors = {
16        backdrop: '.jump-waiting-list__backdrop',
17        popup: '.jump-waiting-list__popup',
18        closeBtn: '.jump-waiting-list__close',
19        form: '.jump-waiting-list__form',
20        emailInput: '#jump-waiting-list-email',
21        termsCheckbox: '#jump-waiting-list-terms',
22        submitBtn: '.jump-waiting-list__submit',
23        productInfo: '.jump-waiting-list__product-info'
24    };
25
26    /**
27     * Verifica se un elemento è un pulsante waiting list
28     * @param {Element} element - Elemento da verificare
29     * @returns {boolean} True se è un pulsante waiting list
30     */
31    function isWaitingListButton(element) {
32        if (!element) return false;
33        
34        const buttonText = element.textContent.toLowerCase().trim();
35        const buttonHTML = element.innerHTML.toLowerCase();
36        
37        // Lista di parole chiave che indicano un pulsante waiting list
38        const waitingListKeywords = [
39            'waiting',
40            'notifica',
41            'avvisa',
42            'avvisami',
43            'lista',
44            'attesa',
45            'disponibile',
46            'stock',
47            'esaurito'
48        ];
49        
50        // Controlla attributi/classi specifici
51        if (element.hasAttribute('data-waiting-list') || 
52            element.classList.contains('waiting-list') ||
53            element.classList.contains('notify-when-available') ||
54            element.classList.contains('jump-waiting-list-btn')) {
55            return true;
56        }
57        
58        // Controlla il testo del pulsante
59        const hasKeyword = waitingListKeywords.some(keyword => 
60            buttonText.includes(keyword) || buttonHTML.includes(keyword)
61        );
62        
63        if (hasKeyword) {
64            return true;
65        }
66        
67        return false;
68    }
69
70    /**
71     * Estrae i dati del prodotto dal componente jump-card-ecommerce
72     * @param {Element} clickedElement - Elemento che ha scatenato l'evento
73     * @returns {Object} Dati del prodotto/variazione
74     */
75    function extractProductFromJumpCard(clickedElement = null) {
76        let productData = {
77            product_id: null,
78            variation_id: null,
79            product_name: '',
80            variation_name: '',
81            sku: ''
82        };
83
84        let jumpCardElement = null;
85
86        // Se abbiamo un elemento cliccato, cerca il jump-card-ecommerce genitore
87        if (clickedElement) {
88            jumpCardElement = clickedElement.closest('jump-card-ecommerce');
89        }
90
91        // Se non trovato, cerca il primo jump-card-ecommerce nella pagina
92        if (!jumpCardElement) {
93            jumpCardElement = document.querySelector('jump-card-ecommerce');
94        }
95
96        if (jumpCardElement) {
97            
98            // Estrai i dati dal componente jump-card-ecommerce
99            // Prova tutti i possibili modi per ottenere l'ID prodotto
100            const productId = jumpCardElement.getAttribute('product-id') || 
101                             jumpCardElement.getAttribute('productid') ||
102                             jumpCardElement.getAttribute('data-product-id') ||
103                             jumpCardElement.dataset.productId ||
104                             jumpCardElement.dataset.product ||
105                             jumpCardElement.productId ||
106                             jumpCardElement.product_id;
107            
108            // Prova tutti i possibili modi per ottenere l'ID variazione
109            const variationId = jumpCardElement.getAttribute('variation-id') || 
110                               jumpCardElement.getAttribute('variationid') ||
111                               jumpCardElement.getAttribute('data-variation-id') ||
112                               jumpCardElement.dataset.variationId ||
113                               jumpCardElement.dataset.variation ||
114                               jumpCardElement.variationId ||
115                               jumpCardElement.variation_id;
116            
117            // Prova tutti i possibili modi per ottenere il nome prodotto
118            const productName = jumpCardElement.getAttribute('product-name') || 
119                               jumpCardElement.getAttribute('productname') ||
120                               jumpCardElement.getAttribute('data-product-name') ||
121                               jumpCardElement.dataset.productName ||
122                               jumpCardElement.dataset.name ||
123                               jumpCardElement.productName ||
124                               jumpCardElement.product_name;
125            
126            // Prova tutti i possibili modi per ottenere lo SKU
127            const sku = jumpCardElement.getAttribute('sku') || 
128                       jumpCardElement.getAttribute('data-sku') ||
129                       jumpCardElement.dataset.sku ||
130                       jumpCardElement.sku;
131
132            if (productId) {
133                productData.product_id = productId;
134            }
135
136            if (variationId && variationId !== '0') {
137                productData.variation_id = variationId;
138            }
139
140            if (productName) {
141                productData.product_name = productName;
142            }
143
144            if (sku) {
145                productData.sku = sku;
146            }
147
148            // Tentativo robusto: ricava la variante corrente dagli attributi "data-variation" e "variations"
149            // molti card espongono: data-variation='{"combined_variation":"{\"attribute_pa_x\":\"valore\"}"}'
150            // e variations='[{"variation_id":123, "raw_attributes":{"attribute_pa_x":"valore"}, ...}, ...]'
151            try {
152                // 1) leggi la variazione "corrente" dai dati del card
153                const dataVariationAttr = jumpCardElement.getAttribute('data-variation') || (jumpCardElement.dataset ? jumpCardElement.dataset.variation : null);
154                let selectedAttributes = null;
155                if (dataVariationAttr) {
156                    const parsed = JSON.parse(dataVariationAttr);
157                    if (parsed && parsed.combined_variation) {
158                        // combined_variation è una stringa JSON: va riparsata
159                        try {
160                            selectedAttributes = JSON.parse(parsed.combined_variation);
161                        } catch (e) {
162                            // fallback: ignora
163                        }
164                    }
165                }
166
167                // 2) cerca nelle varianti esposte sul card quella che matcha gli attributi selezionati
168                const variationsAttr = jumpCardElement.getAttribute('variations');
169                if (!productData.variation_id && variationsAttr) {
170                    const variations = JSON.parse(variationsAttr);
171                    if (Array.isArray(variations) && variations.length) {
172                        let match = null;
173                        if (selectedAttributes && Object.keys(selectedAttributes).length) {
174                            match = variations.find(v => {
175                                // prova raw_attributes prima
176                                if (v && v.raw_attributes) {
177                                    // confronto chiave-valore
178                                    return Object.keys(selectedAttributes).every(k => {
179                                        return (v.raw_attributes && v.raw_attributes[k] === selectedAttributes[k]);
180                                    });
181                                }
182                                return false;
183                            });
184                            // ulteriore fallback su combined_attributes stringificati
185                            if (!match && selectedAttributes) {
186                                const selectedStr = JSON.stringify(selectedAttributes);
187                                match = variations.find(v => v && v.combined_attributes && v.combined_attributes === selectedStr);
188                            }
189                        }
190
191                        // ultimo fallback: se non abbiamo attributi, usa la variante marcata out-of-stock nel card,
192                        // oppure semplicemente la prima variante
193                        if (!match) {
194                            match = variations[0];
195                        }
196
197                        if (match && match.variation_id) {
198                            productData.variation_id = match.variation_id;
199                        }
200                    }
201                }
202            } catch (e) {
203                // console.debug('Impossibile derivare variation_id da jump-card-ecommerce:', e);
204            }
205
206            // Prova a cercare attributi di variazione nel componente
207            const variationAttributes = jumpCardElement.querySelector('[data-variation-attributes]');
208            if (variationAttributes) {
209                try {
210                    const attributes = JSON.parse(variationAttributes.dataset.variationAttributes);
211                    const attributeLabels = Object.entries(attributes).map(([key, value]) => {
212                        return `${key.replace('attribute_pa_', '').replace('_', ' ')}: ${value}`;
213                    });
214                    productData.variation_name = attributeLabels.join(', ');
215                } catch (e) {
216                    // Errore nel parsing, ignora
217                }
218            }
219        }
220
221        // Se non abbiamo trovato dati dal jump-card, usa il fallback tradizionale
222        if (!productData.product_id) {
223            return extractProductFromDOM();
224        }
225
226        return productData;
227    }
228
229    /**
230     * Estrae i dati del prodotto direttamente dall'elemento cliccato (data-attributes)
231     * @param {Element} clickedElement - Elemento che ha scatenato l'evento
232     * @returns {Object} Dati del prodotto/variazione
233     */
234    function extractProductFromElement(clickedElement = null) {
235        let productData = {
236            product_id: null,
237            variation_id: null,
238            product_name: '',
239            variation_name: '',
240            sku: ''
241        };
242
243        if (!clickedElement) {
244            return productData;
245        }
246
247        const el = clickedElement;
248
249        // Prova tutti i possibili modi per ottenere l'ID prodotto
250        const productId = el.getAttribute('data-product-id') ||
251                          (el.dataset ? el.dataset.productId : null) ||
252                          el.getAttribute('product-id') ||
253                          el.getAttribute('productid') ||
254                          (el.dataset ? el.dataset.product : null) ||
255                          el.productId ||
256                          el.product_id;
257
258        // Prova tutti i possibili modi per ottenere l'ID variazione
259        const variationId = el.getAttribute('data-variation-id') ||
260                            (el.dataset ? el.dataset.variationId : null) ||
261                            el.getAttribute('variation-id') ||
262                            el.getAttribute('variationid') ||
263                            (el.dataset ? el.dataset.variation : null) ||
264                            el.variationId ||
265                            el.variation_id;
266
267        // Prova tutti i possibili modi per ottenere il nome prodotto
268        const productName = el.getAttribute('data-product-name') ||
269                            (el.dataset ? el.dataset.productName : null) ||
270                            el.getAttribute('product-name') ||
271                            el.getAttribute('productname') ||
272                            (el.dataset ? el.dataset.name : null) ||
273                            el.productName ||
274                            el.product_name;
275
276        if (productId) {
277            productData.product_id = productId;
278        }
279
280        if (variationId && variationId !== '0') {
281            productData.variation_id = variationId;
282        }
283
284        if (productName) {
285            productData.product_name = productName;
286        }
287
288        return productData;
289    }
290
291    /**
292     * Verifica se i dati contengono un product_id valido
293     * @param {Object|null} data
294     * @returns {boolean}
295     */
296    function hasValidProductId(data) {
297        return !!(data && data.product_id && data.product_id !== 0 && data.product_id !== '0');
298    }
299
300    /**
301     * Estrae i dati del prodotto dal DOM come fallback
302     * @returns {Object} Dati del prodotto/variazione
303     */
304    function extractProductFromDOM() {
305        let productData = {
306            product_id: null,
307            variation_id: null,
308            product_name: '',
309            variation_name: '',
310            sku: ''
311        };
312
313        // Cerca l'ID del prodotto
314        const productIdInput = document.querySelector('input[name="product_id"]');
315        const addToCartForm = document.querySelector('.cart');
316        
317        if (productIdInput) {
318            productData.product_id = productIdInput.value;
319        } else if (addToCartForm && addToCartForm.dataset.productId) {
320            productData.product_id = addToCartForm.dataset.productId;
321        }
322
323        // Cerca l'ID della variazione
324        const variationIdInput = document.querySelector('input[name="variation_id"]');
325        const variationSelect = document.querySelector('.variations_form');
326        
327        if (variationIdInput && variationIdInput.value) {
328            productData.variation_id = variationIdInput.value;
329        } else if (variationSelect) {
330            // Prova a cercare la variazione selezionata dai dropdown
331            const attributeSelects = variationSelect.querySelectorAll('select[name^="attribute_"]');
332            const selectedAttributes = {};
333            
334            attributeSelects.forEach(select => {
335                if (select.value) {
336                    selectedAttributes[select.name] = select.value;
337                }
338            });
339        }
340
341        // Cerca il nome del prodotto
342        const productTitle = document.querySelector('.product_title, .entry-title, h1.product-title, .product-name h1');
343        if (productTitle) {
344            productData.product_name = productTitle.textContent.trim();
345        }
346
347        // Cerca SKU
348        const skuElement = document.querySelector('.sku, .product_meta .sku_wrapper .sku');
349        if (skuElement) {
350            productData.sku = skuElement.textContent.trim();
351        }
352
353        // Cerca informazioni sulla variazione selezionata
354        const variationDescription = document.querySelector('.woocommerce-variation-description');
355        const selectedVariationText = document.querySelector('.single_variation_wrap .single_variation');
356        
357        if (selectedVariationText && selectedVariationText.style.display !== 'none') {
358            const variationPrice = selectedVariationText.querySelector('.price');
359            if (variationPrice) {
360                productData.variation_name = 'Variazione selezionata';
361            }
362        }
363
364        // Costruisci il nome completo della variazione dalle selezioni
365        const attributeLabels = [];
366        document.querySelectorAll('.variations tr').forEach(row => {
367            const label = row.querySelector('label');
368            const select = row.querySelector('select');
369            
370            if (label && select && select.value) {
371                const optionText = select.options[select.selectedIndex].text;
372                if (optionText && optionText !== 'Scegli un\'opzione') {
373                    attributeLabels.push(`${label.textContent.replace(':', '').trim()}: ${optionText}`);
374                }
375            }
376        });
377
378        if (attributeLabels.length > 0) {
379            productData.variation_name = attributeLabels.join(', ');
380        }
381
382        return productData;
383    }
384
385    /**
386     * Aggiorna il display delle informazioni prodotto nel popup
387     * @param {Object} productData - Dati del prodotto
388     */
389    function updateProductDisplay(productData) {
390        const productInfoEl = document.querySelector(selectors.productInfo);
391        if (!productInfoEl) return;
392
393        const productNameEl = productInfoEl.querySelector('.jump-waiting-list__product-name');
394        const variationEl = productInfoEl.querySelector('.jump-waiting-list__product-variation');
395
396        if (productNameEl) {
397            productNameEl.textContent = productData.product_name || 'Prodotto selezionato';
398        }
399
400        if (variationEl) {
401            if (productData.variation_name) {
402                variationEl.textContent = productData.variation_name;
403                variationEl.style.display = 'block';
404            } else {
405                variationEl.style.display = 'none';
406            }
407        }
408        
409        // Aggiorna anche i campi hidden del form
410        updateFormHiddenFields(productData);
411    }
412
413    /**
414     * Aggiorna i campi hidden del form con i dati del prodotto
415     * @param {Object} productData - Dati del prodotto
416     */
417    function updateFormHiddenFields(productData) {
418        const productIdField = document.querySelector('#jump-waiting-list-product-id');
419        const variationIdField = document.querySelector('#jump-waiting-list-variation-id');
420        
421        if (productIdField) {
422            productIdField.value = productData.product_id || '';
423        }
424        
425        if (variationIdField) {
426            variationIdField.value = productData.variation_id || '';
427        }
428    }
429
430    /**
431     * Apre il popup con i dati del prodotto
432     * @param {Object} productData - Dati del prodotto (opzionale)
433     * @param {Element} clickedElement - Elemento che ha scatenato l'evento (opzionale)
434     */
435    function openPopup(productData = null, clickedElement = null) {
436        // Assicurati che il popup HTML esista
437        ensurePopupHTML();
438        
439        // Se il popup è già aperto, prima resetta i dati
440        if (popupState.isOpen) {
441            resetPopupForm();
442        }
443        
444        // Se non sono stati forniti dati del prodotto validi, prova a estrarli
445        if (!hasValidProductId(productData)) {
446            // 1) Tenta dagli attributi del pulsante cliccato
447            productData = extractProductFromElement(clickedElement);
448        }
449
450        if (!hasValidProductId(productData)) {
451            // 2) Tenta dal componente jump-card-ecommerce
452            productData = extractProductFromJumpCard(clickedElement);
453        }
454
455        // Verifica che abbiamo almeno un ID prodotto
456        if (!productData.product_id || productData.product_id === 0 || productData.product_id === '0') {
457            console.error('Unable to determine product ID');
458            alert('Errore: impossibile determinare il prodotto. Ricarica la pagina e riprova.');
459            return;
460        }
461
462        // Converti gli ID in numeri se sono stringhe
463        if (typeof productData.product_id === 'string') {
464            productData.product_id = parseInt(productData.product_id, 10);
465        }
466        if (productData.variation_id && typeof productData.variation_id === 'string') {
467            productData.variation_id = parseInt(productData.variation_id, 10);
468        }
469
470        popupState.currentProductData = productData;
471        
472        // Aggiorna il display del prodotto e i campi hidden
473        updateProductDisplay(productData);
474
475        // Mostra il popup
476        const backdrop = document.querySelector(selectors.backdrop);
477        
478        if (backdrop) {
479            backdrop.classList.add('jump-waiting-list__backdrop--visible');
480            popupState.isOpen = true;
481            
482            // Focus sull'input email
483            setTimeout(() => {
484                const emailInput = document.querySelector(selectors.emailInput);
485                if (emailInput) {
486                    emailInput.focus();
487                }
488            }, 300);
489        } else {
490            console.error('Backdrop element not found! Popup cannot be shown.');
491        }
492    }
493
494    /**
495     * Chiude il popup
496     */
497    function closePopup() {
498        const backdrop = document.querySelector(selectors.backdrop);
499        if (backdrop) {
500            backdrop.classList.remove('jump-waiting-list__backdrop--visible');
501            popupState.isOpen = false;
502            popupState.currentProductData = null;
503            
504            // Rimuovi l'overlay di successo se presente
505            const popup = document.querySelector(selectors.popup);
506            if (popup) {
507                const successOverlay = popup.querySelector('.jump-waiting-list__success-overlay');
508                if (successOverlay) {
509                    successOverlay.remove();
510                }
511            }
512            
513            // Reset completo del form e dei dati
514            resetPopupForm();
515        }
516    }
517
518    /**
519     * Resetta completamente il form del popup
520     */
521    function resetPopupForm() {
522        const form = document.querySelector(selectors.form);
523        if (form) {
524            form.reset();
525            clearErrors();
526        }
527        
528        // Reset dei campi hidden
529        const productIdField = document.querySelector('#jump-waiting-list-product-id');
530        const variationIdField = document.querySelector('#jump-waiting-list-variation-id');
531        
532        if (productIdField) {
533            productIdField.value = '';
534        }
535        
536        if (variationIdField) {
537            variationIdField.value = '';
538        }
539        
540        // Reset del display prodotto
541        const productInfoEl = document.querySelector(selectors.productInfo);
542        if (productInfoEl) {
543            const productNameEl = productInfoEl.querySelector('.jump-waiting-list__product-name');
544            const variationEl = productInfoEl.querySelector('.jump-waiting-list__product-variation');
545            
546            if (productNameEl) {
547                productNameEl.textContent = 'Prodotto';
548            }
549            
550            if (variationEl) {
551                variationEl.textContent = '';
552                variationEl.style.display = 'none';
553            }
554        }
555    }
556
557    /**
558     * Pulisce tutti i messaggi di errore
559     */
560    function clearErrors() {
561        document.querySelectorAll('.jump-waiting-list__error').forEach(error => {
562            error.remove();
563        });
564        
565        document.querySelectorAll('.jump-waiting-list__input--error').forEach(input => {
566            input.classList.remove('jump-waiting-list__input--error');
567        });
568    }
569
570    /**
571     * Mostra un errore per un campo specifico
572     * @param {string} fieldSelector - Selettore del campo
573     * @param {string} message - Messaggio di errore
574     */
575    function showFieldError(fieldSelector, message) {
576        const field = document.querySelector(fieldSelector);
577        if (!field) return;
578
579        // Rimuovi errori esistenti per questo campo
580        const existingError = field.parentNode.querySelector('.jump-waiting-list__error');
581        if (existingError) {
582            existingError.remove();
583        }
584
585        // Aggiungi classe di errore
586        field.classList.add('jump-waiting-list__input--error');
587
588        // Crea e aggiungi il messaggio di errore
589        const errorEl = document.createElement('div');
590        errorEl.className = 'jump-waiting-list__error';
591        errorEl.textContent = message;
592        field.parentNode.appendChild(errorEl);
593    }
594
595    /**
596     * Valida il form
597     * @returns {boolean} True se il form è valido
598     */
599    function validateForm() {
600        let isValid = true;
601        clearErrors();
602
603        const emailInput = document.querySelector(selectors.emailInput);
604        const termsCheckbox = document.querySelector(selectors.termsCheckbox);
605
606        // Validazione email
607        if (!emailInput.value.trim()) {
608            showFieldError(selectors.emailInput, 'L\'email è obbligatoria');
609            isValid = false;
610        } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput.value.trim())) {
611            showFieldError(selectors.emailInput, 'Inserisci un indirizzo email valido');
612            isValid = false;
613        }
614
615        // Validazione privacy policy
616        if (!termsCheckbox.checked) {
617            showFieldError(selectors.termsCheckbox, 'Devi accettare la privacy policy');
618            isValid = false;
619        }
620
621        return isValid;
622    }
623
624    /**
625     * Mostra il messaggio di successo
626     */
627    function showSuccessMessage() {
628        const popup = document.querySelector(selectors.popup);
629        if (!popup) return;
630
631        // Invece di sostituire tutto il contenuto, mostra solo un messaggio temporaneo
632        const existingMessage = popup.querySelector('.jump-waiting-list__success-overlay');
633        if (existingMessage) {
634            existingMessage.remove();
635        }
636
637        // Crea un overlay di successo che non distrugge il form
638        const successOverlay = document.createElement('div');
639        successOverlay.className = 'jump-waiting-list__success-overlay';
640        successOverlay.style.cssText = `
641            position: absolute;
642            top: 0;
643            left: 0;
644            right: 0;
645            bottom: 0;
646            background: rgba(255, 255, 255, 0.95);
647            display: flex;
648            align-items: center;
649            justify-content: center;
650            z-index: 1000;
651            border-radius: inherit;
652        `;
653        
654        successOverlay.innerHTML = `
655            <div class="jump-waiting-list__success" style="text-align: center; padding: 2rem;">
656                <h3 class="jump-waiting-list__success-title">✅ Iscrizione completata!</h3>
657                <p class="jump-waiting-list__success-message">
658                    Ti invieremo una notifica quando il prodotto sarà nuovamente disponibile.
659                </p>
660                <p style="font-size: 0.875rem; color: #666; margin-top: 1rem;">
661                    Il popup si chiuderà automaticamente...
662                </p>
663            </div>
664        `;
665
666        popup.appendChild(successOverlay);
667
668        // Chiudi automaticamente dopo 3 secondi e resetta per permettere nuove iscrizioni
669        setTimeout(() => {
670            closePopup();
671            // Il popup è ora pronto per essere utilizzato con un altro prodotto
672        }, 3000);
673    }
674
675    /**
676     * Gestisce l'invio del form
677     * @param {Event} e - Evento submit
678     */
679    function handleFormSubmit(e) {
680        e.preventDefault();
681        
682        if (!validateForm()) {
683            return;
684        }
685
686        const submitBtn = document.querySelector(selectors.submitBtn);
687        const emailInput = document.querySelector(selectors.emailInput);
688        const termsCheckbox = document.querySelector(selectors.termsCheckbox);
689        
690        // Stato di caricamento
691        submitBtn.disabled = true;
692        submitBtn.classList.add('jump-waiting-list__submit--loading');
693
694        // Prepara i dati da inviare
695        const formData = new FormData();
696        formData.append('action', 'jump_waiting_list_subscribe');
697        formData.append('email', emailInput.value.trim());
698        formData.append('product_id', popupState.currentProductData.product_id);
699        formData.append('variation_id', popupState.currentProductData.variation_id || '');
700        formData.append('terms', termsCheckbox.checked ? '1' : '0');
701        formData.append('nonce', jumpWaitingList.nonce);
702
703        // Invio AJAX
704        fetch(jumpWaitingList.ajaxUrl, {
705            method: 'POST',
706            body: formData
707        })
708        .then(response => response.json())
709        .then(data => {
710            if (data.success) {
711                showSuccessMessage();
712            } else {
713                // Gestisci correttamente l'errore del server
714                const errorMessage = data.data && data.data.message ? data.data.message : 'Errore durante l\'iscrizione';
715                alert('Errore: ' + errorMessage);
716                
717                // Reset dei campi email per permettere una nuova iscrizione
718                // (manteniamo i dati del prodotto per permettere di riprovare)
719                const emailField = document.querySelector(selectors.emailInput);
720                if (emailField) {
721                    emailField.value = '';
722                }
723            }
724        })
725        .catch(error => {
726            console.error('❌ Network/Parse error:', error);
727            alert('Errore di connessione: ' + error.message);
728        })
729        .finally(() => {
730            // Ripristina il pulsante
731            submitBtn.disabled = false;
732            submitBtn.classList.remove('jump-waiting-list__submit--loading');
733        });
734    }
735
736    /**
737     * Inizializza gli event listener
738     */
739    function initEventListeners() {
740        // Event listener per l'evento personalizzato
741        document.addEventListener('jump-add-to-waiting-list', function(e) {
742            // Passa sia i dati dell'evento che l'elemento target
743            openPopup(e.detail, e.target);
744        });
745
746        // Event listener specifico per jump-button.Footer__AddToCart
747        document.addEventListener('click', function(e) {
748            // Gestione chiusura popup
749            if (e.target.matches(selectors.closeBtn) || e.target.closest(selectors.closeBtn)) {
750                closePopup();
751                return;
752            } else if (e.target.matches(selectors.backdrop)) {
753                closePopup();
754                return;
755            }
756
757            // Intercetta click su qualsiasi jump-button (non solo Footer__AddToCart)
758            const jumpButton = e.target.closest('jump-button');
759            if (jumpButton) {
760                // Usa la funzione per verificare se è un pulsante waiting list
761                if (isWaitingListButton(jumpButton)) {
762                    e.preventDefault();
763                    e.stopPropagation();
764                    
765                    // Estrai i dati dal jump-card-ecommerce genitore
766                    openPopup(null, jumpButton);
767                    return;
768                }
769            }
770
771            // Gestione click su altri pulsanti waiting list (fallback)
772            const waitingListBtn = e.target.closest('[data-waiting-list], .waiting-list-btn, .jump-waiting-list-trigger, .jump-waiting-list-btn');
773            if (waitingListBtn && isWaitingListButton(waitingListBtn)) {
774                e.preventDefault();
775                
776                // Estrai i dati dal pulsante o dal suo jump-card genitore
777                openPopup(null, waitingListBtn);
778            }
779        });
780
781        // Event listener per ESC
782        document.addEventListener('keydown', function(e) {
783            if (e.key === 'Escape' && popupState.isOpen) {
784                closePopup();
785            }
786        });
787
788        // Event listener per il form
789        document.addEventListener('submit', function(e) {
790            if (e.target.matches(selectors.form)) {
791                handleFormSubmit(e);
792            }
793        });
794
795        // Event listener per rimuovere errori quando l'utente digita
796        document.addEventListener('input', function(e) {
797            if (e.target.matches(selectors.emailInput)) {
798                e.target.classList.remove('jump-waiting-list__input--error');
799                const error = e.target.parentNode.querySelector('.jump-waiting-list__error');
800                if (error) {
801                    error.remove();
802                }
803            }
804        });
805
806        document.addEventListener('change', function(e) {
807            if (e.target.matches(selectors.termsCheckbox)) {
808                const error = e.target.parentNode.querySelector('.jump-waiting-list__error');
809                if (error) {
810                    error.remove();
811                }
812            }
813        });
814    }
815
816    /**
817     * Crea il markup HTML del popup se non esiste
818     */
819    function ensurePopupHTML() {
820        if (document.querySelector(selectors.backdrop)) {
821            return;
822        }
823        
824        const popupHTML = `
825            <div class="jump-waiting-list__backdrop">
826                <div class="jump-waiting-list__popup">
827                    <button type="button" class="jump-waiting-list__close" aria-label="Chiudi">&times;</button>
828                    
829                    <div class="jump-waiting-list__header">
830                        <h2 class="jump-waiting-list__title">Avvisami quando disponibile</h2>
831                        <p class="jump-waiting-list__subtitle">Ti invieremo una notifica quando il prodotto sarà nuovamente in stock</p>
832                    </div>
833
834                    <div class="jump-waiting-list__product-info">
835                        <div class="jump-waiting-list__product-name">Prodotto</div>
836                        <div class="jump-waiting-list__product-variation" style="display: none;">Variazione</div>
837                    </div>
838
839                    <form class="jump-waiting-list__form">
840                        <div class="jump-waiting-list__field">
841                            <label for="jump-waiting-list-email" class="jump-waiting-list__label">Email *</label>
842                            <input type="email" id="jump-waiting-list-email" class="jump-waiting-list__input" required>
843                        </div>
844
845                        <div class="jump-waiting-list__checkbox-field">
846                            <input type="checkbox" id="jump-waiting-list-terms" class="jump-waiting-list__checkbox" required>
847                            <label for="jump-waiting-list-terms" class="jump-waiting-list__checkbox-label">
848                                Accetto la <a href="/privacy-policy" target="_blank">privacy policy</a> e acconsento al trattamento dei miei dati personali
849                            </label>
850                        </div>
851
852                        <button type="submit" class="jump-waiting-list__submit">Iscrivimi alla waiting list</button>
853                    </form>
854                </div>
855            </div>
856        `;
857
858        document.body.insertAdjacentHTML('beforeend', popupHTML);
859    }
860
861    /**
862     * Inizializzazione principale
863     */
864    function init() {
865        // Attendi che il DOM sia pronto
866        if (document.readyState === 'loading') {
867            document.addEventListener('DOMContentLoaded', function() {
868                ensurePopupHTML();
869                initEventListeners();
870            });
871        } else {
872            ensurePopupHTML();
873            initEventListeners();
874        }
875    }
876
877    // Avvia l'inizializzazione
878    init();
879
880})();

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.