// Kleine DOM-Helfer. Wichtig: Daten aus Places werden NIE per innerHTML // eingefügt, sondern immer über textContent bzw. DOM-APIs (el()). // // Klassisches Script (kein ES-Modul): Die App wird per file:// direkt aus // einem synchronisierten Ordner geöffnet, daher können keine ES-Module // (import/export) verwendet werden (Browser blockieren deren Nachladen // unter file:// per CORS-Policy). Stattdessen hängen alle Dateien ihre // öffentliche API an das globale Namespace-Objekt window.EventMap. // Diese Datei wird als erste geladen und legt den Namespace an. window.EventMap = window.EventMap || {}; window.EventMap.utils = window.EventMap.utils || {}; window.EventMap.config = window.EventMap.config || {}; window.EventMap.models = window.EventMap.models || {}; window.EventMap.store = window.EventMap.store || {}; window.EventMap.map = window.EventMap.map || {}; window.EventMap.ui = window.EventMap.ui || {}; (function () { /** * Erzeugt ein DOM-Element. * @param {string} tag Tag-Name, z.B. 'div' * @param {object} [props] Eigenschaften: className, attrs{}, dataset{}, style{}, * text (setzt textContent), on{ click: fn, ... } für Event-Listener * @param {Array} [children] Kindknoten oder Strings (werden als Text angehängt) */ function el(tag, props = {}, children = []) { const node = document.createElement(tag); if (props.className) { node.className = props.className; } if (props.text !== undefined) { node.textContent = props.text; } if (props.attrs) { for (const [key, value] of Object.entries(props.attrs)) { if (value !== undefined && value !== null) { node.setAttribute(key, value); } } } if (props.dataset) { for (const [key, value] of Object.entries(props.dataset)) { node.dataset[key] = value; } } if (props.style) { Object.assign(node.style, props.style); } if (props.on) { for (const [event, handler] of Object.entries(props.on)) { node.addEventListener(event, handler); } } for (const child of children) { if (child === null || child === undefined) continue; if (typeof child === 'string') { node.appendChild(document.createTextNode(child)); } else { node.appendChild(child); } } return node; } /** * Escaped HTML-Sonderzeichen. Nur als Sicherheitsnetz gedacht, falls an * einzelnen Stellen doch einmal mit HTML-Strings gearbeitet werden muss * (z.B. Leaflet-Popups, die intern innerHTML nutzen). Für normales * Rendering bitte el()/textContent verwenden. */ function escapeHtml(value) { const str = String(value ?? ''); return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } /** Entfernt alle Kindknoten eines Elements. */ function clearChildren(node) { while (node.firstChild) { node.removeChild(node.firstChild); } } EventMap.utils.el = el; EventMap.utils.escapeHtml = escapeHtml; EventMap.utils.clearChildren = clearChildren; })();