diff --git a/.editorconfig b/.editorconfig index f0abb4f..604c949 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,6 @@ trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true -[*.{js,html,scss,json}] +[*.{js,html,scss,json,yml,md}] indent_size = 2 indent_style = space diff --git a/.gitignore b/.gitignore index 1f46137..e728065 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ node_modules/ build/ .sass-cache/ config.json +.idea/ +.eslintrc diff --git a/Gruntfile.js b/Gruntfile.js index b63ad56..6fcc054 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,24 +1,24 @@ module.exports = function (grunt) { - grunt.loadNpmTasks("grunt-git-describe") + grunt.loadNpmTasks("grunt-git-describe"); grunt.initConfig({ "git-describe": { options: {}, default: {} } - }) + }); - grunt.registerTask("saveRevision", function() { + grunt.registerTask("saveRevision", function () { grunt.event.once("git-describe", function (rev) { - grunt.option("gitRevision", rev) - }) - grunt.task.run("git-describe") - }) + grunt.option("gitRevision", rev); + }); + grunt.task.run("git-describe"); + }); - grunt.loadTasks("tasks") + grunt.loadTasks("tasks"); - grunt.registerTask("default", ["bower-install-simple", "lint", "saveRevision", "copy", "sass", "postcss", "requirejs"]) - grunt.registerTask("lint", ["eslint"]) - grunt.registerTask("dev", ["default", "connect:server", "watch"]) -} + grunt.registerTask("default", ["bower-install-simple", "lint", "saveRevision", "copy", "sass", "postcss", "requirejs"]); + grunt.registerTask("lint", ["eslint"]); + grunt.registerTask("dev", ["default", "connect:server", "watch"]); +}; diff --git a/app.js b/app.js index e848121..ef131ef 100644 --- a/app.js +++ b/app.js @@ -29,8 +29,8 @@ require.config({ "tablesort.numeric": ["tablesort"], "helper": ["numeral-intl"] } -}) +}); require(["main", "helper"], function (main) { - getJSON("config.json").then(main) -}) + getJSON("config.json").then(main); +}); diff --git a/build.js b/build.js index 021929a..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/helper.js b/helper.js index 6e00702..1237021 100644 --- a/helper.js +++ b/helper.js @@ -1,228 +1,241 @@ function get(url) { - return new Promise(function(resolve, reject) { - var req = new XMLHttpRequest() - req.open('GET', url) + 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) + resolve(req.response); } else { - reject(Error(req.statusText)) + reject(Error(req.statusText)); } - } + }; - req.onerror = function() { - reject(Error("Network Error")) - } + req.onerror = function () { + reject(Error("Network Error")); + }; - req.send() - }) + req.send(); + }); } function getJSON(url) { - return get(url).then(JSON.parse) + return get(url).then(JSON.parse); } function sortByKey(key, d) { - return d.slice().sort( function (a, b) { - return a[key] - b[key] - }).reverse() + 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[key].isAfter(m) - }) + return d.filter(function (d) { + return d[key].isAfter(m); + }); } function sum(a) { - return a.reduce( function (a, b) { - return a + b - }, 0) + return a.reduce(function (a, b) { + return a + b; + }, 0); } function one() { - return 1 + return 1; } function trueDefault(d) { - return d === undefined ? true : d + return d === undefined ? true : d; } function dictGet(dict, key) { - var k = key.shift() + var k = key.shift(); - if (!(k in dict)) - return null + if (!(k in dict)) { + return null; + } - if (key.length == 0) - return dict[k] + if (key.length == 0) { + return dict[k]; + } - return dictGet(dict[k], key) + return dictGet(dict[k], key); } function localStorageTest() { - var test = 'test' + var test = 'test'; try { - localStorage.setItem(test, test) - localStorage.removeItem(test) - return true - } catch(e) { - return false + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch (e) { + return false; } } function listReplace(s, subst) { for (key in subst) { - var re = new RegExp(key, 'g') - s = s.replace(re, subst[key]) + var re = new RegExp(key, 'g'); + s = s.replace(re, subst[key]); } - return s + return s; } /* Helpers working with nodes */ function offline(d) { - return !d.flags.online + return !d.flags.online; } function online(d) { - return d.flags.online + return d.flags.online; } 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 = {} + var ids = {}; - b.forEach( function (d) { - ids[d.nodeinfo.node_id] = true - }) + b.forEach(function (d) { + ids[d.nodeinfo.node_id] = true; + }); - return a.filter( function (d) { - return !(d.nodeinfo.node_id in ids) - }) + return a.filter(function (d) { + return !(d.nodeinfo.node_id in ids); + }); } /* Helpers working with links */ function showDistance(d) { - if (isNaN(d.distance)) - return + if (isNaN(d.distance)) { + return; + } - return numeral(d.distance).format("0,0") + " m" + 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) - return - - var tr = document.createElement("tr") - var th = document.createElement("th") - if (typeof label === "string") - th.textContent = label - else { - th.appendChild(label) - tr.className = "routerpic" + if (value === null || value == undefined) { + return; } - tr.appendChild(th) + var tr = document.createElement("tr"); + var th = document.createElement("th"); + if (typeof label === "string") { + th.textContent = label; + } else { + th.appendChild(label); + tr.className = "routerpic"; + } - var td = document.createElement("td") + tr.appendChild(th); - if (typeof value == "function") - value(td) - else - td.appendChild(document.createTextNode(value)) + var td = document.createElement("td"); - tr.appendChild(td) + if (typeof value == "function") { + value(td); + } else { + td.appendChild(document.createTextNode(value)); + } - el.appendChild(tr) + tr.appendChild(td); - return td + el.appendChild(tr); + + return td; } function createIframe(opt, width, height) { - el = document.createElement("iframe") - width = typeof width !== 'undefined' ? width : '100%' - height = typeof height !== 'undefined' ? height : '350px' + el = document.createElement("iframe"); + width = typeof width !== 'undefined' ? width : '100%'; + height = typeof height !== 'undefined' ? height : '350px'; - if (opt.src) - el.src = opt.src - else - el.src = opt + if (opt.src) { + el.src = opt.src; + } else { + el.src = opt; + } - if (opt.frameBorder) - el.frameBorder = opt.frameBorder - else - el.frameBorder = 1 + if (opt.frameBorder) { + el.frameBorder = opt.frameBorder; + } else { + el.frameBorder = 1; + } - if (opt.width) - el.width = opt.width - else - el.width = width + if (opt.width) { + el.width = opt.width; + } else { + el.width = width; + } - if (opt.height) - el.height = opt.height - else - el.height = height + if (opt.height) { + el.height = opt.height; + } else { + el.height = height; + } - el.scrolling = "no" - el.seamless = "seamless" + el.scrolling = "no"; + el.seamless = "seamless"; - return el + return el; } function showStat(o, subst) { - var content, caption - subst = typeof subst !== 'undefined' ? subst : {} + var content, caption; + subst = typeof subst !== 'undefined' ? subst : {}; if (o.thumbnail) { - content = document.createElement("img") - content.src = listReplace(o.thumbnail, subst) + content = document.createElement("img"); + content.src = listReplace(o.thumbnail, subst); } if (o.caption) { - caption = listReplace(o.caption, subst) + 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) + 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); + } } - var p = document.createElement("p") + var p = document.createElement("p"); if (o.href) { - var link = document.createElement("a") - link.target = "_blank" - link.href = listReplace(o.href, subst) - link.appendChild(content) + var link = document.createElement("a"); + link.target = "_blank"; + 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) + p.appendChild(link); + } else { + p.appendChild(content); + } - return p + return p; } diff --git a/html/index.html b/html/index.html index 6f5c4c7..cc95aa5 100644 --- a/html/index.html +++ b/html/index.html @@ -1,28 +1,28 @@ - - - - - - - - - - - - -
-

- Lade
-
- Karte & Knoten... -

- -
- + + + + + + + + + + + + +
+

+ Lade
+
+ Karte & Knoten... +

+ +
+ diff --git a/index.html b/index.html index 76dfc6e..96935bc 100644 --- a/index.html +++ b/index.html @@ -1,27 +1,27 @@ - - - - - - - - - - - - - -
-

- Lade
-
- Karte & Knoten... -

- -
- + + + + + + + + + + + + + +
+

+ Lade
+
+ Karte & Knoten... +

+ +
+ diff --git a/lib/about.js b/lib/about.js index 6a44b78..2a20ae5 100644 --- a/lib/about.js +++ b/lib/about.js @@ -1,38 +1,38 @@ define(function () { - return function() { + return function () { this.render = function (d) { - var el = document.createElement("div") - d.appendChild(el) - var s = "

Über HopGlass

" + var el = document.createElement("div"); + d.appendChild(el); + var s = "

Über HopGlass

"; - s += "

Mit Doppelklick und Shift+Doppelklick kann man in der Karte " - s += "auch zoomen.

" + s += "

Mit Doppelklick und Shift+Doppelklick kann man in der Karte "; + s += "auch zoomen.

"; - s += "

AGPL 3

" + s += "

AGPL 3

"; - s += "

Copyright (C) Milan Pässler

" - s += "

Copyright (C) Nils Schneider

" + s += "

Copyright (C) Milan Pässler

"; + s += "

Copyright (C) Nils Schneider

"; - s += "

This program is free software: you can redistribute it and/or " - s += "modify it under the terms of the GNU Affero General Public " - s += "License as published by the Free Software Foundation, either " - s += "version 3 of the License, or (at your option) any later version.

" + s += "

This program is free software: you can redistribute it and/or "; + s += "modify it under the terms of the GNU Affero General Public "; + s += "License as published by the Free Software Foundation, either "; + s += "version 3 of the License, or (at your option) any later version.

"; - s += "

This program is distributed in the hope that it will be useful, " - s += "but WITHOUT ANY WARRANTY; without even the implied warranty of " - s += "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " - s += "GNU Affero General Public License for more details.

" + s += "

This program is distributed in the hope that it will be useful, "; + s += "but WITHOUT ANY WARRANTY; without even the implied warranty of "; + s += "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "; + s += "GNU Affero General Public License for more details.

"; - s += "

You should have received a copy of the GNU Affero General " - s += "Public License along with this program. If not, see " - s += "" - s += "https://www.gnu.org/licenses/.

" + s += "

You should have received a copy of the GNU Affero General "; + s += "Public License along with this program. If not, see "; + s += ""; + s += "https://www.gnu.org/licenses/.

"; - s += "

The source code is available at " - s += "" - s += "https://github.com/plumpudding/hopglass." + s += "

The source code is available at "; + s += ""; + s += "https://github.com/plumpudding/hopglass."; - el.innerHTML = s - } - } -}) + el.innerHTML = s; + }; + }; +}); diff --git a/lib/container.js b/lib/container.js index a33ab75..4f32a84 100644 --- a/lib/container.js +++ b/lib/container.js @@ -1,20 +1,21 @@ define([], function () { return function (tag) { - if (!tag) - tag = "div" + if (!tag) { + tag = "div"; + } - var self = this + var self = this; - var container = document.createElement(tag) + var container = document.createElement(tag); self.add = function (d) { - d.render(container) - } + d.render(container); + }; self.render = function (el) { - el.appendChild(container) - } + el.appendChild(container); + }; - return self - } -}) + return self; + }; +}); diff --git a/lib/datadistributor.js b/lib/datadistributor.js index 67cf7eb..3edeaf6 100644 --- a/lib/datadistributor.js +++ b/lib/datadistributor.js @@ -1,80 +1,91 @@ define(["filters/nodefilter"], function (NodeFilter) { return function () { - var targets = [] - var filterObservers = [] - var filters = [] - var filteredData - var data + var targets = []; + var filterObservers = []; + var filters = []; + var filteredData; + 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) + targets.push(d); - if (filteredData !== undefined) - d.setData(filteredData) - } - - function setData(d) { - data = d - refresh() - } - - function refresh() { - if (data === undefined) - return - - var filter = filters.reduce( function (a, f) { - return function (d) { - return a(d) && f.run(d) - } - }, function () { return true }) - - filteredData = new NodeFilter(filter)(data) - - targets.forEach( function (t) { - t.setData(filteredData) - }) - } - - function notifyObservers() { - filterObservers.forEach( function (d) { - d.filtersChanged(filters) - }) - } - - function addFilter(d) { - filters.push(d) - notifyObservers() - d.setRefresh(refresh) - refresh() - } - - function removeFilter(d) { - filters = filters.filter( function (e) { return d !== e } ) - notifyObservers() - refresh() - } - - function watchFilters(d) { - filterObservers.push(d) - - d.filtersChanged(filters) - - return function () { - filterObservers = filterObservers.filter( function (e) { return d !== e }) + if (filteredData !== undefined) { + d.setData(filteredData); } } - return { add: add, - remove: remove, - setData: setData, - addFilter: addFilter, - removeFilter: removeFilter, - watchFilters: watchFilters, - refresh: refresh - } - } -}) + function setData(d) { + data = d; + refresh(); + } + + function refresh() { + if (data === undefined) { + return; + } + + var filter = filters.reduce(function (a, f) { + return function (d) { + return a(d) && f.run(d); + }; + }, function () { + return true; + }); + + filteredData = new NodeFilter(filter)(data); + + targets.forEach(function (t) { + t.setData(filteredData); + }); + } + + function notifyObservers() { + filterObservers.forEach(function (d) { + d.filtersChanged(filters); + }); + } + + function addFilter(d) { + filters.push(d); + notifyObservers(); + d.setRefresh(refresh); + refresh(); + } + + function removeFilter(d) { + filters = filters.filter(function (e) { + return d !== e; + }); + notifyObservers(); + refresh(); + } + + function watchFilters(d) { + filterObservers.push(d); + + d.filtersChanged(filters); + + return function () { + filterObservers = filterObservers.filter(function (e) { + return d !== e; + }); + }; + } + + 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 f6c6dac..10629d7 100644 --- a/lib/filters/filtergui.js +++ b/lib/filters/filtergui.js @@ -1,40 +1,43 @@ define([], function () { return function (distributor) { - var container = document.createElement("ul") - container.classList.add("filters") - var div = document.createElement("div") + var container = document.createElement("ul"); + container.classList.add("filters"); + var div = document.createElement("div"); function render(el) { - el.appendChild(div) + el.appendChild(div); } function filtersChanged(filters) { - while (container.firstChild) - container.removeChild(container.firstChild) + while (container.firstChild) { + container.removeChild(container.firstChild); + } - filters.forEach( function (d) { - var li = document.createElement("li") - var div = document.createElement("div") - container.appendChild(li) - li.appendChild(div) - d.render(div) + filters.forEach(function (d) { + var li = document.createElement("li"); + var div = document.createElement("div"); + container.appendChild(li); + li.appendChild(div); + d.render(div); - var button = document.createElement("button") - button.textContent = "" + var button = document.createElement("button"); + button.textContent = ""; button.onclick = function () { - distributor.removeFilter(d) - } - li.appendChild(button) - }) + distributor.removeFilter(d); + }; + li.appendChild(button); + }); - if (container.parentNode === div && filters.length === 0) - div.removeChild(container) - else if (filters.length > 0) - div.appendChild(container) + if (container.parentNode === div && filters.length === 0) { + div.removeChild(container); + } 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 4c2a09d..831f575 100644 --- a/lib/filters/genericnode.js +++ b/lib/filters/genericnode.js @@ -1,52 +1,56 @@ define([], function () { return function (name, key, value, f) { - var negate = false - var refresh + var negate = false; + var refresh; - var label = document.createElement("label") - var strong = document.createElement("strong") - label.textContent = name + " " - label.appendChild(strong) + var label = document.createElement("label"); + var strong = document.createElement("strong"); + label.textContent = name + " "; + label.appendChild(strong); function run(d) { - var o = dictGet(d, key.slice(0)) + var o = dictGet(d, key.slice(0)); - if (f) - o = f(o) + if (f) { + o = f(o); + } - return o === value ? !negate : negate + return o === value ? !negate : negate; } function setRefresh(f) { - refresh = f + refresh = f; } function draw(el) { - if (negate) - el.parentNode.classList.add("not") - else - el.parentNode.classList.remove("not") + if (negate) { + el.parentNode.classList.add("not"); + } else { + el.parentNode.classList.remove("not"); + } - strong.textContent = (negate ? "¬" : "" ) + value + strong.textContent = (negate ? "¬" : "" ) + value; } function render(el) { - el.appendChild(label) - draw(el) + el.appendChild(label); + draw(el); label.onclick = function () { - negate = !negate + negate = !negate; - draw(el) + draw(el); - if (refresh) - 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 17e6b41..1d6bf5f 100644 --- a/lib/filters/nodefilter.js +++ b/lib/filters/nodefilter.js @@ -1,33 +1,36 @@ define([], function () { return function (filter) { return function (data) { - var n = Object.create(data) - n.nodes = {} + var n = Object.create(data); + n.nodes = {}; - for (var key in data.nodes) - n.nodes[key] = data.nodes[key].filter(filter) + for (var key in data.nodes) { + n.nodes[key] = data.nodes[key].filter(filter); + } - var filteredIds = new Set() + var filteredIds = new Set(); - n.graph = {} - n.graph.nodes = data.graph.nodes.filter( function (d) { - var r - if (d.node) - r = filter(d.node) - else - r = filter({}) + n.graph = {}; + n.graph.nodes = data.graph.nodes.filter(function (d) { + var r; + if (d.node) { + r = filter(d.node); + } else { + r = filter({}); + } - if (r) - filteredIds.add(d.id) + if (r) { + filteredIds.add(d.id); + } - return r - }) + return r; + }); - n.graph.links = data.graph.links.filter( function (d) { - return filteredIds.has(d.source.id) && filteredIds.has(d.target.id) - }) + n.graph.links = data.graph.links.filter(function (d) { + return filteredIds.has(d.source.id) && filteredIds.has(d.target.id); + }); - return n - } - } -}) + return n; + }; + }; +}); diff --git a/lib/forcegraph.js b/lib/forcegraph.js index 992f1b9..b9a998c 100644 --- a/lib/forcegraph.js +++ b/lib/forcegraph.js @@ -1,792 +1,844 @@ define(["d3"], function (d3) { - var margin = 200 - var NODE_RADIUS = 15 - var LINE_RADIUS = 7 + var margin = 200; + var NODE_RADIUS = 15; + var LINE_RADIUS = 7; return function (config, linkScale, sidebar, router) { - var self = this - var canvas, ctx, screenRect - var nodesDict, linksDict - var zoomBehavior - var force - var el - var doAnimation = false - var intNodes = [] - var intLinks = [] - var highlight - var highlightedNodes = [] - var highlightedLinks = [] - var nodes = [] - var uplinkNodes = [] - var nonUplinkNodes = [] - var unseenNodes = [] - var unknownNodes = [] - var savedPanZoom + var self = this; + var canvas, ctx, screenRect; + var nodesDict, linksDict; + var zoomBehavior; + var force; + var el; + var doAnimation = false; + var intNodes = []; + var intLinks = []; + var highlight; + var highlightedNodes = []; + var highlightedLinks = []; + var nodes = []; + var uplinkNodes = []; + var nonUplinkNodes = []; + var unseenNodes = []; + var unknownNodes = []; + var savedPanZoom; - var draggedNode + var draggedNode; - var LINK_DISTANCE = 70 + var LINK_DISTANCE = 70; function graphDiameter(nodes) { - return Math.sqrt(nodes.length / Math.PI) * LINK_DISTANCE * 1.41 + return Math.sqrt(nodes.length / Math.PI) * LINK_DISTANCE * 1.41; } function savePositions() { - if (!localStorageTest()) - return + 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)) + localStorage.setItem("graph/nodeposition", JSON.stringify(save)); } function nodeName(d) { - if (d.o.node && d.o.node.nodeinfo) - return d.o.node.nodeinfo.hostname - else - return d.o.id + if (d.o.node && d.o.node.nodeinfo) { + return d.o.node.nodeinfo.hostname; + } else { + return d.o.id; + } } function dragstart() { - var e = translateXY(d3.mouse(el)) + var e = translateXY(d3.mouse(el)); var nodes = intNodes.filter(function (d) { - return distancePoint(e, d) < NODE_RADIUS - }) + return distancePoint(e, d) < NODE_RADIUS; + }); - if (nodes.length === 0) - return + if (nodes.length === 0) { + return; + } - draggedNode = nodes[0] - d3.event.sourceEvent.stopPropagation() - d3.event.sourceEvent.preventDefault() - draggedNode.fixed |= 2 + draggedNode = nodes[0]; + d3.event.sourceEvent.stopPropagation(); + d3.event.sourceEvent.preventDefault(); + draggedNode.fixed |= 2; - draggedNode.px = draggedNode.x - draggedNode.py = draggedNode.y + draggedNode.px = draggedNode.x; + draggedNode.py = draggedNode.y; } function dragmove() { if (draggedNode) { - var e = translateXY(d3.mouse(el)) + var e = translateXY(d3.mouse(el)); - draggedNode.px = e.x - draggedNode.py = e.y - force.resume() + draggedNode.px = e.x; + draggedNode.py = e.y; + force.resume(); } } function dragend() { if (draggedNode) { - d3.event.sourceEvent.stopPropagation() - d3.event.sourceEvent.preventDefault() - draggedNode.fixed &= ~2 - draggedNode = undefined + d3.event.sourceEvent.stopPropagation(); + d3.event.sourceEvent.preventDefault(); + draggedNode.fixed &= ~2; + draggedNode = undefined; } } 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() - var scaleP = zoomBehavior.scale() + var translateP = zoomBehavior.translate(); + var scaleP = zoomBehavior.scale(); if (!doAnimation) { - zoomBehavior.translate(translate) - zoomBehavior.scale(scale) - panzoom() + zoomBehavior.translate(translate); + zoomBehavior.scale(scale); + panzoom(); } else { - var start = {x: translateP[0], y: translateP[1], scale: scaleP} - var end = {x: translate[0], y: translate[1], scale: scale} + var start = {x: translateP[0], y: translateP[1], scale: scaleP}; + var end = {x: translate[0], y: translate[1], scale: scale}; - var interpolate = d3.interpolateObject(start, end) - var duration = 500 + var interpolate = d3.interpolateObject(start, end); + var duration = 500; - var ease = d3.ease("cubic-in-out") + var ease = d3.ease("cubic-in-out"); d3.timer(function (t) { - if (t >= duration) - return true + if (t >= duration) { + return true; + } - var v = interpolate(ease(t / duration)) - zoomBehavior.translate([v.x, v.y]) - zoomBehavior.scale(v.scale) - panzoom() + var v = interpolate(ease(t / duration)); + zoomBehavior.translate([v.x, v.y]); + zoomBehavior.scale(v.scale); + panzoom(); - return false - }) + return false; + }); } } function onPanZoom() { - savedPanZoom = {translate: zoomBehavior.translate(), - scale: zoomBehavior.scale()} - panzoom() + savedPanZoom = { + translate: zoomBehavior.translate(), + scale: zoomBehavior.scale() + }; + panzoom(); } function panzoom() { - var translate = zoomBehavior.translate() - var scale = zoomBehavior.scale() + var translate = zoomBehavior.translate(); + var scale = zoomBehavior.scale(); - panzoomReal(translate, 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) + requestAnimationFrame(redraw); } function getSize() { - var sidebarWidth = sidebar() - var width = el.offsetWidth - sidebarWidth - var height = el.offsetHeight + var sidebarWidth = sidebar(); + var width = el.offsetWidth - sidebarWidth; + var height = el.offsetHeight; - return [width, height] + return [width, height]; } function panzoomTo(a, b) { - var sidebarWidth = sidebar() - var size = getSize() + var sidebarWidth = sidebar(); + var size = getSize(); - var targetWidth = Math.max(1, b[0] - a[0]) - var targetHeight = Math.max(1, b[1] - a[1]) + var targetWidth = Math.max(1, b[0] - a[0]); + var targetHeight = Math.max(1, b[1] - a[1]); - var scaleX = size[0] / targetWidth - var scaleY = size[1] / targetHeight - var scaleMax = zoomBehavior.scaleExtent()[1] - var scale = 0.5 * Math.min(scaleMax, Math.min(scaleX, scaleY)) + var scaleX = size[0] / targetWidth; + var scaleY = size[1] / targetHeight; + var scaleMax = zoomBehavior.scaleExtent()[1]; + var scale = 0.5 * Math.min(scaleMax, Math.min(scaleX, scaleY)); - var centroid = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2] - var x = -centroid[0] * scale + size[0] / 2 - var y = -centroid[1] * scale + size[1] / 2 - var translate = [x + sidebarWidth, y] + var centroid = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]; + var x = -centroid[0] * scale + size[0] / 2; + var y = -centroid[1] * scale + size[1] / 2; + var translate = [x + sidebarWidth, y]; - animatePanzoom(translate, scale) + animatePanzoom(translate, scale); } function updateHighlight(nopanzoom) { - highlightedNodes = [] - highlightedLinks = [] + highlightedNodes = []; + highlightedLinks = []; - if (highlight !== undefined) + if (highlight !== undefined) { if (highlight.type === "node") { - var n = nodesDict[highlight.o.nodeinfo.node_id] + var n = nodesDict[highlight.o.nodeinfo.node_id]; if (n) { - highlightedNodes = [n] - - if (!nopanzoom) - panzoomTo([n.x, n.y], [n.x, n.y]) - } - - return - } else if (highlight.type === "link") { - var l = linksDict[highlight.o.id] - - if (l) { - highlightedLinks = [l] + highlightedNodes = [n]; 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 }) - panzoomTo([x[0], y[0]], [x[1], y[1]]) + panzoomTo([n.x, n.y], [n.x, n.y]); } } - return - } + return; + } else if (highlight.type === "link") { + var l = linksDict[highlight.o.id]; - if (!nopanzoom) - if (!savedPanZoom) - panzoomTo([0, 0], force.size()) - else - animatePanzoom(savedPanZoom.translate, savedPanZoom.scale) + if (l) { + 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; + }); + panzoomTo([x[0], y[0]], [x[1], y[1]]); + } + } + + return; + } + } + + if (!nopanzoom) { + if (!savedPanZoom) { + panzoomTo([0, 0], force.size()); + } else { + animatePanzoom(savedPanZoom.translate, savedPanZoom.scale); + } + } } function drawLabel(d) { var neighbours = d.neighbours.filter(function (d) { - return !d.link.o.isVPN - }) + return !d.link.o.isVPN; + }); var sum = neighbours.reduce(function (a, b) { - return [a[0] + b.node.x, a[1] + b.node.y] - }, [0, 0]) + return [a[0] + b.node.x, a[1] + b.node.y]; + }, [0, 0]); - var sumCos = sum[0] - d.x * neighbours.length - var sumSin = sum[1] - d.y * neighbours.length + var sumCos = sum[0] - d.x * neighbours.length; + var sumSin = sum[1] - d.y * neighbours.length; - var angle = Math.PI / 2 + var angle = Math.PI / 2; - if (neighbours.length > 0) - angle = Math.PI + Math.atan2(sumSin, sumCos) + if (neighbours.length > 0) { + angle = Math.PI + Math.atan2(sumSin, sumCos); + } - var cos = Math.cos(angle) - var sin = Math.sin(angle) + var cos = Math.cos(angle); + var sin = Math.sin(angle); - var width = d.labelWidth - var height = d.labelHeight + var width = d.labelWidth; + var height = d.labelHeight; - var x = d.x + d.labelA * Math.pow(Math.abs(cos), 2 / 5) * Math.sign(cos) - width / 2 - var y = d.y + d.labelB * Math.pow(Math.abs(sin), 2 / 5) * Math.sign(sin) - height / 2 + var x = d.x + d.labelA * Math.pow(Math.abs(cos), 2 / 5) * Math.sign(cos) - width / 2; + var y = d.y + d.labelB * Math.pow(Math.abs(sin), 2 / 5) * Math.sign(sin) - height / 2; - ctx.drawImage(d.label, x, y, width, height) + ctx.drawImage(d.label, x, y, width, height); } function visibleLinks(d) { return (d.o.isVPN || - 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.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); } 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) { - var node = document.createElement("canvas") - node.width = scale * radius * 8 * r - node.height = node.width + var node = document.createElement("canvas"); + node.width = scale * radius * 8 * r; + node.height = node.width; - var nctx = node.getContext("2d") - nctx.scale(scale * r, scale * r) - nctx.save() + var nctx = node.getContext("2d"); + nctx.scale(scale * r, scale * r); + nctx.save(); - nctx.translate(-node.width / scale, -node.height / scale) - nctx.lineWidth = radius + nctx.translate(-node.width / scale, -node.height / scale); + nctx.lineWidth = radius; - nctx.beginPath() - nctx.moveTo(radius, 0) - nctx.arc(0, 0, radius, 0, 2 * Math.PI) + nctx.beginPath(); + nctx.moveTo(radius, 0); + nctx.arc(0, 0, radius, 0, 2 * Math.PI); - nctx.strokeStyle = "rgba(255, 0, 0, 1)" - nctx.shadowOffsetX = node.width * 1.5 + 0 - nctx.shadowOffsetY = node.height * 1.5 + 3 - nctx.shadowBlur = 12 - nctx.shadowColor = "rgba(0, 0, 0, 0.16)" - nctx.stroke() - nctx.shadowOffsetX = node.width * 1.5 + 0 - nctx.shadowOffsetY = node.height * 1.5 + 3 - nctx.shadowBlur = 12 - nctx.shadowColor = "rgba(0, 0, 0, 0.23)" - nctx.stroke() + nctx.strokeStyle = "rgba(255, 0, 0, 1)"; + nctx.shadowOffsetX = node.width * 1.5 + 0; + nctx.shadowOffsetY = node.height * 1.5 + 3; + nctx.shadowBlur = 12; + nctx.shadowColor = "rgba(0, 0, 0, 0.16)"; + nctx.stroke(); + nctx.shadowOffsetX = node.width * 1.5 + 0; + nctx.shadowOffsetY = node.height * 1.5 + 3; + nctx.shadowBlur = 12; + nctx.shadowColor = "rgba(0, 0, 0, 0.23)"; + nctx.stroke(); - nctx.restore() - nctx.translate(node.width / 2 / scale / r, node.height / 2 / scale / r) + nctx.restore(); + nctx.translate(node.width / 2 / scale / r, node.height / 2 / scale / r); - nctx.beginPath() - nctx.moveTo(radius, 0) - nctx.arc(0, 0, radius, 0, 2 * Math.PI) + nctx.beginPath(); + nctx.moveTo(radius, 0); + nctx.arc(0, 0, radius, 0, 2 * Math.PI); - nctx.strokeStyle = color - nctx.lineWidth = radius - nctx.stroke() + nctx.strokeStyle = color; + nctx.lineWidth = radius; + nctx.stroke(); - return node + return node; } function redraw() { - var r = window.devicePixelRatio - var translate = zoomBehavior.translate() - var scale = zoomBehavior.scale() - var links = intLinks.filter(visibleLinks) + var r = window.devicePixelRatio; + var translate = zoomBehavior.translate(); + var scale = zoomBehavior.scale(); + var links = intLinks.filter(visibleLinks); - ctx.save() - ctx.setTransform(1, 0, 0, 1, 0, 0) - ctx.clearRect(0, 0, canvas.width, canvas.height) - ctx.restore() + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.restore(); - ctx.save() - ctx.translate(translate[0], translate[1]) - ctx.scale(scale, scale) + ctx.save(); + ctx.translate(translate[0], translate[1]); + ctx.scale(scale, scale); - var clientColor = "rgba(230, 50, 75, 1.0)" - var unknownColor = "#D10E2A" - var nonUplinkColor = "#F2E3C6" - var uplinkColor = "#5BAAEB" - var unseenColor = "#FFA726" - var highlightColor = "rgba(252, 227, 198, 0.15)" - var nodeRadius = 6 - var cableColor = "#50B0F0" + var clientColor = "rgba(230, 50, 75, 1.0)"; + var unknownColor = "#D10E2A"; + var nonUplinkColor = "#F2E3C6"; + var uplinkColor = "#5BAAEB"; + var unseenColor = "#FFA726"; + var highlightColor = "rgba(252, 227, 198, 0.15)"; + var nodeRadius = 6; + var cableColor = "#50B0F0"; // -- draw links -- - ctx.save() + ctx.save(); links.forEach(function (d) { - var dx = d.target.x - d.source.x - var dy = d.target.y - d.source.y - var a = Math.sqrt(dx * dx + dy * dy) * 2 - dx /= a - dy /= a + var dx = d.target.x - d.source.x; + var dy = d.target.y - d.source.y; + var a = Math.sqrt(dx * dx + dy * dy) * 2; + dx /= a; + dy /= a; - var distancex = d.target.x - d.source.x - (10 * dx) - var distancey = d.target.y - d.source.y - (10 * dy) + var distancex = d.target.x - d.source.x - (10 * dx); + var distancey = d.target.y - d.source.y - (10 * dy); - ctx.beginPath() - ctx.moveTo(d.source.x + dx * nodeRadius, d.source.y + dy * nodeRadius) - ctx.lineTo(d.target.x - (distancex / 2) - dx * nodeRadius, d.target.y - (distancey / 2) - dy * nodeRadius) - ctx.strokeStyle = d.o.type === "Kabel" ? cableColor : d.color - ctx.globalAlpha = d.o.isVPN ? 0.1 : 0.8 - ctx.lineWidth = d.o.isVPN ? 1.5 : 2.5 - ctx.stroke() - }) + ctx.beginPath(); + ctx.moveTo(d.source.x + dx * nodeRadius, d.source.y + dy * nodeRadius); + ctx.lineTo(d.target.x - (distancex / 2) - dx * nodeRadius, d.target.y - (distancey / 2) - dy * nodeRadius); + ctx.strokeStyle = d.o.type === "Kabel" ? cableColor : d.color; + ctx.globalAlpha = d.o.isVPN ? 0.1 : 0.8; + ctx.lineWidth = d.o.isVPN ? 1.5 : 2.5; + ctx.stroke(); + }); - ctx.restore() + ctx.restore(); // -- draw unknown nodes -- - ctx.beginPath() + ctx.beginPath(); unknownNodes.filter(visibleNodes).forEach(function (d) { - ctx.moveTo(d.x + nodeRadius, d.y) - ctx.arc(d.x, d.y, nodeRadius, 0, 2 * Math.PI) - }) + ctx.moveTo(d.x + nodeRadius, d.y); + ctx.arc(d.x, d.y, nodeRadius, 0, 2 * Math.PI); + }); - ctx.strokeStyle = unknownColor - ctx.lineWidth = nodeRadius + ctx.strokeStyle = unknownColor; + ctx.lineWidth = nodeRadius; - ctx.stroke() + ctx.stroke(); // -- draw nodes -- - ctx.save() - ctx.scale(1 / scale / r, 1 / scale / r) + ctx.save(); + ctx.scale(1 / scale / r, 1 / scale / r); - var nonUplinkNode = drawNode(nonUplinkColor, nodeRadius, scale, r) + var nonUplinkNode = drawNode(nonUplinkColor, nodeRadius, scale, r); nonUplinkNodes.filter(visibleNodes).forEach(function (d) { - ctx.drawImage(nonUplinkNode, scale * r * d.x - nonUplinkNode.width / 2, scale * r * d.y - nonUplinkNode.height / 2) - }) + ctx.drawImage(nonUplinkNode, scale * r * d.x - nonUplinkNode.width / 2, scale * r * d.y - nonUplinkNode.height / 2); + }); - var uplinkNode = drawNode(uplinkColor, nodeRadius, scale, r) + var uplinkNode = drawNode(uplinkColor, nodeRadius, scale, r); uplinkNodes.filter(visibleNodes).forEach(function (d) { - ctx.drawImage(uplinkNode, scale * r * d.x - uplinkNode.width / 2, scale * r * d.y - uplinkNode.height / 2) - }) + ctx.drawImage(uplinkNode, scale * r * d.x - uplinkNode.width / 2, scale * r * d.y - uplinkNode.height / 2); + }); - var unseenNode = drawNode(unseenColor, nodeRadius, scale, r) + var unseenNode = drawNode(unseenColor, nodeRadius, scale, r); unseenNodes.filter(visibleNodes).forEach(function (d) { - ctx.drawImage(unseenNode, scale * r * d.x - unseenNode.width / 2, scale * r * d.y - unseenNode.height / 2) - }) + ctx.drawImage(unseenNode, scale * r * d.x - unseenNode.width / 2, scale * r * d.y - unseenNode.height / 2); + }); - ctx.restore() + ctx.restore(); // -- draw clients -- - ctx.save() - ctx.beginPath() - if (scale > 0.9) + ctx.save(); + ctx.beginPath(); + if (scale > 0.9) { nodes.filter(visibleNodes).forEach(function (d) { - var clients = d.o.node.statistics.clients - if (clients === 0) - return + var clients = d.o.node.statistics.clients; + if (clients === 0) { + return; + } - var startDistance = 16 - var radius = 3 - var a = 1.2 - var startAngle = Math.PI + var startDistance = 16; + var radius = 3; + var a = 1.2; + var startAngle = Math.PI; for (var orbit = 0, i = 0; i < clients; orbit++) { - var distance = startDistance + orbit * 2 * radius * a - var n = Math.floor((Math.PI * distance) / (a * radius)) - var delta = clients - i + var distance = startDistance + orbit * 2 * radius * a; + var n = Math.floor((Math.PI * distance) / (a * radius)); + var delta = clients - i; for (var j = 0; j < Math.min(delta, n); i++, j++) { - var angle = 2 * Math.PI / n * j - var x = d.x + distance * Math.cos(angle + startAngle) - var y = d.y + distance * Math.sin(angle + startAngle) + var angle = 2 * Math.PI / n * j; + var x = d.x + distance * Math.cos(angle + startAngle); + var y = d.y + distance * Math.sin(angle + startAngle); - ctx.moveTo(x, y) - ctx.arc(x, y, radius, 0, 2 * Math.PI) + ctx.moveTo(x, y); + ctx.arc(x, y, radius, 0, 2 * Math.PI); } } - }) + }); + } - ctx.fillStyle = clientColor - ctx.fill() - ctx.restore() + ctx.fillStyle = clientColor; + ctx.fill(); + ctx.restore(); // -- draw node highlights -- if (highlightedNodes.length) { - ctx.save() - ctx.shadowColor = "rgba(255, 255, 255, 1.0)" - ctx.shadowBlur = 10 * nodeRadius - ctx.shadowOffsetX = 0 - ctx.shadowOffsetY = 0 - ctx.globalCompositeOperation = "lighten" - ctx.fillStyle = highlightColor + ctx.save(); + ctx.shadowColor = "rgba(255, 255, 255, 1.0)"; + ctx.shadowBlur = 10 * nodeRadius; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.globalCompositeOperation = "lighten"; + ctx.fillStyle = highlightColor; - ctx.beginPath() + ctx.beginPath(); highlightedNodes.forEach(function (d) { - ctx.moveTo(d.x + 5 * nodeRadius, d.y) - ctx.arc(d.x, d.y, 5 * nodeRadius, 0, 2 * Math.PI) - }) - ctx.fill() + ctx.moveTo(d.x + 5 * nodeRadius, d.y); + ctx.arc(d.x, d.y, 5 * nodeRadius, 0, 2 * Math.PI); + }); + ctx.fill(); - ctx.restore() + ctx.restore(); } // -- draw link highlights -- if (highlightedLinks.length) { - ctx.save() - ctx.lineWidth = 2 * 5 * nodeRadius - ctx.shadowColor = "rgba(255, 255, 255, 1.0)" - ctx.shadowBlur = 10 * nodeRadius - ctx.shadowOffsetX = 0 - ctx.shadowOffsetY = 0 - ctx.globalCompositeOperation = "lighten" - ctx.strokeStyle = highlightColor - ctx.lineCap = "round" + ctx.save(); + ctx.lineWidth = 2 * 5 * nodeRadius; + ctx.shadowColor = "rgba(255, 255, 255, 1.0)"; + ctx.shadowBlur = 10 * nodeRadius; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.globalCompositeOperation = "lighten"; + ctx.strokeStyle = highlightColor; + ctx.lineCap = "round"; - ctx.beginPath() + ctx.beginPath(); highlightedLinks.forEach(function (d) { - ctx.moveTo(d.source.x, d.source.y) - ctx.lineTo(d.target.x, d.target.y) - }) - ctx.stroke() + ctx.moveTo(d.source.x, d.source.y); + ctx.lineTo(d.target.x, d.target.y); + }); + ctx.stroke(); - ctx.restore() + ctx.restore(); } // -- draw labels -- - if (scale > 0.9) - intNodes.filter(visibleNodes).forEach(drawLabel, scale) + if (scale > 0.9) { + intNodes.filter(visibleNodes).forEach(drawLabel, scale); + } - ctx.restore() + ctx.restore(); } function tickEvent() { - redraw() + redraw(); } function resizeCanvas() { - var r = window.devicePixelRatio - canvas.width = el.offsetWidth * r - canvas.height = el.offsetHeight * r - canvas.style.width = el.offsetWidth + "px" - canvas.style.height = el.offsetHeight + "px" - ctx.setTransform(1, 0, 0, 1, 0, 0) - ctx.scale(r, r) - requestAnimationFrame(redraw) + var r = window.devicePixelRatio; + canvas.width = el.offsetWidth * r; + canvas.height = el.offsetHeight * r; + canvas.style.width = el.offsetWidth + "px"; + canvas.style.height = el.offsetHeight + "px"; + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(r, r); + requestAnimationFrame(redraw); } function distance(ax, ay, bx, by) { - return Math.pow(ax - bx, 2) + Math.pow(ay - by, 2) + return Math.pow(ax - bx, 2) + Math.pow(ay - by, 2); } function distancePoint(a, b) { - return Math.sqrt(distance(a.x, a.y, b.x, b.y)) + return Math.sqrt(distance(a.x, a.y, b.x, b.y)); } function distanceLink(p, a, b) { /* http://stackoverflow.com/questions/849211 */ - var bx = b.x - ((b.x - a.x) / 2) - var by = b.y - ((b.y - a.y) / 2) + var bx = b.x - ((b.x - a.x) / 2); + var by = b.y - ((b.y - a.y) / 2); - var l2 = distance(a.x, a.y, bx, by) + var l2 = distance(a.x, a.y, bx, by); - if (l2 === 0) - return distance(p.x, p.y, a.x, a.y) + if (l2 === 0) { + return distance(p.x, p.y, a.x, a.y); + } - var t = ((p.x - a.x) * (bx - a.x) + (p.y - a.y) * (by - a.y)) / l2 + var t = ((p.x - a.x) * (bx - a.x) + (p.y - a.y) * (by - a.y)) / l2; - if (t < 0) - return distance(p.x, p.y, a.x, a.y) + if (t < 0) { + return distance(p.x, p.y, a.x, a.y); + } - if (t > 1) - return distance(p.x, p.y, bx, by) + if (t > 1) { + return distance(p.x, p.y, bx, by); + } - return Math.sqrt(distance(p.x, p.y, a.x + t * (bx - a.x), a.y + t * (by - a.y) )) + return Math.sqrt(distance(p.x, p.y, a.x + t * (bx - a.x), a.y + t * (by - a.y))); } function translateXY(d) { - var translate = zoomBehavior.translate() - var scale = zoomBehavior.scale() + 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) - return + if (d3.event.defaultPrevented) { + return; + } - var e = translateXY(d3.mouse(el)) + var e = translateXY(d3.mouse(el)); var nodes = intNodes.filter(function (d) { - return distancePoint(e, d) < NODE_RADIUS - }) + return distancePoint(e, d) < NODE_RADIUS; + }); if (nodes.length > 0) { - router.node(nodes[0].o.node)() - return + router.node(nodes[0].o.node)(); + return; } var links = intLinks.filter(function (d) { - return !d.o.isVPN + return !d.o.isVPN; }).filter(function (d) { - return distanceLink(e, d.source, d.target) < LINE_RADIUS - }) + return distanceLink(e, d.source, d.target) < LINE_RADIUS; + }); if (links.length > 0) { - router.link(links[0].o)() - return + router.link(links[0].o)(); + } } function zoom(z, scale) { - var size = getSize() - var newSize = [size[0] / scale, size[1] / scale] + var size = getSize(); + var newSize = [size[0] / scale, size[1] / scale]; - var sidebarWidth = sidebar() - var delta = [size[0] - newSize[0], size[1] - newSize[1]] - var translate = z.translate() - var translateNew = [sidebarWidth + (translate[0] - sidebarWidth - delta[0] / 2) * scale, (translate[1] - delta[1] / 2) * scale] + var sidebarWidth = sidebar(); + var delta = [size[0] - newSize[0], size[1] - newSize[1]]; + var translate = z.translate(); + var translateNew = [sidebarWidth + (translate[0] - sidebarWidth - delta[0] / 2) * scale, (translate[1] - delta[1] / 2) * scale]; - animatePanzoom(translateNew, z.scale() * scale) + animatePanzoom(translateNew, z.scale() * scale); } function keyboardZoom(z) { return function () { - var e = d3.event + var e = d3.event; - if (e.altKey || e.ctrlKey || e.metaKey) - return + if (e.altKey || e.ctrlKey || e.metaKey) { + return; + } - if (e.keyCode === 43) - zoom(z, 1.41) + if (e.keyCode === 43) { + zoom(z, 1.41); + } - if (e.keyCode === 45) - zoom(z, 1 / 1.41) - } + if (e.keyCode === 45) { + zoom(z, 1 / 1.41); + } + }; } - el = document.createElement("div") - el.classList.add("graph") + el = document.createElement("div"); + 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") + ctx = canvas.getContext("2d"); force = d3.layout.force() - .charge(-250) - .gravity(0.1) - .linkDistance(function (d) { - if (d.o.isVPN) - return 0 - else - return LINK_DISTANCE - }) - .linkStrength(function (d) { - if (d.o.isVPN) - 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.isVPN) { + return 0; + } else { + return LINK_DISTANCE; + } + }) + .linkStrength(function (d) { + if (d.o.isVPN) { + return 0; + } else { + return Math.max(0.5, 1 / d.o.tq); + } + }) + .on("tick", tickEvent) + .on("end", savePositions); - window.addEventListener("resize", resizeCanvas) + window.addEventListener("resize", resizeCanvas); - panzoom() + panzoom(); self.setData = function (data) { - var oldNodes = {} - - intNodes.forEach( function (d) { - oldNodes[d.o.id] = d - }) - - intNodes = data.graph.nodes.map( function (d) { - var e - if (d.id in oldNodes) - e = oldNodes[d.id] - else - e = {} - - e.o = d - - return e - }) - - var newNodesDict = {} - - intNodes.forEach( function (d) { - newNodesDict[d.o.id] = d - }) - - var oldLinks = {} - - intLinks.forEach( function (d) { - oldLinks[d.o.id] = d - }) - - intLinks = data.graph.links.map( function (d) { - var e - if (d.id in oldLinks) - e = oldLinks[d.id] - else - e = {} - - e.o = d - e.source = newNodesDict[d.source.id] - e.target = newNodesDict[d.target.id] - - if (d.isVPN) - e.color = "rgba(255, 255, 255, " + (0.6 / d.tq) + ")" - else - e.color = linkScale(d.tq).hex() - - return e - }) - - linksDict = {} - nodesDict = {} + var oldNodes = {}; intNodes.forEach(function (d) { - d.neighbours = {} + oldNodes[d.o.id] = d; + }); - if (d.o.node) - nodesDict[d.o.node.nodeinfo.node_id] = d + intNodes = data.graph.nodes.map(function (d) { + var e; + if (d.id in oldNodes) { + e = oldNodes[d.id]; + } else { + e = {}; + } - var name = nodeName(d) + e.o = d; - var offset = 5 - var lineWidth = 3 - var buffer = document.createElement("canvas") - var r = window.devicePixelRatio - var bctx = buffer.getContext("2d") - bctx.font = "11px Roboto" - var width = bctx.measureText(name).width - var scale = zoomBehavior.scaleExtent()[1] * r - buffer.width = (width + 2 * lineWidth) * scale - buffer.height = (16 + 2 * lineWidth) * scale - bctx.scale(scale, scale) - bctx.textBaseline = "middle" - bctx.textAlign = "center" - bctx.fillStyle = "rgba(242, 227, 198, 1.0)" - bctx.shadowColor = "rgba(0, 0, 0, 1)" - bctx.shadowBlur = 5 - bctx.fillText(name, buffer.width / (2 * scale), buffer.height / (2 * scale)) + return e; + }); - d.label = buffer - d.labelWidth = buffer.width / scale - d.labelHeight = buffer.height / scale - d.labelA = offset + buffer.width / (2 * scale) - d.labelB = offset + buffer.height / (2 * scale) - }) + var newNodesDict = {}; + + intNodes.forEach(function (d) { + newNodesDict[d.o.id] = d; + }); + + var oldLinks = {}; intLinks.forEach(function (d) { - d.source.neighbours[d.target.o.id] = {node: d.target, link: d} - d.target.neighbours[d.source.o.id] = {node: d.source, link: d} + oldLinks[d.o.id] = d; + }); - if (d.o.source && d.o.target) - linksDict[d.o.id] = d - }) + intLinks = data.graph.links.map(function (d) { + var e; + if (d.id in oldLinks) { + e = oldLinks[d.id]; + } else { + e = {}; + } + + e.o = d; + e.source = newNodesDict[d.source.id]; + e.target = newNodesDict[d.target.id]; + + if (d.isVPN) { + e.color = "rgba(255, 255, 255, " + (0.6 / d.tq) + ")"; + } else { + e.color = linkScale(d.tq).hex(); + } + + return e; + }); + + linksDict = {}; + nodesDict = {}; + + intNodes.forEach(function (d) { + d.neighbours = {}; + + if (d.o.node) { + nodesDict[d.o.node.nodeinfo.node_id] = d; + } + + var name = nodeName(d); + + var offset = 5; + var lineWidth = 3; + var buffer = document.createElement("canvas"); + var r = window.devicePixelRatio; + var bctx = buffer.getContext("2d"); + bctx.font = "11px Roboto"; + var width = bctx.measureText(name).width; + var scale = zoomBehavior.scaleExtent()[1] * r; + buffer.width = (width + 2 * lineWidth) * scale; + buffer.height = (16 + 2 * lineWidth) * scale; + bctx.scale(scale, scale); + bctx.textBaseline = "middle"; + bctx.textAlign = "center"; + bctx.fillStyle = "rgba(242, 227, 198, 1.0)"; + bctx.shadowColor = "rgba(0, 0, 0, 1)"; + bctx.shadowBlur = 5; + bctx.fillText(name, buffer.width / (2 * scale), buffer.height / (2 * scale)); + + d.label = buffer; + d.labelWidth = buffer.width / scale; + d.labelHeight = buffer.height / scale; + d.labelA = offset + buffer.width / (2 * scale); + d.labelB = offset + buffer.height / (2 * scale); + }); intLinks.forEach(function (d) { - if (linksDict[d.target.o.node_id + "-" + d.source.o.node_id]) - return + d.source.neighbours[d.target.o.id] = {node: d.target, link: d}; + d.target.neighbours[d.source.o.id] = {node: d.source, link: d}; - var obj = { source: d.target, target: d.source, o: { isVPN: d.o.isVPN, type: "dead", id: d.target.o.node_id + "-" + d.source.o.node_id, tq: 1 }, color: "rgba(255, 255, 255, 0.6)" } - intLinks.push(obj) - linksDict[d.target.o.node_id + "-" + d.source.o.node_id] = obj - }) + if (d.o.source && d.o.target) { + linksDict[d.o.id] = d; + } + }); + + intLinks.forEach(function (d) { + if (linksDict[d.target.o.node_id + "-" + d.source.o.node_id]) { + return; + } + + var obj = { + source: d.target, + target: d.source, + o: {isVPN: d.o.isVPN, type: "dead", id: d.target.o.node_id + "-" + d.source.o.node_id, tq: 1}, + color: "rgba(255, 255, 255, 0.6)" + }; + intLinks.push(obj); + linksDict[d.target.o.node_id + "-" + d.source.o.node_id] = obj; + }); intNodes.forEach(function (d) { d.neighbours = Object.keys(d.neighbours).map(function (k) { - return d.neighbours[k] - }) - }) + return d.neighbours[k]; + }); + }); - 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")) + var save = JSON.parse(localStorage.getItem("graph/nodeposition")); if (save) { - var nodePositions = {} - save.forEach( function (d) { - nodePositions[d.id] = d - }) + var nodePositions = {}; + 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 + d.x = nodePositions[d.o.id].x; + d.y = nodePositions[d.o.id].y; } - }) + }); } } - var diameter = graphDiameter(intNodes) + var diameter = graphDiameter(intNodes); force.nodes(intNodes) - .links(intLinks) - .size([diameter, diameter]) + .links(intLinks) + .size([diameter, diameter]); - updateHighlight(true) + updateHighlight(true); - force.start() - resizeCanvas() - } + force.start(); + resizeCanvas(); + }; self.resetView = function () { - highlight = undefined - updateHighlight() - doAnimation = true - } + highlight = undefined; + updateHighlight(); + doAnimation = true; + }; self.gotoNode = function (d) { - highlight = {type: "node", o: d} - updateHighlight() - doAnimation = true - } + highlight = {type: "node", o: d}; + updateHighlight(); + doAnimation = true; + }; self.gotoLink = function (d) { - highlight = {type: "link", o: d} - updateHighlight() - doAnimation = true - } + highlight = {type: "link", o: d}; + updateHighlight(); + doAnimation = true; + }; self.destroy = function () { - force.stop() - canvas.remove() - force = null + force.stop(); + canvas.remove(); + force = null; - if (el.parentNode) - el.parentNode.removeChild(el) - } + if (el.parentNode) { + el.parentNode.removeChild(el); + } + }; self.render = function (d) { - d.appendChild(el) - resizeCanvas() - updateHighlight() - } + d.appendChild(el); + resizeCanvas(); + updateHighlight(); + }; - return self - } -}) + return self; + }; +}); diff --git a/lib/gui.js b/lib/gui.js index 4e14c50..f757e29 100644 --- a/lib/gui.js +++ b/lib/gui.js @@ -1,125 +1,127 @@ -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 - } - - function addContent(K) { - removeContent() - - content = new K(config, linkScale, sidebar.getWidth, router, buttons) - content.render(contentDiv) - - fanout.add(content) - router.addTarget(content) - } - - function mkView(K) { - return function () { - addContent(K) + content = null; } - } - var loader = document.getElementsByClassName("loader")[0] - loader.classList.add("hide") + function addContent(K) { + removeContent(); - contentDiv = document.createElement("div") - contentDiv.classList.add("content") - document.body.appendChild(contentDiv) + content = new K(config, linkScale, sidebar.getWidth, router, buttons); + content.render(contentDiv); - sidebar = new Sidebar(document.body) + fanout.add(content); + router.addTarget(content); + } - contentDiv.appendChild(buttons) + function mkView(K) { + return function () { + addContent(K); + }; + } - var buttonToggle = document.createElement("button") - buttonToggle.textContent = "\uF133" - buttonToggle.onclick = function () { - if (content.constructor === Map) - router.view("g") - else - router.view("m") - } + var loader = document.getElementsByClassName("loader")[0]; + loader.classList.add("hide"); - buttons.appendChild(buttonToggle) + contentDiv = document.createElement("div"); + contentDiv.classList.add("content"); + document.body.appendChild(contentDiv); - var title = new Title(config) + sidebar = new Sidebar(document.body); - 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() + contentDiv.appendChild(buttons); - fanoutUnfiltered.add(meshstats) - fanoutUnfiltered.add(newnodeslist) - fanoutUnfiltered.add(lostnodeslist) - fanout.add(nodelist) - fanout.add(linklist) - fanout.add(statistics) + var buttonToggle = document.createElement("button"); + buttonToggle.textContent = "\uF133"; + buttonToggle.onclick = function () { + if (content.constructor === Map) { + router.view("g"); + } else { + router.view("m"); + } + }; - sidebar.add(header) - header.add(meshstats) - header.add(legend) + buttons.appendChild(buttonToggle); - overview.add(newnodeslist) - overview.add(lostnodeslist) + var title = new Title(config); - var filterGUI = new FilterGUI(fanout) - fanout.watchFilters(filterGUI) - header.add(filterGUI) + 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(); - sidebar.add(tabs) - tabs.add("Aktuelles", overview) - tabs.add("Knoten", nodelist) - tabs.add("Verbindungen", linklist) - tabs.add("Statistiken", statistics) - tabs.add("Über", about) + fanoutUnfiltered.add(meshstats); + fanoutUnfiltered.add(newnodeslist); + fanoutUnfiltered.add(lostnodeslist); + fanout.add(nodelist); + fanout.add(linklist); + fanout.add(statistics); - router.addTarget(title) - router.addTarget(infobox) + sidebar.add(header); + header.add(meshstats); + header.add(legend); - router.addView("m", mkView(Map)) - router.addView("g", mkView(ForceGraph)) + overview.add(newnodeslist); + overview.add(lostnodeslist); - router.view("m") + var filterGUI = new FilterGUI(fanout); + fanout.watchFilters(filterGUI); + header.add(filterGUI); - self.setData = fanoutUnfiltered.setData + sidebar.add(tabs); + tabs.add("Aktuelles", overview); + tabs.add("Knoten", nodelist); + tabs.add("Verbindungen", linklist); + tabs.add("Statistiken", statistics); + tabs.add("Über", about); - return self - } -}) + 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 82b2d9f..2d5e8c0 100644 --- a/lib/infobox/link.js +++ b/lib/infobox/link.js @@ -1,48 +1,49 @@ define(function () { function showStatImg(o, d) { - var subst = {} - subst["{SOURCE}"] = d.source.node_id - subst["{SOURCE_NAME}"] = d.source.node.nodeinfo.hostname ? d.source.node.nodeinfo.hostname : "unknown" - subst["{TARGET}"] = d.target.node_id - subst["{TARGET_NAME}"] = d.target.node.nodeinfo.hostname ? d.target.node.nodeinfo.hostname : "unknown" - return showStat(o, subst) + var subst = {}; + subst["{SOURCE}"] = d.source.node_id; + subst["{SOURCE_NAME}"] = d.source.node.nodeinfo.hostname ? d.source.node.nodeinfo.hostname : "unknown"; + subst["{TARGET}"] = d.target.node_id; + subst["{TARGET_NAME}"] = d.target.node.nodeinfo.hostname ? d.target.node.nodeinfo.hostname : "unknown"; + return showStat(o, subst); } return function (config, el, router, d) { - var unknown = !(d.source.node) - var h2 = document.createElement("h2") - var a1 = document.createElement("a") + var unknown = !(d.source.node); + var h2 = document.createElement("h2"); + var a1 = document.createElement("a"); if (!unknown) { - a1.href = "#" - a1.onclick = router.node(d.source.node) + a1.href = "#"; + a1.onclick = router.node(d.source.node); } - a1.textContent = unknown ? d.source.id : d.source.node.nodeinfo.hostname - h2.appendChild(a1) - h2.appendChild(document.createTextNode(" → ")) - var a2 = document.createElement("a") - a2.href = "#" - a2.onclick = router.node(d.target.node) - a2.textContent = d.target.node.nodeinfo.hostname - h2.appendChild(a2) - el.appendChild(h2) + a1.textContent = unknown ? d.source.id : d.source.node.nodeinfo.hostname; + h2.appendChild(a1); + h2.appendChild(document.createTextNode(" → ")); + var a2 = document.createElement("a"); + a2.href = "#"; + a2.onclick = router.node(d.target.node); + a2.textContent = d.target.node.nodeinfo.hostname; + h2.appendChild(a2); + el.appendChild(h2); - var attributes = document.createElement("table") - attributes.classList.add("attributes") + var attributes = document.createElement("table"); + attributes.classList.add("attributes"); - attributeEntry(attributes, "TQ", showTq(d)) - attributeEntry(attributes, "Entfernung", showDistance(d)) - attributeEntry(attributes, "Typ", d.type) - var hw1 = unknown ? null : dictGet(d.source.node.nodeinfo, ["hardware", "model"]) - var hw2 = dictGet(d.target.node.nodeinfo, ["hardware", "model"]) - attributeEntry(attributes, "Hardware", (hw1 != null ? hw1 : "unbekannt") + " – " + (hw2 != null ? hw2 : "unbekannt")) - el.appendChild(attributes) + attributeEntry(attributes, "TQ", showTq(d)); + attributeEntry(attributes, "Entfernung", showDistance(d)); + attributeEntry(attributes, "Typ", d.type); + var hw1 = unknown ? null : dictGet(d.source.node.nodeinfo, ["hardware", "model"]); + var hw2 = dictGet(d.target.node.nodeinfo, ["hardware", "model"]); + attributeEntry(attributes, "Hardware", (hw1 != null ? hw1 : "unbekannt") + " – " + (hw2 != null ? hw2 : "unbekannt")); + el.appendChild(attributes); - if (config.linkInfos) - config.linkInfos.forEach( function (linkInfo) { - var h4 = document.createElement("h4") - h4.textContent = linkInfo.name - el.appendChild(h4) - el.appendChild(showStatImg(linkInfo, d)) - }) - } -}) + if (config.linkInfos) { + config.linkInfos.forEach(function (linkInfo) { + var h4 = document.createElement("h4"); + h4.textContent = linkInfo.name; + el.appendChild(h4); + el.appendChild(showStatImg(linkInfo, d)); + }); + } + }; +}); diff --git a/lib/infobox/location.js b/lib/infobox/location.js index 08805b0..9910d33 100644 --- a/lib/infobox/location.js +++ b/lib/infobox/location.js @@ -1,100 +1,103 @@ define(function () { return function (config, el, router, d) { - var sidebarTitle = document.createElement("h2") - sidebarTitle.textContent = "Location: " + d.toString() - el.appendChild(sidebarTitle) + var sidebarTitle = document.createElement("h2"); + sidebarTitle.textContent = "Location: " + d.toString(); + 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" - editLat.value = d.lat.toFixed(9) - el.appendChild(createBox("lat", "Breitengrad", editLat)) + var editLat = document.createElement("input"); + editLat.type = "text"; + editLat.value = d.lat.toFixed(9); + el.appendChild(createBox("lat", "Breitengrad", editLat)); - var editLng = document.createElement("input") - editLng.type = "text" - editLng.value = d.lng.toFixed(9) - el.appendChild(createBox("lng", "Längengrad", editLng)) + var editLng = document.createElement("input"); + editLng.type = "text"; + editLng.value = d.lng.toFixed(9); + el.appendChild(createBox("lng", "Längengrad", editLng)); - var editUci = document.createElement("textarea") + var editUci = document.createElement("textarea"); editUci.value = "uci set gluon-node-info.@location[0]='location'; " + "uci set gluon-node-info.@location[0].share_location='1';" + "uci set gluon-node-info.@location[0].latitude='" + d.lat.toFixed(9) + "';" + "uci set gluon-node-info.@location[0].longitude='" + d.lng.toFixed(9) + "';" + - "uci commit gluon-node-info" + "uci commit gluon-node-info"; - el.appendChild(createBox("uci", "Befehl", editUci, false)) + el.appendChild(createBox("uci", "Befehl", editUci, false)); - var linkPlain = document.createElement("a") - linkPlain.textContent = "plain" - linkPlain.onclick = function() { - switch2plain() - return false - } - linkPlain.href = "#" + var linkPlain = document.createElement("a"); + linkPlain.textContent = "plain"; + linkPlain.onclick = function () { + switch2plain(); + return false; + }; + linkPlain.href = "#"; - var linkUci = document.createElement("a") - linkUci.textContent = "uci" - linkUci.onclick = function() { - switch2uci() - return false - } - linkUci.href = "#" + var linkUci = document.createElement("a"); + linkUci.textContent = "uci"; + linkUci.onclick = function () { + switch2uci(); + return false; + }; + linkUci.href = "#"; - var hintText = document.createElement("p") - hintText.appendChild(document.createTextNode("Du kannst zwischen ")) - hintText.appendChild(linkPlain) - hintText.appendChild(document.createTextNode(" und ")) - hintText.appendChild(linkUci) - hintText.appendChild(document.createTextNode(" wechseln.")) - el.appendChild(hintText) + var hintText = document.createElement("p"); + hintText.appendChild(document.createTextNode("Du kannst zwischen ")); + hintText.appendChild(linkPlain); + hintText.appendChild(document.createTextNode(" und ")); + hintText.appendChild(linkUci); + hintText.appendChild(document.createTextNode(" wechseln.")); + el.appendChild(hintText); function createBox(name, title, inputElem, isVisible) { - var visible = typeof isVisible !== "undefined" ? isVisible : true - var box = document.createElement("div") - var heading = document.createElement("h3") - heading.textContent = title - box.appendChild(heading) - var btn = document.createElement("button") - btn.className = "ion-ios-copy" - btn.title = "Kopieren" - btn.onclick = function() { copy2clip(inputElem.id) } - inputElem.id = "location-" + name - inputElem.readOnly = true - var line = document.createElement("p") - line.appendChild(inputElem) - line.appendChild(btn) - box.appendChild(line) - box.id = "box-" + name - box.style.display = visible ? "block" : "none" - return box + var visible = typeof isVisible !== "undefined" ? isVisible : true; + var box = document.createElement("div"); + var heading = document.createElement("h3"); + heading.textContent = title; + box.appendChild(heading); + var btn = document.createElement("button"); + btn.className = "ion-ios-copy"; + btn.title = "Kopieren"; + btn.onclick = function () { + copy2clip(inputElem.id); + }; + inputElem.id = "location-" + name; + inputElem.readOnly = true; + var line = document.createElement("p"); + line.appendChild(inputElem); + line.appendChild(btn); + box.appendChild(line); + box.id = "box-" + name; + box.style.display = visible ? "block" : "none"; + return box; } function copy2clip(id) { - var copyField = document.querySelector("#" + id) - copyField.select() + var copyField = document.querySelector("#" + id); + copyField.select(); try { - document.execCommand("copy") + document.execCommand("copy"); } catch (err) { - console.log(err) + console.log(err); } } function switch2plain() { - document.getElementById("box-uci").style.display = "none" - document.getElementById("box-lat").style.display = "block" - document.getElementById("box-lng").style.display = "block" + document.getElementById("box-uci").style.display = "none"; + document.getElementById("box-lat").style.display = "block"; + document.getElementById("box-lng").style.display = "block"; } function switch2uci() { - document.getElementById("box-uci").style.display = "block" - document.getElementById("box-lat").style.display = "none" - document.getElementById("box-lng").style.display = "none" + document.getElementById("box-uci").style.display = "block"; + document.getElementById("box-lat").style.display = "none"; + document.getElementById("box-lng").style.display = "none"; } - } -}) + }; +}); diff --git a/lib/infobox/main.js b/lib/infobox/main.js index deebc03..dc900e4 100644 --- a/lib/infobox/main.js +++ b/lib/infobox/main.js @@ -1,51 +1,51 @@ define(["infobox/link", "infobox/node", "infobox/location"], function (Link, Node, Location) { return function (config, sidebar, router) { - var self = this - var el + var self = this; + var el; function destroy() { if (el && el.parentNode) { - el.parentNode.removeChild(el) - el = undefined - sidebar.reveal() + el.parentNode.removeChild(el); + el = undefined; + sidebar.reveal(); } } function create() { - destroy() - sidebar.ensureVisible() - sidebar.hide() + destroy(); + sidebar.ensureVisible(); + sidebar.hide(); - el = document.createElement("div") - sidebar.container.insertBefore(el, sidebar.container.firstChild) + el = document.createElement("div"); + sidebar.container.insertBefore(el, sidebar.container.firstChild); - el.scrollIntoView(false) - el.classList.add("infobox") - el.destroy = destroy + el.scrollIntoView(false); + el.classList.add("infobox"); + el.destroy = destroy; - var closeButton = document.createElement("button") - closeButton.classList.add("close") - closeButton.onclick = router.reset - el.appendChild(closeButton) + var closeButton = document.createElement("button"); + closeButton.classList.add("close"); + closeButton.onclick = router.reset; + el.appendChild(closeButton); } - self.resetView = destroy + self.resetView = destroy; self.gotoNode = function (d) { - create() - new Node(config, el, router, d) - } + create(); + new Node(config, el, router, d); + }; self.gotoLink = function (d) { - create() - new Link(config, el, router, d) - } + create(); + new Link(config, el, router, d); + }; self.gotoLocation = function (d) { - create() - new Location(config, el, router, d) - } + create(); + new Location(config, el, router, d); + }; - return self - } -}) + return self; + }; +}); diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 34e1ee2..3654c3d 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -1,581 +1,631 @@ 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 - } - - 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 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 showFirmware(d) { - var release = dictGet(d.nodeinfo, ["software", "firmware", "release"]) - var base = dictGet(d.nodeinfo, ["software", "firmware", "base"]) - - if (release === null || base === null) - return undefined - - return release + " / " + base - } - - 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 showUptime(d) { - if (!("uptime" in d.statistics)) - return undefined - - return moment.duration(d.statistics.uptime, "seconds").humanize() - } - - function showFirstseen(d) { - if (!("firstseen" in d)) - return undefined - - return d.firstseen.fromNow(true) - } - - function wifiChannelAlias(ch) { - var chlist = { - "1": "2412 MHz", - "2": "2417 MHz", - "3": "2422 MHz", - "4": "2427 MHz", - "5": "2432 MHz", - "6": "2437 MHz", - "7": "2442 MHz", - "8": "2447 MHz", - "9": "2452 MHz", - "10": "2457 MHz", - "11": "2462 MHz", - "12": "2467 MHz", - "13": "2472 MHz", - "36": "5180 MHz (Indoors)", - "40": "5200 MHz (Indoors)", - "44": "5220 MHz (Indoors)", - "48": "5240 MHz (Indoors)", - "52": "5260 MHz (Indoors/DFS/TPC)", - "56": "5280 MHz (Indoors/DFS/TPC)", - "60": "5300 MHz (Indoors/DFS/TPC)", - "64": "5320 MHz (Indoors/DFS/TPC)", - "100": "5500 MHz (DFS) !!", - "104": "5520 MHz (DFS) !!", - "108": "5540 MHz (DFS) !!", - "112": "5560 MHz (DFS) !!", - "116": "5580 MHz (DFS) !!", - "120": "5600 MHz (DFS) !!", - "124": "5620 MHz (DFS) !!", - "128": "5640 MHz (DFS) !!", - "132": "5660 MHz (DFS) !!", - "136": "5680 MHz (DFS) !!", - "140": "5700 MHz (DFS) !!" - } - if (!(ch in chlist)) - return "" - else - return chlist[ch] - } - - function showWifiChannel(ch) { - if (!ch) - return undefined - - return ch + " (" + wifiChannelAlias(ch) + ")" - } - - function showClients(d) { - if (!d.flags.online) - return undefined - - var meshclients = getMeshClients(d) - resetMeshClients(d) - var before = " (" - var after = " in der lokalen Wolke)" - return function (el) { - el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine")) - el.appendChild(document.createTextNode(before)) - el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : "keine")) - el.appendChild(document.createTextNode(after)) - el.appendChild(document.createElement("br")) - - var span = document.createElement("span") - span.classList.add("clients") - span.textContent = " ".repeat(d.statistics.clients) - el.appendChild(span) - - var spanmesh = document.createElement("span") - spanmesh.classList.add("clientsMesh") - spanmesh.textContent = " ".repeat(meshclients - d.statistics.clients) - el.appendChild(spanmesh) - } - } - - function getMeshClients(node) { - var meshclients = 0 - if (node.statistics && !isNaN(node.statistics.clients)) - meshclients = node.statistics.clients - - if (!node) - return 0 - - if (node.parsed) - return 0 - - node.parsed = 1 - node.neighbours.forEach(function (neighbour) { - if (!neighbour.link.isVPN && neighbour.node) - meshclients += getMeshClients(neighbour.node) - }) - - return meshclients - } - - function resetMeshClients(node) { - if (!node.parsed) - return - - node.parsed = 0 - - node.neighbours.forEach(function (neighbour) { - if (!neighbour.link.isVPN && neighbour.node) - resetMeshClients(neighbour.node) - }) - - return - } - - function showMeshClients(d) { - if (!d.flags.online) - return undefined - - var meshclients = getMeshClients(d) - resetMeshClients(d) - return function (el) { - el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : "keine")) - el.appendChild(document.createElement("br")) - } - } - - 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") - if (ip.includes(".")) - a.href = "http://" + ip + "/" - else - a.href = "http://[" + ip + "]/" - a.textContent = ip - el.appendChild(a) - } else - el.appendChild(document.createTextNode(ip)) - }) - } - } - - function showBar(className, v) { - var span = document.createElement("span") - span.classList.add("bar") - span.classList.add(className) - - var bar = document.createElement("span") - bar.style.width = (v * 100) + "%" - span.appendChild(bar) - - var label = document.createElement("label") - label.textContent = (Math.round(v * 100)) + " %" - span.appendChild(label) - - return span - } - - function showLoadBar(className, v) { - var span = document.createElement("span") - span.classList.add("bar") - span.classList.add(className) - - var bar = document.createElement("span") - if (v >= 1) { - bar.style.width = ((v * 100) % 100) + "%" - bar.style.background = "rgba(255, 50, 50, 0.9)" - span.style.background = "rgba(255, 50, 50, 0.6)" - span.appendChild(bar) - } - else - { - bar.style.width = (v * 100) + "%" - span.appendChild(bar) - } - - var label = document.createElement("label") - label.textContent = +(Math.round(v + "e+2") + "e-2") - span.appendChild(label) - - return span - } - - function showLoad(d) { - if (!("loadavg" in d.statistics)) - return undefined - - return function (el) { - el.appendChild(showLoadBar("load-avg", d.statistics.loadavg)) - } - } - - function showRAM(d) { - if (!("memory_usage" in d.statistics)) - return undefined - - return function (el) { - el.appendChild(showBar("memory-usage", d.statistics.memory_usage)) - } - } - - function showAirtime(band, val) { - if (!val) - return undefined - - return function (el) { - el.appendChild(showBar("airtime" + band.toString(), val)) - } - } - - function createLink(target, router) { - if (!target) return document.createTextNode("unknown") - var unknown = !(target.node) - var text = unknown ? (target.id ? target.id : target) : target.node.nodeinfo.hostname - if (!unknown) { - var link = document.createElement("a") - link.classList.add("hostname-link") - link.href = "#" - link.onclick = router.node(target.node) - link.textContent = text - return link - } - return document.createTextNode(text) - } - - function showGateway(d, router) { - var nh - if (dictGet(d.statistics, ["nexthop"])) - nh = dictGet(d.statistics, ["nexthop"]) - if (dictGet(d.statistics, ["gateway_nexthop"])) - nh = dictGet(d.statistics, ["gateway_nexthop"]) - var gw = dictGet(d.statistics, ["gateway"]) - - if (!gw) return null - return function (el) { - var num = 0 - while (gw && nh && gw.id !== nh.id && num < 10) { - if (num !== 0) el.appendChild(document.createTextNode(" -> ")) - el.appendChild(createLink(nh, router)) - num++ - if (!nh.node || !nh.node.statistics) break - if (!dictGet(nh.node.statistics, ["gateway"]) || !dictGet(nh.node.statistics, ["gateway"]).id) break - if (dictGet(nh.node.statistics, ["gateway"]).id !== gw.id) break - if (dictGet(nh.node.statistics, ["gateway_nexthop"])) - nh = dictGet(nh.node.statistics, ["gateway_nexthop"]) - else if (dictGet(nh.node.statistics, ["nexthop"])) - nh = dictGet(nh.node.statistics, ["nexthop"]) - else - break + return a + "° " + numeral(min).format("0.000") + suffix; } - if (gw && nh && gw.id !== nh.id) { - if (num !== 0) el.appendChild(document.createTextNode(" -> ")) - num++ - el.appendChild(document.createTextNode("...")) + + 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 (num !== 0) el.appendChild(document.createTextNode(" -> ")) - el.appendChild(createLink(gw, router)) - } - } - function showPages(d) { - var webpages = dictGet(d.nodeinfo, ["pages"]) - if (webpages === null) - return undefined - - webpages.sort() - - return function (el) { - webpages.forEach( function (webpage, i) { - if (i > 0) - el.appendChild(document.createElement("br")) - - 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 showNodeImg(o, model) { - if (!model) - return document.createTextNode("Knotenname") - - var content, caption - var modelhash = model.split("").reduce(function(a, b) { - a = ((a << 5) - a) + b.charCodeAt(0) - return a & a - }, 0) - - content = document.createElement("img") - content.id = "routerpicture" - content.classList.add("nodeImg") - content.src = o.thumbnail.replace("{MODELHASH}", modelhash) - content.onerror = function() { - document.getElementById("routerpicdiv").outerHTML = "Knotenname" - } - - if (o.caption) { - caption = o.caption.replace("{MODELHASH}", modelhash) - - if (!content) - content = document.createTextNode(caption) - } - - var p = document.createElement("p") - p.appendChild(content) - - return content - } - - 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 attributes = document.createElement("table") - attributes.classList.add("attributes") - - if (config.hwImg) { - var top = document.createElement("div") - top.id = "routerpicdiv" - try { - config.hwImg.forEach(function(hwImg) { - try { - top.appendChild(showNodeImg(hwImg, dictGet(d, ["nodeinfo", "hardware", "model"]))) - } catch (err) { - console.log(err.message) - } - }) - } catch (err) { - console.log(err.message) + if (!has_location(d)) { + return undefined; } - attributeEntry(attributes, top, d.nodeinfo.hostname) - } else { - var h2 = document.createElement("h2") - h2.textContent = d.nodeinfo.hostname - el.appendChild(h2) + + 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); + }; } - 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, "Kanal 2.4 GHz", showWifiChannel(dictGet(d.nodeinfo, ["wireless", "chan2"]))) - attributeEntry(attributes, "Kanal 5 GHz", showWifiChannel(dictGet(d.nodeinfo, ["wireless", "chan5"]))) - attributeEntry(attributes, "Airtime 2.4 GHz", showAirtime(2, dictGet(d.statistics, ["wireless", "airtime2"]))) - attributeEntry(attributes, "Airtime 5 GHz", showAirtime(5, dictGet(d.statistics, ["wireless", "airtime5"]))) - 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", showGateway(d, router)) - attributeEntry(attributes, "Autom. Updates", showAutoupdate(d)) - attributeEntry(attributes, "Clients", showClients(d), showMeshClients(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") - td2.appendChild(createLink(d, router)) - - if (!unknown && has_location(d.node)) { - var span = document.createElement("span") - span.classList.add("icon") - span.classList.add("ion-location") - td2.appendChild(span) + 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") + ")"; } - - tr.appendChild(td2) - - var td3 = document.createElement("td") - var a2 = document.createElement("a") - a2.href = "#" - a2.textContent = showTq(d.link) - a2.onclick = router.link(d.link) - td3.appendChild(a2) - tr.appendChild(td3) - - var td4 = document.createElement("td") - var a3 = document.createElement("a") - a3.href = "#" - a3.textContent = d.link.type - a3.onclick = router.link(d.link) - td4.appendChild(a3) - tr.appendChild(td4) - - var td5 = document.createElement("td") - var a4 = document.createElement("a") - a4.href = "#" - a4.textContent = showDistance(d.link) - a4.onclick = router.link(d.link) - td5.appendChild(a4) - td5.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1) - tr.appendChild(td5) - - tbody.appendChild(tr) - }) - - table.appendChild(tbody) - table.className = "node-links" - - new Tablesort(table) - - el.appendChild(table) + }; } - } -}) + + function showFirmware(d) { + var release = dictGet(d.nodeinfo, ["software", "firmware", "release"]); + var base = dictGet(d.nodeinfo, ["software", "firmware", "base"]); + + if (release === null || base === null) { + return undefined; + } + + return release + " / " + base; + } + + 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 showUptime(d) { + if (!("uptime" in d.statistics)) { + return undefined; + } + + return moment.duration(d.statistics.uptime, "seconds").humanize(); + } + + function showFirstseen(d) { + if (!("firstseen" in d)) { + return undefined; + } + + return d.firstseen.fromNow(true); + } + + function wifiChannelAlias(ch) { + var chlist = { + "1": "2412 MHz", + "2": "2417 MHz", + "3": "2422 MHz", + "4": "2427 MHz", + "5": "2432 MHz", + "6": "2437 MHz", + "7": "2442 MHz", + "8": "2447 MHz", + "9": "2452 MHz", + "10": "2457 MHz", + "11": "2462 MHz", + "12": "2467 MHz", + "13": "2472 MHz", + "36": "5180 MHz (Indoors)", + "40": "5200 MHz (Indoors)", + "44": "5220 MHz (Indoors)", + "48": "5240 MHz (Indoors)", + "52": "5260 MHz (Indoors/DFS/TPC)", + "56": "5280 MHz (Indoors/DFS/TPC)", + "60": "5300 MHz (Indoors/DFS/TPC)", + "64": "5320 MHz (Indoors/DFS/TPC)", + "100": "5500 MHz (DFS) !!", + "104": "5520 MHz (DFS) !!", + "108": "5540 MHz (DFS) !!", + "112": "5560 MHz (DFS) !!", + "116": "5580 MHz (DFS) !!", + "120": "5600 MHz (DFS) !!", + "124": "5620 MHz (DFS) !!", + "128": "5640 MHz (DFS) !!", + "132": "5660 MHz (DFS) !!", + "136": "5680 MHz (DFS) !!", + "140": "5700 MHz (DFS) !!" + }; + if (!(ch in chlist)) { + return ""; + } else { + return chlist[ch]; + } + } + + function showWifiChannel(ch) { + if (!ch) { + return undefined; + } + + return ch + " (" + wifiChannelAlias(ch) + ")"; + } + + function showClients(d) { + if (!d.flags.online) { + return undefined; + } + + var meshclients = getMeshClients(d); + resetMeshClients(d); + var before = " ("; + var after = " in der lokalen Wolke)"; + return function (el) { + el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine")); + el.appendChild(document.createTextNode(before)); + el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : "keine")); + el.appendChild(document.createTextNode(after)); + el.appendChild(document.createElement("br")); + + var span = document.createElement("span"); + span.classList.add("clients"); + span.textContent = " ".repeat(d.statistics.clients); + el.appendChild(span); + + var spanmesh = document.createElement("span"); + spanmesh.classList.add("clientsMesh"); + spanmesh.textContent = " ".repeat(meshclients - d.statistics.clients); + el.appendChild(spanmesh); + }; + } + + function getMeshClients(node) { + var meshclients = 0; + if (node.statistics && !isNaN(node.statistics.clients)) { + meshclients = node.statistics.clients; + } + + if (!node) { + return 0; + } + + if (node.parsed) { + return 0; + } + + node.parsed = 1; + node.neighbours.forEach(function (neighbour) { + if (!neighbour.link.isVPN && neighbour.node) { + meshclients += getMeshClients(neighbour.node); + } + }); + + return meshclients; + } + + function resetMeshClients(node) { + if (!node.parsed) { + return; + } + + node.parsed = 0; + + node.neighbours.forEach(function (neighbour) { + if (!neighbour.link.isVPN && neighbour.node) { + resetMeshClients(neighbour.node); + } + }); + + + } + + function showMeshClients(d) { + if (!d.flags.online) { + return undefined; + } + + var meshclients = getMeshClients(d); + resetMeshClients(d); + return function (el) { + el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : "keine")); + el.appendChild(document.createElement("br")); + }; + } + + 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"); + if (ip.includes(".")) { + a.href = "http://" + ip + "/"; + } else { + a.href = "http://[" + ip + "]/"; + } + a.textContent = ip; + el.appendChild(a); + } else { + el.appendChild(document.createTextNode(ip)); + } + }); + }; + } + + function showBar(className, v) { + var span = document.createElement("span"); + span.classList.add("bar"); + span.classList.add(className); + + var bar = document.createElement("span"); + bar.style.width = (v * 100) + "%"; + span.appendChild(bar); + + var label = document.createElement("label"); + label.textContent = (Math.round(v * 100)) + " %"; + span.appendChild(label); + + return span; + } + + function showLoadBar(className, v) { + var span = document.createElement("span"); + span.classList.add("bar"); + span.classList.add(className); + + var bar = document.createElement("span"); + if (v >= 1) { + bar.style.width = ((v * 100) % 100) + "%"; + bar.style.background = "rgba(255, 50, 50, 0.9)"; + span.style.background = "rgba(255, 50, 50, 0.6)"; + span.appendChild(bar); + } + else { + bar.style.width = (v * 100) + "%"; + span.appendChild(bar); + } + + var label = document.createElement("label"); + label.textContent = +(Math.round(v + "e+2") + "e-2"); + span.appendChild(label); + + return span; + } + + function showLoad(d) { + if (!("loadavg" in d.statistics)) { + return undefined; + } + + return function (el) { + el.appendChild(showLoadBar("load-avg", d.statistics.loadavg)); + }; + } + + function showRAM(d) { + if (!("memory_usage" in d.statistics)) { + return undefined; + } + + return function (el) { + el.appendChild(showBar("memory-usage", d.statistics.memory_usage)); + }; + } + + function showAirtime(band, val) { + if (!val) { + return undefined; + } + + return function (el) { + el.appendChild(showBar("airtime" + band.toString(), val)); + }; + } + + function createLink(target, router) { + if (!target) { + return document.createTextNode("unknown"); + } + var unknown = !(target.node); + var text = unknown ? (target.id ? target.id : target) : target.node.nodeinfo.hostname; + if (!unknown) { + var link = document.createElement("a"); + link.classList.add("hostname-link"); + link.href = "#"; + link.onclick = router.node(target.node); + link.textContent = text; + return link; + } + return document.createTextNode(text); + } + + function showGateway(d, router) { + var nh; + if (dictGet(d.statistics, ["nexthop"])) { + nh = dictGet(d.statistics, ["nexthop"]); + } + if (dictGet(d.statistics, ["gateway_nexthop"])) { + nh = dictGet(d.statistics, ["gateway_nexthop"]); + } + var gw = dictGet(d.statistics, ["gateway"]); + + if (!gw) { + return null; + } + return function (el) { + var num = 0; + while (gw && nh && gw.id !== nh.id && num < 10) { + if (num !== 0) { + el.appendChild(document.createTextNode(" -> ")); + } + el.appendChild(createLink(nh, router)); + num++; + if (!nh.node || !nh.node.statistics) { + break; + } + if (!dictGet(nh.node.statistics, ["gateway"]) || !dictGet(nh.node.statistics, ["gateway"]).id) { + break; + } + if (dictGet(nh.node.statistics, ["gateway"]).id !== gw.id) { + break; + } + if (dictGet(nh.node.statistics, ["gateway_nexthop"])) { + nh = dictGet(nh.node.statistics, ["gateway_nexthop"]); + } else if (dictGet(nh.node.statistics, ["nexthop"])) { + nh = dictGet(nh.node.statistics, ["nexthop"]); + } else { + break; + } + } + if (gw && nh && gw.id !== nh.id) { + if (num !== 0) { + el.appendChild(document.createTextNode(" -> ")); + } + num++; + el.appendChild(document.createTextNode("...")); + } + if (num !== 0) { + el.appendChild(document.createTextNode(" -> ")); + } + el.appendChild(createLink(gw, router)); + }; + } + + function showPages(d) { + var webpages = dictGet(d.nodeinfo, ["pages"]); + if (webpages === null) { + return undefined; + } + + webpages.sort(); + + return function (el) { + webpages.forEach(function (webpage, i) { + if (i > 0) { + el.appendChild(document.createElement("br")); + } + + 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 showNodeImg(o, model) { + if (!model) { + return document.createTextNode("Knotenname"); + } + + var content, caption; + var modelhash = model.split("").reduce(function (a, b) { + a = ((a << 5) - a) + b.charCodeAt(0); + return a & a; + }, 0); + + content = document.createElement("img"); + content.id = "routerpicture"; + content.classList.add("nodeImg"); + content.src = o.thumbnail.replace("{MODELHASH}", modelhash); + content.onerror = function () { + document.getElementById("routerpicdiv").outerHTML = "Knotenname"; + }; + + if (o.caption) { + caption = o.caption.replace("{MODELHASH}", modelhash); + + if (!content) { + content = document.createTextNode(caption); + } + } + + var p = document.createElement("p"); + p.appendChild(content); + + return content; + } + + 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 attributes = document.createElement("table"); + attributes.classList.add("attributes"); + + if (config.hwImg) { + var top = document.createElement("div"); + top.id = "routerpicdiv"; + try { + config.hwImg.forEach(function (hwImg) { + try { + top.appendChild(showNodeImg(hwImg, dictGet(d, ["nodeinfo", "hardware", "model"]))); + } catch (err) { + console.log(err.message); + } + }); + } catch (err) { + console.log(err.message); + } + attributeEntry(attributes, top, d.nodeinfo.hostname); + } else { + var h2 = document.createElement("h2"); + h2.textContent = d.nodeinfo.hostname; + el.appendChild(h2); + } + + 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, "Kanal 2.4 GHz", showWifiChannel(dictGet(d.nodeinfo, ["wireless", "chan2"]))); + attributeEntry(attributes, "Kanal 5 GHz", showWifiChannel(dictGet(d.nodeinfo, ["wireless", "chan5"]))); + attributeEntry(attributes, "Airtime 2.4 GHz", showAirtime(2, dictGet(d.statistics, ["wireless", "airtime2"]))); + attributeEntry(attributes, "Airtime 5 GHz", showAirtime(5, dictGet(d.statistics, ["wireless", "airtime5"]))); + 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", showGateway(d, router)); + attributeEntry(attributes, "Autom. Updates", showAutoupdate(d)); + attributeEntry(attributes, "Clients", showClients(d), showMeshClients(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"); + td2.appendChild(createLink(d, router)); + + 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/legend.js b/lib/legend.js index bee87c3..b01782f 100644 --- a/lib/legend.js +++ b/lib/legend.js @@ -1,41 +1,41 @@ define(function () { return function () { - var self = this + var self = this; self.render = function (el) { - var p = document.createElement("p") - p.setAttribute("class", "legend") - el.appendChild(p) + var p = document.createElement("p"); + p.setAttribute("class", "legend"); + el.appendChild(p); - var spanNew = document.createElement("span") - spanNew.setAttribute("class", "legend-new") - var symbolNew = document.createElement("span") - symbolNew.setAttribute("class", "symbol") - var textNew = document.createTextNode(" Neuer Knoten") - spanNew.appendChild(symbolNew) - spanNew.appendChild(textNew) - p.appendChild(spanNew) + var spanNew = document.createElement("span"); + spanNew.setAttribute("class", "legend-new"); + var symbolNew = document.createElement("span"); + symbolNew.setAttribute("class", "symbol"); + var textNew = document.createTextNode(" Neuer Knoten"); + spanNew.appendChild(symbolNew); + spanNew.appendChild(textNew); + p.appendChild(spanNew); - var spanOnline = document.createElement("span") - spanOnline.setAttribute("class", "legend-online") - var symbolOnline = document.createElement("span") - symbolOnline.setAttribute("class", "symbol") - var textOnline = document.createTextNode(" Knoten ist online") - spanOnline.appendChild(symbolOnline) - spanOnline.appendChild(textOnline) - p.appendChild(spanOnline) + var spanOnline = document.createElement("span"); + spanOnline.setAttribute("class", "legend-online"); + var symbolOnline = document.createElement("span"); + symbolOnline.setAttribute("class", "symbol"); + var textOnline = document.createTextNode(" Knoten ist online"); + spanOnline.appendChild(symbolOnline); + spanOnline.appendChild(textOnline); + p.appendChild(spanOnline); - var spanOffline = document.createElement("span") - spanOffline.setAttribute("class", "legend-offline") - var symbolOffline = document.createElement("span") - symbolOffline.setAttribute("class", "symbol") - var textOffline = document.createTextNode(" Knoten ist offline") - spanOffline.appendChild(symbolOffline) - spanOffline.appendChild(textOffline) - p.appendChild(spanOffline) - } + var spanOffline = document.createElement("span"); + spanOffline.setAttribute("class", "legend-offline"); + var symbolOffline = document.createElement("span"); + symbolOffline.setAttribute("class", "symbol"); + var textOffline = document.createTextNode(" Knoten ist offline"); + spanOffline.appendChild(symbolOffline); + spanOffline.appendChild(textOffline); + p.appendChild(spanOffline); + }; - return self - } -}) + return self; + }; +}); diff --git a/lib/linklist.js b/lib/linklist.js index 1fc8574..96e7447 100644 --- a/lib/linklist.js +++ b/lib/linklist.js @@ -1,60 +1,66 @@ define(["sorttable", "virtual-dom"], function (SortTable, V) { function linkName(d) { - return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + d.target.node.nodeinfo.hostname + return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + d.target.node.nodeinfo.hostname; } - var headings = [{ name: "Knoten", - 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) { - var table = new SortTable(headings, 2, renderRow) + return function (linkScale, router) { + var table = new SortTable(headings, 2, renderRow); function renderRow(d) { - var td1Content = [V.h("a", {href: "#", onclick: router.link(d)}, linkName(d))] + var td1Content = [V.h("a", {href: "#", onclick: router.link(d)}, linkName(d))]; - var td1 = V.h("td", td1Content) - var td2 = V.h("td", {style: {color: linkScale(d.tq).hex()}}, showTq(d)) - var td3 = V.h("td", d.type) - var td4 = V.h("td", showDistance(d)) + var td1 = V.h("td", td1Content); + var td2 = V.h("td", {style: {color: linkScale(d.tq).hex()}}, showTq(d)); + var td3 = V.h("td", d.type); + var td4 = V.h("td", showDistance(d)); - return V.h("tr", [td1, td2, td3, td4]) + return V.h("tr", [td1, td2, td3, td4]); } - this.render = function (d) { - var el = document.createElement("div") - el.last = V.h("div") - d.appendChild(el) + this.render = function (d) { + var el = document.createElement("div"); + el.last = V.h("div"); + d.appendChild(el); - var h2 = document.createElement("h2") - h2.textContent = "Verbindungen" - el.appendChild(h2) + var h2 = document.createElement("h2"); + h2.textContent = "Verbindungen"; + el.appendChild(h2); - el.appendChild(table.el) - } + el.appendChild(table.el); + }; this.setData = function (d) { - table.setData(d.graph.links) - } - } -}) + table.setData(d.graph.links); + }; + }; +}); diff --git a/lib/locationmarker.js b/lib/locationmarker.js index 615eeef..0f8c35a 100644 --- a/lib/locationmarker.js +++ b/lib/locationmarker.js @@ -29,31 +29,31 @@ define(["leaflet"], function (L) { fillOpacity: 0.2 }, - 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) + 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._map.removeLayer(this.accuracyCircle) - this._map.removeLayer(this.outerCircle) - }) + this.on("remove", function () { + this._map.removeLayer(this.accuracyCircle); + this._map.removeLayer(this.outerCircle); + }); }, - setLatLng: function(latlng) { - this.accuracyCircle.setLatLng(latlng) - this.outerCircle.setLatLng(latlng) - L.CircleMarker.prototype.setLatLng.call(this, latlng) + setLatLng: function (latlng) { + this.accuracyCircle.setLatLng(latlng); + this.outerCircle.setLatLng(latlng); + L.CircleMarker.prototype.setLatLng.call(this, latlng); }, - setAccuracy: function(accuracy) { - this.accuracyCircle.setRadius(accuracy) + setAccuracy: function (accuracy) { + this.accuracyCircle.setRadius(accuracy); }, - onAdd: function(map) { - this.accuracyCircle.addTo(map).bringToBack() - this.outerCircle.addTo(map) - L.CircleMarker.prototype.onAdd.call(this, 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 eac2a0f..8305891 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,232 +1,241 @@ define(["moment", "router", "leaflet", "gui", "numeral"], -function (moment, Router, L, GUI, numeral) { - return function (config) { - function handleData(data) { - var dataNodes = {} - dataNodes.nodes = [] - dataNodes.nodeIds = [] - 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 = []; + dataNodes.nodeIds = []; + 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 - } - - 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 { - data[i].nodes.forEach(fillData) - dataNodes.timestamp = data[i].timestamp - } - } - - function fillData (node) { - var position = dataNodes.nodeIds.indexOf(node.nodeinfo.node_id) - if(position === -1){ - dataNodes.nodes.push(node) - dataNodes.nodeIds.push(node.nodeinfo.node_id) + function rearrangeLinks(d) { + d.source += dataGraph.batadv.nodes.length; + d.target += dataGraph.batadv.nodes.length; } - else - if(node.flags.online === true) - dataNodes.nodes[position] = node - } - 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 + 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 { + data[i].nodes.forEach(fillData); + dataNodes.timestamp = data[i].timestamp; } } - }) - graph.links.forEach( function (d) { - d.source = graph.nodes[d.source] + function fillData(node) { + var position = dataNodes.nodeIds.indexOf(node.nodeinfo.node_id); + if (position === -1) { + dataNodes.nodes.push(node); + dataNodes.nodeIds.push(node.nodeinfo.node_id); + } + else if (node.flags.online === true) { + dataNodes.nodes[position] = node; + } + } - if (graph.nodes[d.target].node) - d.target = graph.nodes[d.target] - else - d.target = undefined - }) + var nodes = dataNodes.nodes.filter(function (d) { + return "firstseen" in d && "lastseen" in d; + }); - var links = graph.links.filter( function (d) { - return d.target !== undefined - }) + nodes.forEach(function (node) { + node.firstseen = moment.utc(node.firstseen).local(); + node.lastseen = moment.utc(node.lastseen).local(); + }); - 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 now = moment(); + var age = moment(now).subtract(config.maxAge, "days"); - if (unknown || - !d.source.node.nodeinfo.location || - !d.target.node.nodeinfo.location || + 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]; + + 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 = [] - if (d.statistics) { - /*eslint camelcase:0*/ - if ("gateway" in d.statistics && d.statistics.gateway in graphnodes) - d.statistics.gateway = {"node": graphnodes[d.statistics.gateway], "id": d.statistics.gateway} - if ("nexthop" in d.statistics && d.statistics.nexthop in graphnodes) - d.statistics.nexthop = {"node": graphnodes[d.statistics.nexthop], "id": d.statistics.nexthop} - if ("gateway_nexthop" in d.statistics && d.statistics.gateway_nexthop in graphnodes) - d.statistics.gateway_nexthop = {"node": graphnodes[d.statistics.gateway_nexthop], "id": d.statistics.gateway_nexthop} - } - }) + nodes.forEach(function (d) { + d.neighbours = []; + if (d.statistics) { + /*eslint camelcase:0*/ + if ("gateway" in d.statistics && d.statistics.gateway in graphnodes) { + d.statistics.gateway = {"node": graphnodes[d.statistics.gateway], "id": d.statistics.gateway}; + } + if ("nexthop" in d.statistics && d.statistics.nexthop in graphnodes) { + d.statistics.nexthop = {"node": graphnodes[d.statistics.nexthop], "id": d.statistics.nexthop}; + } + if ("gateway_nexthop" in d.statistics && d.statistics.gateway_nexthop in graphnodes) { + d.statistics.gateway_nexthop = { + "node": graphnodes[d.statistics.gateway_nexthop], + "id": d.statistics.gateway_nexthop + }; + } + } + }); - links.forEach( function (d) { - if (d.type === "tunnel" || d.vpn) { - d.type = "VPN" - d.isVPN = true - } else if (d.type === "fastd") { - d.type = "fastd" - d.isVPN = true - } else if (d.type === "l2tp") { - d.type = "L2TP" - d.isVPN = true - } else if (d.type === "gre") { - d.type = "GRE" - d.isVPN = true - } else if (d.type === "wireless") { - d.type = "Wifi" - d.isVPN = false - } else if (d.type === "other") { - d.type = "Kabel" - d.isVPN = false - } else { - d.type = "N/A" - d.isVPN = false - } + links.forEach(function (d) { + if (d.type === "tunnel" || d.vpn) { + d.type = "VPN"; + d.isVPN = true; + } else if (d.type === "fastd") { + d.type = "fastd"; + d.isVPN = true; + } else if (d.type === "l2tp") { + d.type = "L2TP"; + d.isVPN = true; + } else if (d.type === "gre") { + d.type = "GRE"; + d.isVPN = true; + } else if (d.type === "wireless") { + d.type = "Wifi"; + d.isVPN = false; + } else if (d.type === "other") { + d.type = "Kabel"; + d.isVPN = false; + } else { + d.type = "N/A"; + d.isVPN = false; + } - if (d.isVPN && d.target.node) - d.target.node.flags.uplink = true + if (d.isVPN && d.target.node) { + d.target.node.flags.uplink = true; + } - 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.isVPN) - d.source.node.meshlinks = d.source.node.meshlinks ? d.source.node.meshlinks + 1 : 1 - }) + 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.isVPN) { + 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 aca0097..ea44443 100644 --- a/lib/map.js +++ b/lib/map.js @@ -1,74 +1,75 @@ 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({ options: { @@ -79,470 +80,547 @@ define(["map/clientlayer", "map/labelslayer", button: undefined, initialize: function (f, options) { - L.Util.setOptions(this, options) - this.f = f + L.Util.setOptions(this, options); + this.f = f; }, onAdd: function () { - var button = L.DomUtil.create("button", "coord-picker") - button.textContent = "\uF2A6" + var button = L.DomUtil.create("button", "coord-picker"); + button.textContent = "\uF2A6"; // 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.onClick, this) + 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 (e) { - L.DomEvent.stopPropagation(e) - this.f(!this.active) + L.DomEvent.stopPropagation(e); + this.f(!this.active); } - }) + }); function mkMarker(dict, iconFunc, router) { return function (d) { - var m = L.circleMarker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], iconFunc(d)) + var m = L.circleMarker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], iconFunc(d)); m.resetStyle = function () { - m.setStyle(iconFunc(d)) - } + m.setStyle(iconFunc(d)); + }; - m.on("click", router.node(d)) - m.bindLabel(d.nodeinfo.hostname) + m.on("click", router.node(d)); + m.bindLabel(d.nodeinfo.hostname); - dict[d.nodeinfo.node_id] = m + dict[d.nodeinfo.node_id] = m; - return m - } + return m; + }; } function addLinksToMap(dict, linkScale, graph, router) { - graph = graph.filter( function (d) { - return "distance" in d && d.type !== "VPN" - }) + 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) + var line = L.polyline(d.latlngs, opts); line.resetStyle = function () { - line.setStyle(opts) - } + line.setStyle(opts); + }; - line.bindLabel(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + "
" + showDistance(d) + " / " + showTq(d) + "") - line.on("click", router.link(d)) + line.bindLabel(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + "
" + showDistance(d) + " / " + showTq(d) + ""); + line.on("click", router.link(d)); - dict[d.id] = line + dict[d.id] = line; - return line - }) + return line; + }); - return lines + 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 - var barycenter - var groupOnline, groupOffline, groupNew, groupLost, groupLines - var savedView + var self = this; + var barycenter; + var groupOnline, groupOffline, groupNew, groupLost, groupLines; + var savedView; - var map, userLocation - var layerControl - var customLayers = {} - var baseLayers = {} + var map, userLocation; + var layerControl; + var customLayers = {}; + var baseLayers = {}; var locateUserButton = new LocateButton(function (d) { - if (d) - enableTracking() - else - disableTracking() - }) + if (d) { + enableTracking(); + } else { + disableTracking(); + } + }); - var mybuttons = [] + var mybuttons = []; function addButton(button) { - var el = button.onAdd() - mybuttons.push(el) - buttons.appendChild(el) + var el = button.onAdd(); + mybuttons.push(el); + buttons.appendChild(el); } function clearButtons() { - mybuttons.forEach( function (d) { - buttons.removeChild(d) - }) + mybuttons.forEach(function (d) { + buttons.removeChild(d); + }); } var showCoordsPickerButton = new CoordsPickerButton(function (d) { - if (d) - enableCoords() - else - disableCoords() - }) + if (d) { + enableCoords(); + } 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 - }) - locateUserButton.set(true) + map.locate({ + watch: true, + enableHighAccuracy: true, + setView: true + }); + locateUserButton.set(true); } function disableTracking() { - map.stopLocate() - locationError() - locateUserButton.set(false) + map.stopLocate(); + locationError(); + locateUserButton.set(false); } function enableCoords() { - map.getContainer().classList.add("pick-coordinates") - map.on("click", showCoordinates) - showCoordsPickerButton.set(true) + map.getContainer().classList.add("pick-coordinates"); + map.on("click", showCoordinates); + showCoordsPickerButton.set(true); } function disableCoords() { - map.getContainer().classList.remove("pick-coordinates") - map.off("click", showCoordinates) - showCoordsPickerButton.set(false) + map.getContainer().classList.remove("pick-coordinates"); + map.off("click", showCoordinates); + showCoordsPickerButton.set(false); } function showCoordinates(e) { - router.gotoLocation(e.latlng) + router.gotoLocation(e.latlng); // window.prompt("Koordinaten (Lat, Lng)", e.latlng.lat.toFixed(9) + ", " + e.latlng.lng.toFixed(9)) - disableCoords() + disableCoords(); } function locationFound(e) { - if (!userLocation) - userLocation = new LocationMarker(e.latlng).addTo(map) + if (!userLocation) { + userLocation = new LocationMarker(e.latlng).addTo(map); + } - userLocation.setLatLng(e.latlng) - userLocation.setAccuracy(e.accuracy) + userLocation.setLatLng(e.latlng); + userLocation.setAccuracy(e.accuracy); } function locationError() { if (userLocation) { - map.removeLayer(userLocation) - userLocation = null + map.removeLayer(userLocation); + userLocation = null; } } function addLayer(layerName) { - if (layerName in baseLayers) - return + if (layerName in baseLayers) { + return; + } - if (layerName in customLayers) - return + if (layerName in customLayers) { + return; + } try { - var layer = L.tileLayer.provider(layerName) - layerControl.addBaseLayer(layer, layerName) - customLayers[layerName] = layer + var layer = L.tileLayer.provider(layerName); + layerControl.addBaseLayer(layer, layerName); + customLayers[layerName] = layer; - if (localStorageTest()) - localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers))) + if (localStorageTest()) { + localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers))); + } } catch (e) { - return + } } function contextMenuGotoLocation(e) { - router.gotoLocation(e.latlng) + router.gotoLocation(e.latlng); } - var el = document.createElement("div") - el.classList.add("map") + var el = document.createElement("div"); + el.classList.add("map"); - map = L.map(el, options) + map = L.map(el, options); - var layers = config.mapLayers.map( function (d) { + var layers = config.mapLayers.map(function (d) { return { "name": d.name, "layer": "url" in d ? L.tileLayer(d.url, d.config) : L.tileLayer.provider(d.name) - } - }) + }; + }); - layers[0].layer.addTo(map) + layers[0].layer.addTo(map); - layers.forEach( function (d) { - baseLayers[d.name] = d.layer - }) + layers.forEach(function (d) { + baseLayers[d.name] = d.layer; + }); - map.on("locationfound", locationFound) - map.on("locationerror", locationError) - map.on("dragend", saveView) - map.on("contextmenu", contextMenuGotoLocation) + map.on("locationfound", locationFound); + map.on("locationerror", locationError); + map.on("dragend", saveView); + map.on("contextmenu", contextMenuGotoLocation); - addButton(locateUserButton) - addButton(showCoordsPickerButton) + addButton(locateUserButton); + addButton(showCoordsPickerButton); addButton(new AddLayerButton(function () { /*eslint no-alert:0*/ - var layerName = prompt("Leaflet Provider:") - addLayer(layerName) - })) + var layerName = prompt("Leaflet Provider:"); + addLayer(layerName); + })); - layerControl = L.control.layers(baseLayers, [], {position: "bottomright"}) - layerControl.addTo(map) + layerControl = L.control.layers(baseLayers, [], {position: "bottomright"}); + layerControl.addTo(map); if (localStorageTest()) { - var d = JSON.parse(localStorage.getItem("map/customLayers")) - - 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 + var d = JSON.parse(localStorage.getItem("map/customLayers")); if (d) { - map.removeLayer(layers[0].layer) - map.addLayer(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; + + if (d) { + map.removeLayer(layers[0].layer); + map.addLayer(d); } } - var clientLayer = new ClientLayer({minZoom: 15}) - clientLayer.addTo(map) - clientLayer.setZIndex(5) + var clientLayer = new ClientLayer({minZoom: 15}); + clientLayer.addTo(map); + clientLayer.setZIndex(5); - var labelsLayer = new LabelsLayer({}) - labelsLayer.addTo(map) - labelsLayer.setZIndex(6) + var labelsLayer = new LabelsLayer({}); + labelsLayer.addTo(map); + labelsLayer.setZIndex(6); - 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()) - localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name})) - }) + 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()) { + localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name})); + } + }); - var nodeDict = {} - var linkDict = {} - var highlight + var nodeDict = {}; + var linkDict = {}; + var highlight; function resetMarkerStyles(nodes, links) { - Object.keys(nodes).forEach( function (d) { - nodes[d].resetStyle() - }) + Object.keys(nodes).forEach(function (d) { + nodes[d].resetStyle(); + }); - Object.keys(links).forEach( function (d) { - links[d].resetStyle() - }) + Object.keys(links).forEach(function (d) { + links[d].resetStyle(); + }); } function setView(bounds) { - map.fitBounds(bounds, {paddingTopLeft: [sidebar(), 0]}) + map.fitBounds(bounds, {paddingTopLeft: [sidebar(), 0]}); } function resetZoom() { - if (barycenter) - setView(barycenter.getBounds()) + if (barycenter) { + setView(barycenter.getBounds()); + } } function goto(m) { - var bounds + var bounds; - if ("getBounds" in m) - bounds = m.getBounds() - else - bounds = L.latLngBounds([m.getLatLng()]) + if ("getBounds" in m) { + bounds = m.getBounds(); + } else { + bounds = L.latLngBounds([m.getLatLng()]); + } - setView(bounds) + setView(bounds); - return m + return m; } function updateView(nopanzoom) { - resetMarkerStyles(nodeDict, linkDict) - var m + resetMarkerStyles(nodeDict, linkDict); + var m; - if (highlight !== undefined) + if (highlight !== undefined) { if (highlight.type === "node") { - m = nodeDict[highlight.o.nodeinfo.node_id] + 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] + 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) - goto(m) - else if (savedView) - map.setView(savedView.center, savedView.zoom) - else - resetZoom() + if (!nopanzoom) { + if (m) { + goto(m); + } else if (savedView) { + map.setView(savedView.center, savedView.zoom); + } 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) - return undefined + 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)] + var barycenter = L.latLng(d3.median(lats), d3.median(lngs)); + var barycenterDev = [d3.deviation(lats), d3.deviation(lngs)]; - if (barycenterDev[0] === undefined) - barycenterDev[0] = 0 + if (barycenterDev[0] === undefined) { + barycenterDev[0] = 0; + } - if (barycenterDev[1] === undefined) - barycenterDev[1] = 0 + 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) + var r = barycenter.distanceTo(barycenterCircle); - return L.circle(barycenter, r * config.mapSigmaScale) + return L.circle(barycenter, r * config.mapSigmaScale); } 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 + o.node = d; - return o + return o; } self.setData = function (data) { - nodeDict = {} - linkDict = {} + nodeDict = {}; + linkDict = {}; - if (groupOffline) - groupOffline.clearLayers() + if (groupOffline) { + groupOffline.clearLayers(); + } - if (groupOnline) - groupOnline.clearLayers() + if (groupOnline) { + groupOnline.clearLayers(); + } - if (groupNew) - groupNew.clearLayers() + if (groupNew) { + groupNew.clearLayers(); + } - if (groupLost) - groupLost.clearLayers() + if (groupLost) { + groupLost.clearLayers(); + } - if (groupLines) - groupLines.clearLayers() + if (groupLines) { + groupLines.clearLayers(); + } - var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router) - groupLines = L.featureGroup(lines).addTo(map) + var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router); + groupLines = L.featureGroup(lines).addTo(map); - if (typeof config.fixedCenter === "undefined") - barycenter = calcBarycenter(data.nodes.all.filter(has_location)) - else - barycenter = L.circle(L.latLng(new L.LatLng(config.fixedCenter.lat, config.fixedCenter.lng)), config.fixedCenter.radius * 1000) + if (typeof config.fixedCenter === "undefined") { + barycenter = calcBarycenter(data.nodes.all.filter(has_location)); + } 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 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"))) - return iconAlert + if (d.lastseen.isAfter(moment(data.now).subtract(3, "days"))) { + return iconAlert; + } - return iconLost - }, router)) + return iconLost; + }, router)); - groupOffline = L.featureGroup(markersOffline).addTo(map) - groupOnline = L.featureGroup(markersOnline).addTo(map) - groupLost = L.featureGroup(markersLost).addTo(map) - groupNew = L.featureGroup(markersNew).addTo(map) + groupOffline = L.featureGroup(markersOffline).addTo(map); + groupOnline = L.featureGroup(markersOnline).addTo(map); + groupLost = L.featureGroup(markersLost).addTo(map); + groupNew = L.featureGroup(markersNew).addTo(map); - var rtreeOnlineAll = rbush(9) + var rtreeOnlineAll = rbush(9); - rtreeOnlineAll.load(data.nodes.all.filter(online).filter(has_location).map(mapRTree)) + rtreeOnlineAll.load(data.nodes.all.filter(online).filter(has_location).map(mapRTree)); - clientLayer.setData(rtreeOnlineAll) - 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) - }) + 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) + }); - updateView(true) - } + updateView(true); + }; self.resetView = function () { - disableTracking() - highlight = undefined - updateView() - } + disableTracking(); + highlight = undefined; + updateView(); + }; self.gotoNode = function (d) { - disableTracking() - highlight = {type: "node", o: d} - updateView() - } + disableTracking(); + highlight = {type: "node", o: d}; + updateView(); + }; self.gotoLink = function (d) { - disableTracking() - highlight = {type: "link", o: d} - updateView() - } + disableTracking(); + highlight = {type: "link", o: d}; + updateView(); + }; self.gotoLocation = function () { //ignore - } + }; self.destroy = function () { - clearButtons() - map.remove() + clearButtons(); + map.remove(); - if (el.parentNode) - el.parentNode.removeChild(el) - } + if (el.parentNode) { + el.parentNode.removeChild(el); + } + }; self.render = function (d) { - d.appendChild(el) - map.invalidateSize() - } + d.appendChild(el); + map.invalidateSize(); + }; - return self - } -}) + return self; + }; + }); diff --git a/lib/map/clientlayer.js b/lib/map/clientlayer.js index c3fb439..d6ee4fc 100644 --- a/lib/map/clientlayer.js +++ b/lib/map/clientlayer.js @@ -1,76 +1,79 @@ define(["leaflet", "jshashes"], function (L, jsHashes) { - var MD5 = new jsHashes.MD5() + var MD5 = new jsHashes.MD5(); return L.TileLayer.Canvas.extend({ setData: function (d) { - this.data = d + this.data = d; //pre-calculate start angles this.data.all().forEach(function (d) { - var hash = MD5.hex(d.node.nodeinfo.node_id) - d.startAngle = (parseInt(hash.substr(0, 2), 16) / 255) * 2 * Math.PI - }) - this.redraw() + var hash = MD5.hex(d.node.nodeinfo.node_id); + d.startAngle = (parseInt(hash.substr(0, 2), 16) / 255) * 2 * Math.PI; + }); + this.redraw(); }, drawTile: function (canvas, tilePoint) { function getTileBBox(s, map, tileSize, margin) { - var tl = map.unproject([s.x - margin, s.y - margin]) - var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]) + var tl = map.unproject([s.x - margin, s.y - margin]); + var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]); - return [br.lat, tl.lng, tl.lat, br.lng] + return [br.lat, tl.lng, tl.lat, br.lng]; } - if (!this.data) - return + if (!this.data) { + return; + } - var tileSize = this.options.tileSize - var s = tilePoint.multiplyBy(tileSize) - var map = this._map + var tileSize = this.options.tileSize; + var s = tilePoint.multiplyBy(tileSize); + var map = this._map; - var margin = 50 - var bbox = getTileBBox(s, map, tileSize, margin) + var margin = 50; + var bbox = getTileBBox(s, map, tileSize, margin); - var nodes = this.data.search(bbox) + var nodes = this.data.search(bbox); - if (nodes.length === 0) - return + if (nodes.length === 0) { + return; + } - var ctx = canvas.getContext("2d") + var ctx = canvas.getContext("2d"); - var radius = 3 - var a = 1.2 - var startDistance = 12 + var radius = 3; + var a = 1.2; + var startDistance = 12; - ctx.beginPath() + ctx.beginPath(); nodes.forEach(function (d) { - var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]) - var clients = d.node.statistics.clients + var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]); + var clients = d.node.statistics.clients; - if (clients === 0) - return + if (clients === 0) { + return; + } - p.x -= s.x - p.y -= s.y + p.x -= s.x; + p.y -= s.y; for (var orbit = 0, i = 0; i < clients; orbit++) { - var distance = startDistance + orbit * 2 * radius * a - var n = Math.floor((Math.PI * distance) / (a * radius)) - var delta = clients - i + var distance = startDistance + orbit * 2 * radius * a; + var n = Math.floor((Math.PI * distance) / (a * radius)); + var delta = clients - i; for (var j = 0; j < Math.min(delta, n); i++, j++) { - var angle = 2 * Math.PI / n * j - var x = p.x + distance * Math.cos(angle + d.startAngle) - var y = p.y + distance * Math.sin(angle + d.startAngle) + var angle = 2 * Math.PI / n * j; + var x = p.x + distance * Math.cos(angle + d.startAngle); + var y = p.y + distance * Math.sin(angle + d.startAngle); - ctx.moveTo(x, y) - ctx.arc(x, y, radius, 0, 2 * Math.PI) + ctx.moveTo(x, y); + ctx.arc(x, y, radius, 0, 2 * Math.PI); } } - }) + }); - ctx.fillStyle = "rgba(220, 0, 103, 0.7)" - ctx.fill() + ctx.fillStyle = "rgba(220, 0, 103, 0.7)"; + ctx.fill(); } - }) -}) + }); + }); diff --git a/lib/map/labelslayer.js b/lib/map/labelslayer.js index 2912c10..e616877 100644 --- a/lib/map/labelslayer.js +++ b/lib/map/labelslayer.js @@ -1,88 +1,93 @@ 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 + var fontFamily = "Roboto"; + var nodeRadius = 4; - var ctx = document.createElement("canvas").getContext("2d") + var ctx = document.createElement("canvas").getContext("2d"); function measureText(font, text) { - ctx.font = font - return ctx.measureText(text) + ctx.font = font; + return ctx.measureText(text); } function mapRTree(d) { - var o = [d.position.lat, d.position.lng, d.position.lat, d.position.lng] + var o = [d.position.lat, d.position.lng, d.position.lat, d.position.lng]; - o.label = d + o.label = d; - return o + return o; } 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 - } - } + 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 + }; + }; } 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) { - var margin = 1 + 1.41 * (1 - (z - minZoom) / (maxZoom - minZoom)) + var margin = 1 + 1.41 * (1 - (z - minZoom) / (maxZoom - minZoom)); - var width = label.width * margin - var height = label.height * margin + 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]] + var x = p.x + offset[0] + dx[anchor[0]]; + var y = p.y + offset[1] + dy[anchor[1]]; - return [x, y, x + width, y + height] + return [x, y, x + width, y + height]; } var c = L.TileLayer.Canvas.extend({ onAdd: function (map) { - L.TileLayer.Canvas.prototype.onAdd.call(this, map) - if (this.data) - this.prepareLabels() + L.TileLayer.Canvas.prototype.onAdd.call(this, map); + if (this.data) { + this.prepareLabels(); + } }, setData: function (d) { - this.data = d - if (this._map) - this.prepareLabels() + this.data = d; + if (this._map) { + this.prepareLabels(); + } }, prepareLabels: function () { - var d = this.data + var d = this.data; // label: // - position (WGS84 coords) @@ -92,137 +97,144 @@ define(["leaflet", "rbush"], // - label (string) // - color (string) - var labelsOnline = d.online.map(prepareLabel("rgba(0, 0, 0, 0.9)", 10, 8, true, 13)) - var labelsOffline = d.offline.map(prepareLabel("rgba(212, 62, 42, 0.9)", 9, 5, false, 16)) - var labelsNew = d.new.map(prepareLabel("rgba(48, 99, 20, 0.9)", 11, 8, true, 0)) - var labelsLost = d.lost.map(prepareLabel("rgba(212, 62, 42, 0.9)", 11, 8, true, 0)) + var labelsOnline = d.online.map(prepareLabel("rgba(0, 0, 0, 0.9)", 10, 8, true, 13)); + var labelsOffline = d.offline.map(prepareLabel("rgba(212, 62, 42, 0.9)", 9, 5, false, 16)); + var labelsNew = d.new.map(prepareLabel("rgba(48, 99, 20, 0.9)", 11, 8, true, 0)); + 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 + var minZoom = this.options.minZoom; + var maxZoom = this.options.maxZoom; - var trees = [] + var trees = []; - var map = this._map + var map = this._map; function nodeToRect(z) { return function (d) { - var p = map.project(d.position, z) + 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]; + }; } for (var z = minZoom; z <= maxZoom; z++) { - trees[z] = rbush(9) - trees[z].load(labels.map(nodeToRect(z))) + trees[z] = rbush(9); + trees[z].load(labels.map(nodeToRect(z))); } labels = labels.map(function (d) { var best = labelLocations.map(function (loc) { - var offset = calcOffset(d.offset, loc) - var z + var offset = calcOffset(d.offset, loc); + var z; for (z = maxZoom; z >= d.minZoom; z--) { - var p = map.project(d.position, z) - var rect = labelRect(p, offset, loc, d, minZoom, maxZoom, z) - var candidates = trees[z].search(rect) + var p = map.project(d.position, z); + var rect = labelRect(p, offset, loc, d, minZoom, maxZoom, z); + var candidates = trees[z].search(rect); - if (candidates.length > 0) - break + if (candidates.length > 0) { + break; + } } - return {loc: loc, z: z + 1} + return {loc: loc, z: z + 1}; }).filter(function (d) { - return d.z <= maxZoom + return d.z <= maxZoom; }).sort(function (a, b) { - return a.z - b.z - })[0] + return a.z - b.z; + })[0]; if (best !== undefined) { - d.offset = calcOffset(d.offset, best.loc) - d.minZoom = best.z - d.anchor = best.loc + d.offset = calcOffset(d.offset, best.loc); + d.minZoom = best.z; + d.anchor = best.loc; for (var z = maxZoom; z >= best.z; z--) { - var p = map.project(d.position, z) - var rect = labelRect(p, d.offset, best.loc, d, minZoom, maxZoom, z) - trees[z].insert(rect) + var p = map.project(d.position, z); + var rect = labelRect(p, d.offset, best.loc, d, minZoom, maxZoom, z); + trees[z].insert(rect); } - return d - } else - return undefined - }).filter(function (d) { return d !== undefined }) + return d; + } else { + return undefined; + } + }).filter(function (d) { + return d !== undefined; + }); - this.margin = 16 + this.margin = 16; - if (labels.length > 0) + if (labels.length > 0) { this.margin += labels.map(function (d) { - return d.width - }).sort().reverse()[0] + return d.width; + }).sort().reverse()[0]; + } - this.labels = rbush(9) - this.labels.load(labels.map(mapRTree)) + this.labels = rbush(9); + this.labels.load(labels.map(mapRTree)); - this.redraw() + this.redraw(); }, drawTile: function (canvas, tilePoint, zoom) { function getTileBBox(s, map, tileSize, margin) { - var tl = map.unproject([s.x - margin, s.y - margin]) - var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]) + var tl = map.unproject([s.x - margin, s.y - margin]); + var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]); - return [br.lat, tl.lng, tl.lat, br.lng] + return [br.lat, tl.lng, tl.lat, br.lng]; } - if (!this.labels) - return + if (!this.labels) { + return; + } - var tileSize = this.options.tileSize - var s = tilePoint.multiplyBy(tileSize) - var map = this._map + var tileSize = this.options.tileSize; + var s = tilePoint.multiplyBy(tileSize); + var map = this._map; function projectNodes(d) { - var p = map.project(d.label.position) + var p = map.project(d.label.position); - p.x -= s.x - p.y -= s.y + p.x -= s.x; + p.y -= s.y; - return {p: p, label: d.label} + return {p: p, label: d.label}; } - var bbox = getTileBBox(s, map, tileSize, this.margin) + var bbox = getTileBBox(s, map, tileSize, this.margin); - var labels = this.labels.search(bbox).map(projectNodes) + var labels = this.labels.search(bbox).map(projectNodes); - var ctx = canvas.getContext("2d") + var ctx = canvas.getContext("2d"); - ctx.lineWidth = 5 - ctx.strokeStyle = "rgba(255, 255, 255, 0.8)" - ctx.miterLimit = 2 + ctx.lineWidth = 5; + ctx.strokeStyle = "rgba(255, 255, 255, 0.8)"; + ctx.miterLimit = 2; function drawLabel(d) { - ctx.font = d.label.font - ctx.textAlign = d.label.anchor[0] - ctx.textBaseline = d.label.anchor[1] - ctx.fillStyle = d.label.fillStyle + ctx.font = d.label.font; + ctx.textAlign = d.label.anchor[0]; + ctx.textBaseline = d.label.anchor[1]; + ctx.fillStyle = d.label.fillStyle; - if (d.label.stroke) - ctx.strokeText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]) + 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]) + ctx.fillText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]); } labels.filter(function (d) { - return zoom >= d.label.minZoom - }).forEach(drawLabel) + return zoom >= d.label.minZoom; + }).forEach(drawLabel); } - }) + }); - return c -}) + return c; + }); diff --git a/lib/meshstats.js b/lib/meshstats.js index 9d50044..0abd3cc 100644 --- a/lib/meshstats.js +++ b/lib/meshstats.js @@ -1,55 +1,61 @@ define(function () { return function (config) { - var self = this - var stats, timestamp + var self = this; + var stats, timestamp; self.setData = function (d) { - var totalNodes = sum(d.nodes.all.map(one)) - var totalOnlineNodes = sum(d.nodes.all.filter(online).map(one)) - var totalOfflineNodes = sum(d.nodes.all.filter(function (node) {return !node.flags.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) { - return d.statistics.clients ? d.statistics.clients : 0 - })) - var totalGateways = sum(Array.from(new Set(d.nodes.all.filter(online).map( function(d) { - return ("gateway" in d.statistics && d.statistics.gateway.id) ? d.statistics.gateway.id : d.statistics.gateway - }).concat(d.nodes.all.filter( function (d) { - return d.flags.gateway - })))).map(function(d) { - return (typeof d === "string") ? 1 : 0 - })) + var totalNodes = sum(d.nodes.all.map(one)); + var totalOnlineNodes = sum(d.nodes.all.filter(online).map(one)); + var totalOfflineNodes = sum(d.nodes.all.filter(function (node) { + return !node.flags.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) { + return d.statistics.clients ? d.statistics.clients : 0; + })); + var totalGateways = sum(Array.from(new Set(d.nodes.all.filter(online).map(function (d) { + return ("gateway" in d.statistics && d.statistics.gateway.id) ? d.statistics.gateway.id : d.statistics.gateway; + }).concat(d.nodes.all.filter(function (d) { + return d.flags.gateway; + })))).map(function (d) { + return (typeof d === "string") ? 1 : 0; + })); - var nodetext = [{ count: totalOnlineNodes, label: "online" }, - { count: totalOfflineNodes, label: "offline" }, - { 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: totalOfflineNodes, label: "offline"}, + {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") + "." - } + timestamp.textContent = "Diese Daten sind von " + d.timestamp.format("LLLL") + "."; + }; self.render = function (el) { - var h2 = document.createElement("h2") - h2.textContent = config.siteName - el.appendChild(h2) + var h2 = document.createElement("h2"); + h2.textContent = config.siteName; + el.appendChild(h2); - var p = document.createElement("p") - el.appendChild(p) - stats = document.createTextNode("") - p.appendChild(stats) - p.appendChild(document.createElement("br")) - timestamp = document.createTextNode("") - p.appendChild(timestamp) - } + var p = document.createElement("p"); + el.appendChild(p); + stats = document.createTextNode(""); + p.appendChild(stats); + p.appendChild(document.createElement("br")); + timestamp = document.createTextNode(""); + p.appendChild(timestamp); + }; - return self - } -}) + return self; + }; +}); diff --git a/lib/nodelist.js b/lib/nodelist.js index 2130061..b81ec59 100644 --- a/lib/nodelist.js +++ b/lib/nodelist.js @@ -1,97 +1,107 @@ define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral) { function getUptime(now, d) { - if (d.flags.online && "uptime" in d.statistics) - return Math.round(d.statistics.uptime) - else if (!d.flags.online && "lastseen" in d) - return Math.round(-(now.unix() - d.lastseen.unix())) + if (d.flags.online && "uptime" in d.statistics) { + return Math.round(d.statistics.uptime); + } else if (!d.flags.online && "lastseen" in d) { + return Math.round(-(now.unix() - d.lastseen.unix())); + } } function showUptime(uptime) { - var s = "" - uptime /= 3600 + var s = ""; + uptime /= 3600; - if (uptime !== undefined) - if (Math.abs(uptime) >= 24) - s = Math.round(uptime / 24) + "d" - else - s = Math.round(uptime) + "h" + if (uptime !== undefined) { + if (Math.abs(uptime) >= 24) { + s = Math.round(uptime / 24) + "d"; + } else { + s = Math.round(uptime) + "h"; + } + } - return s + return s; } - var headings = [{ name: "Knoten", - sort: function (a, b) { - var aname = typeof a.nodeinfo.hostname === "string" ? a.nodeinfo.hostname : a.nodeinfo.node_id - var bname = typeof b.nodeinfo.hostname === "string" ? b.nodeinfo.hostname : b.nodeinfo.node_id - if (typeof aname === "string" && typeof bname === "string") - return aname.localeCompare(bname) - return typeof aname === "string" ? 1 : typeof bname === "string" ? -1 : 0 - }, - 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) { + var aname = typeof a.nodeinfo.hostname === "string" ? a.nodeinfo.hostname : a.nodeinfo.node_id; + var bname = typeof b.nodeinfo.hostname === "string" ? b.nodeinfo.hostname : b.nodeinfo.node_id; + if (typeof aname === "string" && typeof bname === "string") { + return aname.localeCompare(bname); + } + return typeof aname === "string" ? 1 : typeof bname === "string" ? -1 : 0; + }, + 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"] + var td1Content = []; + var aClass = ["hostname", d.flags.online ? "online" : "offline"]; - td1Content.push(V.h("a", { className: aClass.join(" "), - onclick: router.node(d), - href: "#!n:" + d.nodeinfo.node_id - }, d.nodeinfo.hostname)) + td1Content.push(V.h("a", { + className: aClass.join(" "), + onclick: router.node(d), + href: "#!n:" + d.nodeinfo.node_id + }, d.nodeinfo.hostname)); - if (has_location(d)) - td1Content.push(V.h("span", {className: "icon ion-location"})) + 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)) - var td3 = V.h("td", d.meshlinks.toString()) - var td4 = V.h("td", numeral("clients" in d.statistics ? d.statistics.clients : "").format("0,0")) + var td1 = V.h("td", td1Content); + var td2 = V.h("td", showUptime(d.uptime)); + var td3 = V.h("td", d.meshlinks.toString()); + var td4 = V.h("td", numeral("clients" in d.statistics ? d.statistics.clients : "").format("0,0")); - return V.h("tr", [td1, td2, td3, td4]) + return V.h("tr", [td1, td2, td3, td4]); } - var table = new SortTable(headings, 0, renderRow) + var table = new SortTable(headings, 0, renderRow); this.render = function (d) { - var el = document.createElement("div") - d.appendChild(el) + var el = document.createElement("div"); + d.appendChild(el); - var h2 = document.createElement("h2") - h2.textContent = "Alle Knoten" - el.appendChild(h2) + var h2 = document.createElement("h2"); + h2.textContent = "Alle Knoten"; + el.appendChild(h2); - el.appendChild(table.el) - } + el.appendChild(table.el); + }; this.setData = function (d) { var data = d.nodes.all.map(function (e) { - var n = Object.create(e) - n.uptime = getUptime(d.now, e) || 0 - n.meshlinks = e.meshlinks || 0 - return n - }) + var n = Object.create(e); + n.uptime = getUptime(d.now, e) || 0; + n.meshlinks = e.meshlinks || 0; + return n; + }); - table.setData(data) - } - } -}) + table.setData(data); + }; + }; +}); diff --git a/lib/proportions.js b/lib/proportions.js index f5b0b26..8523165 100644 --- a/lib/proportions.js +++ b/lib/proportions.js @@ -1,236 +1,278 @@ -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 count(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] = 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 showStatGlobal(o) { + return showStat(o); } - } - function fillTable(name, table, data) { - if (!table.last) - table.last = V.h("table") + function count(nodes, key, f) { + var dict = {}; - var max = 0 - data.forEach(function (d) { - if (d[1] > max) - max = d[1] - }) + nodes.forEach(function (d) { + var v = dictGet(d, key.slice(0)); - 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") + if (f !== undefined) { + v = f(v); + } - var filter = new Filter(name, d[2], d[0], d[3]) + if (v === null) { + return; + } - var a = V.h("a", { href: "#", onclick: addFilter(filter) }, d[0]) + dict[v] = 1 + (v in dict ? dict[v] : 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 Object.keys(dict).map(function (d) { + return [d, dict[d], key, f]; + }); + } - return V.h("tr", [th, td]) - }) + function countClients(nodes, key, f) { + var dict = {}; - var tableNew = V.h("table", items) - table = V.patch(table, V.diff(table.last, tableNew)) - table.last = tableNew - } + nodes.forEach(function (d) { + var v = dictGet(d, key.slice(0)); - self.setData = function (data) { - var onlineNodes = data.nodes.all.filter(online) - var nodes = onlineNodes.concat(data.nodes.lost) - var nodeDict = {} + if (f !== undefined) { + v = f(v); + } - data.nodes.all.forEach(function (d) { - nodeDict[d.nodeinfo.node_id] = d - }) + if (v === null) { + return; + } - 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"], function (d) { - if (d) { - d = d.replace(/\(r\)|\(tm\)/gi, "").replace(/AMD |Intel |TP-Link | CPU| Processor/g, "") - if (d.indexOf("@") > 0) d = d.substring(0, d.indexOf("@")) + 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"); } - return d - }) - 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 max = 0; + data.forEach(function (d) { + if (d[1] > max) { + max = d[1]; + } + }); - var uplinkDict = count(nodes, ["flags", "uplink"], function (d) { - return d ? "ja" : "nein" - }) + 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 gwNodesDict = count(onlineNodes, ["statistics", "gateway"], function (d) { - if (d === null) - return null + var filter = new Filter(name, d[2], d[0], d[3]); - if (d.node) - return d.node.nodeinfo.hostname + var a = V.h("a", {href: "#", onclick: addFilter(filter)}, d[0]); - if (d.id) - return d.id + 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 d - }) + return V.h("tr", [th, td]); + }); - var gwClientsDict = countClients(onlineNodes, ["statistics", "gateway"], function (d) { - if (d === null) - return null - - if (d.node) - return d.node.nodeinfo.hostname - - if (d.id) - return d.id - - 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("Gateway", gwNodesTable, gwNodesDict.sort(function (a, b) { return b[1] - a[1] })) - fillTable("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)) - }) + var tableNew = V.h("table", items); + table = V.patch(table, V.diff(table.last, tableNew)); + table.last = tableNew; } - 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.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"], function (d) { + if (d) { + d = d.replace(/\(r\)|\(tm\)/gi, "").replace(/AMD |Intel |TP-Link | CPU| Processor/g, ""); + if (d.indexOf("@") > 0) { + d = d.substring(0, d.indexOf("@")); + } + } + return d; + }); + 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.node) { + return d.node.nodeinfo.hostname; + } + + if (d.id) { + return d.id; + } + + return d; + }); + + var gwClientsDict = countClients(onlineNodes, ["statistics", "gateway"], function (d) { + if (d === null) { + return null; + } + + if (d.node) { + return d.node.nodeinfo.hostname; + } + + if (d.id) { + return d.id; + } + + 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("Gateway", gwNodesTable, gwNodesDict.sort(function (a, b) { + return b[1] - a[1]; + })); + fillTable("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)); + }); + } + }; + + 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 fb56bb1..f719cc0 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,214 +1,230 @@ define(function () { return function () { - var self = this - var objects = { nodes: {}, links: {} } - var targets = [] - var views = {} - var currentView - var currentObject - var running = false + var self = this; + var objects = {nodes: {}, links: {}}; + var targets = []; + var views = {}; + var currentView; + var currentObject; + var running = false; function saveState() { - var e = [] + var e = []; - if (currentView) - e.push("v:" + currentView) - - if (currentObject) { - if ("node" in currentObject) - e.push("n:" + encodeURIComponent(currentObject.node.nodeinfo.node_id)) - - if ("link" in currentObject) - e.push("l:" + encodeURIComponent(currentObject.link.id)) + if (currentView) { + e.push("v:" + currentView); } - var s = "#!" + e.join(";") + if (currentObject) { + if ("node" in currentObject) { + e.push("n:" + encodeURIComponent(currentObject.node.nodeinfo.node_id)); + } - window.history.pushState(s, undefined, s) + if ("link" in currentObject) { + e.push("l:" + encodeURIComponent(currentObject.link.id)); + } + } + + var s = "#!" + e.join(";"); + + window.history.pushState(s, undefined, s); } function resetView(push) { - push = trueDefault(push) + push = trueDefault(push); - targets.forEach( function (t) { - t.resetView() - }) + targets.forEach(function (t) { + t.resetView(); + }); if (push) { - currentObject = undefined - saveState() + currentObject = undefined; + saveState(); } } function gotoNode(d) { - if (!d) - return false + if (!d) { + return false; + } - targets.forEach( function (t) { - t.gotoNode(d) - }) + targets.forEach(function (t) { + t.gotoNode(d); + }); - return true + return true; } function gotoLink(d) { - if (!d) - return false + if (!d) { + return false; + } - targets.forEach( function (t) { - t.gotoLink(d) - }) + targets.forEach(function (t) { + t.gotoLink(d); + }); - return true + return true; } function gotoLocation(d) { - if (!d) - return false + if (!d) { + return false; + } - targets.forEach( function (t) { - if(!t.gotoLocation)console.warn("has no gotoLocation", t) - t.gotoLocation(d) - }) + targets.forEach(function (t) { + if (!t.gotoLocation) { + console.warn("has no gotoLocation", t); + } + t.gotoLocation(d); + }); - return true + return true; } function loadState(s) { - if (!s) - return false + if (!s) { + return false; + } - s = decodeURIComponent(s) + s = decodeURIComponent(s); - if (!s.startsWith("#!")) - return false + if (!s.startsWith("#!")) { + return false; + } - var targetSet = false + var targetSet = false; s.slice(2).split(";").forEach(function (d) { - var args = d.split(":") + var args = d.split(":"); if (args[0] === "v" && args[1] in views) { - currentView = args[1] - views[args[1]]() + currentView = args[1]; + views[args[1]](); } - var id + var id; if (args[0] === "n") { - id = args[1] + id = args[1]; if (id in objects.nodes) { - currentObject = { node: objects.nodes[id] } - gotoNode(objects.nodes[id]) - targetSet = true + currentObject = {node: objects.nodes[id]}; + gotoNode(objects.nodes[id]); + targetSet = true; } } if (args[0] === "l") { - id = args[1] + id = args[1]; if (id in objects.links) { - currentObject = { link: objects.links[id] } - gotoLink(objects.links[id]) - targetSet = true + currentObject = {link: objects.links[id]}; + gotoLink(objects.links[id]); + targetSet = true; } } - }) + }); - return targetSet + return targetSet; } self.start = function () { - running = true + running = true; - if (!loadState(window.location.hash)) - resetView(false) + if (!loadState(window.location.hash)) { + resetView(false); + } window.onpopstate = function (d) { - if (!loadState(d.state)) - resetView(false) - } - } + if (!loadState(d.state)) { + resetView(false); + } + }; + }; self.view = function (d) { if (d in views) { - views[d]() + views[d](); - if (!currentView || running) - currentView = d - - if (!running) - return - - saveState() - - if (!currentObject) { - resetView(false) - return + if (!currentView || running) { + currentView = d; } - if ("node" in currentObject) - gotoNode(currentObject.node) + if (!running) { + return; + } - if ("link" in currentObject) - gotoLink(currentObject.link) + saveState(); + + if (!currentObject) { + resetView(false); + return; + } + + if ("node" in currentObject) { + gotoNode(currentObject.node); + } + + if ("link" in currentObject) { + gotoLink(currentObject.link); + } } - } + }; self.node = function (d) { return function () { if (gotoNode(d)) { - currentObject = { node: d } - saveState() + currentObject = {node: d}; + saveState(); } - return false - } - } + return false; + }; + }; self.link = function (d) { return function () { if (gotoLink(d)) { - currentObject = { link: d } - saveState() + currentObject = {link: d}; + saveState(); } - return false - } - } + return false; + }; + }; - self.gotoLocation = gotoLocation + self.gotoLocation = gotoLocation; self.reset = function () { - resetView() - } + resetView(); + }; self.addTarget = function (d) { - targets.push(d) - } + targets.push(d); + }; self.removeTarget = function (d) { - targets = targets.filter( function (e) { - return d !== e - }) - } + targets = targets.filter(function (e) { + return d !== e; + }); + }; self.addView = function (k, d) { - views[k] = d - } + views[k] = d; + }; self.setData = function (data) { - objects.nodes = {} - objects.links = {} + objects.nodes = {}; + objects.links = {}; - data.nodes.all.forEach( function (d) { - objects.nodes[d.nodeinfo.node_id] = d - }) + data.nodes.all.forEach(function (d) { + objects.nodes[d.nodeinfo.node_id] = d; + }); - data.graph.links.forEach( function (d) { - objects.links[d.id] = d - }) - } + data.graph.links.forEach(function (d) { + objects.links[d.id] = d; + }); + }; - return self - } -}) + return self; + }; +}); diff --git a/lib/sidebar.js b/lib/sidebar.js index 464c0fa..4c839cc 100644 --- a/lib/sidebar.js +++ b/lib/sidebar.js @@ -1,49 +1,50 @@ define([], function () { return function (el) { - var self = this + var self = this; - var sidebar = document.createElement("div") - sidebar.classList.add("sidebar") - el.appendChild(sidebar) + var sidebar = document.createElement("div"); + sidebar.classList.add("sidebar"); + el.appendChild(sidebar); - var button = document.createElement("button") - sidebar.appendChild(button) + var button = document.createElement("button"); + sidebar.appendChild(button); - button.classList.add("sidebarhandle") + button.classList.add("sidebarhandle"); button.onclick = function () { - sidebar.classList.toggle("hidden") - } + sidebar.classList.toggle("hidden"); + }; - var container = document.createElement("div") - container.classList.add("container") - sidebar.appendChild(container) + var container = document.createElement("div"); + container.classList.add("container"); + 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 - } + var small = window.matchMedia("(max-width: 630pt)"); + return small.matches ? 0 : sidebar.offsetWidth; + }; self.add = function (d) { - d.render(container) - } + d.render(container); + }; self.ensureVisible = function () { - sidebar.classList.remove("hidden") - } + sidebar.classList.remove("hidden"); + }; self.hide = function () { - container.classList.add("hidden") - } + container.classList.add("hidden"); + }; self.reveal = function () { - container.classList.remove("hidden") - } + container.classList.remove("hidden"); + }; - self.container = sidebar + self.container = sidebar; - return self - } -}) + return self; + }; +}); diff --git a/lib/simplenodelist.js b/lib/simplenodelist.js index 8a763df..a327203 100644 --- a/lib/simplenodelist.js +++ b/lib/simplenodelist.js @@ -1,63 +1,66 @@ define(["moment", "virtual-dom"], function (moment, V) { - return function(nodes, field, router, title) { - var self = this - var el, tbody + return function (nodes, field, router, title) { + var self = this; + var el, tbody; self.render = function (d) { - el = document.createElement("div") - d.appendChild(el) - } + el = document.createElement("div"); + d.appendChild(el); + }; self.setData = function (data) { - var list = data.nodes[nodes] + 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 + tbody = null; - return + return; } if (!tbody) { - var h2 = document.createElement("h2") - h2.textContent = title - el.appendChild(h2) + var h2 = document.createElement("h2"); + h2.textContent = title; + el.appendChild(h2); - var table = document.createElement("table") - el.appendChild(table) + var table = document.createElement("table"); + el.appendChild(table); - tbody = document.createElement("tbody") - tbody.last = V.h("tbody") - table.appendChild(tbody) + tbody = document.createElement("tbody"); + tbody.last = V.h("tbody"); + table.appendChild(tbody); } - var items = list.map( function (d) { - var time = moment(d[field]).from(data.now) - var td1Content = [] + var items = list.map(function (d) { + var time = moment(d[field]).from(data.now); + var td1Content = []; - var aClass = ["hostname", d.flags.online ? "online" : "offline"] + var aClass = ["hostname", d.flags.online ? "online" : "offline"]; - td1Content.push(V.h("a", { className: aClass.join(" "), - onclick: router.node(d), - href: "#!n:" + d.nodeinfo.node_id - }, d.nodeinfo.hostname)) + td1Content.push(V.h("a", { + className: aClass.join(" "), + onclick: router.node(d), + href: "#!n:" + d.nodeinfo.node_id + }, d.nodeinfo.hostname)); - if (has_location(d)) - td1Content.push(V.h("span", {className: "icon ion-location"})) + 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) + var td1 = V.h("td", td1Content); + var td2 = V.h("td", time); - return V.h("tr", [td1, td2]) - }) + return V.h("tr", [td1, td2]); + }); - var tbodyNew = V.h("tbody", items) - tbody = V.patch(tbody, V.diff(tbody.last, tbodyNew)) - tbody.last = tbodyNew - } + var tbodyNew = V.h("tbody", items); + tbody = V.patch(tbody, V.diff(tbody.last, tbodyNew)); + tbody.last = tbodyNew; + }; - return self - } -}) + return self; + }; +}); diff --git a/lib/sorttable.js b/lib/sorttable.js index d6a39a1..881efba 100644 --- a/lib/sorttable.js +++ b/lib/sorttable.js @@ -1,57 +1,62 @@ define(["virtual-dom"], function (V) { - return function(headings, sortIndex, renderRow) { - var data - var sortReverse = false - var el = document.createElement("table") - var elLast = V.h("table") + return function (headings, sortIndex, renderRow) { + var data; + var sortReverse = false; + var el = document.createElement("table"); + var elLast = V.h("table"); function sortTable(i) { - sortReverse = i === sortIndex ? !sortReverse : false - sortIndex = i + sortReverse = i === sortIndex ? !sortReverse : false; + sortIndex = i; - updateView() + updateView(); } function sortTableHandler(i) { - return function () { sortTable(i) } + return function () { + sortTable(i); + }; } function updateView() { - var children = [] + var children = []; 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) - properties.className += sortReverse ? " sort-up" : " sort-down" + if (sortIndex === i) { + properties.className += sortReverse ? " sort-up" : " sort-down"; + } - return V.h("th", properties, d.name) - }) + return V.h("th", properties, d.name); + }); - var links = data.slice(0).sort(headings[sortIndex].sort) + var links = data.slice(0).sort(headings[sortIndex].sort); - if (headings[sortIndex].reverse ? !sortReverse : sortReverse) - links = links.reverse() + 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))) + children.push(V.h("thead", V.h("tr", th))); + children.push(V.h("tbody", links.map(renderRow))); } - var elNew = V.h("table", children) - el = V.patch(el, V.diff(elLast, elNew)) - elLast = elNew + var elNew = V.h("table", children); + el = V.patch(el, V.diff(elLast, elNew)); + elLast = elNew; } this.setData = function (d) { - data = d - updateView() - } + data = d; + updateView(); + }; - this.el = el + this.el = el; - return this - } -}) + return this; + }; +}); diff --git a/lib/tabs.js b/lib/tabs.js index 7605742..f4fed6d 100644 --- a/lib/tabs.js +++ b/lib/tabs.js @@ -1,57 +1,61 @@ define([], function () { return function () { - var self = this + var self = this; - var tabs = document.createElement("ul") - tabs.classList.add("tabs") + var tabs = document.createElement("ul"); + tabs.classList.add("tabs"); - var container = document.createElement("div") + var container = document.createElement("div"); function gotoTab(li) { - for (var i = 0; i < tabs.children.length; i++) - tabs.children[i].classList.remove("visible") + for (var i = 0; i < tabs.children.length; i++) { + tabs.children[i].classList.remove("visible"); + } - while (container.firstChild) - container.removeChild(container.firstChild) + while (container.firstChild) { + container.removeChild(container.firstChild); + } - li.classList.add("visible") + li.classList.add("visible"); - var tab = document.createElement("div") - tab.classList.add("tab") - container.appendChild(tab) - li.child.render(tab) + var tab = document.createElement("div"); + tab.classList.add("tab"); + container.appendChild(tab); + li.child.render(tab); } function switchTab() { - gotoTab(this) + gotoTab(this); - return false + return false; } self.add = function (title, d) { - var li = document.createElement("li") - li.textContent = title - li.onclick = switchTab - li.child = d - tabs.appendChild(li) + var li = document.createElement("li"); + li.textContent = title; + li.onclick = switchTab; + li.child = d; + tabs.appendChild(li); - var anyVisible = false + var anyVisible = false; - for (var i = 0; i < tabs.children.length; i++) + for (var i = 0; i < tabs.children.length; i++) { if (tabs.children[i].classList.contains("visible")) { - anyVisible = true - break + anyVisible = true; + break; } + } - if (!anyVisible) - gotoTab(li) - } + if (!anyVisible) { + gotoTab(li); + } + }; self.render = function (el) { - el.appendChild(tabs) - el.appendChild(container) - } + el.appendChild(tabs); + el.appendChild(container); + }; - return self - } -}) + return self; + }; +}); diff --git a/lib/title.js b/lib/title.js index e9377a4..4c99c2c 100644 --- a/lib/title.js +++ b/lib/title.js @@ -1,35 +1,38 @@ define(function () { - return function (config) { + return function (config) { function setTitle(d) { - var title = [config.siteName] + var title = [config.siteName]; - if (d !== undefined) - title.push(d) + if (d !== undefined) { + title.push(d); + } - document.title = title.join(": ") + document.title = title.join(": "); } this.resetView = function () { - setTitle() - } + setTitle(); + }; this.gotoNode = function (d) { - if (d) - setTitle(d.nodeinfo.hostname) - } + if (d) { + setTitle(d.nodeinfo.hostname); + } + }; this.gotoLink = function (d) { - if (d) - setTitle((d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + d.target.node.nodeinfo.hostname) - } + 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 - } + }; this.destroy = function () { - } + }; - return this - } -}) + return this; + }; +}); diff --git a/lib/vercomp.js b/lib/vercomp.js index 428f258..4752b11 100644 --- a/lib/vercomp.js +++ b/lib/vercomp.js @@ -1,60 +1,68 @@ define([], function () { function order(c) { - if (/^\d$/.test(c)) - return 0 - else if (/^[a-z]$/i.test(c)) - return c.charCodeAt(0) - else if (c === "~") - return -1 - else if (c) - return c.charCodeAt(0) + 256 - else - return 0 + if (/^\d$/.test(c)) { + return 0; + } else if (/^[a-z]$/i.test(c)) { + return c.charCodeAt(0); + } else if (c === "~") { + return -1; + } else if (c) { + return c.charCodeAt(0) + 256; + } else { + return 0; + } } // Based on dpkg code function vercomp(a, b) { - var apos = 0, bpos = 0 + var apos = 0, bpos = 0; while (apos < a.length || bpos < b.length) { - var firstDiff = 0 + var firstDiff = 0; while ((apos < a.length && !/^\d$/.test(a[apos])) || (bpos < b.length && !/^\d$/.test(b[bpos]))) { - var ac = order(a[apos]) - var bc = order(b[bpos]) + var ac = order(a[apos]); + var bc = order(b[bpos]); - if (ac !== bc) - return ac - bc + if (ac !== bc) { + return ac - bc; + } - apos++ - bpos++ + apos++; + bpos++; } - while (a[apos] === "0") - apos++ + while (a[apos] === "0") { + apos++; + } - while (b[bpos] === "0") - bpos++ + while (b[bpos] === "0") { + bpos++; + } while (/^\d$/.test(a[apos]) && /^\d$/.test(b[bpos])) { - if (firstDiff === 0) - firstDiff = a.charCodeAt(apos) - b.charCodeAt(bpos) + if (firstDiff === 0) { + firstDiff = a.charCodeAt(apos) - b.charCodeAt(bpos); + } - apos++ - bpos++ + apos++; + bpos++; } - if (/^\d$/.test(a[apos])) - return 1 + if (/^\d$/.test(a[apos])) { + return 1; + } - if (/^\d$/.test(b[bpos])) - return -1 + if (/^\d$/.test(b[bpos])) { + return -1; + } - if (firstDiff !== 0) - return firstDiff + if (firstDiff !== 0) { + return firstDiff; + } } - return 0 + return 0; } - return vercomp -}) + return vercomp; +}); diff --git a/scss/main.scss b/scss/main.scss index 47cbed9..0415212 100644 --- a/scss/main.scss +++ b/scss/main.scss @@ -131,14 +131,14 @@ table.attributes tr.routerpic td { font-weight: bold; /*background-color: red;*/ font-size: large; - vertical-align:bottom; + vertical-align: bottom; } table.attributes tr.routerpic th { font-weight: bold; /*background-color: red;*/ font-size: large; - vertical-align:bottom; + vertical-align: bottom; } .nodenamesidebar { @@ -146,7 +146,7 @@ table.attributes tr.routerpic th { font-weight: bold; /*background-color: red;*/ font-size: large; - vertical-align:bottom; + vertical-align: bottom; bottom: 0px; } diff --git a/tasks/build.js b/tasks/build.js index d76ccfc..2fd1e83 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -1,11 +1,11 @@ -module.exports = function(grunt) { +module.exports = function (grunt) { grunt.config.merge({ bowerdir: "bower_components", copy: { html: { options: { process: function (content) { - return content.replace("#revision#", grunt.option("gitRevision")) + return content.replace("#revision#", grunt.option("gitRevision")); } }, src: ["*.html"], @@ -26,31 +26,31 @@ module.exports = function(grunt) { 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/" @@ -83,26 +83,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: { @@ -116,11 +116,11 @@ module.exports = function(grunt) { } } } - }) + }); - grunt.loadNpmTasks("grunt-bower-install-simple") - grunt.loadNpmTasks("grunt-contrib-copy") - grunt.loadNpmTasks("grunt-contrib-requirejs") - grunt.loadNpmTasks("grunt-sass") - grunt.loadNpmTasks("grunt-postcss") -} + grunt.loadNpmTasks("grunt-bower-install-simple"); + grunt.loadNpmTasks("grunt-contrib-copy"); + grunt.loadNpmTasks("grunt-contrib-requirejs"); + grunt.loadNpmTasks("grunt-sass"); + grunt.loadNpmTasks("grunt-postcss"); +}; diff --git a/tasks/clean.js b/tasks/clean.js index 989ef08..ed0e234 100644 --- a/tasks/clean.js +++ b/tasks/clean.js @@ -3,7 +3,7 @@ module.exports = function (grunt) { clean: { build: ["build/**/*", "node_modules/grunt-newer/.cache"] } - }) + }); - grunt.loadNpmTasks("grunt-contrib-clean") -} + grunt.loadNpmTasks("grunt-contrib-clean"); +}; diff --git a/tasks/development.js b/tasks/development.js index 6c799d2..2c00cd7 100644 --- a/tasks/development.js +++ b/tasks/development.js @@ -24,8 +24,8 @@ module.exports = function (grunt) { tasks: [] } } - }) + }); - grunt.loadNpmTasks("grunt-contrib-connect") - grunt.loadNpmTasks("grunt-contrib-watch") -} + grunt.loadNpmTasks("grunt-contrib-connect"); + grunt.loadNpmTasks("grunt-contrib-watch"); +}; diff --git a/tasks/linting.js b/tasks/linting.js index a05bce0..71311dc 100644 --- a/tasks/linting.js +++ b/tasks/linting.js @@ -31,8 +31,8 @@ module.exports = function (grunt) { src: ["Gruntfile.js", "tasks/*.js"] } } - }) + }); - grunt.loadNpmTasks("grunt-check-dependencies") - grunt.loadNpmTasks("grunt-eslint") -} + grunt.loadNpmTasks("grunt-check-dependencies"); + grunt.loadNpmTasks("grunt-eslint"); +};