PageSourceSearch

https://www.bestogoo.com/js/search-map.js?v=1767042099

js bestogoo.com collected 2026-10-05 07:56:21 UTC 30,948 bytes, 825 lines download raw bytes

1/**
2 * Search Map - Gestione mappa Google Maps per i risultati di ricerca
3 * Bestogoo 2025
4 *
5 * Features:
6 * - Marker numerati per ogni risultato
7 * - Info card al click sul marker
8 * - Pulsante "Cerca qui" quando si sposta la mappa
9 * - Accumulo marker quando si caricano più risultati
10 */
11
12(function() {
13    'use strict';
14
15    // Stato globale della mappa
16    window.SearchMap = {
17        map: null,
18        markers: [],
19        infoWindow: null,
20        results: [],
21        resultIds: new Set(), // Per evitare duplicati
22        currentView: 'list',
23        bounds: null,
24        selectedMarker: null,
25        searchHereButton: null,
26        lastSearchBounds: null,
27        isUserInteraction: false,
28        markerIndex: 0, // Contatore globale per i marker
29        lastSearchedCity: null, // Ultima città cercata per rilevare cambio città
30        geocodeCache: {} // Cache per risultati geocoding (risparmio API calls)
31    };
32
33    /**
34     * Inizializza la mappa Google Maps
35     */
36    window.initSearchMap = function(centerLat, centerLng) {
37        const mapContainer = document.getElementById('search-map');
38        if (!mapContainer) return;
39
40        // Valida coordinate centro - default Roma se non valide
41        let lat = parseFloat(centerLat);
42        let lng = parseFloat(centerLng);
43
44        if (isNaN(lat) || isNaN(lng) || lat === 0 || lng === 0) {
45            lat = 41.9028;  // Roma
46            lng = 12.4964;
47        }
48
49        const center = { lat: lat, lng: lng };
50
51        SearchMap.map = new google.maps.Map(mapContainer, {
52            zoom: 12,
53            center: center,
54            mapTypeControl: false,
55            streetViewControl: false,
56            fullscreenControl: true,
57            zoomControl: true,
58            styles: [
59                {
60                    featureType: "poi",
61                    elementType: "labels",
62                    stylers: [{ visibility: "off" }]
63                }
64            ]
65        });
66
67        SearchMap.bounds = new google.maps.LatLngBounds();
68        SearchMap.infoWindow = new google.maps.InfoWindow();
69
70        // Crea il pulsante "Cerca qui"
71        createSearchHereButton();
72
73        // Listener per movimento mappa
74        SearchMap.map.addListener('dragend', function() {
75            SearchMap.isUserInteraction = true;
76            showSearchHereButton();
77        });
78
79        SearchMap.map.addListener('zoom_changed', function() {
80            if (SearchMap.isUserInteraction) {
81                showSearchHereButton();
82            }
83        });
84
85        // Imposta flag interazione utente
86        SearchMap.map.addListener('dragstart', function() {
87            SearchMap.isUserInteraction = true;
88        });
89
90        console.log('[SearchMap] Mappa inizializzata con centro:', lat, lng);
91    };
92
93    /**
94     * Crea il pulsante "Cerca qui"
95     */
96    function createSearchHereButton() {
97        const button = document.createElement('button');
98        button.id = 'search-here-btn';
99        button.className = 'search-here-button';
100        button.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg> Cerca qui';
101        button.style.display = 'none';
102        button.onclick = searchInCurrentArea;
103
104        const mapContainer = document.getElementById('search-map-container');
105        if (mapContainer) {
106            mapContainer.appendChild(button);
107        }
108
109        SearchMap.searchHereButton = button;
110    }
111
112    /**
113     * Mostra il pulsante "Cerca qui"
114     */
115    function showSearchHereButton() {
116        if (!SearchMap.searchHereButton) return;
117
118        // Mostra solo se abbiamo spostato significativamente la mappa
119        const currentBounds = SearchMap.map.getBounds();
120        if (!currentBounds) return;
121
122        if (SearchMap.lastSearchBounds) {
123            const lastCenter = SearchMap.lastSearchBounds.getCenter();
124            const currentCenter = currentBounds.getCenter();
125
126            // Calcola la distanza tra i centri
127            const distance = google.maps.geometry ?
128                google.maps.geometry.spherical.computeDistanceBetween(lastCenter, currentCenter) :
129                Math.sqrt(Math.pow(lastCenter.lat() - currentCenter.lat(), 2) + Math.pow(lastCenter.lng() - currentCenter.lng(), 2)) * 111000;
130
131            // Mostra pulsante solo se spostamento > 500m
132            if (distance > 500) {
133                SearchMap.searchHereButton.style.display = 'flex';
134            }
135        } else {
136            SearchMap.searchHereButton.style.display = 'flex';
137        }
138    }
139
140    /**
141     * Nasconde il pulsante "Cerca qui"
142     */
143    function hideSearchHereButton() {
144        if (SearchMap.searchHereButton) {
145            SearchMap.searchHereButton.style.display = 'none';
146        }
147    }
148
149    /**
150     * Cerca attività nell'area corrente della mappa
151     */
152    var isSearchingFromMap = false; // Flag per prevenire chiamate duplicate
153
154    function searchInCurrentArea() {
155        // Previeni chiamate duplicate
156        if (isSearchingFromMap) {
157            console.log('[SearchMap] Ricerca già in corso, skip');
158            return;
159        }
160
161        const bounds = SearchMap.map.getBounds();
162        if (!bounds) return;
163
164        const center = bounds.getCenter();
165        const ne = bounds.getNorthEast();
166
167        // Calcola raggio approssimativo in km
168        const radius = google.maps.geometry ?
169            google.maps.geometry.spherical.computeDistanceBetween(center, ne) / 1000 :
170            Math.sqrt(Math.pow(center.lat() - ne.lat(), 2) + Math.pow(center.lng() - ne.lng(), 2)) * 111;
171
172        console.log('[SearchMap] Cerca nell\'area:', center.lat(), center.lng(), 'raggio:', radius.toFixed(2), 'km');
173
174        // Imposta flag per prevenire duplicati
175        isSearchingFromMap = true;
176
177        // NON pulisci i marker qui - lascia che sia updateSearchMap a gestirli
178        // clearSearchMarkers();
179        // SearchMap.markerIndex = 0;
180
181        // Salva bounds correnti
182        SearchMap.lastSearchBounds = bounds;
183        hideSearchHereButton();
184
185        // Chiudi info card
186        closeMapInfoCard();
187
188        // Crea chiave cache basata su coordinate arrotondate (precisione ~1km)
189        const cacheKey = Math.round(center.lat() * 100) + '_' + Math.round(center.lng() * 100);
190
191        // Funzione per eseguire la ricerca con il nome città
192        function executeSearch(cityName) {
193            console.log('[SearchMap] Città trovata:', cityName);
194
195            // Aggiorna il campo di ricerca location
196            const locInput = document.getElementById('loc');
197            if (locInput && cityName) {
198                locInput.value = cityName;
199
200                // Chiama direttamente geocodeAndFind per evitare doppia chiamata da findNear
201                if (typeof geocodeAndFind === 'function') {
202                    geocodeAndFind(cityName,
203                        null,
204                        typeof subcategorySelected !== 'undefined' ? subcategorySelected : null,
205                        typeof searchType !== 'undefined' ? searchType : null);
206                }
207            }
208
209            // Reset flag dopo un breve delay per permettere alla ricerca di completarsi
210            setTimeout(function() {
211                isSearchingFromMap = false;
212            }, 2000);
213        }
214
215        // Controlla cache prima di chiamare API
216        if (SearchMap.geocodeCache[cacheKey]) {
217            console.log('[SearchMap] Usando città da cache:', SearchMap.geocodeCache[cacheKey]);
218            executeSearch(SearchMap.geocodeCache[cacheKey]);
219            return;
220        }
221
222        // Usa reverse geocoding per ottenere il nome della località
223        const geocoder = new google.maps.Geocoder();
224        geocoder.geocode({ location: { lat: center.lat(), lng: center.lng() } }, function(results, status) {
225            if (status === 'OK' && results && results.length > 0) {
226                // Cerca il nome della città nei risultati
227                let cityName = '';
228                for (let i = 0; i < results.length; i++) {
229                    const result = results[i];
230                    // Cerca locality (città) o administrative_area_level_3 (comune)
231                    for (let j = 0; j < result.address_components.length; j++) {
232                        const component = result.address_components[j];
233                        if (component.types.includes('locality') ||
234                            component.types.includes('administrative_area_level_3')) {
235                            cityName = component.long_name;
236                            break;
237                        }
238                    }
239                    if (cityName) break;
240                }
241
242                // Se non trova città, usa l'indirizzo formattato
243                if (!cityName && results[0]) {
244                    cityName = results[0].formatted_address;
245                }
246
247                // Salva in cache
248                SearchMap.geocodeCache[cacheKey] = cityName;
249
250                executeSearch(cityName);
251            } else {
252                console.warn('[SearchMap] Geocoding fallito:', status);
253                alert('Non è stato possibile determinare la località. Prova a spostarti su una zona diversa.');
254                isSearchingFromMap = false;
255            }
256        });
257    }
258
259    /**
260     * Aggiunge i marker sulla mappa dai risultati della ricerca
261     * @param {Array} results - Risultati da aggiungere
262     * @param {boolean} append - Se true, aggiunge ai marker esistenti
263     */
264    window.addSearchMarkers = function(results, append) {
265        if (!SearchMap.map) {
266            console.warn('[SearchMap] Mappa non inizializzata');
267            return;
268        }
269
270        // Se non append, pulisci i marker esistenti
271        if (!append) {
272            clearSearchMarkers();
273            SearchMap.markerIndex = 0;
274        }
275
276        let addedCount = 0;
277
278        results.forEach(function(result) {
279            // Crea ID univoco per evitare duplicati
280            const resultId = result.place_id || result.id || (result.name + '_' + (result.geometry?.location?.lat || ''));
281
282            // Salta se già presente
283            if (SearchMap.resultIds.has(resultId)) {
284                return;
285            }
286
287            // Verifica che ci siano coordinate valide
288            if (!result.geometry || !result.geometry.location) {
289                console.log('[SearchMap] Risultato senza coordinate (saltato):', decodeText(result.name));
290                return;
291            }
292
293            const lat = parseFloat(result.geometry.location.lat);
294            const lng = parseFloat(result.geometry.location.lng);
295
296            // Salta se coordinate non valide o zero
297            if (isNaN(lat) || isNaN(lng) || (lat === 0 && lng === 0)) {
298                console.log('[SearchMap] Coordinate non valide (saltato):', decodeText(result.name));
299                return;
300            }
301
302            const position = { lat: lat, lng: lng };
303            SearchMap.markerIndex++;
304            const markerNumber = SearchMap.markerIndex;
305
306            // Crea marker personalizzato con numero
307            const marker = new google.maps.Marker({
308                position: position,
309                map: SearchMap.map,
310                title: decodeText(result.name),
311                label: {
312                    text: String(markerNumber),
313                    color: '#FFFFFF',
314                    fontSize: '12px',
315                    fontWeight: 'bold'
316                },
317                icon: {
318                    path: google.maps.SymbolPath.CIRCLE,
319                    scale: 18,
320                    fillColor: '#70c542',
321                    fillOpacity: 1,
322                    strokeColor: '#FFFFFF',
323                    strokeWeight: 2
324                },
325                zIndex: 1000 - markerNumber
326            });
327
328            // Salva dati nel marker
329            marker.resultData = result;
330            marker.resultIndex = markerNumber - 1;
331            marker.resultId = resultId;
332
333            // Click handler
334            marker.addListener('click', function() {
335                selectMarker(marker);
336                showMapInfoCard(result, marker.resultIndex);
337            });
338
339            // Hover effect
340            marker.addListener('mouseover', function() {
341                highlightListItem(marker.resultIndex, true);
342            });
343
344            marker.addListener('mouseout', function() {
345                highlightListItem(marker.resultIndex, false);
346            });
347
348            SearchMap.markers.push(marker);
349            SearchMap.results.push(result);
350            SearchMap.resultIds.add(resultId);
351            SearchMap.bounds.extend(position);
352            addedCount++;
353        });
354
355        console.log('[SearchMap] Aggiunti ' + addedCount + ' nuovi marker (totale: ' + SearchMap.markers.length + ')');
356    };
357
358    /**
359     * Pulisce tutti i marker dalla mappa
360     */
361    window.clearSearchMarkers = function() {
362        SearchMap.markers.forEach(function(marker) {
363            marker.setMap(null);
364        });
365        SearchMap.markers = [];
366        SearchMap.results = [];
367        SearchMap.resultIds.clear();
368        SearchMap.bounds = new google.maps.LatLngBounds();
369    };
370
371    /**
372     * Seleziona un marker e lo evidenzia
373     */
374    function selectMarker(marker) {
375        // Reset marker precedente
376        if (SearchMap.selectedMarker && SearchMap.selectedMarker !== marker) {
377            const prevIndex = SearchMap.selectedMarker.resultIndex + 1;
378            SearchMap.selectedMarker.setIcon({
379                path: google.maps.SymbolPath.CIRCLE,
380                scale: 18,
381                fillColor: '#70c542',
382                fillOpacity: 1,
383                strokeColor: '#FFFFFF',
384                strokeWeight: 2
385            });
386            SearchMap.selectedMarker.setLabel({
387                text: String(prevIndex),
388                color: '#FFFFFF',
389                fontSize: '12px',
390                fontWeight: 'bold'
391            });
392        }
393
394        // Evidenzia nuovo marker
395        marker.setIcon({
396            path: google.maps.SymbolPath.CIRCLE,
397            scale: 22,
398            fillColor: '#ff6b35',
399            fillOpacity: 1,
400            strokeColor: '#FFFFFF',
401            strokeWeight: 3
402        });
403
404        SearchMap.selectedMarker = marker;
405    }
406
407    /**
408     * Mostra la card informativa al click sul marker
409     */
410    window.showMapInfoCard = function(result, index) {
411        const card = document.getElementById('map-info-card');
412        const content = card.querySelector('.map-info-card-content');
413
414        if (!card || !content) return;
415
416        // Costruisci URL della pagina di dettaglio
417        const detailUrl = buildDetailUrl(result);
418
419        // Decodifica nome (potrebbe essere URL-encoded dal DB)
420        const name = decodeText(result.name);
421
422        // Immagine
423        let imageHtml = '';
424        let photoUrl = null;
425
426        if (result.photos && result.photos.length > 0) {
427            const photo = result.photos[0];
428            // Caso 1: URL diretto dal DB
429            if (photo.url) {
430                let imgUrl = photo.url;
431                // Fix percorsi relativi
432                if (imgUrl.includes('../bestogoo/')) {
433                    imgUrl = imgUrl.replace('../', 'https://www.bestogoo.com/');
434                } else if (imgUrl.includes('home/')) {
435                    imgUrl = imgUrl.replace('home/', '').replace('/web/htdocs/', 'https://');
436                } else if (imgUrl.startsWith('../')) {
437                    imgUrl = imgUrl.replace('../', 'https://www.bestogoo.com/bestogoo/');
438                }
439                photoUrl = imgUrl;
440            }
441            // Caso 2: Metodo getUrl() di Google Places
442            else if (typeof photo.getUrl === 'function') {
443                photoUrl = photo.getUrl({ maxWidth: 400 });
444            }
445            // Caso 3: URL diretto come stringa
446            else if (typeof photo === 'string') {
447                photoUrl = photo;
448            }
449        }
450
451        if (photoUrl) {
452            imageHtml = '<img src="' + photoUrl + '" alt="' + escapeHtml(name) + '" class="map-info-card-image" onerror="this.parentElement.innerHTML=\'<div class=map-info-card-image-placeholder><svg width=48 height=48 viewBox=0_0_24_24 fill=none stroke=#ccc stroke-width=1.5><rect x=3 y=3 width=18 height=18 rx=2 ry=2></rect></svg></div>\'">';
453        } else {
454            imageHtml = '<div class="map-info-card-image-placeholder"><svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#ccc" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg></div>';
455        }
456
457        // Rating
458        let ratingHtml = '';
459        if (result.rating) {
460            const stars = Math.round(result.rating);
461            ratingHtml = '<div class="map-info-card-rating">';
462            for (let i = 0; i < 5; i++) {
463                ratingHtml += i < stars ?
464                    '<span class="star filled">&#9733;</span>' :
465                    '<span class="star">&#9734;</span>';
466            }
467            ratingHtml += ' <span class="rating-value">' + result.rating.toFixed(1) + '</span>';
468            ratingHtml += '</div>';
469        }
470
471        // Indirizzo completo: prima l'indirizzo specifico, poi città/regione
472        let addressParts = [];
473        if (result.address) {
474            addressParts.push(decodeText(result.address));
475        }
476        if (result.formatted_address) {
477            addressParts.push(decodeText(result.formatted_address));
478        } else if (result.city) {
479            addressParts.push(decodeText(result.city));
480        }
481        // Rimuovi duplicati e unisci
482        const address = [...new Set(addressParts)].join(' - ');
483
484        content.innerHTML =
485            '<a href="' + detailUrl + '" class="map-info-card-link">' +
486                imageHtml +
487                '<div class="map-info-card-body">' +
488                    '<div class="map-info-card-position">' + (index + 1) + '</div>' +
489                    '<h3 class="map-info-card-title">' + escapeHtml(name) + '</h3>' +
490                    ratingHtml +
491                    (address ? '<p class="map-info-card-address">' + escapeHtml(address) + '</p>' : '') +
492                '</div>' +
493                '<div class="map-info-card-arrow">' +
494                    '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"></polyline></svg>
494' +
495                '</div>' +
496            '</a>';
497
498        card.style.display = 'block';
499
500        // Animazione entrata
501        card.classList.remove('visible');
502        setTimeout(function() {
503            card.classList.add('visible');
504        }, 10);
505    };
506
507    /**
508     * Chiude la card informativa
509     */
510    window.closeMapInfoCard = function() {
511        const card = document.getElementById('map-info-card');
512        if (card) {
513            card.classList.remove('visible');
514            setTimeout(function() {
515                card.style.display = 'none';
516            }, 300);
517        }
518
519        // Deseleziona marker
520        if (SearchMap.selectedMarker) {
521            const prevIndex = SearchMap.selectedMarker.resultIndex + 1;
522            SearchMap.selectedMarker.setIcon({
523                path: google.maps.SymbolPath.CIRCLE,
524                scale: 18,
525                fillColor: '#70c542',
526                fillOpacity: 1,
527                strokeColor: '#FFFFFF',
528                strokeWeight: 2
529            });
530            SearchMap.selectedMarker.setLabel({
531                text: String(prevIndex),
532                color: '#FFFFFF',
533                fontSize: '12px',
534                fontWeight: 'bold'
535            });
536            SearchMap.selectedMarker = null;
537        }
538    };
539
540    /**
541     * Costruisce URL per la pagina di dettaglio
542     */
543    function buildDetailUrl(result) {
544        // Se ha un link specifico, usalo
545        if (result.link) {
546            return result.link;
547        }
548
549        // Altrimenti costruisci l'URL standard
550        const baseUrl = window.location.origin;
551        const lang = document.documentElement.lang || 'it';
552
553        if (result.linkitem) {
554            return baseUrl + '/' + lang + '/' + result.linkitem;
555        }
556
557        // Usa place_id con parametro activity (formato Bestogoo)
558        if (result.place_id) {
559            return baseUrl + '/detail.php?activity=' + encodeURIComponent(result.place_id);
560        }
561
562        // Fallback: usa id
563        if (result.id) {
564            return baseUrl + '/detail.php?id=' + encodeURIComponent(result.id);
565        }
566
567        return '#';
568    }
569
570    /**
571     * Evidenzia elemento nella lista quando si passa sopra il marker
572     */
573    function highlightListItem(index, highlight) {
574        const listItems = document.querySelectorAll('#box006b .box007');
575        if (listItems[index]) {
576            if (highlight) {
577                listItems[index].classList.add('map-hover-highlight');
578            } else {
579                listItems[index].classList.remove('map-hover-highlight');
580            }
581        }
582    }
583
584    /**
585     * Switch tra vista lista e mappa
586     */
587    window.switchSearchView = function(view) {
588        const listContainer = document.getElementById('box006b');
589        const mapContainer = document.getElementById('search-map-container');
590        const tabs = document.querySelectorAll('.search-view-tab');
591        const loaderPager = document.getElementById('loader_pager');
592
593        if (!listContainer || !mapContainer) return;
594
595        SearchMap.currentView = view;
596
597        // Aggiorna tabs
598        tabs.forEach(function(tab) {
599            if (tab.dataset.view === view) {
600                tab.classList.add('active');
601            } else {
602                tab.classList.remove('active');
603            }
604        });
605
606        if (view === 'map') {
607            // Aggiungi classe al body per stili specifici mappa
608            document.body.classList.add('search-map-active');
609            listContainer.style.display = 'none';
610            mapContainer.style.display = 'block';
611            if (loaderPager) loaderPager.style.display = 'none';
612
613            // Inizializza mappa se non esiste
614            if (!SearchMap.map) {
615                initSearchMap();
616            }
617
618            // Trigger resize per fix rendering
619            setTimeout(function() {
620                google.maps.event.trigger(SearchMap.map, 'resize');
621                if (SearchMap.markers.length > 0) {
622                    SearchMap.map.fitBounds(SearchMap.bounds);
623                    // Limita lo zoom massimo a 15 (con pochi marker fitBounds zooma troppo)
624                    if (SearchMap.map.getZoom() > 15) {
625                        SearchMap.map.setZoom(15);
626                    }
627                    // Salva bounds correnti come riferimento
628                    SearchMap.lastSearchBounds = SearchMap.map.getBounds();
629                } else if (SearchMap.lastSearchedCity) {
630                    // Nessun marker ma abbiamo una città cercata - centra su quella
631                    centerMapOnCity(null, null, SearchMap.lastSearchedCity);
632                }
633            }, 100);
634
635        } else {
636            // Rimuovi classe dal body quando si torna alla lista
637            document.body.classList.remove('search-map-active');
638            listContainer.style.display = 'block';
639            mapContainer.style.display = 'none';
640            if (loaderPager) loaderPager.style.display = 'block';
641            closeMapInfoCard();
642            hideSearchHereButton();
643        }
644
645        console.log('[SearchMap] Vista cambiata a: ' + view);
646    };
647
648    /**
649     * Centra la mappa su un marker specifico (chiamato dalla lista)
650     */
651    window.focusMapMarker = function(index) {
652        if (SearchMap.currentView !== 'map') {
653            switchSearchView('map');
654        }
655
656        setTimeout(function() {
657            if (SearchMap.markers[index]) {
658                const marker = SearchMap.markers[index];
659                selectMarker(marker);
660                showMapInfoCard(marker.resultData, index);
661                SearchMap.map.panTo(marker.getPosition());
662                SearchMap.map.setZoom(15);
663            }
664        }, 150);
665    };
666
667    /**
668     * Utility: escape HTML
669     */
670    function escapeHtml(text) {
671        if (!text) return '';
672        const div = document.createElement('div');
673        div.textContent = text;
674        return div.innerHTML;
675    }
676
677    /**
678     * Utility: decodifica testo URL-encoded
679     */
680    function decodeText(text) {
681        if (!text) return '';
682        try {
683            // Prima prova decodeURIComponent standard
684            let decoded = decodeURIComponent(text);
685            // Se contiene ancora % potrebbe essere doppia codifica
686            if (decoded.includes('%')) {
687                try {
688                    decoded = decodeURIComponent(decoded);
689                } catch (e2) {
690                    // Ignora errori di doppia decodifica
691                }
692            }
693            return decoded;
694        } catch (e) {
695            // Fallback: prova unescape (deprecato ma gestisce più casi)
696            try {
697                return unescape(text);
698            } catch (e2) {
699                return text;
700            }
701        }
702    }
703
704    /**
705     * Aggiorna la mappa quando i risultati cambiano
706     * Chiamato dal callback in core.js
707     * @param {Array} results - Nuovi risultati
708     * @param {number} centerLat - Latitudine centro
709     * @param {number} centerLng - Longitudine centro
710     * @param {boolean} append - Se true, aggiunge ai marker esistenti (default: false)
711     * @param {string} searchedCity - Nome della città cercata (per geocoding fallback)
712     */
713    window.updateSearchMap = function(results, centerLat, centerLng, append, searchedCity) {
714        // Rileva se la città è cambiata
715        var cityChanged = !append && searchedCity && searchedCity !== SearchMap.lastSearchedCity;
716        if (searchedCity && !append) {
717            SearchMap.lastSearchedCity = searchedCity;
718        }
719
720        // Inizializza mappa se necessario
721        if (!SearchMap.map) {
722            initSearchMap(centerLat, centerLng);
723        }
724
725        // Disabilita temporaneamente il flag interazione utente
726        SearchMap.isUserInteraction = false;
727
728        // Aggiungi marker (append = true per aggiungere, false per sostituire)
729        addSearchMarkers(results, append === true);
730
731        // Se siamo in vista mappa, aggiorna la visualizzazione
732        if (SearchMap.currentView === 'map') {
733            setTimeout(function() {
734                google.maps.event.trigger(SearchMap.map, 'resize');
735                if (SearchMap.markers.length > 0 && !append) {
736                    SearchMap.map.fitBounds(SearchMap.bounds);
737                    // Limita lo zoom massimo a 15 (con pochi marker fitBounds zooma troppo)
738                    if (SearchMap.map.getZoom() > 15) {
739                        SearchMap.map.setZoom(15);
740                    }
741                    SearchMap.lastSearchBounds = SearchMap.map.getBounds();
742                } else if (SearchMap.markers.length === 0 && !append) {
743                    // Nessun marker - centra sulla città cercata usando geocoding
744                    centerMapOnCity(centerLat, centerLng, searchedCity);
745                } else if (cityChanged && SearchMap.markers.length === 0) {
746                    // Città cambiata ma nessun marker con coordinate valide
747                    centerMapOnCity(centerLat, centerLng, searchedCity);
748                }
749                // Nascondi pulsante cerca qui dopo aggiornamento
750                hideSearchHereButton();
751            }, 100);
752        } else {
753            // Anche se non siamo in vista mappa, prepara il centro corretto per quando si switcha
754            if (cityChanged || (SearchMap.markers.length === 0 && !append && searchedCity)) {
755                centerMapOnCity(centerLat, centerLng, searchedCity);
756            }
757        }
758    };
759
760    /**
761     * Centra la mappa sulla città cercata usando geocoding
762     * @param {number} centerLat - Latitudine fornita (può essere null)
763     * @param {number} centerLng - Longitudine fornita (può essere null)
764     * @param {string} cityName - Nome della città
765     */
766    function centerMapOnCity(centerLat, centerLng, cityName) {
767        // Verifica che la mappa sia inizializzata
768        if (!SearchMap.map) {
769            console.log('[SearchMap] centerMapOnCity: mappa non ancora inizializzata, skip');
770            return;
771        }
772
773        // Prima prova le coordinate fornite
774        var lat = parseFloat(centerLat);
775        var lng = parseFloat(centerLng);
776
777        if (!isNaN(lat) && !isNaN(lng) && lat !== 0 && lng !== 0) {
778            SearchMap.map.setCenter({ lat: lat, lng: lng });
779            SearchMap.map.setZoom(12);
780            console.log('[SearchMap] Centrato su coordinate fornite:', lat, lng);
781            return;
782        }
783
784        // Fallback: usa Geocoding API per la città (con cache)
785        if (cityName && typeof google !== 'undefined' && google.maps && google.maps.Geocoder) {
786            // Crea chiave cache per la città
787            var cityCacheKey = 'city_' + cityName.toLowerCase().replace(/[^a-z0-9]/g, '_');
788
789            // Controlla cache
790            if (SearchMap.geocodeCache[cityCacheKey]) {
791                var cached = SearchMap.geocodeCache[cityCacheKey];
792                SearchMap.map.setCenter({ lat: cached.lat, lng: cached.lng });
793                SearchMap.map.setZoom(12);
794                console.log('[SearchMap] Centrato su città (da cache):', cityName, cached.lat, cached.lng);
795                return;
796            }
797
798            var geocoder = new google.maps.Geocoder();
799            geocoder.geocode({ address: cityName + ', Italia' }, function(results, status) {
800                if (status === 'OK' && results && results[0]) {
801                    var location = results[0].geometry.location;
802                    var lat = typeof location.lat === 'function' ? location.lat() : location.lat;
803                    var lng = typeof location.lng === 'function' ? location.lng() : location.lng;
804
805                    // Salva in cache
806                    SearchMap.geocodeCache[cityCacheKey] = { lat: lat, lng: lng };
807
808                    SearchMap.map.setCenter({ lat: lat, lng: lng });
809                    SearchMap.map.setZoom(12);
810                    console.log('[SearchMap] Centrato su città (geocoding):', cityName, lat, lng);
811                } else {
812                    console.warn('[SearchMap] Geocoding fallito per:', cityName, status);
813                }
814            });
815        }
816    }
817
818    /**
819     * Esponi funzione per aggiungere marker senza sostituire
820     */
821    window.appendSearchMarkers = function(results) {
822        updateSearchMap(results, null, null, true);
823    };
824
825})();

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.