Geoposicionador de cosas

This commit is contained in:
Jose Ibáñez
2026-02-13 13:14:41 +01:00
commit a17bd50744
10 changed files with 3440 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
/**
* Estilos del admin - FP Geo Content
*/
.fp-geo-settings {
max-width: 1000px;
}
.fp-geo-settings h1 {
display: flex;
align-items: center;
gap: 10px;
color: #1E6B52;
}
.fp-geo-settings h1 .dashicons {
font-size: 28px;
width: 28px;
height: 28px;
}
.fp-geo-settings-intro {
background: linear-gradient(135deg, #1E6B52 0%, #2A9D8F 100%);
color: white;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
.fp-geo-settings-intro p {
margin: 0;
font-size: 15px;
}
.fp-geo-settings h2 {
color: #1E6B52;
border-bottom: 2px solid #1E6B52;
padding-bottom: 10px;
margin-top: 30px;
}
.fp-geo-settings h3 {
color: #333;
margin-top: 25px;
}
.fp-geo-settings-help {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
margin-top: 30px;
}
.fp-geo-shortcode {
display: block;
background: #f0f0f0;
padding: 15px 20px;
border-radius: 6px;
font-size: 16px;
margin: 15px 0;
}
.fp-geo-settings pre {
background: #f8f9fa;
padding: 15px;
border-radius: 6px;
overflow-x: auto;
}
.fp-geo-settings pre code {
background: none;
padding: 0;
}
.fp-geo-settings table code {
background: #f0f0f0;
padding: 2px 6px;
border-radius: 3px;
font-size: 12px;
}
/* Media uploader */
.fp-geo-media-upload {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.fp-geo-image-preview {
border: 2px dashed #ccc;
border-radius: 6px;
padding: 10px;
background: #f9f9f9;
}
.fp-geo-image-preview img {
display: block;
border-radius: 4px;
}
.fp-geo-upload-btn,
.fp-geo-remove-btn {
margin-top: 0 !important;
}
.fp-geo-remove-btn {
color: #dc3232;
}
/* Color picker */
.fp-geo-color-picker {
width: 60px;
height: 40px;
padding: 2px;
cursor: pointer;
border: 1px solid #ddd;
border-radius: 4px;
}
/* Category color options */
.fp-geo-category-color-options {
background: #f9f9f9;
padding: 15px;
border-radius: 6px;
border: 1px solid #e0e0e0;
}
.fp-geo-category-color-options select {
margin-left: 5px;
}
+514
View File
@@ -0,0 +1,514 @@
/**
* Estilos base del mapa - FP Geo Content
*
* Estilos mínimos para layout y funcionalidad.
* Los estilos visuales se pueden personalizar desde:
* - Ajustes > FP Geo Content > CSS Personalizado
* - O desde el tema activo
*/
/* === LAYOUT BASE === */
.fp-geo-wrapper {
position: relative;
width: 100%;
font-family: inherit;
}
.fp-geo-map-container {
position: relative;
display: flex;
width: 100%;
}
.fp-geo-map {
flex: 1;
min-height: 400px;
z-index: 1;
}
/* === FILTROS - LAYOUT === */
.fp-geo-filters {
padding: 1rem;
}
.fp-geo-filters-inner {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: flex-start;
}
.fp-geo-filter-group {
flex: 1;
min-width: 200px;
}
.fp-geo-filter-label {
display: block;
font-size: 0.875rem;
margin-bottom: 0.5rem;
font-weight: 500;
}
.fp-geo-filter-buttons {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.fp-geo-filter-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
border: 1px solid currentColor;
border-radius: 4px;
font-size: 0.875rem;
cursor: pointer;
transition: all 0.2s ease;
background: transparent;
}
.fp-geo-filter-icon {
width: 18px;
height: 18px;
}
.fp-geo-clear-filters {
padding: 8px 16px;
border-radius: 4px;
font-size: 0.8rem;
cursor: pointer;
background: transparent;
font-weight: bold;
}
/* Footer de filtros con contador */
.fp-geo-filters-footer {
display: flex;
align-items: center;
gap: 16px;
margin-top: 8px;
flex-wrap: wrap;
}
.fp-geo-results-count {
font-size: 0.875rem;
}
.fp-geo-results-number {
font-weight: 600;
}
/* === LEYENDA - LAYOUT === */
.fp-geo-legend {
position: absolute;
bottom: 20px;
right: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
padding: 14px 18px;
z-index: 800;
min-width: 180px;
}
.fp-geo-sidebar-left .fp-geo-legend {
right: auto;
left: 20px;
}
.fp-geo-legend-title {
font-size: 0.85rem;
font-weight: 700;
margin-bottom: 10px;
letter-spacing: 0.01em;
}
/* Secciones */
.fp-geo-legend-section {
margin-bottom: 4px;
}
.fp-geo-legend-section-title {
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: #6b7280;
margin-bottom: 6px;
}
.fp-geo-legend-items {
display: flex;
flex-direction: column;
gap: 7px;
}
.fp-geo-legend-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.85rem;
line-height: 1.3;
}
/* Marcador circular (iniciativas) */
.fp-geo-legend-marker {
width: 14px;
height: 14px;
border-radius: 50%;
border: 2px solid white;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
flex-shrink: 0;
}
/* Sección Bloque Piloto — separada visualmente */
.fp-geo-legend-pilot-section {
margin-top: 8px;
padding-top: 10px;
border-top: 1px solid #e5e7eb;
}
.fp-geo-legend-marker-pilot {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 28px;
}
.fp-geo-legend-marker-pilot svg {
display: block;
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));
}
.fp-geo-legend-pilot-desc {
font-size: 0.7rem;
color: #9ca3af;
margin: 2px 0 0 0;
line-height: 1.35;
}
/* === AVISO DE SCROLL === */
.fp-geo-scroll-hint {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.75);
color: white;
padding: 12px 24px;
border-radius: 25px;
font-size: 0.9rem;
z-index: 900;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
white-space: nowrap;
}
.fp-geo-scroll-hint.visible {
opacity: 1;
}
/* === PANEL DE DETALLE - LAYOUT === */
.fp-geo-detail {
position: absolute;
background: #fff;
z-index: 1000;
overflow: hidden;
transition: transform 0.3s ease, opacity 0.3s ease;
}
/* Sidebar derecha (por defecto) */
.fp-geo-sidebar {
top: 0;
right: 0;
width: 350px;
max-width: 40%;
height: 100%;
box-shadow: -4px 0 20px rgba(0,0,0,0.15);
transform: translateX(100%);
opacity: 0;
pointer-events: none;
}
.fp-geo-sidebar.visible {
transform: translateX(0);
opacity: 1;
pointer-events: auto;
}
/* Sidebar izquierda */
.fp-geo-sidebar-left .fp-geo-sidebar {
right: auto;
left: 0;
transform: translateX(-100%);
box-shadow: 4px 0 20px rgba(0,0,0,0.15);
}
.fp-geo-sidebar-left .fp-geo-sidebar.visible {
transform: translateX(0);
}
/* Modal */
.fp-geo-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0.9);
width: 90%;
max-width: 500px;
max-height: 80vh;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
opacity: 0;
pointer-events: none;
z-index: 10001;
}
.fp-geo-modal.visible {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
pointer-events: auto;
}
.fp-geo-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 10000;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.fp-geo-overlay.visible {
opacity: 1;
pointer-events: auto;
}
/* === CONTENIDO DEL DETALLE === */
.fp-geo-detail-content {
height: 100%;
overflow-y: auto;
}
.fp-geo-detail-close {
position: absolute;
top: 10px;
right: 10px;
width: 32px;
height: 32px;
background: rgba(255,255,255,0.9);
border: none;
border-radius: 50%;
font-size: 1.5rem;
line-height: 1;
cursor: pointer;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
}
.fp-geo-detail-header {
padding: 1.5rem;
}
.fp-geo-detail-taxonomies {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.fp-geo-detail-tax-tag {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 10px;
border-radius: 20px;
font-size: 0.75rem;
}
.fp-geo-detail-tax-icon {
width: 14px;
height: 14px;
}
.fp-geo-detail-thumbnail {
width: 100%;
aspect-ratio: 16/9;
overflow: hidden;
display: none;
}
.fp-geo-detail-thumbnail.has-image {
display: block;
}
.fp-geo-detail-thumbnail img {
width: 100%;
height: 100%;
object-fit: cover;
}
.fp-geo-detail-title {
font-size: 1.25rem;
font-weight: 600;
margin: 0;
padding: 1.25rem 1.25rem 0.5rem;
line-height: 1.3;
}
.fp-geo-detail-excerpt {
font-size: 0.9rem;
line-height: 1.5;
padding: 0 1.25rem;
margin-bottom: 1rem;
}
.fp-geo-detail-meta {
padding: 0 1.25rem;
margin-bottom: 1rem;
}
.fp-geo-detail-location,
.fp-geo-detail-contact {
font-size: 0.85rem;
}
.fp-geo-detail-location {
display: flex;
align-items: flex-start;
gap: 8px;
margin-bottom: 0.75rem;
}
.fp-geo-detail-contact-item {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 0.5rem;
}
.fp-geo-detail-contact-item a {
text-decoration: none;
}
.fp-geo-detail-footer {
padding: 1.25rem;
border-top: 1px solid #eee;
}
.fp-geo-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 10px 24px;
border: none;
border-radius: 4px;
font-size: 0.9rem;
font-weight: 500;
text-decoration: none;
cursor: pointer;
}
/* === MARCADORES === */
.fp-geo-circle-marker {
background: transparent !important;
border: none !important;
}
.fp-geo-circle-marker svg {
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
}
/* === CLUSTERS === */
.marker-cluster {
background-clip: padding-box;
border-radius: 20px;
}
.marker-cluster div {
width: 30px;
height: 30px;
margin-left: 5px;
margin-top: 5px;
text-align: center;
border-radius: 15px;
font: 12px sans-serif;
font-weight: bold;
line-height: 30px;
}
/* === ERROR === */
.fp-geo-error {
background: #fee;
color: #c00;
padding: 15px 20px;
border-radius: 8px;
border-left: 4px solid #c00;
}
/* === RESPONSIVE === */
@media (max-width: 768px) {
.fp-geo-filters-inner {
flex-direction: column;
align-items: stretch;
}
.fp-geo-filter-group {
min-width: 100%;
}
.fp-geo-sidebar {
width: 100%;
max-width: 100%;
height: 60%;
top: auto;
bottom: 0;
right: 0;
left: 0;
transform: translateY(100%);
border-radius: 12px 12px 0 0;
box-shadow: 0 -4px 20px rgba(0,0,0,0.15);
}
.fp-geo-sidebar.visible {
transform: translateY(0);
}
.fp-geo-sidebar-left .fp-geo-sidebar {
right: 0;
left: 0;
transform: translateY(100%);
}
.fp-geo-sidebar-left .fp-geo-sidebar.visible {
transform: translateY(0);
}
.fp-geo-modal {
width: 95%;
max-height: 90vh;
}
.fp-geo-legend {
bottom: 10px;
right: 10px;
left: auto;
padding: 10px 12px;
}
.fp-geo-sidebar-left .fp-geo-legend {
right: 10px;
left: auto;
}
}
+88
View File
@@ -0,0 +1,88 @@
/**
* Scripts del admin - FP Geo Content
*/
(function($) {
'use strict';
$(document).ready(function() {
// Copiar shortcode al portapapeles
$('.fp-geo-shortcode').on('click', function() {
const text = $(this).text();
navigator.clipboard.writeText(text).then(function() {
alert('Shortcode copiado al portapapeles');
});
}).css('cursor', 'pointer').attr('title', 'Clic para copiar');
// Media uploader para icono personalizado
let mediaUploader;
$('.fp-geo-upload-btn').on('click', function(e) {
e.preventDefault();
const targetId = $(this).data('target');
const $input = $('#' + targetId);
const $preview = $('#' + targetId + '_preview');
const $removeBtn = $('.fp-geo-remove-btn[data-target="' + targetId + '"]');
// Si el uploader ya existe, abrirlo
if (mediaUploader) {
mediaUploader.open();
return;
}
// Crear el media uploader
mediaUploader = wp.media({
title: 'Seleccionar icono de marcador',
button: {
text: 'Usar este icono'
},
multiple: false,
library: {
type: 'image'
}
});
// Cuando se selecciona una imagen
mediaUploader.on('select', function() {
const attachment = mediaUploader.state().get('selection').first().toJSON();
// Actualizar input
$input.val(attachment.id);
// Mostrar preview
const imgUrl = attachment.sizes.thumbnail ? attachment.sizes.thumbnail.url : attachment.url;
$preview.html('<img src="' + imgUrl + '" alt="" style="max-width: 60px; height: auto;">').show();
// Mostrar botón eliminar
$removeBtn.show();
});
mediaUploader.open();
});
// Eliminar imagen
$('.fp-geo-remove-btn').on('click', function(e) {
e.preventDefault();
const targetId = $(this).data('target');
const $input = $('#' + targetId);
const $preview = $('#' + targetId + '_preview');
$input.val('');
$preview.html('').hide();
$(this).hide();
});
// Toggle opciones de color por categoría
$('input[name="fp_geo_content_options[use_category_colors]"]').on('change', function() {
const $options = $('.fp-geo-category-color-options');
if ($(this).is(':checked')) {
$options.slideDown();
} else {
$options.slideUp();
}
}).trigger('change');
});
})(jQuery);
+630
View File
@@ -0,0 +1,630 @@
/**
* 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);
}
},
};
})();