631 lines
22 KiB
JavaScript
631 lines
22 KiB
JavaScript
/**
|
|
* 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 = '<svg xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + (size + 12) + '" viewBox="0 0 ' + size + ' ' + (size + 12) + '">' +
|
|
'<circle cx="' + (size/2) + '" cy="' + (size/2) + '" r="' + (size/2 - 2) + '" fill="rgba(0,0,0,0.3)" transform="translate(2, 2)"/>' +
|
|
'<circle cx="' + (size/2) + '" cy="' + (size/2) + '" r="' + (size/2 - strokeWidth) + '" fill="' + color + '" stroke="white" stroke-width="' + strokeWidth + '"/>' +
|
|
'<polygon points="' + (size/2) + ',' + (size + 2) + ' ' + (size/2 + 5) + ',' + (size + 12) + ' ' + (size/2 - 5) + ',' + (size + 12) + '" fill="' + color + '" stroke="white" stroke-width="1"/>' +
|
|
'<text x="' + (size/2) + '" y="' + (size/2 + 4) + '" text-anchor="middle" fill="white" font-size="14" font-weight="bold">★</text>' +
|
|
'</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 = '<svg xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + (size + 12) + '" viewBox="0 0 ' + size + ' ' + (size + 12) + '">' +
|
|
'<circle cx="' + (size/2) + '" cy="' + (size/2) + '" r="' + (size/2 - 2) + '" fill="rgba(0,0,0,0.3)" transform="translate(2, 2)"/>' +
|
|
'<circle cx="' + (size/2) + '" cy="' + (size/2) + '" r="' + (size/2 - strokeWidth) + '" fill="' + color + '" stroke="white" stroke-width="' + strokeWidth + '"/>' +
|
|
'<polygon points="' + (size/2) + ',' + (size + 10) + ' ' + (size/2 + 6) + ',' + (size - 4) + ' ' + (size/2 - 6) + ',' + (size - 4) + '" fill="' + color + '" stroke="white" stroke-width="2" stroke-linejoin="round"/>' +
|
|
'</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 += '<img src="' + term.icono + '" alt="" class="fp-geo-detail-tax-icon">';
|
|
}
|
|
html += '<span>' + term.name + '</span>';
|
|
tag.innerHTML = html;
|
|
taxEl.appendChild(tag);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Thumbnail
|
|
const thumbEl = detailEl.querySelector('.fp-geo-detail-thumbnail');
|
|
if (thumbEl) {
|
|
if (data.thumbnail) {
|
|
thumbEl.innerHTML = '<img src="' + data.thumbnail + '" alt="' + 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 = '<span>' + locationParts.join(', ') + '</span>';
|
|
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 += '<div class="fp-geo-detail-contact-item">📞 <a href="tel:' + data.telefono.replace(/\s/g, '') + '">' + data.telefono + '</a></div>';
|
|
}
|
|
if (data.email) {
|
|
contactHtml += '<div class="fp-geo-detail-contact-item">✉️ <a href="mailto:' + data.email + '">' + data.email + '</a></div>';
|
|
}
|
|
if (data.web) {
|
|
contactHtml += '<div class="fp-geo-detail-contact-item">🌐 <a href="' + data.web + '" target="_blank">Sitio web</a></div>';
|
|
}
|
|
|
|
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);
|
|
}
|
|
},
|
|
};
|
|
|
|
})();
|