[TASK] Reformat code spacing & remove braces jslint

This commit is contained in:
Xaver Maierhofer 2016-05-22 14:51:30 +02:00
parent c0726461fb
commit a828a55d51
35 changed files with 1631 additions and 1359 deletions

View File

@ -8,7 +8,7 @@ module.exports = function (grunt) {
} }
}); });
grunt.registerTask("saveRevision", function() { grunt.registerTask("saveRevision", function () {
grunt.event.once("git-describe", function (rev) { grunt.event.once("git-describe", function (rev) {
grunt.option("gitRevision", rev); grunt.option("gitRevision", rev);
}); });

View File

@ -1,9 +1,9 @@
({ ({
baseUrl: "lib", baseUrl: "lib",
name: "../bower_components/almond/almond", name: "../bower_components/almond/almond",
mainConfigFile: "app.js", mainConfigFile: "app.js",
include: "../app", include: "../app",
wrap: true, wrap: true,
optimize: "uglify", optimize: "uglify",
out: "app-combined.js" out: "app-combined.js"
}); });

View File

@ -13,9 +13,21 @@
} }
], ],
"siteNames": [ "siteNames": [
{ "site": "ffhl", "name": "Lübeck" }, {
{ "site": "ffeh", "name": "Entenhausen" }, "site": "ffhl",
{ "site": "ffgt", "name": "Gothamcity" }, "name": "Lübeck"
{ "site": "ffal", "name": "Atlantis" } },
{
"site": "ffeh",
"name": "Entenhausen"
},
{
"site": "ffgt",
"name": "Gothamcity"
},
{
"site": "ffal",
"name": "Atlantis"
}
] ]
} }

View File

@ -1,9 +1,9 @@
function get(url) { function get(url) {
return new Promise(function(resolve, reject) { return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.open('GET', url); req.open('GET', url);
req.onload = function() { req.onload = function () {
if (req.status == 200) { if (req.status == 200) {
resolve(req.response); resolve(req.response);
} }
@ -12,7 +12,7 @@ function get(url) {
} }
}; };
req.onerror = function() { req.onerror = function () {
reject(Error("Network Error")); reject(Error("Network Error"));
}; };
@ -25,19 +25,19 @@ function getJSON(url) {
} }
function sortByKey(key, d) { function sortByKey(key, d) {
return d.slice().sort( function (a, b) { return d.slice().sort(function (a, b) {
return a[key] - b[key] return a[key] - b[key]
}).reverse() }).reverse()
} }
function limit(key, m, d) { function limit(key, m, d) {
return d.filter( function (d) { return d.filter(function (d) {
return d[key].isAfter(m) return d[key].isAfter(m)
}) })
} }
function sum(a) { function sum(a) {
return a.reduce( function (a, b) { return a.reduce(function (a, b) {
return a + b return a + b
}, 0) }, 0)
} }
@ -53,11 +53,13 @@ function trueDefault(d) {
function dictGet(dict, key) { function dictGet(dict, key) {
var k = key.shift(); var k = key.shift();
if (!(k in dict)) if (!(k in dict)) {
return null; return null;
}
if (key.length == 0) if (key.length == 0) {
return dict[k]; return dict[k];
}
return dictGet(dict[k], key) return dictGet(dict[k], key)
} }
@ -68,7 +70,7 @@ function localStorageTest() {
localStorage.setItem(test, test); localStorage.setItem(test, test);
localStorage.removeItem(test); localStorage.removeItem(test);
return true return true
} catch(e) { } catch (e) {
return false return false
} }
} }
@ -93,18 +95,18 @@ function online(d) {
function has_location(d) { function has_location(d) {
return "location" in d.nodeinfo && return "location" in d.nodeinfo &&
Math.abs(d.nodeinfo.location.latitude) < 90 && Math.abs(d.nodeinfo.location.latitude) < 90 &&
Math.abs(d.nodeinfo.location.longitude) < 180 Math.abs(d.nodeinfo.location.longitude) < 180
} }
function subtract(a, b) { function subtract(a, b) {
var ids = {}; var ids = {};
b.forEach( function (d) { b.forEach(function (d) {
ids[d.nodeinfo.node_id] = true ids[d.nodeinfo.node_id] = true
}); });
return a.filter( function (d) { return a.filter(function (d) {
return !(d.nodeinfo.node_id in ids) return !(d.nodeinfo.node_id in ids)
}) })
} }
@ -112,21 +114,23 @@ function subtract(a, b) {
/* Helpers working with links */ /* Helpers working with links */
function showDistance(d) { function showDistance(d) {
if (isNaN(d.distance)) if (isNaN(d.distance)) {
return; return;
}
return numeral(d.distance).format("0,0") + " m" return numeral(d.distance).format("0,0") + " m"
} }
function showTq(d) { function showTq(d) {
return numeral(1/d.tq).format("0%") return numeral(1 / d.tq).format("0%")
} }
/* Infobox stuff (XXX: move to module) */ /* Infobox stuff (XXX: move to module) */
function attributeEntry(el, label, value) { function attributeEntry(el, label, value) {
if (value === null || value == undefined) if (value === null || value == undefined) {
return; return;
}
var tr = document.createElement("tr"); var tr = document.createElement("tr");
var th = document.createElement("th"); var th = document.createElement("th");
@ -135,10 +139,11 @@ function attributeEntry(el, label, value) {
var td = document.createElement("td"); var td = document.createElement("td");
if (typeof value == "function") if (typeof value == "function") {
value(td); value(td);
else } else {
td.appendChild(document.createTextNode(value)); td.appendChild(document.createTextNode(value));
}
tr.appendChild(td); tr.appendChild(td);
@ -152,25 +157,29 @@ function createIframe(opt, width, height) {
width = typeof width !== 'undefined' ? width : '525px'; width = typeof width !== 'undefined' ? width : '525px';
height = typeof height !== 'undefined' ? height : '350px'; height = typeof height !== 'undefined' ? height : '350px';
if (opt.src) if (opt.src) {
el.src = opt.src; el.src = opt.src;
else } else {
el.src = opt; el.src = opt;
}
if (opt.frameBorder) if (opt.frameBorder) {
el.frameBorder = opt.frameBorder; el.frameBorder = opt.frameBorder;
else } else {
el.frameBorder = 1; el.frameBorder = 1;
}
if (opt.width) if (opt.width) {
el.width = opt.width; el.width = opt.width;
else } else {
el.width = width; el.width = width;
}
if (opt.height) if (opt.height) {
el.height = opt.height; el.height = opt.height;
else } else {
el.height = height; el.height = height;
}
el.scrolling = "no"; el.scrolling = "no";
el.seamless = "seamless"; el.seamless = "seamless";
@ -190,16 +199,18 @@ function showStat(o, subst) {
if (o.caption) { if (o.caption) {
caption = listReplace(o.caption, subst); caption = listReplace(o.caption, subst);
if (!content) if (!content) {
content = document.createTextNode(caption) content = document.createTextNode(caption)
}
} }
if (o.iframe) { if (o.iframe) {
content = createIframe(o.iframe, o.width, o.height); content = createIframe(o.iframe, o.width, o.height);
if (o.iframe.src) if (o.iframe.src) {
content.src = listReplace(o.iframe.src, subst); content.src = listReplace(o.iframe.src, subst);
else } else {
content.src = listReplace(o.iframe, subst) content.src = listReplace(o.iframe, subst)
}
} }
var p = document.createElement("p"); var p = document.createElement("p");
@ -210,12 +221,14 @@ function showStat(o, subst) {
link.href = listReplace(o.href, subst); link.href = listReplace(o.href, subst);
link.appendChild(content); link.appendChild(content);
if (caption && o.thumbnail) if (caption && o.thumbnail) {
link.title = caption; link.title = caption;
}
p.appendChild(link) p.appendChild(link)
} else } else {
p.appendChild(content); p.appendChild(content);
}
return p return p
} }

View File

@ -1,18 +1,18 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no"> <meta name="viewport" content="width=device-width, user-scalable=no">
<link rel="stylesheet" href="css/ionicons.min.css"> <link rel="stylesheet" href="css/ionicons.min.css">
<link rel="stylesheet" href="roboto-slab-fontface.css"> <link rel="stylesheet" href="roboto-slab-fontface.css">
<link rel="stylesheet" href="roboto-fontface.css"> <link rel="stylesheet" href="roboto-fontface.css">
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<script src="vendor/es6-shim/es6-shim.min.js"></script> <script src="vendor/es6-shim/es6-shim.min.js"></script>
<script src="app.js"></script> <script src="app.js"></script>
<script> <script>
console.log("Version: #revision#") console.log("Version: #revision#")
</script> </script>
</head> </head>
<body> <body>
</body> </body>
</html> </html>

View File

@ -1,17 +1,17 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no"> <meta name="viewport" content="width=device-width, user-scalable=no">
<link rel="stylesheet" href="bower_components/roboto-slab-fontface/roboto-slab-fontface.css"> <link rel="stylesheet" href="bower_components/roboto-slab-fontface/roboto-slab-fontface.css">
<link rel="stylesheet" href="bower_components/roboto-fontface/roboto-fontface.css"> <link rel="stylesheet" href="bower_components/roboto-fontface/roboto-fontface.css">
<link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css"> <link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css">
<link rel="stylesheet" href="bower_components/Leaflet.label/dist/leaflet.label.css"> <link rel="stylesheet" href="bower_components/Leaflet.label/dist/leaflet.label.css">
<link rel="stylesheet" href="bower_components/ionicons/css/ionicons.min.css"> <link rel="stylesheet" href="bower_components/ionicons/css/ionicons.min.css">
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<script src="bower_components/es6-shim/es6-shim.min.js"></script> <script src="bower_components/es6-shim/es6-shim.min.js"></script>
<script src="bower_components/requirejs/require.js" data-main="app"></script> <script src="bower_components/requirejs/require.js" data-main="app"></script>
</head> </head>
<body> <body>
</body> </body>
</html> </html>

View File

@ -1,5 +1,5 @@
define(function () { define(function () {
return function() { return function () {
this.render = function (d) { this.render = function (d) {
var el = document.createElement("div"); var el = document.createElement("div");
d.appendChild(el); d.appendChild(el);

View File

@ -1,7 +1,8 @@
define([], function () { define([], function () {
return function (tag) { return function (tag) {
if (!tag) if (!tag) {
tag = "div"; tag = "div";
}
var self = this; var self = this;

View File

@ -7,14 +7,17 @@ define(["filters/nodefilter"], function (NodeFilter) {
var data; var data;
function remove(d) { function remove(d) {
targets = targets.filter( function (e) { return d !== e; } ); targets = targets.filter(function (e) {
return d !== e;
});
} }
function add(d) { function add(d) {
targets.push(d); targets.push(d);
if (filteredData !== undefined) if (filteredData !== undefined) {
d.setData(filteredData); d.setData(filteredData);
}
} }
function setData(d) { function setData(d) {
@ -23,24 +26,27 @@ define(["filters/nodefilter"], function (NodeFilter) {
} }
function refresh() { function refresh() {
if (data === undefined) if (data === undefined) {
return; return;
}
var filter = filters.reduce( function (a, f) { var filter = filters.reduce(function (a, f) {
return function (d) { return function (d) {
return a(d) && f.run(d); return a(d) && f.run(d);
}; };
}, function () { return true; }); }, function () {
return true;
});
filteredData = new NodeFilter(filter)(data); filteredData = new NodeFilter(filter)(data);
targets.forEach( function (t) { targets.forEach(function (t) {
t.setData(filteredData); t.setData(filteredData);
}); });
} }
function notifyObservers() { function notifyObservers() {
filterObservers.forEach( function (d) { filterObservers.forEach(function (d) {
d.filtersChanged(filters); d.filtersChanged(filters);
}); });
} }
@ -53,7 +59,9 @@ define(["filters/nodefilter"], function (NodeFilter) {
} }
function removeFilter(d) { function removeFilter(d) {
filters = filters.filter( function (e) { return d !== e; } ); filters = filters.filter(function (e) {
return d !== e;
});
notifyObservers(); notifyObservers();
refresh(); refresh();
} }
@ -64,17 +72,20 @@ define(["filters/nodefilter"], function (NodeFilter) {
d.filtersChanged(filters); d.filtersChanged(filters);
return function () { return function () {
filterObservers = filterObservers.filter( function (e) { return d !== e; }); filterObservers = filterObservers.filter(function (e) {
return d !== e;
});
}; };
} }
return { add: add, return {
remove: remove, add: add,
setData: setData, remove: remove,
addFilter: addFilter, setData: setData,
removeFilter: removeFilter, addFilter: addFilter,
watchFilters: watchFilters, removeFilter: removeFilter,
refresh: refresh watchFilters: watchFilters,
}; refresh: refresh
};
}; };
}); });

View File

@ -9,10 +9,11 @@ define([], function () {
} }
function filtersChanged(filters) { function filtersChanged(filters) {
while (container.firstChild) while (container.firstChild) {
container.removeChild(container.firstChild); container.removeChild(container.firstChild);
}
filters.forEach( function (d) { filters.forEach(function (d) {
var li = document.createElement("li"); var li = document.createElement("li");
var div = document.createElement("div"); var div = document.createElement("div");
container.appendChild(li); container.appendChild(li);
@ -27,14 +28,16 @@ define([], function () {
li.appendChild(button); li.appendChild(button);
}); });
if (container.parentNode === div && filters.length === 0) if (container.parentNode === div && filters.length === 0) {
div.removeChild(container); div.removeChild(container);
else if (filters.length > 0) } else if (filters.length > 0) {
div.appendChild(container); div.appendChild(container);
}
} }
return { render: render, return {
filtersChanged: filtersChanged render: render,
}; filtersChanged: filtersChanged
};
}; };
}); });

View File

@ -11,8 +11,9 @@ define([], function () {
function run(d) { function run(d) {
var o = dictGet(d, key.slice(0)); var o = dictGet(d, key.slice(0));
if (f) if (f) {
o = f(o); o = f(o);
}
return o === value ? !negate : negate; return o === value ? !negate : negate;
} }
@ -22,10 +23,11 @@ define([], function () {
} }
function draw(el) { function draw(el) {
if (negate) if (negate) {
el.parentNode.classList.add("not"); el.parentNode.classList.add("not");
else } else {
el.parentNode.classList.remove("not"); el.parentNode.classList.remove("not");
}
strong.textContent = (negate ? "¬" : "" ) + value; strong.textContent = (negate ? "¬" : "" ) + value;
} }
@ -39,14 +41,16 @@ define([], function () {
draw(el); draw(el);
if (refresh) if (refresh) {
refresh(); refresh();
}
}; };
} }
return { run: run, return {
setRefresh: setRefresh, run: run,
render: render setRefresh: setRefresh,
}; render: render
};
}; };
}); });

View File

@ -11,20 +11,22 @@ define([], function () {
var filteredIds = new Set(); var filteredIds = new Set();
n.graph = {}; n.graph = {};
n.graph.nodes = data.graph.nodes.filter( function (d) { n.graph.nodes = data.graph.nodes.filter(function (d) {
var r; var r;
if (d.node) if (d.node) {
r = filter(d.node); r = filter(d.node);
else } else {
r = filter({}); r = filter({});
}
if (r) if (r) {
filteredIds.add(d.id); filteredIds.add(d.id);
}
return r; return r;
}); });
n.graph.links = data.graph.links.filter( function (d) { n.graph.links = data.graph.links.filter(function (d) {
return filteredIds.has(d.source.id) && filteredIds.has(d.target.id); return filteredIds.has(d.source.id) && filteredIds.has(d.target.id);
}); });

View File

@ -32,21 +32,23 @@ define(["d3"], function (d3) {
} }
function savePositions() { function savePositions() {
if (!localStorageTest()) if (!localStorageTest()) {
return; return;
}
var save = intNodes.map( function (d) { var save = intNodes.map(function (d) {
return { id: d.o.id, x: d.x, y: d.y }; return {id: d.o.id, x: d.x, y: d.y};
}); });
localStorage.setItem("graph/nodeposition", JSON.stringify(save)); localStorage.setItem("graph/nodeposition", JSON.stringify(save));
} }
function nodeName(d) { function nodeName(d) {
if (d.o.node && d.o.node.nodeinfo) if (d.o.node && d.o.node.nodeinfo) {
return d.o.node.nodeinfo.hostname; return d.o.node.nodeinfo.hostname;
else } else {
return d.o.id; return d.o.id;
}
} }
function dragstart() { function dragstart() {
@ -56,8 +58,9 @@ define(["d3"], function (d3) {
return distancePoint(e, d) < NODE_RADIUS; return distancePoint(e, d) < NODE_RADIUS;
}); });
if (nodes.length === 0) if (nodes.length === 0) {
return; return;
}
draggedNode = nodes[0]; draggedNode = nodes[0];
d3.event.sourceEvent.stopPropagation(); d3.event.sourceEvent.stopPropagation();
@ -88,9 +91,9 @@ define(["d3"], function (d3) {
} }
var draggableNode = d3.behavior.drag() var draggableNode = d3.behavior.drag()
.on("dragstart", dragstart) .on("dragstart", dragstart)
.on("drag", dragmove) .on("drag", dragmove)
.on("dragend", dragend); .on("dragend", dragend);
function animatePanzoom(translate, scale) { function animatePanzoom(translate, scale) {
var translateP = zoomBehavior.translate(); var translateP = zoomBehavior.translate();
@ -110,8 +113,9 @@ define(["d3"], function (d3) {
var ease = d3.ease("cubic-in-out"); var ease = d3.ease("cubic-in-out");
d3.timer(function (t) { d3.timer(function (t) {
if (t >= duration) if (t >= duration) {
return true; return true;
}
var v = interpolate(ease(t / duration)); var v = interpolate(ease(t / duration));
zoomBehavior.translate([v.x, v.y]); zoomBehavior.translate([v.x, v.y]);
@ -124,8 +128,10 @@ define(["d3"], function (d3) {
} }
function onPanZoom() { function onPanZoom() {
savedPanZoom = {translate: zoomBehavior.translate(), savedPanZoom = {
scale: zoomBehavior.scale()}; translate: zoomBehavior.translate(),
scale: zoomBehavior.scale()
};
panzoom(); panzoom();
} }
@ -133,14 +139,15 @@ define(["d3"], function (d3) {
var translate = zoomBehavior.translate(); var translate = zoomBehavior.translate();
var scale = zoomBehavior.scale(); var scale = zoomBehavior.scale();
panzoomReal(translate, scale); panzoomReal(translate, scale);
} }
function panzoomReal(translate, scale) { function panzoomReal(translate, scale) {
screenRect = {left: -translate[0] / scale, top: -translate[1] / scale, screenRect = {
right: (canvas.width - translate[0]) / scale, left: -translate[0] / scale, top: -translate[1] / scale,
bottom: (canvas.height - translate[1]) / scale}; right: (canvas.width - translate[0]) / scale,
bottom: (canvas.height - translate[1]) / scale
};
requestAnimationFrame(redraw); requestAnimationFrame(redraw);
} }
@ -177,15 +184,16 @@ define(["d3"], function (d3) {
highlightedNodes = []; highlightedNodes = [];
highlightedLinks = []; highlightedLinks = [];
if (highlight !== undefined) if (highlight !== undefined) {
if (highlight.type === "node") { if (highlight.type === "node") {
var n = nodesDict[highlight.o.nodeinfo.node_id]; var n = nodesDict[highlight.o.nodeinfo.node_id];
if (n) { if (n) {
highlightedNodes = [n]; highlightedNodes = [n];
if (!nopanzoom) if (!nopanzoom) {
panzoomTo([n.x, n.y], [n.x, n.y]); panzoomTo([n.x, n.y], [n.x, n.y]);
}
} }
return; return;
@ -196,20 +204,27 @@ define(["d3"], function (d3) {
highlightedLinks = [l]; highlightedLinks = [l];
if (!nopanzoom) { if (!nopanzoom) {
var x = d3.extent([l.source, l.target], function (d) { return d.x; }); var x = d3.extent([l.source, l.target], function (d) {
var y = d3.extent([l.source, l.target], function (d) { return d.y; }); return d.x;
});
var y = d3.extent([l.source, l.target], function (d) {
return d.y;
});
panzoomTo([x[0], y[0]], [x[1], y[1]]); panzoomTo([x[0], y[0]], [x[1], y[1]]);
} }
} }
return; return;
} }
}
if (!nopanzoom) if (!nopanzoom) {
if (!savedPanZoom) if (!savedPanZoom) {
panzoomTo([0, 0], force.size()); panzoomTo([0, 0], force.size());
else } else {
animatePanzoom(savedPanZoom.translate, savedPanZoom.scale); animatePanzoom(savedPanZoom.translate, savedPanZoom.scale);
}
}
} }
function drawLabel(d) { function drawLabel(d) {
@ -226,8 +241,9 @@ define(["d3"], function (d3) {
var angle = Math.PI / 2; var angle = Math.PI / 2;
if (neighbours.length > 0) if (neighbours.length > 0) {
angle = Math.PI + Math.atan2(sumSin, sumCos); angle = Math.PI + Math.atan2(sumSin, sumCos);
}
var cos = Math.cos(angle); var cos = Math.cos(angle);
var sin = Math.sin(angle); var sin = Math.sin(angle);
@ -243,14 +259,14 @@ define(["d3"], function (d3) {
function visibleLinks(d) { function visibleLinks(d) {
return (d.source.x > screenRect.left && d.source.x < screenRect.right && return (d.source.x > screenRect.left && d.source.x < screenRect.right &&
d.source.y > screenRect.top && d.source.y < screenRect.bottom) || d.source.y > screenRect.top && d.source.y < screenRect.bottom) ||
(d.target.x > screenRect.left && d.target.x < screenRect.right && (d.target.x > screenRect.left && d.target.x < screenRect.right &&
d.target.y > screenRect.top && d.target.y < screenRect.bottom); d.target.y > screenRect.top && d.target.y < screenRect.bottom);
} }
function visibleNodes(d) { function visibleNodes(d) {
return d.x + margin > screenRect.left && d.x - margin < screenRect.right && return d.x + margin > screenRect.left && d.x - margin < screenRect.right &&
d.y + margin > screenRect.top && d.y - margin < screenRect.bottom; d.y + margin > screenRect.top && d.y - margin < screenRect.bottom;
} }
function drawNode(color, radius, scale, r) { function drawNode(color, radius, scale, r) {
@ -351,7 +367,6 @@ define(["d3"], function (d3) {
ctx.stroke(); ctx.stroke();
// -- draw nodes -- // -- draw nodes --
ctx.save(); ctx.save();
ctx.scale(1 / scale / r, 1 / scale / r); ctx.scale(1 / scale / r, 1 / scale / r);
@ -378,8 +393,9 @@ define(["d3"], function (d3) {
ctx.beginPath(); ctx.beginPath();
nodes.filter(visibleNodes).forEach(function (d) { nodes.filter(visibleNodes).forEach(function (d) {
var clients = d.o.node.statistics.clients; var clients = d.o.node.statistics.clients;
if (clients === 0) if (clients === 0) {
return; return;
}
var startDistance = 16; var startDistance = 16;
var radius = 3; var radius = 3;
@ -449,8 +465,9 @@ define(["d3"], function (d3) {
} }
// -- draw labels -- // -- draw labels --
if (scale > 0.9) if (scale > 0.9) {
intNodes.filter(visibleNodes).forEach(drawLabel, scale); intNodes.filter(visibleNodes).forEach(drawLabel, scale);
}
ctx.restore(); ctx.restore();
} }
@ -483,33 +500,40 @@ define(["d3"], function (d3) {
var l2 = distance(a, b); var l2 = distance(a, b);
if (l2 === 0) if (l2 === 0) {
return distance(p, a); return distance(p, a);
}
var t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / l2; var t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / l2;
if (t < 0) if (t < 0) {
return distance(p, a); return distance(p, a);
}
if (t > 1) if (t > 1) {
return distance(p, b); return distance(p, b);
}
return Math.sqrt(distance(p, { x: a.x + t * (b.x - a.x), return Math.sqrt(distance(p, {
y: a.y + t * (b.y - a.y) })); x: a.x + t * (b.x - a.x),
y: a.y + t * (b.y - a.y)
}));
} }
function translateXY(d) { function translateXY(d) {
var translate = zoomBehavior.translate(); var translate = zoomBehavior.translate();
var scale = zoomBehavior.scale(); var scale = zoomBehavior.scale();
return {x: (d[0] - translate[0]) / scale, return {
y: (d[1] - translate[1]) / scale x: (d[0] - translate[0]) / scale,
}; y: (d[1] - translate[1]) / scale
};
} }
function onClick() { function onClick() {
if (d3.event.defaultPrevented) if (d3.event.defaultPrevented) {
return; return;
}
var e = translateXY(d3.mouse(el)); var e = translateXY(d3.mouse(el));
@ -550,14 +574,17 @@ define(["d3"], function (d3) {
return function () { return function () {
var e = d3.event; var e = d3.event;
if (e.altKey || e.ctrlKey || e.metaKey) if (e.altKey || e.ctrlKey || e.metaKey) {
return; return;
}
if (e.keyCode === 43) if (e.keyCode === 43) {
zoom(z, 1.41); zoom(z, 1.41);
}
if (e.keyCode === 45) if (e.keyCode === 45) {
zoom(z, 1 / 1.41); zoom(z, 1 / 1.41);
}
}; };
} }
@ -565,38 +592,40 @@ define(["d3"], function (d3) {
el.classList.add("graph"); el.classList.add("graph");
zoomBehavior = d3.behavior.zoom() zoomBehavior = d3.behavior.zoom()
.scaleExtent([1 / 3, 3]) .scaleExtent([1 / 3, 3])
.on("zoom", onPanZoom) .on("zoom", onPanZoom)
.translate([sidebar(), 0]); .translate([sidebar(), 0]);
canvas = d3.select(el) canvas = d3.select(el)
.attr("tabindex", 1) .attr("tabindex", 1)
.on("keypress", keyboardZoom(zoomBehavior)) .on("keypress", keyboardZoom(zoomBehavior))
.call(zoomBehavior) .call(zoomBehavior)
.append("canvas") .append("canvas")
.on("click", onClick) .on("click", onClick)
.call(draggableNode) .call(draggableNode)
.node(); .node();
ctx = canvas.getContext("2d"); ctx = canvas.getContext("2d");
force = d3.layout.force() force = d3.layout.force()
.charge(-250) .charge(-250)
.gravity(0.1) .gravity(0.1)
.linkDistance(function (d) { .linkDistance(function (d) {
if (d.o.type === "fastd" || d.o.type === "L2TP") if (d.o.type === "fastd" || d.o.type === "L2TP") {
return 0; return 0;
else } else {
return LINK_DISTANCE; return LINK_DISTANCE;
}) }
.linkStrength(function (d) { })
if (d.o.type === "fastd" || d.o.type === "L2TP") .linkStrength(function (d) {
return 0; if (d.o.type === "fastd" || d.o.type === "L2TP") {
else return 0;
return Math.max(0.5, 1 / d.o.tq); } else {
}) return Math.max(0.5, 1 / d.o.tq);
.on("tick", tickEvent) }
.on("end", savePositions); })
.on("tick", tickEvent)
.on("end", savePositions);
window.addEventListener("resize", resizeCanvas); window.addEventListener("resize", resizeCanvas);
@ -605,16 +634,17 @@ define(["d3"], function (d3) {
self.setData = function (data) { self.setData = function (data) {
var oldNodes = {}; var oldNodes = {};
intNodes.forEach( function (d) { intNodes.forEach(function (d) {
oldNodes[d.o.id] = d; oldNodes[d.o.id] = d;
}); });
intNodes = data.graph.nodes.map( function (d) { intNodes = data.graph.nodes.map(function (d) {
var e; var e;
if (d.id in oldNodes) if (d.id in oldNodes) {
e = oldNodes[d.id]; e = oldNodes[d.id];
else } else {
e = {}; e = {};
}
e.o = d; e.o = d;
@ -623,31 +653,33 @@ define(["d3"], function (d3) {
var newNodesDict = {}; var newNodesDict = {};
intNodes.forEach( function (d) { intNodes.forEach(function (d) {
newNodesDict[d.o.id] = d; newNodesDict[d.o.id] = d;
}); });
var oldLinks = {}; var oldLinks = {};
intLinks.forEach( function (d) { intLinks.forEach(function (d) {
oldLinks[d.o.id] = d; oldLinks[d.o.id] = d;
}); });
intLinks = data.graph.links.map( function (d) { intLinks = data.graph.links.map(function (d) {
var e; var e;
if (d.id in oldLinks) if (d.id in oldLinks) {
e = oldLinks[d.id]; e = oldLinks[d.id];
else } else {
e = {}; e = {};
}
e.o = d; e.o = d;
e.source = newNodesDict[d.source.id]; e.source = newNodesDict[d.source.id];
e.target = newNodesDict[d.target.id]; e.target = newNodesDict[d.target.id];
if (d.type === "fastd" || d.type === "L2TP") if (d.type === "fastd" || d.type === "L2TP") {
e.color = "rgba(255, 255, 255, " + (0.6 / d.tq) + ")"; e.color = "rgba(255, 255, 255, " + (0.6 / d.tq) + ")";
else } else {
e.color = linkScale(d.tq).hex(); e.color = linkScale(d.tq).hex();
}
return e; return e;
}); });
@ -658,8 +690,9 @@ define(["d3"], function (d3) {
intNodes.forEach(function (d) { intNodes.forEach(function (d) {
d.neighbours = {}; d.neighbours = {};
if (d.o.node) if (d.o.node) {
nodesDict[d.o.node.nodeinfo.node_id] = d; nodesDict[d.o.node.nodeinfo.node_id] = d;
}
var name = nodeName(d); var name = nodeName(d);
@ -692,8 +725,9 @@ define(["d3"], function (d3) {
d.source.neighbours[d.target.o.id] = {node: d.target, link: d}; d.source.neighbours[d.target.o.id] = {node: d.target, link: d};
d.target.neighbours[d.source.o.id] = {node: d.source, link: d}; d.target.neighbours[d.source.o.id] = {node: d.source, link: d};
if (d.o.source && d.o.target) if (d.o.source && d.o.target) {
linksDict[d.o.id] = d; linksDict[d.o.id] = d;
}
}); });
intNodes.forEach(function (d) { intNodes.forEach(function (d) {
@ -702,22 +736,32 @@ define(["d3"], function (d3) {
}); });
}); });
nodes = intNodes.filter(function (d) { return !d.o.unseen && d.o.node; }); nodes = intNodes.filter(function (d) {
uplinkNodes = nodes.filter(function (d) { return d.o.node.flags.uplink; }); return !d.o.unseen && d.o.node;
nonUplinkNodes = nodes.filter(function (d) { return !d.o.node.flags.uplink; }); });
unseenNodes = intNodes.filter(function (d) { return d.o.unseen && d.o.node; }); uplinkNodes = nodes.filter(function (d) {
unknownNodes = intNodes.filter(function (d) { return !d.o.node; }); return d.o.node.flags.uplink;
});
nonUplinkNodes = nodes.filter(function (d) {
return !d.o.node.flags.uplink;
});
unseenNodes = intNodes.filter(function (d) {
return d.o.unseen && d.o.node;
});
unknownNodes = intNodes.filter(function (d) {
return !d.o.node;
});
if (localStorageTest()) { if (localStorageTest()) {
var save = JSON.parse(localStorage.getItem("graph/nodeposition")); var save = JSON.parse(localStorage.getItem("graph/nodeposition"));
if (save) { if (save) {
var nodePositions = {}; var nodePositions = {};
save.forEach( function (d) { save.forEach(function (d) {
nodePositions[d.id] = d; nodePositions[d.id] = d;
}); });
intNodes.forEach( function (d) { intNodes.forEach(function (d) {
if (nodePositions[d.o.id] && (d.x === undefined || d.y === undefined)) { if (nodePositions[d.o.id] && (d.x === undefined || d.y === undefined)) {
d.x = nodePositions[d.o.id].x; d.x = nodePositions[d.o.id].x;
d.y = nodePositions[d.o.id].y; d.y = nodePositions[d.o.id].y;
@ -729,8 +773,8 @@ define(["d3"], function (d3) {
var diameter = graphDiameter(intNodes); var diameter = graphDiameter(intNodes);
force.nodes(intNodes) force.nodes(intNodes)
.links(intLinks) .links(intLinks)
.size([diameter, diameter]); .size([diameter, diameter]);
updateHighlight(true); updateHighlight(true);
@ -761,8 +805,9 @@ define(["d3"], function (d3) {
canvas.remove(); canvas.remove();
force = null; force = null;
if (el.parentNode) if (el.parentNode) {
el.parentNode.removeChild(el); el.parentNode.removeChild(el);
}
}; };
self.render = function (d) { self.render = function (d) {

View File

@ -1,122 +1,124 @@
define([ "chroma-js", "map", "sidebar", "tabs", "container", "meshstats", define(["chroma-js", "map", "sidebar", "tabs", "container", "meshstats",
"legend", "linklist", "nodelist", "simplenodelist", "infobox/main", "legend", "linklist", "nodelist", "simplenodelist", "infobox/main",
"proportions", "forcegraph", "title", "about", "datadistributor", "proportions", "forcegraph", "title", "about", "datadistributor",
"filters/filtergui" ], "filters/filtergui"],
function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist, function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph, Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph,
Title, About, DataDistributor, FilterGUI) { Title, About, DataDistributor, FilterGUI) {
return function (config, router) { return function (config, router) {
var self = this; var self = this;
var content; var content;
var contentDiv; var contentDiv;
var linkScale = chroma.scale(chroma.interpolate.bezier(["#04C714", "#FF5500", "#F02311"])).domain([1, 5]); var linkScale = chroma.scale(chroma.interpolate.bezier(["#04C714", "#FF5500", "#F02311"])).domain([1, 5]);
var sidebar; var sidebar;
var buttons = document.createElement("div"); var buttons = document.createElement("div");
buttons.classList.add("buttons"); buttons.classList.add("buttons");
var fanout = new DataDistributor(); var fanout = new DataDistributor();
var fanoutUnfiltered = new DataDistributor(); var fanoutUnfiltered = new DataDistributor();
fanoutUnfiltered.add(fanout); fanoutUnfiltered.add(fanout);
function removeContent() { function removeContent() {
if (!content) if (!content) {
return; return;
}
router.removeTarget(content); router.removeTarget(content);
fanout.remove(content); fanout.remove(content);
content.destroy(); content.destroy();
content = null; content = null;
} }
function addContent(K) { function addContent(K) {
removeContent(); removeContent();
content = new K(config, linkScale, sidebar.getWidth, router, buttons); content = new K(config, linkScale, sidebar.getWidth, router, buttons);
content.render(contentDiv); content.render(contentDiv);
fanout.add(content); fanout.add(content);
router.addTarget(content); router.addTarget(content);
} }
function mkView(K) { function mkView(K) {
return function () { return function () {
addContent(K); addContent(K);
};
}
contentDiv = document.createElement("div");
contentDiv.classList.add("content");
document.body.appendChild(contentDiv);
sidebar = new Sidebar(document.body);
contentDiv.appendChild(buttons);
var buttonToggle = document.createElement("button");
buttonToggle.textContent = "\uF133";
buttonToggle.onclick = function () {
if (content.constructor === Map) {
router.view("g");
} else {
router.view("m");
}
}; };
}
contentDiv = document.createElement("div"); buttons.appendChild(buttonToggle);
contentDiv.classList.add("content");
document.body.appendChild(contentDiv);
sidebar = new Sidebar(document.body); var title = new Title(config);
contentDiv.appendChild(buttons); var header = new Container("header");
var infobox = new Infobox(config, sidebar, router);
var tabs = new Tabs();
var overview = new Container();
var meshstats = new Meshstats(config);
var legend = new Legend();
var newnodeslist = new SimpleNodelist("new", "firstseen", router, "Neue Knoten");
var lostnodeslist = new SimpleNodelist("lost", "lastseen", router, "Verschwundene Knoten");
var nodelist = new Nodelist(router);
var linklist = new Linklist(linkScale, router);
var statistics = new Proportions(config, fanout);
var about = new About();
var buttonToggle = document.createElement("button"); fanoutUnfiltered.add(meshstats);
buttonToggle.textContent = "\uF133"; fanoutUnfiltered.add(newnodeslist);
buttonToggle.onclick = function () { fanoutUnfiltered.add(lostnodeslist);
if (content.constructor === Map) fanout.add(nodelist);
router.view("g"); fanout.add(linklist);
else fanout.add(statistics);
router.view("m");
sidebar.add(header);
header.add(meshstats);
header.add(legend);
overview.add(newnodeslist);
overview.add(lostnodeslist);
var filterGUI = new FilterGUI(fanout);
fanout.watchFilters(filterGUI);
header.add(filterGUI);
sidebar.add(tabs);
tabs.add("Aktuelles", overview);
tabs.add("Knoten", nodelist);
tabs.add("Verbindungen", linklist);
tabs.add("Statistiken", statistics);
tabs.add("Über", about);
router.addTarget(title);
router.addTarget(infobox);
router.addView("m", mkView(Map));
router.addView("g", mkView(ForceGraph));
router.view("m");
self.setData = fanoutUnfiltered.setData;
return self;
}; };
});
buttons.appendChild(buttonToggle);
var title = new Title(config);
var header = new Container("header");
var infobox = new Infobox(config, sidebar, router);
var tabs = new Tabs();
var overview = new Container();
var meshstats = new Meshstats(config);
var legend = new Legend();
var newnodeslist = new SimpleNodelist("new", "firstseen", router, "Neue Knoten");
var lostnodeslist = new SimpleNodelist("lost", "lastseen", router, "Verschwundene Knoten");
var nodelist = new Nodelist(router);
var linklist = new Linklist(linkScale, router);
var statistics = new Proportions(config, fanout);
var about = new About();
fanoutUnfiltered.add(meshstats);
fanoutUnfiltered.add(newnodeslist);
fanoutUnfiltered.add(lostnodeslist);
fanout.add(nodelist);
fanout.add(linklist);
fanout.add(statistics);
sidebar.add(header);
header.add(meshstats);
header.add(legend);
overview.add(newnodeslist);
overview.add(lostnodeslist);
var filterGUI = new FilterGUI(fanout);
fanout.watchFilters(filterGUI);
header.add(filterGUI);
sidebar.add(tabs);
tabs.add("Aktuelles", overview);
tabs.add("Knoten", nodelist);
tabs.add("Verbindungen", linklist);
tabs.add("Statistiken", statistics);
tabs.add("Über", about);
router.addTarget(title);
router.addTarget(infobox);
router.addView("m", mkView(Map));
router.addView("g", mkView(ForceGraph));
router.view("m");
self.setData = fanoutUnfiltered.setData;
return self;
};
});

View File

@ -38,7 +38,7 @@ define(function () {
if (config.linkInfos) { if (config.linkInfos) {
var source = d.source.node_id; var source = d.source.node_id;
var target = d.target.node_id; var target = d.target.node_id;
config.linkInfos.forEach( function (linkInfo) { config.linkInfos.forEach(function (linkInfo) {
var h4 = document.createElement("h4"); var h4 = document.createElement("h4");
h4.textContent = linkInfo.name; h4.textContent = linkInfo.name;
el.appendChild(h4); el.appendChild(h4);

View File

@ -5,10 +5,11 @@ define(function () {
el.appendChild(sidebarTitle); el.appendChild(sidebarTitle);
getJSON("https://nominatim.openstreetmap.org/reverse?format=json&lat=" + d.lat + "&lon=" + d.lng + "&zoom=18&addressdetails=0") getJSON("https://nominatim.openstreetmap.org/reverse?format=json&lat=" + d.lat + "&lon=" + d.lng + "&zoom=18&addressdetails=0")
.then(function(result) { .then(function (result) {
if(result.display_name) if (result.display_name) {
sidebarTitle.textContent = result.display_name; sidebarTitle.textContent = result.display_name;
}); }
});
var editLat = document.createElement("input"); var editLat = document.createElement("input");
editLat.type = "text"; editLat.type = "text";
@ -32,7 +33,7 @@ define(function () {
var linkPlain = document.createElement("a"); var linkPlain = document.createElement("a");
linkPlain.textContent = "plain"; linkPlain.textContent = "plain";
linkPlain.onclick = function() { linkPlain.onclick = function () {
switch2plain(); switch2plain();
return false; return false;
}; };
@ -40,7 +41,7 @@ define(function () {
var linkUci = document.createElement("a"); var linkUci = document.createElement("a");
linkUci.textContent = "uci"; linkUci.textContent = "uci";
linkUci.onclick = function() { linkUci.onclick = function () {
switch2uci(); switch2uci();
return false; return false;
}; };
@ -55,7 +56,7 @@ define(function () {
el.appendChild(hintText); el.appendChild(hintText);
function createBox(name, title, inputElem, isVisible) { function createBox(name, title, inputElem, isVisible) {
var visible = typeof isVisible !== "undefined" ? isVisible : true; var visible = typeof isVisible !== "undefined" ? isVisible : true;
var box = document.createElement("div"); var box = document.createElement("div");
var heading = document.createElement("h3"); var heading = document.createElement("h3");
heading.textContent = title; heading.textContent = title;
@ -63,7 +64,9 @@ define(function () {
var btn = document.createElement("button"); var btn = document.createElement("button");
btn.className = "ion-ios-copy"; btn.className = "ion-ios-copy";
btn.title = "Kopieren"; btn.title = "Kopieren";
btn.onclick = function() { copy2clip(inputElem.id); }; btn.onclick = function () {
copy2clip(inputElem.id);
};
inputElem.id = "location-" + name; inputElem.id = "location-" + name;
inputElem.readOnly = true; inputElem.readOnly = true;
var line = document.createElement("p"); var line = document.createElement("p");

View File

@ -1,367 +1,385 @@
define(["moment", "numeral", "tablesort", "tablesort.numeric"], define(["moment", "numeral", "tablesort", "tablesort.numeric"],
function (moment, numeral, Tablesort) { function (moment, numeral, Tablesort) {
function showGeoURI(d) { function showGeoURI(d) {
function showLatitude(d) { function showLatitude(d) {
var suffix = Math.sign(d) > -1 ? "'N" : "'S"; var suffix = Math.sign(d) > -1 ? "'N" : "'S";
d = Math.abs(d); d = Math.abs(d);
var a = Math.floor(d); var a = Math.floor(d);
var min = (d * 60) % 60; var min = (d * 60) % 60;
a = (a < 10 ? "0" : "") + a; a = (a < 10 ? "0" : "") + a;
return a + "° " + numeral(min).format("0.000") + suffix; return a + "° " + numeral(min).format("0.000") + suffix;
}
function showLongitude(d) {
var suffix = Math.sign(d) > -1 ? "'E" : "'W";
d = Math.abs(d);
var a = Math.floor(d);
var min = (d * 60) % 60;
a = (a < 100 ? "0" + (a < 10 ? "0" : "") : "") + a;
return a + "° " + numeral(min).format("0.000") + suffix;
}
if (!has_location(d)) {
return undefined;
}
return function (el) {
var latitude = d.nodeinfo.location.latitude;
var longitude = d.nodeinfo.location.longitude;
var a = document.createElement("a");
a.textContent = showLatitude(latitude) + " " +
showLongitude(longitude);
a.href = "geo:" + latitude + "," + longitude;
el.appendChild(a);
};
} }
function showLongitude(d) { function showStatus(d) {
var suffix = Math.sign(d) > -1 ? "'E" : "'W"; return function (el) {
d = Math.abs(d); el.classList.add(d.flags.unseen ? "unseen" : (d.flags.online ? "online" : "offline"));
var a = Math.floor(d); if (d.flags.online) {
var min = (d * 60) % 60; el.textContent = "online, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")";
a = (a < 100 ? "0" + (a < 10 ? "0" : "") : "") + a; } else {
el.textContent = "offline, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")";
return a + "° " + numeral(min).format("0.000") + suffix; }
};
} }
if (!has_location(d)) function showFirmware(d) {
return undefined; var release = dictGet(d.nodeinfo, ["software", "firmware", "release"]);
var base = dictGet(d.nodeinfo, ["software", "firmware", "base"]);
return function (el) { if (release === null || base === null) {
var latitude = d.nodeinfo.location.latitude; return undefined;
var longitude = d.nodeinfo.location.longitude; }
var a = document.createElement("a");
a.textContent = showLatitude(latitude) + " " +
showLongitude(longitude);
a.href = "geo:" + latitude + "," + longitude; return release + " / " + base;
el.appendChild(a); }
};
}
function showStatus(d) { function showSite(d, config) {
return function (el) { var site = dictGet(d.nodeinfo, ["system", "site_code"]);
el.classList.add(d.flags.unseen ? "unseen" : (d.flags.online ? "online" : "offline")); var rt = site;
if (d.flags.online) if (config.siteNames) {
el.textContent = "online, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")"; config.siteNames.forEach(function (t) {
else if (site === t.site) {
el.textContent = "offline, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")"; rt = t.name;
}; }
} });
}
return rt;
}
function showFirmware(d) { function showUptime(d) {
var release = dictGet(d.nodeinfo, ["software", "firmware", "release"]); if (!("uptime" in d.statistics)) {
var base = dictGet(d.nodeinfo, ["software", "firmware", "base"]); return undefined;
}
if (release === null || base === null) return moment.duration(d.statistics.uptime, "seconds").humanize();
return undefined; }
return release + " / " + base; function showFirstseen(d) {
} if (!("firstseen" in d)) {
return undefined;
}
function showSite(d, config) { return d.firstseen.fromNow(true);
var site = dictGet(d.nodeinfo, ["system", "site_code"]); }
var rt = site;
if (config.siteNames)
config.siteNames.forEach( function (t) {
if(site === t.site)
rt = t.name;
});
return rt;
}
function showUptime(d) { function showClients(d) {
if (!("uptime" in d.statistics)) if (!d.flags.online) {
return undefined; return undefined;
}
return moment.duration(d.statistics.uptime, "seconds").humanize(); return function (el) {
} el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine"));
el.appendChild(document.createElement("br"));
function showFirstseen(d) { var span = document.createElement("span");
if (!("firstseen" in d)) span.classList.add("clients");
return undefined; span.textContent = " ".repeat(d.statistics.clients);
el.appendChild(span);
};
}
return d.firstseen.fromNow(true); function showIPs(d) {
} var ips = dictGet(d.nodeinfo, ["network", "addresses"]);
if (ips === null) {
return undefined;
}
function showClients(d) { ips.sort();
if (!d.flags.online)
return undefined;
return function (el) { return function (el) {
el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine")); ips.forEach(function (ip, i) {
el.appendChild(document.createElement("br")); var link = !ip.startsWith("fe80:");
if (i > 0) {
el.appendChild(document.createElement("br"));
}
if (link) {
var a = document.createElement("a");
a.href = "http://[" + ip + "]/";
a.textContent = ip;
el.appendChild(a);
} else {
el.appendChild(document.createTextNode(ip));
}
});
};
}
function showBar(className, v) {
var span = document.createElement("span"); var span = document.createElement("span");
span.classList.add("clients"); span.classList.add("bar");
span.textContent = " ".repeat(d.statistics.clients); span.classList.add(className);
el.appendChild(span);
};
}
function showIPs(d) { var bar = document.createElement("span");
var ips = dictGet(d.nodeinfo, ["network", "addresses"]);
if (ips === null)
return undefined;
ips.sort();
return function (el) {
ips.forEach( function (ip, i) {
var link = !ip.startsWith("fe80:");
if (i > 0)
el.appendChild(document.createElement("br"));
if (link) {
var a = document.createElement("a");
a.href = "http://[" + ip + "]/";
a.textContent = ip;
el.appendChild(a);
} else
el.appendChild(document.createTextNode(ip));
});
};
}
function showBar(className, v) {
var span = document.createElement("span");
span.classList.add("bar");
span.classList.add(className);
var bar = document.createElement("span");
bar.style.width = (v * 100) + "%";
span.appendChild(bar);
var label = document.createElement("label");
label.textContent = (Math.round(v * 100)) + " %";
span.appendChild(label);
return span;
}
function showLoadBar(className, v) {
var span = document.createElement("span");
span.classList.add("bar");
span.classList.add(className);
var bar = document.createElement("span");
if (v >= 1) {
bar.style.width = ((v * 100) % 100) + "%";
bar.style.background = "rgba(255, 50, 50, 0.9)";
span.style.background = "rgba(255, 50, 50, 0.6)";
span.appendChild(bar);
}
else
{
bar.style.width = (v * 100) + "%"; bar.style.width = (v * 100) + "%";
span.appendChild(bar); span.appendChild(bar);
var label = document.createElement("label");
label.textContent = (Math.round(v * 100)) + " %";
span.appendChild(label);
return span;
} }
var label = document.createElement("label"); function showLoadBar(className, v) {
label.textContent = (v); var span = document.createElement("span");
span.appendChild(label); span.classList.add("bar");
span.classList.add(className);
return span; var bar = document.createElement("span");
} if (v >= 1) {
bar.style.width = ((v * 100) % 100) + "%";
bar.style.background = "rgba(255, 50, 50, 0.9)";
span.style.background = "rgba(255, 50, 50, 0.6)";
span.appendChild(bar);
}
else {
bar.style.width = (v * 100) + "%";
span.appendChild(bar);
}
function showLoad(d) { var label = document.createElement("label");
if (!("loadavg" in d.statistics)) label.textContent = (v);
return undefined; span.appendChild(label);
return function (el) { return span;
el.appendChild(showLoadBar("load-avg", d.statistics.loadavg)); }
};
}
function showRAM(d) { function showLoad(d) {
if (!("memory_usage" in d.statistics)) if (!("loadavg" in d.statistics)) {
return undefined; return undefined;
}
return function (el) { return function (el) {
el.appendChild(showBar("memory-usage", d.statistics.memory_usage)); el.appendChild(showLoadBar("load-avg", d.statistics.loadavg));
}; };
} }
function showPages(d) { function showRAM(d) {
var webpages = dictGet(d.nodeinfo, ["pages"]); if (!("memory_usage" in d.statistics)) {
if (webpages === null) return undefined;
return undefined; }
webpages.sort(); return function (el) {
el.appendChild(showBar("memory-usage", d.statistics.memory_usage));
};
}
return function (el) { function showPages(d) {
webpages.forEach( function (webpage, i) { var webpages = dictGet(d.nodeinfo, ["pages"]);
if (i > 0) if (webpages === null) {
el.appendChild(document.createElement("br")); return undefined;
}
var a = document.createElement("span"); webpages.sort();
var link = document.createElement("a");
link.href = webpage; return function (el) {
if (webpage.search(/^https:\/\//i) !== -1) { webpages.forEach(function (webpage, i) {
var lock = document.createElement("span"); if (i > 0) {
lock.className = "ion-android-lock"; el.appendChild(document.createElement("br"));
a.appendChild(lock);
var t1 = document.createTextNode(" ");
a.appendChild(t1);
link.textContent = webpage.replace(/^https:\/\//i, "");
} }
else
link.textContent = webpage.replace(/^http:\/\//i, "");
a.appendChild(link);
el.appendChild(a);
});
};
}
function showAutoupdate(d) { var a = document.createElement("span");
var au = dictGet(d.nodeinfo, ["software", "autoupdater"]); var link = document.createElement("a");
if (!au) link.href = webpage;
return undefined; if (webpage.search(/^https:\/\//i) !== -1) {
var lock = document.createElement("span");
return au.enabled ? "aktiviert (" + au.branch + ")" : "deaktiviert"; lock.className = "ion-android-lock";
} a.appendChild(lock);
var t1 = document.createTextNode(" ");
function showStatImg(o, d) { a.appendChild(t1);
var subst = {}; link.textContent = webpage.replace(/^https:\/\//i, "");
subst["{NODE_ID}"] = d.nodeinfo.node_id ? d.nodeinfo.node_id : "unknown"; }
subst["{NODE_NAME}"] = d.nodeinfo.hostname ? d.nodeinfo.hostname : "unknown"; else {
return showStat(o, subst); link.textContent = webpage.replace(/^http:\/\//i, "");
} }
a.appendChild(link);
return function(config, el, router, d) { el.appendChild(a);
var h2 = document.createElement("h2"); });
h2.textContent = d.nodeinfo.hostname; };
el.appendChild(h2);
var attributes = document.createElement("table");
attributes.classList.add("attributes");
attributeEntry(attributes, "Status", showStatus(d));
attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null);
attributeEntry(attributes, "Koordinaten", showGeoURI(d));
if (config.showContact)
attributeEntry(attributes, "Kontakt", dictGet(d.nodeinfo, ["owner", "contact"]));
attributeEntry(attributes, "Hardware", dictGet(d.nodeinfo, ["hardware", "model"]));
attributeEntry(attributes, "Primäre MAC", dictGet(d.nodeinfo, ["network", "mac"]));
attributeEntry(attributes, "Node ID", dictGet(d.nodeinfo, ["node_id"]));
attributeEntry(attributes, "Firmware", showFirmware(d));
attributeEntry(attributes, "Site", showSite(d, config));
attributeEntry(attributes, "Uptime", showUptime(d));
attributeEntry(attributes, "Teil des Netzes", showFirstseen(d));
attributeEntry(attributes, "Systemlast", showLoad(d));
attributeEntry(attributes, "Arbeitsspeicher", showRAM(d));
attributeEntry(attributes, "IP Adressen", showIPs(d));
attributeEntry(attributes, "Webseite", showPages(d));
attributeEntry(attributes, "Gewähltes Gateway", dictGet(d.statistics, ["gateway"]));
attributeEntry(attributes, "Autom. Updates", showAutoupdate(d));
attributeEntry(attributes, "Clients", showClients(d));
el.appendChild(attributes);
if (config.nodeInfos)
config.nodeInfos.forEach( function (nodeInfo) {
var h4 = document.createElement("h4");
h4.textContent = nodeInfo.name;
el.appendChild(h4);
el.appendChild(showStatImg(nodeInfo, d));
});
if (d.neighbours.length > 0) {
var h3 = document.createElement("h3");
h3.textContent = "Links (" + d.neighbours.length + ")";
el.appendChild(h3);
var table = document.createElement("table");
var thead = document.createElement("thead");
var tr = document.createElement("tr");
var th1 = document.createElement("th");
th1.textContent = " ";
tr.appendChild(th1);
var th2 = document.createElement("th");
th2.textContent = "Knoten";
th2.classList.add("sort-default");
tr.appendChild(th2);
var th3 = document.createElement("th");
th3.textContent = "TQ";
tr.appendChild(th3);
var th4 = document.createElement("th");
th4.textContent = "Typ";
tr.appendChild(th4);
var th5 = document.createElement("th");
th5.textContent = "Entfernung";
tr.appendChild(th5);
thead.appendChild(tr);
table.appendChild(thead);
var tbody = document.createElement("tbody");
d.neighbours.forEach( function (d) {
var unknown = !(d.node);
var tr = document.createElement("tr");
var td1 = document.createElement("td");
td1.appendChild(document.createTextNode(d.incoming ? " ← " : " → "));
tr.appendChild(td1);
var td2 = document.createElement("td");
var a1 = document.createElement("a");
a1.classList.add("hostname");
a1.textContent = unknown ? d.id : d.node.nodeinfo.hostname;
if (!unknown)
a1.href = "#";
a1.onclick = router.node(d.node);
td2.appendChild(a1);
if (!unknown && has_location(d.node)) {
var span = document.createElement("span");
span.classList.add("icon");
span.classList.add("ion-location");
td2.appendChild(span);
}
tr.appendChild(td2);
var td3 = document.createElement("td");
var a2 = document.createElement("a");
a2.href = "#";
a2.textContent = showTq(d.link);
a2.onclick = router.link(d.link);
td3.appendChild(a2);
tr.appendChild(td3);
var td4 = document.createElement("td");
var a3 = document.createElement("a");
a3.href = "#";
a3.textContent = d.link.type;
a3.onclick = router.link(d.link);
td4.appendChild(a3);
tr.appendChild(td4);
var td5 = document.createElement("td");
var a4 = document.createElement("a");
a4.href = "#";
a4.textContent = showDistance(d.link);
a4.onclick = router.link(d.link);
td5.appendChild(a4);
td5.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1);
tr.appendChild(td5);
tbody.appendChild(tr);
});
table.appendChild(tbody);
table.className = "node-links";
new Tablesort(table);
el.appendChild(table);
} }
};
}); function showAutoupdate(d) {
var au = dictGet(d.nodeinfo, ["software", "autoupdater"]);
if (!au) {
return undefined;
}
return au.enabled ? "aktiviert (" + au.branch + ")" : "deaktiviert";
}
function showStatImg(o, d) {
var subst = {};
subst["{NODE_ID}"] = d.nodeinfo.node_id ? d.nodeinfo.node_id : "unknown";
subst["{NODE_NAME}"] = d.nodeinfo.hostname ? d.nodeinfo.hostname : "unknown";
return showStat(o, subst);
}
return function (config, el, router, d) {
var h2 = document.createElement("h2");
h2.textContent = d.nodeinfo.hostname;
el.appendChild(h2);
var attributes = document.createElement("table");
attributes.classList.add("attributes");
attributeEntry(attributes, "Status", showStatus(d));
attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null);
attributeEntry(attributes, "Koordinaten", showGeoURI(d));
if (config.showContact) {
attributeEntry(attributes, "Kontakt", dictGet(d.nodeinfo, ["owner", "contact"]));
}
attributeEntry(attributes, "Hardware", dictGet(d.nodeinfo, ["hardware", "model"]));
attributeEntry(attributes, "Primäre MAC", dictGet(d.nodeinfo, ["network", "mac"]));
attributeEntry(attributes, "Node ID", dictGet(d.nodeinfo, ["node_id"]));
attributeEntry(attributes, "Firmware", showFirmware(d));
attributeEntry(attributes, "Site", showSite(d, config));
attributeEntry(attributes, "Uptime", showUptime(d));
attributeEntry(attributes, "Teil des Netzes", showFirstseen(d));
attributeEntry(attributes, "Systemlast", showLoad(d));
attributeEntry(attributes, "Arbeitsspeicher", showRAM(d));
attributeEntry(attributes, "IP Adressen", showIPs(d));
attributeEntry(attributes, "Webseite", showPages(d));
attributeEntry(attributes, "Gewähltes Gateway", dictGet(d.statistics, ["gateway"]));
attributeEntry(attributes, "Autom. Updates", showAutoupdate(d));
attributeEntry(attributes, "Clients", showClients(d));
el.appendChild(attributes);
if (config.nodeInfos) {
config.nodeInfos.forEach(function (nodeInfo) {
var h4 = document.createElement("h4");
h4.textContent = nodeInfo.name;
el.appendChild(h4);
el.appendChild(showStatImg(nodeInfo, d));
});
}
if (d.neighbours.length > 0) {
var h3 = document.createElement("h3");
h3.textContent = "Links (" + d.neighbours.length + ")";
el.appendChild(h3);
var table = document.createElement("table");
var thead = document.createElement("thead");
var tr = document.createElement("tr");
var th1 = document.createElement("th");
th1.textContent = " ";
tr.appendChild(th1);
var th2 = document.createElement("th");
th2.textContent = "Knoten";
th2.classList.add("sort-default");
tr.appendChild(th2);
var th3 = document.createElement("th");
th3.textContent = "TQ";
tr.appendChild(th3);
var th4 = document.createElement("th");
th4.textContent = "Typ";
tr.appendChild(th4);
var th5 = document.createElement("th");
th5.textContent = "Entfernung";
tr.appendChild(th5);
thead.appendChild(tr);
table.appendChild(thead);
var tbody = document.createElement("tbody");
d.neighbours.forEach(function (d) {
var unknown = !(d.node);
var tr = document.createElement("tr");
var td1 = document.createElement("td");
td1.appendChild(document.createTextNode(d.incoming ? " ← " : " → "));
tr.appendChild(td1);
var td2 = document.createElement("td");
var a1 = document.createElement("a");
a1.classList.add("hostname");
a1.textContent = unknown ? d.id : d.node.nodeinfo.hostname;
if (!unknown) {
a1.href = "#";
}
a1.onclick = router.node(d.node);
td2.appendChild(a1);
if (!unknown && has_location(d.node)) {
var span = document.createElement("span");
span.classList.add("icon");
span.classList.add("ion-location");
td2.appendChild(span);
}
tr.appendChild(td2);
var td3 = document.createElement("td");
var a2 = document.createElement("a");
a2.href = "#";
a2.textContent = showTq(d.link);
a2.onclick = router.link(d.link);
td3.appendChild(a2);
tr.appendChild(td3);
var td4 = document.createElement("td");
var a3 = document.createElement("a");
a3.href = "#";
a3.textContent = d.link.type;
a3.onclick = router.link(d.link);
td4.appendChild(a3);
tr.appendChild(td4);
var td5 = document.createElement("td");
var a4 = document.createElement("a");
a4.href = "#";
a4.textContent = showDistance(d.link);
a4.onclick = router.link(d.link);
td5.appendChild(a4);
td5.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1);
tr.appendChild(td5);
tbody.appendChild(tr);
});
table.appendChild(tbody);
table.className = "node-links";
new Tablesort(table);
el.appendChild(table);
}
};
});

View File

@ -3,31 +3,37 @@ define(["sorttable", "virtual-dom"], function (SortTable, V) {
return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " " + d.target.node.nodeinfo.hostname; return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " " + d.target.node.nodeinfo.hostname;
} }
var headings = [{ name: "Knoten", var headings = [{
sort: function (a, b) { name: "Knoten",
return linkName(a).localeCompare(linkName(b)); sort: function (a, b) {
}, return linkName(a).localeCompare(linkName(b));
reverse: false },
}, reverse: false
{ name: "TQ", },
sort: function (a, b) { return a.tq - b.tq; }, {
reverse: true name: "TQ",
}, sort: function (a, b) {
{ name: "Typ", return a.tq - b.tq;
sort: function (a, b) { },
return a.type.localeCompare(b.type); reverse: true
}, },
reverse: false {
}, name: "Typ",
{ name: "Entfernung", sort: function (a, b) {
sort: function (a, b) { return a.type.localeCompare(b.type);
return (a.distance === undefined ? -1 : a.distance) - },
(b.distance === undefined ? -1 : b.distance); reverse: false
}, },
reverse: true {
}]; name: "Entfernung",
sort: function (a, b) {
return (a.distance === undefined ? -1 : a.distance) -
(b.distance === undefined ? -1 : b.distance);
},
reverse: true
}];
return function(linkScale, router) { return function (linkScale, router) {
var table = new SortTable(headings, 2, renderRow); var table = new SortTable(headings, 2, renderRow);
function renderRow(d) { function renderRow(d) {
@ -41,7 +47,7 @@ define(["sorttable", "virtual-dom"], function (SortTable, V) {
return V.h("tr", [td1, td2, td3, td4]); return V.h("tr", [td1, td2, td3, td4]);
} }
this.render = function (d) { this.render = function (d) {
var el = document.createElement("div"); var el = document.createElement("div");
el.last = V.h("div"); el.last = V.h("div");
d.appendChild(el); d.appendChild(el);

View File

@ -29,28 +29,28 @@ define(["leaflet"], function (L) {
fillOpacity: 0.2 fillOpacity: 0.2
}, },
initialize: function(latlng) { initialize: function (latlng) {
this.accuracyCircle = L.circle(latlng, 0, this.accuracyCircle); this.accuracyCircle = L.circle(latlng, 0, this.accuracyCircle);
this.outerCircle = L.circleMarker(latlng, this.outerCircle); this.outerCircle = L.circleMarker(latlng, this.outerCircle);
L.CircleMarker.prototype.initialize.call(this, latlng, this.innerCircle); L.CircleMarker.prototype.initialize.call(this, latlng, this.innerCircle);
this.on("remove", function() { this.on("remove", function () {
this._map.removeLayer(this.accuracyCircle); this._map.removeLayer(this.accuracyCircle);
this._map.removeLayer(this.outerCircle); this._map.removeLayer(this.outerCircle);
}); });
}, },
setLatLng: function(latlng) { setLatLng: function (latlng) {
this.accuracyCircle.setLatLng(latlng); this.accuracyCircle.setLatLng(latlng);
this.outerCircle.setLatLng(latlng); this.outerCircle.setLatLng(latlng);
L.CircleMarker.prototype.setLatLng.call(this, latlng); L.CircleMarker.prototype.setLatLng.call(this, latlng);
}, },
setAccuracy: function(accuracy) { setAccuracy: function (accuracy) {
this.accuracyCircle.setRadius(accuracy); this.accuracyCircle.setRadius(accuracy);
}, },
onAdd: function(map) { onAdd: function (map) {
this.accuracyCircle.addTo(map).bringToBack(); this.accuracyCircle.addTo(map).bringToBack();
this.outerCircle.addTo(map); this.outerCircle.addTo(map);
L.CircleMarker.prototype.onAdd.call(this, map); L.CircleMarker.prototype.onAdd.call(this, map);

View File

@ -1,195 +1,198 @@
define(["moment", "router", "leaflet", "gui", "numeral"], define(["moment", "router", "leaflet", "gui", "numeral"],
function (moment, Router, L, GUI, numeral) { function (moment, Router, L, GUI, numeral) {
return function (config) { return function (config) {
function handleData(data) { function handleData(data) {
var dataNodes = {}; var dataNodes = {};
dataNodes.nodes = []; dataNodes.nodes = [];
var dataGraph = {}; var dataGraph = {};
dataGraph.batadv = {}; dataGraph.batadv = {};
dataGraph.batadv.nodes = []; dataGraph.batadv.nodes = [];
dataGraph.batadv.links = []; dataGraph.batadv.links = [];
function rearrangeLinks(d) {
d.source += dataGraph.batadv.nodes.length;
d.target += dataGraph.batadv.nodes.length;
}
function rearrangeLinks(d) { for (var i = 0; i < data.length; ++i) {
d.source += dataGraph.batadv.nodes.length; var vererr;
d.target += dataGraph.batadv.nodes.length; if (i % 2) {
} if (data[i].version !== 1) {
vererr = "Unsupported graph version: " + data[i].version;
for (var i = 0; i < data.length; ++i) { console.log(vererr); //silent fail
var vererr; } else {
if(i % 2) data[i].batadv.links.forEach(rearrangeLinks);
if (data[i].version !== 1) { dataGraph.batadv.nodes = dataGraph.batadv.nodes.concat(data[i].batadv.nodes);
vererr = "Unsupported graph version: " + data[i].version; dataGraph.batadv.links = dataGraph.batadv.links.concat(data[i].batadv.links);
console.log(vererr); //silent fail dataGraph.timestamp = data[i].timestamp;
} else { }
data[i].batadv.links.forEach(rearrangeLinks); } else if (data[i].version !== 2) {
dataGraph.batadv.nodes = dataGraph.batadv.nodes.concat(data[i].batadv.nodes);
dataGraph.batadv.links = dataGraph.batadv.links.concat(data[i].batadv.links);
dataGraph.timestamp = data[i].timestamp;
}
else
if (data[i].version !== 2) {
vererr = "Unsupported nodes version: " + data[i].version; vererr = "Unsupported nodes version: " + data[i].version;
console.log(vererr); //silent fail console.log(vererr); //silent fail
} else { } else {
dataNodes.nodes = dataNodes.nodes.concat(data[i].nodes); dataNodes.nodes = dataNodes.nodes.concat(data[i].nodes);
dataNodes.timestamp = data[i].timestamp; dataNodes.timestamp = data[i].timestamp;
} }
}
var nodes = dataNodes.nodes.filter( function (d) {
return "firstseen" in d && "lastseen" in d;
});
nodes.forEach( function(node) {
node.firstseen = moment.utc(node.firstseen).local();
node.lastseen = moment.utc(node.lastseen).local();
});
var now = moment();
var age = moment(now).subtract(config.maxAge, "days");
var newnodes = limit("firstseen", age, sortByKey("firstseen", nodes).filter(online));
var lostnodes = limit("lastseen", age, sortByKey("lastseen", nodes).filter(offline));
var graphnodes = {};
dataNodes.nodes.forEach( function (d) {
graphnodes[d.nodeinfo.node_id] = d;
});
var graph = dataGraph.batadv;
graph.nodes.forEach( function (d) {
if (d.node_id in graphnodes) {
d.node = graphnodes[d.node_id];
if (d.unseen) {
d.node.flags.online = true;
d.node.flags.unseen = true;
}
} }
});
graph.links.forEach( function (d) { var nodes = dataNodes.nodes.filter(function (d) {
d.source = graph.nodes[d.source]; return "firstseen" in d && "lastseen" in d;
});
if (graph.nodes[d.target].node) nodes.forEach(function (node) {
d.target = graph.nodes[d.target]; node.firstseen = moment.utc(node.firstseen).local();
else node.lastseen = moment.utc(node.lastseen).local();
d.target = undefined; });
});
var links = graph.links.filter( function (d) { var now = moment();
return d.target !== undefined; var age = moment(now).subtract(config.maxAge, "days");
});
links.forEach( function (d) { var newnodes = limit("firstseen", age, sortByKey("firstseen", nodes).filter(online));
var unknown = (d.source.node === undefined); var lostnodes = limit("lastseen", age, sortByKey("lastseen", nodes).filter(offline));
var ids;
if (unknown)
ids = [d.source.id.replace(/:/g, ""), d.target.node.nodeinfo.node_id];
else
ids = [d.source.node.nodeinfo.node_id, d.target.node.nodeinfo.node_id];
d.id = ids.join("-");
if (unknown || var graphnodes = {};
!d.source.node.nodeinfo.location ||
!d.target.node.nodeinfo.location || dataNodes.nodes.forEach(function (d) {
graphnodes[d.nodeinfo.node_id] = d;
});
var graph = dataGraph.batadv;
graph.nodes.forEach(function (d) {
if (d.node_id in graphnodes) {
d.node = graphnodes[d.node_id];
if (d.unseen) {
d.node.flags.online = true;
d.node.flags.unseen = true;
}
}
});
graph.links.forEach(function (d) {
d.source = graph.nodes[d.source];
if (graph.nodes[d.target].node) {
d.target = graph.nodes[d.target];
} else {
d.target = undefined;
}
});
var links = graph.links.filter(function (d) {
return d.target !== undefined;
});
links.forEach(function (d) {
var unknown = (d.source.node === undefined);
var ids;
if (unknown) {
ids = [d.source.id.replace(/:/g, ""), d.target.node.nodeinfo.node_id];
} else {
ids = [d.source.node.nodeinfo.node_id, d.target.node.nodeinfo.node_id];
}
d.id = ids.join("-");
if (unknown || !d.source.node.nodeinfo.location || !d.target.node.nodeinfo.location ||
isNaN(d.source.node.nodeinfo.location.latitude) || isNaN(d.source.node.nodeinfo.location.latitude) ||
isNaN(d.source.node.nodeinfo.location.longitude) || isNaN(d.source.node.nodeinfo.location.longitude) ||
isNaN(d.target.node.nodeinfo.location.latitude) || isNaN(d.target.node.nodeinfo.location.latitude) ||
isNaN(d.target.node.nodeinfo.location.longitude)) isNaN(d.target.node.nodeinfo.location.longitude)) {
return; return;
}
d.latlngs = []; d.latlngs = [];
d.latlngs.push(L.latLng(d.source.node.nodeinfo.location.latitude, d.source.node.nodeinfo.location.longitude)); d.latlngs.push(L.latLng(d.source.node.nodeinfo.location.latitude, d.source.node.nodeinfo.location.longitude));
d.latlngs.push(L.latLng(d.target.node.nodeinfo.location.latitude, d.target.node.nodeinfo.location.longitude)); d.latlngs.push(L.latLng(d.target.node.nodeinfo.location.latitude, d.target.node.nodeinfo.location.longitude));
d.distance = d.latlngs[0].distanceTo(d.latlngs[1]); d.distance = d.latlngs[0].distanceTo(d.latlngs[1]);
}); });
nodes.forEach( function (d) { nodes.forEach(function (d) {
d.neighbours = []; d.neighbours = [];
}); });
links.forEach( function (d) { links.forEach(function (d) {
if (d.type === "tunnel" || d.type === "fastd") if (d.type === "tunnel" || d.type === "fastd") {
d.type = "fastd"; d.type = "fastd";
else if (d.type === "l2tp") { } else if (d.type === "l2tp") {
d.type = "L2TP"; d.type = "L2TP";
d.target.node.flags.uplink = true; d.target.node.flags.uplink = true;
} else if (d.type === "wireless") } else if (d.type === "wireless") {
d.type = "Wifi"; d.type = "Wifi";
else if (d.type === "other") } else if (d.type === "other") {
d.type = "Kabel"; d.type = "Kabel";
else } else {
d.type = "N/A"; d.type = "N/A";
var unknown = (d.source.node === undefined); }
if (unknown) { var unknown = (d.source.node === undefined);
d.target.node.neighbours.push({ id: d.source.id, link: d, incoming: true }); if (unknown) {
return; d.target.node.neighbours.push({id: d.source.id, link: d, incoming: true});
} return;
d.source.node.neighbours.push({ node: d.target.node, link: d, incoming: false }); }
d.target.node.neighbours.push({ node: d.source.node, link: d, incoming: true }); d.source.node.neighbours.push({node: d.target.node, link: d, incoming: false});
if (d.type !== "fastd" && d.type !== "L2TP") d.target.node.neighbours.push({node: d.source.node, link: d, incoming: true});
d.source.node.meshlinks = d.source.node.meshlinks ? d.source.node.meshlinks + 1 : 1; if (d.type !== "fastd" && d.type !== "L2TP") {
}); d.source.node.meshlinks = d.source.node.meshlinks ? d.source.node.meshlinks + 1 : 1;
}
});
links.sort( function (a, b) { links.sort(function (a, b) {
return b.tq - a.tq; return b.tq - a.tq;
}); });
return { now: now, return {
timestamp: moment.utc(dataNodes.timestamp).local(), now: now,
nodes: { timestamp: moment.utc(dataNodes.timestamp).local(),
all: nodes, nodes: {
new: newnodes, all: nodes,
lost: lostnodes new: newnodes,
}, lost: lostnodes
graph: { },
links: links, graph: {
nodes: graph.nodes links: links,
} nodes: graph.nodes
}; }
} };
}
numeral.language("de"); numeral.language("de");
moment.locale("de"); moment.locale("de");
var router = new Router(); var router = new Router();
var urls = []; var urls = [];
if (typeof config.dataPath === "string" || config.dataPath instanceof String) if (typeof config.dataPath === "string" || config.dataPath instanceof String) {
config.dataPath = [config.dataPath]; config.dataPath = [config.dataPath];
}
for (var i in config.dataPath) { for (var i in config.dataPath) {
urls.push(config.dataPath[i] + "nodes.json"); urls.push(config.dataPath[i] + "nodes.json");
urls.push(config.dataPath[i] + "graph.json"); urls.push(config.dataPath[i] + "graph.json");
} }
function update() { function update() {
return Promise.all(urls.map(getJSON)) return Promise.all(urls.map(getJSON))
.then(handleData); .then(handleData);
} }
update() update()
.then(function (d) { .then(function (d) {
var gui = new GUI(config, router); var gui = new GUI(config, router);
gui.setData(d); gui.setData(d);
router.setData(d); router.setData(d);
router.start(); router.start();
window.setInterval(function () { window.setInterval(function () {
update().then(function (d) { update().then(function (d) {
gui.setData(d); gui.setData(d);
router.setData(d); router.setData(d);
}); });
}, 60000); }, 60000);
}) })
.catch(function (e) { .catch(function (e) {
document.body.textContent = e; document.body.textContent = e;
console.log(e); console.log(e);
}); });
}; };
}); });

View File

@ -1,73 +1,74 @@
define(["map/clientlayer", "map/labelslayer", define(["map/clientlayer", "map/labelslayer",
"d3", "leaflet", "moment", "locationmarker", "rbush", "d3", "leaflet", "moment", "locationmarker", "rbush",
"leaflet.label", "leaflet.providers"], "leaflet.label", "leaflet.providers"],
function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush) { function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush) {
var options = { worldCopyJump: true, var options = {
zoomControl: false worldCopyJump: true,
}; zoomControl: false
};
var AddLayerButton = L.Control.extend({ var AddLayerButton = L.Control.extend({
options: { options: {
position: "bottomright" position: "bottomright"
}, },
initialize: function (f, options) { initialize: function (f, options) {
L.Util.setOptions(this, options); L.Util.setOptions(this, options);
this.f = f; this.f = f;
}, },
onAdd: function () { onAdd: function () {
var button = L.DomUtil.create("button", "add-layer"); var button = L.DomUtil.create("button", "add-layer");
button.textContent = "\uF2C7"; button.textContent = "\uF2C7";
// L.DomEvent.disableClickPropagation(button) // L.DomEvent.disableClickPropagation(button)
// Click propagation isn't disabled as this causes problems with the // Click propagation isn't disabled as this causes problems with the
// location picking mode; instead propagation is stopped in onClick(). // location picking mode; instead propagation is stopped in onClick().
L.DomEvent.addListener(button, "click", this.f, this); L.DomEvent.addListener(button, "click", this.f, this);
this.button = button; this.button = button;
return button; return button;
} }
}); });
var LocateButton = L.Control.extend({ var LocateButton = L.Control.extend({
options: { options: {
position: "bottomright" position: "bottomright"
}, },
active: false, active: false,
button: undefined, button: undefined,
initialize: function (f, options) { initialize: function (f, options) {
L.Util.setOptions(this, options); L.Util.setOptions(this, options);
this.f = f; this.f = f;
}, },
onAdd: function () { onAdd: function () {
var button = L.DomUtil.create("button", "locate-user"); var button = L.DomUtil.create("button", "locate-user");
button.textContent = "\uF2E9"; button.textContent = "\uF2E9";
L.DomEvent.disableClickPropagation(button); L.DomEvent.disableClickPropagation(button);
L.DomEvent.addListener(button, "click", this.onClick, this); L.DomEvent.addListener(button, "click", this.onClick, this);
this.button = button; this.button = button;
return button; return button;
}, },
update: function() { update: function () {
this.button.classList.toggle("active", this.active); this.button.classList.toggle("active", this.active);
}, },
set: function(v) { set: function (v) {
this.active = v; this.active = v;
this.update(); this.update();
}, },
onClick: function () { onClick: function () {
this.f(!this.active); this.f(!this.active);
} }
}); });
var CoordsPickerButton = L.Control.extend({ var CoordsPickerButton = L.Control.extend({
@ -96,11 +97,11 @@ define(["map/clientlayer", "map/labelslayer",
return button; return button;
}, },
update: function() { update: function () {
this.button.classList.toggle("active", this.active); this.button.classList.toggle("active", this.active);
}, },
set: function(v) { set: function (v) {
this.active = v; this.active = v;
this.update(); this.update();
}, },
@ -130,16 +131,17 @@ define(["map/clientlayer", "map/labelslayer",
} }
function addLinksToMap(dict, linkScale, graph, router) { function addLinksToMap(dict, linkScale, graph, router) {
graph = graph.filter( function (d) { graph = graph.filter(function (d) {
return "distance" in d && d.type !== "VPN"; return "distance" in d && d.type !== "VPN";
}); });
var lines = graph.map( function (d) { var lines = graph.map(function (d) {
var opts = { color: d.type === "Kabel" ? "#50B0F0" : linkScale(d.tq).hex(), var opts = {
weight: 4, color: d.type === "Kabel" ? "#50B0F0" : linkScale(d.tq).hex(),
opacity: 0.5, weight: 4,
dashArray: "none" opacity: 0.5,
}; dashArray: "none"
};
var line = L.polyline(d.latlngs, opts); var line = L.polyline(d.latlngs, opts);
@ -158,11 +160,11 @@ define(["map/clientlayer", "map/labelslayer",
return lines; return lines;
} }
var iconOnline = { color: "#1566A9", fillColor: "#1566A9", radius: 6, fillOpacity: 0.5, opacity: 0.5, weight: 2, className: "stroke-first" }; var iconOnline = {color: "#1566A9", fillColor: "#1566A9", radius: 6, fillOpacity: 0.5, opacity: 0.5, weight: 2, className: "stroke-first"};
var iconOffline = { color: "#D43E2A", fillColor: "#D43E2A", radius: 3, fillOpacity: 0.5, opacity: 0.5, weight: 1, className: "stroke-first" }; var iconOffline = {color: "#D43E2A", fillColor: "#D43E2A", radius: 3, fillOpacity: 0.5, opacity: 0.5, weight: 1, className: "stroke-first"};
var iconLost = { color: "#D43E2A", fillColor: "#D43E2A", radius: 6, fillOpacity: 0.8, opacity: 0.8, weight: 1, className: "stroke-first" }; var iconLost = {color: "#D43E2A", fillColor: "#D43E2A", radius: 6, fillOpacity: 0.8, opacity: 0.8, weight: 1, className: "stroke-first"};
var iconAlert = { color: "#D43E2A", fillColor: "#D43E2A", radius: 6, fillOpacity: 0.8, opacity: 0.8, weight: 2, className: "stroke-first node-alert" }; var iconAlert = {color: "#D43E2A", fillColor: "#D43E2A", radius: 6, fillOpacity: 0.8, opacity: 0.8, weight: 2, className: "stroke-first node-alert"};
var iconNew = { color: "#1566A9", fillColor: "#93E929", radius: 6, fillOpacity: 1.0, opacity: 0.5, weight: 2 }; var iconNew = {color: "#1566A9", fillColor: "#93E929", radius: 6, fillOpacity: 1.0, opacity: 0.5, weight: 2};
return function (config, linkScale, sidebar, router, buttons) { return function (config, linkScale, sidebar, router, buttons) {
var self = this; var self = this;
@ -176,10 +178,11 @@ define(["map/clientlayer", "map/labelslayer",
var baseLayers = {}; var baseLayers = {};
var locateUserButton = new LocateButton(function (d) { var locateUserButton = new LocateButton(function (d) {
if (d) if (d) {
enableTracking(); enableTracking();
else } else {
disableTracking(); disableTracking();
}
}); });
var mybuttons = []; var mybuttons = [];
@ -191,28 +194,32 @@ define(["map/clientlayer", "map/labelslayer",
} }
function clearButtons() { function clearButtons() {
mybuttons.forEach( function (d) { mybuttons.forEach(function (d) {
buttons.removeChild(d); buttons.removeChild(d);
}); });
} }
var showCoordsPickerButton = new CoordsPickerButton(function (d) { var showCoordsPickerButton = new CoordsPickerButton(function (d) {
if (d) if (d) {
enableCoords(); enableCoords();
else } else {
disableCoords(); disableCoords();
}
}); });
function saveView() { function saveView() {
savedView = {center: map.getCenter(), savedView = {
zoom: map.getZoom()}; center: map.getCenter(),
zoom: map.getZoom()
};
} }
function enableTracking() { function enableTracking() {
map.locate({watch: true, map.locate({
enableHighAccuracy: true, watch: true,
setView: true enableHighAccuracy: true,
}); setView: true
});
locateUserButton.set(true); locateUserButton.set(true);
} }
@ -241,8 +248,9 @@ define(["map/clientlayer", "map/labelslayer",
} }
function locationFound(e) { function locationFound(e) {
if (!userLocation) if (!userLocation) {
userLocation = new LocationMarker(e.latlng).addTo(map); userLocation = new LocationMarker(e.latlng).addTo(map);
}
userLocation.setLatLng(e.latlng); userLocation.setLatLng(e.latlng);
userLocation.setAccuracy(e.accuracy); userLocation.setAccuracy(e.accuracy);
@ -256,19 +264,22 @@ define(["map/clientlayer", "map/labelslayer",
} }
function addLayer(layerName) { function addLayer(layerName) {
if (layerName in baseLayers) if (layerName in baseLayers) {
return; return;
}
if (layerName in customLayers) if (layerName in customLayers) {
return; return;
}
try { try {
var layer = L.tileLayer.provider(layerName); var layer = L.tileLayer.provider(layerName);
layerControl.addBaseLayer(layer, layerName); layerControl.addBaseLayer(layer, layerName);
customLayers[layerName] = layer; customLayers[layerName] = layer;
if (localStorageTest()) if (localStorageTest()) {
localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers))); localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers)));
}
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
@ -283,7 +294,7 @@ define(["map/clientlayer", "map/labelslayer",
map = L.map(el, options); map = L.map(el, options);
var layers = config.mapLayers.map( function (d) { var layers = config.mapLayers.map(function (d) {
return { return {
"name": d.name, "name": d.name,
"layer": "url" in d ? L.tileLayer(d.url, d.config) : L.tileLayer.provider(d.name) "layer": "url" in d ? L.tileLayer(d.url, d.config) : L.tileLayer.provider(d.name)
@ -292,7 +303,7 @@ define(["map/clientlayer", "map/labelslayer",
layers[0].layer.addTo(map); layers[0].layer.addTo(map);
layers.forEach( function (d) { layers.forEach(function (d) {
baseLayers[d.name] = d.layer; baseLayers[d.name] = d.layer;
}); });
@ -316,8 +327,9 @@ define(["map/clientlayer", "map/labelslayer",
if (localStorageTest()) { if (localStorageTest()) {
var d = JSON.parse(localStorage.getItem("map/customLayers")); var d = JSON.parse(localStorage.getItem("map/customLayers"));
if (d) if (d) {
d.forEach(addLayer); d.forEach(addLayer);
}
d = JSON.parse(localStorage.getItem("map/selectedLayer")); d = JSON.parse(localStorage.getItem("map/selectedLayer"));
d = d && d.name in baseLayers ? baseLayers[d.name] : d && d.name in customLayers ? customLayers[d.name] : false; d = d && d.name in baseLayers ? baseLayers[d.name] : d && d.name in customLayers ? customLayers[d.name] : false;
@ -336,13 +348,16 @@ define(["map/clientlayer", "map/labelslayer",
labelsLayer.addTo(map); labelsLayer.addTo(map);
labelsLayer.setZIndex(6); labelsLayer.setZIndex(6);
map.on("baselayerchange", function(e) { map.on("baselayerchange", function (e) {
map.options.maxZoom = e.layer.options.maxZoom; map.options.maxZoom = e.layer.options.maxZoom;
clientLayer.options.maxZoom = map.options.maxZoom; clientLayer.options.maxZoom = map.options.maxZoom;
labelsLayer.options.maxZoom = map.options.maxZoom; labelsLayer.options.maxZoom = map.options.maxZoom;
if (map.getZoom() > map.options.maxZoom) map.setZoom(map.options.maxZoom); if (map.getZoom() > map.options.maxZoom) {
if (localStorageTest()) map.setZoom(map.options.maxZoom);
}
if (localStorageTest()) {
localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name})); localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name}));
}
}); });
var nodeDict = {}; var nodeDict = {};
@ -350,11 +365,11 @@ define(["map/clientlayer", "map/labelslayer",
var highlight; var highlight;
function resetMarkerStyles(nodes, links) { function resetMarkerStyles(nodes, links) {
Object.keys(nodes).forEach( function (d) { Object.keys(nodes).forEach(function (d) {
nodes[d].resetStyle(); nodes[d].resetStyle();
}); });
Object.keys(links).forEach( function (d) { Object.keys(links).forEach(function (d) {
links[d].resetStyle(); links[d].resetStyle();
}); });
} }
@ -364,17 +379,19 @@ define(["map/clientlayer", "map/labelslayer",
} }
function resetZoom() { function resetZoom() {
if (barycenter) if (barycenter) {
setView(barycenter.getBounds()); setView(barycenter.getBounds());
}
} }
function goto(m) { function goto(m) {
var bounds; var bounds;
if ("getBounds" in m) if ("getBounds" in m) {
bounds = m.getBounds(); bounds = m.getBounds();
else } else {
bounds = L.latLngBounds([m.getLatLng()]); bounds = L.latLngBounds([m.getLatLng()]);
}
setView(bounds); setView(bounds);
@ -385,48 +402,62 @@ define(["map/clientlayer", "map/labelslayer",
resetMarkerStyles(nodeDict, linkDict); resetMarkerStyles(nodeDict, linkDict);
var m; var m;
if (highlight !== undefined) if (highlight !== undefined) {
if (highlight.type === "node") { if (highlight.type === "node") {
m = nodeDict[highlight.o.nodeinfo.node_id]; m = nodeDict[highlight.o.nodeinfo.node_id];
if (m) if (m) {
m.setStyle({ color: "orange", weight: 20, fillOpacity: 1, opacity: 0.7, className: "stroke-first" }); m.setStyle({color: "orange", weight: 20, fillOpacity: 1, opacity: 0.7, className: "stroke-first"});
}
} else if (highlight.type === "link") { } else if (highlight.type === "link") {
m = linkDict[highlight.o.id]; m = linkDict[highlight.o.id];
if (m) if (m) {
m.setStyle({ weight: 7, opacity: 1, dashArray: "10, 10" }); m.setStyle({weight: 7, opacity: 1, dashArray: "10, 10"});
}
} }
}
if (!nopanzoom) if (!nopanzoom) {
if (m) if (m) {
goto(m); goto(m);
else if (savedView) } else if (savedView) {
map.setView(savedView.center, savedView.zoom); map.setView(savedView.center, savedView.zoom);
else } else {
resetZoom(); resetZoom();
}
}
} }
function calcBarycenter(nodes) { function calcBarycenter(nodes) {
nodes = nodes.map(function (d) { return d.nodeinfo.location; }); nodes = nodes.map(function (d) {
return d.nodeinfo.location;
});
if (nodes.length === 0) if (nodes.length === 0) {
return undefined; return undefined;
}
var lats = nodes.map(function (d) { return d.latitude; }); var lats = nodes.map(function (d) {
var lngs = nodes.map(function (d) { return d.longitude; }); return d.latitude;
});
var lngs = nodes.map(function (d) {
return d.longitude;
});
var barycenter = L.latLng(d3.median(lats), d3.median(lngs)); var barycenter = L.latLng(d3.median(lats), d3.median(lngs));
var barycenterDev = [d3.deviation(lats), d3.deviation(lngs)]; var barycenterDev = [d3.deviation(lats), d3.deviation(lngs)];
if (barycenterDev[0] === undefined) if (barycenterDev[0] === undefined) {
barycenterDev[0] = 0; barycenterDev[0] = 0;
}
if (barycenterDev[1] === undefined) if (barycenterDev[1] === undefined) {
barycenterDev[1] = 0; barycenterDev[1] = 0;
}
var barycenterCircle = L.latLng(barycenter.lat + barycenterDev[0], var barycenterCircle = L.latLng(barycenter.lat + barycenterDev[0],
barycenter.lng + barycenterDev[1]); barycenter.lng + barycenterDev[1]);
var r = barycenter.distanceTo(barycenterCircle); var r = barycenter.distanceTo(barycenterCircle);
@ -434,8 +465,8 @@ define(["map/clientlayer", "map/labelslayer",
} }
function mapRTree(d) { function mapRTree(d) {
var o = [ d.nodeinfo.location.latitude, d.nodeinfo.location.longitude, var o = [d.nodeinfo.location.latitude, d.nodeinfo.location.longitude,
d.nodeinfo.location.latitude, d.nodeinfo.location.longitude]; d.nodeinfo.location.latitude, d.nodeinfo.location.longitude];
o.node = d; o.node = d;
@ -446,45 +477,58 @@ define(["map/clientlayer", "map/labelslayer",
nodeDict = {}; nodeDict = {};
linkDict = {}; linkDict = {};
if (groupOffline) if (groupOffline) {
groupOffline.clearLayers(); groupOffline.clearLayers();
}
if (groupOnline) if (groupOnline) {
groupOnline.clearLayers(); groupOnline.clearLayers();
}
if (groupNew) if (groupNew) {
groupNew.clearLayers(); groupNew.clearLayers();
}
if (groupLost) if (groupLost) {
groupLost.clearLayers(); groupLost.clearLayers();
}
if (groupLines) if (groupLines) {
groupLines.clearLayers(); groupLines.clearLayers();
}
var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router); var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router);
groupLines = L.featureGroup(lines).addTo(map); groupLines = L.featureGroup(lines).addTo(map);
if (typeof config.fixedCenter === "undefined") if (typeof config.fixedCenter === "undefined") {
barycenter = calcBarycenter(data.nodes.all.filter(has_location)); barycenter = calcBarycenter(data.nodes.all.filter(has_location));
else } else {
barycenter = L.circle(L.latLng(new L.LatLng(config.fixedCenter.lat, config.fixedCenter.lng)), config.fixedCenter.radius * 1000); barycenter = L.circle(L.latLng(new L.LatLng(config.fixedCenter.lat, config.fixedCenter.lng)), config.fixedCenter.radius * 1000);
}
var nodesOnline = subtract(data.nodes.all.filter(online), data.nodes.new); var nodesOnline = subtract(data.nodes.all.filter(online), data.nodes.new);
var nodesOffline = subtract(data.nodes.all.filter(offline), data.nodes.lost); var nodesOffline = subtract(data.nodes.all.filter(offline), data.nodes.lost);
var markersOnline = nodesOnline.filter(has_location) var markersOnline = nodesOnline.filter(has_location)
.map(mkMarker(nodeDict, function () { return iconOnline; }, router)); .map(mkMarker(nodeDict, function () {
return iconOnline;
}, router));
var markersOffline = nodesOffline.filter(has_location) var markersOffline = nodesOffline.filter(has_location)
.map(mkMarker(nodeDict, function () { return iconOffline; }, router)); .map(mkMarker(nodeDict, function () {
return iconOffline;
}, router));
var markersNew = data.nodes.new.filter(has_location) var markersNew = data.nodes.new.filter(has_location)
.map(mkMarker(nodeDict, function () { return iconNew; }, router)); .map(mkMarker(nodeDict, function () {
return iconNew;
}, router));
var markersLost = data.nodes.lost.filter(has_location) var markersLost = data.nodes.lost.filter(has_location)
.map(mkMarker(nodeDict, function (d) { .map(mkMarker(nodeDict, function (d) {
if (d.lastseen.isAfter(moment(data.now).subtract(3, "days"))) if (d.lastseen.isAfter(moment(data.now).subtract(3, "days"))) {
return iconAlert; return iconAlert;
}
return iconLost; return iconLost;
}, router)); }, router));
@ -499,11 +543,12 @@ define(["map/clientlayer", "map/labelslayer",
rtreeOnlineAll.load(data.nodes.all.filter(online).filter(has_location).map(mapRTree)); rtreeOnlineAll.load(data.nodes.all.filter(online).filter(has_location).map(mapRTree));
clientLayer.setData(rtreeOnlineAll); clientLayer.setData(rtreeOnlineAll);
labelsLayer.setData({online: nodesOnline.filter(has_location), labelsLayer.setData({
offline: nodesOffline.filter(has_location), online: nodesOnline.filter(has_location),
new: data.nodes.new.filter(has_location), offline: nodesOffline.filter(has_location),
lost: data.nodes.lost.filter(has_location) new: data.nodes.new.filter(has_location),
}); lost: data.nodes.lost.filter(has_location)
});
updateView(true); updateView(true);
}; };
@ -534,8 +579,9 @@ define(["map/clientlayer", "map/labelslayer",
clearButtons(); clearButtons();
map.remove(); map.remove();
if (el.parentNode) if (el.parentNode) {
el.parentNode.removeChild(el); el.parentNode.removeChild(el);
}
}; };
self.render = function (d) { self.render = function (d) {
@ -545,4 +591,4 @@ define(["map/clientlayer", "map/labelslayer",
return self; return self;
}; };
}); });

View File

@ -21,8 +21,9 @@ define(["leaflet", "jshashes"],
return [br.lat, tl.lng, tl.lat, br.lng]; return [br.lat, tl.lng, tl.lat, br.lng];
} }
if (!this.data) if (!this.data) {
return; return;
}
var tileSize = this.options.tileSize; var tileSize = this.options.tileSize;
var s = tilePoint.multiplyBy(tileSize); var s = tilePoint.multiplyBy(tileSize);
@ -33,8 +34,9 @@ define(["leaflet", "jshashes"],
var nodes = this.data.search(bbox); var nodes = this.data.search(bbox);
if (nodes.length === 0) if (nodes.length === 0) {
return; return;
}
var ctx = canvas.getContext("2d"); var ctx = canvas.getContext("2d");
@ -47,8 +49,9 @@ define(["leaflet", "jshashes"],
var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]); var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]);
var clients = d.node.statistics.clients; var clients = d.node.statistics.clients;
if (clients === 0) if (clients === 0) {
return; return;
}
p.x -= s.x; p.x -= s.x;
p.y -= s.y; p.y -= s.y;
@ -73,4 +76,4 @@ define(["leaflet", "jshashes"],
ctx.fill(); ctx.fill();
} }
}); });
}); });

View File

@ -1,13 +1,13 @@
define(["leaflet", "rbush"], define(["leaflet", "rbush"],
function (L, rbush) { function (L, rbush) {
var labelLocations = [["left", "middle", 0 / 8], var labelLocations = [["left", "middle", 0 / 8],
["center", "top", 6 / 8], ["center", "top", 6 / 8],
["right", "middle", 4 / 8], ["right", "middle", 4 / 8],
["left", "top", 7 / 8], ["left", "top", 7 / 8],
["left", "ideographic", 1 / 8], ["left", "ideographic", 1 / 8],
["right", "top", 5 / 8], ["right", "top", 5 / 8],
["center", "ideographic", 2 / 8], ["center", "ideographic", 2 / 8],
["right", "ideographic", 3 / 8]]; ["right", "ideographic", 3 / 8]];
var fontFamily = "Roboto"; var fontFamily = "Roboto";
var nodeRadius = 4; var nodeRadius = 4;
@ -30,22 +30,23 @@ define(["leaflet", "rbush"],
function prepareLabel(fillStyle, fontSize, offset, stroke, minZoom) { function prepareLabel(fillStyle, fontSize, offset, stroke, minZoom) {
return function (d) { return function (d) {
var font = fontSize + "px " + fontFamily; var font = fontSize + "px " + fontFamily;
return { position: L.latLng(d.nodeinfo.location.latitude, d.nodeinfo.location.longitude), return {
label: d.nodeinfo.hostname, position: L.latLng(d.nodeinfo.location.latitude, d.nodeinfo.location.longitude),
offset: offset, label: d.nodeinfo.hostname,
fillStyle: fillStyle, offset: offset,
height: fontSize * 1.2, fillStyle: fillStyle,
font: font, height: fontSize * 1.2,
stroke: stroke, font: font,
minZoom: minZoom, stroke: stroke,
width: measureText(font, d.nodeinfo.hostname).width minZoom: minZoom,
}; width: measureText(font, d.nodeinfo.hostname).width
};
}; };
} }
function calcOffset(offset, loc) { function calcOffset(offset, loc) {
return [ offset * Math.cos(loc[2] * 2 * Math.PI), return [offset * Math.cos(loc[2] * 2 * Math.PI),
-offset * Math.sin(loc[2] * 2 * Math.PI)]; -offset * Math.sin(loc[2] * 2 * Math.PI)];
} }
function labelRect(p, offset, anchor, label, minZoom, maxZoom, z) { function labelRect(p, offset, anchor, label, minZoom, maxZoom, z) {
@ -54,15 +55,17 @@ define(["leaflet", "rbush"],
var width = label.width * margin; var width = label.width * margin;
var height = label.height * margin; var height = label.height * margin;
var dx = { left: 0, var dx = {
right: -width, left: 0,
center: -width / 2 right: -width,
}; center: -width / 2
};
var dy = { top: 0, var dy = {
ideographic: -height, top: 0,
middle: -height / 2 ideographic: -height,
}; middle: -height / 2
};
var x = p.x + offset[0] + dx[anchor[0]]; var x = p.x + offset[0] + dx[anchor[0]];
var y = p.y + offset[1] + dy[anchor[1]]; var y = p.y + offset[1] + dy[anchor[1]];
@ -73,13 +76,15 @@ define(["leaflet", "rbush"],
var c = L.TileLayer.Canvas.extend({ var c = L.TileLayer.Canvas.extend({
onAdd: function (map) { onAdd: function (map) {
L.TileLayer.Canvas.prototype.onAdd.call(this, map); L.TileLayer.Canvas.prototype.onAdd.call(this, map);
if (this.data) if (this.data) {
this.prepareLabels(); this.prepareLabels();
}
}, },
setData: function (d) { setData: function (d) {
this.data = d; this.data = d;
if (this._map) if (this._map) {
this.prepareLabels(); this.prepareLabels();
}
}, },
prepareLabels: function () { prepareLabels: function () {
var d = this.data; var d = this.data;
@ -98,10 +103,10 @@ define(["leaflet", "rbush"],
var labelsLost = d.lost.map(prepareLabel("rgba(212, 62, 42, 0.9)", 11, 8, true, 0)); var labelsLost = d.lost.map(prepareLabel("rgba(212, 62, 42, 0.9)", 11, 8, true, 0));
var labels = [] var labels = []
.concat(labelsNew) .concat(labelsNew)
.concat(labelsLost) .concat(labelsLost)
.concat(labelsOnline) .concat(labelsOnline)
.concat(labelsOffline); .concat(labelsOffline);
var minZoom = this.options.minZoom; var minZoom = this.options.minZoom;
var maxZoom = this.options.maxZoom; var maxZoom = this.options.maxZoom;
@ -114,7 +119,7 @@ define(["leaflet", "rbush"],
return function (d) { return function (d) {
var p = map.project(d.position, z); var p = map.project(d.position, z);
return [p.x - nodeRadius, p.y - nodeRadius, return [p.x - nodeRadius, p.y - nodeRadius,
p.x + nodeRadius, p.y + nodeRadius]; p.x + nodeRadius, p.y + nodeRadius];
}; };
} }
@ -133,8 +138,9 @@ define(["leaflet", "rbush"],
var rect = labelRect(p, offset, loc, d, minZoom, maxZoom, z); var rect = labelRect(p, offset, loc, d, minZoom, maxZoom, z);
var candidates = trees[z].search(rect); var candidates = trees[z].search(rect);
if (candidates.length > 0) if (candidates.length > 0) {
break; break;
}
} }
return {loc: loc, z: z + 1}; return {loc: loc, z: z + 1};
@ -156,16 +162,20 @@ define(["leaflet", "rbush"],
} }
return d; return d;
} else } else {
return undefined; return undefined;
}).filter(function (d) { return d !== undefined; }); }
}).filter(function (d) {
return d !== undefined;
});
this.margin = 16; this.margin = 16;
if (labels.length > 0) if (labels.length > 0) {
this.margin += labels.map(function (d) { this.margin += labels.map(function (d) {
return d.width; return d.width;
}).sort().reverse()[0]; }).sort().reverse()[0];
}
this.labels = rbush(9); this.labels = rbush(9);
this.labels.load(labels.map(mapRTree)); this.labels.load(labels.map(mapRTree));
@ -180,8 +190,9 @@ define(["leaflet", "rbush"],
return [br.lat, tl.lng, tl.lat, br.lng]; return [br.lat, tl.lng, tl.lat, br.lng];
} }
if (!this.labels) if (!this.labels) {
return; return;
}
var tileSize = this.options.tileSize; var tileSize = this.options.tileSize;
var s = tilePoint.multiplyBy(tileSize); var s = tilePoint.multiplyBy(tileSize);
@ -212,8 +223,9 @@ define(["leaflet", "rbush"],
ctx.textBaseline = d.label.anchor[1]; ctx.textBaseline = d.label.anchor[1];
ctx.fillStyle = d.label.fillStyle; ctx.fillStyle = d.label.fillStyle;
if (d.label.stroke) if (d.label.stroke) {
ctx.strokeText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]); ctx.strokeText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]);
}
ctx.fillText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]); ctx.fillText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]);
} }
@ -225,4 +237,4 @@ define(["leaflet", "rbush"],
}); });
return c; return c;
}); });

View File

@ -8,24 +8,28 @@ define(function () {
var totalOnlineNodes = sum(d.nodes.all.filter(online).map(one)); var totalOnlineNodes = sum(d.nodes.all.filter(online).map(one));
var totalNewNodes = sum(d.nodes.new.map(one)); var totalNewNodes = sum(d.nodes.new.map(one));
var totalLostNodes = sum(d.nodes.lost.map(one)); var totalLostNodes = sum(d.nodes.lost.map(one));
var totalClients = sum(d.nodes.all.filter(online).map( function (d) { var totalClients = sum(d.nodes.all.filter(online).map(function (d) {
return d.statistics.clients ? d.statistics.clients : 0; return d.statistics.clients ? d.statistics.clients : 0;
})); }));
var totalGateways = sum(d.nodes.all.filter(online).filter( function (d) { var totalGateways = sum(d.nodes.all.filter(online).filter(function (d) {
return d.flags.gateway; return d.flags.gateway;
}).map(one)); }).map(one));
var nodetext = [{ count: totalOnlineNodes, label: "online" }, var nodetext = [{count: totalOnlineNodes, label: "online"},
{ count: totalNewNodes, label: "neu" }, {count: totalNewNodes, label: "neu"},
{ count: totalLostNodes, label: "verschwunden" } {count: totalLostNodes, label: "verschwunden"}
].filter( function (d) { return d.count > 0; } ) ].filter(function (d) {
.map( function (d) { return [d.count, d.label].join(" "); } ) return d.count > 0;
.join(", "); })
.map(function (d) {
return [d.count, d.label].join(" ");
})
.join(", ");
stats.textContent = totalNodes + " Knoten " + stats.textContent = totalNodes + " Knoten " +
"(" + nodetext + "), " + "(" + nodetext + "), " +
totalClients + " Client" + ( totalClients === 1 ? ", " : "s, " ) + totalClients + " Client" + ( totalClients === 1 ? ", " : "s, " ) +
totalGateways + " Gateways"; totalGateways + " Gateways";
timestamp.textContent = "Diese Daten sind von " + d.timestamp.format("LLLL") + "."; timestamp.textContent = "Diese Daten sind von " + d.timestamp.format("LLLL") + ".";
}; };

View File

@ -1,62 +1,71 @@
define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral) { define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral) {
function getUptime(now, d) { function getUptime(now, d) {
if (d.flags.online && "uptime" in d.statistics) if (d.flags.online && "uptime" in d.statistics) {
return Math.round(d.statistics.uptime); return Math.round(d.statistics.uptime);
else if (!d.flags.online && "lastseen" in d) } else if (!d.flags.online && "lastseen" in d) {
return Math.round(-(now.unix() - d.lastseen.unix())); return Math.round(-(now.unix() - d.lastseen.unix()));
}
} }
function showUptime(uptime) { function showUptime(uptime) {
var s = ""; var s = "";
uptime /= 3600; uptime /= 3600;
if (uptime !== undefined) if (uptime !== undefined) {
if (Math.abs(uptime) >= 24) if (Math.abs(uptime) >= 24) {
s = Math.round(uptime / 24) + "d"; s = Math.round(uptime / 24) + "d";
else } else {
s = Math.round(uptime) + "h"; s = Math.round(uptime) + "h";
}
}
return s; return s;
} }
var headings = [{ name: "Knoten", var headings = [{
sort: function (a, b) { name: "Knoten",
return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname); sort: function (a, b) {
}, return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname);
reverse: false },
}, reverse: false
{ name: "Uptime", },
sort: function (a, b) { {
return a.uptime - b.uptime; name: "Uptime",
}, sort: function (a, b) {
reverse: true return a.uptime - b.uptime;
}, },
{ name: "#Links", reverse: true
sort: function (a, b) { },
return a.meshlinks - b.meshlinks; {
}, name: "#Links",
reverse: true sort: function (a, b) {
}, return a.meshlinks - b.meshlinks;
{ name: "Clients", },
sort: function (a, b) { reverse: true
return ("clients" in a.statistics ? a.statistics.clients : -1) - },
("clients" in b.statistics ? b.statistics.clients : -1); {
}, name: "Clients",
reverse: true sort: function (a, b) {
}]; return ("clients" in a.statistics ? a.statistics.clients : -1) -
("clients" in b.statistics ? b.statistics.clients : -1);
},
reverse: true
}];
return function(router) { return function (router) {
function renderRow(d) { function renderRow(d) {
var td1Content = []; var td1Content = [];
var aClass = ["hostname", d.flags.online ? "online" : "offline"]; var aClass = ["hostname", d.flags.online ? "online" : "offline"];
td1Content.push(V.h("a", { className: aClass.join(" "), td1Content.push(V.h("a", {
onclick: router.node(d), className: aClass.join(" "),
href: "#" onclick: router.node(d),
}, d.nodeinfo.hostname)); href: "#"
}, d.nodeinfo.hostname));
if (has_location(d)) if (has_location(d)) {
td1Content.push(V.h("span", {className: "icon ion-location"})); td1Content.push(V.h("span", {className: "icon ion-location"}));
}
var td1 = V.h("td", td1Content); var td1 = V.h("td", td1Content);
var td2 = V.h("td", showUptime(d.uptime)); var td2 = V.h("td", showUptime(d.uptime));

View File

@ -1,224 +1,260 @@
define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "vercomp" ], define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "vercomp"],
function (Chroma, V, numeral, Filter, vercomp) { function (Chroma, V, numeral, Filter, vercomp) {
return function (config, filterManager) { return function (config, filterManager) {
var self = this; var self = this;
var scale = Chroma.scale("YlGnBu").mode("lab"); var scale = Chroma.scale("YlGnBu").mode("lab");
var statusTable = document.createElement("table"); var statusTable = document.createElement("table");
statusTable.classList.add("proportion"); statusTable.classList.add("proportion");
var fwTable = document.createElement("table"); var fwTable = document.createElement("table");
fwTable.classList.add("proportion"); fwTable.classList.add("proportion");
var hwTable = document.createElement("table"); var hwTable = document.createElement("table");
hwTable.classList.add("proportion"); hwTable.classList.add("proportion");
var geoTable = document.createElement("table"); var geoTable = document.createElement("table");
geoTable.classList.add("proportion"); geoTable.classList.add("proportion");
var autoTable = document.createElement("table"); var autoTable = document.createElement("table");
autoTable.classList.add("proportion"); autoTable.classList.add("proportion");
var uplinkTable = document.createElement("table"); var uplinkTable = document.createElement("table");
uplinkTable.classList.add("proportion"); uplinkTable.classList.add("proportion");
var gwNodesTable = document.createElement("table"); var gwNodesTable = document.createElement("table");
gwNodesTable.classList.add("proportion"); gwNodesTable.classList.add("proportion");
var gwClientsTable = document.createElement("table"); var gwClientsTable = document.createElement("table");
gwClientsTable.classList.add("proportion"); gwClientsTable.classList.add("proportion");
var siteTable = document.createElement("table"); var siteTable = document.createElement("table");
siteTable.classList.add("proportion"); siteTable.classList.add("proportion");
function showStatGlobal(o) { function showStatGlobal(o) {
return showStat(o); return showStat(o);
} }
function count(nodes, key, f) { function count(nodes, key, f) {
var dict = {}; var dict = {};
nodes.forEach( function (d) { nodes.forEach(function (d) {
var v = dictGet(d, key.slice(0)); var v = dictGet(d, key.slice(0));
if (f !== undefined) if (f !== undefined) {
v = f(v); v = f(v);
}
if (v === null) if (v === null) {
return; return;
}
dict[v] = 1 + (v in dict ? dict[v] : 0); dict[v] = 1 + (v in dict ? dict[v] : 0);
});
return Object.keys(dict).map(function (d) { return [d, dict[d], key, f]; });
}
function countClients(nodes, key, f) {
var dict = {};
nodes.forEach( function (d) {
var v = dictGet(d, key.slice(0));
if (f !== undefined)
v = f(v);
if (v === null)
return;
dict[v] = d.statistics.clients + (v in dict ? dict[v] : 0);
});
return Object.keys(dict).map(function (d) { return [d, dict[d], key, f]; });
}
function addFilter(filter) {
return function () {
filterManager.addFilter(filter);
return false;
};
}
function fillTable(name, table, data) {
if (!table.last)
table.last = V.h("table");
var max = 0;
data.forEach(function (d) {
if (d[1] > max)
max = d[1];
});
var items = data.map(function (d) {
var v = d[1] / max;
var c1 = Chroma.contrast(scale(v), "white");
var c2 = Chroma.contrast(scale(v), "black");
var filter = new Filter(name, d[2], d[0], d[3]);
var a = V.h("a", { href: "#", onclick: addFilter(filter) }, d[0]);
var th = V.h("th", a);
var td = V.h("td", V.h("span", {style: {
width: Math.round(v * 100) + "%",
backgroundColor: scale(v).hex(),
color: c1 > c2 ? "white" : "black"
}}, numeral(d[1]).format("0,0")));
return V.h("tr", [th, td]);
});
var tableNew = V.h("table", items);
table = V.patch(table, V.diff(table.last, tableNew));
table.last = tableNew;
}
self.setData = function (data) {
var onlineNodes = data.nodes.all.filter(online);
var nodes = onlineNodes.concat(data.nodes.lost);
var nodeDict = {};
data.nodes.all.forEach(function (d) {
nodeDict[d.nodeinfo.node_id] = d;
});
var statusDict = count(nodes, ["flags", "online"], function (d) {
return d ? "online" : "offline";
});
var fwDict = count(nodes, ["nodeinfo", "software", "firmware", "release"]);
var hwDict = count(nodes, ["nodeinfo", "hardware", "model"]);
var geoDict = count(nodes, ["nodeinfo", "location"], function (d) {
return d && d.longitude && d.latitude ? "ja" : "nein";
});
var autoDict = count(nodes, ["nodeinfo", "software", "autoupdater"], function (d) {
if (d === null)
return null;
else if (d.enabled)
return d.branch;
else
return "(deaktiviert)";
});
var uplinkDict = count(nodes, ["flags", "uplink"], function (d) {
return d ? "ja" : "nein";
});
var gwNodesDict = count(onlineNodes, ["statistics", "gateway"], function (d) {
if (d === null)
return null;
if (d in nodeDict)
return nodeDict[d].nodeinfo.hostname;
return d;
});
var gwClientsDict = countClients(onlineNodes, ["statistics", "gateway"], function (d) {
if (d === null)
return null;
if (d in nodeDict)
return nodeDict[d].nodeinfo.hostname;
return d;
});
var siteDict = count(nodes, ["nodeinfo", "system", "site_code"], function (d) {
var rt = d;
if (config.siteNames)
config.siteNames.forEach( function (t) {
if(d === t.site)
rt = t.name;
});
return rt;
});
fillTable("Status", statusTable, statusDict.sort(function (a, b) { return b[1] - a[1]; }));
fillTable("Firmware", fwTable, fwDict.sort(function (a, b) { return vercomp(b[0], a[0]); }));
fillTable("Hardware", hwTable, hwDict.sort(function (a, b) { return b[1] - a[1]; }));
fillTable("Koordinaten", geoTable, geoDict.sort(function (a, b) { return b[1] - a[1]; }));
fillTable("Uplink", uplinkTable, uplinkDict.sort(function (a, b) { return b[1] - a[1]; }));
fillTable("Autom. Updates", autoTable, autoDict.sort(function (a, b) { return b[1] - a[1]; }));
fillTable("Nodes an Gateway", gwNodesTable, gwNodesDict.sort(function (a, b) { return b[1] - a[1]; }));
fillTable("Clients an Gateway", gwClientsTable, gwClientsDict.sort(function (a, b) { return b[1] - a[1]; }));
fillTable("Site", siteTable, siteDict.sort(function (a, b) { return b[1] - a[1]; }));
};
self.render = function (el) {
var h2;
self.renderSingle(el, "Status", statusTable);
self.renderSingle(el, "Nodes an Gateway", gwNodesTable);
self.renderSingle(el, "Clients an Gateway", gwClientsTable);
self.renderSingle(el, "Firmwareversionen", fwTable);
self.renderSingle(el, "Uplink", uplinkTable);
self.renderSingle(el, "Hardwaremodelle", hwTable);
self.renderSingle(el, "Auf der Karte sichtbar", geoTable);
self.renderSingle(el, "Autoupdater", autoTable);
self.renderSingle(el, "Site", siteTable);
if (config.globalInfos)
config.globalInfos.forEach(function (globalInfo) {
h2 = document.createElement("h2");
h2.textContent = globalInfo.name;
el.appendChild(h2);
el.appendChild(showStatGlobal(globalInfo));
}); });
return Object.keys(dict).map(function (d) {
return [d, dict[d], key, f];
});
}
function countClients(nodes, key, f) {
var dict = {};
nodes.forEach(function (d) {
var v = dictGet(d, key.slice(0));
if (f !== undefined) {
v = f(v);
}
if (v === null) {
return;
}
dict[v] = d.statistics.clients + (v in dict ? dict[v] : 0);
});
return Object.keys(dict).map(function (d) {
return [d, dict[d], key, f];
});
}
function addFilter(filter) {
return function () {
filterManager.addFilter(filter);
return false;
};
}
function fillTable(name, table, data) {
if (!table.last) {
table.last = V.h("table");
}
var max = 0;
data.forEach(function (d) {
if (d[1] > max) {
max = d[1];
}
});
var items = data.map(function (d) {
var v = d[1] / max;
var c1 = Chroma.contrast(scale(v), "white");
var c2 = Chroma.contrast(scale(v), "black");
var filter = new Filter(name, d[2], d[0], d[3]);
var a = V.h("a", {href: "#", onclick: addFilter(filter)}, d[0]);
var th = V.h("th", a);
var td = V.h("td", V.h("span", {
style: {
width: Math.round(v * 100) + "%",
backgroundColor: scale(v).hex(),
color: c1 > c2 ? "white" : "black"
}
}, numeral(d[1]).format("0,0")));
return V.h("tr", [th, td]);
});
var tableNew = V.h("table", items);
table = V.patch(table, V.diff(table.last, tableNew));
table.last = tableNew;
}
self.setData = function (data) {
var onlineNodes = data.nodes.all.filter(online);
var nodes = onlineNodes.concat(data.nodes.lost);
var nodeDict = {};
data.nodes.all.forEach(function (d) {
nodeDict[d.nodeinfo.node_id] = d;
});
var statusDict = count(nodes, ["flags", "online"], function (d) {
return d ? "online" : "offline";
});
var fwDict = count(nodes, ["nodeinfo", "software", "firmware", "release"]);
var hwDict = count(nodes, ["nodeinfo", "hardware", "model"]);
var geoDict = count(nodes, ["nodeinfo", "location"], function (d) {
return d && d.longitude && d.latitude ? "ja" : "nein";
});
var autoDict = count(nodes, ["nodeinfo", "software", "autoupdater"], function (d) {
if (d === null) {
return null;
} else if (d.enabled) {
return d.branch;
} else {
return "(deaktiviert)";
}
});
var uplinkDict = count(nodes, ["flags", "uplink"], function (d) {
return d ? "ja" : "nein";
});
var gwNodesDict = count(onlineNodes, ["statistics", "gateway"], function (d) {
if (d === null) {
return null;
}
if (d in nodeDict) {
return nodeDict[d].nodeinfo.hostname;
}
return d;
});
var gwClientsDict = countClients(onlineNodes, ["statistics", "gateway"], function (d) {
if (d === null) {
return null;
}
if (d in nodeDict) {
return nodeDict[d].nodeinfo.hostname;
}
return d;
});
var siteDict = count(nodes, ["nodeinfo", "system", "site_code"], function (d) {
var rt = d;
if (config.siteNames) {
config.siteNames.forEach(function (t) {
if (d === t.site) {
rt = t.name;
}
});
}
return rt;
});
fillTable("Status", statusTable, statusDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable("Firmware", fwTable, fwDict.sort(function (a, b) {
return vercomp(b[0], a[0]);
}));
fillTable("Hardware", hwTable, hwDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable("Koordinaten", geoTable, geoDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable("Uplink", uplinkTable, uplinkDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable("Autom. Updates", autoTable, autoDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable("Nodes an Gateway", gwNodesTable, gwNodesDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable("Clients an Gateway", gwClientsTable, gwClientsDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable("Site", siteTable, siteDict.sort(function (a, b) {
return b[1] - a[1];
}));
}; };
self.renderSingle = function (el, heading, table) { self.render = function (el) {
var h2; var h2;
h2 = document.createElement("h2"); self.renderSingle(el, "Status", statusTable);
h2.textContent = heading; self.renderSingle(el, "Nodes an Gateway", gwNodesTable);
h2.onclick = function () { self.renderSingle(el, "Clients an Gateway", gwClientsTable);
table.classList.toggle("hidden"); self.renderSingle(el, "Firmwareversionen", fwTable);
}; self.renderSingle(el, "Uplink", uplinkTable);
el.appendChild(h2); self.renderSingle(el, "Hardwaremodelle", hwTable);
el.appendChild(table); self.renderSingle(el, "Auf der Karte sichtbar", geoTable);
}; self.renderSingle(el, "Autoupdater", autoTable);
return self; self.renderSingle(el, "Site", siteTable);
};
}); if (config.globalInfos) {
config.globalInfos.forEach(function (globalInfo) {
h2 = document.createElement("h2");
h2.textContent = globalInfo.name;
el.appendChild(h2);
el.appendChild(showStatGlobal(globalInfo));
});
}
};
self.renderSingle = function (el, heading, table) {
var h2;
h2 = document.createElement("h2");
h2.textContent = heading;
h2.onclick = function () {
table.classList.toggle("hidden");
};
el.appendChild(h2);
el.appendChild(table);
};
return self;
};
});

View File

@ -1,7 +1,7 @@
define(function () { define(function () {
return function () { return function () {
var self = this; var self = this;
var objects = { nodes: {}, links: {} }; var objects = {nodes: {}, links: {}};
var targets = []; var targets = [];
var views = {}; var views = {};
var currentView; var currentView;
@ -11,15 +11,18 @@ define(function () {
function saveState() { function saveState() {
var e = []; var e = [];
if (currentView) if (currentView) {
e.push("v:" + currentView); e.push("v:" + currentView);
}
if (currentObject) { if (currentObject) {
if ("node" in currentObject) if ("node" in currentObject) {
e.push("n:" + encodeURIComponent(currentObject.node.nodeinfo.node_id)); e.push("n:" + encodeURIComponent(currentObject.node.nodeinfo.node_id));
}
if ("link" in currentObject) if ("link" in currentObject) {
e.push("l:" + encodeURIComponent(currentObject.link.id)); e.push("l:" + encodeURIComponent(currentObject.link.id));
}
} }
var s = "#!" + e.join(";"); var s = "#!" + e.join(";");
@ -30,7 +33,7 @@ define(function () {
function resetView(push) { function resetView(push) {
push = trueDefault(push); push = trueDefault(push);
targets.forEach( function (t) { targets.forEach(function (t) {
t.resetView(); t.resetView();
}); });
@ -41,10 +44,11 @@ define(function () {
} }
function gotoNode(d) { function gotoNode(d) {
if (!d) if (!d) {
return false; return false;
}
targets.forEach( function (t) { targets.forEach(function (t) {
t.gotoNode(d); t.gotoNode(d);
}); });
@ -52,10 +56,11 @@ define(function () {
} }
function gotoLink(d) { function gotoLink(d) {
if (!d) if (!d) {
return false; return false;
}
targets.forEach( function (t) { targets.forEach(function (t) {
t.gotoLink(d); t.gotoLink(d);
}); });
@ -63,11 +68,14 @@ define(function () {
} }
function gotoLocation(d) { function gotoLocation(d) {
if (!d) if (!d) {
return false; return false;
}
targets.forEach( function (t) { targets.forEach(function (t) {
if(!t.gotoLocation)console.warn("has no gotoLocation", t); if (!t.gotoLocation) {
console.warn("has no gotoLocation", t);
}
t.gotoLocation(d); t.gotoLocation(d);
}); });
@ -75,13 +83,15 @@ define(function () {
} }
function loadState(s) { function loadState(s) {
if (!s) if (!s) {
return false; return false;
}
s = decodeURIComponent(s); s = decodeURIComponent(s);
if (!s.startsWith("#!")) if (!s.startsWith("#!")) {
return false; return false;
}
var targetSet = false; var targetSet = false;
@ -98,7 +108,7 @@ define(function () {
if (args[0] === "n") { if (args[0] === "n") {
id = args[1]; id = args[1];
if (id in objects.nodes) { if (id in objects.nodes) {
currentObject = { node: objects.nodes[id] }; currentObject = {node: objects.nodes[id]};
gotoNode(objects.nodes[id]); gotoNode(objects.nodes[id]);
targetSet = true; targetSet = true;
} }
@ -107,7 +117,7 @@ define(function () {
if (args[0] === "l") { if (args[0] === "l") {
id = args[1]; id = args[1];
if (id in objects.links) { if (id in objects.links) {
currentObject = { link: objects.links[id] }; currentObject = {link: objects.links[id]};
gotoLink(objects.links[id]); gotoLink(objects.links[id]);
targetSet = true; targetSet = true;
} }
@ -120,12 +130,14 @@ define(function () {
self.start = function () { self.start = function () {
running = true; running = true;
if (!loadState(window.location.hash)) if (!loadState(window.location.hash)) {
resetView(false); resetView(false);
}
window.onpopstate = function (d) { window.onpopstate = function (d) {
if (!loadState(d.state)) if (!loadState(d.state)) {
resetView(false); resetView(false);
}
}; };
}; };
@ -133,11 +145,13 @@ define(function () {
if (d in views) { if (d in views) {
views[d](); views[d]();
if (!currentView || running) if (!currentView || running) {
currentView = d; currentView = d;
}
if (!running) if (!running) {
return; return;
}
saveState(); saveState();
@ -146,18 +160,20 @@ define(function () {
return; return;
} }
if ("node" in currentObject) if ("node" in currentObject) {
gotoNode(currentObject.node); gotoNode(currentObject.node);
}
if ("link" in currentObject) if ("link" in currentObject) {
gotoLink(currentObject.link); gotoLink(currentObject.link);
}
} }
}; };
self.node = function (d) { self.node = function (d) {
return function () { return function () {
if (gotoNode(d)) { if (gotoNode(d)) {
currentObject = { node: d }; currentObject = {node: d};
saveState(); saveState();
} }
@ -168,7 +184,7 @@ define(function () {
self.link = function (d) { self.link = function (d) {
return function () { return function () {
if (gotoLink(d)) { if (gotoLink(d)) {
currentObject = { link: d }; currentObject = {link: d};
saveState(); saveState();
} }
@ -187,7 +203,7 @@ define(function () {
}; };
self.removeTarget = function (d) { self.removeTarget = function (d) {
targets = targets.filter( function (e) { targets = targets.filter(function (e) {
return d !== e; return d !== e;
}); });
}; };
@ -200,11 +216,11 @@ define(function () {
objects.nodes = {}; objects.nodes = {};
objects.links = {}; objects.links = {};
data.nodes.all.forEach( function (d) { data.nodes.all.forEach(function (d) {
objects.nodes[d.nodeinfo.node_id] = d; objects.nodes[d.nodeinfo.node_id] = d;
}); });
data.graph.links.forEach( function (d) { data.graph.links.forEach(function (d) {
objects.links[d.id] = d; objects.links[d.id] = d;
}); });
}; };

View File

@ -19,8 +19,9 @@ define([], function () {
sidebar.appendChild(container); sidebar.appendChild(container);
self.getWidth = function () { self.getWidth = function () {
if (sidebar.classList.contains("hidden")) if (sidebar.classList.contains("hidden")) {
return 0; return 0;
}
var small = window.matchMedia("(max-width: 630pt)"); var small = window.matchMedia("(max-width: 630pt)");
return small.matches ? 0 : sidebar.offsetWidth; return small.matches ? 0 : sidebar.offsetWidth;

View File

@ -1,5 +1,5 @@
define(["moment", "virtual-dom"], function (moment, V) { define(["moment", "virtual-dom"], function (moment, V) {
return function(nodes, field, router, title) { return function (nodes, field, router, title) {
var self = this; var self = this;
var el, tbody; var el, tbody;
@ -12,8 +12,9 @@ define(["moment", "virtual-dom"], function (moment, V) {
var list = data.nodes[nodes]; var list = data.nodes[nodes];
if (list.length === 0) { if (list.length === 0) {
while (el.firstChild) while (el.firstChild) {
el.removeChild(el.firstChild); el.removeChild(el.firstChild);
}
tbody = null; tbody = null;
@ -33,19 +34,21 @@ define(["moment", "virtual-dom"], function (moment, V) {
table.appendChild(tbody); table.appendChild(tbody);
} }
var items = list.map( function (d) { var items = list.map(function (d) {
var time = moment(d[field]).from(data.now); var time = moment(d[field]).from(data.now);
var td1Content = []; var td1Content = [];
var aClass = ["hostname", d.flags.online ? "online" : "offline"]; var aClass = ["hostname", d.flags.online ? "online" : "offline"];
td1Content.push(V.h("a", { className: aClass.join(" "), td1Content.push(V.h("a", {
onclick: router.node(d), className: aClass.join(" "),
href: "#" onclick: router.node(d),
}, d.nodeinfo.hostname)); href: "#"
}, d.nodeinfo.hostname));
if (has_location(d)) if (has_location(d)) {
td1Content.push(V.h("span", {className: "icon ion-location"})); td1Content.push(V.h("span", {className: "icon ion-location"}));
}
var td1 = V.h("td", td1Content); var td1 = V.h("td", td1Content);
var td2 = V.h("td", time); var td2 = V.h("td", time);

View File

@ -1,5 +1,5 @@
define(["virtual-dom"], function (V) { define(["virtual-dom"], function (V) {
return function(headings, sortIndex, renderRow) { return function (headings, sortIndex, renderRow) {
var data; var data;
var sortReverse = false; var sortReverse = false;
var el = document.createElement("table"); var el = document.createElement("table");
@ -13,7 +13,9 @@ define(["virtual-dom"], function (V) {
} }
function sortTableHandler(i) { function sortTableHandler(i) {
return function () { sortTable(i); }; return function () {
sortTable(i);
};
} }
function updateView() { function updateView() {
@ -21,20 +23,23 @@ define(["virtual-dom"], function (V) {
if (data.length !== 0) { if (data.length !== 0) {
var th = headings.map(function (d, i) { var th = headings.map(function (d, i) {
var properties = { onclick: sortTableHandler(i), var properties = {
className: "sort-header" onclick: sortTableHandler(i),
}; className: "sort-header"
};
if (sortIndex === i) if (sortIndex === i) {
properties.className += sortReverse ? " sort-up" : " sort-down"; properties.className += sortReverse ? " sort-up" : " sort-down";
}
return V.h("th", properties, d.name); return V.h("th", properties, d.name);
}); });
var links = data.slice(0).sort(headings[sortIndex].sort); var links = data.slice(0).sort(headings[sortIndex].sort);
if (headings[sortIndex].reverse ? !sortReverse : sortReverse) if (headings[sortIndex].reverse ? !sortReverse : sortReverse) {
links = links.reverse(); links = links.reverse();
}
children.push(V.h("thead", V.h("tr", th))); children.push(V.h("thead", V.h("tr", th)));
children.push(V.h("tbody", links.map(renderRow))); children.push(V.h("tbody", links.map(renderRow)));

View File

@ -8,11 +8,13 @@ define([], function () {
var container = document.createElement("div"); var container = document.createElement("div");
function gotoTab(li) { function gotoTab(li) {
for (var i = 0; i < tabs.children.length; i++) for (var i = 0; i < tabs.children.length; i++) {
tabs.children[i].classList.remove("visible"); tabs.children[i].classList.remove("visible");
}
while (container.firstChild) while (container.firstChild) {
container.removeChild(container.firstChild); container.removeChild(container.firstChild);
}
li.classList.add("visible"); li.classList.add("visible");
@ -37,14 +39,16 @@ define([], function () {
var anyVisible = false; var anyVisible = false;
for (var i = 0; i < tabs.children.length; i++) for (var i = 0; i < tabs.children.length; i++) {
if (tabs.children[i].classList.contains("visible")) { if (tabs.children[i].classList.contains("visible")) {
anyVisible = true; anyVisible = true;
break; break;
} }
}
if (!anyVisible) if (!anyVisible) {
gotoTab(li); gotoTab(li);
}
}; };
self.render = function (el) { self.render = function (el) {

View File

@ -1,10 +1,11 @@
define(function () { define(function () {
return function (config) { return function (config) {
function setTitle(d) { function setTitle(d) {
var title = [config.siteName]; var title = [config.siteName];
if (d !== undefined) if (d !== undefined) {
title.push(d); title.push(d);
}
document.title = title.join(": "); document.title = title.join(": ");
} }
@ -14,16 +15,18 @@ define(function () {
}; };
this.gotoNode = function (d) { this.gotoNode = function (d) {
if (d) if (d) {
setTitle(d.nodeinfo.hostname); setTitle(d.nodeinfo.hostname);
}
}; };
this.gotoLink = function (d) { this.gotoLink = function (d) {
if (d) if (d) {
setTitle((d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " " + d.target.node.nodeinfo.hostname); setTitle((d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " " + d.target.node.nodeinfo.hostname);
}
}; };
this.gotoLocation = function() { this.gotoLocation = function () {
//ignore //ignore
}; };

View File

@ -1,15 +1,16 @@
define([], function () { define([], function () {
function order(c) { function order(c) {
if (/^\d$/.test(c)) if (/^\d$/.test(c)) {
return 0; return 0;
else if (/^[a-z]$/i.test(c)) } else if (/^[a-z]$/i.test(c)) {
return c.charCodeAt(0); return c.charCodeAt(0);
else if (c === "~") } else if (c === "~") {
return -1; return -1;
else if (c) } else if (c) {
return c.charCodeAt(0) + 256; return c.charCodeAt(0) + 256;
else } else {
return 0; return 0;
}
} }
// Based on dpkg code // Based on dpkg code
@ -22,35 +23,42 @@ define([], function () {
var ac = order(a[apos]); var ac = order(a[apos]);
var bc = order(b[bpos]); var bc = order(b[bpos]);
if (ac !== bc) if (ac !== bc) {
return ac - bc; return ac - bc;
}
apos++; apos++;
bpos++; bpos++;
} }
while (a[apos] === "0") while (a[apos] === "0") {
apos++; apos++;
}
while (b[bpos] === "0") while (b[bpos] === "0") {
bpos++; bpos++;
}
while (/^\d$/.test(a[apos]) && /^\d$/.test(b[bpos])) { while (/^\d$/.test(a[apos]) && /^\d$/.test(b[bpos])) {
if (firstDiff === 0) if (firstDiff === 0) {
firstDiff = a.charCodeAt(apos) - b.charCodeAt(bpos); firstDiff = a.charCodeAt(apos) - b.charCodeAt(bpos);
}
apos++; apos++;
bpos++; bpos++;
} }
if (/^\d$/.test(a[apos])) if (/^\d$/.test(a[apos])) {
return 1; return 1;
}
if (/^\d$/.test(b[bpos])) if (/^\d$/.test(b[bpos])) {
return -1; return -1;
}
if (firstDiff !== 0) if (firstDiff !== 0) {
return firstDiff; return firstDiff;
}
} }
return 0; return 0;

View File

@ -1,4 +1,4 @@
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.config.merge({ grunt.config.merge({
bowerdir: "bower_components", bowerdir: "bower_components",
copy: { copy: {
@ -19,37 +19,37 @@ module.exports = function(grunt) {
dest: "build/" dest: "build/"
}, },
vendorjs: { vendorjs: {
src: [ "es6-shim/es6-shim.min.js" ], src: ["es6-shim/es6-shim.min.js"],
expand: true, expand: true,
cwd: "bower_components/", cwd: "bower_components/",
dest: "build/vendor/" dest: "build/vendor/"
}, },
robotoSlab: { robotoSlab: {
src: [ "fonts/*", src: ["fonts/*",
"roboto-slab-fontface.css" "roboto-slab-fontface.css"
], ],
expand: true, expand: true,
dest: "build/", dest: "build/",
cwd: "bower_components/roboto-slab-fontface" cwd: "bower_components/roboto-slab-fontface"
}, },
roboto: { roboto: {
src: [ "fonts/*", src: ["fonts/*",
"roboto-fontface.css" "roboto-fontface.css"
], ],
expand: true, expand: true,
dest: "build/", dest: "build/",
cwd: "bower_components/roboto-fontface" cwd: "bower_components/roboto-fontface"
}, },
ionicons: { ionicons: {
src: [ "fonts/*", src: ["fonts/*",
"css/ionicons.min.css" "css/ionicons.min.css"
], ],
expand: true, expand: true,
dest: "build/", dest: "build/",
cwd: "bower_components/ionicons/" cwd: "bower_components/ionicons/"
}, },
leafletImages: { leafletImages: {
src: [ "images/*" ], src: ["images/*"],
expand: true, expand: true,
dest: "build/", dest: "build/",
cwd: "bower_components/leaflet/dist/" cwd: "bower_components/leaflet/dist/"
@ -82,26 +82,26 @@ module.exports = function(grunt) {
cssmin: { cssmin: {
target: { target: {
files: { files: {
"build/style.css": [ "bower_components/leaflet/dist/leaflet.css", "build/style.css": ["bower_components/leaflet/dist/leaflet.css",
"bower_components/Leaflet.label/dist/leaflet.label.css", "bower_components/Leaflet.label/dist/leaflet.label.css",
"style.css" "style.css"
] ]
} }
} }
}, },
"bower-install-simple": { "bower-install-simple": {
options: { options: {
directory: "<%=bowerdir%>", directory: "<%=bowerdir%>",
color: true, color: true,
interactive: false, interactive: false,
production: true production: true
},
"prod": {
options: {
production: true
}
}
}, },
"prod": {
options: {
production: true
}
}
},
requirejs: { requirejs: {
compile: { compile: {
options: { options: {

View File

@ -14,7 +14,6 @@ module.exports = function (grunt) {
eslint: { eslint: {
options: { options: {
rules: { rules: {
"curly": [2, "multi"],
"strict": [2, "never"], "strict": [2, "never"],
"no-multi-spaces": 0, "no-multi-spaces": 0,
"no-new": 0, "no-new": 0,