link.href === href);
if (existing) {
resolve();
return;
}
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
},
highlightMarker(accommodationId) {
// Primero limpiar todos los marcadores para evitar estados inconsistentes
this.markers.forEach(m => {
const el = m.getElement();
const priceEl = el.querySelector('.simple-price-marker');
if (priceEl) {
priceEl.classList.remove('highlighted');
}
// Restaurar z-index original
if (el.dataset.originalZindex !== undefined) {
el.style.zIndex = el.dataset.originalZindex;
} else {
el.style.zIndex = '';
}
el.classList.remove('marker-highlighted');
});
// Ahora destacar el marcador específico
const marker = this.markers.find(m => {
const el = m.getElement();
return el && String(el.getAttribute('data-accommodation-id')) === String(accommodationId);
});
if (marker) {
const el = marker.getElement();
const priceEl = el.querySelector('.simple-price-marker');
if (priceEl) {
priceEl.classList.add('highlighted');
}
// Guardar z-index original si no está guardado
if (!el.dataset.originalZindex) {
el.dataset.originalZindex = el.style.zIndex || '';
}
el.style.zIndex = '9999';
el.classList.add('marker-highlighted');
// Mover la cámara del mapa hasta el marcador manteniendo el zoom actual
const lngLat = marker.getLngLat();
this.map.flyTo({
center: [lngLat.lng, lngLat.lat],
duration: 1000, // Duración de la animación en ms
essential: true // Esta animación es considerada esencial
});
}
},
unhighlightMarker(accommodationId) {
const marker = this.markers.find(m => {
const el = m.getElement();
return el && String(el.getAttribute('data-accommodation-id')) === String(accommodationId);
});
if (marker) {
const el = marker.getElement();
const priceEl = el.querySelector('.simple-price-marker');
if (priceEl) {
// Limpiar todas las clases relacionadas con el estado de hover
priceEl.classList.remove('highlighted');
// También limpiar cualquier estado de hover nativo que pueda haber quedado
priceEl.classList.remove('show-price');
// Solo restaurar el texto si no está en estado clicked
if (!priceEl.classList.contains('clicked')) {
// Para marcadores con precio oculto, limpiar el texto
if (priceEl.classList.contains('hidden-price')) {
priceEl.textContent = '';
}
}
}
// Restaurar z-index original
if (el.dataset.originalZindex !== undefined) {
el.style.zIndex = el.dataset.originalZindex;
} else {
el.style.zIndex = '';
}
el.classList.remove('marker-highlighted');
}
},
updateMarkers(accommodations) {
// Verificar que el mapa esté inicializado
if (!this.map || !this.mapInitialized) {
return;
}
// Limpiar marcadores existentes
this.markers.forEach(marker => marker.remove());
this.markers = [];
accommodations.forEach(accommodation => {
const lat = accommodation.lat;
const lng = accommodation.lng;
const price = accommodation.totalPrice;
const shouldDisplayPrice = accommodation.display === true;
const el = document.createElement('div');
// Aplicar clase condicional basada en la propiedad display
const markerClass = shouldDisplayPrice ? 'simple-price-marker' : 'simple-price-marker hidden-price';
const displayText = shouldDisplayPrice ? price : '';
el.innerHTML = `
${displayText}
`;
try {
// Usar la URL ya construida desde PHP que incluye todos los parámetros necesarios
const accommodationUrl = accommodation.url;
// Crear el popup
const fromLabel = 'Desde';
const popupContent = document.createElement('div');
popupContent.className = 'card card--map';
const link = document.createElement('a');
link.href = accommodationUrl;
link.className = 'card__link';
popupContent.appendChild(link);
if (accommodation.picture) {
const img = document.createElement('img');
img.src = accommodation.picture;
img.className = 'card__image';
img.alt = accommodation.name || 'Alojamiento';
img.addEventListener('error', () => {
img.style.display = 'none';
});
popupContent.appendChild(img);
}
const content = document.createElement('div');
content.className = 'card__content';
const title = document.createElement('h3');
title.className = 'card__title';
title.textContent = accommodation.name || 'Alojamiento';
content.appendChild(title);
const priceWrap = document.createElement('div');
priceWrap.className = 'card__price';
priceWrap.appendChild(document.createTextNode(fromLabel + ' '));
const priceMoney = document.createElement('span');
priceMoney.className = 'card__price-money';
priceMoney.textContent = price;
priceWrap.appendChild(priceMoney);
priceWrap.appendChild(document.createTextNode(' ' + (accommodation.priceSuffix || '')));
content.appendChild(priceWrap);
popupContent.appendChild(content);
const popup = new mapboxgl.Popup({ offset: 45, closeButton: true });
if (typeof popup.setDOMContent === 'function') {
popup.setDOMContent(popupContent);
} else {
popup.setHTML(popupContent.outerHTML);
}
// Crear el marcador
const marker = new mapboxgl.Marker(el)
.setLngLat([lng, lat])
.setPopup(popup)
.addTo(this.map);
// Asignar el atributo al contenedor real del marker
const markerElement = marker.getElement();
markerElement.setAttribute('data-accommodation-id', accommodation.id);
// Añadir listeners para hover y click
const priceEl = el.querySelector('.simple-price-marker');
if (priceEl) {
// Funcionalidad estándar de hover para todos los marcadores
priceEl.addEventListener('mouseenter', () => {
markerElement.dataset.originalZindex = markerElement.style.zIndex || '';
markerElement.style.zIndex = '99999';
markerElement.classList.add('marker-highlighted');
});
priceEl.addEventListener('mouseleave', () => {
markerElement.style.zIndex = markerElement.dataset.originalZindex || '';
markerElement.classList.remove('marker-highlighted');
});
// Funcionalidad especial para marcadores con precio oculto
if (!shouldDisplayPrice) {
// Mostrar precio al hacer hover
priceEl.addEventListener('mouseenter', () => {
priceEl.classList.add('show-price');
priceEl.textContent = price;
});
priceEl.addEventListener('mouseleave', () => {
if (!priceEl.classList.contains('clicked')) {
priceEl.classList.remove('show-price');
priceEl.textContent = '';
}
});
// Manejar click en marcadores ocultos
priceEl.addEventListener('click', (e) => {
// No prevenir la propagación para permitir que el popup se abra
// Remover clase clicked de todos los otros marcadores
this.markers.forEach(otherMarker => {
const otherPriceEl = otherMarker.getElement().querySelector('.simple-price-marker');
if (otherPriceEl && otherPriceEl !== priceEl) {
otherPriceEl.classList.remove('clicked', 'show-price');
otherPriceEl.textContent = '';
}
});
// Toggle del estado clicked en el marcador actual
if (priceEl.classList.contains('clicked')) {
priceEl.classList.remove('clicked', 'show-price');
priceEl.textContent = '';
} else {
priceEl.classList.add('clicked', 'show-price');
priceEl.textContent = price;
}
});
}
}
this.markers.push(marker);
} catch (e) {
console.error('Error creating marker element:', e);
}
});
// Solo ajustar bounds automáticamente si:
// 1. Hay marcadores
// 2. No ha habido interacción del usuario
// 3. Es la primera carga (no hay centro inicial establecido por alojamientos)
if (this.markers.length > 0 && !this.userAct) {
const bounds = new mapboxgl.LngLatBounds();
this.markers.forEach(marker => bounds.extend(marker.getLngLat()));
// Solo hacer fitBounds si no hay un alojamiento específico como centro inicial
const hasSpecificCenter = this.accommodations && this.accommodations.length > 0 &&
this.accommodations[0].lat && this.accommodations[0].lng &&
this.accommodations[0].lat !== 0 && this.accommodations[0].lng !== 0;
if (!hasSpecificCenter) {
this.map.fitBounds(bounds, { padding: 100, maxZoom: 15 });
}
}
}
}">