In most networks it’s important to know which nodes have the greatest impact, and consider what the effects would be if they failed.
In the Impact view, all nodes are sized based on how many nodes depend on them. In the Alerts view, only nodes with warnings or errors are sized by their dependencies.
By interacting with the chart, you can focus on a single node and examine its dependencies.
Styling in KeyLines can also improve the flow of the chart. For example, gradients on links matching the node colours on each end can smooth the transitions between areas on the graph and curved links applied to sequential layout enhance the directionality of links.
Key functions used:
import KeyLines from "keylines";
import {
data,
fontIcons,
errorColour,
warningColour,
dimmedColour,
bgColour,
annotationsState,
} from "./data.js";
// declaring global vars
let chart;
let impactAlertsSelection = document.querySelector(
'input[name="dependencyView"]:checked',
).value;
let showNodes = null;
let selectedNodeId = null;
const squareNodeText = {};
const rectangleNodeText = {};
const initialNodeStyles = [];
const alertsItems = [];
let hoveredGlyphId = null;
let hoveredAnnotationGlyphId = null;
const graph = KeyLines.getGraphEngine();
graph.load(data);
const impactAlertsRadios = Array.from(
document.querySelectorAll('input[name="dependencyView"]'),
);
const alertsList = document.getElementById("alerts");
const resetButton = document.getElementById("reset");
function getScaleFactor(numOfDeps) {
return numOfDeps ** (1 / 3);
}
function highlightErrorsAndWarnings() {
const colourItems = [];
const warnings = [];
let errors = [];
chart.each({ type: "node" }, (item) => {
if (item.d.logLevel === "error") {
errors.push(item.id);
let neighbours = graph.neighbours(item.id, {
direction: "from",
hops: 5,
}).nodes;
errors = errors.concat(neighbours);
} else if (item.d.logLevel !== "warning") {
colourItems.push({ id: item.id, c: dimmedColour });
}
});
chart.each({ type: "node" }, (item) => {
if (item.d.logLevel === "warning") {
warnings.push(item.id);
let neighbours = graph.neighbours(item.id, {
direction: "from",
hops: 5,
}).nodes;
neighbours.forEach((id) => {
if (!errors.includes(id)) {
warnings.push(id);
}
});
}
});
errors.forEach((id) => colourItems.push({ id, c: errorColour }));
warnings.forEach((id) => colourItems.push({ id, c: warningColour }));
chart.each({ type: "link" }, (item) => {
if (errors.includes(item.id2)) {
colourItems.push({ id: item.id, c: errorColour, c2: errorColour });
} else if (warnings.includes(item.id2)) {
colourItems.push({ id: item.id, c: warningColour, c2: warningColour });
} else {
colourItems.push({ id: item.id, c: dimmedColour, c2: dimmedColour });
}
});
chart.setProperties(colourItems);
applyAnnotationStateUpdateToChart();
}
function applyAnnotationStateUpdateToChart() {
const annotations = [];
for (const [_, value] of Object.entries(annotationsState)) {
if (value.hidden) continue;
annotations.push(value.annotation);
}
chart.merge(annotations);
}
function removeAnnotationFromChart(id) {
chart.removeItem(id);
}
function constructTable() {
const tableBody = document.querySelector(".alertsTableBody");
alertsItems.forEach((item) => {
const label = document.createElement("td");
const labelText = document.createTextNode(item.label);
label.appendChild(labelText);
const type = document.createElement("td");
const typeText = document.createTextNode(
item.type.charAt(0).toUpperCase() + item.type.slice(1),
);
type.appendChild(typeText);
const description = document.createElement("td");
const descriptionText = document.createTextNode(item.description);
description.appendChild(descriptionText);
const row = document.createElement("tr");
row.id = item.nodeId;
row.setAttribute("name", "alertRow");
row.appendChild(label);
row.appendChild(type);
row.appendChild(description);
row.onclick = function () {
setSelection({ id: item.nodeId });
};
tableBody.appendChild(row);
});
}
function clearTableStyling() {
const alertsTableRows = document.querySelectorAll('tr[name="alertRow"]');
alertsTableRows.forEach((row) => {
row.classList.remove("selected-row");
});
}
function showHideAlertsList() {
impactAlertsSelection = document.querySelector(
'input[name="dependencyView"]:checked',
).value;
if (impactAlertsSelection === "impact") {
chart.each({ type: "annotation" }, (annotation) => {
chart.removeItem(annotation.id);
});
chart.setProperties(initialNodeStyles);
alertsList.classList.add("hidden");
} else {
highlightErrorsAndWarnings();
alertsList.classList.remove("hidden");
}
}
async function applyFilters() {
chart.lock(true);
const result = await chart.filter(
(item) => {
if (item.type === "node" && showNodes) {
return showNodes[item.id] !== undefined;
}
return true;
},
{ time: 300 },
);
chart.lock(false);
return result;
}
async function doLayout() {
const layoutOptions = {
consistent: true,
mode: "adaptive",
};
let layoutName;
// A node needs to be selected for the selected layout to be run
if (selectedNodeId) {
// Set the top node to be the selected one
layoutName = "sequential";
// Increase the space between levels for gentler curves in the links
// but reduce the space between nodes within the same level
layoutOptions.stretch = 2;
layoutOptions.tightness = 8;
layoutOptions.orientation = "right";
layoutOptions.linkShape = "curved";
} else {
layoutName = "organic";
layoutOptions.tightness = 6;
}
await chart.layout(layoutName, layoutOptions);
}
function hoverReaction({ id, subItem }) {
const item = chart.getItem(id);
const isAnnotation = id && id.includes("annotation");
const changes = [];
// Clear any existing hover styling
if (hoveredGlyphId) {
const node = chart.getItem(hoveredGlyphId);
chart.setProperties({
id: hoveredGlyphId,
g: [
{
...node.g[0],
border: {
width: 1.5,
colour: bgColour,
},
},
],
});
hoveredGlyphId = null;
}
if (hoveredAnnotationGlyphId) {
const annotation = chart.getItem(hoveredAnnotationGlyphId);
if (annotation) {
changes.push({
id: hoveredAnnotationGlyphId,
g: [
annotation.g[0],
{
...annotation.g[1],
border: {
width: 1.5,
colour: bgColour,
},
},
],
});
}
hoveredAnnotationGlyphId = null;
}
// Add any new glyph hover styling
if (subItem.type === "glyph" && !isAnnotation) {
hoveredGlyphId = id;
chart.setProperties({
id,
g: [
{
...item.g[0],
border: {
width: 1.5,
colour: "white",
},
},
],
});
}
if (isAnnotation && subItem.type === "glyph" && subItem.index === 1) {
chart.setProperties({
id,
g: [
item.g[0],
{
...item.g[1],
border: {
width: 1.5,
colour: "white",
},
},
],
});
hoveredAnnotationGlyphId = id;
return;
}
// Handle node + node's tree hover styling and clearing
const tree =
item && item.type === "node"
? graph.distances(id, { direction: "from" })
: [];
chart.each({ type: "node" }, (node) => {
// don't overwrite selected node styling
if (node.id !== selectedNodeId) {
const highlight = node.id in tree && !hoveredGlyphId;
const borderWidth = node.w === 300 ? node.w / 40 : node.w / 20;
changes.push({
id: node.id,
b: highlight ? "white" : undefined,
bw: highlight ? borderWidth : 0,
});
}
});
chart.setProperties(changes);
}
async function setSelection({ id, subItem }) {
const isAnnotation = id && id.includes("annotation");
if (id && subItem?.type === "glyph" && !isAnnotation) {
annotationsState[id].hidden = false;
applyAnnotationStateUpdateToChart();
chart.setProperties({ id, g: [] });
return;
}
if (isAnnotation && subItem?.type === "glyph" && subItem.index === 1) {
const subjectId = chart.getItem(id).subject;
annotationsState[subjectId].hidden = true;
removeAnnotationFromChart(id);
chart.setProperties({
id: subjectId,
g: [annotationsState[subjectId].glyphStyling],
});
return;
}
if (isAnnotation) {
// annotations are not selectable
return;
}
clearTableStyling();
// Update the selected node with the one passed in
if (id === null) {
const prevSelectedNodeId = selectedNodeId;
selectedNodeId = null;
resetButton.disabled = true;
setNodeStyling("square");
// click on chart background or network button - reshow hidden items
showNodes = null;
if (prevSelectedNodeId) {
const result = await applyFilters();
// Only layout if any nodes were shown or hidden
if (result.shown.nodes.length > 0 || result.hidden.nodes.length > 0) {
doLayout();
}
}
} else {
selectedNodeId = id;
const item = chart.getItem(id);
chart.selection(id);
if (item && item.type === "node") {
resetButton.disabled = false;
const alertTableRow = document.getElementById(id);
if (alertTableRow) {
alertTableRow.classList.add("selected-row");
}
showNodes = {
...graph.distances(id, { direction: "to" }),
...graph.distances(id, { direction: "from" }),
};
if (item.d.dependencies.length) {
item.d.dependencies.forEach((id) => (showNodes[id] = 1));
}
await applyFilters();
setNodeStyling("rectangle");
doLayout();
}
}
}
function storeNodeStyling() {
const fontColour = bgColour;
const backgroundColour = "rgba(0,0,0,0)";
// set and store label styling for square and rectanglular nodes
data.items.forEach((item) => {
if (item.type === "node") {
squareNodeText[item.id] = {
t: [
{
fi: {
t: fontIcons[item.d.type],
},
position: {
vertical: "top",
},
fc: fontColour,
fbc: backgroundColour,
},
{
t: item.t.replace("-", "\n"),
position: {
vertical: "middle",
},
fc: fontColour,
fbc: backgroundColour,
fb: true,
},
],
};
rectangleNodeText[item.id] = {
fi: null,
t: [
{
fi: {
t: fontIcons[item.d.type],
},
position: {
vertical: "middle",
horizontal: "left",
},
fc: fontColour,
fbc: backgroundColour,
fs: 30,
padding: {
left: 13,
},
},
{
t: item.t,
fc: fontColour,
fbc: backgroundColour,
position: {
vertical: "top",
horizontal: 45,
},
padding: {
top: item.d.logMessage ? 19 : 24,
},
fb: true,
},
{
fc: fontColour,
fbc: backgroundColour,
t: item.d.logMessage,
fs: 9,
},
],
};
initialNodeStyles.push({ id: item.id, c: item.c });
} else if (item.type === "link") {
initialNodeStyles.push({ id: item.id, c: item.c, c2: item.c2 });
}
});
}
function setNodeStyling(shape) {
const isRectangle = shape === "rectangle";
const styles = [];
chart.each({ type: "node" }, (node) => {
const id = node.id;
const logLevel = node.d.logLevel;
const b = id === selectedNodeId ? "white" : node.c;
const bw = 6;
let scaleFactor = 1;
let w = 40;
let h = 50;
let borderRadius = 10;
let t;
let g = [];
const dependencies = Object.keys(
graph.distances(id, { direction: "from" }),
).length;
if (impactAlertsSelection === "impact" || logLevel) {
if (dependencies > 0) {
scaleFactor = getScaleFactor(dependencies);
}
}
if (isRectangle) {
t = [...rectangleNodeText[node.id].t];
w = 300;
h = 60;
t[0].fs = 48;
t[0].padding = { right: 30, left: 15 };
t[1].margin = { left: 14 };
if (t[2]) {
t[2].margin = { left: 14 };
}
} else {
t = [...squareNodeText[node.id].t];
w = h *= scaleFactor;
borderRadius *= scaleFactor;
t[0].fs = 22 * scaleFactor;
t[0].padding = { top: 7 * scaleFactor };
t[1].fs = 5 * scaleFactor;
t[1].padding = { top: 17 * scaleFactor };
}
if (impactAlertsSelection === "alerts" && logLevel) {
const icon = fontIcons.exclamationTriangle;
const iconSize = isRectangle ? 2 : 1.2 * scaleFactor;
const glyphStyling = {
c: "white",
p: "ne",
fi: {
t: icon,
c: bgColour,
},
e: iconSize,
border: {
width: 1.5,
colour: bgColour,
},
};
if (annotationsState[id].hidden) {
g.push(glyphStyling);
}
annotationsState[id].glyphStyling = glyphStyling;
}
styles.push({ id, t, w, h, b, bw, borderRadius, g });
});
chart.setProperties(styles);
}
function initialiseInteractions() {
// On hover, foreground connected nodes
chart.on("hover", hoverReaction);
// On click, focus on the selected node
chart.on("click", setSelection);
// Hide/show alerts list based on radio selection
impactAlertsRadios.forEach((radio) => {
radio.addEventListener("click", () => {
showHideAlertsList();
const isAnnotation =
selectedNodeId && selectedNodeId.includes("annotation");
setSelection({ id: isAnnotation ? null : selectedNodeId });
doLayout();
});
});
resetButton.addEventListener("click", () => setSelection({ id: null }));
}
function initialiseTableData() {
data.items.forEach((item) => {
if (item.type == "node" && item.d.logLevel) {
alertsItems.push({
label: item.t,
type: item.d.logLevel,
description: item.d.logMessage,
nodeId: item.id,
});
}
});
}
async function startKeyLines() {
const imageAlignment = {
"fas fa-server": { e: 0.75 },
"fas fa-desktop": { e: 0.75 },
"fas fa-exclamation-triangle": { dy: -11 },
};
const options = {
arrows: "large",
backColour: bgColour,
navigation: false,
overview: false,
drag: {
links: false,
},
hover: 0,
handMode: true,
minZoom: 0.02,
iconFontFamily: "Font Awesome 5 Free",
imageAlignment,
// Disable chart selection styles
selectedNode: {},
selectedLink: {},
};
chart = await KeyLines.create({
container: "klchart",
options,
});
chart.load(data);
initialiseInteractions();
initialiseTableData();
constructTable();
storeNodeStyling();
setNodeStyling("square");
doLayout();
}
function loadKeyLines() {
document.fonts.load('24px "Font Awesome 5 Free"').then(startKeyLines);
}
window.addEventListener("DOMContentLoaded", loadKeyLines); import KeyLines from "keylines";
export const fontIcons = {
switch: "fas fa-exchange-alt",
server: "fas fa-server ",
vhost: "fas fa-server",
service: "fas fa-desktop",
exclamationTriangle: "fas fa-exclamation-triangle",
eyeSlash: "fas fa-eye-slash",
};
export const errorColour = "#FF6052";
export const warningColour = "rgba(254,221,0,1)";
export const dimmedColour = "#969696";
export const bgColour = "#212121";
const virtualHostColour = "#75D8FF";
const serverColour = "#2CE0C2";
const serviceColour = "#4598F7";
const switchColour = "#56F580";
const errorColourLight = "rgb(255, 140, 130)";
const warningColourLight = "rgb(236, 223, 138)";
export const data = {
type: "LinkChart",
items: [
{
type: "link",
id: "svr-a-plg-c",
id1: "svr-a",
id2: "plg-c",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-b-plg-d",
id1: "svr-b",
id2: "plg-d",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-c-plg-b",
id1: "svr-c",
id2: "plg-b",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-c-plg-d",
id1: "svr-c",
id2: "plg-d",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-d-plg-c",
id1: "svr-d",
id2: "plg-c",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-d-plg-b",
id1: "svr-d",
id2: "plg-b",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-e-plg-c",
id1: "svr-e",
id2: "plg-c",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-f-plg-d",
id1: "svr-f",
id2: "plg-d",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-g-plg-a",
id1: "svr-g",
id2: "plg-a",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-h-plg-c",
id1: "svr-h",
id2: "plg-c",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-i-plg-a",
id1: "svr-i",
id2: "plg-a",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-j-plg-a",
id1: "svr-j",
id2: "plg-a",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-k-plg-a",
id1: "svr-k",
id2: "plg-a",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "svr-k-plg-c",
id1: "svr-k",
id2: "plg-c",
a1: true,
w: 4,
off: 0,
c: serverColour,
c2: switchColour,
},
{
type: "link",
id: "vh-a-svr-e",
id1: "vh-a",
id2: "svr-e",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-a-svr-b",
id1: "vh-a",
id2: "svr-b",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-a-svr-f",
id1: "vh-a",
id2: "svr-f",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-b-svr-i",
id1: "vh-b",
id2: "svr-i",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-c-svr-c",
id1: "vh-c",
id2: "svr-c",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-c-svr-j",
id1: "vh-c",
id2: "svr-j",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-d-svr-k",
id1: "vh-d",
id2: "svr-k",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-d-svr-a",
id1: "vh-d",
id2: "svr-a",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-e-svr-i",
id1: "vh-e",
id2: "svr-i",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-e-svr-d",
id1: "vh-e",
id2: "svr-d",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-f-svr-c",
id1: "vh-f",
id2: "svr-c",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-f-svr-b",
id1: "vh-f",
id2: "svr-b",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-g-svr-b",
id1: "vh-g",
id2: "svr-b",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-h-svr-d",
id1: "vh-h",
id2: "svr-d",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-i-svr-c",
id1: "vh-i",
id2: "svr-c",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-j-svr-e",
id1: "vh-j",
id2: "svr-e",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-k-svr-j",
id1: "vh-k",
id2: "svr-j",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-l-svr-e",
id1: "vh-l",
id2: "svr-e",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-l-svr-f",
id1: "vh-l",
id2: "svr-f",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-l-svr-h",
id1: "vh-l",
id2: "svr-h",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-m-svr-j",
id1: "vh-m",
id2: "svr-j",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-n-svr-h",
id1: "vh-n",
id2: "svr-h",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-n-svr-e",
id1: "vh-n",
id2: "svr-e",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-o-svr-b",
id1: "vh-o",
id2: "svr-b",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-p-svr-e",
id1: "vh-p",
id2: "svr-e",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-p-svr-a",
id1: "vh-p",
id2: "svr-a",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-q-svr-b",
id1: "vh-q",
id2: "svr-b",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-q-svr-g",
id1: "vh-q",
id2: "svr-g",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-r-svr-c",
id1: "vh-r",
id2: "svr-c",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-r-svr-k",
id1: "vh-r",
id2: "svr-k",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-s-svr-f",
id1: "vh-s",
id2: "svr-f",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-s-svr-k",
id1: "vh-s",
id2: "svr-k",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-t-svr-g",
id1: "vh-t",
id2: "svr-g",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-u-svr-f",
id1: "vh-u",
id2: "svr-f",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-u-svr-e",
id1: "vh-u",
id2: "svr-e",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-v-svr-h",
id1: "vh-v",
id2: "svr-h",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-w-svr-i",
id1: "vh-w",
id2: "svr-i",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-w-svr-c",
id1: "vh-w",
id2: "svr-c",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-x-svr-d",
id1: "vh-x",
id2: "svr-d",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-y-svr-j",
id1: "vh-y",
id2: "svr-j",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-y-svr-b",
id1: "vh-y",
id2: "svr-b",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-z-svr-k",
id1: "vh-z",
id2: "svr-k",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-z-svr-e",
id1: "vh-z",
id2: "svr-e",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-ab-svr-c",
id1: "vh-ab",
id2: "svr-c",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-ab-svr-j",
id1: "vh-ab",
id2: "svr-j",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-ac-svr-f",
id1: "vh-ac",
id2: "svr-f",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-ac-svr-d",
id1: "vh-ac",
id2: "svr-d",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-ad-svr-j",
id1: "vh-ad",
id2: "svr-j",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-ae-svr-h",
id1: "vh-ae",
id2: "svr-h",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-af-svr-c",
id1: "vh-af",
id2: "svr-c",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-af-svr-i",
id1: "vh-af",
id2: "svr-i",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-ag-svr-f",
id1: "vh-ag",
id2: "svr-f",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "vh-ah-svr-i",
id1: "vh-ah",
id2: "svr-i",
a1: true,
w: 4,
off: 0,
c: virtualHostColour,
c2: serverColour,
},
{
type: "link",
id: "srvc-a-vh-q",
id1: "srvc-a",
id2: "vh-q",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-b-vh-j",
id1: "srvc-b",
id2: "vh-j",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-c-vh-f",
id1: "srvc-c",
id2: "vh-f",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-c-vh-m",
id1: "srvc-c",
id2: "vh-m",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-d-vh-c",
id1: "srvc-d",
id2: "vh-c",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-e-vh-af",
id1: "srvc-e",
id2: "vh-af",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-e-vh-v",
id1: "srvc-e",
id2: "vh-v",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-f-vh-h",
id1: "srvc-f",
id2: "vh-h",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-f-vh-m",
id1: "srvc-f",
id2: "vh-m",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-g-vh-k",
id1: "srvc-g",
id2: "vh-k",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-g-vh-d",
id1: "srvc-g",
id2: "vh-d",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-h-vh-d",
id1: "srvc-h",
id2: "vh-d",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-i-vh-d",
id1: "srvc-i",
id2: "vh-d",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-i-vh-i",
id1: "srvc-i",
id2: "vh-i",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-j-vh-c",
id1: "srvc-j",
id2: "vh-c",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-j-vh-j",
id1: "srvc-j",
id2: "vh-j",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-k-vh-v",
id1: "srvc-k",
id2: "vh-v",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-l-vh-ab",
id1: "srvc-l",
id2: "vh-ab",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-m-vh-e",
id1: "srvc-m",
id2: "vh-e",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-n-vh-s",
id1: "srvc-n",
id2: "vh-s",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-n-vh-i",
id1: "srvc-n",
id2: "vh-i",
a1: true,
w: 4,
off: 0,
c: serviceColour,
c2: virtualHostColour,
},
{
type: "link",
id: "srvc-o-vh-o",
id1: "srvc-o",
id2: "vh-o",
a1: true,
// ...truncated 2848 lines <!doctype html>
<html lang="en" style="background-color: #2d383f">
<head>
<meta charset="utf-8" />
<title>Impact Analysis</title>
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/minimalsdk.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/sdk-layout.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css" />
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="klchart" class="klchart"></div>
<script type="module" src="./code.js"></script>
</body>
</html> #nodeDependantWarning {
background-color: #fcf8e3;
padding-left: 4px;
border-style: solid;
border-width: 1px;
border-radius: 2px;
}
.radio {
padding-left: 25px;
}
table {
border: 1px solid #969696;
border-collapse: collapse;
font-size: 14px;
width: 100%;
height: 30vh;
}
table thead:hover {
background-color: #969696;
}
table thead tr {
display: block;
width: 100%;
background-color: #969696;
}
table thead th {
color: #212121;
background-color: #969696;
position: sticky;
top: 0;
text-align: left;
}
table tbody {
display: block;
overflow-y: scroll;
height: 100%;
background-color: white;
}
table tbody::-webkit-scrollbar {
width: 8px;
}
table tbody::-webkit-scrollbar-thumb {
background: #969696;
border-radius: 5px;
}
table tbody tr {
border-radius: 30px;
cursor: pointer;
display: table-row;
transition: all 0.3s ease;
}
.selected-row,
#alerts .selected-row:hover {
background-color: #212121;
color: white;
}
table tr *:nth-child(1) {
width: 130px;
}
table tr *:nth-child(2) {
text-align: center;
width: 100px;
}
table tr *:nth-child(3) {
width: 170px;
}
table th,
table td {
padding: 0.5em;
}
#reset {
text-align: center;
margin: 12px 0px;
border-style: solid;
border-width: 1px;
}