/** * Script del mapa - FP Geo Content */ (function() { 'use strict'; // Almacenar instancias de mapas const mapInstances = {}; /** * Inicializar cuando el DOM esté listo */ document.addEventListener('DOMContentLoaded', function() { // Buscar todos los wrappers de mapas document.querySelectorAll('.fp-geo-wrapper').forEach(function(wrapper) { const instance = wrapper.dataset.instance; const configName = 'fpGeoConfig_' + instance; if (window[configName]) { initMap(window[configName]); } }); }); /** * Inicializar un mapa */ function initMap(config) { const mapEl = document.getElementById(config.mapId); if (!mapEl) return; // Configurar opciones de scroll wheel zoom let scrollWheelZoomOption = false; if (config.scrollWheelZoom === 'always') { scrollWheelZoomOption = true; } else if (config.scrollWheelZoom === 'ctrl') { scrollWheelZoomOption = 'ctrl'; } // Crear mapa Leaflet const map = L.map(mapEl, { center: config.center, zoom: config.zoom, minZoom: config.minZoom, maxZoom: config.maxZoom, scrollWheelZoom: scrollWheelZoomOption === 'ctrl' ? false : scrollWheelZoomOption, }); // Añadir capa de tiles L.tileLayer(config.tileUrl, { attribution: config.tileAttribution, subdomains: config.tileSubdomains || 'abc', maxZoom: 19, }).addTo(map); // Crear grupo de marcadores (con o sin cluster) let markersLayer; if (config.clusterEnabled) { markersLayer = L.markerClusterGroup({ showCoverageOnHover: false, maxClusterRadius: 50, }); } else { markersLayer = L.layerGroup(); } // Almacenar datos const mapData = { map: map, markersLayer: markersLayer, allMarkers: [], config: config, activeFilters: {}, }; mapInstances[config.mapId] = mapData; // Configurar control de scroll con Ctrl if (config.scrollWheelZoom === 'ctrl') { initCtrlScrollZoom(mapData); } // Añadir marcadores iniciales addMarkers(mapData, config.markers); // Añadir capa al mapa map.addLayer(markersLayer); // Ajustar zoom para mostrar todos los marcadores fitMapToMarkers(mapData); // Inicializar filtros initFilters(mapData); // Inicializar panel de detalle initDetailPanel(mapData); } /** * Inicializar control de zoom con Ctrl+scroll */ function initCtrlScrollZoom(mapData) { const { map, config } = mapData; const mapEl = document.getElementById(config.mapId); let hintTimeout; // Crear elemento de aviso const scrollHint = document.createElement('div'); scrollHint.className = 'fp-geo-scroll-hint'; scrollHint.textContent = config.i18n.scrollZoomHint || 'Usa Ctrl + scroll para hacer zoom'; mapEl.parentElement.appendChild(scrollHint); // Detectar scroll sin Ctrl mapEl.addEventListener('wheel', function(e) { if (e.ctrlKey) { // Habilitar zoom cuando Ctrl está presionado map.scrollWheelZoom.enable(); scrollHint.classList.remove('visible'); } else { // Mostrar aviso map.scrollWheelZoom.disable(); scrollHint.classList.add('visible'); clearTimeout(hintTimeout); hintTimeout = setTimeout(function() { scrollHint.classList.remove('visible'); }, 1500); } }); // Deshabilitar zoom al soltar Ctrl document.addEventListener('keyup', function(e) { if (e.key === 'Control') { map.scrollWheelZoom.disable(); } }); } /** * Ajustar el zoom del mapa para mostrar todos los marcadores */ function fitMapToMarkers(mapData) { const { map, markersLayer, config } = mapData; // Obtener los límites del grupo de marcadores const layers = markersLayer.getLayers(); if (layers.length === 0) { // Sin marcadores, usar centro y zoom por defecto return; } if (layers.length === 1) { // Un solo marcador: centrar en él con zoom razonable const marker = layers[0]; map.setView(marker.getLatLng(), Math.min(config.zoom || 14, config.maxZoom || 18)); return; } // Varios marcadores: ajustar para mostrar todos const bounds = markersLayer.getBounds(); if (bounds.isValid()) { map.fitBounds(bounds, { padding: [50, 50], // Margen de 50px maxZoom: config.maxZoom || 18, }); } } /** * Crear icono de marcador circular SVG */ function createCircleMarkerIcon(color, isPilot = false) { const size = 36; const strokeWidth = 3; // Crear SVG simple y limpio let svg; if (isPilot) { // Marcador con estrella para pilotos svg = '' + '' + '' + '' + '' + ''; return L.divIcon({ className: 'fp-geo-circle-marker fp-geo-pilot-marker', html: svg, iconSize: [size, size + 12], iconAnchor: [size/2, size + 12], popupAnchor: [0, -(size + 12)], }); } else { // Marcador circular normal con punta svg = '' + '' + '' + '' + ''; return L.divIcon({ className: 'fp-geo-circle-marker', html: svg, iconSize: [size, size + 12], iconAnchor: [size/2, size + 10], popupAnchor: [0, -(size + 10)], }); } } /** * Añadir marcadores al mapa */ function addMarkers(mapData, markers) { const { markersLayer, config } = mapData; // Limpiar marcadores existentes markersLayer.clearLayers(); mapData.allMarkers = []; // Color por defecto const defaultColor = config.markerDefaultColor || '#F97316'; markers.forEach(function(markerData) { let icon; let markerColor = defaultColor; // es_piloto viene directamente del post (actuación), no de los términos let isPilot = markerData.es_piloto === true; // Buscar color de la taxonomía configurada para colores if (config.useCategoryColors && markerData.taxonomies) { // Primero intentar con la taxonomía de leyenda configurada if (config.legendTaxonomy) { const categoryTerms = markerData.taxonomies[config.legendTaxonomy]; if (categoryTerms && categoryTerms.length > 0) { if (categoryTerms[0].color) { markerColor = categoryTerms[0].color; } } } // Si no hay color, buscar en cualquier taxonomía if (markerColor === defaultColor) { for (const taxonomy in markerData.taxonomies) { const terms = markerData.taxonomies[taxonomy]; if (terms && terms.length > 0 && terms[0].color) { markerColor = terms[0].color; break; } } } } // Usar icono personalizado si existe, o círculo de color if (config.markerIcon && config.markerIcon !== '') { icon = L.icon({ iconUrl: config.markerIcon, iconSize: [40, 50], iconAnchor: [20, 50], popupAnchor: [0, -50], }); } else { // Crear marcador circular con el color icon = createCircleMarkerIcon(markerColor, isPilot); } // Crear marcador const marker = L.marker([markerData.lat, markerData.lng], { icon: icon }); // Guardar datos en el marcador marker.markerData = markerData; marker.markerColor = markerColor; // Evento de clic marker.on('click', function() { showDetail(mapData, markerData); }); // Añadir al grupo markersLayer.addLayer(marker); mapData.allMarkers.push(marker); }); // Actualizar contador updateResultsCount(mapData, markers.length); // Debug: mostrar en consola cuántos marcadores se añadieron console.log('[FP Geo] Añadidos ' + markers.length + ' marcadores con color por defecto: ' + defaultColor); } /** * Actualizar contador de resultados */ function updateResultsCount(mapData, count) { const { config } = mapData; const wrapper = document.getElementById(config.mapId + '-wrapper'); if (!wrapper) return; const countEl = wrapper.querySelector('.fp-geo-results-number'); const labelEl = wrapper.querySelector('.fp-geo-results-label'); if (countEl) { countEl.textContent = count; } if (labelEl) { // Si solo hay un tipo de contenido, mostrar su nombre if (config.postTypes && config.postTypes.length === 1) { const postType = config.postTypes[0]; // Usar el nombre del post type en plural/singular const labels = config.postTypeLabels || {}; if (count === 1) { labelEl.textContent = labels[postType]?.singular || postType; } else { labelEl.textContent = labels[postType]?.plural || postType + 's'; } } else { labelEl.textContent = count === 1 ? 'resultado' : 'resultados'; } } } /** * Inicializar filtros */ function initFilters(mapData) { const { config } = mapData; const wrapper = document.getElementById(config.mapId + '-wrapper'); if (!wrapper) return; const filtersEl = wrapper.querySelector('.fp-geo-filters'); if (!filtersEl) return; // Botones de filtro filtersEl.querySelectorAll('.fp-geo-filter-btn').forEach(function(btn) { btn.addEventListener('click', function() { const taxonomy = btn.dataset.taxonomy; const slug = btn.dataset.slug; // Toggle activo btn.classList.toggle('active'); // Actualizar filtros activos if (!mapData.activeFilters[taxonomy]) { mapData.activeFilters[taxonomy] = []; } const index = mapData.activeFilters[taxonomy].indexOf(slug); if (index > -1) { mapData.activeFilters[taxonomy].splice(index, 1); } else { mapData.activeFilters[taxonomy].push(slug); } // Aplicar filtros applyFilters(mapData); }); }); // Botón limpiar const clearBtn = filtersEl.querySelector('.fp-geo-clear-filters'); if (clearBtn) { clearBtn.addEventListener('click', function() { // Limpiar todos los filtros mapData.activeFilters = {}; filtersEl.querySelectorAll('.fp-geo-filter-btn.active').forEach(function(btn) { btn.classList.remove('active'); }); applyFilters(mapData); }); } } /** * Aplicar filtros a los marcadores */ function applyFilters(mapData) { const { markersLayer, allMarkers, activeFilters, config } = mapData; const filterCombine = config.filterCombine || 'OR'; // Verificar si hay filtros activos const hasActiveFilters = Object.values(activeFilters).some(arr => arr.length > 0); let visibleCount = 0; allMarkers.forEach(function(marker) { const data = marker.markerData; let shouldShow = true; if (hasActiveFilters) { if (filterCombine === 'AND') { // Debe cumplir TODOS los filtros shouldShow = Object.entries(activeFilters).every(function([taxonomy, slugs]) { if (slugs.length === 0) return true; const markerTerms = data.taxonomies && data.taxonomies[taxonomy]; if (!markerTerms) return false; return slugs.some(function(slug) { return markerTerms.some(function(term) { return term.slug === slug; }); }); }); } else { // Debe cumplir AL MENOS UN filtro (OR) shouldShow = Object.entries(activeFilters).some(function([taxonomy, slugs]) { if (slugs.length === 0) return false; const markerTerms = data.taxonomies && data.taxonomies[taxonomy]; if (!markerTerms) return false; return slugs.some(function(slug) { return markerTerms.some(function(term) { return term.slug === slug; }); }); }); // Si no hay filtros activos en ninguna taxonomía, mostrar todo if (!Object.values(activeFilters).some(arr => arr.length > 0)) { shouldShow = true; } } } if (shouldShow) { markersLayer.addLayer(marker); visibleCount++; } else { markersLayer.removeLayer(marker); } }); // Actualizar contador de resultados updateResultsCount(mapData, visibleCount); } /** * Inicializar panel de detalle */ function initDetailPanel(mapData) { const { config } = mapData; const wrapper = document.getElementById(config.mapId + '-wrapper'); if (!wrapper) return; const detailEl = document.getElementById(config.mapId + '-detail'); const overlayEl = document.getElementById(config.mapId + '-overlay'); if (!detailEl) return; // Botón cerrar const closeBtn = detailEl.querySelector('.fp-geo-detail-close'); if (closeBtn) { closeBtn.addEventListener('click', function() { hideDetail(mapData); }); } // Cerrar al hacer clic en overlay (modal) if (overlayEl) { overlayEl.addEventListener('click', function() { hideDetail(mapData); }); } // Cerrar con Escape document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { hideDetail(mapData); } }); } /** * Mostrar detalle de un marcador */ function showDetail(mapData, data) { const { config } = mapData; const detailEl = document.getElementById(config.mapId + '-detail'); const overlayEl = document.getElementById(config.mapId + '-overlay'); if (!detailEl) return; // Rellenar taxonomías const taxEl = detailEl.querySelector('.fp-geo-detail-taxonomies'); if (taxEl) { taxEl.innerHTML = ''; if (data.taxonomies) { for (const taxonomy in data.taxonomies) { data.taxonomies[taxonomy].forEach(function(term) { const tag = document.createElement('span'); tag.className = 'fp-geo-detail-tax-tag'; tag.style.cssText = term.color ? 'background-color: ' + term.color + ';' : ''; let html = ''; if (term.icono) { html += ''; } html += '' + term.name + ''; tag.innerHTML = html; taxEl.appendChild(tag); }); } } } // Thumbnail const thumbEl = detailEl.querySelector('.fp-geo-detail-thumbnail'); if (thumbEl) { if (data.thumbnail) { thumbEl.innerHTML = '' + data.title + ''; thumbEl.classList.add('has-image'); } else { thumbEl.innerHTML = ''; thumbEl.classList.remove('has-image'); } } // Título const titleEl = detailEl.querySelector('.fp-geo-detail-title'); if (titleEl) { titleEl.textContent = data.title; } // Excerpt const excerptEl = detailEl.querySelector('.fp-geo-detail-excerpt'); if (excerptEl) { excerptEl.textContent = data.excerpt || ''; } // Ubicación const locationEl = detailEl.querySelector('.fp-geo-detail-location'); if (locationEl) { const locationParts = [data.direccion, data.localidad].filter(Boolean); if (locationParts.length > 0) { locationEl.innerHTML = '' + locationParts.join(', ') + ''; locationEl.style.display = 'flex'; } else { locationEl.style.display = 'none'; } } // Contacto const contactEl = detailEl.querySelector('.fp-geo-detail-contact'); if (contactEl) { let contactHtml = ''; if (data.telefono) { contactHtml += '
📞 ' + data.telefono + '
'; } if (data.email) { contactHtml += '
✉️ ' + data.email + '
'; } if (data.web) { contactHtml += '
🌐 Sitio web
'; } contactEl.innerHTML = contactHtml; } // Link - actualizar texto si está configurado const linkEl = detailEl.querySelector('.fp-geo-detail-link'); if (linkEl) { linkEl.href = data.url; if (config.detailButtonText) { linkEl.textContent = config.detailButtonText; } } // Footer - mostrar/ocultar según configuración const footerEl = detailEl.querySelector('.fp-geo-detail-footer'); if (footerEl) { footerEl.style.display = config.showDetailButton ? 'block' : 'none'; } // Mostrar panel detailEl.classList.add('visible'); if (overlayEl) { overlayEl.classList.add('visible'); } } /** * Ocultar panel de detalle */ function hideDetail(mapData) { const { config } = mapData; const detailEl = document.getElementById(config.mapId + '-detail'); const overlayEl = document.getElementById(config.mapId + '-overlay'); if (detailEl) { detailEl.classList.remove('visible'); } if (overlayEl) { overlayEl.classList.remove('visible'); } } // Exponer API pública window.FPGeoContent = { getMap: function(mapId) { return mapInstances[mapId]; }, refreshMarkers: function(mapId, markers) { const mapData = mapInstances[mapId]; if (mapData) { addMarkers(mapData, markers); } }, applyFilters: function(mapId) { const mapData = mapInstances[mapId]; if (mapData) { applyFilters(mapData); } }, fitToMarkers: function(mapId) { const mapData = mapInstances[mapId]; if (mapData) { fitMapToMarkers(mapData); } }, }; })();