document.addEventListener('DOMContentLoaded', () => { const SVG_NS = 'http://www.w3.org/2000/svg'; const DEFAULT_PLAN_SIZE = { width: 2000, height: 1000 }; const filterForm = document.getElementById('infra-filter-form'); const floorSelect = document.getElementById('infra-floor-select'); if (filterForm && floorSelect) { floorSelect.addEventListener('change', () => { filterForm.submit(); }); } const canvas = document.getElementById('infra-floor-canvas'); const overlay = document.getElementById('infra-floor-overlay'); const floorSvg = canvas ? canvas.querySelector('.infra-floor-svg') : null; if (!canvas || !overlay || !floorSvg) { return; } const patchPanels = safeJsonParse(canvas.dataset.patchpanels); const outlets = safeJsonParse(canvas.dataset.outlets); const planSize = { ...DEFAULT_PLAN_SIZE }; const updateOverlayViewBox = () => { overlay.setAttribute('viewBox', `0 0 ${planSize.width} ${planSize.height}`); }; const createMarker = (entry, type) => { const x = Number(entry.x); const y = Number(entry.y); if (!Number.isFinite(x) || !Number.isFinite(y)) { return; } const marker = document.createElementNS(SVG_NS, 'rect'); marker.classList.add('infra-overlay-marker', type); marker.setAttribute('x', String(Math.round(x))); marker.setAttribute('y', String(Math.round(y))); if (type === 'patchpanel') { marker.setAttribute('width', String(Math.max(1, Number(entry.width) || 20))); marker.setAttribute('height', String(Math.max(1, Number(entry.height) || 5))); } else { marker.setAttribute('width', '10'); marker.setAttribute('height', '10'); } const tooltipLines = buildTooltipLines(entry, type); if (tooltipLines.length > 0) { const titleNode = document.createElementNS(SVG_NS, 'title'); titleNode.textContent = tooltipLines.join('\n'); marker.appendChild(titleNode); } overlay.appendChild(marker); }; patchPanels.forEach((entry) => createMarker(entry, 'patchpanel')); outlets.forEach((entry) => createMarker(entry, 'outlet')); const loadPlanDimensions = async (svgUrl) => { if (!svgUrl) { updateOverlayViewBox(); return; } try { const response = await fetch(svgUrl, { credentials: 'same-origin' }); if (!response.ok) { throw new Error('SVG not available'); } const raw = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(raw, 'image/svg+xml'); const root = doc.documentElement; if (!root || root.nodeName.toLowerCase() === 'parsererror') { throw new Error('Invalid SVG'); } const vb = String(root.getAttribute('viewBox') || '').trim(); if (vb) { const parts = vb.split(/\s+/).map((value) => Number(value)); if (parts.length === 4 && parts.every((value) => Number.isFinite(value))) { planSize.width = Math.max(1, parts[2]); planSize.height = Math.max(1, parts[3]); updateOverlayViewBox(); return; } } const width = Number(root.getAttribute('width')); const height = Number(root.getAttribute('height')); if (Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0) { planSize.width = width; planSize.height = height; } else { planSize.width = DEFAULT_PLAN_SIZE.width; planSize.height = DEFAULT_PLAN_SIZE.height; } updateOverlayViewBox(); } catch (error) { planSize.width = DEFAULT_PLAN_SIZE.width; planSize.height = DEFAULT_PLAN_SIZE.height; updateOverlayViewBox(); } }; loadPlanDimensions(floorSvg.getAttribute('src') || ''); }); function safeJsonParse(value) { if (!value) { return []; } try { const parsed = JSON.parse(value); return Array.isArray(parsed) ? parsed : []; } catch (error) { return []; } } function buildTooltipLines(entry, type) { if (type === 'patchpanel') { const lines = [ `Patchpanel: ${entry.name || '-'}`, `Ports: ${Number(entry.port_count) || 0}`, `Position: ${Number(entry.x) || 0} x ${Number(entry.y) || 0}` ]; if (entry.comment) { lines.push(`Kommentar: ${entry.comment}`); } return lines; } const roomLabel = `${entry.room_name || '-'}${entry.room_number ? ` (${entry.room_number})` : ''}`; const lines = [ `Wandbuchse: ${entry.name || '-'}`, `Raum: ${roomLabel}`, `Position: ${Number(entry.x) || 0} x ${Number(entry.y) || 0}` ]; if (entry.port_names) { lines.push(`Ports: ${entry.port_names}`); } if (entry.comment) { lines.push(`Kommentar: ${entry.comment}`); } return lines; }