/* global L */ // Erzeugt Leaflet-Marker für Places inkl. Kategorie-Icon (L.divIcon) und // Popup-Inhalt. Popup-Inhalte werden als DOM-Nodes (nicht als HTML-String) // erzeugt, damit Place-Daten niemals ungesichert als innerHTML landen. (function () { const { getCategoryById } = EventMap.config; const { getPlaceCategory, getPlaceLatLng } = EventMap.models; const { el } = EventMap.utils; const iconCache = new Map(); function buildDivIcon(category) { return L.divIcon({ className: 'eventmap-marker', html: `${category.icon}`, iconSize: [32, 32], iconAnchor: [16, 16], popupAnchor: [0, -16], }); } function getIconForCategory(categoryId) { if (!iconCache.has(categoryId)) { iconCache.set(categoryId, buildDivIcon(getCategoryById(categoryId))); } return iconCache.get(categoryId); } function buildPopupContent(place) { const category = getPlaceCategory(place); return el('div', { className: 'eventmap-popup' }, [ el('strong', { className: 'eventmap-popup__title', text: place.name }), el('div', { className: 'eventmap-popup__category', style: { color: category.color }, text: `${category.icon} ${category.label}`, }), el('p', { className: 'eventmap-popup__desc', text: place.shortDescription }), ]); } const COST_LABELS = { free: 'Kostenlos', paid: 'Kostenpflichtig', mixed: 'Teils kostenpflichtig', unknown: null }; function formatTooltipAge(ageRecommendation) { const { min, max } = ageRecommendation || {}; if (min == null && max == null) return null; if (min != null && max != null) return `${min}–${max} Jahre`; if (min != null) return `Ab ${min} Jahren`; return `Bis ${max} Jahre`; } /** Kompakte Hover-Vorschau (Leaflet-Tooltip), zusätzlich zum Klick-Popup. */ function buildTooltipContent(place) { const category = getPlaceCategory(place); const facts = []; const age = formatTooltipAge(place.ageRecommendation); if (age) facts.push(age); const costLabel = COST_LABELS[(place.cost || {}).type]; if (costLabel) facts.push(costLabel); if (typeof place.rating === 'number') facts.push(`★ ${place.rating.toFixed(1)}`); const children = [ el('strong', { className: 'eventmap-tooltip__title', text: place.name }), el('div', { className: 'eventmap-tooltip__category', style: { color: category.color }, text: `${category.icon} ${category.label}`, }), ]; if (place.shortDescription) { children.push(el('p', { className: 'eventmap-tooltip__desc', text: place.shortDescription })); } if (facts.length > 0) { children.push(el('p', { className: 'eventmap-tooltip__facts', text: facts.join(' · ') })); } return el('div', { className: 'eventmap-tooltip' }, children); } /** * Erzeugt einen Leaflet-Marker für ein Place-Objekt. * @param {object} place * @param {{onSelect?: (placeId: string) => void}} [handlers] */ function createMarkerForPlace(place, { onSelect } = {}) { const { lat, lng } = getPlaceLatLng(place); const marker = L.marker([lat, lng], { icon: getIconForCategory(place.category) }); marker.bindPopup(buildPopupContent(place)); // Hover-Vorschau zusätzlich zum Klick-Popup: sticky folgt dem Mauszeiger, // damit die Vorschau nicht die Position wechselt, während man über den // Marker fährt. Schließt sich automatisch bei mouseout bzw. wenn der // Klick-Popup geöffnet wird. marker.bindTooltip(buildTooltipContent(place), { direction: 'top', offset: [0, -18], opacity: 0.97, sticky: false, className: 'eventmap-tooltip-wrapper', }); marker.on('click', () => { if (onSelect) { onSelect(place.id); } }); return marker; } let userLocationIcon = null; function getUserLocationIcon() { if (!userLocationIcon) { userLocationIcon = L.divIcon({ className: 'eventmap-user-marker', html: '', iconSize: [22, 22], iconAnchor: [11, 11], }); } return userLocationIcon; } /** * Erzeugt einen (nicht klickbaren) Marker für den übermittelten/manuell * eingegebenen Nutzerstandort (Gitea Issue #2 "Standort auf Karte * anzeigen"). Bewusst kein Popup/Interaktion, da es kein Ausflugsziel ist. */ function createUserLocationMarker(lat, lng) { return L.marker([lat, lng], { icon: getUserLocationIcon(), interactive: false, keyboard: false, zIndexOffset: 1000, }); } EventMap.map.createMarkerForPlace = createMarkerForPlace; EventMap.map.createUserLocationMarker = createUserLocationMarker; })();