1/** 2 * Buscador de Centros con Google Maps 3 * Inicializa el mapa y gestiona el filtrado del listado 4 */ 5 6(function() { 7 'use strict'; 8 9 // Variables globales 10 let map; 11 let markers = []; 12 let infoWindow; 13 14 /** 15 * Inicializa el mapa de Google Maps 16 */ 17 window.initBuscarCentrosMapa = function() { 18 const mapElement = document.getElementById('mapa-buscar-centros'); 19 20 if (!mapElement || !window.centrosData) { 21 console.warn('No se encontró el elemento del mapa o los datos de centros'); 22 return; 23 } 24 25 // Detectar si es móvil 26 const isMobile = window.innerWidth <= 768; 27 const zoomLevel = isMobile ? 5 : 6; 28 29 // Configuración del mapa centrado en España 30 map = new google.maps.Map(mapElement, { 31 zoom: zoomLevel, 32 center: { lat: 40.4637, lng: -3.7492 }, // Madrid (centro de España) 33 disableDefaultUI: true, 34 zoomControl: true, 35 streetViewControl: false, 36 fullscreenControl: false, 37 mapTypeControl: false, 38 clickableIcons: false, 39 gestureHandling: 'cooperative' 40 }); 41 42 // Crear infoWindow única 43 infoWindow = new google.maps.InfoWindow(); 44 45 // Geocodificar y agregar marcadores para cada centro 46 const geocoder = new google.maps.Geocoder(); 47 48 window.centrosData.forEach(function(centro, index) { 49 if (!centro.direccion) return; 50 51 // Agregar delay para evitar lÃmite de peticiones 52 setTimeout(function() { 53 geocodificarYCrearMarcador(geocoder, centro); 54 }, index * 100); 55 }); 56 }; 57 58 /** 59 * Geocodifica una dirección y crea un marcador 60 */ 61 function geocodificarYCrearMarcador(geocoder, centro) { 62 geocoder.geocode({ address: centro.direccion }, function(results, status) { 63 if (status === 'OK' && results[0]) { 64 const marker = new google.maps.Marker({ 65 position: results[0].geometry.location, 66 map: map, 67 title: centro.titulo, 68 icon: { 69 url: window.iconUrl, 70 scaledSize: new google.maps.Size(40, 40) 71 }, 72 animation: google.maps.Animation.DROP 73 }); 74 75 // Guardar referencia al centro en el marcador 76 marker.centroData = centro; 77 markers.push(marker); 78 79 // Evento click en el marcador 80 marker.addListener('click', function() { 81 const content = ` 82 <div style="padding: 5px 10px 10px 10px; max-width: 280px; min-width: 260px;"> 83 <h3 style="margin: 0 0 4px; color: #1C3E8F; font-family: Sztos; font-size: 16px; font-weight: 500; line-height: 19px; letter-spacing: -0.14px;">Láserum - ${centro.ciudad}</h3> 84 <p style="margin: 0 0 12px; font-size: 14px; color: #6B7280;">Especialistas en depilación láser</p> 85 <p style="margin: 0 0 4px; font-size: 12px; color: #9CA3AF; font-weight: 700;">Centro estético</p> 86 <p style="margin: 0 0 12px; font-size: 13px; color: #9CA3AF;">${centro.direccion}</p> 87 ${centro.telefono ? `<p style="margin: 0 0 12px; font-size: 13px; font-weight: 500; color: #1C3E8F;">Tel: ${centro.telefono}</p>` : ''} 88 <a href="${centro.url}" style="display: inline-flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 400; color: #1C3E8F; text-decoration: none;">Ver página de centro</a> 89 </div> 90 `; 91 92 infoWindow.setContent(content); 93 infoWindow.open(map, marker); 94 95 // Ajustar el margen del contenedor del InfoWindow 96 google.maps.event.addListenerOnce(infoWindow, 'domready', function() { 97 const iwOuter = document.querySelector('.gm-style-iw-d'); 98 if (iwOuter) { 99 iwOuter.style.marginTop = '-30px'; 100 } 101 }); 102 }); 103 } 104 }); 105 } 106 107 /** 108 * Filtra los centros según el texto de búsqueda 109 */ 110 function filtrarCentros(searchText) { 111 const items = document.querySelectorAll('.centro-item'); 112 const listado = document.getElementById('listado-centros'); 113 let visibleCount = 0; 114 115 // Normalizar texto de búsqueda 116 const searchNormalized = searchText.toLowerCase().trim(); 117
118 items.forEach(function(item) { 119 const titulo = item.dataset.titulo.toLowerCase(); 120 const provincia = item.dataset.provincia.toLowerCase(); 121 const codigoPostal = item.dataset.codigoPostal; 122 123 // Verificar si coincide con la búsqueda 124 const coincide = searchNormalized === '' || 125 titulo.includes(searchNormalized) || 126 provincia.includes(searchNormalized) || 127 (codigoPostal && codigoPostal.includes(searchNormalized)); 128 129 if (coincide) { 130 item.classList.remove('hidden'); 131 visibleCount++; 132 } else { 133 item.classList.add('hidden'); 134 } 135 }); 136 137 // Mostrar mensaje si no hay resultados 138 let noResultadosMsg = listado.querySelector('.no-resultados'); 139 140 if (visibleCount === 0 && searchNormalized !== '') { 141 if (!noResultadosMsg) { 142 noResultadosMsg = document.createElement('div'); 143 noResultadosMsg.className = 'no-resultados'; 144 noResultadosMsg.textContent = 'No se encontraron centros para tu búsqueda'; 145 listado.appendChild(noResultadosMsg); 146 } 147 } else if (noResultadosMsg) { 148 noResultadosMsg.remove(); 149 } 150 151 // Filtrar marcadores en el mapa 152 filtrarMarcadores(searchNormalized); 153 } 154 155 /** 156 * Filtra los marcadores del mapa según la búsqueda 157 */ 158 function filtrarMarcadores(searchText) { 159 markers.forEach(function(marker) { 160 const centro = marker.centroData; 161 162 if (!centro) { 163 marker.setMap(map); 164 return; 165 } 166 167 const titulo = centro.ciudad.toLowerCase(); 168 const provincia = centro.provincia.toLowerCase(); 169 const codigoPostal = centro.codigo_postal; 170 171 const coincide = searchText === '' || 172 titulo.includes(searchText) || 173 provincia.includes(searchText) || 174 (codigoPostal && codigoPostal.includes(searchText)); 175 176 // Mostrar u ocultar marcador 177 marker.setMap(coincide ? map : null); 178 }); 179 180 // Ajustar zoom si hay filtros activos 181 if (searchText !== '' && markers.length > 0) { 182 const bounds = new google.maps.LatLngBounds(); 183 let hasVisibleMarkers = false; 184 185 markers.forEach(function(marker) { 186 if (marker.getMap()) { 187 bounds.extend(marker.getPosition()); 188 hasVisibleMarkers = true; 189 } 190 }); 191 192 if (hasVisibleMarkers) { 193 map.fitBounds(bounds); 194 195 // Limitar el zoom máximo 196 google.maps.event.addListenerOnce(map, 'bounds_changed', function() { 197 if (map.getZoom() > 15) { 198 map.setZoom(15); 199 } 200 }); 201 } 202 } 203 } 204 205 /** 206 * Inicializa los event listeners 207 */ 208 function inicializarEventListeners() { 209 const input = document.getElementById('buscar-centros-input'); 210 const button = document.querySelector('.buscar-centros-btn'); 211 212 if (!input) return; 213 214 // Filtrar mientras se escribe (con debounce) 215 let timeoutId; 216 input.addEventListener('input', function() { 217 clearTimeout(timeoutId); 218 timeoutId = setTimeout(function() { 219 filtrarCentros(input.value); 220 }, 300); 221 }); 222 223 // Filtrar al hacer click en el botón 224 if (button) { 225 button.addEventListener('click', function() { 226 filtrarCentros(input.value); 227 }); 228 } 229 230 // Filtrar al presionar Enter 231 input.addEventListener('keypress', function(e) { 232 if (e.key === 'Enter') { 233 e.preventDefault(); 234 filtrarCentros(input.value); 235 } 236 }); 237 } 238 239 /** 240 * Cargar el script de Google Maps 241 */ 242 function cargarGoogleMaps() { 243 // Verificar si ya existe el script 244 if (window.google && window.google.maps) { 245 initBuscarCentrosMapa(); 246 return; 247 } 248 249 // Verificar si ya se está cargando 250 if (document.querySelector('script[src*="maps.googleapis.com"]')) { 251 return; 252 } 253 254 // Crear y cargar el script 255 const script = document.createElement('script');
256 script.src = 'https://maps.googleapis.com/maps/api/js?key=AIzaSyCjSxKoaBzfLYt9bQGb8-F0eI6zfJGHTzk&callback=initBuscarCentrosMapa'; 257 script.async = true; 258 script.defer = true; 259 document.head.appendChild(script); 260 } 261 262 /** 263 * Inicializa el slider mobile con barra de scroll personalizada 264 */ 265 function inicializarSliderMobile() { 266 const listadoScroll = document.querySelector('.listado-centros-scroll'); 267 const panel = document.querySelector('.panel-buscar-centros'); 268 269 /*console.log('Inicializando slider mobile', { 270 listadoScroll: !!listadoScroll, 271 panel: !!panel, 272 width: window.innerWidth 273 });*/ 274 275 if (!listadoScroll || !panel) { 276 /*console.log('No se encontraron elementos necesarios');*/ 277 return; 278 } 279 280 if (window.innerWidth > 480) { 281 /*console.log('Ancho mayor a 480px, no se inicializa');*/ 282 return; 283 } 284 285 // Eliminar TODOS los elementos de indicadores que puedan existir 286 const allIndicators = panel.querySelectorAll('.slider-indicators, .slider-indicator'); 287 allIndicators.forEach(el => { 288 el.remove(); 289 /*console.log('Elemento eliminado:', el.className);*/ 290 }); 291 292 // Buscar y eliminar cualquier div que contenga puntos pequeños 293 const allDivs = panel.querySelectorAll('div'); 294 allDivs.forEach(div => { 295 if (div.style.width === '6px' || div.style.height === '6px' || 296 div.classList.contains('indicator') || div.classList.contains('dot')) { 297 div.remove(); 298 /*console.log('Div sospechoso eliminado');*/ 299 } 300 }); 301 302 // Crear barra de scroll personalizada si no existe 303 let customScrollbar = panel.querySelector('.custom-scrollbar'); 304 if (!customScrollbar) { 305 customScrollbar = document.createElement('div'); 306 customScrollbar.className = 'custom-scrollbar'; 307 308 const thumb = document.createElement('div'); 309 thumb.className = 'custom-scrollbar-thumb'; 310 customScrollbar.appendChild(thumb); 311 312 panel.appendChild(customScrollbar); 313 /*console.log('Barra de scroll creada');*/ 314 } 315 316 const thumb = customScrollbar.querySelector('.custom-scrollbar-thumb'); 317 318 // Función para actualizar la barra de scroll 319 function actualizarScrollbar() { 320 const scrollWidth = listadoScroll.scrollWidth; 321 const clientWidth = listadoScroll.clientWidth; 322 const scrollLeft = listadoScroll.scrollLeft; 323 324 // Contar centros visibles 325 const centrosVisibles = listadoScroll.querySelectorAll('.centro-item:not([style*="display: none"])').length; 326 327 /*console.log('Actualizando scrollbar', { 328 scrollWidth, 329 clientWidth, 330 scrollLeft, 331 centrosVisibles 332 });*/ 333 334 // Ocultar si solo hay un centro o si no hay scroll 335 if (scrollWidth <= clientWidth || centrosVisibles <= 1) { 336 customScrollbar.style.display = 'none'; 337 /*console.log('Contenido no desborda o solo hay un centro, ocultando scrollbar');*/ 338 return; 339 } 340 341 customScrollbar.style.display = 'block'; 342 343 // Obtener el ancho real del contenedor de la scrollbar (que es fijo) 344 const scrollbarWidth = customScrollbar.getBoundingClientRect().width; 345 346 // Calcular ancho del thumb en pÃxeles basado en la proporción de contenido visible 347 const thumbWidthPx = (clientWidth / scrollWidth) * scrollbarWidth; 348 // Calcular posición del thumb en pÃxeles basada en el scroll real 349 const maxScroll = Math.max(scrollWidth - clientWidth, 1); 350 const scrollPercentage = scrollLeft / maxScroll; 351 const availableSpace = scrollbarWidth - thumbWidthPx; 352 const thumbLeftPx = Math.min(scrollPercentage * availableSpace, availableSpace); 353 354 thumb.style.width = thumbWidthPx + 'px'; 355 thumb.style.left = thumbLeftPx + 'px'; 356 357 /*console.log('Scrollbar actualizada', {
358 scrollbarWidth: scrollbarWidth + 'px', 359 thumbWidth: thumbWidthPx + 'px', 360 thumbLeft: thumbLeftPx + 'px', 361 scrollPercentage: scrollPercentage 362 });*/ 363 } 364 365 // Actualizar al hacer scroll 366 listadoScroll.addEventListener('scroll', actualizarScrollbar); 367 368 // Drag en la barra de scroll personalizada 369 let isDragging = false; 370 let startX = 0; 371 let startScrollLeft = 0; 372 373 thumb.addEventListener('mousedown', startDrag); 374 thumb.addEventListener('touchstart', startDrag); 375 376 function startDrag(e) { 377 isDragging = true; 378 startX = e.type === 'mousedown' ? e.clientX : e.touches[0].clientX; 379 startScrollLeft = listadoScroll.scrollLeft; 380 e.preventDefault(); 381 } 382 383 document.addEventListener('mousemove', drag); 384 document.addEventListener('touchmove', drag); 385 386 function drag(e) { 387 if (!isDragging) return; 388 389 const currentX = e.type === 'mousemove' ? e.clientX : e.touches[0].clientX; 390 const diff = currentX - startX; 391 const scrollbarWidth = customScrollbar.offsetWidth; 392 const scrollWidth = listadoScroll.scrollWidth; 393 394 const scrollDiff = (diff / scrollbarWidth) * scrollWidth; 395 listadoScroll.scrollLeft = startScrollLeft + scrollDiff; 396 } 397 398 document.addEventListener('mouseup', stopDrag); 399 document.addEventListener('touchend', stopDrag); 400 401 function stopDrag() { 402 isDragging = false; 403 } 404 405 // Click en track para saltar 406 customScrollbar.addEventListener('click', function(e) { 407 if (e.target === customScrollbar) { 408 const rect = customScrollbar.getBoundingClientRect(); 409 const clickX = e.clientX - rect.left; 410 const percentage = clickX / rect.width; 411 listadoScroll.scrollLeft = percentage * listadoScroll.scrollWidth; 412 } 413 }); 414 415 // Actualizar al cambiar contenido 416 const observer = new MutationObserver(actualizarScrollbar); 417 const listado = listadoScroll.querySelector('.listado-centros'); 418 if (listado) { 419 observer.observe(listado, { 420 childList: true, 421 subtree: true, 422 attributes: true, 423 attributeFilter: ['class', 'style'] 424 }); 425 } 426 427 // Actualizar inicialmente 428 actualizarScrollbar(); 429 430 // Actualizar al cambiar tamaño 431 window.addEventListener('resize', actualizarScrollbar); 432 } 433 434 /** 435 * Inicialización cuando el DOM está listo 436 */ 437 if (document.readyState === 'loading') { 438 document.addEventListener('DOMContentLoaded', function() { 439 if (document.querySelector('.mapa-buscar-centros')) { 440 inicializarEventListeners(); 441 cargarGoogleMaps(); 442 // Delay para asegurar que el contenido esté renderizado 443 setTimeout(inicializarSliderMobile, 500); 444 } 445 }); 446 } else { 447 if (document.querySelector('.mapa-buscar-centros')) { 448 inicializarEventListeners(); 449 cargarGoogleMaps(); 450 // Delay para asegurar que el contenido esté renderizado 451 setTimeout(inicializarSliderMobile, 500); 452 } 453 } 454 455 // Reinicializar slider al cambiar tamaño de ventana 456 let resizeTimeout; 457 window.addEventListener('resize', function() { 458 clearTimeout(resizeTimeout); 459 resizeTimeout = setTimeout(function() { 460 inicializarSliderMobile(); 461 }, 250); 462 }); 463 464})();
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.