PageSourceSearch

https://www.accademiamoda.it/wp-content/themes/iuad-theme/src/js/custom/richiesta-info-form.js?ver=1784801684

js accademiamoda.it collected 2026-10-04 13:24:26 UTC 30,839 bytes, 747 lines download raw bytes

1/**
2 * Form Richiesta Info - Dynamic Filtering
3 * Gestisce il filtering dinamico tra sede e corsi
4 */
5
6document.addEventListener('DOMContentLoaded', function() {
7    const sedeSelect = document.getElementById('sede');
8    const corsoSelect = document.getElementById('corso');
9    const tipoRichiestaSelect = document.getElementById('tipo_richiesta');
10    const dataOrarioSelect = document.getElementById('data_orario');
11    
12    if (!sedeSelect || !corsoSelect || !tipoRichiestaSelect || !dataOrarioSelect) {
13        return;
14    }
15    
16    // Snapshot delle option (clone) per poter ricostruire i select senza perdere data-*
17    const allCorsiOptions = Array.from(corsoSelect.options).map(function(opt) {
18        return opt.cloneNode(true);
19    });
20    const allSediOptions = Array.from(sedeSelect.options).map(function(opt) {
21        return opt.cloneNode(true);
22    });
23    
24    // Salva le opzioni del tipo richiesta
25    const openDayOption = tipoRichiestaSelect.querySelector('option[value="open_day"]');
26    const orientamentoOption = tipoRichiestaSelect.querySelector('option[value="orientamento"]');
27
28    function parseIdList(attr) {
29        if (!attr) {
30            return [];
31        }
32        try {
33            const parsed = JSON.parse(attr);
34            if (!Array.isArray(parsed)) {
35                return [];
36            }
37            return parsed
38                .map(function(id) { return parseInt(id, 10); })
39                .filter(function(id) { return !Number.isNaN(id); });
40        } catch (e) {
41            console.error('Errore parsing ID list:', e);
42            return [];
43        }
44    }
45
46    function findOptionByValue(options, value) {
47        const valueStr = String(value || '');
48        return options.find(function(opt) { return String(opt.value) === valueStr; }) || null;
49    }
50
51    // Helpers per formattare data/ora come in open-day-form.js
52    const monthsFallback = [
53        'Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno',
54        'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'
55    ];
56
57    function getMonths() {
58        const i18nMonths = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n && iuadRichiestaInfo.i18n.months) ? iuadRichiestaInfo.i18n.months : null;
59        return (Array.isArray(i18nMonths) && i18nMonths.length === 12) ? i18nMonths : monthsFallback;
60    }
61
62    function formatDate(dateString) {
63        if (!dateString) return '';
64        const date = new Date(dateString);
65        const day = date.getDate();
66        const months = getMonths();
67        const month = months[date.getMonth()];
68        const year = date.getFullYear();
69        return `${day} ${month} ${year}`;
70    }
71
72    function formatTime(timeString) {
73        if (!timeString) return '';
74        return timeString; // già in formato HH:MM
75    }
76
77    function resolveOpenDayDateBucket(sedeOption) {
78        if (!sedeOption || !sedeOption.value) {
79            return null;
80        }
81        const fromAttr = sedeOption.getAttribute('data-open-day-dates-key');
82        if (fromAttr === 'milano' || fromAttr === 'napoli') {
83            return fromAttr;
84        }
85        const label = String(sedeOption.textContent || '').toLowerCase();
86        if (label.includes('milano') || label.includes('milan')) {
87            return 'milano';
88        }
89        if (label.includes('napoli') || label.includes('naples')) {
90            return 'napoli';
91        }
92        return null;
93    }
94
95    function openDayTipoLabelFromCardType(type, isEnglish) {
96        const i18n = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n) ? iuadRichiestaInfo.i18n : {};
97        let label;
98        switch (type) {
99            case 'online':
100                label = i18n.openDayTipoOnline || 'Online';
101                break;
102            case 'in_sede_e_online':
103                label = i18n.openDayTipoInSedeEOnline || 'In sede e Online';
104                break;
105            case 'in_sede':
106            default:
107                label = i18n.openDayTipoInSede || 'In sede';
108                break;
109        }
110        if (isEnglish) {
111            const suffix = i18n.openDayTipoInEnglishSuffix || ', in inglese';
112            label += suffix;
113        }
114        return label;
115    }
116    
117    /**
118     * Filtra i corsi in base alla sede selezionata e al tipo richiesta:
119     * - orientamento: corsi per sede (senza vincoli sulle date Open Day)
120     * - open_day: solo corsi con data-has-openday="1" e (se sede Milano/Napoli) almeno 1 data per quella sede
121     */
122    function filterCorsi(sedeId, tipoRichiesta) {
123        // Rimuovi tutte le opzioni tranne la prima (placeholder)
124        while (corsoSelect.options.length > 1) {
125            corsoSelect.remove(1);
126        }
127        
128        const mustHaveOpenDay = (tipoRichiesta === 'open_day');
129        const shouldCheckDatesForSede = mustHaveOpenDay; // le date contano solo per Open Day
130
131        const sedeOption = sedeId ? sedeSelect.querySelector(`option[value="${sedeId}"]`) : null;
132        const dateBucket = resolveOpenDayDateBucket(sedeOption);
133        const requireMilanoDates = shouldCheckDatesForSede && dateBucket === 'milano';
134        const requireNapoliDates = shouldCheckDatesForSede && dateBucket === 'napoli';
135
136        // Filtra e aggiungi corsi
137        allCorsiOptions.forEach((option, index) => {
138            if (index === 0) return; // Skip placeholder
139
140            if (mustHaveOpenDay && option.getAttribute('data-has-openday') !== '1') {
141                return;
142            }
143
144            // Se nessuna sede è selezionata, includi (eventualmente filtrato per Open Day)
145            if (!sedeId) {
146                corsoSelect.add(option.cloneNode(true));
147                return;
148            }
149
150            const sediArray = parseIdList(option.getAttribute('data-sedi'));
151            if (sediArray.includes(parseInt(sedeId, 10))) {
152                // Se stiamo filtrando Open Day per Milano/Napoli, includi solo corsi con almeno 1 data per quella sede
153                if (requireMilanoDates || requireNapoliDates) {
154                    const dateAttr = requireMilanoDates
155                        ? option.getAttribute('data-date-milano')
156                        : option.getAttribute('data-date-napoli');
157
158                    let dateArray = [];
159                    if (dateAttr) {
160                        try {
161                            dateArray = JSON.parse(dateAttr);
162                        } catch (e) {
163                            dateArray = [];
164                        }
165                    }
166
167                    if (Array.isArray(dateArray) && dateArray.length > 0) {
168                        corsoSelect.add(option.cloneNode(true));
169                    }
170                } else {
171                    corsoSelect.add(option.cloneNode(true));
172                }
173            }
174        });
175        
176        // Se non ci sono corsi per questa sede, mostra un messaggio
177        if (corsoSelect.options.length === 1) {
178            const noCorsiOption = document.createElement('option');
179            const i18n = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n) ? iuadRichiestaInfo.i18n : {};
180            const fallbackNoCourses = 'Nessun corso disponibile per questa sede';
181            const fallbackNoOpenDayCourses = 'Nessun corso con Open Day disponibile per questa sede';
182            noCorsiOption.textContent = mustHaveOpenDay
183                ? (i18n.noOpenDayCoursesForSede || fallbackNoOpenDayCourses)
184                : (i18n.noCoursesForSede || fallbackNoCourses);
185            noCorsiOption.disabled = true;
186            corsoSelect.add(noCorsiOption);
187        }
188    }
189    
190    /**
191     * Filtra le sedi in base al corso selezionato (data-sedi).
192     * Se nessuna sede corrente è valida, seleziona automaticamente quando possibile.
193     */
194    function filterSedi(corsoId) {
195        const currentSedeId = sedeSelect.value;
196
197        while (sedeSelect.options.length > 1) {
198            sedeSelect.remove(1);
199        }
200
201        if (!corsoId) {
202            allSediOptions.forEach(function(option, index) {
203                if (index === 0) return;
204                sedeSelect.add(option.cloneNode(true));
205            });
206            if (currentSedeId && Array.from(sedeSelect.options).some(function(opt) {
207                return String(opt.value) === String(currentSedeId);
208            })) {
209                sedeSelect.value = currentSedeId;
210            }
211            return;
212        }
213
214        const corsoOption = findOptionByValue(allCorsiOptions, corsoId);
215        if (!corsoOption) {
216            return;
217        }
218
219        const sediArray = parseIdList(corsoOption.getAttribute('data-sedi'));
220        allSediOptions.forEach(function(option, index) {
221            if (index === 0) return;
222            if (sediArray.includes(parseInt(option.value, 10))) {
223                sedeSelect.add(option.cloneNode(true));
224            }
225        });
226
227        if (sediArray.length === 1) {
228            sedeSelect.value = String(sediArray[0]);
229        } else if (sediArray.includes(parseInt(currentSedeId, 10))) {
230            sedeSelect.value = currentSedeId;
231        } else if (sediArray.length > 1) {
232            sedeSelect.value = String(sediArray[0]);
233        } else {
234            sedeSelect.value = '';
235        }
236    }
237
238    /**
239     * @deprecated mantenuto come alias per chiarezza nei call site legacy
240     */
241    function updateSedeByCorso(corsoId) {
242        filterSedi(corsoId);
243    }
244    
245    /**
246     * Aggiorna il campo "Data e orario" in base al corso, alla sede e al tipo di richiesta
247     */
248    function updateDataOrarioByCorsoAndSede(corsoId, sedeId, tipoRichiesta) {
249        // Rimuovi tutte le opzioni tranne il placeholder
250        while (dataOrarioSelect.options.length > 1) {
251            dataOrarioSelect.remove(1);
252        }
253        
254        // Se il tipo richiesta è "orientamento", mostra solo "Orientamento personalizzato"
255        if (tipoRichiesta === 'orientamento') {
256            const orientamentoOpt = document.createElement('option');
257        orientamentoOpt.value = 'orientamento_personalizzato';
258        orientamentoOpt.textContent = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n && iuadRichiestaInfo.i18n.daDefinire)
259            ? iuadRichiestaInfo.i18n.daDefinire
260            : 'Da definire';
261            dataOrarioSelect.add(orientamentoOpt);
262            dataOrarioSelect.value = 'orientamento_personalizzato';
263            return;
264        }
265        
266        if (!corsoId || !sedeId) {
267            // Se non ci sono corso o sede selezionati, mostra solo orientamento personalizzato
268            const orientamentoOpt = document.createElement('option');
269        orientamentoOpt.value = 'orientamento_personalizzato';
270        orientamentoOpt.textContent = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n && iuadRichiestaInfo.i18n.daDefinire)
271            ? iuadRichiestaInfo.i18n.daDefinire
272            : 'Da definire';
273            dataOrarioSelect.add(orientamentoOpt);
274            dataOrarioSelect.value = 'orientamento_personalizzato';
275            return;
276        }
277        
278        // Trova l'opzione del corso selezionato
279        const corsoOption = findOptionByValue(allCorsiOptions, corsoId);
280        if (!corsoOption) return;
281        
282        const sedeOption = sedeSelect.querySelector(`option[value="${sedeId}"]`);
283        const dateBucket = resolveOpenDayDateBucket(sedeOption);
284
285        let dateData = null;
286        if (dateBucket === 'milano') {
287            dateData = corsoOption.getAttribute('data-date-milano');
288        } else if (dateBucket === 'napoli') {
289            dateData = corsoOption.getAttribute('data-date-napoli');
290        }
291        
292        let dateArray = [];
293        if (dateData) {
294            try {
295                dateArray = JSON.parse(dateData);
296            } catch (e) {
297                console.error('Errore parsing date corso:', e);
298            }
299        }
300        
301        // Se ci sono date disponibili, aggiungile
302        if (dateArray && dateArray.length > 0) {
303            dateArray.forEach(dateObj => {
304                const option = document.createElement('option');
305
306                // Formatta data/ora come open-day-form.js
307                const dataFormatted = formatDate(dateObj.data);
308                const oraFormatted = formatTime(dateObj.ora);
309
310                const valueParts = [dateObj.tipo, dateObj.data];
311                if (oraFormatted) valueParts.push(dateObj.ora);
312
313                let text = `${dateObj.tipo} - ${dataFormatted}`;
314                if (oraFormatted) text += ` - ${oraFormatted}`;
315
316                option.value = valueParts.join('_');
317                option.textContent = text;
318                dataOrarioSelect.add(option);
319            });
320        } else {
321            // Se non ci sono date, mostra "Orientamento personalizzato"
322            const orientamentoOpt = document.createElement('option');
323        orientamentoOpt.value = 'orientamento_personalizzato';
324        orientamentoOpt.textContent = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n && iuadRichiestaInfo.i18n.daDefinire)
325            ? iuadRichiestaInfo.i18n.daDefinire
326            : 'Da definire';
327            dataOrarioSelect.add(orientamentoOpt);
328            dataOrarioSelect.value = 'orientamento_personalizzato';
329        }
330    }
331    
332    /**
333     * Aggiorna il campo "Per cosa vuoi iscriverti" in base al corso selezionato
334     */
335    function updateTipoRichiestaByCorso(corsoId) {
336        // Manteniamo sempre selezionabili Open Day / Orientamento.
337        // Il filtro dei corsi avviene in base a tipo_richiesta (vedi filterCorsi).
338        void corsoId;
339    }
340    
341    /**
342     * Event listener per il cambio di sede
343     */
344    sedeSelect.addEventListener('change', function() {
345        const sedeId = this.value;
346        const currentCorsoId = corsoSelect.value;
347        
348        // Filtra i corsi in base alla sede selezionata
349        filterCorsi(sedeId, tipoRichiestaSelect.value);
350        
351        // Se il corso corrente non è più disponibile, resettalo e ripristina tutte le sedi
352        const corsoStillAvailable = Array.from(corsoSelect.options).some(function(opt) {
353            return String(opt.value) === String(currentCorsoId);
354        });
355        if (!corsoStillAvailable) {
356            corsoSelect.value = '';
357            filterSedi('');
358            if (sedeId) {
359                sedeSelect.value = sedeId;
360            }
361        }
362        
363        // Aggiorna il campo data e orario
364        updateDataOrarioByCorsoAndSede(corsoSelect.value, sedeSelect.value, tipoRichiestaSelect.value);
365    });
366    
367    /**
368     * Event listener per il cambio di corso
369     */
370    corsoSelect.addEventListener('change', function() {
371        const corsoId = this.value;
372
373        // Filtra le sedi associate al corso (e seleziona se possibile)
374        filterSedi(corsoId);
375
376        const sedeId = sedeSelect.value;
377        updateTipoRichiestaByCorso(corsoId);
378
379        // Allinea anche l'elenco corsi alla sede risultante, mantenendo la selezione corrente
380        filterCorsi(sedeId, tipoRichiestaSelect.value);
381        if (corsoId && Array.from(corsoSelect.options).some(function(opt) {
382            return String(opt.value) === String(corsoId);
383        })) {
384            corsoSelect.value = corsoId;
385        }
386
387        // Se l'utente ha già selezionato "Open Day" ma questo corso non lo supporta,
388        // non forziamo la select tipo_richiesta: deselezioniamo il corso e filtriamo l'elenco.
389        if (tipoRichiestaSelect.value === 'open_day' && corsoId) {
390            const corsoOption = findOptionByValue(allCorsiOptions, corsoId);
391            const hasOpenDay = corsoOption && corsoOption.getAttribute('data-has-openday') === '1';
392            if (!hasOpenDay) {
393                corsoSelect.value = '';
394                filterSedi('');
395                filterCorsi(sedeSelect.value, 'open_day');
396            }
397        }
398        updateDataOrarioByCorsoAndSede(corsoSelect.value, sedeSelect.value, tipoRichiestaSelect.value);
399    });
400    
401    /**
402     * Event listener per il cambio di tipo richiesta
403     */
404    tipoRichiestaSelect.addEventListener('change', function() {
405        const tipoRichiesta = this.value;
406        const currentCorsoId = corsoSelect.value;
407        const sedeId = sedeSelect.value;
408
409        // Rifiltra i corsi in base al tipo richiesta (Open Day vs Orientamento)
410        filterCorsi(sedeId, tipoRichiesta);
411
412        const corsoStillAvailable = Array.from(corsoSelect.options).some(function(opt) {
413            return String(opt.value) === String(currentCorsoId);
414        });
415        if (corsoStillAvailable) {
416            corsoSelect.value = currentCorsoId;
417            filterSedi(currentCorsoId);
418        } else {
419            corsoSelect.value = '';
420            filterSedi('');
421            if (sedeId) {
422                sedeSelect.value = sedeId;
423            }
424        }
425
426        const corsoId = corsoSelect.value;
427        updateDataOrarioByCorsoAndSede(corsoId, sedeSelect.value, tipoRichiesta);
428    });
429    
430    // Flag per evitare doppia inizializzazione quando arriviamo con parametri in query string
431    let prefilledFromOpenDayLink = false;
432
433    /**
434     * Pre-compilazione da URL (es. link dal dettaglio corso)
435     * Parametri supportati:
436     *  - open_day_course: ID del corso
437     *  - open_day_type: tipo richiesta (es. 'open_day')
438     */
439    try {
440        const urlParams = new URLSearchParams(window.location.search);
441        const prefillCourseId = urlParams.get('open_day_course');
442        const prefillType     = urlParams.get('open_day_type');
443
444        if (prefillCourseId) {
445            const corsoOptionExists = allCorsiOptions.some(function(opt) {
446                return String(opt.value) === String(prefillCourseId);
447            });
448            if (corsoOptionExists) {
449                // Imposta il corso senza triggerare il cambio sede automatico
450                corsoSelect.value = prefillCourseId;
451
452                // Aggiorna le opzioni di tipo richiesta in base al corso
453                updateTipoRichiestaByCorso(prefillCourseId);
454
455                // Forza il tipo richiesta a Open Day se consentito
456                if (prefillType === 'open_day' && openDayOption) {
457                    tipoRichiestaSelect.value = 'open_day';
458                }
459
460                filterSedi(prefillCourseId);
461                filterCorsi(sedeSelect.value, tipoRichiestaSelect.value);
462                if (Array.from(corsoSelect.options).some(function(opt) {
463                    return String(opt.value) === String(prefillCourseId);
464                })) {
465                    corsoSelect.value = prefillCourseId;
466                }
467
468                // Aggiorna il campo Data/Orario in base a corso + sede corrente
469                updateDataOrarioByCorsoAndSede(prefillCourseId, sedeSelect.value, tipoRichiestaSelect.value);
470
471                prefilledFromOpenDayLink = true;
472            }
473        }
474    } catch (e) {
475        console.error('Errore nella lettura dei parametri Open Day dalla URL:', e);
476    }
477
478    // Inizializza il filtro al caricamento della pagina
479    // Se c'è un corso preselezionato (e non siamo arrivati con parametri Open Day),
480    // aggiorna la sede, il tipo richiesta e la data/orario di conseguenza
481    if (corsoSelect.value && !prefilledFromOpenDayLink) {
482        const selectedCorso = corsoSelect.value;
483        filterSedi(selectedCorso);
484        filterCorsi(sedeSelect.value, tipoRichiestaSelect.value);
485        if (Array.from(corsoSelect.options).some(function(opt) {
486            return String(opt.value) === String(selectedCorso);
487        })) {
488            corsoSelect.value = selectedCorso;
489        }
490        updateTipoRichiestaByCorso(selectedCorso);
491        updateDataOrarioByCorsoAndSede(corsoSelect.value, sedeSelect.value, tipoRichiestaSelect.value);
492    } else if (!prefilledFromOpenDayLink) {
493        filterCorsi(sedeSelect.value, tipoRichiestaSelect.value);
494    }
495
496    /**
497     * Pre-compilazione del form quando si clicca su una card dell'Open Day Grid
498     * - Tipo di richiesta: Open Day
499     * - Sede: sede del corso della card
500     * - Corso: corso della card
501     * - Data e orario: se disponibili sulla card, vengono preselezionati
502     * La select "Data e orario" viene comunque popolata dai listener esistenti.
503     */
504    const openDayButtons = document.querySelectorAll('.open-day-grid__card-button');
505    if (openDayButtons.length) {
506        openDayButtons.forEach(button => {
507            button.addEventListener('click', function () {
508                const corsoId = this.getAttribute('data-open-day-corso');
509                const sedeId  = this.getAttribute('data-open-day-sede');
510                const dateRaw = this.getAttribute('data-open-day-date');
511                const timeRaw = this.getAttribute('data-open-day-time');
512                const type    = this.getAttribute('data-open-day-type');
513                const isEnglish = this.getAttribute('data-open-day-english') === '1';
514
515                // Imposta prima la sede (così il filtro corsi si aggiorna correttamente)
516                if (sedeId) {
517                    sedeSelect.value = sedeId;
518                    sedeSelect.dispatchEvent(new Event('change', { bubbles: true }));
519                }
520
521                // Poi imposta il corso (dopo che le opzioni sono state filtrate per sede)
522                if (corsoId) {
523                    // Verifica che l'opzione corso esista nel select corrente
524                    const corsoOptionExists = Array.from(corsoSelect.options).some(opt => opt.value === corsoId);
525                    if (corsoOptionExists) {
526                        corsoSelect.value = corsoId;
527                        corsoSelect.dispatchEvent(new Event('change', { bubbles: true }));
528                    } else {
529                        // Fallback: imposta comunque il valore e lascia che la logica di change gestisca la coerenza
530                        corsoSelect.value = corsoId;
531                        corsoSelect.dispatchEvent(new Event('change', { bubbles: true }));
532                    }
533                }
534
535                // Forza il tipo richiesta su "Open Day"
536                if (openDayOption) {
537                    tipoRichiestaSelect.value = 'open_day';
538                    tipoRichiestaSelect.dispatchEvent(new Event('change', { bubbles: true }));
539                }
540
541                // Se abbiamo data/ora dalla card, prova a preselezionare la voce corrispondente
542                if (dateRaw) {
543                    const tipoLabel = openDayTipoLabelFromCardType(type, isEnglish);
544
545                    const valueParts = [tipoLabel, dateRaw];
546                    if (timeRaw) {
547                        valueParts.push(timeRaw);
548                    }
549                    const targetValue = valueParts.join('_');
550
551                    // Piccolo delay per assicurarsi che le opzioni siano state appena rigenerate
552                    setTimeout(() => {
553                        const matchingOption = Array.from(dataOrarioSelect.options).find(
554                            opt => opt.value === targetValue
555                        );
556                        if (matchingOption) {
557                            dataOrarioSelect.value = targetValue;
558                        }
559                    }, 50);
560                }
561            });
562        });
563    }
564});
565
566/**
567 * Form Richiesta Info - AJAX Submit Handler
568 * Gestisce la submit del form in AJAX e mostra il messaggio di thank you
569 */
570document.addEventListener('DOMContentLoaded', function() {
571    const form = document.getElementById('form-richiesta-info');
572    if (!form) return;
573
574    const formWrapper = form.closest('.form_richiesta_info_form');
575    const formContainer = form.closest('.form_richiesta_info_container');
576    const titleDiv = formContainer ? formContainer.querySelector('.form_richiesta_info_title') : null;
577    const thankyouDiv = formWrapper ? formWrapper.querySelector('.form-richiesta-info-thankyou') : null;
578    const submitButton = form.querySelector('button[type="submit"]');
579    
580    if (!formWrapper || !thankyouDiv || !submitButton) return;
581
582    // Salva l'HTML originale del bottone all'inizializzazione
583    const originalButtonHTML = submitButton.innerHTML;
584
585    /**
586     * Resetta il form ai valori di default
587     */
588    function resetForm() {
589        form.reset();
590        
591        // Ripristina completamente il bottone al suo stato originale
592        submitButton.disabled = false;
593        submitButton.innerHTML = originalButtonHTML;
594        
595        // Se ci sono select custom, potrebbe essere necessario reinizializzarli
596        // Ad esempio se usi Select2 o altre librerie
597        const customSelects = form.querySelectorAll('.select-custom');
598        customSelects.forEach(select => {
599            select.value = '';
600            // Se usi Select2, potresti dover chiamare select2('destroy') e reinizializzarlo
601            if (typeof jQuery !== 'undefined' && jQuery(select).hasClass('select2-hidden-accessible')) {
602                jQuery(select).val(null).trigger('change');
603            }
604        });
605
606        thankyouDiv.style.display = 'none';
607        form.style.removeProperty('display');
608        if (titleDiv) {
609            titleDiv.style.removeProperty('display');
610        }
611    }
612
613    /**
614     * Mostra il messaggio di thank you e nasconde il form
615     */
616    function showThankYou() {
617        form.style.display = 'none';
618        if (titleDiv) {
619            titleDiv.style.display = 'none';
620        }
621        thankyouDiv.style.display = 'block';
622    }
623
624    // Event listener per il bottone "Nuova richiesta"
625    const newRequestBtn = thankyouDiv.querySelector('.btn-new-request');
626    if (newRequestBtn) {
627        newRequestBtn.addEventListener('click', function(e) {
628            e.preventDefault();
629            resetForm();
630        });
631    }
632
633    // Funzione helper per l'invio AJAX del form
634    function submitFormAjax(formData, originalButtonHTML) {
635        const ajaxUrl = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.ajaxurl) 
636            ? iuadRichiestaInfo.ajaxurl 
637            : form.action;
638
639        fetch(ajaxUrl, {
640            method: 'POST',
641            body: formData,
642            credentials: 'same-origin'
643        })
644            .then(function(response) {
645                const contentType = response.headers.get('content-type') || '';
646                if (!response.ok || contentType.indexOf('application/json') === -1) {
647                    const httpError = new Error('HTTP ' + response.status);
648                    httpError.status = response.status;
649                    throw httpError;
650                }
651                return response.json();
652            })
653            .then(data => {
654                if (data.success) {
655                    // Google Ads conversione: Invio modulo da sito web
656                    if (typeof gtag === 'function') {
657                        try {
658                            gtag('event', 'conversion', {
659                                'send_to': 'AW-1001604186/uFRTCIf4hoMcENqIzd0D',
660                                'value': 1.0,
661                                'currency': 'EUR'
662                            });
663                        } catch (e) {
664                            // Silenzia eventuali errori JS legati al tracking
665                        }
666                    }
667
668                    // Mostra il messaggio di thank you
669                    showThankYou();
670                } else {
671                    // Mostra errore
672                    const fallbackError = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n && iuadRichiestaInfo.i18n.errorGeneric)
673                        ? iuadRichiestaInfo.i18n.errorGeneric
674                        : 'Si è verificato un errore. Riprova.';
675                    alert(data.data && data.data.message ? data.data.message : fallbackError);
676
677                    // Riabilita il bottone submit
678                    submitButton.disabled = false;
679                    submitButton.innerHTML = originalButtonHTML;
680                }
681            })
682            .catch(error => {
683                console.error('Errore AJAX:', error);
684                const fallbackError = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n && iuadRichiestaInfo.i18n.errorGeneric)
685                    ? iuadRichiestaInfo.i18n.errorGeneric
686                    : 'Si è verificato un errore. Riprova.';
687                alert(fallbackError);
688
689                // Riabilita il bottone submit
690                submitButton.disabled = false;
691                submitButton.innerHTML = originalButtonHTML;
692            });
693    }
694
695    // Gestione submit del form
696    form.addEventListener('submit', function(e) {
697        e.preventDefault();
698
699        // Salva l'HTML originale del bottone per poterlo ripristinare
700        const originalButtonHTML = submitButton ? submitButton.innerHTML : '';
701        
702        // Disabilita il bottone submit per prevenire doppie submission
703        if (submitButton) {
704            submitButton.disabled = true;
705            const submittingLabel = (typeof iuadRichiestaInfo !== 'undefined' && iuadRichiestaInfo.i18n && iuadRichiestaInfo.i18n.submitting)
706                ? iuadRichiestaInfo.i18n.submitting
707                : 'Invio in corso...';
708            submitButton.innerHTML = submittingLabel;
709        }
710
711        // Prepara i dati del form
712        const formData = new FormData(form);
713
714        // Aggiungi action per AJAX (fallback, nel caso mancasse l'hidden nel markup)
715        formData.append('action', 'submit_richiesta_info');
716
717        // Integrazione reCAPTCHA v3 lato client
718        const hasRecaptchaConfig = (
719            typeof iuadRichiestaInfo !== 'undefined' &&
720            iuadRichiestaInfo.recaptchaSiteKey &&
721            typeof grecaptcha !== 'undefined'
722        );
723
724        if (hasRecaptchaConfig) {
725            const siteKey = iuadRichiestaInfo.recaptchaSiteKey;
726            const action = iuadRichiestaInfo.recaptchaAction || 'richiesta_info';
727            const tokenInput = form.querySelector('#g-recaptcha-response');
728
729            grecaptcha.ready(function() {
730                grecaptcha.execute(siteKey, { action: action }).then(function(token) {
731                    if (tokenInput) {
732                        tokenInput.value = token;
733                    }
734                    formData.set('g-recaptcha-response', token);
735                    submitFormAjax(formData, originalButtonHTML);
736                }).catch(function() {
737                    // In caso di errore nel recupero del token, procediamo comunque senza bloccare l'invio
738                    submitFormAjax(formData, originalButtonHTML);
739                });
740            });
741        } else {
742            // Nessuna configurazione reCAPTCHA: procediamo con l'invio normale
743            submitFormAjax(formData, originalButtonHTML);
744        }
745    });
746});
747

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.