In this demo, rectangular combos work alongside the sequential layout to deliver a clear overview of the network, its levels and connections.
Setting the adapt option to 'inCombo' preserves the sequential layout at the top chart level when the combo opens or closes.
Rectangular combos
Rectangular combos with grid arrangement are great for viewing large amounts of data at once. The items inside open combos are organised into a compact view that maximises the amount of instantly visible information.
Visualising connections
Using curved links in sequential layout can improve the readability of connections, especially for large charts with multiple connections between levels.
Combine them with features such as flow animation for additional highlight or to represent the flow in your data.
Another way to add detailed information is to use the chart.combo().reveal() function to keep underlying or important links visible. In this demo, the particularly suspicious computer and connection are both marked with a glyph and contrasting colour.
Key functions used:
import KeyLines from "keylines";
import { data } from "./data.js";
let chart;
let graph;
// Style info
const backgroundColour = "#1f1f1f";
const alertColour = "#e5309a";
const selectedColour = "white";
const linkColour = "#454545";
const topLevelLinkColour = "#39bffd";
// State tracking
let alwaysRevealedLinks = [];
// Find all the links that have alerts attached.
function getAlertLinks() {
const alerts = [];
chart.each({ type: "link" }, (link) => {
if (link.d.alert) {
alerts.push(link.id);
}
});
return alerts;
}
function decorateAlertLinks(linkIds) {
chart.setProperties(
linkIds.map((id) => ({
id,
c: alertColour,
g: [
{
c: alertColour,
b: null,
fi: { t: "fas fa-exclamation", c: "white" },
e: 1.5,
},
],
})),
);
}
function showTraffic(linkIds) {
const toForeground = {};
const newLinkProperties = linkIds.map((id) => {
toForeground[id] = true;
const link = chart.getItem(id);
const c = link.d.alert ? alertColour : selectedColour;
return {
id,
c,
flow: link.d.flow,
};
});
chart.setProperties(newLinkProperties);
const reveal = alwaysRevealedLinks.concat(linkIds);
chart.combo().reveal(reveal);
chart.foreground((link) => toForeground[link.id], {
type: "link",
items: "underlying",
});
}
function hideTraffic() {
const revealedIds = chart.combo().reveal();
chart.setProperties(
revealedIds.map((id) => {
const link = chart.getItem(id);
return {
id: link.id,
c: link.d.alert ? alertColour : linkColour,
flow: false,
};
}),
);
chart.combo().reveal(alwaysRevealedLinks);
}
function select(selections) {
const itemId =
selections.length > 0 ? selections[selections.length - 1] : null;
const item = chart.getItem(itemId);
if (item && item.type === "node" && !chart.combo().isCombo(itemId)) {
showTraffic(graph.neighbours(itemId, { all: true }).links);
} else {
chart.foreground(() => true);
}
}
async function onDoubleClick({ id, preventDefault, button }) {
if (!id || button !== 0) {
return;
}
const api = chart.combo();
const combo = api.isCombo(id) ? id : chart.getItem(id).parentId;
const opts = { adapt: "inCombo" };
if (api.isCombo(combo)) {
preventDefault();
if (api.isOpen(combo)) {
await api.close(combo, opts);
} else {
await api.open(combo, opts);
}
layout();
}
}
function initialiseInteractions() {
// perform layout on combo open/close
chart.on("double-click", onDoubleClick);
// reveal and foreground selected nodes and links
chart.on("selection-change", () => {
hideTraffic();
select(chart.selection());
});
}
// generate a list of all combos ordered by how deeply nested they are
function getComboList() {
const comboIds = [];
chart.each({ type: "node", items: "all" }, ({ id }) => {
if (chart.combo().isCombo(id)) {
comboIds.push(id);
}
});
return comboIds;
}
function getInnerCombos() {
const combo = chart.combo();
return getComboList().filter((id) => {
let isInnerCombo = true;
// is not an inner combo if any child is a combo
chart.each({ items: "all", type: "node" }, (item) => {
if (item.parentId === id && combo.isCombo(item.id)) {
isInnerCombo = false;
}
});
return isInnerCombo;
});
}
async function openAllCombos() {
const allCombos = getComboList();
const innerCombos = getInnerCombos();
const outerCombos = allCombos.filter((id) => !innerCombos.includes(id));
await chart.combo().open(allCombos, { animate: false });
await chart.combo().arrange(outerCombos, {
name: "grid",
animate: false,
});
await chart.combo().arrange(innerCombos, {
name: "grid",
animate: false,
tightness: 3,
});
}
function colourTopLevelLinks() {
chart.each({ type: "link", items: "toplevel" }, ({ id }) => {
chart.setProperties({ id, c: topLevelLinkColour });
});
}
async function startKeyLines() {
const options = {
backColour: backgroundColour,
backgroundAlpha: 0.45,
combos: {
shape: "rectangle",
},
controlTheme: "dark",
selectedLink: {},
selectedNode: {
b: selectedColour,
bw: 3,
},
iconFontFamily: "Font Awesome 5 Free",
handMode: true,
defaultStyles: {
comboGlyph: {
p: 45,
},
},
imageAlignment: {
"fas fa-print": { e: 0.8 },
"fas fa-laptop": { e: 0.75 },
"fas fa-phone": { e: 0.8 },
"fas fa-server": { e: 0.8 },
"fas fa-sitemap": { e: 0.75, dy: -8 },
},
};
chart = await KeyLines.create({ container: "klchart", options });
initialiseInteractions();
// Use a graph engine to track relations in the underlying data
graph = KeyLines.getGraphEngine();
graph.load(data);
chart.load(data);
// reveal the links with alerts attached and make sure they stay that way
alwaysRevealedLinks = getAlertLinks();
chart.combo().reveal(alwaysRevealedLinks);
// also style them dramatically...
decorateAlertLinks(alwaysRevealedLinks);
await openAllCombos();
colourTopLevelLinks();
layout();
}
function layout() {
chart.layout("sequential", { level: "level", linkShape: "curved" });
}
function loadFontsAndStart() {
document.fonts.load('24px "Font Awesome 5 Free"').then(startKeyLines);
}
window.addEventListener("DOMContentLoaded", loadFontsAndStart); import KeyLines from "keylines";
export const data = {
items: [
{
type: "node",
id: "223.112.181.149",
t: [{ t: "Cambridge", position: "s" }, { t: "223.112.181.149" }],
c: "grey",
bw: 3,
d: {
type: "network",
level: 1,
location: "Cambridge",
},
fi: {
t: "fas fa-sitemap",
c: "white",
},
e: 2,
sh: "box",
fbc: "rgba(0,0,0,0)",
fc: "white",
oc: {
c: "#292929",
b: "#39bffd",
t: "Cambridge\n223.112.181.149",
},
},
{
type: "node",
id: "192.168.162.0",
t: [{ t: "192.168.162.0" }],
c: "grey",
bw: 3,
d: {
type: "router",
network: "223.112.181.149",
ipPattern: "192.168.162.",
},
parentId: "223.112.181.149",
e: 1.5,
sh: "box",
fbc: "rgba(0,0,0,0)",
fc: "white",
fi: {
t: "fas fa-sitemap",
c: "white",
},
oc: {
bw: 3,
c: "#333333",
b: "#39bffd",
t: "192.168.162.0",
},
},
{
id: "223.112.181.149-192.168.162.0",
type: "link",
id1: "223.112.181.149",
id2: "192.168.162.0",
w: 4,
d: {
port1: 80,
port2: 443,
flow: {
velocity: 4.950921471449721,
},
},
c: "#454545",
},
{
type: "node",
id: "192.168.248.0",
t: [{ t: "192.168.248.0" }],
c: "grey",
bw: 3,
d: {
type: "router",
network: "223.112.181.149",
ipPattern: "192.168.248.",
},
parentId: "223.112.181.149",
e: 1.5,
sh: "box",
fbc: "rgba(0,0,0,0)",
fc: "white",
fi: {
t: "fas fa-sitemap",
c: "white",
},
oc: {
bw: 3,
c: "#333333",
b: "#39bffd",
t: "192.168.248.0",
},
},
{
id: "223.112.181.149-192.168.248.0",
type: "link",
id1: "223.112.181.149",
id2: "192.168.248.0",
w: 4,
d: {
port1: 8080,
port2: 443,
flow: {
velocity: 2.356821830746494,
},
},
c: "#454545",
},
{
type: "node",
id: "223.112.181.149::192.168.162.126",
t: [
{
fi: {
t: "fas fa-laptop",
},
fs: 60,
position: {
vertical: "top",
},
padding: {
top: 5,
},
},
{
t: "192.168.162.126",
position: {
vertical: "bottom",
},
fs: 15,
padding: {
bottom: 4,
},
},
],
c: "grey",
w: 130,
h: 70,
d: {
type: "linux",
network: "223.112.181.149",
subnet: "192.168.162.0",
},
parentId: "192.168.162.0",
fbc: "rgba(0,0,0,0)",
fc: "white",
},
{
id: "192.168.162.0-223.112.181.149::192.168.162.126",
type: "link",
id1: "192.168.162.0",
id2: "223.112.181.149::192.168.162.126",
w: 4,
d: {
port1: 20,
port2: 443,
flow: {
velocity: 3.966807830948753,
},
},
c: "#454545",
},
{
type: "node",
id: "223.112.181.149::192.168.248.236",
t: [
{
fi: {
t: "fas fa-laptop",
},
fs: 60,
position: {
vertical: "top",
},
padding: {
top: 5,
},
},
{
t: "192.168.248.236",
position: {
vertical: "bottom",
},
fs: 15,
padding: {
bottom: 4,
},
},
],
c: "grey",
w: 130,
h: 70,
d: {
type: "linux",
network: "223.112.181.149",
subnet: "192.168.248.0",
},
parentId: "192.168.248.0",
sh: "box",
fbc: "rgba(0,0,0,0)",
fc: "white",
},
{
id: "192.168.248.0-223.112.181.149::192.168.248.236",
type: "link",
id1: "192.168.248.0",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 20,
port2: 80,
flow: {
velocity: 2.0052035235238552,
},
},
c: "#454545",
},
{
type: "node",
id: "223.112.181.149::192.168.162.242",
t: [
{
fi: {
t: "fas fa-phone",
},
fs: 60,
position: {
vertical: "top",
},
padding: {
top: 5,
},
},
{
t: "192.168.162.242",
position: {
vertical: "bottom",
},
fs: 15,
padding: {
bottom: 4,
},
},
],
c: "grey",
w: 130,
h: 70,
d: {
type: "phone",
network: "223.112.181.149",
subnet: "192.168.162.0",
},
parentId: "192.168.162.0",
fbc: "rgba(0,0,0,0)",
fc: "white",
},
{
id: "192.168.162.0-223.112.181.149::192.168.162.242",
type: "link",
id1: "192.168.162.0",
id2: "223.112.181.149::192.168.162.242",
w: 4,
d: {
port1: 443,
port2: 80,
flow: {
velocity: 9.51712284042533,
},
},
c: "#454545",
},
{
type: "node",
id: "223.112.181.149::192.168.162.137",
t: [
{
fi: {
t: "fas fa-laptop",
},
fs: 60,
position: {
vertical: "top",
},
padding: {
top: 5,
},
},
{
t: "192.168.162.137",
position: {
vertical: "bottom",
},
fs: 15,
padding: {
bottom: 4,
},
},
],
c: "grey",
w: 130,
h: 70,
d: {
type: "linux",
network: "223.112.181.149",
subnet: "192.168.162.0",
},
parentId: "192.168.162.0",
sh: "box",
fbc: "rgba(0,0,0,0)",
fc: "white",
},
{
type: "node",
id: "223.112.181.149::192.168.162.118",
t: [
{
fi: {
t: "fas fa-laptop",
},
fs: 60,
position: {
vertical: "top",
},
padding: {
top: 5,
},
},
{
t: "192.168.162.118",
position: {
vertical: "bottom",
},
fs: 15,
padding: {
bottom: 4,
},
},
],
c: "grey",
w: 130,
h: 70,
d: {
type: "linux",
network: "223.112.181.149",
subnet: "192.168.162.0",
},
parentId: "192.168.162.0",
sh: "box",
fbc: "rgba(0,0,0,0)",
fc: "white",
},
{
id: "192.168.162.0-223.112.181.149::192.168.162.118",
type: "link",
id1: "192.168.162.0",
id2: "223.112.181.149::192.168.162.118",
w: 4,
d: {
port1: 20,
port2: 8080,
flow: {
velocity: 0.03162958284387729,
},
},
c: "#454545",
},
{
id: "192.168.162.0-223.112.181.149::192.168.162.137",
type: "link",
id1: "192.168.162.0",
id2: "223.112.181.149::192.168.162.137",
w: 4,
d: {
port1: 8080,
port2: 20,
flow: {
velocity: 1.1161726836188146,
},
},
c: "#454545",
},
{
type: "node",
id: "223.112.181.149::192.168.248.192",
t: [
{
fi: {
t: "fas fa-laptop",
},
fs: 60,
position: {
vertical: "top",
},
padding: {
top: 5,
},
},
{
t: "192.168.248.192",
position: {
vertical: "bottom",
},
fs: 15,
padding: {
bottom: 4,
},
},
],
c: "grey",
w: 130,
h: 70,
d: {
type: "win",
network: "223.112.181.149",
subnet: "192.168.248.0",
},
parentId: "192.168.248.0",
sh: "box",
fbc: "rgba(0,0,0,0)",
fc: "white",
},
{
id: "192.168.248.0-223.112.181.149::192.168.248.192",
type: "link",
id1: "192.168.248.0",
id2: "223.112.181.149::192.168.248.192",
w: 4,
d: {
port1: 8080,
port2: 8080,
flow: {
velocity: 8.30920446549413,
},
},
c: "#454545",
},
{
type: "node",
id: "223.112.181.149::192.168.248.155",
t: [
{
fi: {
t: "fas fa-laptop",
},
fs: 60,
position: {
vertical: "top",
},
padding: {
top: 5,
},
},
{
t: "192.168.248.155",
position: {
vertical: "bottom",
},
fs: 15,
padding: {
bottom: 4,
},
},
],
c: "grey",
w: 130,
h: 70,
d: {
type: "linux",
network: "223.112.181.149",
subnet: "192.168.248.0",
},
parentId: "192.168.248.0",
sh: "box",
fbc: "rgba(0,0,0,0)",
fc: "white",
},
{
id: "192.168.248.0-223.112.181.149::192.168.248.155",
type: "link",
id1: "192.168.248.0",
id2: "223.112.181.149::192.168.248.155",
w: 4,
d: {
port1: 443,
port2: 22,
flow: {
velocity: 6.664337520331777,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.126-223.112.181.149::192.168.162.118",
type: "link",
id1: "223.112.181.149::192.168.162.126",
id2: "223.112.181.149::192.168.162.118",
w: 4,
d: {
port1: 22,
port2: 8080,
flow: {
velocity: 2.0425414548962095,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.126-223.112.181.149::192.168.248.236",
type: "link",
id1: "223.112.181.149::192.168.162.126",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 20,
port2: 20,
flow: {
velocity: 4.691027944372806,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.126-223.112.181.149::192.168.162.126",
type: "link",
id1: "223.112.181.149::192.168.162.126",
id2: "223.112.181.149::192.168.162.126",
w: 4,
d: {
port1: 20,
port2: 8080,
flow: {
velocity: 6.90387391871047,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.126-223.112.181.149::192.168.162.242",
type: "link",
id1: "223.112.181.149::192.168.162.126",
id2: "223.112.181.149::192.168.162.242",
w: 4,
d: {
port1: 80,
port2: 22,
flow: {
velocity: 7.060971955773012,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.126-223.112.181.149::192.168.248.155",
type: "link",
id1: "223.112.181.149::192.168.162.126",
id2: "223.112.181.149::192.168.248.155",
w: 4,
d: {
port1: 20,
port2: 22,
flow: {
velocity: 9.723244415824547,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.236-223.112.181.149::192.168.248.192",
type: "link",
id1: "223.112.181.149::192.168.248.236",
id2: "223.112.181.149::192.168.248.192",
w: 4,
d: {
port1: 443,
port2: 8080,
flow: {
velocity: 0.9449581426781406,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.236-223.112.181.149::192.168.248.155",
type: "link",
id1: "223.112.181.149::192.168.248.236",
id2: "223.112.181.149::192.168.248.155",
w: 4,
d: {
port1: 8080,
port2: 80,
flow: {
velocity: 7.042428747932439,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.236-223.112.181.149::192.168.162.126",
type: "link",
id1: "223.112.181.149::192.168.248.236",
id2: "223.112.181.149::192.168.162.126",
w: 4,
d: {
port1: 8080,
port2: 20,
flow: {
velocity: 3.7032959392292075,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.236-223.112.181.149::192.168.248.236",
type: "link",
id1: "223.112.181.149::192.168.248.236",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 443,
port2: 20,
flow: {
velocity: 4.116079461387418,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.236-223.112.181.149::192.168.248.192",
type: "link",
id1: "223.112.181.149::192.168.248.236",
id2: "223.112.181.149::192.168.248.192",
w: 4,
d: {
port1: 80,
port2: 22,
flow: {
velocity: 8.67323757284284,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.236-223.112.181.149::192.168.162.118",
type: "link",
id1: "223.112.181.149::192.168.248.236",
id2: "223.112.181.149::192.168.162.118",
w: 4,
d: {
port1: 22,
port2: 8080,
flow: {
velocity: 5.9946802689124645,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.236-223.112.181.149::192.168.162.126",
type: "link",
id1: "223.112.181.149::192.168.248.236",
id2: "223.112.181.149::192.168.162.126",
w: 4,
d: {
port1: 443,
port2: 443,
flow: {
velocity: 9.997017366850457,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.242-223.112.181.149::192.168.162.118",
type: "link",
id1: "223.112.181.149::192.168.162.242",
id2: "223.112.181.149::192.168.162.118",
w: 4,
d: {
port1: 80,
port2: 22,
flow: {
velocity: 8.888415939650695,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.242-223.112.181.149::192.168.248.192",
type: "link",
id1: "223.112.181.149::192.168.162.242",
id2: "223.112.181.149::192.168.248.192",
w: 4,
d: {
port1: 20,
port2: 8080,
flow: {
velocity: 3.6163660426700694,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.242-223.112.181.149::192.168.162.118",
type: "link",
id1: "223.112.181.149::192.168.162.242",
id2: "223.112.181.149::192.168.162.118",
w: 4,
d: {
port1: 20,
port2: 22,
flow: {
velocity: 9.280001759770045,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.242-223.112.181.149::192.168.162.137",
type: "link",
id1: "223.112.181.149::192.168.162.242",
id2: "223.112.181.149::192.168.162.137",
w: 4,
d: {
port1: 22,
port2: 22,
flow: {
velocity: 1.4941183350521925,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.118-223.112.181.149::192.168.162.242",
type: "link",
id1: "223.112.181.149::192.168.162.118",
id2: "223.112.181.149::192.168.162.242",
w: 4,
d: {
port1: 20,
port2: 20,
flow: {
velocity: 4.046308247421475,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.118-223.112.181.149::192.168.162.126",
type: "link",
id1: "223.112.181.149::192.168.162.118",
id2: "223.112.181.149::192.168.162.126",
w: 4,
d: {
port1: 20,
port2: 443,
flow: {
velocity: 6.795889630088077,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.118-223.112.181.149::192.168.248.236",
type: "link",
id1: "223.112.181.149::192.168.162.118",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 8080,
port2: 443,
flow: {
velocity: 0.6245614644998576,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.137-223.112.181.149::192.168.248.155",
type: "link",
id1: "223.112.181.149::192.168.162.137",
id2: "223.112.181.149::192.168.248.155",
w: 4,
d: {
port1: 80,
port2: 80,
flow: {
velocity: 9.353581551521204,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.137-223.112.181.149::192.168.162.118",
type: "link",
id1: "223.112.181.149::192.168.162.137",
id2: "223.112.181.149::192.168.162.118",
w: 4,
d: {
port1: 80,
port2: 20,
flow: {
velocity: 9.31322157785755,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.137-223.112.181.149::192.168.248.236",
type: "link",
id1: "223.112.181.149::192.168.162.137",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 443,
port2: 443,
flow: {
velocity: 4.131968468890945,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.137-223.112.181.149::192.168.248.236",
type: "link",
id1: "223.112.181.149::192.168.162.137",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 443,
port2: 20,
flow: {
velocity: 8.147784657086078,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.137-223.112.181.149::192.168.162.118",
type: "link",
id1: "223.112.181.149::192.168.162.137",
id2: "223.112.181.149::192.168.162.118",
w: 4,
d: {
port1: 443,
port2: 20,
flow: {
velocity: 6.394807610242763,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.137-223.112.181.149::192.168.162.137",
type: "link",
id1: "223.112.181.149::192.168.162.137",
id2: "223.112.181.149::192.168.162.137",
w: 4,
d: {
port1: 443,
port2: 22,
flow: {
velocity: 5.97366969615851,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.137-223.112.181.149::192.168.248.236",
type: "link",
id1: "223.112.181.149::192.168.162.137",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 8080,
port2: 443,
flow: {
velocity: 0.5260086750719006,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.162.137-223.112.181.149::192.168.162.242",
type: "link",
id1: "223.112.181.149::192.168.162.137",
id2: "223.112.181.149::192.168.162.242",
w: 4,
d: {
port1: 22,
port2: 22,
flow: {
velocity: 4.855363791923056,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.192-223.112.181.149::192.168.162.126",
type: "link",
id1: "223.112.181.149::192.168.248.192",
id2: "223.112.181.149::192.168.162.126",
w: 4,
d: {
port1: 8080,
port2: 8080,
flow: {
velocity: 8.450919070188087,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.192-223.112.181.149::192.168.162.137",
type: "link",
id1: "223.112.181.149::192.168.248.192",
id2: "223.112.181.149::192.168.162.137",
w: 4,
d: {
port1: 80,
port2: 8080,
flow: {
velocity: 4.573762733480549,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.155-223.112.181.149::192.168.162.137",
type: "link",
id1: "223.112.181.149::192.168.248.155",
id2: "223.112.181.149::192.168.162.137",
w: 4,
d: {
port1: 22,
port2: 8080,
flow: {
velocity: 9.373201700983657,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.155-223.112.181.149::192.168.248.236",
type: "link",
id1: "223.112.181.149::192.168.248.155",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 443,
port2: 8080,
flow: {
velocity: 4.88708157709882,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.155-223.112.181.149::192.168.162.126",
type: "link",
id1: "223.112.181.149::192.168.248.155",
id2: "223.112.181.149::192.168.162.126",
w: 4,
d: {
port1: 80,
port2: 443,
flow: {
velocity: 6.654234954636449,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.155-223.112.181.149::192.168.162.118",
type: "link",
id1: "223.112.181.149::192.168.248.155",
id2: "223.112.181.149::192.168.162.118",
w: 4,
d: {
port1: 443,
port2: 443,
flow: {
velocity: 1.4787139498451385,
},
},
c: "#454545",
},
{
id: "223.112.181.149::192.168.248.155-223.112.181.149::192.168.248.236",
type: "link",
id1: "223.112.181.149::192.168.248.155",
id2: "223.112.181.149::192.168.248.236",
w: 4,
d: {
port1: 22,
port2: 443,
flow: {
// ...truncated 4334 lines <!doctype html>
<html lang="en" style="background-color: #2d383f">
<head>
<meta charset="utf-8" />
<title>Arranging IT Networks</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"
type="text/css"
href="@fortawesome/[email protected]/css/fontawesome.css"
/>
<link
rel="stylesheet"
type="text/css"
href="@fortawesome/[email protected]/css/solid.css"
/>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="klchart" class="klchart"></div>
<script type="module" src="./code.js"></script>
</body>
</html> .klchart {
border: none;
background-color: #1f1f1f;
}