openwrt-routing/luci-app-bmx6/bmx6/www/luci-static/resources/bmx6/js/bmx6-graph.js

235 lines
6.4 KiB
JavaScript

var graph, canvas, layouter, renderer, divwait, nodes, announcements, nodesIndex, palette, localInfo;
document.addEventListener( "DOMContentLoaded", init, false);
/**
* Returns an index of nodes by name
*/
function createNodeIndex(nodes) {
var inode, index = {};
for (inode in nodes)
index[nodes[inode].name] = nodes[inode];
return index;
}
/**
* Updates to have announcements in nodes list
*/
function processNodeAnnouncements(nodes, announcements) {
var iannouncement, remoteNode, announcement;
nodesIndex = createNodeIndex(nodes);
for(iannouncement in announcements) {
announcement = announcements[iannouncement];
if (announcement.remoteName == '---' ) continue;
if (!( announcement.remoteName in nodesIndex )) {
newNode = {
name: announcement.remoteName,
links: []
};
nodes.push(newNode);
nodesIndex[newNode.name] = newNode;
};
remoteNode = nodesIndex[announcement.remoteName];
if (!( 'announcements' in remoteNode )) remoteNode.announcements = [];
remoteNode.announcements.push(announcement);
};
}
function init() {
palette = generatePalette(200);
graph = new Graph();
canvas = document.getElementById('canvas');
layouter = new Graph.Layout.Spring(graph);
renderer = new Graph.Renderer.Raphael(canvas.id, graph, canvas.offsetWidth, canvas.offsetHeight);
divwait = document.getElementById("wait");
XHR.get('/cgi-bin/luci/admin/network/BMX6/topology', null, function(nodesRequest, nodesData) {
nodes = nodesData;
XHR.get('/cgi-bin/bmx6-info?$myself&', null, function(myselfRequest, myselfData) {
if (myselfData)
localAnnouncements = [
{remoteName: myselfData.myself.hostname, advNet: myselfData.myself.net4},
{remoteName: myselfData.myself.hostname, advNet: myselfData.myself.net6}
];
XHR.get('/cgi-bin/bmx6-info?$tunnels=&', null, function(tunnelsRequest, tunnelsData) {
var iAnnouncement;
announcements = tunnelsData.tunnels;
for(iAnnouncement in localAnnouncements) {
announcements.push(localAnnouncements[iAnnouncement])
};
processNodeAnnouncements(nodes, announcements);
divwait.parentNode.removeChild(divwait);
draw(nodes);
});
});
});
}
function hashCode(str) {
var hash = 0;
if (str.length == 0) return hash;
for (i = 0; i < str.length; i++) {
char = str.charCodeAt(i);
hash = ((hash<<5)-hash)+char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
function generatePalette(size) {
var i, arr = [];
Raphael.getColor(); // just to remove the grey one
for(i = 0; i < size; i++) {
arr.push(Raphael.getColor())
}
return arr;
}
function getFillFromHash(hash) {
return palette[Math.abs(hash % palette.length)];
}
function hashAnnouncementsNames(announcementsNames) {
return hashCode(announcementsNames.sort().join('-'));
}
function getNodeAnnouncements(networkNode) {
return networkNode.announcements;
}
function nodeRenderer(raphael, node) {
var nodeFill, renderedNode, options;
options = {
'fill': 'announcements' in node.networkNode ? getFillFromHash(
hashAnnouncementsNames(
getNodeAnnouncements(node.networkNode).map(function(ann) {return ann.advNet;})
)
) : '#bfbfbf',
'stroke-width': 1,
};
renderedNode = raphael.set();
renderedNode.push(raphael.ellipse(node.point[0], node.point[1], 30, 20).attr({"fill": options['fill'], "stroke-width": options['stroke-width']}));
renderedNode.push(raphael.text(node.point[0], node.point[1] + 30, node.networkNode.name).attr({}));
renderedNode.items.forEach(function(el) {
var announcements, tooltip = raphael.set();
tooltip.push(raphael.rect(-60, -60, 120, 60).attr({"fill": "#fec", "stroke-width": 1, r : "9px"}));
announcements = getNodeAnnouncements(node.networkNode);
if (announcements) {
announcements = announcements.map(function(ann) {return ann.advNet});
tooltip.push(raphael.text(0, -40, 'announcements\n' + announcements.join('\n')).attr({}));
};
el.tooltip(tooltip);
});
return renderedNode;
}
function genericNodeRenderer(raphael, node) {
var renderedNode;
renderedNode = raphael.set();
renderedNode.push(raphael.ellipse(node.point[0], node.point[1], 30, 20).attr({"fill": '#bfbfbf', "stroke-width": 1}));
renderedNode.push(raphael.text(node.point[0], node.point[1] + 30, node.networkNode.name).attr({}));
return renderedNode;
}
function redraw() {
layouter.layout();
renderer.draw();
}
function interpolateColor(minColor,maxColor,maxDepth,depth){
function d2h(d) {return d.toString(16);}
function h2d(h) {return parseInt(h,16);}
if(depth == 0){
return minColor;
}
if(depth == maxDepth){
return maxColor;
}
var color = "#";
for(var i=1; i <= 6; i+=2){
var minVal = new Number(h2d(minColor.substr(i,2)));
var maxVal = new Number(h2d(maxColor.substr(i,2)));
var nVal = minVal + (maxVal-minVal) * (depth/maxDepth);
var val = d2h(Math.floor(nVal));
while(val.length < 2){
val = "0"+val;
}
color += val;
}
return color;
}
function draw(nodes) {
var node, neighbourNode, seenKey, rxRate, txRate, seen, i, j, currentName, linkQuality;
seen = { };
for (i = 0; i < (nodes.length); i++) {
node = nodes[i];
graph.addNode(node.name, {
networkNode: node,
render: nodeRenderer
});
};
for (i = 0; i < (nodes.length); i++) {
node = nodes[i];
if (! node.name) continue;
currentName = node.name;
for (j = 0; j < (node.links.length); j++) {
neighbourNode = node.links[j];
graph.addNode(neighbourNode.name, {render: genericNodeRenderer, networkNode: neighbourNode});
seenKey = (node.name < neighbourNode.name) ? node.name + '|' + neighbourNode.name : neighbourNode.name + '|' + node.name;
rxRate = neighbourNode.rxRate;
txRate = neighbourNode.txRate;
if (!seen[seenKey] && rxRate > 0 && txRate > 0) {
linkQuality = ( rxRate + txRate ) / 2;
graph.addEdge(node.name, neighbourNode.name, {
'label': rxRate + '/' + txRate,
'directed': false,
'stroke': interpolateColor('FF0000','00FF00', 5, 5 * ( linkQuality - 1 )/100),
'fill': interpolateColor('FF0000','00FF00', 5, 5 * ( linkQuality - 1 )/100),
'label-style': { 'font-size': 8 }
});
seen[seenKey] = true;
}
}
}
redraw();
}