diff --git a/Gruntfile.js b/Gruntfile.js index 6d034b7..6fcc054 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,7 +8,7 @@ module.exports = function (grunt) { } }); - grunt.registerTask("saveRevision", function() { + grunt.registerTask("saveRevision", function () { grunt.event.once("git-describe", function (rev) { grunt.option("gitRevision", rev); }); diff --git a/build.js b/build.js index 1295a7d..41f588b 100644 --- a/build.js +++ b/build.js @@ -1,9 +1,9 @@ ({ - baseUrl: "lib", - name: "../bower_components/almond/almond", - mainConfigFile: "app.js", - include: "../app", - wrap: true, - optimize: "uglify", - out: "app-combined.js" + baseUrl: "lib", + name: "../bower_components/almond/almond", + mainConfigFile: "app.js", + include: "../app", + wrap: true, + optimize: "uglify", + out: "app-combined.js" }); diff --git a/config.json.example b/config.json.example index 933ba02..6909ea9 100644 --- a/config.json.example +++ b/config.json.example @@ -13,9 +13,21 @@ } ], "siteNames": [ - { "site": "ffhl", "name": "Lübeck" }, - { "site": "ffeh", "name": "Entenhausen" }, - { "site": "ffgt", "name": "Gothamcity" }, - { "site": "ffal", "name": "Atlantis" } + { + "site": "ffhl", + "name": "Lübeck" + }, + { + "site": "ffeh", + "name": "Entenhausen" + }, + { + "site": "ffgt", + "name": "Gothamcity" + }, + { + "site": "ffal", + "name": "Atlantis" + } ] } diff --git a/helper.js b/helper.js index 949d7d0..d95ddb3 100644 --- a/helper.js +++ b/helper.js @@ -1,9 +1,9 @@ function get(url) { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', url); - req.onload = function() { + req.onload = function () { if (req.status == 200) { resolve(req.response); } @@ -12,7 +12,7 @@ function get(url) { } }; - req.onerror = function() { + req.onerror = function () { reject(Error("Network Error")); }; @@ -25,19 +25,19 @@ function getJSON(url) { } function sortByKey(key, d) { - return d.slice().sort( function (a, b) { + return d.slice().sort(function (a, b) { return a[key] - b[key] }).reverse() } function limit(key, m, d) { - return d.filter( function (d) { + return d.filter(function (d) { return d[key].isAfter(m) }) } function sum(a) { - return a.reduce( function (a, b) { + return a.reduce(function (a, b) { return a + b }, 0) } @@ -53,11 +53,13 @@ function trueDefault(d) { function dictGet(dict, key) { var k = key.shift(); - if (!(k in dict)) + if (!(k in dict)) { return null; + } - if (key.length == 0) + if (key.length == 0) { return dict[k]; + } return dictGet(dict[k], key) } @@ -68,7 +70,7 @@ function localStorageTest() { localStorage.setItem(test, test); localStorage.removeItem(test); return true - } catch(e) { + } catch (e) { return false } } @@ -93,18 +95,18 @@ function online(d) { function has_location(d) { return "location" in d.nodeinfo && - Math.abs(d.nodeinfo.location.latitude) < 90 && - Math.abs(d.nodeinfo.location.longitude) < 180 + Math.abs(d.nodeinfo.location.latitude) < 90 && + Math.abs(d.nodeinfo.location.longitude) < 180 } function subtract(a, b) { var ids = {}; - b.forEach( function (d) { + b.forEach(function (d) { ids[d.nodeinfo.node_id] = true }); - return a.filter( function (d) { + return a.filter(function (d) { return !(d.nodeinfo.node_id in ids) }) } @@ -112,21 +114,23 @@ function subtract(a, b) { /* Helpers working with links */ function showDistance(d) { - if (isNaN(d.distance)) + if (isNaN(d.distance)) { return; + } return numeral(d.distance).format("0,0") + " m" } function showTq(d) { - return numeral(1/d.tq).format("0%") + return numeral(1 / d.tq).format("0%") } /* Infobox stuff (XXX: move to module) */ function attributeEntry(el, label, value) { - if (value === null || value == undefined) + if (value === null || value == undefined) { return; + } var tr = document.createElement("tr"); var th = document.createElement("th"); @@ -135,10 +139,11 @@ function attributeEntry(el, label, value) { var td = document.createElement("td"); - if (typeof value == "function") + if (typeof value == "function") { value(td); - else + } else { td.appendChild(document.createTextNode(value)); + } tr.appendChild(td); @@ -152,25 +157,29 @@ function createIframe(opt, width, height) { width = typeof width !== 'undefined' ? width : '525px'; height = typeof height !== 'undefined' ? height : '350px'; - if (opt.src) + if (opt.src) { el.src = opt.src; - else + } else { el.src = opt; + } - if (opt.frameBorder) + if (opt.frameBorder) { el.frameBorder = opt.frameBorder; - else + } else { el.frameBorder = 1; + } - if (opt.width) + if (opt.width) { el.width = opt.width; - else + } else { el.width = width; + } - if (opt.height) + if (opt.height) { el.height = opt.height; - else + } else { el.height = height; + } el.scrolling = "no"; el.seamless = "seamless"; @@ -190,16 +199,18 @@ function showStat(o, subst) { if (o.caption) { caption = listReplace(o.caption, subst); - if (!content) - content = document.createTextNode(caption) + if (!content) { + content = document.createTextNode(caption) + } } if (o.iframe) { content = createIframe(o.iframe, o.width, o.height); - if (o.iframe.src) - content.src = listReplace(o.iframe.src, subst); - else - content.src = listReplace(o.iframe, subst) + if (o.iframe.src) { + content.src = listReplace(o.iframe.src, subst); + } else { + content.src = listReplace(o.iframe, subst) + } } var p = document.createElement("p"); @@ -210,12 +221,14 @@ function showStat(o, subst) { link.href = listReplace(o.href, subst); link.appendChild(content); - if (caption && o.thumbnail) - link.title = caption; + if (caption && o.thumbnail) { + link.title = caption; + } p.appendChild(link) - } else - p.appendChild(content); + } else { + p.appendChild(content); + } return p } diff --git a/html/index.html b/html/index.html index fc4ff6e..676cc92 100644 --- a/html/index.html +++ b/html/index.html @@ -1,18 +1,18 @@ -
- - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/index.html b/index.html index fcbf858..0644f3c 100644 --- a/index.html +++ b/index.html @@ -1,17 +1,17 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/lib/about.js b/lib/about.js index 2e80987..2a20ae5 100644 --- a/lib/about.js +++ b/lib/about.js @@ -1,5 +1,5 @@ define(function () { - return function() { + return function () { this.render = function (d) { var el = document.createElement("div"); d.appendChild(el); diff --git a/lib/container.js b/lib/container.js index cbdb3f5..4f32a84 100644 --- a/lib/container.js +++ b/lib/container.js @@ -1,7 +1,8 @@ define([], function () { return function (tag) { - if (!tag) + if (!tag) { tag = "div"; + } var self = this; diff --git a/lib/datadistributor.js b/lib/datadistributor.js index e7df6b8..3edeaf6 100644 --- a/lib/datadistributor.js +++ b/lib/datadistributor.js @@ -7,14 +7,17 @@ define(["filters/nodefilter"], function (NodeFilter) { var data; function remove(d) { - targets = targets.filter( function (e) { return d !== e; } ); + targets = targets.filter(function (e) { + return d !== e; + }); } function add(d) { targets.push(d); - if (filteredData !== undefined) + if (filteredData !== undefined) { d.setData(filteredData); + } } function setData(d) { @@ -23,24 +26,27 @@ define(["filters/nodefilter"], function (NodeFilter) { } function refresh() { - if (data === undefined) + if (data === undefined) { return; + } - var filter = filters.reduce( function (a, f) { + var filter = filters.reduce(function (a, f) { return function (d) { return a(d) && f.run(d); }; - }, function () { return true; }); + }, function () { + return true; + }); filteredData = new NodeFilter(filter)(data); - targets.forEach( function (t) { + targets.forEach(function (t) { t.setData(filteredData); }); } function notifyObservers() { - filterObservers.forEach( function (d) { + filterObservers.forEach(function (d) { d.filtersChanged(filters); }); } @@ -53,7 +59,9 @@ define(["filters/nodefilter"], function (NodeFilter) { } function removeFilter(d) { - filters = filters.filter( function (e) { return d !== e; } ); + filters = filters.filter(function (e) { + return d !== e; + }); notifyObservers(); refresh(); } @@ -64,17 +72,20 @@ define(["filters/nodefilter"], function (NodeFilter) { d.filtersChanged(filters); return function () { - filterObservers = filterObservers.filter( function (e) { return d !== e; }); + filterObservers = filterObservers.filter(function (e) { + return d !== e; + }); }; } - return { add: add, - remove: remove, - setData: setData, - addFilter: addFilter, - removeFilter: removeFilter, - watchFilters: watchFilters, - refresh: refresh - }; + return { + add: add, + remove: remove, + setData: setData, + addFilter: addFilter, + removeFilter: removeFilter, + watchFilters: watchFilters, + refresh: refresh + }; }; }); diff --git a/lib/filters/filtergui.js b/lib/filters/filtergui.js index 600deec..10629d7 100644 --- a/lib/filters/filtergui.js +++ b/lib/filters/filtergui.js @@ -9,10 +9,11 @@ define([], function () { } function filtersChanged(filters) { - while (container.firstChild) + while (container.firstChild) { container.removeChild(container.firstChild); + } - filters.forEach( function (d) { + filters.forEach(function (d) { var li = document.createElement("li"); var div = document.createElement("div"); container.appendChild(li); @@ -27,14 +28,16 @@ define([], function () { li.appendChild(button); }); - if (container.parentNode === div && filters.length === 0) + if (container.parentNode === div && filters.length === 0) { div.removeChild(container); - else if (filters.length > 0) + } else if (filters.length > 0) { div.appendChild(container); + } } - return { render: render, - filtersChanged: filtersChanged - }; + return { + render: render, + filtersChanged: filtersChanged + }; }; }); diff --git a/lib/filters/genericnode.js b/lib/filters/genericnode.js index 900da26..831f575 100644 --- a/lib/filters/genericnode.js +++ b/lib/filters/genericnode.js @@ -11,8 +11,9 @@ define([], function () { function run(d) { var o = dictGet(d, key.slice(0)); - if (f) + if (f) { o = f(o); + } return o === value ? !negate : negate; } @@ -22,10 +23,11 @@ define([], function () { } function draw(el) { - if (negate) + if (negate) { el.parentNode.classList.add("not"); - else + } else { el.parentNode.classList.remove("not"); + } strong.textContent = (negate ? "¬" : "" ) + value; } @@ -39,14 +41,16 @@ define([], function () { draw(el); - if (refresh) + if (refresh) { refresh(); + } }; } - return { run: run, - setRefresh: setRefresh, - render: render - }; + return { + run: run, + setRefresh: setRefresh, + render: render + }; }; }); diff --git a/lib/filters/nodefilter.js b/lib/filters/nodefilter.js index ac07d7b..1d6bf5f 100644 --- a/lib/filters/nodefilter.js +++ b/lib/filters/nodefilter.js @@ -11,20 +11,22 @@ define([], function () { var filteredIds = new Set(); n.graph = {}; - n.graph.nodes = data.graph.nodes.filter( function (d) { + n.graph.nodes = data.graph.nodes.filter(function (d) { var r; - if (d.node) + if (d.node) { r = filter(d.node); - else + } else { r = filter({}); + } - if (r) + if (r) { filteredIds.add(d.id); + } 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); }); diff --git a/lib/forcegraph.js b/lib/forcegraph.js index bb5af85..3958be8 100644 --- a/lib/forcegraph.js +++ b/lib/forcegraph.js @@ -32,21 +32,23 @@ define(["d3"], function (d3) { } function savePositions() { - if (!localStorageTest()) + if (!localStorageTest()) { return; + } - var save = intNodes.map( function (d) { - return { id: d.o.id, x: d.x, y: d.y }; + var save = intNodes.map(function (d) { + return {id: d.o.id, x: d.x, y: d.y}; }); localStorage.setItem("graph/nodeposition", JSON.stringify(save)); } 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; - else + } else { return d.o.id; + } } function dragstart() { @@ -56,8 +58,9 @@ define(["d3"], function (d3) { return distancePoint(e, d) < NODE_RADIUS; }); - if (nodes.length === 0) + if (nodes.length === 0) { return; + } draggedNode = nodes[0]; d3.event.sourceEvent.stopPropagation(); @@ -88,9 +91,9 @@ define(["d3"], function (d3) { } var draggableNode = d3.behavior.drag() - .on("dragstart", dragstart) - .on("drag", dragmove) - .on("dragend", dragend); + .on("dragstart", dragstart) + .on("drag", dragmove) + .on("dragend", dragend); function animatePanzoom(translate, scale) { var translateP = zoomBehavior.translate(); @@ -110,8 +113,9 @@ define(["d3"], function (d3) { var ease = d3.ease("cubic-in-out"); d3.timer(function (t) { - if (t >= duration) + if (t >= duration) { return true; + } var v = interpolate(ease(t / duration)); zoomBehavior.translate([v.x, v.y]); @@ -124,8 +128,10 @@ define(["d3"], function (d3) { } function onPanZoom() { - savedPanZoom = {translate: zoomBehavior.translate(), - scale: zoomBehavior.scale()}; + savedPanZoom = { + translate: zoomBehavior.translate(), + scale: zoomBehavior.scale() + }; panzoom(); } @@ -133,14 +139,15 @@ define(["d3"], function (d3) { var translate = zoomBehavior.translate(); var scale = zoomBehavior.scale(); - panzoomReal(translate, scale); } function panzoomReal(translate, scale) { - screenRect = {left: -translate[0] / scale, top: -translate[1] / scale, - right: (canvas.width - translate[0]) / scale, - bottom: (canvas.height - translate[1]) / scale}; + screenRect = { + left: -translate[0] / scale, top: -translate[1] / scale, + right: (canvas.width - translate[0]) / scale, + bottom: (canvas.height - translate[1]) / scale + }; requestAnimationFrame(redraw); } @@ -177,15 +184,16 @@ define(["d3"], function (d3) { highlightedNodes = []; highlightedLinks = []; - if (highlight !== undefined) + if (highlight !== undefined) { if (highlight.type === "node") { var n = nodesDict[highlight.o.nodeinfo.node_id]; if (n) { highlightedNodes = [n]; - if (!nopanzoom) + if (!nopanzoom) { panzoomTo([n.x, n.y], [n.x, n.y]); + } } return; @@ -196,20 +204,27 @@ define(["d3"], function (d3) { highlightedLinks = [l]; if (!nopanzoom) { - var x = d3.extent([l.source, l.target], function (d) { return d.x; }); - var y = d3.extent([l.source, l.target], function (d) { return d.y; }); + var x = d3.extent([l.source, l.target], function (d) { + 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]]); } } return; } + } - if (!nopanzoom) - if (!savedPanZoom) + if (!nopanzoom) { + if (!savedPanZoom) { panzoomTo([0, 0], force.size()); - else + } else { animatePanzoom(savedPanZoom.translate, savedPanZoom.scale); + } + } } function drawLabel(d) { @@ -226,8 +241,9 @@ define(["d3"], function (d3) { var angle = Math.PI / 2; - if (neighbours.length > 0) + if (neighbours.length > 0) { angle = Math.PI + Math.atan2(sumSin, sumCos); + } var cos = Math.cos(angle); var sin = Math.sin(angle); @@ -243,14 +259,14 @@ define(["d3"], function (d3) { function visibleLinks(d) { return (d.source.x > screenRect.left && d.source.x < screenRect.right && - d.source.y > screenRect.top && d.source.y < screenRect.bottom) || - (d.target.x > screenRect.left && d.target.x < screenRect.right && - d.target.y > screenRect.top && d.target.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.y > screenRect.top && d.target.y < screenRect.bottom); } function visibleNodes(d) { 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) { @@ -351,7 +367,6 @@ define(["d3"], function (d3) { ctx.stroke(); - // -- draw nodes -- ctx.save(); ctx.scale(1 / scale / r, 1 / scale / r); @@ -378,8 +393,9 @@ define(["d3"], function (d3) { ctx.beginPath(); nodes.filter(visibleNodes).forEach(function (d) { var clients = d.o.node.statistics.clients; - if (clients === 0) + if (clients === 0) { return; + } var startDistance = 16; var radius = 3; @@ -449,8 +465,9 @@ define(["d3"], function (d3) { } // -- draw labels -- - if (scale > 0.9) + if (scale > 0.9) { intNodes.filter(visibleNodes).forEach(drawLabel, scale); + } ctx.restore(); } @@ -483,33 +500,40 @@ define(["d3"], function (d3) { var l2 = distance(a, b); - if (l2 === 0) + if (l2 === 0) { return distance(p, a); + } 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); + } - if (t > 1) + if (t > 1) { return distance(p, b); + } - return Math.sqrt(distance(p, { x: a.x + t * (b.x - a.x), - y: a.y + t * (b.y - a.y) })); + return Math.sqrt(distance(p, { + x: a.x + t * (b.x - a.x), + y: a.y + t * (b.y - a.y) + })); } function translateXY(d) { var translate = zoomBehavior.translate(); var scale = zoomBehavior.scale(); - return {x: (d[0] - translate[0]) / scale, - y: (d[1] - translate[1]) / scale - }; + return { + x: (d[0] - translate[0]) / scale, + y: (d[1] - translate[1]) / scale + }; } function onClick() { - if (d3.event.defaultPrevented) + if (d3.event.defaultPrevented) { return; + } var e = translateXY(d3.mouse(el)); @@ -550,14 +574,17 @@ define(["d3"], function (d3) { return function () { var e = d3.event; - if (e.altKey || e.ctrlKey || e.metaKey) + if (e.altKey || e.ctrlKey || e.metaKey) { return; + } - if (e.keyCode === 43) + if (e.keyCode === 43) { zoom(z, 1.41); + } - if (e.keyCode === 45) + if (e.keyCode === 45) { zoom(z, 1 / 1.41); + } }; } @@ -565,38 +592,40 @@ define(["d3"], function (d3) { el.classList.add("graph"); zoomBehavior = d3.behavior.zoom() - .scaleExtent([1 / 3, 3]) - .on("zoom", onPanZoom) - .translate([sidebar(), 0]); + .scaleExtent([1 / 3, 3]) + .on("zoom", onPanZoom) + .translate([sidebar(), 0]); canvas = d3.select(el) - .attr("tabindex", 1) - .on("keypress", keyboardZoom(zoomBehavior)) - .call(zoomBehavior) - .append("canvas") - .on("click", onClick) - .call(draggableNode) - .node(); + .attr("tabindex", 1) + .on("keypress", keyboardZoom(zoomBehavior)) + .call(zoomBehavior) + .append("canvas") + .on("click", onClick) + .call(draggableNode) + .node(); ctx = canvas.getContext("2d"); force = d3.layout.force() - .charge(-250) - .gravity(0.1) - .linkDistance(function (d) { - if (d.o.type === "fastd" || d.o.type === "L2TP") - return 0; - else - return LINK_DISTANCE; - }) - .linkStrength(function (d) { - if (d.o.type === "fastd" || d.o.type === "L2TP") - return 0; - else - return Math.max(0.5, 1 / d.o.tq); - }) - .on("tick", tickEvent) - .on("end", savePositions); + .charge(-250) + .gravity(0.1) + .linkDistance(function (d) { + if (d.o.type === "fastd" || d.o.type === "L2TP") { + return 0; + } else { + return LINK_DISTANCE; + } + }) + .linkStrength(function (d) { + if (d.o.type === "fastd" || d.o.type === "L2TP") { + return 0; + } else { + return Math.max(0.5, 1 / d.o.tq); + } + }) + .on("tick", tickEvent) + .on("end", savePositions); window.addEventListener("resize", resizeCanvas); @@ -605,16 +634,17 @@ define(["d3"], function (d3) { self.setData = function (data) { var oldNodes = {}; - intNodes.forEach( function (d) { + intNodes.forEach(function (d) { oldNodes[d.o.id] = d; }); - intNodes = data.graph.nodes.map( function (d) { + intNodes = data.graph.nodes.map(function (d) { var e; - if (d.id in oldNodes) + if (d.id in oldNodes) { e = oldNodes[d.id]; - else + } else { e = {}; + } e.o = d; @@ -623,31 +653,33 @@ define(["d3"], function (d3) { var newNodesDict = {}; - intNodes.forEach( function (d) { + intNodes.forEach(function (d) { newNodesDict[d.o.id] = d; }); var oldLinks = {}; - intLinks.forEach( function (d) { + intLinks.forEach(function (d) { oldLinks[d.o.id] = d; }); - intLinks = data.graph.links.map( function (d) { + intLinks = data.graph.links.map(function (d) { var e; - if (d.id in oldLinks) + if (d.id in oldLinks) { e = oldLinks[d.id]; - else + } else { e = {}; + } e.o = d; e.source = newNodesDict[d.source.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) + ")"; - else + } else { e.color = linkScale(d.tq).hex(); + } return e; }); @@ -658,8 +690,9 @@ define(["d3"], function (d3) { intNodes.forEach(function (d) { d.neighbours = {}; - if (d.o.node) + if (d.o.node) { nodesDict[d.o.node.nodeinfo.node_id] = 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.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; + } }); intNodes.forEach(function (d) { @@ -702,22 +736,32 @@ define(["d3"], function (d3) { }); }); - nodes = intNodes.filter(function (d) { return !d.o.unseen && d.o.node; }); - uplinkNodes = nodes.filter(function (d) { 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; }); + nodes = intNodes.filter(function (d) { + return !d.o.unseen && d.o.node; + }); + uplinkNodes = nodes.filter(function (d) { + 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()) { var save = JSON.parse(localStorage.getItem("graph/nodeposition")); if (save) { var nodePositions = {}; - save.forEach( function (d) { + save.forEach(function (d) { nodePositions[d.id] = d; }); - intNodes.forEach( function (d) { + intNodes.forEach(function (d) { if (nodePositions[d.o.id] && (d.x === undefined || d.y === undefined)) { d.x = nodePositions[d.o.id].x; d.y = nodePositions[d.o.id].y; @@ -729,8 +773,8 @@ define(["d3"], function (d3) { var diameter = graphDiameter(intNodes); force.nodes(intNodes) - .links(intLinks) - .size([diameter, diameter]); + .links(intLinks) + .size([diameter, diameter]); updateHighlight(true); @@ -761,8 +805,9 @@ define(["d3"], function (d3) { canvas.remove(); force = null; - if (el.parentNode) + if (el.parentNode) { el.parentNode.removeChild(el); + } }; self.render = function (d) { diff --git a/lib/gui.js b/lib/gui.js index d8b3052..8379895 100644 --- a/lib/gui.js +++ b/lib/gui.js @@ -1,122 +1,124 @@ -define([ "chroma-js", "map", "sidebar", "tabs", "container", "meshstats", - "legend", "linklist", "nodelist", "simplenodelist", "infobox/main", - "proportions", "forcegraph", "title", "about", "datadistributor", - "filters/filtergui" ], -function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist, - Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph, - Title, About, DataDistributor, FilterGUI) { - return function (config, router) { - var self = this; - var content; - var contentDiv; +define(["chroma-js", "map", "sidebar", "tabs", "container", "meshstats", + "legend", "linklist", "nodelist", "simplenodelist", "infobox/main", + "proportions", "forcegraph", "title", "about", "datadistributor", + "filters/filtergui"], + function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist, + Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph, + Title, About, DataDistributor, FilterGUI) { + return function (config, router) { + var self = this; + var content; + var contentDiv; - var linkScale = chroma.scale(chroma.interpolate.bezier(["#04C714", "#FF5500", "#F02311"])).domain([1, 5]); - var sidebar; + var linkScale = chroma.scale(chroma.interpolate.bezier(["#04C714", "#FF5500", "#F02311"])).domain([1, 5]); + var sidebar; - var buttons = document.createElement("div"); - buttons.classList.add("buttons"); + var buttons = document.createElement("div"); + buttons.classList.add("buttons"); - var fanout = new DataDistributor(); - var fanoutUnfiltered = new DataDistributor(); - fanoutUnfiltered.add(fanout); + var fanout = new DataDistributor(); + var fanoutUnfiltered = new DataDistributor(); + fanoutUnfiltered.add(fanout); - function removeContent() { - if (!content) - return; + function removeContent() { + if (!content) { + return; + } - router.removeTarget(content); - fanout.remove(content); + router.removeTarget(content); + fanout.remove(content); - content.destroy(); + content.destroy(); - content = null; - } + content = null; + } - function addContent(K) { - removeContent(); + function addContent(K) { + removeContent(); - content = new K(config, linkScale, sidebar.getWidth, router, buttons); - content.render(contentDiv); + content = new K(config, linkScale, sidebar.getWidth, router, buttons); + content.render(contentDiv); - fanout.add(content); - router.addTarget(content); - } + fanout.add(content); + router.addTarget(content); + } - function mkView(K) { - return function () { - addContent(K); + function mkView(K) { + return function () { + 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"); - contentDiv.classList.add("content"); - document.body.appendChild(contentDiv); + buttons.appendChild(buttonToggle); - 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"); - buttonToggle.textContent = "\uF133"; - buttonToggle.onclick = function () { - if (content.constructor === Map) - router.view("g"); - else - router.view("m"); + 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; }; - - 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; - }; -}); + }); diff --git a/lib/infobox/link.js b/lib/infobox/link.js index a57efd8..8554cb3 100644 --- a/lib/infobox/link.js +++ b/lib/infobox/link.js @@ -38,7 +38,7 @@ define(function () { if (config.linkInfos) { var source = d.source.node_id; var target = d.target.node_id; - config.linkInfos.forEach( function (linkInfo) { + config.linkInfos.forEach(function (linkInfo) { var h4 = document.createElement("h4"); h4.textContent = linkInfo.name; el.appendChild(h4); diff --git a/lib/infobox/location.js b/lib/infobox/location.js index a9c3034..9910d33 100644 --- a/lib/infobox/location.js +++ b/lib/infobox/location.js @@ -5,10 +5,11 @@ define(function () { el.appendChild(sidebarTitle); getJSON("https://nominatim.openstreetmap.org/reverse?format=json&lat=" + d.lat + "&lon=" + d.lng + "&zoom=18&addressdetails=0") - .then(function(result) { - if(result.display_name) - sidebarTitle.textContent = result.display_name; - }); + .then(function (result) { + if (result.display_name) { + sidebarTitle.textContent = result.display_name; + } + }); var editLat = document.createElement("input"); editLat.type = "text"; @@ -32,7 +33,7 @@ define(function () { var linkPlain = document.createElement("a"); linkPlain.textContent = "plain"; - linkPlain.onclick = function() { + linkPlain.onclick = function () { switch2plain(); return false; }; @@ -40,7 +41,7 @@ define(function () { var linkUci = document.createElement("a"); linkUci.textContent = "uci"; - linkUci.onclick = function() { + linkUci.onclick = function () { switch2uci(); return false; }; @@ -55,7 +56,7 @@ define(function () { el.appendChild(hintText); 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 heading = document.createElement("h3"); heading.textContent = title; @@ -63,7 +64,9 @@ define(function () { var btn = document.createElement("button"); btn.className = "ion-ios-copy"; btn.title = "Kopieren"; - btn.onclick = function() { copy2clip(inputElem.id); }; + btn.onclick = function () { + copy2clip(inputElem.id); + }; inputElem.id = "location-" + name; inputElem.readOnly = true; var line = document.createElement("p"); diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 2588f88..25dba43 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -1,367 +1,385 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"], function (moment, numeral, Tablesort) { - function showGeoURI(d) { - function showLatitude(d) { - var suffix = Math.sign(d) > -1 ? "' N" : "' S"; - d = Math.abs(d); - var a = Math.floor(d); - var min = (d * 60) % 60; - a = (a < 10 ? "0" : "") + a; + function showGeoURI(d) { + function showLatitude(d) { + var suffix = Math.sign(d) > -1 ? "' N" : "' S"; + d = Math.abs(d); + var a = Math.floor(d); + var min = (d * 60) % 60; + 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) { - 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; + function showStatus(d) { + return function (el) { + el.classList.add(d.flags.unseen ? "unseen" : (d.flags.online ? "online" : "offline")); + if (d.flags.online) { + el.textContent = "online, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")"; + } else { + el.textContent = "offline, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")"; + } + }; } - if (!has_location(d)) - return undefined; + function showFirmware(d) { + var release = dictGet(d.nodeinfo, ["software", "firmware", "release"]); + var base = dictGet(d.nodeinfo, ["software", "firmware", "base"]); - 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); + if (release === null || base === null) { + return undefined; + } - a.href = "geo:" + latitude + "," + longitude; - el.appendChild(a); - }; - } + return release + " / " + base; + } - function showStatus(d) { - return function (el) { - el.classList.add(d.flags.unseen ? "unseen" : (d.flags.online ? "online" : "offline")); - if (d.flags.online) - el.textContent = "online, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")"; - else - el.textContent = "offline, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")"; - }; - } + function showSite(d, config) { + 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 showFirmware(d) { - var release = dictGet(d.nodeinfo, ["software", "firmware", "release"]); - var base = dictGet(d.nodeinfo, ["software", "firmware", "base"]); + function showUptime(d) { + if (!("uptime" in d.statistics)) { + return undefined; + } - if (release === null || base === null) - return undefined; + return moment.duration(d.statistics.uptime, "seconds").humanize(); + } - return release + " / " + base; - } + function showFirstseen(d) { + if (!("firstseen" in d)) { + return undefined; + } - function showSite(d, config) { - 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; - } + return d.firstseen.fromNow(true); + } - function showUptime(d) { - if (!("uptime" in d.statistics)) - return undefined; + function showClients(d) { + if (!d.flags.online) { + 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) { - if (!("firstseen" in d)) - return undefined; + var span = document.createElement("span"); + span.classList.add("clients"); + 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) { - if (!d.flags.online) - return undefined; + ips.sort(); - return function (el) { - el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine")); - el.appendChild(document.createElement("br")); + 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("clients"); - span.textContent = " ".repeat(d.statistics.clients); - el.appendChild(span); - }; - } + span.classList.add("bar"); + span.classList.add(className); - function showIPs(d) { - 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 - { + 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; } - var label = document.createElement("label"); - label.textContent = (v); - span.appendChild(label); + function showLoadBar(className, v) { + var span = document.createElement("span"); + 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) { - if (!("loadavg" in d.statistics)) - return undefined; + var label = document.createElement("label"); + label.textContent = (v); + span.appendChild(label); - return function (el) { - el.appendChild(showLoadBar("load-avg", d.statistics.loadavg)); - }; - } + return span; + } - function showRAM(d) { - if (!("memory_usage" in d.statistics)) - return undefined; + function showLoad(d) { + if (!("loadavg" in d.statistics)) { + return undefined; + } - return function (el) { - el.appendChild(showBar("memory-usage", d.statistics.memory_usage)); - }; - } + return function (el) { + el.appendChild(showLoadBar("load-avg", d.statistics.loadavg)); + }; + } - function showPages(d) { - var webpages = dictGet(d.nodeinfo, ["pages"]); - if (webpages === null) - return undefined; + function showRAM(d) { + if (!("memory_usage" in d.statistics)) { + return undefined; + } - webpages.sort(); + return function (el) { + el.appendChild(showBar("memory-usage", d.statistics.memory_usage)); + }; + } - return function (el) { - webpages.forEach( function (webpage, i) { - if (i > 0) - el.appendChild(document.createElement("br")); + function showPages(d) { + var webpages = dictGet(d.nodeinfo, ["pages"]); + if (webpages === null) { + return undefined; + } - var a = document.createElement("span"); - var link = document.createElement("a"); - link.href = webpage; - if (webpage.search(/^https:\/\//i) !== -1) { - var lock = document.createElement("span"); - lock.className = "ion-android-lock"; - a.appendChild(lock); - var t1 = document.createTextNode(" "); - a.appendChild(t1); - link.textContent = webpage.replace(/^https:\/\//i, ""); + webpages.sort(); + + return function (el) { + webpages.forEach(function (webpage, i) { + if (i > 0) { + el.appendChild(document.createElement("br")); } - else - link.textContent = webpage.replace(/^http:\/\//i, ""); - a.appendChild(link); - el.appendChild(a); - }); - }; - } - 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); + var a = document.createElement("span"); + var link = document.createElement("a"); + link.href = webpage; + if (webpage.search(/^https:\/\//i) !== -1) { + var lock = document.createElement("span"); + lock.className = "ion-android-lock"; + 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 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); + } + }; + }); diff --git a/lib/linklist.js b/lib/linklist.js index c50194f..96e7447 100644 --- a/lib/linklist.js +++ b/lib/linklist.js @@ -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; } - var headings = [{ name: "Knoten", - sort: function (a, b) { - return linkName(a).localeCompare(linkName(b)); - }, - reverse: false - }, - { name: "TQ", - sort: function (a, b) { return a.tq - b.tq; }, - reverse: true - }, - { name: "Typ", - sort: function (a, b) { - return a.type.localeCompare(b.type); - }, - reverse: false - }, - { name: "Entfernung", - sort: function (a, b) { - return (a.distance === undefined ? -1 : a.distance) - - (b.distance === undefined ? -1 : b.distance); - }, - reverse: true - }]; + var headings = [{ + name: "Knoten", + sort: function (a, b) { + return linkName(a).localeCompare(linkName(b)); + }, + reverse: false + }, + { + name: "TQ", + sort: function (a, b) { + return a.tq - b.tq; + }, + reverse: true + }, + { + name: "Typ", + sort: function (a, b) { + return a.type.localeCompare(b.type); + }, + reverse: false + }, + { + 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); function renderRow(d) { @@ -41,7 +47,7 @@ define(["sorttable", "virtual-dom"], function (SortTable, V) { return V.h("tr", [td1, td2, td3, td4]); } - this.render = function (d) { + this.render = function (d) { var el = document.createElement("div"); el.last = V.h("div"); d.appendChild(el); diff --git a/lib/locationmarker.js b/lib/locationmarker.js index 97dad4d..0f8c35a 100644 --- a/lib/locationmarker.js +++ b/lib/locationmarker.js @@ -29,28 +29,28 @@ define(["leaflet"], function (L) { fillOpacity: 0.2 }, - initialize: function(latlng) { + initialize: function (latlng) { this.accuracyCircle = L.circle(latlng, 0, this.accuracyCircle); this.outerCircle = L.circleMarker(latlng, this.outerCircle); 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.outerCircle); }); }, - setLatLng: function(latlng) { + setLatLng: function (latlng) { this.accuracyCircle.setLatLng(latlng); this.outerCircle.setLatLng(latlng); L.CircleMarker.prototype.setLatLng.call(this, latlng); }, - setAccuracy: function(accuracy) { + setAccuracy: function (accuracy) { this.accuracyCircle.setRadius(accuracy); }, - onAdd: function(map) { + onAdd: function (map) { this.accuracyCircle.addTo(map).bringToBack(); this.outerCircle.addTo(map); L.CircleMarker.prototype.onAdd.call(this, map); diff --git a/lib/main.js b/lib/main.js index e956e36..e4aad85 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,195 +1,198 @@ define(["moment", "router", "leaflet", "gui", "numeral"], -function (moment, Router, L, GUI, numeral) { - return function (config) { - function handleData(data) { - var dataNodes = {}; - dataNodes.nodes = []; - var dataGraph = {}; - dataGraph.batadv = {}; - dataGraph.batadv.nodes = []; - dataGraph.batadv.links = []; + function (moment, Router, L, GUI, numeral) { + return function (config) { + function handleData(data) { + var dataNodes = {}; + dataNodes.nodes = []; + var dataGraph = {}; + dataGraph.batadv = {}; + dataGraph.batadv.nodes = []; + dataGraph.batadv.links = []; + function rearrangeLinks(d) { + d.source += dataGraph.batadv.nodes.length; + d.target += dataGraph.batadv.nodes.length; + } - function rearrangeLinks(d) { - d.source += dataGraph.batadv.nodes.length; - d.target += dataGraph.batadv.nodes.length; - } - - for (var i = 0; i < data.length; ++i) { - var vererr; - if(i % 2) - if (data[i].version !== 1) { - vererr = "Unsupported graph version: " + data[i].version; - console.log(vererr); //silent fail - } else { - data[i].batadv.links.forEach(rearrangeLinks); - 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) { + for (var i = 0; i < data.length; ++i) { + var vererr; + if (i % 2) { + if (data[i].version !== 1) { + vererr = "Unsupported graph version: " + data[i].version; + console.log(vererr); //silent fail + } else { + data[i].batadv.links.forEach(rearrangeLinks); + 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; console.log(vererr); //silent fail } else { dataNodes.nodes = dataNodes.nodes.concat(data[i].nodes); 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) { - d.source = graph.nodes[d.source]; + var nodes = dataNodes.nodes.filter(function (d) { + return "firstseen" in d && "lastseen" in d; + }); - if (graph.nodes[d.target].node) - d.target = graph.nodes[d.target]; - else - d.target = undefined; - }); + nodes.forEach(function (node) { + node.firstseen = moment.utc(node.firstseen).local(); + node.lastseen = moment.utc(node.lastseen).local(); + }); - var links = graph.links.filter( function (d) { - return d.target !== undefined; - }); + var now = moment(); + var age = moment(now).subtract(config.maxAge, "days"); - 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("-"); + var newnodes = limit("firstseen", age, sortByKey("firstseen", nodes).filter(online)); + var lostnodes = limit("lastseen", age, sortByKey("lastseen", nodes).filter(offline)); - if (unknown || - !d.source.node.nodeinfo.location || - !d.target.node.nodeinfo.location || + 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) { + 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.longitude) || isNaN(d.target.node.nodeinfo.location.latitude) || - isNaN(d.target.node.nodeinfo.location.longitude)) - return; + isNaN(d.target.node.nodeinfo.location.longitude)) { + return; + } - 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.target.node.nodeinfo.location.latitude, d.target.node.nodeinfo.location.longitude)); + 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.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) { - d.neighbours = []; - }); + nodes.forEach(function (d) { + d.neighbours = []; + }); - links.forEach( function (d) { - if (d.type === "tunnel" || d.type === "fastd") - d.type = "fastd"; - else if (d.type === "l2tp") { - d.type = "L2TP"; - d.target.node.flags.uplink = true; - } else if (d.type === "wireless") - d.type = "Wifi"; - else if (d.type === "other") - d.type = "Kabel"; - else - d.type = "N/A"; - var unknown = (d.source.node === undefined); - if (unknown) { - 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 }); - if (d.type !== "fastd" && d.type !== "L2TP") - d.source.node.meshlinks = d.source.node.meshlinks ? d.source.node.meshlinks + 1 : 1; - }); + links.forEach(function (d) { + if (d.type === "tunnel" || d.type === "fastd") { + d.type = "fastd"; + } else if (d.type === "l2tp") { + d.type = "L2TP"; + d.target.node.flags.uplink = true; + } else if (d.type === "wireless") { + d.type = "Wifi"; + } else if (d.type === "other") { + d.type = "Kabel"; + } else { + d.type = "N/A"; + } + var unknown = (d.source.node === undefined); + if (unknown) { + 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}); + 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) { - return b.tq - a.tq; - }); + links.sort(function (a, b) { + return b.tq - a.tq; + }); - return { now: now, - timestamp: moment.utc(dataNodes.timestamp).local(), - nodes: { - all: nodes, - new: newnodes, - lost: lostnodes - }, - graph: { - links: links, - nodes: graph.nodes - } - }; - } + return { + now: now, + timestamp: moment.utc(dataNodes.timestamp).local(), + nodes: { + all: nodes, + new: newnodes, + lost: lostnodes + }, + graph: { + links: links, + nodes: graph.nodes + } + }; + } - numeral.language("de"); - moment.locale("de"); + numeral.language("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) - config.dataPath = [config.dataPath]; + if (typeof config.dataPath === "string" || config.dataPath instanceof String) { + config.dataPath = [config.dataPath]; + } - for (var i in config.dataPath) { - urls.push(config.dataPath[i] + "nodes.json"); - urls.push(config.dataPath[i] + "graph.json"); - } + for (var i in config.dataPath) { + urls.push(config.dataPath[i] + "nodes.json"); + urls.push(config.dataPath[i] + "graph.json"); + } - function update() { - return Promise.all(urls.map(getJSON)) - .then(handleData); - } + function update() { + return Promise.all(urls.map(getJSON)) + .then(handleData); + } - update() - .then(function (d) { - var gui = new GUI(config, router); - gui.setData(d); - router.setData(d); - router.start(); + update() + .then(function (d) { + var gui = new GUI(config, router); + gui.setData(d); + router.setData(d); + router.start(); - window.setInterval(function () { - update().then(function (d) { - gui.setData(d); - router.setData(d); - }); - }, 60000); - }) - .catch(function (e) { - document.body.textContent = e; - console.log(e); - }); - }; -}); + window.setInterval(function () { + update().then(function (d) { + gui.setData(d); + router.setData(d); + }); + }, 60000); + }) + .catch(function (e) { + document.body.textContent = e; + console.log(e); + }); + }; + }); diff --git a/lib/map.js b/lib/map.js index d5d4bdf..0923d89 100644 --- a/lib/map.js +++ b/lib/map.js @@ -1,73 +1,74 @@ define(["map/clientlayer", "map/labelslayer", - "d3", "leaflet", "moment", "locationmarker", "rbush", - "leaflet.label", "leaflet.providers"], + "d3", "leaflet", "moment", "locationmarker", "rbush", + "leaflet.label", "leaflet.providers"], function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush) { - var options = { worldCopyJump: true, - zoomControl: false - }; + var options = { + worldCopyJump: true, + zoomControl: false + }; var AddLayerButton = L.Control.extend({ - options: { - position: "bottomright" - }, + options: { + position: "bottomright" + }, - initialize: function (f, options) { - L.Util.setOptions(this, options); - this.f = f; - }, + initialize: function (f, options) { + L.Util.setOptions(this, options); + this.f = f; + }, - onAdd: function () { - var button = L.DomUtil.create("button", "add-layer"); - button.textContent = "\uF2C7"; + onAdd: function () { + var button = L.DomUtil.create("button", "add-layer"); + button.textContent = "\uF2C7"; - // L.DomEvent.disableClickPropagation(button) - // Click propagation isn't disabled as this causes problems with the - // location picking mode; instead propagation is stopped in onClick(). - L.DomEvent.addListener(button, "click", this.f, this); + // L.DomEvent.disableClickPropagation(button) + // Click propagation isn't disabled as this causes problems with the + // location picking mode; instead propagation is stopped in onClick(). + L.DomEvent.addListener(button, "click", this.f, this); - this.button = button; + this.button = button; - return button; - } + return button; + } }); var LocateButton = L.Control.extend({ - options: { - position: "bottomright" - }, + options: { + position: "bottomright" + }, - active: false, - button: undefined, + active: false, + button: undefined, - initialize: function (f, options) { - L.Util.setOptions(this, options); - this.f = f; - }, + initialize: function (f, options) { + L.Util.setOptions(this, options); + this.f = f; + }, - onAdd: function () { - var button = L.DomUtil.create("button", "locate-user"); - button.textContent = "\uF2E9"; + onAdd: function () { + var button = L.DomUtil.create("button", "locate-user"); + button.textContent = "\uF2E9"; - L.DomEvent.disableClickPropagation(button); - L.DomEvent.addListener(button, "click", this.onClick, this); + L.DomEvent.disableClickPropagation(button); + L.DomEvent.addListener(button, "click", this.onClick, this); - this.button = button; + this.button = button; - return button; - }, + return button; + }, - update: function() { - this.button.classList.toggle("active", this.active); - }, + update: function () { + this.button.classList.toggle("active", this.active); + }, - set: function(v) { - this.active = v; - this.update(); - }, + set: function (v) { + this.active = v; + this.update(); + }, - onClick: function () { - this.f(!this.active); - } + onClick: function () { + this.f(!this.active); + } }); var CoordsPickerButton = L.Control.extend({ @@ -96,11 +97,11 @@ define(["map/clientlayer", "map/labelslayer", return button; }, - update: function() { + update: function () { this.button.classList.toggle("active", this.active); }, - set: function(v) { + set: function (v) { this.active = v; this.update(); }, @@ -130,16 +131,17 @@ define(["map/clientlayer", "map/labelslayer", } function addLinksToMap(dict, linkScale, graph, router) { - graph = graph.filter( function (d) { + graph = graph.filter(function (d) { return "distance" in d && d.type !== "VPN"; }); - var lines = graph.map( function (d) { - var opts = { color: d.type === "Kabel" ? "#50B0F0" : linkScale(d.tq).hex(), - weight: 4, - opacity: 0.5, - dashArray: "none" - }; + var lines = graph.map(function (d) { + var opts = { + color: d.type === "Kabel" ? "#50B0F0" : linkScale(d.tq).hex(), + weight: 4, + opacity: 0.5, + dashArray: "none" + }; var line = L.polyline(d.latlngs, opts); @@ -158,11 +160,11 @@ define(["map/clientlayer", "map/labelslayer", return lines; } - 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 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 iconNew = { color: "#1566A9", fillColor: "#93E929", radius: 6, fillOpacity: 1.0, opacity: 0.5, weight: 2 }; + 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 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 iconNew = {color: "#1566A9", fillColor: "#93E929", radius: 6, fillOpacity: 1.0, opacity: 0.5, weight: 2}; return function (config, linkScale, sidebar, router, buttons) { var self = this; @@ -176,10 +178,11 @@ define(["map/clientlayer", "map/labelslayer", var baseLayers = {}; var locateUserButton = new LocateButton(function (d) { - if (d) + if (d) { enableTracking(); - else + } else { disableTracking(); + } }); var mybuttons = []; @@ -191,28 +194,32 @@ define(["map/clientlayer", "map/labelslayer", } function clearButtons() { - mybuttons.forEach( function (d) { + mybuttons.forEach(function (d) { buttons.removeChild(d); }); } var showCoordsPickerButton = new CoordsPickerButton(function (d) { - if (d) + if (d) { enableCoords(); - else + } else { disableCoords(); + } }); function saveView() { - savedView = {center: map.getCenter(), - zoom: map.getZoom()}; + savedView = { + center: map.getCenter(), + zoom: map.getZoom() + }; } function enableTracking() { - map.locate({watch: true, - enableHighAccuracy: true, - setView: true - }); + map.locate({ + watch: true, + enableHighAccuracy: true, + setView: true + }); locateUserButton.set(true); } @@ -241,8 +248,9 @@ define(["map/clientlayer", "map/labelslayer", } function locationFound(e) { - if (!userLocation) + if (!userLocation) { userLocation = new LocationMarker(e.latlng).addTo(map); + } userLocation.setLatLng(e.latlng); userLocation.setAccuracy(e.accuracy); @@ -256,19 +264,22 @@ define(["map/clientlayer", "map/labelslayer", } function addLayer(layerName) { - if (layerName in baseLayers) + if (layerName in baseLayers) { return; + } - if (layerName in customLayers) + if (layerName in customLayers) { return; + } try { var layer = L.tileLayer.provider(layerName); layerControl.addBaseLayer(layer, layerName); customLayers[layerName] = layer; - if (localStorageTest()) + if (localStorageTest()) { localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers))); + } } catch (e) { console.log(e); } @@ -283,7 +294,7 @@ define(["map/clientlayer", "map/labelslayer", map = L.map(el, options); - var layers = config.mapLayers.map( function (d) { + var layers = config.mapLayers.map(function (d) { return { "name": 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.forEach( function (d) { + layers.forEach(function (d) { baseLayers[d.name] = d.layer; }); @@ -316,8 +327,9 @@ define(["map/clientlayer", "map/labelslayer", if (localStorageTest()) { var d = JSON.parse(localStorage.getItem("map/customLayers")); - if (d) + if (d) { d.forEach(addLayer); + } 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; @@ -336,13 +348,16 @@ define(["map/clientlayer", "map/labelslayer", labelsLayer.addTo(map); labelsLayer.setZIndex(6); - map.on("baselayerchange", function(e) { + map.on("baselayerchange", function (e) { map.options.maxZoom = e.layer.options.maxZoom; clientLayer.options.maxZoom = map.options.maxZoom; labelsLayer.options.maxZoom = map.options.maxZoom; - if (map.getZoom() > map.options.maxZoom) map.setZoom(map.options.maxZoom); - if (localStorageTest()) + if (map.getZoom() > map.options.maxZoom) { + map.setZoom(map.options.maxZoom); + } + if (localStorageTest()) { localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name})); + } }); var nodeDict = {}; @@ -350,11 +365,11 @@ define(["map/clientlayer", "map/labelslayer", var highlight; function resetMarkerStyles(nodes, links) { - Object.keys(nodes).forEach( function (d) { + Object.keys(nodes).forEach(function (d) { nodes[d].resetStyle(); }); - Object.keys(links).forEach( function (d) { + Object.keys(links).forEach(function (d) { links[d].resetStyle(); }); } @@ -364,17 +379,19 @@ define(["map/clientlayer", "map/labelslayer", } function resetZoom() { - if (barycenter) + if (barycenter) { setView(barycenter.getBounds()); + } } function goto(m) { var bounds; - if ("getBounds" in m) + if ("getBounds" in m) { bounds = m.getBounds(); - else + } else { bounds = L.latLngBounds([m.getLatLng()]); + } setView(bounds); @@ -385,48 +402,62 @@ define(["map/clientlayer", "map/labelslayer", resetMarkerStyles(nodeDict, linkDict); var m; - if (highlight !== undefined) + if (highlight !== undefined) { if (highlight.type === "node") { m = nodeDict[highlight.o.nodeinfo.node_id]; - if (m) - m.setStyle({ color: "orange", weight: 20, fillOpacity: 1, opacity: 0.7, className: "stroke-first" }); + if (m) { + m.setStyle({color: "orange", weight: 20, fillOpacity: 1, opacity: 0.7, className: "stroke-first"}); + } } else if (highlight.type === "link") { m = linkDict[highlight.o.id]; - if (m) - m.setStyle({ weight: 7, opacity: 1, dashArray: "10, 10" }); + if (m) { + m.setStyle({weight: 7, opacity: 1, dashArray: "10, 10"}); + } } + } - if (!nopanzoom) - if (m) + if (!nopanzoom) { + if (m) { goto(m); - else if (savedView) + } else if (savedView) { map.setView(savedView.center, savedView.zoom); - else + } else { resetZoom(); + } + } } 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; + } - var lats = nodes.map(function (d) { return d.latitude; }); - var lngs = nodes.map(function (d) { return d.longitude; }); + var lats = nodes.map(function (d) { + return d.latitude; + }); + var lngs = nodes.map(function (d) { + return d.longitude; + }); var barycenter = L.latLng(d3.median(lats), d3.median(lngs)); var barycenterDev = [d3.deviation(lats), d3.deviation(lngs)]; - if (barycenterDev[0] === undefined) + if (barycenterDev[0] === undefined) { barycenterDev[0] = 0; + } - if (barycenterDev[1] === undefined) + if (barycenterDev[1] === undefined) { barycenterDev[1] = 0; + } var barycenterCircle = L.latLng(barycenter.lat + barycenterDev[0], - barycenter.lng + barycenterDev[1]); + barycenter.lng + barycenterDev[1]); var r = barycenter.distanceTo(barycenterCircle); @@ -434,8 +465,8 @@ define(["map/clientlayer", "map/labelslayer", } function mapRTree(d) { - var o = [ d.nodeinfo.location.latitude, d.nodeinfo.location.longitude, - 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]; o.node = d; @@ -446,45 +477,58 @@ define(["map/clientlayer", "map/labelslayer", nodeDict = {}; linkDict = {}; - if (groupOffline) + if (groupOffline) { groupOffline.clearLayers(); + } - if (groupOnline) + if (groupOnline) { groupOnline.clearLayers(); + } - if (groupNew) + if (groupNew) { groupNew.clearLayers(); + } - if (groupLost) + if (groupLost) { groupLost.clearLayers(); + } - if (groupLines) + if (groupLines) { groupLines.clearLayers(); + } var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router); groupLines = L.featureGroup(lines).addTo(map); - if (typeof config.fixedCenter === "undefined") + if (typeof config.fixedCenter === "undefined") { 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); + } var nodesOnline = subtract(data.nodes.all.filter(online), data.nodes.new); var nodesOffline = subtract(data.nodes.all.filter(offline), data.nodes.lost); 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) - .map(mkMarker(nodeDict, function () { return iconOffline; }, router)); + .map(mkMarker(nodeDict, function () { + return iconOffline; + }, router)); 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) .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 iconLost; }, router)); @@ -499,11 +543,12 @@ define(["map/clientlayer", "map/labelslayer", rtreeOnlineAll.load(data.nodes.all.filter(online).filter(has_location).map(mapRTree)); clientLayer.setData(rtreeOnlineAll); - labelsLayer.setData({online: nodesOnline.filter(has_location), - offline: nodesOffline.filter(has_location), - new: data.nodes.new.filter(has_location), - lost: data.nodes.lost.filter(has_location) - }); + labelsLayer.setData({ + online: nodesOnline.filter(has_location), + offline: nodesOffline.filter(has_location), + new: data.nodes.new.filter(has_location), + lost: data.nodes.lost.filter(has_location) + }); updateView(true); }; @@ -534,8 +579,9 @@ define(["map/clientlayer", "map/labelslayer", clearButtons(); map.remove(); - if (el.parentNode) + if (el.parentNode) { el.parentNode.removeChild(el); + } }; self.render = function (d) { @@ -545,4 +591,4 @@ define(["map/clientlayer", "map/labelslayer", return self; }; -}); + }); diff --git a/lib/map/clientlayer.js b/lib/map/clientlayer.js index 06fd784..d6ee4fc 100644 --- a/lib/map/clientlayer.js +++ b/lib/map/clientlayer.js @@ -21,8 +21,9 @@ define(["leaflet", "jshashes"], return [br.lat, tl.lng, tl.lat, br.lng]; } - if (!this.data) + if (!this.data) { return; + } var tileSize = this.options.tileSize; var s = tilePoint.multiplyBy(tileSize); @@ -33,8 +34,9 @@ define(["leaflet", "jshashes"], var nodes = this.data.search(bbox); - if (nodes.length === 0) + if (nodes.length === 0) { return; + } 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 clients = d.node.statistics.clients; - if (clients === 0) + if (clients === 0) { return; + } p.x -= s.x; p.y -= s.y; @@ -73,4 +76,4 @@ define(["leaflet", "jshashes"], ctx.fill(); } }); -}); + }); diff --git a/lib/map/labelslayer.js b/lib/map/labelslayer.js index fe5a94c..e616877 100644 --- a/lib/map/labelslayer.js +++ b/lib/map/labelslayer.js @@ -1,13 +1,13 @@ define(["leaflet", "rbush"], function (L, rbush) { - var labelLocations = [["left", "middle", 0 / 8], - ["center", "top", 6 / 8], - ["right", "middle", 4 / 8], - ["left", "top", 7 / 8], - ["left", "ideographic", 1 / 8], - ["right", "top", 5 / 8], - ["center", "ideographic", 2 / 8], - ["right", "ideographic", 3 / 8]]; + var labelLocations = [["left", "middle", 0 / 8], + ["center", "top", 6 / 8], + ["right", "middle", 4 / 8], + ["left", "top", 7 / 8], + ["left", "ideographic", 1 / 8], + ["right", "top", 5 / 8], + ["center", "ideographic", 2 / 8], + ["right", "ideographic", 3 / 8]]; var fontFamily = "Roboto"; var nodeRadius = 4; @@ -30,22 +30,23 @@ define(["leaflet", "rbush"], function prepareLabel(fillStyle, fontSize, offset, stroke, minZoom) { return function (d) { var font = fontSize + "px " + fontFamily; - return { position: L.latLng(d.nodeinfo.location.latitude, d.nodeinfo.location.longitude), - label: d.nodeinfo.hostname, - offset: offset, - fillStyle: fillStyle, - height: fontSize * 1.2, - font: font, - stroke: stroke, - minZoom: minZoom, - width: measureText(font, d.nodeinfo.hostname).width - }; + return { + position: L.latLng(d.nodeinfo.location.latitude, d.nodeinfo.location.longitude), + label: d.nodeinfo.hostname, + offset: offset, + fillStyle: fillStyle, + height: fontSize * 1.2, + font: font, + stroke: stroke, + minZoom: minZoom, + width: measureText(font, d.nodeinfo.hostname).width + }; }; } function calcOffset(offset, loc) { - return [ offset * Math.cos(loc[2] * 2 * Math.PI), - -offset * Math.sin(loc[2] * 2 * Math.PI)]; + return [offset * Math.cos(loc[2] * 2 * Math.PI), + -offset * Math.sin(loc[2] * 2 * Math.PI)]; } function labelRect(p, offset, anchor, label, minZoom, maxZoom, z) { @@ -54,15 +55,17 @@ define(["leaflet", "rbush"], var width = label.width * margin; var height = label.height * margin; - var dx = { left: 0, - right: -width, - center: -width / 2 - }; + var dx = { + left: 0, + right: -width, + center: -width / 2 + }; - var dy = { top: 0, - ideographic: -height, - middle: -height / 2 - }; + var dy = { + top: 0, + ideographic: -height, + middle: -height / 2 + }; var x = p.x + offset[0] + dx[anchor[0]]; var y = p.y + offset[1] + dy[anchor[1]]; @@ -73,13 +76,15 @@ define(["leaflet", "rbush"], var c = L.TileLayer.Canvas.extend({ onAdd: function (map) { L.TileLayer.Canvas.prototype.onAdd.call(this, map); - if (this.data) + if (this.data) { this.prepareLabels(); + } }, setData: function (d) { this.data = d; - if (this._map) + if (this._map) { this.prepareLabels(); + } }, prepareLabels: function () { 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 labels = [] - .concat(labelsNew) - .concat(labelsLost) - .concat(labelsOnline) - .concat(labelsOffline); + .concat(labelsNew) + .concat(labelsLost) + .concat(labelsOnline) + .concat(labelsOffline); var minZoom = this.options.minZoom; var maxZoom = this.options.maxZoom; @@ -114,7 +119,7 @@ define(["leaflet", "rbush"], return function (d) { var p = map.project(d.position, z); 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 candidates = trees[z].search(rect); - if (candidates.length > 0) + if (candidates.length > 0) { break; + } } return {loc: loc, z: z + 1}; @@ -156,16 +162,20 @@ define(["leaflet", "rbush"], } return d; - } else + } else { return undefined; - }).filter(function (d) { return d !== undefined; }); + } + }).filter(function (d) { + return d !== undefined; + }); this.margin = 16; - if (labels.length > 0) + if (labels.length > 0) { this.margin += labels.map(function (d) { return d.width; }).sort().reverse()[0]; + } this.labels = rbush(9); this.labels.load(labels.map(mapRTree)); @@ -180,8 +190,9 @@ define(["leaflet", "rbush"], return [br.lat, tl.lng, tl.lat, br.lng]; } - if (!this.labels) + if (!this.labels) { return; + } var tileSize = this.options.tileSize; var s = tilePoint.multiplyBy(tileSize); @@ -212,8 +223,9 @@ define(["leaflet", "rbush"], ctx.textBaseline = d.label.anchor[1]; 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.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; -}); + }); diff --git a/lib/meshstats.js b/lib/meshstats.js index 119b37c..57ec349 100644 --- a/lib/meshstats.js +++ b/lib/meshstats.js @@ -8,24 +8,28 @@ define(function () { var totalOnlineNodes = sum(d.nodes.all.filter(online).map(one)); var totalNewNodes = sum(d.nodes.new.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; })); - 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; }).map(one)); - var nodetext = [{ count: totalOnlineNodes, label: "online" }, - { count: totalNewNodes, label: "neu" }, - { count: totalLostNodes, label: "verschwunden" } - ].filter( function (d) { return d.count > 0; } ) - .map( function (d) { return [d.count, d.label].join(" "); } ) - .join(", "); + var nodetext = [{count: totalOnlineNodes, label: "online"}, + {count: totalNewNodes, label: "neu"}, + {count: totalLostNodes, label: "verschwunden"} + ].filter(function (d) { + return d.count > 0; + }) + .map(function (d) { + return [d.count, d.label].join(" "); + }) + .join(", "); stats.textContent = totalNodes + " Knoten " + - "(" + nodetext + "), " + - totalClients + " Client" + ( totalClients === 1 ? ", " : "s, " ) + - totalGateways + " Gateways"; + "(" + nodetext + "), " + + totalClients + " Client" + ( totalClients === 1 ? ", " : "s, " ) + + totalGateways + " Gateways"; timestamp.textContent = "Diese Daten sind von " + d.timestamp.format("LLLL") + "."; }; diff --git a/lib/nodelist.js b/lib/nodelist.js index e154919..1c9c6be 100644 --- a/lib/nodelist.js +++ b/lib/nodelist.js @@ -1,62 +1,71 @@ define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral) { 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); - else if (!d.flags.online && "lastseen" in d) + } else if (!d.flags.online && "lastseen" in d) { return Math.round(-(now.unix() - d.lastseen.unix())); + } } function showUptime(uptime) { var s = ""; uptime /= 3600; - if (uptime !== undefined) - if (Math.abs(uptime) >= 24) + if (uptime !== undefined) { + if (Math.abs(uptime) >= 24) { s = Math.round(uptime / 24) + "d"; - else + } else { s = Math.round(uptime) + "h"; + } + } return s; } - var headings = [{ name: "Knoten", - sort: function (a, b) { - return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname); - }, - reverse: false - }, - { name: "Uptime", - sort: function (a, b) { - return a.uptime - b.uptime; - }, - reverse: true - }, - { name: "#Links", - sort: function (a, b) { - return a.meshlinks - b.meshlinks; - }, - reverse: true - }, - { name: "Clients", - sort: function (a, b) { - return ("clients" in a.statistics ? a.statistics.clients : -1) - - ("clients" in b.statistics ? b.statistics.clients : -1); - }, - reverse: true - }]; + var headings = [{ + name: "Knoten", + sort: function (a, b) { + return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname); + }, + reverse: false + }, + { + name: "Uptime", + sort: function (a, b) { + return a.uptime - b.uptime; + }, + reverse: true + }, + { + name: "#Links", + sort: function (a, b) { + return a.meshlinks - b.meshlinks; + }, + reverse: true + }, + { + name: "Clients", + 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) { var td1Content = []; var aClass = ["hostname", d.flags.online ? "online" : "offline"]; - td1Content.push(V.h("a", { className: aClass.join(" "), - onclick: router.node(d), - href: "#" - }, d.nodeinfo.hostname)); + td1Content.push(V.h("a", { + className: aClass.join(" "), + onclick: router.node(d), + href: "#" + }, d.nodeinfo.hostname)); - if (has_location(d)) + if (has_location(d)) { td1Content.push(V.h("span", {className: "icon ion-location"})); + } var td1 = V.h("td", td1Content); var td2 = V.h("td", showUptime(d.uptime)); diff --git a/lib/proportions.js b/lib/proportions.js index 40f3292..26f4c4f 100644 --- a/lib/proportions.js +++ b/lib/proportions.js @@ -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) { - return function (config, filterManager) { - var self = this; - var scale = Chroma.scale("YlGnBu").mode("lab"); + return function (config, filterManager) { + var self = this; + var scale = Chroma.scale("YlGnBu").mode("lab"); - var statusTable = document.createElement("table"); - statusTable.classList.add("proportion"); + var statusTable = document.createElement("table"); + statusTable.classList.add("proportion"); - var fwTable = document.createElement("table"); - fwTable.classList.add("proportion"); + var fwTable = document.createElement("table"); + fwTable.classList.add("proportion"); - var hwTable = document.createElement("table"); - hwTable.classList.add("proportion"); + var hwTable = document.createElement("table"); + hwTable.classList.add("proportion"); - var geoTable = document.createElement("table"); - geoTable.classList.add("proportion"); + var geoTable = document.createElement("table"); + geoTable.classList.add("proportion"); - var autoTable = document.createElement("table"); - autoTable.classList.add("proportion"); + var autoTable = document.createElement("table"); + autoTable.classList.add("proportion"); - var uplinkTable = document.createElement("table"); - uplinkTable.classList.add("proportion"); + var uplinkTable = document.createElement("table"); + uplinkTable.classList.add("proportion"); - var gwNodesTable = document.createElement("table"); - gwNodesTable.classList.add("proportion"); + var gwNodesTable = document.createElement("table"); + gwNodesTable.classList.add("proportion"); - var gwClientsTable = document.createElement("table"); - gwClientsTable.classList.add("proportion"); + var gwClientsTable = document.createElement("table"); + gwClientsTable.classList.add("proportion"); - var siteTable = document.createElement("table"); - siteTable.classList.add("proportion"); + var siteTable = document.createElement("table"); + siteTable.classList.add("proportion"); - function showStatGlobal(o) { - return showStat(o); - } + function showStatGlobal(o) { + return showStat(o); + } - function count(nodes, key, f) { - var dict = {}; + function count(nodes, key, f) { + var dict = {}; - nodes.forEach( function (d) { - var v = dictGet(d, key.slice(0)); + nodes.forEach(function (d) { + var v = dictGet(d, key.slice(0)); - if (f !== undefined) - v = f(v); + if (f !== undefined) { + v = f(v); + } - if (v === null) - return; + if (v === null) { + return; + } - 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)); + 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.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; - }; -}); + 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)); + }); + } + }; + + 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; + }; + }); diff --git a/lib/router.js b/lib/router.js index c737673..f719cc0 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,7 +1,7 @@ define(function () { return function () { var self = this; - var objects = { nodes: {}, links: {} }; + var objects = {nodes: {}, links: {}}; var targets = []; var views = {}; var currentView; @@ -11,15 +11,18 @@ define(function () { function saveState() { var e = []; - if (currentView) + if (currentView) { e.push("v:" + currentView); + } if (currentObject) { - if ("node" in currentObject) + if ("node" in currentObject) { e.push("n:" + encodeURIComponent(currentObject.node.nodeinfo.node_id)); + } - if ("link" in currentObject) + if ("link" in currentObject) { e.push("l:" + encodeURIComponent(currentObject.link.id)); + } } var s = "#!" + e.join(";"); @@ -30,7 +33,7 @@ define(function () { function resetView(push) { push = trueDefault(push); - targets.forEach( function (t) { + targets.forEach(function (t) { t.resetView(); }); @@ -41,10 +44,11 @@ define(function () { } function gotoNode(d) { - if (!d) + if (!d) { return false; + } - targets.forEach( function (t) { + targets.forEach(function (t) { t.gotoNode(d); }); @@ -52,10 +56,11 @@ define(function () { } function gotoLink(d) { - if (!d) + if (!d) { return false; + } - targets.forEach( function (t) { + targets.forEach(function (t) { t.gotoLink(d); }); @@ -63,11 +68,14 @@ define(function () { } function gotoLocation(d) { - if (!d) + if (!d) { return false; + } - targets.forEach( function (t) { - if(!t.gotoLocation)console.warn("has no gotoLocation", t); + targets.forEach(function (t) { + if (!t.gotoLocation) { + console.warn("has no gotoLocation", t); + } t.gotoLocation(d); }); @@ -75,13 +83,15 @@ define(function () { } function loadState(s) { - if (!s) + if (!s) { return false; + } s = decodeURIComponent(s); - if (!s.startsWith("#!")) + if (!s.startsWith("#!")) { return false; + } var targetSet = false; @@ -98,7 +108,7 @@ define(function () { if (args[0] === "n") { id = args[1]; if (id in objects.nodes) { - currentObject = { node: objects.nodes[id] }; + currentObject = {node: objects.nodes[id]}; gotoNode(objects.nodes[id]); targetSet = true; } @@ -107,7 +117,7 @@ define(function () { if (args[0] === "l") { id = args[1]; if (id in objects.links) { - currentObject = { link: objects.links[id] }; + currentObject = {link: objects.links[id]}; gotoLink(objects.links[id]); targetSet = true; } @@ -120,12 +130,14 @@ define(function () { self.start = function () { running = true; - if (!loadState(window.location.hash)) + if (!loadState(window.location.hash)) { resetView(false); + } window.onpopstate = function (d) { - if (!loadState(d.state)) + if (!loadState(d.state)) { resetView(false); + } }; }; @@ -133,11 +145,13 @@ define(function () { if (d in views) { views[d](); - if (!currentView || running) + if (!currentView || running) { currentView = d; + } - if (!running) + if (!running) { return; + } saveState(); @@ -146,18 +160,20 @@ define(function () { return; } - if ("node" in currentObject) + if ("node" in currentObject) { gotoNode(currentObject.node); + } - if ("link" in currentObject) + if ("link" in currentObject) { gotoLink(currentObject.link); + } } }; self.node = function (d) { return function () { if (gotoNode(d)) { - currentObject = { node: d }; + currentObject = {node: d}; saveState(); } @@ -168,7 +184,7 @@ define(function () { self.link = function (d) { return function () { if (gotoLink(d)) { - currentObject = { link: d }; + currentObject = {link: d}; saveState(); } @@ -187,7 +203,7 @@ define(function () { }; self.removeTarget = function (d) { - targets = targets.filter( function (e) { + targets = targets.filter(function (e) { return d !== e; }); }; @@ -200,11 +216,11 @@ define(function () { objects.nodes = {}; objects.links = {}; - data.nodes.all.forEach( function (d) { + data.nodes.all.forEach(function (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; }); }; diff --git a/lib/sidebar.js b/lib/sidebar.js index 7150f7e..4c839cc 100644 --- a/lib/sidebar.js +++ b/lib/sidebar.js @@ -19,8 +19,9 @@ define([], function () { sidebar.appendChild(container); self.getWidth = function () { - if (sidebar.classList.contains("hidden")) - return 0; + if (sidebar.classList.contains("hidden")) { + return 0; + } var small = window.matchMedia("(max-width: 630pt)"); return small.matches ? 0 : sidebar.offsetWidth; diff --git a/lib/simplenodelist.js b/lib/simplenodelist.js index d7be3e8..f20e02b 100644 --- a/lib/simplenodelist.js +++ b/lib/simplenodelist.js @@ -1,5 +1,5 @@ define(["moment", "virtual-dom"], function (moment, V) { - return function(nodes, field, router, title) { + return function (nodes, field, router, title) { var self = this; var el, tbody; @@ -12,8 +12,9 @@ define(["moment", "virtual-dom"], function (moment, V) { var list = data.nodes[nodes]; if (list.length === 0) { - while (el.firstChild) - el.removeChild(el.firstChild); + while (el.firstChild) { + el.removeChild(el.firstChild); + } tbody = null; @@ -33,19 +34,21 @@ define(["moment", "virtual-dom"], function (moment, V) { table.appendChild(tbody); } - var items = list.map( function (d) { + var items = list.map(function (d) { var time = moment(d[field]).from(data.now); var td1Content = []; var aClass = ["hostname", d.flags.online ? "online" : "offline"]; - td1Content.push(V.h("a", { className: aClass.join(" "), - onclick: router.node(d), - href: "#" - }, d.nodeinfo.hostname)); + td1Content.push(V.h("a", { + className: aClass.join(" "), + onclick: router.node(d), + href: "#" + }, d.nodeinfo.hostname)); - if (has_location(d)) + if (has_location(d)) { td1Content.push(V.h("span", {className: "icon ion-location"})); + } var td1 = V.h("td", td1Content); var td2 = V.h("td", time); diff --git a/lib/sorttable.js b/lib/sorttable.js index 4419243..881efba 100644 --- a/lib/sorttable.js +++ b/lib/sorttable.js @@ -1,5 +1,5 @@ define(["virtual-dom"], function (V) { - return function(headings, sortIndex, renderRow) { + return function (headings, sortIndex, renderRow) { var data; var sortReverse = false; var el = document.createElement("table"); @@ -13,7 +13,9 @@ define(["virtual-dom"], function (V) { } function sortTableHandler(i) { - return function () { sortTable(i); }; + return function () { + sortTable(i); + }; } function updateView() { @@ -21,20 +23,23 @@ define(["virtual-dom"], function (V) { if (data.length !== 0) { var th = headings.map(function (d, i) { - var properties = { onclick: sortTableHandler(i), - className: "sort-header" - }; + var properties = { + onclick: sortTableHandler(i), + className: "sort-header" + }; - if (sortIndex === i) + if (sortIndex === i) { properties.className += sortReverse ? " sort-up" : " sort-down"; + } return V.h("th", properties, d.name); }); var links = data.slice(0).sort(headings[sortIndex].sort); - if (headings[sortIndex].reverse ? !sortReverse : sortReverse) + if (headings[sortIndex].reverse ? !sortReverse : sortReverse) { links = links.reverse(); + } children.push(V.h("thead", V.h("tr", th))); children.push(V.h("tbody", links.map(renderRow))); diff --git a/lib/tabs.js b/lib/tabs.js index 63eaea8..f4fed6d 100644 --- a/lib/tabs.js +++ b/lib/tabs.js @@ -8,11 +8,13 @@ define([], function () { var container = document.createElement("div"); 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"); + } - while (container.firstChild) + while (container.firstChild) { container.removeChild(container.firstChild); + } li.classList.add("visible"); @@ -37,14 +39,16 @@ define([], function () { 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")) { anyVisible = true; break; } + } - if (!anyVisible) + if (!anyVisible) { gotoTab(li); + } }; self.render = function (el) { diff --git a/lib/title.js b/lib/title.js index 7585c82..4c99c2c 100644 --- a/lib/title.js +++ b/lib/title.js @@ -1,10 +1,11 @@ define(function () { - return function (config) { + return function (config) { function setTitle(d) { var title = [config.siteName]; - if (d !== undefined) + if (d !== undefined) { title.push(d); + } document.title = title.join(": "); } @@ -14,16 +15,18 @@ define(function () { }; this.gotoNode = function (d) { - if (d) + if (d) { setTitle(d.nodeinfo.hostname); + } }; 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); + } }; - this.gotoLocation = function() { + this.gotoLocation = function () { //ignore }; diff --git a/lib/vercomp.js b/lib/vercomp.js index b829b94..4752b11 100644 --- a/lib/vercomp.js +++ b/lib/vercomp.js @@ -1,15 +1,16 @@ define([], function () { function order(c) { - if (/^\d$/.test(c)) + if (/^\d$/.test(c)) { return 0; - else if (/^[a-z]$/i.test(c)) + } else if (/^[a-z]$/i.test(c)) { return c.charCodeAt(0); - else if (c === "~") + } else if (c === "~") { return -1; - else if (c) + } else if (c) { return c.charCodeAt(0) + 256; - else + } else { return 0; + } } // Based on dpkg code @@ -22,35 +23,42 @@ define([], function () { var ac = order(a[apos]); var bc = order(b[bpos]); - if (ac !== bc) + if (ac !== bc) { return ac - bc; + } apos++; bpos++; } - while (a[apos] === "0") + while (a[apos] === "0") { apos++; + } - while (b[bpos] === "0") + while (b[bpos] === "0") { bpos++; + } while (/^\d$/.test(a[apos]) && /^\d$/.test(b[bpos])) { - if (firstDiff === 0) + if (firstDiff === 0) { firstDiff = a.charCodeAt(apos) - b.charCodeAt(bpos); + } apos++; bpos++; } - if (/^\d$/.test(a[apos])) + if (/^\d$/.test(a[apos])) { return 1; + } - if (/^\d$/.test(b[bpos])) + if (/^\d$/.test(b[bpos])) { return -1; + } - if (firstDiff !== 0) + if (firstDiff !== 0) { return firstDiff; + } } return 0; diff --git a/tasks/build.js b/tasks/build.js index d4f4a45..497da6d 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -1,4 +1,4 @@ -module.exports = function(grunt) { +module.exports = function (grunt) { grunt.config.merge({ bowerdir: "bower_components", copy: { @@ -19,37 +19,37 @@ module.exports = function(grunt) { dest: "build/" }, vendorjs: { - src: [ "es6-shim/es6-shim.min.js" ], + src: ["es6-shim/es6-shim.min.js"], expand: true, cwd: "bower_components/", dest: "build/vendor/" }, robotoSlab: { - src: [ "fonts/*", - "roboto-slab-fontface.css" - ], + src: ["fonts/*", + "roboto-slab-fontface.css" + ], expand: true, dest: "build/", cwd: "bower_components/roboto-slab-fontface" }, roboto: { - src: [ "fonts/*", - "roboto-fontface.css" - ], + src: ["fonts/*", + "roboto-fontface.css" + ], expand: true, dest: "build/", cwd: "bower_components/roboto-fontface" }, ionicons: { - src: [ "fonts/*", - "css/ionicons.min.css" - ], + src: ["fonts/*", + "css/ionicons.min.css" + ], expand: true, dest: "build/", cwd: "bower_components/ionicons/" }, leafletImages: { - src: [ "images/*" ], + src: ["images/*"], expand: true, dest: "build/", cwd: "bower_components/leaflet/dist/" @@ -82,26 +82,26 @@ module.exports = function(grunt) { cssmin: { target: { files: { - "build/style.css": [ "bower_components/leaflet/dist/leaflet.css", - "bower_components/Leaflet.label/dist/leaflet.label.css", - "style.css" - ] + "build/style.css": ["bower_components/leaflet/dist/leaflet.css", + "bower_components/Leaflet.label/dist/leaflet.label.css", + "style.css" + ] } } }, "bower-install-simple": { - options: { - directory: "<%=bowerdir%>", - color: true, - interactive: false, - production: true - }, - "prod": { - options: { - production: true - } - } + options: { + directory: "<%=bowerdir%>", + color: true, + interactive: false, + production: true }, + "prod": { + options: { + production: true + } + } + }, requirejs: { compile: { options: { diff --git a/tasks/linting.js b/tasks/linting.js index 716e838..8c43568 100644 --- a/tasks/linting.js +++ b/tasks/linting.js @@ -14,7 +14,6 @@ module.exports = function (grunt) { eslint: { options: { rules: { - "curly": [2, "multi"], "strict": [2, "never"], "no-multi-spaces": 0, "no-new": 0,