Extract CSS and JS from index.html into separate ES modules

This commit is contained in:
Ian Gulliver
2026-01-30 11:38:09 -08:00
parent 052d3b89dd
commit e3aa25d85f
12 changed files with 3379 additions and 3300 deletions

162
static/js/topology.js Normal file
View File

@@ -0,0 +1,162 @@
import { getLabel, getNodeIdentifiers, isSwitch } from './nodes.js';
import { incrementAnonCounter } from './state.js';
export function buildSwitchUplinks(allSwitches, switchLinks) {
const uplinks = new Map();
if (allSwitches.length === 0 || switchLinks.length === 0) return uplinks;
const adjacency = new Map();
allSwitches.forEach(sw => adjacency.set(sw.id, []));
switchLinks.forEach(link => {
adjacency.get(link.switchA.id).push({
neighbor: link.switchB,
localPort: link.portA,
remotePort: link.portB,
localSpeed: link.speedA,
localErrors: link.errorsA,
localRates: link.ratesA
});
adjacency.get(link.switchB.id).push({
neighbor: link.switchA,
localPort: link.portB,
remotePort: link.portA,
localSpeed: link.speedB,
localErrors: link.errorsB,
localRates: link.ratesB
});
});
for (const edges of adjacency.values()) {
edges.sort((a, b) => getLabel(a.neighbor).localeCompare(getLabel(b.neighbor)));
}
const sortedSwitches = [...allSwitches].sort((a, b) =>
getLabel(a).localeCompare(getLabel(b)));
let bestRoot = sortedSwitches[0];
let bestReachable = 0;
let bestMaxDepth = Infinity;
for (const candidate of sortedSwitches) {
const visited = new Set([candidate.id]);
const queue = [{ sw: candidate, depth: 0 }];
let maxDepth = 0;
while (queue.length > 0) {
const { sw, depth } = queue.shift();
maxDepth = Math.max(maxDepth, depth);
for (const edge of adjacency.get(sw.id) || []) {
if (!visited.has(edge.neighbor.id)) {
visited.add(edge.neighbor.id);
queue.push({ sw: edge.neighbor, depth: depth + 1 });
}
}
}
const reachable = visited.size;
if (reachable > bestReachable ||
(reachable === bestReachable && maxDepth < bestMaxDepth)) {
bestReachable = reachable;
bestMaxDepth = maxDepth;
bestRoot = candidate;
}
}
uplinks.set(bestRoot.id, 'ROOT');
const visited = new Set([bestRoot.id]);
const queue = [bestRoot];
while (queue.length > 0) {
const current = queue.shift();
for (const edge of adjacency.get(current.id) || []) {
if (!visited.has(edge.neighbor.id)) {
visited.add(edge.neighbor.id);
const reverseEdge = adjacency.get(edge.neighbor.id).find(e => e.neighbor.id === current.id);
uplinks.set(edge.neighbor.id, {
localPort: reverseEdge?.localPort || '?',
remotePort: reverseEdge?.remotePort || '?',
parentNode: current,
parentName: getLabel(current),
speed: reverseEdge?.localSpeed || 0,
errors: reverseEdge?.localErrors || null,
rates: reverseEdge?.localRates || null
});
queue.push(edge.neighbor);
}
}
}
return uplinks;
}
export function buildLocationTree(locations, parent) {
if (!locations) return [];
return locations.map((loc, idx) => {
let locId;
let anonymous = false;
if (loc.name) {
locId = 'loc_' + loc.name.replace(/[^a-zA-Z0-9]/g, '_');
} else {
locId = 'loc_anon_' + incrementAnonCounter();
anonymous = true;
}
const locObj = {
id: locId,
name: loc.name || '',
anonymous: anonymous,
direction: loc.direction || 'horizontal',
nodeRefs: (loc.nodes || []).map(n => n.toLowerCase()),
parent: parent,
children: []
};
locObj.children = buildLocationTree(loc.children, locObj);
return locObj;
});
}
export function getSwitchesInLocation(loc, assignedNodes) {
const switches = [];
const nodes = assignedNodes.get(loc) || [];
nodes.forEach(n => {
if (isSwitch(n)) switches.push(n);
});
loc.children.forEach(child => {
if (child.anonymous) {
switches.push(...getSwitchesInLocation(child, assignedNodes));
}
});
return switches;
}
export function findEffectiveSwitch(loc, assignedNodes) {
if (!loc) return null;
const switches = getSwitchesInLocation(loc, assignedNodes);
if (switches.length === 1) {
return switches[0];
}
if (loc.parent) {
return findEffectiveSwitch(loc.parent, assignedNodes);
}
return null;
}
export function buildNodeIndex(locations, index) {
locations.forEach(loc => {
loc.nodeRefs.forEach(ref => {
index.set(ref, loc);
});
buildNodeIndex(loc.children, index);
});
}
export function findLocationForNode(node, nodeIndex) {
const identifiers = getNodeIdentifiers(node);
for (const ident of identifiers) {
if (nodeIndex.has(ident)) {
return nodeIndex.get(ident);
}
}
return null;
}