Extract CSS and JS from index.html into separate ES modules
This commit is contained in:
162
static/js/topology.js
Normal file
162
static/js/topology.js
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user