From 8b5bcbfede181689f5ed43110c0bf0616eed9ad5 Mon Sep 17 00:00:00 2001 From: H4ndl3 <beier.florian@gmail.com> Date: Mon, 13 Mar 2017 22:54:36 +0100 Subject: [PATCH 01/32] add loading animation Create _loader.scss Update main.scss Update _base.scss Update gui.js Update index.html removed trailing spaces removed trailing white spaces for real --- html/index.html | 10 ++++++++++ index.html | 10 ++++++++++ lib/gui.js | 3 +++ scss/_base.scss | 4 ++++ scss/_loader.scss | 23 +++++++++++++++++++++++ scss/main.scss | 1 + 6 files changed, 51 insertions(+) create mode 100644 scss/_loader.scss diff --git a/html/index.html b/html/index.html index fc4ff6e..6f5c4c7 100644 --- a/html/index.html +++ b/html/index.html @@ -14,5 +14,15 @@ </script> </head> <body> + <div class="loader"> + <p> + Lade<br /> + <span class="spinner"></span><br /> + Karte & Knoten... + </p> + <noscript> + <strong>JavaScript required</strong> + </noscript> + </div> </body> </html> diff --git a/index.html b/index.html index fcbf858..76dfc6e 100644 --- a/index.html +++ b/index.html @@ -13,5 +13,15 @@ <script src="bower_components/requirejs/require.js" data-main="app"></script> </head> <body> + <div class="loader"> + <p> + Lade<br/> + <span class="spinner"></span><br \> + Karte & Knoten... + </p> + <noscript> + <strong>JavaScript required</strong> + </noscript> + </div> </body> </html> diff --git a/lib/gui.js b/lib/gui.js index 4ce989d..4e14c50 100644 --- a/lib/gui.js +++ b/lib/gui.js @@ -48,6 +48,9 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist, } } + var loader = document.getElementsByClassName("loader")[0] + loader.classList.add("hide") + contentDiv = document.createElement("div") contentDiv.classList.add("content") document.body.appendChild(contentDiv) diff --git a/scss/_base.scss b/scss/_base.scss index 52f18c3..21ca3ff 100644 --- a/scss/_base.scss +++ b/scss/_base.scss @@ -28,3 +28,7 @@ h5 { h6 { font-size: 0.67em; } + +.hide { + display: none; +} diff --git a/scss/_loader.scss b/scss/_loader.scss new file mode 100644 index 0000000..d56c27e --- /dev/null +++ b/scss/_loader.scss @@ -0,0 +1,23 @@ +.loader { + color: #000000; + font-size: 1.8em; + line-height: 2; + margin: 30vh auto; + text-align: center; +} + +.spinner { + animation: .6s spinner ease-in-out infinite alternate; + border-bottom: 2px solid #000000; + border-radius: 50%; + display: inline-block; + height: 64px; + margin-top: 10px; + width: 64px; +} + +@keyframes spinner { + to { + transform: rotate(360deg); + } +} diff --git a/scss/main.scss b/scss/main.scss index 34fd594..47cbed9 100644 --- a/scss/main.scss +++ b/scss/main.scss @@ -4,6 +4,7 @@ @import '_leaflet'; @import '_leaflet.label'; @import '_filters'; +@import '_loader'; $minscreenwidth: 630pt; $sidebarwidth: 420pt; From eb4c7a04b099e0c3abf8c16b9e2cc0117981273f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20P=C3=A4ssler?= <me@petabyteboy.de> Date: Fri, 17 Mar 2017 03:14:57 +0100 Subject: [PATCH 02/32] change code style to ffrgb/meshviewer fork --- .editorconfig | 2 +- .gitignore | 2 + Gruntfile.js | 24 +- app.js | 6 +- build.js | 16 +- helper.js | 249 ++++---- html/index.html | 50 +- index.html | 48 +- lib/about.js | 56 +- lib/container.js | 23 +- lib/datadistributor.js | 149 ++--- lib/filters/filtergui.js | 57 +- lib/filters/genericnode.js | 62 +- lib/filters/nodefilter.js | 49 +- lib/forcegraph.js | 1072 ++++++++++++++++---------------- lib/gui.js | 200 +++--- lib/infobox/link.js | 77 +-- lib/infobox/location.js | 141 ++--- lib/infobox/main.js | 60 +- lib/infobox/node.js | 1190 +++++++++++++++++++----------------- lib/legend.js | 64 +- lib/linklist.js | 96 +-- lib/locationmarker.js | 40 +- lib/main.js | 411 +++++++------ lib/map.js | 716 ++++++++++++---------- lib/map/clientlayer.js | 89 +-- lib/map/labelslayer.js | 264 ++++---- lib/meshstats.js | 92 +-- lib/nodelist.js | 154 ++--- lib/proportions.js | 446 ++++++++------ lib/router.js | 258 ++++---- lib/sidebar.js | 59 +- lib/simplenodelist.js | 83 +-- lib/sorttable.js | 69 ++- lib/tabs.js | 70 ++- lib/title.js | 41 +- lib/vercomp.js | 78 +-- scss/main.scss | 6 +- tasks/build.js | 68 +-- tasks/clean.js | 6 +- tasks/development.js | 8 +- tasks/linting.js | 8 +- 42 files changed, 3505 insertions(+), 3154 deletions(-) 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 @@ <!DOCTYPE html> <html> - <head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, user-scalable=no"> - <link rel="stylesheet" href="css/ionicons.min.css"> - <link rel="stylesheet" href="roboto-slab-fontface.css"> - <link rel="stylesheet" href="roboto-fontface.css"> - <link rel="stylesheet" href="style.css"> - <script src="vendor/es6-shim/es6-shim.min.js"></script> - <script src="app.js"></script> - <script> - console.log("Version: #revision#") - </script> - </head> - <body> - <div class="loader"> - <p> - Lade<br /> - <span class="spinner"></span><br /> - Karte & Knoten... - </p> - <noscript> - <strong>JavaScript required</strong> - </noscript> - </div> - </body> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, user-scalable=no"> + <link rel="stylesheet" href="css/ionicons.min.css"> + <link rel="stylesheet" href="roboto-slab-fontface.css"> + <link rel="stylesheet" href="roboto-fontface.css"> + <link rel="stylesheet" href="style.css"> + <script src="vendor/es6-shim/es6-shim.min.js"></script> + <script src="app.js"></script> + <script> + console.log("Version: #revision#"); + </script> +</head> +<body> +<div class="loader"> + <p> + Lade<br/> + <span class="spinner"></span><br/> + Karte & Knoten... + </p> + <noscript> + <strong>JavaScript required</strong> + </noscript> +</div> +</body> </html> diff --git a/index.html b/index.html index 76dfc6e..96935bc 100644 --- a/index.html +++ b/index.html @@ -1,27 +1,27 @@ <!DOCTYPE html> <html> - <head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, user-scalable=no"> - <link rel="stylesheet" href="bower_components/roboto-slab-fontface/roboto-slab-fontface.css"> - <link rel="stylesheet" href="bower_components/roboto-fontface/roboto-fontface.css"> - <link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css"> - <link rel="stylesheet" href="bower_components/Leaflet.label/dist/leaflet.label.css"> - <link rel="stylesheet" href="bower_components/ionicons/css/ionicons.min.css"> - <link rel="stylesheet" href="style.css"> - <script src="bower_components/es6-shim/es6-shim.min.js"></script> - <script src="bower_components/requirejs/require.js" data-main="app"></script> - </head> - <body> - <div class="loader"> - <p> - Lade<br/> - <span class="spinner"></span><br \> - Karte & Knoten... - </p> - <noscript> - <strong>JavaScript required</strong> - </noscript> - </div> - </body> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, user-scalable=no"> + <link rel="stylesheet" href="bower_components/roboto-slab-fontface/roboto-slab-fontface.css"> + <link rel="stylesheet" href="bower_components/roboto-fontface/roboto-fontface.css"> + <link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css"> + <link rel="stylesheet" href="bower_components/Leaflet.label/dist/leaflet.label.css"> + <link rel="stylesheet" href="bower_components/ionicons/css/ionicons.min.css"> + <link rel="stylesheet" href="style.css"> + <script src="bower_components/es6-shim/es6-shim.min.js"></script> + <script src="bower_components/requirejs/require.js" data-main="app"></script> +</head> +<body> + <div class="loader"> + <p> + Lade<br/> + <span class="spinner"></span><br \> + Karte & Knoten... + </p> + <noscript> + <strong>JavaScript required</strong> + </noscript> + </div> +</body> </html> 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 = "<h2>Über HopGlass</h2>" + var el = document.createElement("div"); + d.appendChild(el); + var s = "<h2>Über HopGlass</h2>"; - s += "<p>Mit Doppelklick und Shift+Doppelklick kann man in der Karte " - s += "auch zoomen.</p>" + s += "<p>Mit Doppelklick und Shift+Doppelklick kann man in der Karte "; + s += "auch zoomen.</p>"; - s += "<h3>AGPL 3</h3>" + s += "<h3>AGPL 3</h3>"; - s += "<p>Copyright (C) Milan Pässler</p>" - s += "<p>Copyright (C) Nils Schneider</p>" + s += "<p>Copyright (C) Milan Pässler</p>"; + s += "<p>Copyright (C) Nils Schneider</p>"; - s += "<p>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.</p>" + s += "<p>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.</p>"; - s += "<p>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.</p>" + s += "<p>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.</p>"; - s += "<p>You should have received a copy of the GNU Affero General " - s += "Public License along with this program. If not, see " - s += "<a href=\"https://www.gnu.org/licenses/\">" - s += "https://www.gnu.org/licenses/</a>.</p>" + s += "<p>You should have received a copy of the GNU Affero General "; + s += "Public License along with this program. If not, see "; + s += "<a href=\"https://www.gnu.org/licenses/\">"; + s += "https://www.gnu.org/licenses/</a>.</p>"; - s += "<p>The source code is available at " - s += "<a href=\"https://github.com/plumpudding/hopglass\">" - s += "https://github.com/plumpudding/hopglass</a>." + s += "<p>The source code is available at "; + s += "<a href=\"https://github.com/plumpudding/hopglass\">"; + s += "https://github.com/plumpudding/hopglass</a>."; - 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 + "<br><strong>" + showDistance(d) + " / " + showTq(d) + "</strong>") - line.on("click", router.link(d)) + line.bindLabel(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + "<br><strong>" + showDistance(d) + " / " + showTq(d) + "</strong>"); + 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"); +}; From 9afb214360a3adfd6e3331089cdbbc907ceffb32 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Sun, 22 May 2016 15:26:16 +0200 Subject: [PATCH 03/32] [TASK] Use own iconfont set Reduce overhead in initial load --- assets/icons/fonts/hopglass.eot | Bin 0 -> 3352 bytes assets/icons/fonts/hopglass.svg | 27 +++++++++ assets/icons/fonts/hopglass.ttf | Bin 0 -> 3184 bytes assets/icons/fonts/hopglass.woff | Bin 0 -> 2300 bytes assets/icons/fonts/hopglass.woff2 | Bin 0 -> 1700 bytes assets/icons/hopglass-icons.css | 92 ++++++++++++++++++++++++++++++ bower.json | 1 - html/index.html | 2 +- index.html | 2 +- lib/map.js | 2 +- tasks/build.js | 4 +- 11 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 assets/icons/fonts/hopglass.eot create mode 100644 assets/icons/fonts/hopglass.svg create mode 100644 assets/icons/fonts/hopglass.ttf create mode 100644 assets/icons/fonts/hopglass.woff create mode 100644 assets/icons/fonts/hopglass.woff2 create mode 100644 assets/icons/hopglass-icons.css diff --git a/assets/icons/fonts/hopglass.eot b/assets/icons/fonts/hopglass.eot new file mode 100644 index 0000000000000000000000000000000000000000..c98534acce803009679a9b96251e6a6796287354 GIT binary patch literal 3352 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 B599y< literal 0 HcmV?d00001 diff --git a/assets/icons/fonts/hopglass.svg b/assets/icons/fonts/hopglass.svg new file mode 100644 index 0000000..da13e93 --- /dev/null +++ b/assets/icons/fonts/hopglass.svg @@ -0,0 +1,27 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata>Generated by Fontastic.me</metadata> +<defs> +<font id="hopglass" horiz-adv-x="512"> +<font-face font-family="hopglass" units-per-em="512" ascent="480" descent="-32"/> +<missing-glyph horiz-adv-x="512" /> + +<glyph glyph-name="android-add" unicode="" d="M416 235l-139 0 0-139-42 0 0 139-139 0 0 42 139 0 0 139 42 0 0-139 139 0z"/> +<glyph glyph-name="chevron-left" unicode="" d="M214 256l167 174c4 4 4 11 0 16l-30 30c-4 5-11 5-16 1l-204-213c-2-2-3-5-3-8 0-3 1-6 3-8l204-213c5-4 12-4 16 0l30 31c4 4 4 11 0 16z"/> +<glyph glyph-name="chevron-right" unicode="" d="M298 256l-167 174c-4 4-4 11 0 16l30 30c5 5 12 5 16 1l204-213c2-2 3-5 3-8 0-3-1-6-3-8l-204-213c-4-4-11-4-16 0l-30 31c-4 4-4 11 0 16z"/> +<glyph glyph-name="pin" unicode="" d="M332 284c-1 0-3 1-4 2-7 4-12 11-14 19l-18 118 0 5c0 7 4 11 10 13 0 1 1 1 2 1 7 4 12 8 12 17 0 20-7 21-18 21l-92 0c-11 0-18-1-18-21 0-9 5-13 12-17 1 0 2 0 2-1 6-3 10-6 10-13l0-5-18-118c-2-8-7-15-14-19-1-1-2-2-4-2-19-10-36-31-36-55 0-16 4-21 15-21l81 0 12-176 8 0 12 176 81 0c12 0 15 5 15 21 0 24-16 45-36 55z"/> +<glyph glyph-name="wifi" unicode="" d="M256 416c-80 0-156-30-214-84l-10-10 10-9 32-32 10-10 9 9c45 41 102 64 163 64 60 0 118-23 163-64l9-9 10 10 32 32 10 9-10 10c-58 54-134 84-214 84z m141-160c-38 35-88 54-141 54l-9 0 0 0c-49-2-96-21-132-54l-11-10 10-10 33-32 9-9 10 8c25 22 57 35 90 35 33 0 65-13 91-35l9-8 9 9 33 32 10 10z m-141-160l10 9 53 53 10 10-11 10c-16 11-33 20-62 20-29 0-45-10-61-20l-12-10 11-10 53-53z"/> +<glyph glyph-name="eye" unicode="" d="M256 384c-82 0-146-49-224-128 67-68 124-128 224-128 100 0 173 76 224 127-52 58-125 129-224 129z m0-219c-49 0-90 41-90 91 0 50 41 91 90 91 49 0 90-41 90-91 0-50-41-91-90-91z m0 123c0 8 3 15 8 21-3 0-5 0-8 0-29 0-52-24-52-53 0-29 23-53 52-53 29 0 52 24 52 53 0 2 0 5 0 7-6-4-12-7-20-7-18 0-32 14-32 32z"/> +<glyph glyph-name="ios-arrow-thin-left" unicode="" d="M190 162c3 3 3 8 0 12l-67 74 285 0c4 0 8 4 8 8 0 4-4 8-8 8l-285 0 67 74c3 4 3 8 0 12-3 3-9 3-12 0 0 0-79-87-80-88 0-1-2-3-2-6 0-3 2-5 2-6 1-1 80-88 80-88 2-1 4-2 6-2 2 0 4 1 6 2z"/> +<glyph glyph-name="ios-arrow-thin-right" unicode="" d="M322 162c-3 3-3 8 0 12l67 74-285 0c-4 0-8 4-8 8 0 4 4 8 8 8l285 0-67 74c-3 4-3 8 0 12 3 3 9 3 12 0 0 0 79-87 80-88 0-1 2-3 2-6 0-3-2-5-2-6-1-1-80-88-80-88-2-1-4-2-6-2-2 0-4 1-6 2z"/> +<glyph glyph-name="arrow-up-b" unicode="" d="M413 185l-2 2-136 156c-4 6-11 9-19 9-8 0-14-4-19-9l-136-156-2-3c-2-2-3-5-3-8 0-9 7-16 17-16l286 0c10 0 17 7 17 16 0 3-1 6-3 9z"/> +<glyph glyph-name="arrow-down-b" unicode="" d="M99 327l2-2 136-156c4-6 11-9 19-9 8 0 15 3 19 9l136 156 2 2c2 3 3 6 3 9 0 9-7 16-17 16l-286 0c-10 0-17-7-17-16 0-3 1-6 3-9z"/> +<glyph glyph-name="android-locate" unicode="" d="M256 336c-44 0-80-36-80-80 0-44 36-80 80-80 44 0 80 36 80 80 0 44-36 80-80 80z m191-59c-10 89-81 160-170 170l0 33-42 0 0-33c-89-10-160-81-170-170l-33 0 0-42 33 0c10-89 81-160 170-170l0-33 42 0 0 33c89 10 160 81 170 170l33 0 0 42z m-191-170c-82 0-149 67-149 149 0 82 67 149 149 149 82 0 149-67 149-149 0-82-67-149-149-149z"/> +<glyph glyph-name="android-close" unicode="" d="M405 375l-30 30-119-119-119 119-30-30 119-119-119-119 30-30 119 119 119-119 30 30-119 119z"/> +<glyph glyph-name="android-lock" unicode="" d="M376 326l-20 0 0 40c0 55-45 100-100 100-55 0-100-45-100-100l0-40-20 0c-22 0-40-18-40-40l0-200c0-22 18-40 40-40l240 0c22 0 40 18 40 40l0 200c0 22-18 40-40 40z m-120-182c-22 0-40 18-40 40 0 22 18 40 40 40 22 0 40-18 40-40 0-22-18-40-40-40z m62 182l-124 0 0 40c0 34 28 62 62 62 34 0 62-28 62-62z"/> +<glyph glyph-name="ios-copy" unicode="" d="M144 96l0 304-32 0 0-336 240 0 0 32z m181 352l-165 0 0-336 240 0 0 261z m43-112l-80 0 0 80 16 0 0-64 64 0z"/> +<glyph glyph-name="location" unicode="" d="M256 448c-66 0-119-54-119-120 0-115 119-264 119-264 0 0 119 149 119 264 0 66-53 120-119 120z m0-178c-31 0-56 25-56 56 0 32 25 57 56 57 31 0 56-25 56-57 0-31-25-56-56-56z"/> +<glyph glyph-name="android-remove" unicode="" d="M96 277l320 0 0-42-320 0z"/> +<glyph glyph-name="ios-person" unicode="" d="M363 148c-13 5-31 6-43 9-7 1-17 5-20 9-3 4-1 41-1 41 0 0 6 10 9 18 4 8 7 31 7 31 0 0 7 0 9 12 3 13 7 19 6 28 0 9-5 10-5 10 0 0 5 13 5 42 1 34-25 68-74 68-49 0-75-34-74-68 0-28 5-42 5-42 0 0-5-1-5-10-1-9 3-14 6-27 2-12 9-12 9-12 0 0 4-24 7-32 3-8 9-18 9-18 0 0 2-37-1-41-3-4-13-8-20-9-12-3-30-4-43-9-13-5-53-20-53-52l320 0c0 32-40 47-53 52z"/> +</font></defs></svg> diff --git a/assets/icons/fonts/hopglass.ttf b/assets/icons/fonts/hopglass.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d117d0cd3e44fa916e7e29fbe10a9d730601881a GIT binary patch literal 3184 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 literal 0 HcmV?d00001 diff --git a/assets/icons/fonts/hopglass.woff b/assets/icons/fonts/hopglass.woff new file mode 100644 index 0000000000000000000000000000000000000000..b1a185a83939c6c289fd9d803986ba16ec3af579 GIT binary patch literal 2300 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 literal 0 HcmV?d00001 diff --git a/assets/icons/fonts/hopglass.woff2 b/assets/icons/fonts/hopglass.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..cce227441683b22d72d300daabbcad81e633863a GIT binary patch literal 1700 zcmV;V23z@ePew8T0RR9100yK04FCWD01S8l00vC}0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/assets/icons/hopglass-icons.css b/assets/icons/hopglass-icons.css new file mode 100644 index 0000000..f3ab178 --- /dev/null +++ b/assets/icons/hopglass-icons.css @@ -0,0 +1,92 @@ +@font-face { + font-family: "Ionicons"; + src: url("fonts/hopglass.woff2") format("woff2"), + url("fonts/hopglass.woff") format("woff"), + url("fonts/hopglass.ttf") format("truetype"); + font-weight: normal; + font-style: normal; + +} + +[class^="ion-"]:before, +[class*=" ion-"]:before, .ion-inside { + display: inline-block; + font-family: "ionicons" !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + text-rendering: auto; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.ion-android-add:before { + content: "\f2c7"; +} + +.ion-chevron-left:before { + content: "\f124"; +} + +.ion-chevron-right:before { + content: "\f125"; +} + +.ion-pin:before { + content: "\f3a3"; +} + +.ion-wifi:before { + content: "\f25c"; +} + +.ion-eye:before { + content: "\f133"; +} + +.ion-ios-arrow-thin-left:before { + content: "\f3d5"; +} + +.ion-ios-arrow-thin-right:before { + content: "\f3d6"; +} + +.ion-arrow-up-b:before { + content: "\f10d"; +} + +.ion-arrow-down-b:before { + content: "\f104"; +} + +.ion-android-locate:before { + content: "\f2e9"; +} + +.ion-android-close:before { + content: "\f2d7"; +} + +.ion-android-lock:before { + content: "\f392"; +} + +.ion-ios-copy:before { + content: "\f41c"; +} + +.ion-location:before { + content: "\f456"; +} + +.ion-android-remove:before { + content: "\f2f4"; +} + +.ion-ios-person:before { + content: "\f47e"; +} diff --git a/bower.json b/bower.json index be1795a..e8d473a 100644 --- a/bower.json +++ b/bower.json @@ -11,7 +11,6 @@ "Leaflet.label": "~0.2.1", "chroma-js": "~0.6.1", "leaflet": "~0.7.3", - "ionicons": "~2.0.1", "moment": "~2.9.0", "requirejs": "~2.1.16", "tablesort": "https://github.com/tristen/tablesort.git#v3.0.2", diff --git a/html/index.html b/html/index.html index cc95aa5..382ec6f 100644 --- a/html/index.html +++ b/html/index.html @@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> - <link rel="stylesheet" href="css/ionicons.min.css"> + <link rel="stylesheet" href="hopglass-icons.css"> <link rel="stylesheet" href="roboto-slab-fontface.css"> <link rel="stylesheet" href="roboto-fontface.css"> <link rel="stylesheet" href="style.css"> diff --git a/index.html b/index.html index 96935bc..cc6f536 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ <link rel="stylesheet" href="bower_components/roboto-fontface/roboto-fontface.css"> <link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css"> <link rel="stylesheet" href="bower_components/Leaflet.label/dist/leaflet.label.css"> - <link rel="stylesheet" href="bower_components/ionicons/css/ionicons.min.css"> + <link rel="stylesheet" href="assets/hopglass-icons.css"> <link rel="stylesheet" href="style.css"> <script src="bower_components/es6-shim/es6-shim.min.js"></script> <script src="bower_components/requirejs/require.js" data-main="app"></script> diff --git a/lib/map.js b/lib/map.js index ea44443..ff030a0 100644 --- a/lib/map.js +++ b/lib/map.js @@ -86,7 +86,7 @@ define(["map/clientlayer", "map/labelslayer", onAdd: function () { var button = L.DomUtil.create("button", "coord-picker"); - button.textContent = "\uF2A6"; + button.textContent = "\uF3A3"; // Click propagation isn't disabled as this causes problems with the // location picking mode; instead propagation is stopped in onClick(). diff --git a/tasks/build.js b/tasks/build.js index 2fd1e83..b360a60 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -43,11 +43,11 @@ module.exports = function (grunt) { }, ionicons: { src: ["fonts/*", - "css/ionicons.min.css" + "hopglass-icons.css" ], expand: true, dest: "build/", - cwd: "bower_components/ionicons/" + cwd: "assets/icons/" }, leafletImages: { src: ["images/*"], From 0a22ed5e6f2af0d2ea8d2350737827506d3d897c Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Sun, 22 May 2016 15:40:13 +0200 Subject: [PATCH 04/32] [TASK] Only include additional german locale English is default --- app.js | 4 +++- lib/infobox/node.js | 2 +- lib/main.js | 2 +- lib/map.js | 2 +- lib/simplenodelist.js | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index ef131ef..22d04c7 100644 --- a/app.js +++ b/app.js @@ -5,7 +5,8 @@ require.config({ "leaflet.label": "../bower_components/Leaflet.label/dist/leaflet.label", "leaflet.providers": "../bower_components/leaflet-providers/leaflet-providers", "chroma-js": "../bower_components/chroma-js/chroma.min", - "moment": "../bower_components/moment/min/moment-with-locales.min", + "moment": "../bower_components/moment/min/moment.min", + "moment.de": "../bower_components/moment/locale/de", "tablesort": "../bower_components/tablesort/tablesort.min", "tablesort.numeric": "../bower_components/tablesort/src/sorts/tablesort.numeric", "d3": "../bower_components/d3/d3.min", @@ -19,6 +20,7 @@ require.config({ shim: { "leaflet.label": ["leaflet"], "leaflet.providers": ["leaflet"], + "moment.de": ["moment"], "tablesort": { exports: "Tablesort" }, diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 3654c3d..21e0167 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -1,4 +1,4 @@ -define(["moment", "numeral", "tablesort", "tablesort.numeric"], +define(["moment", "numeral", "tablesort", "tablesort.numeric", "moment.de"], function (moment, numeral, Tablesort) { function showGeoURI(d) { function showLatitude(d) { diff --git a/lib/main.js b/lib/main.js index 8305891..bfb3637 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,4 +1,4 @@ -define(["moment", "router", "leaflet", "gui", "numeral"], +define(["moment", "router", "leaflet", "gui", "numeral", "moment.de"], function (moment, Router, L, GUI, numeral) { return function (config) { function handleData(data) { diff --git a/lib/map.js b/lib/map.js index ff030a0..57b28b0 100644 --- a/lib/map.js +++ b/lib/map.js @@ -1,6 +1,6 @@ define(["map/clientlayer", "map/labelslayer", "d3", "leaflet", "moment", "locationmarker", "rbush", - "leaflet.label", "leaflet.providers"], + "leaflet.label", "leaflet.providers", "moment.de"], function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush) { var options = { worldCopyJump: true, diff --git a/lib/simplenodelist.js b/lib/simplenodelist.js index a327203..bac6c25 100644 --- a/lib/simplenodelist.js +++ b/lib/simplenodelist.js @@ -1,4 +1,4 @@ -define(["moment", "virtual-dom"], function (moment, V) { +define(["moment", "virtual-dom", "moment.de"], function (moment, V) { return function (nodes, field, router, title) { var self = this; var el, tbody; From f7f5744a7832b116fd1987ca6ef0b0e6a43219e6 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Sun, 22 May 2016 15:58:09 +0200 Subject: [PATCH 05/32] [TASK] Update bower components --- Gruntfile.js | 18 +----------------- app.js | 4 ++-- bower.json | 23 +++++++++++------------ lib/gui.js | 2 +- lib/infobox/node.js | 2 +- package.json | 29 ++++++++++++++--------------- tasks/build.js | 5 ----- 7 files changed, 30 insertions(+), 53 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 6fcc054..1e79651 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,23 +1,7 @@ module.exports = function (grunt) { - grunt.loadNpmTasks("grunt-git-describe"); - - grunt.initConfig({ - "git-describe": { - options: {}, - default: {} - } - }); - - grunt.registerTask("saveRevision", function () { - grunt.event.once("git-describe", function (rev) { - grunt.option("gitRevision", rev); - }); - grunt.task.run("git-describe"); - }); - grunt.loadTasks("tasks"); - grunt.registerTask("default", ["bower-install-simple", "lint", "saveRevision", "copy", "sass", "postcss", "requirejs"]); + grunt.registerTask("default", ["bower-install-simple", "lint", "copy", "sass", "postcss", "requirejs"]); grunt.registerTask("lint", ["eslint"]); grunt.registerTask("dev", ["default", "connect:server", "watch"]); }; diff --git a/app.js b/app.js index 22d04c7..8ae6bb1 100644 --- a/app.js +++ b/app.js @@ -8,7 +8,7 @@ require.config({ "moment": "../bower_components/moment/min/moment.min", "moment.de": "../bower_components/moment/locale/de", "tablesort": "../bower_components/tablesort/tablesort.min", - "tablesort.numeric": "../bower_components/tablesort/src/sorts/tablesort.numeric", + "tablesort.number": "../bower_components/tablesort/src/sorts/tablesort.number", "d3": "../bower_components/d3/d3.min", "numeral": "../bower_components/numeraljs/min/numeral.min", "numeral-intl": "../bower_components/numeraljs/min/languages.min", @@ -28,7 +28,7 @@ require.config({ deps: ["numeral"], exports: "numeral" }, - "tablesort.numeric": ["tablesort"], + "tablesort.number": ["tablesort"], "helper": ["numeral-intl"] } }); diff --git a/bower.json b/bower.json index e8d473a..e63962c 100644 --- a/bower.json +++ b/bower.json @@ -9,21 +9,20 @@ ], "dependencies": { "Leaflet.label": "~0.2.1", - "chroma-js": "~0.6.1", - "leaflet": "~0.7.3", - "moment": "~2.9.0", - "requirejs": "~2.1.16", - "tablesort": "https://github.com/tristen/tablesort.git#v3.0.2", + "chroma-js": "~1.1.1", + "leaflet": "~0.7.7", + "moment": "~2.13.0", + "requirejs": "~2.2.0", + "tablesort": "https://github.com/tristen/tablesort.git#v4.0.1", "roboto-slab-fontface": "*", - "es6-shim": "~0.27.1", - "almond": "~0.3.1", - "r.js": "~2.1.16", - "d3": "~3.5.5", + "es6-shim": "~0.35.1", + "almond": "~0.3.2", + "d3": "~3.5.17", "numeraljs": "~1.5.3", "roboto-fontface": "~0.3.0", - "virtual-dom": "~2.0.1", - "leaflet-providers": "~1.0.27", - "rbush": "https://github.com/mourner/rbush.git#~1.3.5", + "virtual-dom": "~2.1.1", + "leaflet-providers": "~1.1.10", + "rbush": "https://github.com/mourner/rbush.git#~1.4.3", "jshashes": "~1.0.5" }, "authors": [ diff --git a/lib/gui.js b/lib/gui.js index f757e29..f8427ed 100644 --- a/lib/gui.js +++ b/lib/gui.js @@ -10,7 +10,7 @@ define(["chroma-js", "map", "sidebar", "tabs", "container", "meshstats", var content; var contentDiv; - var linkScale = chroma.scale(chroma.interpolate.bezier(["#04C714", "#FF5500", "#F02311"])).domain([1, 5]); + var linkScale = chroma.scale(chroma.bezier(["#04C714", "#FF5500", "#F02311"])).domain([1, 5]); var sidebar; var buttons = document.createElement("div"); diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 21e0167..3710272 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -1,4 +1,4 @@ -define(["moment", "numeral", "tablesort", "tablesort.numeric", "moment.de"], +define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], function (moment, numeral, Tablesort) { function showGeoURI(d) { function showLatitude(d) { diff --git a/package.json b/package.json index d5cbd37..98738ec 100644 --- a/package.json +++ b/package.json @@ -4,21 +4,20 @@ "test": "node -e \"require('grunt').cli()\" '' clean lint" }, "devDependencies": { - "autoprefixer": "^6.3.3", - "grunt": "^0.4.5", - "grunt-check-dependencies": "^0.6.0", - "grunt-contrib-clean": "^0.6.0", - "grunt-contrib-connect": "^0.8.0", - "grunt-contrib-copy": "^0.5.0", - "grunt-contrib-cssmin": "^0.12.2", - "grunt-contrib-requirejs": "^0.4.4", - "grunt-sass": "^1.1.0", - "grunt-postcss": "^0.7.2", - "grunt-contrib-uglify": "^0.5.1", - "grunt-contrib-watch": "^0.6.1", - "grunt-eslint": "^10.0.0", - "grunt-bower-install-simple": "^1.1.2", - "grunt-git-describe": "^2.3.2" + "autoprefixer": "^6.3.6", + "grunt": "^1.0.1", + "grunt-bower-install-simple": "^1.2.1", + "grunt-check-dependencies": "^0.12.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-connect": "^1.0.2", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-cssmin": "^1.0.1", + "grunt-contrib-requirejs": "^1.0.0", + "grunt-contrib-uglify": "^1.0.1", + "grunt-contrib-watch": "^1.0.0", + "grunt-eslint": "^18.1.0", + "grunt-postcss": "^0.8.0", + "grunt-sass": "^1.2.0" }, "eslintConfig": { "env": { diff --git a/tasks/build.js b/tasks/build.js index b360a60..388904b 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -3,11 +3,6 @@ module.exports = function (grunt) { bowerdir: "bower_components", copy: { html: { - options: { - process: function (content) { - return content.replace("#revision#", grunt.option("gitRevision")); - } - }, src: ["*.html"], expand: true, cwd: "html/", From 30b6f84b6d4246d9bb103173f24b6a25cf9e3e2a Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Sun, 22 May 2016 18:51:42 +0200 Subject: [PATCH 06/32] [BUGFIX] Correct css path to icon font --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index cc6f536..eb6c970 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ <link rel="stylesheet" href="bower_components/roboto-fontface/roboto-fontface.css"> <link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css"> <link rel="stylesheet" href="bower_components/Leaflet.label/dist/leaflet.label.css"> - <link rel="stylesheet" href="assets/hopglass-icons.css"> + <link rel="stylesheet" href="assets/icons/hopglass-icons.css"> <link rel="stylesheet" href="style.css"> <script src="bower_components/es6-shim/es6-shim.min.js"></script> <script src="bower_components/requirejs/require.js" data-main="app"></script> From 44571c51f03d4ce8f75ed45e973ac38e5694926f Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Sun, 22 May 2016 22:13:46 +0200 Subject: [PATCH 07/32] [TASK] Inline style and icon css and es6-shim --- Gruntfile.js | 2 +- html/index.html | 6 +++--- package.json | 1 + tasks/build.js | 11 +++++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 1e79651..2c15b33 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,7 +1,7 @@ module.exports = function (grunt) { grunt.loadTasks("tasks"); - grunt.registerTask("default", ["bower-install-simple", "lint", "copy", "sass", "postcss", "requirejs"]); + grunt.registerTask("default", ["bower-install-simple", "lint", "copy", "sass", "postcss", "requirejs", "inline"]); grunt.registerTask("lint", ["eslint"]); grunt.registerTask("dev", ["default", "connect:server", "watch"]); }; diff --git a/html/index.html b/html/index.html index 382ec6f..ff5fa76 100644 --- a/html/index.html +++ b/html/index.html @@ -3,11 +3,11 @@ <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> - <link rel="stylesheet" href="hopglass-icons.css"> + <link rel="stylesheet" href="hopglass-icons.css?__inline=true"> <link rel="stylesheet" href="roboto-slab-fontface.css"> <link rel="stylesheet" href="roboto-fontface.css"> - <link rel="stylesheet" href="style.css"> - <script src="vendor/es6-shim/es6-shim.min.js"></script> + <link rel="stylesheet" href="style.css?__inline=true"> + <script src="vendor/es6-shim/es6-shim.min.js?__inline=true"></script> <script src="app.js"></script> <script> console.log("Version: #revision#"); diff --git a/package.json b/package.json index 98738ec..2ffe259 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "grunt-contrib-uglify": "^1.0.1", "grunt-contrib-watch": "^1.0.0", "grunt-eslint": "^18.1.0", + "grunt-inline": "^0.3.6", "grunt-postcss": "^0.8.0", "grunt-sass": "^1.2.0" }, diff --git a/tasks/build.js b/tasks/build.js index 388904b..54cad5a 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -85,6 +85,16 @@ module.exports = function (grunt) { } } }, + inline: { + dist: { + options: { + cssmin: true, + uglify: true + }, + src: "build/index.html", + dest: "build/index.html" + } + }, "bower-install-simple": { options: { directory: "<%=bowerdir%>", @@ -118,4 +128,5 @@ module.exports = function (grunt) { grunt.loadNpmTasks("grunt-contrib-requirejs"); grunt.loadNpmTasks("grunt-sass"); grunt.loadNpmTasks("grunt-postcss"); + grunt.loadNpmTasks("grunt-inline"); }; From 8bb0da9d878032faae78fa3e17f87fa92785db63 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Sun, 22 May 2016 23:32:05 +0200 Subject: [PATCH 08/32] [TASK] CGL - Optimize returns and unnecessary math --- lib/forcegraph.js | 8 +++----- lib/map.js | 4 +--- lib/map/labelslayer.js | 4 +--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/forcegraph.js b/lib/forcegraph.js index b9a998c..d1a24d3 100644 --- a/lib/forcegraph.js +++ b/lib/forcegraph.js @@ -273,8 +273,7 @@ define(["d3"], function (d3) { function drawNode(color, radius, scale, r) { var node = document.createElement("canvas"); - node.width = scale * radius * 8 * r; - node.height = node.width; + node.height = node.width = scale * radius * 8 * r; var nctx = node.getContext("2d"); nctx.scale(scale * r, scale * r); @@ -288,12 +287,12 @@ define(["d3"], function (d3) { nctx.arc(0, 0, radius, 0, 2 * Math.PI); nctx.strokeStyle = "rgba(255, 0, 0, 1)"; - nctx.shadowOffsetX = node.width * 1.5 + 0; + nctx.shadowOffsetX = node.width * 1.5; 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.shadowOffsetX = node.width * 1.5; nctx.shadowOffsetY = node.height * 1.5 + 3; nctx.shadowBlur = 12; nctx.shadowColor = "rgba(0, 0, 0, 0.23)"; @@ -562,7 +561,6 @@ define(["d3"], function (d3) { if (links.length > 0) { router.link(links[0].o)(); - } } diff --git a/lib/map.js b/lib/map.js index 57b28b0..c54cf55 100644 --- a/lib/map.js +++ b/lib/map.js @@ -135,7 +135,7 @@ define(["map/clientlayer", "map/labelslayer", return "distance" in d && d.type !== "VPN"; }); - var lines = graph.map(function (d) { + return graph.map(function (d) { var opts = { color: d.type === "Kabel" ? "#50B0F0" : linkScale(d.tq).hex(), weight: 4, @@ -156,8 +156,6 @@ define(["map/clientlayer", "map/labelslayer", return line; }); - - return lines; } var iconOnline = { diff --git a/lib/map/labelslayer.js b/lib/map/labelslayer.js index e616877..a6af979 100644 --- a/lib/map/labelslayer.js +++ b/lib/map/labelslayer.js @@ -73,7 +73,7 @@ define(["leaflet", "rbush"], return [x, y, x + width, y + height]; } - var c = L.TileLayer.Canvas.extend({ + return L.TileLayer.Canvas.extend({ onAdd: function (map) { L.TileLayer.Canvas.prototype.onAdd.call(this, map); if (this.data) { @@ -235,6 +235,4 @@ define(["leaflet", "rbush"], }).forEach(drawLabel); } }); - - return c; }); From 88f43bc57cecdca6a7a5fa1a14699fe6413f695d Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Sun, 22 May 2016 23:39:19 +0200 Subject: [PATCH 09/32] [TASK] Add index option to grunt-conncet server --- tasks/development.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tasks/development.js b/tasks/development.js index 2c00cd7..3ae384e 100644 --- a/tasks/development.js +++ b/tasks/development.js @@ -3,7 +3,12 @@ module.exports = function (grunt) { connect: { server: { options: { - base: "build/", //TODO: once grunt-contrib-connect 0.9 is released, set index file + base: { + path: 'build', + options: { + index: 'index.html' + } + }, livereload: true } } From 231bfcedd7ef065b5ef83d08d67877e8b6787eb9 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Mon, 23 May 2016 00:13:21 +0200 Subject: [PATCH 10/32] [TASK] Seperate dev and default for requirejs --- Gruntfile.js | 5 +++-- README.md | 4 ++++ tasks/build.js | 18 ++++++++++++++---- tasks/development.js | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 2c15b33..99fcd04 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,8 +1,9 @@ module.exports = function (grunt) { grunt.loadTasks("tasks"); - grunt.registerTask("default", ["bower-install-simple", "lint", "copy", "sass", "postcss", "requirejs", "inline"]); + grunt.registerTask("default", ["bower-install-simple", "lint", "copy", "sass", "postcss", "requirejs:default", "inline"]); grunt.registerTask("lint", ["eslint"]); - grunt.registerTask("dev", ["default", "connect:server", "watch"]); + grunt.registerTask("dev", ["bower-install-simple", "lint", "copy", "sass", "requirejs:dev"]); + grunt.registerTask("serve", ["dev", "connect:server", "watch"]); }; diff --git a/README.md b/README.md index ae7bba7..4a15dbc 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ Just run the following command from the hopglass directory: This will generate `build/` containing all required files. +## Development + +Use `grunt serve` for development. + # Configure Copy `config.json.example` to `build/config.json` and change it to match your community. diff --git a/tasks/build.js b/tasks/build.js index 54cad5a..9c62838 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -109,15 +109,25 @@ module.exports = function (grunt) { } }, requirejs: { - compile: { + default: { options: { baseUrl: "lib", name: "../bower_components/almond/almond", mainConfigFile: "app.js", include: "../app", - wrap: true, - optimize: "uglify", - out: "build/app.js" + out: "build/app.js", + build: false + } + }, + dev: { + options: { + baseUrl: "lib", + name: "../bower_components/almond/almond", + mainConfigFile: "app.js", + include: "../app", + optimize: "none", + out: "build/app.js", + build: false } } } diff --git a/tasks/development.js b/tasks/development.js index 3ae384e..02826b0 100644 --- a/tasks/development.js +++ b/tasks/development.js @@ -19,7 +19,7 @@ module.exports = function (grunt) { livereload: true }, files: ["*.css", "app.js", "lib/**/*.js", "*.html"], - tasks: ["default"] + tasks: ["dev"] }, config: { options: { From 7f3a86596ade6a3002054d983e680528d2adeb6d Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Mon, 23 May 2016 00:48:03 +0200 Subject: [PATCH 11/32] [TASK] Replace all specialchars in NODE_NAME in stat urls --- lib/infobox/node.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 3710272..68137fc 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -469,7 +469,7 @@ define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], 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"; + subst["{NODE_NAME}"] = d.nodeinfo.hostname ? d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_') : "unknown"; return showStat(o, subst); } From b510dda00613daf7107365e5338e973c5767eb57 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xmaierhofer@1drop.de> Date: Mon, 23 May 2016 09:30:19 +0200 Subject: [PATCH 12/32] [BUGFIX] Enable build for default --- tasks/build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/build.js b/tasks/build.js index 9c62838..3a70fe8 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -116,7 +116,7 @@ module.exports = function (grunt) { mainConfigFile: "app.js", include: "../app", out: "build/app.js", - build: false + build: true } }, dev: { From d367bf58bff711055ef99308062dc1c5fef51399 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Tue, 24 May 2016 00:22:34 +0200 Subject: [PATCH 13/32] [TASK] Unicode instead of utf8 icons --- lib/infobox/link.js | 3 ++- lib/infobox/node.js | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/infobox/link.js b/lib/infobox/link.js index 2d5e8c0..260931f 100644 --- a/lib/infobox/link.js +++ b/lib/infobox/link.js @@ -18,7 +18,8 @@ define(function () { } a1.textContent = unknown ? d.source.id : d.source.node.nodeinfo.hostname; h2.appendChild(a1); - h2.appendChild(document.createTextNode(" → ")); + h2.appendChild(document.createTextNode(" \uF3D6 ")); + h2.className = 'ion-inside'; var a2 = document.createElement("a"); a2.href = "#"; a2.onclick = router.node(d.target.node); diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 68137fc..eccc466 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -156,12 +156,12 @@ define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], var span = document.createElement("span"); span.classList.add("clients"); - span.textContent = " ".repeat(d.statistics.clients); + span.textContent = "\uF47E ".repeat(d.statistics.clients); el.appendChild(span); var spanmesh = document.createElement("span"); spanmesh.classList.add("clientsMesh"); - spanmesh.textContent = " ".repeat(meshclients - d.statistics.clients); + spanmesh.textContent = "\uF47E ".repeat(meshclients - d.statistics.clients); el.appendChild(spanmesh); }; } @@ -577,7 +577,8 @@ define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], var tr = document.createElement("tr"); var td1 = document.createElement("td"); - td1.appendChild(document.createTextNode(d.incoming ? " ← " : " → ")); + td1.className = 'ion-inside'; + td1.appendChild(document.createTextNode(d.incoming ? " \uF3D5 " : " \uF3D6 ")); tr.appendChild(td1); var td2 = document.createElement("td"); From 136157eb37b3f885045d5b8c05e51408214636cb Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Tue, 24 May 2016 00:23:35 +0200 Subject: [PATCH 14/32] [TASK] Only add german language from numeral.js --- app.js | 6 +++--- lib/proportions.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 8ae6bb1..f08f023 100644 --- a/app.js +++ b/app.js @@ -11,7 +11,7 @@ require.config({ "tablesort.number": "../bower_components/tablesort/src/sorts/tablesort.number", "d3": "../bower_components/d3/d3.min", "numeral": "../bower_components/numeraljs/min/numeral.min", - "numeral-intl": "../bower_components/numeraljs/min/languages.min", + "numeral.de": "../bower_components/numeraljs/min/languages/de.min", "virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom", "rbush": "../bower_components/rbush/rbush", "helper": "../helper", @@ -24,12 +24,12 @@ require.config({ "tablesort": { exports: "Tablesort" }, - "numeral-intl": { + "numeral.de": { deps: ["numeral"], exports: "numeral" }, "tablesort.number": ["tablesort"], - "helper": ["numeral-intl"] + "helper": ["numeral.de"] } }); diff --git a/lib/proportions.js b/lib/proportions.js index 8523165..9d9b2e7 100644 --- a/lib/proportions.js +++ b/lib/proportions.js @@ -1,4 +1,4 @@ -define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "vercomp"], +define(["chroma-js", "virtual-dom", "numeral.de", "filters/genericnode", "vercomp"], function (Chroma, V, numeral, Filter, vercomp) { return function (config, filterManager) { From 6908e2953fbd1fb92e9ad73c0fc02734c3256738 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Tue, 24 May 2016 23:26:46 +0200 Subject: [PATCH 15/32] [TASK] Format load average --- lib/infobox/node.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/infobox/node.js b/lib/infobox/node.js index eccc466..659c596 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -297,7 +297,7 @@ define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], } return function (el) { - el.appendChild(showLoadBar("load-avg", d.statistics.loadavg)); + el.appendChild(showLoadBar("load-avg", numeral(d.statistics.loadavg).format("0.00"))); }; } From 8520abf6765efaecc0280ba18308faa88fb81084 Mon Sep 17 00:00:00 2001 From: Nils Schneider <nils@nilsschneider.net> Date: Mon, 29 Feb 2016 14:34:37 +0100 Subject: [PATCH 16/32] [TASK] remove build.js, it is not needed anymore --- build.js | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 build.js diff --git a/build.js b/build.js deleted file mode 100644 index 41f588b..0000000 --- a/build.js +++ /dev/null @@ -1,9 +0,0 @@ -({ - baseUrl: "lib", - name: "../bower_components/almond/almond", - mainConfigFile: "app.js", - include: "../app", - wrap: true, - optimize: "uglify", - out: "app-combined.js" -}); From f1e9aacdf7fe95458c691b70a5968b283bca669d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20P=C3=A4ssler?= <me@petabyteboy.de> Date: Fri, 17 Mar 2017 03:19:03 +0100 Subject: [PATCH 17/32] fix linting --- lib/map.js | 2 +- tasks/linting.js | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/map.js b/lib/map.js index c54cf55..890492b 100644 --- a/lib/map.js +++ b/lib/map.js @@ -311,7 +311,7 @@ define(["map/clientlayer", "map/labelslayer", localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers))); } } catch (e) { - + console.error(e); } } diff --git a/tasks/linting.js b/tasks/linting.js index 71311dc..de1b8d5 100644 --- a/tasks/linting.js +++ b/tasks/linting.js @@ -13,15 +13,12 @@ module.exports = function (grunt) { }, eslint: { options: { + extends: [ + "defaults/configurations/eslint" + ], rules: { - "semi": [2, "never"], - "curly": [2, "multi"], - "strict": [2, "never"], - "no-multi-spaces": 0, - "no-new": 0, - "no-shadow": 0, - "no-use-before-define": [1, "nofunc"], - "no-underscore-dangle": 0 + "semi": [2, "always"], + "no-undef": 0 } }, sources: { From 41c6a03e6b96b34e63c6a4dfad743976eec6a47e Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Wed, 25 May 2016 22:52:29 +0200 Subject: [PATCH 18/32] [TASK] Adjust marker style - focus on online nodes --- lib/map.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/map.js b/lib/map.js index 890492b..7269023 100644 --- a/lib/map.js +++ b/lib/map.js @@ -179,7 +179,7 @@ define(["map/clientlayer", "map/labelslayer", var iconLost = { color: "#D43E2A", fillColor: "#D43E2A", - radius: 6, + radius: 4, fillOpacity: 0.8, opacity: 0.8, weight: 1, @@ -188,11 +188,11 @@ define(["map/clientlayer", "map/labelslayer", var iconAlert = { color: "#D43E2A", fillColor: "#D43E2A", - radius: 6, + radius: 5, fillOpacity: 0.8, opacity: 0.8, weight: 2, - className: "stroke-first node-alert" + className: "stroke-first" }; var iconNew = {color: "#1566A9", fillColor: "#93E929", radius: 6, fillOpacity: 1.0, opacity: 0.5, weight: 2}; @@ -443,7 +443,7 @@ define(["map/clientlayer", "map/labelslayer", m = linkDict[highlight.o.id]; if (m) { - m.setStyle({weight: 7, opacity: 1, dashArray: "10, 10"}); + m.setStyle({weight: 4, opacity: 1, dashArray: "5, 10"}); } } } @@ -560,12 +560,14 @@ define(["map/clientlayer", "map/labelslayer", return iconAlert; } - return iconLost; + if (d.lastseen.isAfter(moment(data.now).subtract(14, "days"))) { + return iconLost; + } }, router)); groupOffline = L.featureGroup(markersOffline).addTo(map); - groupOnline = L.featureGroup(markersOnline).addTo(map); groupLost = L.featureGroup(markersLost).addTo(map); + groupOnline = L.featureGroup(markersOnline).addTo(map); groupNew = L.featureGroup(markersNew).addTo(map); var rtreeOnlineAll = rbush(9); From d5aa447ab8322783e31617496e81ebfc202809df Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Wed, 25 May 2016 23:34:09 +0200 Subject: [PATCH 19/32] [TASK] Add helper.js to grunt watch --- tasks/development.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/development.js b/tasks/development.js index 02826b0..933f123 100644 --- a/tasks/development.js +++ b/tasks/development.js @@ -18,7 +18,7 @@ module.exports = function (grunt) { options: { livereload: true }, - files: ["*.css", "app.js", "lib/**/*.js", "*.html"], + files: ["*.css", "app.js", "helper.js", "lib/**/*.js", "*.html"], tasks: ["dev"] }, config: { From 27389673435b3c1b8cda4350f0e3fc9b014f3ae4 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Wed, 25 May 2016 23:34:36 +0200 Subject: [PATCH 20/32] [TASK] Update npm bower install module --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ffe259..613c552 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "devDependencies": { "autoprefixer": "^6.3.6", "grunt": "^1.0.1", - "grunt-bower-install-simple": "^1.2.1", + "grunt-bower-install-simple": "^1.2.3", "grunt-check-dependencies": "^0.12.0", "grunt-contrib-clean": "^1.0.0", "grunt-contrib-connect": "^1.0.2", From fe3ba88887c30318a819c963e7763b24105e5424 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Wed, 25 May 2016 23:38:18 +0200 Subject: [PATCH 21/32] [TASK] Replace numeraljs with toFixed vanilla js --- app.js | 9 +-------- bower.json | 1 - helper.js | 4 ++-- lib/infobox/node.js | 10 +++++----- lib/main.js | 6 ++---- lib/nodelist.js | 4 ++-- lib/proportions.js | 7 +++---- 7 files changed, 15 insertions(+), 26 deletions(-) diff --git a/app.js b/app.js index f08f023..53b3a4b 100644 --- a/app.js +++ b/app.js @@ -10,8 +10,6 @@ require.config({ "tablesort": "../bower_components/tablesort/tablesort.min", "tablesort.number": "../bower_components/tablesort/src/sorts/tablesort.number", "d3": "../bower_components/d3/d3.min", - "numeral": "../bower_components/numeraljs/min/numeral.min", - "numeral.de": "../bower_components/numeraljs/min/languages/de.min", "virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom", "rbush": "../bower_components/rbush/rbush", "helper": "../helper", @@ -24,12 +22,7 @@ require.config({ "tablesort": { exports: "Tablesort" }, - "numeral.de": { - deps: ["numeral"], - exports: "numeral" - }, - "tablesort.number": ["tablesort"], - "helper": ["numeral.de"] + "tablesort.number": ["tablesort"] } }); diff --git a/bower.json b/bower.json index e63962c..ef1fd19 100644 --- a/bower.json +++ b/bower.json @@ -18,7 +18,6 @@ "es6-shim": "~0.35.1", "almond": "~0.3.2", "d3": "~3.5.17", - "numeraljs": "~1.5.3", "roboto-fontface": "~0.3.0", "virtual-dom": "~2.1.1", "leaflet-providers": "~1.1.10", diff --git a/helper.js b/helper.js index 1237021..a97b38f 100644 --- a/helper.js +++ b/helper.js @@ -118,11 +118,11 @@ function showDistance(d) { return; } - return numeral(d.distance).format("0,0") + " m"; + return d.distance.toFixed(0) + " m" } function showTq(d) { - return numeral(1 / d.tq).format("0%"); + return (1 / d.tq * 100).toFixed(0) + "%" } /* Infobox stuff (XXX: move to module) */ diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 659c596..421a60d 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -1,5 +1,5 @@ -define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], - function (moment, numeral, Tablesort) { +define(["moment", "tablesort", "tablesort.number", "moment.de"], + function (moment, Tablesort) { function showGeoURI(d) { function showLatitude(d) { var suffix = Math.sign(d) > -1 ? "' N" : "' S"; @@ -8,7 +8,7 @@ define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], var min = (d * 60) % 60; a = (a < 10 ? "0" : "") + a; - return a + "° " + numeral(min).format("0.000") + suffix; + return a + "° " + min.toFixed(3) + suffix; } function showLongitude(d) { @@ -18,7 +18,7 @@ define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], var min = (d * 60) % 60; a = (a < 100 ? "0" + (a < 10 ? "0" : "") : "") + a; - return a + "° " + numeral(min).format("0.000") + suffix; + return a + "° " + min.toFixed(3) + suffix; } if (!has_location(d)) { @@ -297,7 +297,7 @@ define(["moment", "numeral", "tablesort", "tablesort.number", "moment.de"], } return function (el) { - el.appendChild(showLoadBar("load-avg", numeral(d.statistics.loadavg).format("0.00"))); + el.appendChild(showLoadBar("load-avg", d.statistics.loadavg.toFixed(2))); }; } diff --git a/lib/main.js b/lib/main.js index bfb3637..aa4163a 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,5 +1,5 @@ -define(["moment", "router", "leaflet", "gui", "numeral", "moment.de"], - function (moment, Router, L, GUI, numeral) { +define(["moment", "router", "leaflet", "gui", "moment.de"], + function (moment, Router, L, GUI) { return function (config) { function handleData(data) { var dataNodes = {}; @@ -197,8 +197,6 @@ define(["moment", "router", "leaflet", "gui", "numeral", "moment.de"], } }; } - - numeral.language("de"); moment.locale("de"); var router = new Router(); diff --git a/lib/nodelist.js b/lib/nodelist.js index b81ec59..950bdd3 100644 --- a/lib/nodelist.js +++ b/lib/nodelist.js @@ -1,4 +1,4 @@ -define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral) { +define(["sorttable", "virtual-dom"], function (SortTable, V) { function getUptime(now, d) { if (d.flags.online && "uptime" in d.statistics) { return Math.round(d.statistics.uptime); @@ -75,7 +75,7 @@ define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral) 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 td4 = V.h("td", ("clients" in d.statistics ? d.statistics.clients : 0).toFixed(0)); return V.h("tr", [td1, td2, td3, td4]); } diff --git a/lib/proportions.js b/lib/proportions.js index 9d9b2e7..e7b6e56 100644 --- a/lib/proportions.js +++ b/lib/proportions.js @@ -1,5 +1,5 @@ -define(["chroma-js", "virtual-dom", "numeral.de", "filters/genericnode", "vercomp"], - function (Chroma, V, numeral, Filter, vercomp) { +define(["chroma-js", "virtual-dom", "filters/genericnode", "vercomp"], + function (Chroma, V, Filter, vercomp) { return function (config, filterManager) { var self = this; @@ -80,7 +80,6 @@ define(["chroma-js", "virtual-dom", "numeral.de", "filters/genericnode", "vercom }); } - function addFilter(filter) { return function () { filterManager.addFilter(filter); @@ -117,7 +116,7 @@ define(["chroma-js", "virtual-dom", "numeral.de", "filters/genericnode", "vercom backgroundColor: scale(v).hex(), color: c1 > c2 ? "white" : "black" } - }, numeral(d[1]).format("0,0"))); + }, d[1].toFixed(0))); return V.h("tr", [th, td]); }); From d4670d8742d208c28cc928d0cb331cfbca798fdb Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Wed, 25 May 2016 23:53:46 +0200 Subject: [PATCH 22/32] [TASK] Simple sort for firmware versions --- lib/proportions.js | 8 ++++-- lib/vercomp.js | 68 ---------------------------------------------- 2 files changed, 5 insertions(+), 71 deletions(-) delete mode 100644 lib/vercomp.js diff --git a/lib/proportions.js b/lib/proportions.js index e7b6e56..6bf6390 100644 --- a/lib/proportions.js +++ b/lib/proportions.js @@ -1,5 +1,5 @@ -define(["chroma-js", "virtual-dom", "filters/genericnode", "vercomp"], - function (Chroma, V, Filter, vercomp) { +define(["chroma-js", "virtual-dom", "filters/genericnode"], + function (Chroma, V, Filter) { return function (config, filterManager) { var self = this; @@ -214,7 +214,9 @@ define(["chroma-js", "virtual-dom", "filters/genericnode", "vercomp"], return b[1] - a[1]; })); fillTable("Firmware", fwTable, fwDict.sort(function (a, b) { - return vercomp(b[0], a[0]); + if(b[0] < a[0]) return -1; + if(b[0] > a[0]) return 1; + return 0; })); fillTable("Hardware", hwTable, hwDict.sort(function (a, b) { return b[1] - a[1]; diff --git a/lib/vercomp.js b/lib/vercomp.js deleted file mode 100644 index 4752b11..0000000 --- a/lib/vercomp.js +++ /dev/null @@ -1,68 +0,0 @@ -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; - } - } - - // Based on dpkg code - function vercomp(a, b) { - var apos = 0, bpos = 0; - while (apos < a.length || bpos < b.length) { - 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]); - - if (ac !== bc) { - return ac - bc; - } - - apos++; - bpos++; - } - - while (a[apos] === "0") { - apos++; - } - - while (b[bpos] === "0") { - bpos++; - } - - while (/^\d$/.test(a[apos]) && /^\d$/.test(b[bpos])) { - if (firstDiff === 0) { - firstDiff = a.charCodeAt(apos) - b.charCodeAt(bpos); - } - - apos++; - bpos++; - } - - if (/^\d$/.test(a[apos])) { - return 1; - } - - if (/^\d$/.test(b[bpos])) { - return -1; - } - - if (firstDiff !== 0) { - return firstDiff; - } - } - - return 0; - } - - return vercomp; -}); From 09714f3b58f3a0c79c90aec71ae34fe1328d1921 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Thu, 26 May 2016 00:27:46 +0200 Subject: [PATCH 23/32] [TASK] Remove jsHashes --- app.js | 3 +-- bower.json | 3 +-- lib/map/clientlayer.js | 8 +++----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app.js b/app.js index 53b3a4b..8720290 100644 --- a/app.js +++ b/app.js @@ -12,8 +12,7 @@ require.config({ "d3": "../bower_components/d3/d3.min", "virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom", "rbush": "../bower_components/rbush/rbush", - "helper": "../helper", - "jshashes": "../bower_components/jshashes/hashes" + "helper": "../helper" }, shim: { "leaflet.label": ["leaflet"], diff --git a/bower.json b/bower.json index ef1fd19..69d1b13 100644 --- a/bower.json +++ b/bower.json @@ -21,8 +21,7 @@ "roboto-fontface": "~0.3.0", "virtual-dom": "~2.1.1", "leaflet-providers": "~1.1.10", - "rbush": "https://github.com/mourner/rbush.git#~1.4.3", - "jshashes": "~1.0.5" + "rbush": "https://github.com/mourner/rbush.git#~1.4.3" }, "authors": [ "Milan Pässler <me@petabyteboy.de>", diff --git a/lib/map/clientlayer.js b/lib/map/clientlayer.js index d6ee4fc..7579f4e 100644 --- a/lib/map/clientlayer.js +++ b/lib/map/clientlayer.js @@ -1,6 +1,5 @@ -define(["leaflet", "jshashes"], - function (L, jsHashes) { - var MD5 = new jsHashes.MD5(); +define(["leaflet"], + function (L) { return L.TileLayer.Canvas.extend({ setData: function (d) { @@ -8,8 +7,7 @@ define(["leaflet", "jshashes"], //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; + d.startAngle = (parseInt(d.node.nodeinfo.node_id.substr(10, 2), 16) / 255) * 2 * Math.PI; }); this.redraw(); }, From c4517750213df395f3efb740d21b26092d27703c Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Thu, 26 May 2016 01:48:56 +0200 Subject: [PATCH 24/32] [TASK] Remove tablesort number No complex different styled numbers need to be sorted --- app.js | 6 ++---- lib/infobox/node.js | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 8720290..899049a 100644 --- a/app.js +++ b/app.js @@ -7,8 +7,7 @@ require.config({ "chroma-js": "../bower_components/chroma-js/chroma.min", "moment": "../bower_components/moment/min/moment.min", "moment.de": "../bower_components/moment/locale/de", - "tablesort": "../bower_components/tablesort/tablesort.min", - "tablesort.number": "../bower_components/tablesort/src/sorts/tablesort.number", + "tablesort": "../bower_components/tablesort/src/tablesort", "d3": "../bower_components/d3/d3.min", "virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom", "rbush": "../bower_components/rbush/rbush", @@ -20,8 +19,7 @@ require.config({ "moment.de": ["moment"], "tablesort": { exports: "Tablesort" - }, - "tablesort.number": ["tablesort"] + } } }); diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 421a60d..415596b 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -1,4 +1,4 @@ -define(["moment", "tablesort", "tablesort.number", "moment.de"], +define(["moment", "tablesort", "moment.de"], function (moment, Tablesort) { function showGeoURI(d) { function showLatitude(d) { From 173674c2a16d2964c9a48f6931a20d0c55d6777b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20P=C3=A4ssler?= <me@petabyteboy.de> Date: Sat, 18 Mar 2017 15:33:49 +0100 Subject: [PATCH 25/32] [!!!][TASK] Use eslint default --- .eslintrc | 8 ++ .gitignore | 1 - app.js | 6 +- lib/filters/genericnode.js | 4 +- lib/forcegraph.js | 6 +- lib/infobox/link.js | 18 +-- lib/infobox/location.js | 6 +- lib/infobox/main.js | 6 +- lib/infobox/node.js | 106 ++++++++-------- lib/linklist.js | 6 +- lib/main.js | 17 +-- lib/map.js | 36 +++--- lib/meshstats.js | 18 +-- lib/nodelist.js | 4 +- lib/proportions.js | 20 +-- lib/router.js | 4 +- lib/simplenodelist.js | 4 +- lib/utils/helper.js | 241 +++++++++++++++++++++++++++++++++++++ package.json | 20 +-- tasks/development.js | 6 +- tasks/linting.js | 9 -- 21 files changed, 387 insertions(+), 159 deletions(-) create mode 100644 .eslintrc create mode 100644 lib/utils/helper.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..f0764d2 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,8 @@ +--- +"extends": + - "defaults/configurations/eslint" + +rules: + "semi": ["error", "always"] + "no-undef": 0 + "no-console": ["error", { allow: ["warn", "error"] }] diff --git a/.gitignore b/.gitignore index e728065..d7fc55a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ build/ .sass-cache/ config.json .idea/ -.eslintrc diff --git a/app.js b/app.js index 899049a..d6a1227 100644 --- a/app.js +++ b/app.js @@ -11,7 +11,7 @@ require.config({ "d3": "../bower_components/d3/d3.min", "virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom", "rbush": "../bower_components/rbush/rbush", - "helper": "../helper" + "helper": "utils/helper" }, shim: { "leaflet.label": ["leaflet"], @@ -23,6 +23,6 @@ require.config({ } }); -require(["main", "helper"], function (main) { - getJSON("config.json").then(main); +require(["main", "helper"], function (main, helper) { + helper.getJSON("config.json").then(main); }); diff --git a/lib/filters/genericnode.js b/lib/filters/genericnode.js index 831f575..c4fe7a9 100644 --- a/lib/filters/genericnode.js +++ b/lib/filters/genericnode.js @@ -1,4 +1,4 @@ -define([], function () { +define(["helper"], function (helper) { return function (name, key, value, f) { var negate = false; var refresh; @@ -9,7 +9,7 @@ define([], function () { label.appendChild(strong); function run(d) { - var o = dictGet(d, key.slice(0)); + var o = helper.dictGet(d, key.slice(0)); if (f) { o = f(o); diff --git a/lib/forcegraph.js b/lib/forcegraph.js index d1a24d3..645b053 100644 --- a/lib/forcegraph.js +++ b/lib/forcegraph.js @@ -1,4 +1,4 @@ -define(["d3"], function (d3) { +define(["d3", "helper"], function (d3, helper) { var margin = 200; var NODE_RADIUS = 15; var LINE_RADIUS = 7; @@ -32,7 +32,7 @@ define(["d3"], function (d3) { } function savePositions() { - if (!localStorageTest()) { + if (!helper.localStorageTest()) { return; } @@ -773,7 +773,7 @@ define(["d3"], function (d3) { return !d.o.node; }); - if (localStorageTest()) { + if (helper.localStorageTest()) { var save = JSON.parse(localStorage.getItem("graph/nodeposition")); if (save) { diff --git a/lib/infobox/link.js b/lib/infobox/link.js index 260931f..d24e144 100644 --- a/lib/infobox/link.js +++ b/lib/infobox/link.js @@ -1,11 +1,11 @@ -define(function () { +define(["helper"], function (helper) { 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); + return helper.showStat(o, subst); } return function (config, el, router, d) { @@ -19,7 +19,7 @@ define(function () { a1.textContent = unknown ? d.source.id : d.source.node.nodeinfo.hostname; h2.appendChild(a1); h2.appendChild(document.createTextNode(" \uF3D6 ")); - h2.className = 'ion-inside'; + h2.className = "ion-inside"; var a2 = document.createElement("a"); a2.href = "#"; a2.onclick = router.node(d.target.node); @@ -30,12 +30,12 @@ define(function () { 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")); + helper.attributeEntry(attributes, "TQ", helper.showTq(d)); + helper.attributeEntry(attributes, "Entfernung", helper.showDistance(d)); + helper.attributeEntry(attributes, "Typ", d.type); + var hw1 = unknown ? null : helper.dictGet(d.source.node.nodeinfo, ["hardware", "model"]); + var hw2 = helper.dictGet(d.target.node.nodeinfo, ["hardware", "model"]); + helper.attributeEntry(attributes, "Hardware", (hw1 != null ? hw1 : "unbekannt") + " – " + (hw2 != null ? hw2 : "unbekannt")); el.appendChild(attributes); if (config.linkInfos) { diff --git a/lib/infobox/location.js b/lib/infobox/location.js index 9910d33..4695ac4 100644 --- a/lib/infobox/location.js +++ b/lib/infobox/location.js @@ -1,10 +1,10 @@ -define(function () { +define(["helper"], function (helper) { return function (config, el, router, d) { 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") + helper.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; @@ -84,7 +84,7 @@ define(function () { try { document.execCommand("copy"); } catch (err) { - console.log(err); + console.warn(err); } } diff --git a/lib/infobox/main.js b/lib/infobox/main.js index dc900e4..8f1ef4a 100644 --- a/lib/infobox/main.js +++ b/lib/infobox/main.js @@ -33,17 +33,17 @@ define(["infobox/link", "infobox/node", "infobox/location"], function (Link, Nod self.gotoNode = function (d) { create(); - new Node(config, el, router, d); + Node(config, el, router, d); }; self.gotoLink = function (d) { create(); - new Link(config, el, router, d); + Link(config, el, router, d); }; self.gotoLocation = function (d) { create(); - new Location(config, el, router, d); + Location(config, el, router, d); }; return self; diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 415596b..6839206 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -1,5 +1,5 @@ -define(["moment", "tablesort", "moment.de"], - function (moment, Tablesort) { +define(["moment", "tablesort", "helper", "moment.de"], + function (moment, Tablesort, helper) { function showGeoURI(d) { function showLatitude(d) { var suffix = Math.sign(d) > -1 ? "' N" : "' S"; @@ -21,7 +21,7 @@ define(["moment", "tablesort", "moment.de"], return a + "° " + min.toFixed(3) + suffix; } - if (!has_location(d)) { + if (!helper.hasLocation(d)) { return undefined; } @@ -49,8 +49,8 @@ define(["moment", "tablesort", "moment.de"], } function showFirmware(d) { - var release = dictGet(d.nodeinfo, ["software", "firmware", "release"]); - var base = dictGet(d.nodeinfo, ["software", "firmware", "base"]); + var release = helper.dictGet(d.nodeinfo, ["software", "firmware", "release"]); + var base = helper.dictGet(d.nodeinfo, ["software", "firmware", "base"]); if (release === null || base === null) { return undefined; @@ -60,7 +60,7 @@ define(["moment", "tablesort", "moment.de"], } function showSite(d, config) { - var site = dictGet(d.nodeinfo, ["system", "site_code"]); + var site = helper.dictGet(d.nodeinfo, ["system", "site_code"]); var rt = site; if (config.siteNames) { config.siteNames.forEach(function (t) { @@ -220,7 +220,7 @@ define(["moment", "tablesort", "moment.de"], } function showIPs(d) { - var ips = dictGet(d.nodeinfo, ["network", "addresses"]); + var ips = helper.dictGet(d.nodeinfo, ["network", "addresses"]); if (ips === null) { return undefined; } @@ -340,13 +340,13 @@ define(["moment", "tablesort", "moment.de"], function showGateway(d, router) { var nh; - if (dictGet(d.statistics, ["nexthop"])) { - nh = dictGet(d.statistics, ["nexthop"]); + if (helper.dictGet(d.statistics, ["nexthop"])) { + nh = helper.dictGet(d.statistics, ["nexthop"]); } - if (dictGet(d.statistics, ["gateway_nexthop"])) { - nh = dictGet(d.statistics, ["gateway_nexthop"]); + if (helper.dictGet(d.statistics, ["gateway_nexthop"])) { + nh = helper.dictGet(d.statistics, ["gateway_nexthop"]); } - var gw = dictGet(d.statistics, ["gateway"]); + var gw = helper.dictGet(d.statistics, ["gateway"]); if (!gw) { return null; @@ -362,16 +362,16 @@ define(["moment", "tablesort", "moment.de"], if (!nh.node || !nh.node.statistics) { break; } - if (!dictGet(nh.node.statistics, ["gateway"]) || !dictGet(nh.node.statistics, ["gateway"]).id) { + if (!helper.dictGet(nh.node.statistics, ["gateway"]) || !helper.dictGet(nh.node.statistics, ["gateway"]).id) { break; } - if (dictGet(nh.node.statistics, ["gateway"]).id !== gw.id) { + if (helper.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"]); + if (helper.dictGet(nh.node.statistics, ["gateway_nexthop"])) { + nh = helper.dictGet(nh.node.statistics, ["gateway_nexthop"]); + } else if (helper.dictGet(nh.node.statistics, ["nexthop"])) { + nh = helper.dictGet(nh.node.statistics, ["nexthop"]); } else { break; } @@ -391,7 +391,7 @@ define(["moment", "tablesort", "moment.de"], } function showPages(d) { - var webpages = dictGet(d.nodeinfo, ["pages"]); + var webpages = helper.dictGet(d.nodeinfo, ["pages"]); if (webpages === null) { return undefined; } @@ -425,7 +425,7 @@ define(["moment", "tablesort", "moment.de"], } function showAutoupdate(d) { - var au = dictGet(d.nodeinfo, ["software", "autoupdater"]); + var au = helper.dictGet(d.nodeinfo, ["software", "autoupdater"]); if (!au) { return undefined; } @@ -469,8 +469,8 @@ define(["moment", "tablesort", "moment.de"], 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.replace(/[^a-z0-9\-]/ig, '_') : "unknown"; - return showStat(o, subst); + subst["{NODE_NAME}"] = d.nodeinfo.hostname ? d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, "_") : "unknown"; + return helper.showStat(o, subst); } return function (config, el, router, d) { @@ -483,47 +483,47 @@ define(["moment", "tablesort", "moment.de"], try { config.hwImg.forEach(function (hwImg) { try { - top.appendChild(showNodeImg(hwImg, dictGet(d, ["nodeinfo", "hardware", "model"]))); + top.appendChild(showNodeImg(hwImg, helper.dictGet(d, ["nodeinfo", "hardware", "model"]))); } catch (err) { - console.log(err.message); + console.warn(err.message); } }); } catch (err) { - console.log(err.message); + console.warn(err.message); } - attributeEntry(attributes, top, d.nodeinfo.hostname); + helper.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)); + helper.attributeEntry(attributes, "Status", showStatus(d)); + helper.attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null); + helper.attributeEntry(attributes, "Koordinaten", showGeoURI(d)); if (config.showContact) { - attributeEntry(attributes, "Kontakt", dictGet(d.nodeinfo, ["owner", "contact"])); + helper.attributeEntry(attributes, "Kontakt", helper.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)); + helper.attributeEntry(attributes, "Hardware", helper.dictGet(d.nodeinfo, ["hardware", "model"])); + helper.attributeEntry(attributes, "Primäre MAC", helper.dictGet(d.nodeinfo, ["network", "mac"])); + helper.attributeEntry(attributes, "Node ID", helper.dictGet(d.nodeinfo, ["node_id"])); + helper.attributeEntry(attributes, "Firmware", showFirmware(d)); + helper.attributeEntry(attributes, "Site", showSite(d, config)); + helper.attributeEntry(attributes, "Uptime", showUptime(d)); + helper.attributeEntry(attributes, "Teil des Netzes", showFirstseen(d)); + helper.attributeEntry(attributes, "Kanal 2.4 GHz", showWifiChannel(helper.dictGet(d.nodeinfo, ["wireless", "chan2"]))); + helper.attributeEntry(attributes, "Kanal 5 GHz", showWifiChannel(helper.dictGet(d.nodeinfo, ["wireless", "chan5"]))); + helper.attributeEntry(attributes, "Airtime 2.4 GHz", showAirtime(2, helper.dictGet(d.statistics, ["wireless", "airtime2"]))); + helper.attributeEntry(attributes, "Airtime 5 GHz", showAirtime(5, helper.dictGet(d.statistics, ["wireless", "airtime5"]))); + helper.attributeEntry(attributes, "Systemlast", showLoad(d)); + helper.attributeEntry(attributes, "Arbeitsspeicher", showRAM(d)); + helper.attributeEntry(attributes, "IP Adressen", showIPs(d)); + helper.attributeEntry(attributes, "Webseite", showPages(d)); + helper.attributeEntry(attributes, "Gewähltes Gateway", showGateway(d, router)); + helper.attributeEntry(attributes, "Autom. Updates", showAutoupdate(d)); + helper.attributeEntry(attributes, "Clients", showClients(d), showMeshClients(d)); el.appendChild(attributes); @@ -577,14 +577,14 @@ define(["moment", "tablesort", "moment.de"], var tr = document.createElement("tr"); var td1 = document.createElement("td"); - td1.className = 'ion-inside'; + td1.className = "ion-inside"; td1.appendChild(document.createTextNode(d.incoming ? " \uF3D5 " : " \uF3D6 ")); tr.appendChild(td1); var td2 = document.createElement("td"); td2.appendChild(createLink(d, router)); - if (!unknown && has_location(d.node)) { + if (!unknown && helper.hasLocation(d.node)) { var span = document.createElement("span"); span.classList.add("icon"); span.classList.add("ion-location"); @@ -596,7 +596,7 @@ define(["moment", "tablesort", "moment.de"], var td3 = document.createElement("td"); var a2 = document.createElement("a"); a2.href = "#"; - a2.textContent = showTq(d.link); + a2.textContent = helper.showTq(d.link); a2.onclick = router.link(d.link); td3.appendChild(a2); tr.appendChild(td3); @@ -612,7 +612,7 @@ define(["moment", "tablesort", "moment.de"], var td5 = document.createElement("td"); var a4 = document.createElement("a"); a4.href = "#"; - a4.textContent = showDistance(d.link); + a4.textContent = helper.showDistance(d.link); a4.onclick = router.link(d.link); td5.appendChild(a4); td5.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1); @@ -624,7 +624,7 @@ define(["moment", "tablesort", "moment.de"], table.appendChild(tbody); table.className = "node-links"; - new Tablesort(table); + Tablesort(table); el.appendChild(table); } diff --git a/lib/linklist.js b/lib/linklist.js index 96e7447..661b777 100644 --- a/lib/linklist.js +++ b/lib/linklist.js @@ -1,4 +1,4 @@ -define(["sorttable", "virtual-dom"], function (SortTable, V) { +define(["sorttable", "virtual-dom", "helper"], function (SortTable, V, helper) { function linkName(d) { return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + d.target.node.nodeinfo.hostname; } @@ -40,9 +40,9 @@ define(["sorttable", "virtual-dom"], function (SortTable, V) { 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 td2 = V.h("td", {style: {color: linkScale(d.tq).hex()}}, helper.showTq(d)); var td3 = V.h("td", d.type); - var td4 = V.h("td", showDistance(d)); + var td4 = V.h("td", helper.showDistance(d)); return V.h("tr", [td1, td2, td3, td4]); } diff --git a/lib/main.js b/lib/main.js index aa4163a..36d421c 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,5 +1,5 @@ -define(["moment", "router", "leaflet", "gui", "moment.de"], - function (moment, Router, L, GUI) { +define(["moment", "router", "leaflet", "gui", "helper", "moment.de"], + function (moment, Router, L, GUI, helper) { return function (config) { function handleData(data) { var dataNodes = {}; @@ -21,7 +21,7 @@ define(["moment", "router", "leaflet", "gui", "moment.de"], if (i % 2) { if (data[i].version !== 1) { vererr = "Unsupported graph version: " + data[i].version; - console.log(vererr); //silent fail + console.error(vererr); //silent fail } else { data[i].batadv.links.forEach(rearrangeLinks); dataGraph.batadv.nodes = dataGraph.batadv.nodes.concat(data[i].batadv.nodes); @@ -30,7 +30,7 @@ define(["moment", "router", "leaflet", "gui", "moment.de"], } } else if (data[i].version !== 2) { vererr = "Unsupported nodes version: " + data[i].version; - console.log(vererr); //silent fail + console.error(vererr); //silent fail } else { data[i].nodes.forEach(fillData); dataNodes.timestamp = data[i].timestamp; @@ -60,8 +60,8 @@ define(["moment", "router", "leaflet", "gui", "moment.de"], 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 newnodes = helper.limit("firstseen", age, helper.sortByKey("firstseen", nodes).filter(helper.online)); + var lostnodes = helper.limit("lastseen", age, helper.sortByKey("lastseen", nodes).filter(helper.offline)); var graphnodes = {}; @@ -197,6 +197,7 @@ define(["moment", "router", "leaflet", "gui", "moment.de"], } }; } + moment.locale("de"); var router = new Router(); @@ -213,7 +214,7 @@ define(["moment", "router", "leaflet", "gui", "moment.de"], } function update() { - return Promise.all(urls.map(getJSON)) + return Promise.all(urls.map(helper.getJSON)) .then(handleData); } @@ -233,7 +234,7 @@ define(["moment", "router", "leaflet", "gui", "moment.de"], }) .catch(function (e) { document.body.textContent = e; - console.log(e); + console.warn(e); }); }; }); diff --git a/lib/map.js b/lib/map.js index 7269023..f099fb6 100644 --- a/lib/map.js +++ b/lib/map.js @@ -1,7 +1,7 @@ define(["map/clientlayer", "map/labelslayer", - "d3", "leaflet", "moment", "locationmarker", "rbush", + "d3", "leaflet", "moment", "locationmarker", "rbush", "helper", "leaflet.label", "leaflet.providers", "moment.de"], - function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush) { + function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush, helper) { var options = { worldCopyJump: true, zoomControl: false @@ -149,7 +149,7 @@ define(["map/clientlayer", "map/labelslayer", line.setStyle(opts); }; - line.bindLabel(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + "<br><strong>" + showDistance(d) + " / " + showTq(d) + "</strong>"); + line.bindLabel(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + "<br><strong>" + helper.showDistance(d) + " / " + helper.showTq(d) + "</strong>"); line.on("click", router.link(d)); dict[d.id] = line; @@ -307,7 +307,7 @@ define(["map/clientlayer", "map/labelslayer", layerControl.addBaseLayer(layer, layerName); customLayers[layerName] = layer; - if (localStorageTest()) { + if (helper.localStorageTest()) { localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers))); } } catch (e) { @@ -354,7 +354,7 @@ define(["map/clientlayer", "map/labelslayer", layerControl = L.control.layers(baseLayers, [], {position: "bottomright"}); layerControl.addTo(map); - if (localStorageTest()) { + if (helper.localStorageTest()) { var d = JSON.parse(localStorage.getItem("map/customLayers")); if (d) { @@ -385,7 +385,7 @@ define(["map/clientlayer", "map/labelslayer", if (map.getZoom() > map.options.maxZoom) { map.setZoom(map.options.maxZoom); } - if (localStorageTest()) { + if (helper.localStorageTest()) { localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name})); } }); @@ -531,30 +531,30 @@ define(["map/clientlayer", "map/labelslayer", groupLines = L.featureGroup(lines).addTo(map); if (typeof config.fixedCenter === "undefined") { - barycenter = calcBarycenter(data.nodes.all.filter(has_location)); + barycenter = calcBarycenter(data.nodes.all.filter(helper.hasLocation)); } 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 = helper.subtract(data.nodes.all.filter(helper.online), data.nodes.new); + var nodesOffline = helper.subtract(data.nodes.all.filter(helper.offline), data.nodes.lost); - var markersOnline = nodesOnline.filter(has_location) + var markersOnline = nodesOnline.filter(helper.hasLocation) .map(mkMarker(nodeDict, function () { return iconOnline; }, router)); - var markersOffline = nodesOffline.filter(has_location) + var markersOffline = nodesOffline.filter(helper.hasLocation) .map(mkMarker(nodeDict, function () { return iconOffline; }, router)); - var markersNew = data.nodes.new.filter(has_location) + var markersNew = data.nodes.new.filter(helper.hasLocation) .map(mkMarker(nodeDict, function () { return iconNew; }, router)); - var markersLost = data.nodes.lost.filter(has_location) + var markersLost = data.nodes.lost.filter(helper.hasLocation) .map(mkMarker(nodeDict, function (d) { if (d.lastseen.isAfter(moment(data.now).subtract(3, "days"))) { return iconAlert; @@ -572,14 +572,14 @@ define(["map/clientlayer", "map/labelslayer", var rtreeOnlineAll = rbush(9); - rtreeOnlineAll.load(data.nodes.all.filter(online).filter(has_location).map(mapRTree)); + rtreeOnlineAll.load(data.nodes.all.filter(helper.online).filter(helper.hasLocation).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) + online: nodesOnline.filter(helper.hasLocation), + offline: nodesOffline.filter(helper.hasLocation), + new: data.nodes.new.filter(helper.hasLocation), + lost: data.nodes.lost.filter(helper.hasLocation) }); updateView(true); diff --git a/lib/meshstats.js b/lib/meshstats.js index 0abd3cc..bc6c025 100644 --- a/lib/meshstats.js +++ b/lib/meshstats.js @@ -1,20 +1,20 @@ -define(function () { +define(["helper"], function (helper) { return function (config) { 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) { + var totalNodes = helper.sum(d.nodes.all.map(helper.one)); + var totalOnlineNodes = helper.sum(d.nodes.all.filter(helper.online).map(helper.one)); + var totalOfflineNodes = helper.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) { + }).map(helper.one)); + var totalNewNodes = helper.sum(d.nodes.new.map(helper.one)); + var totalLostNodes = helper.sum(d.nodes.lost.map(helper.one)); + var totalClients = helper.sum(d.nodes.all.filter(helper.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) { + var totalGateways = helper.sum(Array.from(new Set(d.nodes.all.filter(helper.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; diff --git a/lib/nodelist.js b/lib/nodelist.js index 950bdd3..12687c4 100644 --- a/lib/nodelist.js +++ b/lib/nodelist.js @@ -1,4 +1,4 @@ -define(["sorttable", "virtual-dom"], function (SortTable, V) { +define(["sorttable", "virtual-dom", "helper"], function (SortTable, V, helper) { function getUptime(now, d) { if (d.flags.online && "uptime" in d.statistics) { return Math.round(d.statistics.uptime); @@ -68,7 +68,7 @@ define(["sorttable", "virtual-dom"], function (SortTable, V) { href: "#!n:" + d.nodeinfo.node_id }, d.nodeinfo.hostname)); - if (has_location(d)) { + if (helper.hasLocation(d)) { td1Content.push(V.h("span", {className: "icon ion-location"})); } diff --git a/lib/proportions.js b/lib/proportions.js index 6bf6390..391145e 100644 --- a/lib/proportions.js +++ b/lib/proportions.js @@ -1,5 +1,5 @@ -define(["chroma-js", "virtual-dom", "filters/genericnode"], - function (Chroma, V, Filter) { +define(["chroma-js", "virtual-dom", "filters/genericnode", "helper"], + function (Chroma, V, Filter, helper) { return function (config, filterManager) { var self = this; @@ -33,14 +33,14 @@ define(["chroma-js", "virtual-dom", "filters/genericnode"], siteTable.classList.add("proportion"); function showStatGlobal(o) { - return showStat(o); + return helper.showStat(o); } function count(nodes, key, f) { var dict = {}; nodes.forEach(function (d) { - var v = dictGet(d, key.slice(0)); + var v = helper.dictGet(d, key.slice(0)); if (f !== undefined) { v = f(v); @@ -62,7 +62,7 @@ define(["chroma-js", "virtual-dom", "filters/genericnode"], var dict = {}; nodes.forEach(function (d) { - var v = dictGet(d, key.slice(0)); + var v = helper.dictGet(d, key.slice(0)); if (f !== undefined) { v = f(v); @@ -127,7 +127,7 @@ define(["chroma-js", "virtual-dom", "filters/genericnode"], } self.setData = function (data) { - var onlineNodes = data.nodes.all.filter(online); + var onlineNodes = data.nodes.all.filter(helper.online); var nodes = onlineNodes.concat(data.nodes.lost); var nodeDict = {}; @@ -214,8 +214,12 @@ define(["chroma-js", "virtual-dom", "filters/genericnode"], return b[1] - a[1]; })); fillTable("Firmware", fwTable, fwDict.sort(function (a, b) { - if(b[0] < a[0]) return -1; - if(b[0] > a[0]) return 1; + if (b[0] < a[0]) { + return -1; + } + if (b[0] > a[0]) { + return 1; + } return 0; })); fillTable("Hardware", hwTable, hwDict.sort(function (a, b) { diff --git a/lib/router.js b/lib/router.js index f719cc0..d841d32 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,4 +1,4 @@ -define(function () { +define(["helper"], function (helper) { return function () { var self = this; var objects = {nodes: {}, links: {}}; @@ -31,7 +31,7 @@ define(function () { } function resetView(push) { - push = trueDefault(push); + push = helper.trueDefault(push); targets.forEach(function (t) { t.resetView(); diff --git a/lib/simplenodelist.js b/lib/simplenodelist.js index bac6c25..2ba0207 100644 --- a/lib/simplenodelist.js +++ b/lib/simplenodelist.js @@ -1,4 +1,4 @@ -define(["moment", "virtual-dom", "moment.de"], function (moment, V) { +define(["moment", "virtual-dom", "helper", "moment.de"], function (moment, V, helper) { return function (nodes, field, router, title) { var self = this; var el, tbody; @@ -46,7 +46,7 @@ define(["moment", "virtual-dom", "moment.de"], function (moment, V) { href: "#!n:" + d.nodeinfo.node_id }, d.nodeinfo.hostname)); - if (has_location(d)) { + if (helper.hasLocation(d)) { td1Content.push(V.h("span", {className: "icon ion-location"})); } diff --git a/lib/utils/helper.js b/lib/utils/helper.js new file mode 100644 index 0000000..e3dce69 --- /dev/null +++ b/lib/utils/helper.js @@ -0,0 +1,241 @@ +define({ + get: function (url) { + return new Promise(function (resolve, reject) { + var req = new XMLHttpRequest(); + req.open("GET", url); + + req.onload = function () { + if (req.status == 200) { + resolve(req.response); + } + else { + reject(Error(req.statusText)); + } + }; + + req.onerror = function () { + reject(Error("Network Error")); + }; + + req.send(); + }); + }, + + getJSON: function (url) { + return require("helper").get(url).then(JSON.parse); + }, + + sortByKey: function (key, d) { + return d.slice().sort(function (a, b) { + return a[key] - b[key]; + }).reverse(); + }, + + limit: function (key, m, d) { + return d.filter(function (d) { + return d[key].isAfter(m); + }); + }, + + sum: function (a) { + return a.reduce(function (a, b) { + return a + b; + }, 0); + }, + + one: function () { + return 1; + }, + + trueDefault: function (d) { + return d === undefined ? true : d; + }, + + dictGet: function (dict, key) { + var k = key.shift(); + + if (!(k in dict)) { + return null; + } + + if (key.length == 0) { + return dict[k]; + } + + return this.dictGet(dict[k], key); + }, + + localStorageTest: function () { + var test = "test"; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + return true; + } catch (e) { + return false; + } + }, + + listReplace: function (s, subst) { + for (key in subst) { + var re = new RegExp(key, "g"); + s = s.replace(re, subst[key]); + } + return s; + }, + + /* Helpers working with nodes */ + + offline: function (d) { + return !d.flags.online; + }, + + online: function (d) { + return d.flags.online; + }, + + hasLocation: function (d) { + return "location" in d.nodeinfo && + Math.abs(d.nodeinfo.location.latitude) < 90 && + Math.abs(d.nodeinfo.location.longitude) < 180; + }, + + subtract: function (a, b) { + var ids = {}; + + b.forEach(function (d) { + ids[d.nodeinfo.node_id] = true; + }); + + return a.filter(function (d) { + return !(d.nodeinfo.node_id in ids); + }); + }, + + /* Helpers working with links */ + + showDistance: function (d) { + if (isNaN(d.distance)) { + return; + } + + return d.distance.toFixed(0) + " m"; + }, + + showTq: function (d) { + return (1 / d.tq * 100).toFixed(0) + "%"; + }, + + attributeEntry: function (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.classList.add("routerpic"); + } + + tr.appendChild(th); + + var td = document.createElement("td"); + + if (typeof value == "function") { + value(td); + } else { + td.appendChild(document.createTextNode(value)); + } + + tr.appendChild(td); + + el.appendChild(tr); + + return td; + }, + + createIframe: function (opt, width, height) { + var el = document.createElement("iframe"); + width = typeof width !== "undefined" ? width : "525px"; + height = typeof height !== "undefined" ? height : "350px"; + + if (opt.src) { + el.src = opt.src; + } else { + el.src = opt; + } + + if (opt.frameBorder) { + el.frameBorder = opt.frameBorder; + } else { + el.frameBorder = 1; + } + + if (opt.width) { + el.width = opt.width; + } else { + el.width = width; + } + + if (opt.height) { + el.height = opt.height; + } else { + el.height = height; + } + + el.scrolling = "no"; + el.seamless = "seamless"; + + return el; + }, + + showStat: function (o, subst) { + var content, caption; + subst = typeof subst !== "undefined" ? subst : {}; + + if (o.thumbnail) { + content = document.createElement("img"); + content.src = require("helper").listReplace(o.thumbnail, subst); + } + + if (o.caption) { + caption = require("helper").listReplace(o.caption, subst); + + if (!content) { + content = document.createTextNode(caption); + } + } + + if (o.iframe) { + content = require("helper").createIframe(o.iframe, o.width, o.height); + if (o.iframe.src) { + content.src = require("helper").listReplace(o.iframe.src, subst); + } else { + content.src = require("helper").listReplace(o.iframe, subst); + } + } + + var p = document.createElement("p"); + + if (o.href) { + var link = document.createElement("a"); + link.target = "_blank"; + link.href = require("helper").listReplace(o.href, subst); + link.appendChild(content); + + if (caption && o.thumbnail) { + link.title = caption; + } + + p.appendChild(link); + } else { + p.appendChild(content); + } + + return p; + } +}); diff --git a/package.json b/package.json index 613c552..caa6d5c 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ }, "devDependencies": { "autoprefixer": "^6.3.6", + "eslint": "^2.10.2", + "eslint-config-defaults": "^9.0.0", "grunt": "^1.0.1", "grunt-bower-install-simple": "^1.2.3", "grunt-check-dependencies": "^0.12.0", @@ -26,24 +28,6 @@ "amd": true, "es6": true, "node": true - }, - "globals": { - "showStat": false, - "attributeEntry": false, - "dictGet": false, - "getJSON": false, - "has_location": false, - "limit": false, - "localStorageTest": false, - "offline": false, - "one": false, - "online": false, - "showDistance": false, - "showTq": false, - "sortByKey": false, - "subtract": false, - "sum": false, - "trueDefault": false } } } diff --git a/tasks/development.js b/tasks/development.js index 933f123..d366a1c 100644 --- a/tasks/development.js +++ b/tasks/development.js @@ -4,9 +4,9 @@ module.exports = function (grunt) { server: { options: { base: { - path: 'build', + path: "build", options: { - index: 'index.html' + index: "index.html" } }, livereload: true @@ -18,7 +18,7 @@ module.exports = function (grunt) { options: { livereload: true }, - files: ["*.css", "app.js", "helper.js", "lib/**/*.js", "*.html"], + files: ["*.css", "app.js", "lib/**/*.js", "*.html"], tasks: ["dev"] }, config: { diff --git a/tasks/linting.js b/tasks/linting.js index de1b8d5..5409fd5 100644 --- a/tasks/linting.js +++ b/tasks/linting.js @@ -12,15 +12,6 @@ module.exports = function (grunt) { npm: {} }, eslint: { - options: { - extends: [ - "defaults/configurations/eslint" - ], - rules: { - "semi": [2, "always"], - "no-undef": 0 - } - }, sources: { src: ["app.js", "!Gruntfile.js", "lib/**/*.js"] }, From 6869a19ea0b05001dbabc11aae9e14fe185be658 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Thu, 26 May 2016 22:28:28 +0200 Subject: [PATCH 26/32] [TASK] Style layer selector --- assets/icons/fonts/hopglass.eot | Bin 3352 -> 0 bytes assets/icons/fonts/hopglass.svg | 27 ------------------------ assets/icons/fonts/hopglass.ttf | Bin 3184 -> 0 bytes assets/icons/fonts/hopglass.woff | Bin 2300 -> 0 bytes assets/icons/fonts/hopglass.woff2 | Bin 1700 -> 0 bytes assets/icons/fonts/icon.ttf | Bin 0 -> 3400 bytes assets/icons/fonts/icon.woff | Bin 0 -> 2472 bytes assets/icons/fonts/icon.woff2 | Bin 0 -> 1760 bytes assets/icons/hopglass-icons.css | 10 ++++++--- scss/_leaflet-layer.scss | 33 ++++++++++++++++++++++++++++++ scss/main.scss | 1 + 11 files changed, 41 insertions(+), 30 deletions(-) delete mode 100644 assets/icons/fonts/hopglass.eot delete mode 100644 assets/icons/fonts/hopglass.svg delete mode 100644 assets/icons/fonts/hopglass.ttf delete mode 100644 assets/icons/fonts/hopglass.woff delete mode 100644 assets/icons/fonts/hopglass.woff2 create mode 100644 assets/icons/fonts/icon.ttf create mode 100644 assets/icons/fonts/icon.woff create mode 100644 assets/icons/fonts/icon.woff2 create mode 100644 scss/_leaflet-layer.scss diff --git a/assets/icons/fonts/hopglass.eot b/assets/icons/fonts/hopglass.eot deleted file mode 100644 index c98534acce803009679a9b96251e6a6796287354..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3352 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 B599y< diff --git a/assets/icons/fonts/hopglass.svg b/assets/icons/fonts/hopglass.svg deleted file mode 100644 index da13e93..0000000 --- a/assets/icons/fonts/hopglass.svg +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg"> -<metadata>Generated by Fontastic.me</metadata> -<defs> -<font id="hopglass" horiz-adv-x="512"> -<font-face font-family="hopglass" units-per-em="512" ascent="480" descent="-32"/> -<missing-glyph horiz-adv-x="512" /> - -<glyph glyph-name="android-add" unicode="" d="M416 235l-139 0 0-139-42 0 0 139-139 0 0 42 139 0 0 139 42 0 0-139 139 0z"/> -<glyph glyph-name="chevron-left" unicode="" d="M214 256l167 174c4 4 4 11 0 16l-30 30c-4 5-11 5-16 1l-204-213c-2-2-3-5-3-8 0-3 1-6 3-8l204-213c5-4 12-4 16 0l30 31c4 4 4 11 0 16z"/> -<glyph glyph-name="chevron-right" unicode="" d="M298 256l-167 174c-4 4-4 11 0 16l30 30c5 5 12 5 16 1l204-213c2-2 3-5 3-8 0-3-1-6-3-8l-204-213c-4-4-11-4-16 0l-30 31c-4 4-4 11 0 16z"/> -<glyph glyph-name="pin" unicode="" d="M332 284c-1 0-3 1-4 2-7 4-12 11-14 19l-18 118 0 5c0 7 4 11 10 13 0 1 1 1 2 1 7 4 12 8 12 17 0 20-7 21-18 21l-92 0c-11 0-18-1-18-21 0-9 5-13 12-17 1 0 2 0 2-1 6-3 10-6 10-13l0-5-18-118c-2-8-7-15-14-19-1-1-2-2-4-2-19-10-36-31-36-55 0-16 4-21 15-21l81 0 12-176 8 0 12 176 81 0c12 0 15 5 15 21 0 24-16 45-36 55z"/> -<glyph glyph-name="wifi" unicode="" d="M256 416c-80 0-156-30-214-84l-10-10 10-9 32-32 10-10 9 9c45 41 102 64 163 64 60 0 118-23 163-64l9-9 10 10 32 32 10 9-10 10c-58 54-134 84-214 84z m141-160c-38 35-88 54-141 54l-9 0 0 0c-49-2-96-21-132-54l-11-10 10-10 33-32 9-9 10 8c25 22 57 35 90 35 33 0 65-13 91-35l9-8 9 9 33 32 10 10z m-141-160l10 9 53 53 10 10-11 10c-16 11-33 20-62 20-29 0-45-10-61-20l-12-10 11-10 53-53z"/> -<glyph glyph-name="eye" unicode="" d="M256 384c-82 0-146-49-224-128 67-68 124-128 224-128 100 0 173 76 224 127-52 58-125 129-224 129z m0-219c-49 0-90 41-90 91 0 50 41 91 90 91 49 0 90-41 90-91 0-50-41-91-90-91z m0 123c0 8 3 15 8 21-3 0-5 0-8 0-29 0-52-24-52-53 0-29 23-53 52-53 29 0 52 24 52 53 0 2 0 5 0 7-6-4-12-7-20-7-18 0-32 14-32 32z"/> -<glyph glyph-name="ios-arrow-thin-left" unicode="" d="M190 162c3 3 3 8 0 12l-67 74 285 0c4 0 8 4 8 8 0 4-4 8-8 8l-285 0 67 74c3 4 3 8 0 12-3 3-9 3-12 0 0 0-79-87-80-88 0-1-2-3-2-6 0-3 2-5 2-6 1-1 80-88 80-88 2-1 4-2 6-2 2 0 4 1 6 2z"/> -<glyph glyph-name="ios-arrow-thin-right" unicode="" d="M322 162c-3 3-3 8 0 12l67 74-285 0c-4 0-8 4-8 8 0 4 4 8 8 8l285 0-67 74c-3 4-3 8 0 12 3 3 9 3 12 0 0 0 79-87 80-88 0-1 2-3 2-6 0-3-2-5-2-6-1-1-80-88-80-88-2-1-4-2-6-2-2 0-4 1-6 2z"/> -<glyph glyph-name="arrow-up-b" unicode="" d="M413 185l-2 2-136 156c-4 6-11 9-19 9-8 0-14-4-19-9l-136-156-2-3c-2-2-3-5-3-8 0-9 7-16 17-16l286 0c10 0 17 7 17 16 0 3-1 6-3 9z"/> -<glyph glyph-name="arrow-down-b" unicode="" d="M99 327l2-2 136-156c4-6 11-9 19-9 8 0 15 3 19 9l136 156 2 2c2 3 3 6 3 9 0 9-7 16-17 16l-286 0c-10 0-17-7-17-16 0-3 1-6 3-9z"/> -<glyph glyph-name="android-locate" unicode="" d="M256 336c-44 0-80-36-80-80 0-44 36-80 80-80 44 0 80 36 80 80 0 44-36 80-80 80z m191-59c-10 89-81 160-170 170l0 33-42 0 0-33c-89-10-160-81-170-170l-33 0 0-42 33 0c10-89 81-160 170-170l0-33 42 0 0 33c89 10 160 81 170 170l33 0 0 42z m-191-170c-82 0-149 67-149 149 0 82 67 149 149 149 82 0 149-67 149-149 0-82-67-149-149-149z"/> -<glyph glyph-name="android-close" unicode="" d="M405 375l-30 30-119-119-119 119-30-30 119-119-119-119 30-30 119 119 119-119 30 30-119 119z"/> -<glyph glyph-name="android-lock" unicode="" d="M376 326l-20 0 0 40c0 55-45 100-100 100-55 0-100-45-100-100l0-40-20 0c-22 0-40-18-40-40l0-200c0-22 18-40 40-40l240 0c22 0 40 18 40 40l0 200c0 22-18 40-40 40z m-120-182c-22 0-40 18-40 40 0 22 18 40 40 40 22 0 40-18 40-40 0-22-18-40-40-40z m62 182l-124 0 0 40c0 34 28 62 62 62 34 0 62-28 62-62z"/> -<glyph glyph-name="ios-copy" unicode="" d="M144 96l0 304-32 0 0-336 240 0 0 32z m181 352l-165 0 0-336 240 0 0 261z m43-112l-80 0 0 80 16 0 0-64 64 0z"/> -<glyph glyph-name="location" unicode="" d="M256 448c-66 0-119-54-119-120 0-115 119-264 119-264 0 0 119 149 119 264 0 66-53 120-119 120z m0-178c-31 0-56 25-56 56 0 32 25 57 56 57 31 0 56-25 56-57 0-31-25-56-56-56z"/> -<glyph glyph-name="android-remove" unicode="" d="M96 277l320 0 0-42-320 0z"/> -<glyph glyph-name="ios-person" unicode="" d="M363 148c-13 5-31 6-43 9-7 1-17 5-20 9-3 4-1 41-1 41 0 0 6 10 9 18 4 8 7 31 7 31 0 0 7 0 9 12 3 13 7 19 6 28 0 9-5 10-5 10 0 0 5 13 5 42 1 34-25 68-74 68-49 0-75-34-74-68 0-28 5-42 5-42 0 0-5-1-5-10-1-9 3-14 6-27 2-12 9-12 9-12 0 0 4-24 7-32 3-8 9-18 9-18 0 0 2-37-1-41-3-4-13-8-20-9-12-3-30-4-43-9-13-5-53-20-53-52l320 0c0 32-40 47-53 52z"/> -</font></defs></svg> diff --git a/assets/icons/fonts/hopglass.ttf b/assets/icons/fonts/hopglass.ttf deleted file mode 100644 index d117d0cd3e44fa916e7e29fbe10a9d730601881a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3184 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 diff --git a/assets/icons/fonts/hopglass.woff b/assets/icons/fonts/hopglass.woff deleted file mode 100644 index b1a185a83939c6c289fd9d803986ba16ec3af579..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2300 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 diff --git a/assets/icons/fonts/hopglass.woff2 b/assets/icons/fonts/hopglass.woff2 deleted file mode 100644 index cce227441683b22d72d300daabbcad81e633863a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1700 zcmV;V23z@ePew8T0RR9100yK04FCWD01S8l00vC}0RR9100000000000000000000 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 diff --git a/assets/icons/fonts/icon.ttf b/assets/icons/fonts/icon.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8b8d8221cc5bfa2d9530bb615bce4ea55a9ed94d GIT binary patch literal 3400 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 Z5|%vFi$kN4OFM^mc3*!_C~W*6@ox!-B0c~B literal 0 HcmV?d00001 diff --git a/assets/icons/fonts/icon.woff b/assets/icons/fonts/icon.woff new file mode 100644 index 0000000000000000000000000000000000000000..7cc19f45434c92dfaadee96d32043182541c9272 GIT binary patch literal 2472 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 DW%qBU literal 0 HcmV?d00001 diff --git a/assets/icons/fonts/icon.woff2 b/assets/icons/fonts/icon.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0377338e069c95378c91bcf8ce06f6efe4592d5a GIT binary patch literal 1760 zcmV<61|Ru%Pew8T0RR9100!Uy4FCWD01Z$800xNw0RR9100000000000000000000 z0000#Mn+Uk92y=5U;u+c5eN#5bcs*_HUcCAG7BsK1Rw>25(k1M8<q-DlrK_}>`RPY z4-#o;GTrMUBSoAW?*1nm8}}Xb__2Egpa`G=00000{r`FP_ob<$j`#-rKOHIqx*Q}u zsXn=ryaIRBVk7JZ;`#l3bM6-xLfX)bwvDJD5-Z#GjgE?L+dkv_aqab`bxF?!%9>gD zb_)ziqg`TOY<_N_2qdB+I1qARdHes(W`BH16r~D<P^BOXiK$%+EN`Wo8i=S(s5YYn zC882-%HJO}<LbOXFp~ko@EZ{`v(^89ztV{)kV>#p`S(pkZ&lF>F6Qxw=56s2S+N!0 zqjp8@I%C4K!?OSY0l-tqd<y_P8wgy8`w9RB5C8yypd%cFzJ%ja1+qg1%+i1?08p(S z`-d1-00agLBulvLd}!&aGy(k3bINB9Q_Kayr3Y{+q#&WNcHb>6Aogw~=K#clAp=<F z10+h8d5mJ8=N=mm)T~7pEv;`#14h_O7W3B`t#7xl`OHnMbS9lf$50d06jh)PU|<8B zYqAO%Pm@CGv8H$ad(cZr<I+U`!;=As1W<q=dX?qmgE$dU=S<0hM3X|?hQyT9xS5DR z&WEbf1Znj6XUu$1kjkK9b*aPm3e$<osgeZRgW!)!n<;Y56L?lc5}YgLSmrwUQQg&e zDUaIBq2Tik(ENsKFIM{1v#%f7di(wU_^+nIU*UKB>1|LH@A$j%J|T(Xy}i5m%EiIR zr^B}v9E-A%nJ*;KzEn2^R(3=Fj^Ph#E=GlFqoF5{E2)1`YqiD2s!(s@rgD>YrPWR) z?@=7#V*4lsB?!k}3^-Y|9Wi>;nQ{9EU;0<^O)yPk358c_Rff7YGSas0b`>_EjkVuf zKsc!+{K6jq;Clf0yNq36w{Wk*d*Ob(>s{YzZ{jOBi84z|f(q>MUX>}u1YShw0O-UM zeF~m21Bd9LYsZTC#2}k9uQ<OPGKU|{%JpDi#iKTtq87pga~QgA+QrBo8tb08%7XxZ zumWFCPL@|bJZQ;mg|7ir`=j}mtv+0;fvB$BR??DOd5tgoh7$O`PKSw0qR&!x`++&( zkl#a!*->R!y|X8zhwf>JCEbPy14nZFw4SAPYgoxATIrwgrqwOB57wn8uc_8lz3<XK zT6!SKXRjWkYw7+o7uHP0g5VQz^L%VfssFeCm+Zdku(wsE$gH<QKUWj*G5>@}(~+71 zs#=b<wE4AZHU9(6%i*nmXVX__;etYaXIF09csu*|H&-4|uI2x_Vk@zrf9qH)j9JDe zVCe}ke)c~!Z{Ic(l_fXur>@yPV>R`M+qCtBe0)2*j%-@CdJTKe=4}<*kKaj2`1Pz; zz~2Asm#j3y!TArKWIAmHTwnGT?KGrUl?@M<siKmjR7_$8P-m{S??1kvs|()}hQozq z0lcMtiAg8gP9!DzabE5r34Rx~-pkmFzQ=#}g2a%F%#0AIk0U}3&o;P~x(zH>)I7QY zorlp~5#HpT!9Dx1fhdUeXB+iry?%1n_E2$f`)Wm1S#7w+tx4cJWMy@BHgy3KQr-6E z?lh9Zo*lLirs9_G$!7R4`?Cye4V)lA>fKmnWqL;b^4A}w{;eNVkJE=IjSv_OMuMVX zVqgB9e9O$e38Ff*50QQxstMPPRL#-V3R=`lW)be3gr$BlRbR00bTJn2yL}FV1ziVn zhS|bDU{gxtvw>iyI1m#iU;Yz-hOY4l(3b#=#g)I_e>SOH9}C|I>b?Xfz~qp^2$M`W z!aBZ#wUZHmKlvyo1yDULkfbTTSnUWip}CyEM4nthIt%3_W^Uz5Y`mSP!i*OkqSu1p zo`F=k!bzN5M+F&jJ#G%j4McMLHb&fh;S>@MtV}VA6Ac5~u){hv>P&mLVaAHPdAJO; zbz0q1qq27u5+W{RcNr^YFzFacRebsJjLSJ&hNqJaPZoTdOC_z;1XaVLiyR?a%{bkd ziu3Wdkd<+IHlM|(r>12CIDA*Dj1xPU0wxVW*+W;S?6gZFPBW%Xo#8KF7AxT25><ls zS0EB8kqW7i25Ddan^K5I3}O+7LQp6QqgrKI^71B9C-P&uf~niVk5S_`v&T^6r_2@? z{duz8YI7;fbIr6<8n=&5@ndd_#VnIvyTz^_6-}~aH}Rt;6KxC_pOTzr#$GcejgPv_ z^=0$4<S|n{2TUp)cG1@^Ie8F8m(9NWN`7_6A^aM@bzE}H)hr5+=GWDHjt7iOA^-r^ C$3N-- literal 0 HcmV?d00001 diff --git a/assets/icons/hopglass-icons.css b/assets/icons/hopglass-icons.css index f3ab178..8cfb80e 100644 --- a/assets/icons/hopglass-icons.css +++ b/assets/icons/hopglass-icons.css @@ -1,8 +1,8 @@ @font-face { font-family: "Ionicons"; - src: url("fonts/hopglass.woff2") format("woff2"), - url("fonts/hopglass.woff") format("woff"), - url("fonts/hopglass.ttf") format("truetype"); + src: url("fonts/icon.woff2") format("woff2"), + url("fonts/icon.woff") format("woff"), + url("fonts/icon.ttf") format("truetype"); font-weight: normal; font-style: normal; @@ -90,3 +90,7 @@ .ion-ios-person:before { content: "\f47e"; } + +.ion-layer:before { + content: "\f229"; +} diff --git a/scss/_leaflet-layer.scss b/scss/_leaflet-layer.scss new file mode 100644 index 0000000..a22f74e --- /dev/null +++ b/scss/_leaflet-layer.scss @@ -0,0 +1,33 @@ +.leaflet-control-layers { + box-shadow: none; + border-radius: 0; + background: none; +} + +.leaflet-control-layers-toggle { + background: none; + &::before { + content: "\f229"; + display: inline-block; + font-family: "ionicons" !important; + font-size: 2.3rem; + speak: none; + text-rendering: auto; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #e32d6d; + } +} + +.leaflet-control-layers-expanded { + padding: 0; +} + +.leaflet-control-layers-list { + background: rgba(255, 255, 255, 0.9); + padding: 10px; + label { + cursor: pointer; + } +} diff --git a/scss/main.scss b/scss/main.scss index 0415212..e1dd673 100644 --- a/scss/main.scss +++ b/scss/main.scss @@ -3,6 +3,7 @@ @import '_base'; @import '_leaflet'; @import '_leaflet.label'; +@import '_leaflet-layer'; @import '_filters'; @import '_loader'; From 5c15ccf340e07945763a9daf889ea60a3bc6fbf9 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Thu, 26 May 2016 22:29:21 +0200 Subject: [PATCH 27/32] [TASK] Add scss to grunt watcher --- tasks/development.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/development.js b/tasks/development.js index d366a1c..e9b312e 100644 --- a/tasks/development.js +++ b/tasks/development.js @@ -18,7 +18,7 @@ module.exports = function (grunt) { options: { livereload: true }, - files: ["*.css", "app.js", "lib/**/*.js", "*.html"], + files: ["*.css", "app.js", "lib/**/*.js", "*.html", "scss/**/*.scss"], tasks: ["dev"] }, config: { From 9987e443f7e42f46c6c3b99062c6902ef8383d78 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xaver.maierhofer@xwissen.info> Date: Fri, 27 May 2016 23:59:01 +0200 Subject: [PATCH 28/32] [TASK] Use strict --- Gruntfile.js | 2 ++ app.js | 2 ++ bower.json | 2 +- lib/about.js | 2 ++ lib/container.js | 2 ++ lib/datadistributor.js | 2 ++ lib/filters/filtergui.js | 4 +++- lib/filters/genericnode.js | 2 ++ lib/filters/nodefilter.js | 4 +++- lib/forcegraph.js | 2 ++ lib/gui.js | 2 ++ lib/infobox/link.js | 2 ++ lib/infobox/location.js | 2 ++ lib/infobox/main.js | 2 ++ lib/infobox/node.js | 2 ++ lib/legend.js | 2 ++ lib/linklist.js | 2 ++ lib/locationmarker.js | 2 ++ lib/main.js | 2 ++ lib/map.js | 2 ++ lib/map/clientlayer.js | 1 + lib/map/labelslayer.js | 2 ++ lib/meshstats.js | 2 ++ lib/nodelist.js | 2 ++ lib/proportions.js | 1 + lib/router.js | 2 ++ lib/sidebar.js | 4 +++- lib/simplenodelist.js | 2 ++ lib/sorttable.js | 2 ++ lib/tabs.js | 4 +++- lib/title.js | 2 ++ lib/utils/helper.js | 4 +++- tasks/build.js | 2 ++ tasks/clean.js | 2 ++ tasks/development.js | 2 ++ tasks/linting.js | 2 ++ 36 files changed, 74 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 99fcd04..c508723 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,4 +1,6 @@ module.exports = function (grunt) { + "use strict"; + grunt.loadTasks("tasks"); grunt.registerTask("default", ["bower-install-simple", "lint", "copy", "sass", "postcss", "requirejs:default", "inline"]); diff --git a/app.js b/app.js index d6a1227..f2d4e3c 100644 --- a/app.js +++ b/app.js @@ -1,3 +1,5 @@ +"use strict"; + require.config({ baseUrl: "lib", paths: { diff --git a/bower.json b/bower.json index 69d1b13..8233913 100644 --- a/bower.json +++ b/bower.json @@ -18,7 +18,7 @@ "es6-shim": "~0.35.1", "almond": "~0.3.2", "d3": "~3.5.17", - "roboto-fontface": "~0.3.0", + "roboto-fontface": "~0.4.5", "virtual-dom": "~2.1.1", "leaflet-providers": "~1.1.10", "rbush": "https://github.com/mourner/rbush.git#~1.4.3" diff --git a/lib/about.js b/lib/about.js index 2a20ae5..9222794 100644 --- a/lib/about.js +++ b/lib/about.js @@ -1,4 +1,6 @@ define(function () { + "use strict"; + return function () { this.render = function (d) { var el = document.createElement("div"); diff --git a/lib/container.js b/lib/container.js index 4f32a84..d799167 100644 --- a/lib/container.js +++ b/lib/container.js @@ -1,4 +1,6 @@ define([], function () { + "use strict"; + return function (tag) { if (!tag) { tag = "div"; diff --git a/lib/datadistributor.js b/lib/datadistributor.js index 3edeaf6..ea79341 100644 --- a/lib/datadistributor.js +++ b/lib/datadistributor.js @@ -1,4 +1,6 @@ define(["filters/nodefilter"], function (NodeFilter) { + "use strict"; + return function () { var targets = []; var filterObservers = []; diff --git a/lib/filters/filtergui.js b/lib/filters/filtergui.js index 10629d7..4db1b4b 100644 --- a/lib/filters/filtergui.js +++ b/lib/filters/filtergui.js @@ -1,4 +1,6 @@ -define([], function () { +define(function () { + "use strict"; + return function (distributor) { var container = document.createElement("ul"); container.classList.add("filters"); diff --git a/lib/filters/genericnode.js b/lib/filters/genericnode.js index c4fe7a9..253c38b 100644 --- a/lib/filters/genericnode.js +++ b/lib/filters/genericnode.js @@ -1,4 +1,6 @@ define(["helper"], function (helper) { + "use strict"; + return function (name, key, value, f) { var negate = false; var refresh; diff --git a/lib/filters/nodefilter.js b/lib/filters/nodefilter.js index 1d6bf5f..68da159 100644 --- a/lib/filters/nodefilter.js +++ b/lib/filters/nodefilter.js @@ -1,4 +1,6 @@ -define([], function () { +define(function () { + "use strict"; + return function (filter) { return function (data) { var n = Object.create(data); diff --git a/lib/forcegraph.js b/lib/forcegraph.js index 645b053..a113345 100644 --- a/lib/forcegraph.js +++ b/lib/forcegraph.js @@ -1,4 +1,6 @@ define(["d3", "helper"], function (d3, helper) { + "use strict"; + var margin = 200; var NODE_RADIUS = 15; var LINE_RADIUS = 7; diff --git a/lib/gui.js b/lib/gui.js index f8427ed..10c3bb2 100644 --- a/lib/gui.js +++ b/lib/gui.js @@ -5,6 +5,8 @@ define(["chroma-js", "map", "sidebar", "tabs", "container", "meshstats", function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist, Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph, Title, About, DataDistributor, FilterGUI) { + "use strict"; + return function (config, router) { var self = this; var content; diff --git a/lib/infobox/link.js b/lib/infobox/link.js index d24e144..8cfe683 100644 --- a/lib/infobox/link.js +++ b/lib/infobox/link.js @@ -1,4 +1,6 @@ define(["helper"], function (helper) { + "use strict"; + function showStatImg(o, d) { var subst = {}; subst["{SOURCE}"] = d.source.node_id; diff --git a/lib/infobox/location.js b/lib/infobox/location.js index 4695ac4..fe40011 100644 --- a/lib/infobox/location.js +++ b/lib/infobox/location.js @@ -1,4 +1,6 @@ define(["helper"], function (helper) { + "use strict"; + return function (config, el, router, d) { var sidebarTitle = document.createElement("h2"); sidebarTitle.textContent = "Location: " + d.toString(); diff --git a/lib/infobox/main.js b/lib/infobox/main.js index 8f1ef4a..9690a2a 100644 --- a/lib/infobox/main.js +++ b/lib/infobox/main.js @@ -1,4 +1,6 @@ define(["infobox/link", "infobox/node", "infobox/location"], function (Link, Node, Location) { + "use strict"; + return function (config, sidebar, router) { var self = this; var el; diff --git a/lib/infobox/node.js b/lib/infobox/node.js index 6839206..b6fcdac 100644 --- a/lib/infobox/node.js +++ b/lib/infobox/node.js @@ -1,5 +1,7 @@ define(["moment", "tablesort", "helper", "moment.de"], function (moment, Tablesort, helper) { + "use strict"; + function showGeoURI(d) { function showLatitude(d) { var suffix = Math.sign(d) > -1 ? "' N" : "' S"; diff --git a/lib/legend.js b/lib/legend.js index b01782f..20c0e97 100644 --- a/lib/legend.js +++ b/lib/legend.js @@ -1,4 +1,6 @@ define(function () { + "use strict"; + return function () { var self = this; diff --git a/lib/linklist.js b/lib/linklist.js index 661b777..0bf6d7e 100644 --- a/lib/linklist.js +++ b/lib/linklist.js @@ -1,4 +1,6 @@ define(["sorttable", "virtual-dom", "helper"], function (SortTable, V, helper) { + "use strict"; + function linkName(d) { return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + d.target.node.nodeinfo.hostname; } diff --git a/lib/locationmarker.js b/lib/locationmarker.js index 0f8c35a..a4a596f 100644 --- a/lib/locationmarker.js +++ b/lib/locationmarker.js @@ -1,4 +1,6 @@ define(["leaflet"], function (L) { + "use strict"; + return L.CircleMarker.extend({ outerCircle: { stroke: false, diff --git a/lib/main.js b/lib/main.js index 36d421c..693191c 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,5 +1,7 @@ define(["moment", "router", "leaflet", "gui", "helper", "moment.de"], function (moment, Router, L, GUI, helper) { + "use strict"; + return function (config) { function handleData(data) { var dataNodes = {}; diff --git a/lib/map.js b/lib/map.js index f099fb6..0bc17cb 100644 --- a/lib/map.js +++ b/lib/map.js @@ -2,6 +2,8 @@ define(["map/clientlayer", "map/labelslayer", "d3", "leaflet", "moment", "locationmarker", "rbush", "helper", "leaflet.label", "leaflet.providers", "moment.de"], function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush, helper) { + "use strict"; + var options = { worldCopyJump: true, zoomControl: false diff --git a/lib/map/clientlayer.js b/lib/map/clientlayer.js index 7579f4e..1dc8cb7 100644 --- a/lib/map/clientlayer.js +++ b/lib/map/clientlayer.js @@ -1,5 +1,6 @@ define(["leaflet"], function (L) { + "use strict"; return L.TileLayer.Canvas.extend({ setData: function (d) { diff --git a/lib/map/labelslayer.js b/lib/map/labelslayer.js index a6af979..d54c4af 100644 --- a/lib/map/labelslayer.js +++ b/lib/map/labelslayer.js @@ -1,5 +1,7 @@ define(["leaflet", "rbush"], function (L, rbush) { + "use strict"; + var labelLocations = [["left", "middle", 0 / 8], ["center", "top", 6 / 8], ["right", "middle", 4 / 8], diff --git a/lib/meshstats.js b/lib/meshstats.js index bc6c025..dd1d1f7 100644 --- a/lib/meshstats.js +++ b/lib/meshstats.js @@ -1,4 +1,6 @@ define(["helper"], function (helper) { + "use strict"; + return function (config) { var self = this; var stats, timestamp; diff --git a/lib/nodelist.js b/lib/nodelist.js index 12687c4..471aba4 100644 --- a/lib/nodelist.js +++ b/lib/nodelist.js @@ -1,4 +1,6 @@ define(["sorttable", "virtual-dom", "helper"], function (SortTable, V, helper) { + "use strict"; + function getUptime(now, d) { if (d.flags.online && "uptime" in d.statistics) { return Math.round(d.statistics.uptime); diff --git a/lib/proportions.js b/lib/proportions.js index 391145e..d8a9cda 100644 --- a/lib/proportions.js +++ b/lib/proportions.js @@ -1,5 +1,6 @@ define(["chroma-js", "virtual-dom", "filters/genericnode", "helper"], function (Chroma, V, Filter, helper) { + "use strict"; return function (config, filterManager) { var self = this; diff --git a/lib/router.js b/lib/router.js index d841d32..7c9fc4a 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,4 +1,6 @@ define(["helper"], function (helper) { + "use strict"; + return function () { var self = this; var objects = {nodes: {}, links: {}}; diff --git a/lib/sidebar.js b/lib/sidebar.js index 4c839cc..f644c6e 100644 --- a/lib/sidebar.js +++ b/lib/sidebar.js @@ -1,4 +1,6 @@ -define([], function () { +define(function () { + "use strict"; + return function (el) { var self = this; diff --git a/lib/simplenodelist.js b/lib/simplenodelist.js index 2ba0207..be480d1 100644 --- a/lib/simplenodelist.js +++ b/lib/simplenodelist.js @@ -1,4 +1,6 @@ define(["moment", "virtual-dom", "helper", "moment.de"], function (moment, V, helper) { + "use strict"; + return function (nodes, field, router, title) { var self = this; var el, tbody; diff --git a/lib/sorttable.js b/lib/sorttable.js index 881efba..2fc7353 100644 --- a/lib/sorttable.js +++ b/lib/sorttable.js @@ -1,4 +1,6 @@ define(["virtual-dom"], function (V) { + "use strict"; + return function (headings, sortIndex, renderRow) { var data; var sortReverse = false; diff --git a/lib/tabs.js b/lib/tabs.js index f4fed6d..701af29 100644 --- a/lib/tabs.js +++ b/lib/tabs.js @@ -1,4 +1,6 @@ -define([], function () { +define(function () { + "use strict"; + return function () { var self = this; diff --git a/lib/title.js b/lib/title.js index 4c99c2c..243c6bd 100644 --- a/lib/title.js +++ b/lib/title.js @@ -1,4 +1,6 @@ define(function () { + "use strict"; + return function (config) { function setTitle(d) { var title = [config.siteName]; diff --git a/lib/utils/helper.js b/lib/utils/helper.js index e3dce69..7a568c3 100644 --- a/lib/utils/helper.js +++ b/lib/utils/helper.js @@ -1,3 +1,5 @@ +"use strict"; + define({ get: function (url) { return new Promise(function (resolve, reject) { @@ -77,7 +79,7 @@ define({ }, listReplace: function (s, subst) { - for (key in subst) { + for (var key in subst) { var re = new RegExp(key, "g"); s = s.replace(re, subst[key]); } diff --git a/tasks/build.js b/tasks/build.js index 3a70fe8..4701cfb 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -1,4 +1,6 @@ module.exports = function (grunt) { + "use strict"; + grunt.config.merge({ bowerdir: "bower_components", copy: { diff --git a/tasks/clean.js b/tasks/clean.js index ed0e234..67181e0 100644 --- a/tasks/clean.js +++ b/tasks/clean.js @@ -1,4 +1,6 @@ module.exports = function (grunt) { + "use strict"; + grunt.config.merge({ clean: { build: ["build/**/*", "node_modules/grunt-newer/.cache"] diff --git a/tasks/development.js b/tasks/development.js index e9b312e..938cd87 100644 --- a/tasks/development.js +++ b/tasks/development.js @@ -1,4 +1,6 @@ module.exports = function (grunt) { + "use strict"; + grunt.config.merge({ connect: { server: { diff --git a/tasks/linting.js b/tasks/linting.js index 5409fd5..6667d68 100644 --- a/tasks/linting.js +++ b/tasks/linting.js @@ -1,4 +1,6 @@ module.exports = function (grunt) { + "use strict"; + grunt.config.merge({ checkDependencies: { options: { From 4c26a8c2c9151a8a2ea8b3e4004a6015d81b2c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20P=C3=A4ssler?= <me@petabyteboy.de> Date: Sat, 18 Mar 2017 16:29:30 +0100 Subject: [PATCH 29/32] [TASK] Add required information into package.json --- package.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/package.json b/package.json index caa6d5c..288b031 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,13 @@ { "name": "hopglass", + "license": "AGPL-3.0", + "repository": { + "type": "git", + "url": "https://github.com/hopglass/hopglass.git" + }, + "bugs": { + "url": "https://github.com/hopglass/hopglass/issues" + }, "scripts": { "test": "node -e \"require('grunt').cli()\" '' clean lint" }, From 4171594782482986121d1b0a59b63d8b2d0ff036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20P=C3=A4ssler?= <me@petabyteboy.de> Date: Sat, 18 Mar 2017 16:29:47 +0100 Subject: [PATCH 30/32] [BUGFIX] bower package name should be lowercase --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 8233913..18464c2 100644 --- a/bower.json +++ b/bower.json @@ -1,5 +1,5 @@ { - "name": "HopGlass", + "name": "hopglass", "ignore": [ "node_modules", "bower_components", From 27704d4e3e03f201b08423b502d55de1efd4004c Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer <xmaierhofer@1drop.de> Date: Sun, 29 May 2016 00:50:48 +0200 Subject: [PATCH 31/32] [TASK] Update eslint to 2.11.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 288b031..8996160 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "autoprefixer": "^6.3.6", - "eslint": "^2.10.2", + "eslint": "^2.11.0", "eslint-config-defaults": "^9.0.0", "grunt": "^1.0.1", "grunt-bower-install-simple": "^1.2.3", From bdde93a2c8c7fa4ef58657b692a404b2657a024b Mon Sep 17 00:00:00 2001 From: Milan Paessler <me@petabyteboy.de> Date: Tue, 21 Mar 2017 22:56:16 +0100 Subject: [PATCH 32/32] [TASK] travis: bump node version --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 50cef3f..5f3eab1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: node_js +node_js: node before_install: - gem install sass - npm install -g grunt-cli