Files
netwatch/app/assets/js/floor-infrastructure-list.js
2026-02-16 11:57:24 +01:00

149 lines
5.2 KiB
JavaScript

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;
}