PageSourceSearch

https://visitfano.info/wp-content/plugins/entourance/assets/js/entourance-common.js?ver=1.8.0

js visitfano.info collected 2026-10-09 05:12:38 UTC 15,298 bytes, 491 lines download raw bytes

1/**
2 * Entourance Common JavaScript Utilities
3 * 
4 * Funzioni comuni utilizzate da product-list e product-slider
5 * 
6 * @package EntourancePlugin
7 */
8
9/**
10 * Invia un evento analytics (POST JSON) senza mai bloccare la pagina:
11 * ignora errori di rete, CORS, URL non validi e promise non gestite.
12 *
13 * @param {string} url  Endpoint completo.
14 * @param {Object} body Oggetto serializzabile in JSON.
15 */
16function entouranceSafeAnalyticsPost(url, body) {
17	if (typeof fetch !== 'function') {
18		return;
19	}
20	if (!url || typeof url !== 'string') {
21		return;
22	}
23	body = body && typeof body === 'object' ? body : {};
24	if (
25		body.realm_id == null &&
26		window.entouranceAnalyticsConfig &&
27		window.entouranceAnalyticsConfig.realmId != null &&
28		window.entouranceAnalyticsConfig.realmId !== ''
29	) {
30		body.realm_id = window.entouranceAnalyticsConfig.realmId;
31	}
32	var json;
33	try {
34		json = JSON.stringify(body);
35	} catch (e) {
36		return;
37	}
38	var request;
39	try {
40		request = fetch(url, {
41			method: 'POST',
42			headers: { 'Content-Type': 'application/json' },
43			body: json,
44			credentials: 'omit',
45			cache: 'no-store',
46			keepalive: true,
47		});
48	} catch (e) {
49		return;
50	}
51	if (request && typeof request.then === 'function') {
52		request
53			.then(
54				function () {
55					return undefined;
56				},
57				function () {
58					return undefined;
59				}
60			)
61			.catch(function () {
62				return undefined;
63			});
64	}
65}
66window.entouranceSafeAnalyticsPost = entouranceSafeAnalyticsPost;
67
68// Namespace per evitare conflitti
69window.EntouranceCommon = window.EntouranceCommon || {};
70
71/**
72 * Funzioni per la gestione dei filtri
73 */
74EntouranceCommon.Filters = {
75    /**
76     * Estrae filtri unici da un array di dati
77     * @param {Array} data - Array di oggetti
78     * @param {string} field - Campo da estrarre
79     * @returns {Array} Array di valori unici
80     */
81    getUniqueFilters: function(data, field) {
82        const uniqueFilter = new Set();
83        
84        data.forEach(item => {
85            if (item[field] !== undefined && item[field] !== null) {
86                uniqueFilter.add(item[field]);
87            }
88        });
89        
90        return Array.from(uniqueFilter);
91    },
92
93    /**
94     * Crea un elemento di filtro per città
95     * @param {Object} item - Oggetto città
96     * @param {string} type - Tipo di prodotto
97     * @returns {HTMLElement} Elemento DOM
98     */
99    createCityFilterItem: function(item, type) {
100        const option = document.createElement('option');
101        option.value = item;
102        option.textContent = item;
103        return option;
104    },
105
106    /**
107     * Crea un elemento di filtro per categoria
108     * @param {Object} item - Oggetto categoria
109     * @param {string} type - Tipo di prodotto
110     * @returns {HTMLElement} Elemento DOM
111     */
112    createCategoryFilterItem: function(item, type) {
113        const option = document.createElement('option');
114        option.value = item;
115        option.textContent = item;
116        return option;
117    },
118
119    /**
120     * Crea un elemento di filtro per POI
121     * @param {Object} item - Oggetto POI
122     * @param {string} type - Tipo di prodotto
123     * @returns {HTMLElement} Elemento DOM
124     */
125    createPoiFilterItem: function(item, type) {
126        const option = document.createElement('option');
127        option.value = item;
128        option.textContent = item;
129        return option;
130    },
131
132    /**
133     * Crea un elemento di filtro per sottocategoria
134     * @param {Object} item - Oggetto sottocategoria
135     * @param {string} type - Tipo di prodotto
136     * @param {Array} relatedSubcategories - Sottocategorie correlate
137     * @returns {HTMLElement} Elemento DOM
138     */
139    createSubcategoryFilterItem: function(item, type, relatedSubcategories) {
140        const option = document.createElement('option');
141        option.value = item;
142        option.textContent = item;
143        return option;
144    },
145
146    /**
147     * Crea un elemento di filtro per gruppo
148     * @param {Object} item - Oggetto gruppo
149     * @param {string} type - Tipo di prodotto
150     * @returns {HTMLElement} Elemento DOM
151     */
152    createGroupFilterItem: function(item, type) {
153        const option = document.createElement('option');
154        option.value = item;
155        option.textContent = item;
156        return option;
157    }
158};
159
160/**
161 * Funzioni per la gestione dei prodotti
162 */
163EntouranceCommon.Products = {
164    /**
165     * Genera l'URL per un prodotto in base al tipo
166     * @param {Object} product - Oggetto prodotto
167     * @param {string} type - Tipo di prodotto
168     * @param {string} baseUrl - URL base del sito
169     * @returns {string} URL del prodotto
170     */
171    generateUrl: function(product, type, baseUrl) {
172        const slug = product.slug || product.id;
173        
174        switch(type) {
175            case 'experience':
176                return `${baseUrl}/experience/${slug}`;
177            case 'event':
178                return `${baseUrl}/event/${slug}`;
179            case 'eatery':
180                return `${baseUrl}/eatery/${slug}`;
181            case 'host':
182                return `${baseUrl}/host/${slug}`;
183            case 'article':
184                return `${baseUrl}/article/${slug}`;
185            case 'itinerary':
186                return `${baseUrl}/itinerary/${slug}`;
187            case 'poi':
188                return `${baseUrl}/poi/${slug}`;
189            case 'experienceSupplier':
190            case 'experience-supplier':
191                return `${baseUrl}/service/${slug}`;
192            default:
193                return '#';
194        }
195    },
196
197    /**
198     * Formatta la durata rimuovendo i secondi
199     * @param {string} duration - Durata in formato HH:MM:SS
200     * @returns {string} Durata formattata
201     */
202    formatDuration: function(duration) {
203        if (!duration) return '';
204        return duration.split(':').slice(0, 2).join(':');
205    },
206
207    /**
208     * Traduce il tipo di prodotto
209     * @param {string} type - Tipo di prodotto
210     * @param {string} language - Lingua corrente
211     * @returns {string} Tipo tradotto
212     */
213    translateType: function(type, language) {
214        const translations = {
215            'experience': language === 'it_IT' ? 'Esperienza' : 'Experience',
216            'eatery': language === 'it_IT' ? 'Ristorante' : 'Eatery',
217            'host': language === 'it_IT' ? 'Struttura' : 'Host',
218            'article': language === 'it_IT' ? 'Articolo' : 'Article',
219            'itinerary': language === 'it_IT' ? 'Itinerario' : 'Itinerary',
220            'poi': language === 'it_IT' ? 'Punto di interesse' : 'Point of interest',
221            'experienceSupplier': language === 'it_IT' ? 'Servizio' : 'Service',
222            'experience-supplier': language === 'it_IT' ? 'Servizio' : 'Service'
223        };
224        return translations[type] || type;
225    },
226
227    /**
228     * Crea una card prodotto
229     * @param {Object} product - Oggetto prodotto
230     * @param {HTMLElement} template - Template della card
231     * @param {string} type - Tipo di prodotto
232     * @param {string} language - Lingua corrente
233     * @param {string} baseUrl - URL base del sito
234     * @returns {HTMLElement} Card DOM
235     */
236    createCard: function(product, template, type, language, baseUrl) {
237        const card = template.content.cloneNode(true);
238        
239        // Immagine
240        const img = card.querySelector('img');
241        const imgWrapper = card.querySelector('.card-img-wrapper');
242        const imgSrc = Array.isArray(product.images) && product.images[0] ? product.images[0] : '';
243        
244        if (imgSrc) {
245            img.src = imgSrc;
246            img.alt = product.title || product.name;
247        } else {
248            // Placeholder
249            img.remove();
250            const placeholder = document.createElement('div');
251            placeholder.className = 'card-img-placeholder d-flex align-items-center justify-content-center';
252            placeholder.innerHTML = '<i class="fas fa-image fa-3x text-secondary"></i>';
253            imgWrapper.appendChild(placeholder);
254            imgWrapper.style.background = '#f5f5f5';
255            imgWrapper.style.minHeight = '200px';
256        }
257        
258        // Link
259        const link = card.querySelector('.card-link');
260        link.href = this.generateUrl(product, type, baseUrl);
261        
262        // Titolo
263        const title = card.querySelector('.card-title');
264        title.textContent = product.title || product.name;
265        
266        // Duration
267        const duration = card.querySelector('.duration');
268        if (duration && product.duration) {
269            duration.textContent = this.formatDuration(product.duration);
270        }
271        
272        // Prezzo
273        const price = card.querySelector('.price');
274        if (price && product.price) {
275            price.textContent = product.price;
276        }
277        
278        // Badge tipo
279        const typeBadge = card.querySelector('.type-badge');
280        if (typeBadge) {
281            typeBadge.textContent = this.translateType(type, language);
282        }
283        
284        // Badge categoria
285        const categoryBadge = card.querySelector('.category-badge');
286        if (categoryBadge && product.category) {
287            categoryBadge.textContent = product.category;
288        }
289        
290        return card;
291    }
292};
293
294/**
295 * Funzioni per la gestione delle date
296 */
297EntouranceCommon.Dates = {
298    /**
299     * Calcola l'offset tra due mesi
300     * @param {number} month1 - Primo mese
301     * @param {number} month2 - Secondo mese
302     * @returns {number} Offset
303     */
304    calculateMonthOffset: function(month1, month2) {
305        return month2 - month1;
306    },
307
308    /**
309     * Controlla se un array contiene una sottostringa
310     * @param {Array} array - Array da controllare
311     * @param {string} substring - Sottostringa da cercare
312     * @returns {boolean} True se trovata
313     */
314    hasSubstring: function(array, substring) {
315        return array.some(item => item.includes(substring));
316    },
317
318    /**
319     * Gestisce la pressione dei tasti
320     * @param {Event} event - Evento keypress
321     * @param {string} type - Tipo di prodotto
322     */
323    handleKeyPress: function(event, type) {
324        if (event.key === 'Enter') {
325            // Logica per gestire Enter
326        }
327    },
328
329    /**
330     * Ottiene il testo di ricerca
331     * @param {string} type - Tipo di prodotto
332     * @returns {string} Testo di ricerca
333     */
334    getSearchText: function(type) {
335        const searchInput = document.getElementById('searchInput');
336        return searchInput ? searchInput.value : '';
337    }
338};
339
340/**
341 * Funzioni per la gestione della ricerca
342 */
343EntouranceCommon.Search = {
344    /**
345     * Filtra i prodotti in base al testo di ricerca
346     * @param {Array} products - Array di prodotti
347     * @param {string} searchText - Testo di ricerca
348     * @returns {Array} Prodotti filtrati
349     */
350    filterProducts: function(products, searchText) {
351        if (!searchText) return products;
352        
353        const lowerSearchText = searchText.toLowerCase();
354        return products.filter(product => {
355            const title = (product.title || product.name || '').toLowerCase();
356            const description = (product.description || '').toLowerCase();
357            const category = (product.category || '').toLowerCase();
358            
359            return title.includes(lowerSearchText) || 
360                   description.includes(lowerSearchText) || 
361                   category.includes(lowerSearchText);
362        });
363    },
364
365    /**
366     * Inizializza la ricerca
367     * @param {string} type - Tipo di prodotto
368     * @param {Function} onSearch - Callback per la ricerca
369     */
370    initSearch: function(type, onSearch) {
371        const searchInput = document.getElementById('searchInput');
372        const searchButton = document.getElementById('searchButton');
373        
374        if (searchInput) {
375            searchInput.addEventListener('input', function() {
376                const searchText = this.value;
377                onSearch(searchText);
378            });
379        }
380        
381        if (searchButton) {
382            searchButton.addEventListener('click', function() {
383                const searchText = searchInput ? searchInput.value : '';
384                onSearch(searchText);
385            });
386        }
387    }
388};
389
390/**
391 * Funzioni per la gestione del responsive
392 */
393EntouranceCommon.Responsive = {
394    /**
395     * Determina il numero di card per slide in base alla dimensione dello schermo
396     * @returns {number} Numero di card per slide
397     */
398    getCardsPerSlide: function() {
399        if (window.innerWidth <= 575.98) {
400            return 1;
401        } else if (window.innerWidth <= 991.98) {
402            return 2;
403        } else {
404            return 3;
405        }
406    },
407
408    /**
409     * Aggiunge listener per il resize della finestra
410     * @param {Function} callback - Callback da eseguire al resize
411     */
412    addResizeListener: function(callback) {
413        window.addEventListener('resize', function() {
414            callback();
415        });
416    }
417};
418
419/**
420 * Funzioni per la gestione degli errori
421 */
422EntouranceCommon.Errors = {
423    /**
424     * Gestisce gli errori di caricamento
425     * @param {Error} error - Errore
426     * @param {string} context - Contesto dell'errore
427     */
428    handleError: function(error, context) {
429        console.error(`Entourance ${context} error:`, error);
430        
431        // Mostra messaggio di errore all'utente
432        const errorMessage = document.createElement('div');
433        errorMessage.className = 'alert alert-danger';
434        errorMessage.textContent = 'Si è verificato un errore durante il caricamento dei dati.';
435        
436        // Inserisci il messaggio di errore nel DOM appropriato
437        const container = document.querySelector('.entourance-plugin');
438        if (container) {
439            container.insertBefore(errorMessage, container.firstChild);
440        }
441    },
442
443    /**
444     * Gestisce gli errori di rete
445     * @param {Response} response - Risposta della rete
446     * @param {string} context - Contesto dell'errore
447     */
448    handleNetworkError: function(response, context) {
449        if (!response.ok) {
450            throw new Error(`HTTP error! status: ${response.status}`);
451        }
452    }
453};
454
455/**
456 * Funzioni per la gestione del caricamento
457 */
458EntouranceCommon.Loading = {
459    /**
460     * Mostra l'indicatore di caricamento
461     * @param {string} containerId - ID del container
462     */
463    showLoading: function(containerId) {
464        const container = document.getElementById(containerId);
465        if (container) {
466            const loading = document.createElement('div');
467            loading.className = 'text-center py-4';
468            loading.innerHTML = '<div class="spinner-border" role="status"><span class="entourance-sr-only">Caricamento...</span></div>';
469            container.appendChild(loading);
470        }
471    },
472
473    /**
474     * Nasconde l'indicatore di caricamento
475     * @param {string} containerId - ID del container
476     */
477    hideLoading: function(containerId) {
478        const container = document.getElementById(containerId);
479        if (container) {
480            const loading = container.querySelector('.spinner-border');
481            if (loading) {
482                loading.remove();
483            }
484        }
485    }
486};
487
488// Esporta per compatibilità con moduli
489if (typeof module !== 'undefined' && module.exports) {
490    module.exports = EntouranceCommon;
491} 

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.