diff --git a/helper.js b/helper.js index 3bfdeeb..844c122 100644 --- a/helper.js +++ b/helper.js @@ -108,3 +108,28 @@ function linkId(d) { return ids.sort().join("-") } + +/* 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") + th.textContent = label + 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 +} diff --git a/lib/history.js b/lib/history.js index 9ad77a1..ea4727a 100644 --- a/lib/history.js +++ b/lib/history.js @@ -1,4 +1,4 @@ -require(["map"], function (Map) { +require(["map", "infobox/main"], function (Map, Infobox) { main() function main() { @@ -9,11 +9,15 @@ require(["map"], function (Map) { var sidebar = mkSidebar(document.body) - var map = new Map(sidebar) - document.body.insertBefore(map.div, document.body.firstChild) + var gotoAnything = new gotoBuilder(config) + + var infobox = new Infobox(config, sidebar, gotoAnything) + gotoAnything.addTarget(infobox) + + var map = new Map(sidebar, gotoAnything) + document.body.insertBefore(map.div, document.body.firstChild) + gotoAnything.addTarget(map) - var infobox = new Infobox(sidebar) - var gotoAnything = new gotoBuilder(config, infobox, showNodeinfo, showLinkinfo) var urls = [ config.dataPath + 'nodes.json', config.dataPath + 'graph.json' @@ -90,9 +94,7 @@ require(["map"], function (Map) { d.target.node.neighbours.push({ node: d.source.node, link: d }) }) - map.setData(linkScale, sidebar, now, newnodes, lostnodes, onlinenodes, links, gotoAnything) - - gotoAnything.addTarget(map) + map.setData(linkScale, sidebar, now, newnodes, lostnodes, onlinenodes, links) showMeshstats(sidebar, nodes) mkNodesList(sidebar, config.showContact, "firstseen", gotoAnything.node, "Neue Knoten", newnodes) @@ -281,297 +283,6 @@ require(["map"], function (Map) { el.appendChild(p) } - function Infobox(sidebar) { - var gotoAnything - var self = this - el = undefined - - function close() { - gotoAnything.reset() - destroy() - pushHistory() - } - - function destroy() { - if (el && el.parentNode) { - el.parentNode.removeChild(el) - el = undefined - } - } - - self.create = function () { - destroy() - - el = document.createElement("div") - sidebar.insertBefore(el, sidebar.firstChild) - - el.scrollIntoView(false) - el.classList.add("infobox") - el.close = close - el.destroy = destroy - - var closeButton = document.createElement("button") - closeButton.classList.add("close") - closeButton.onclick = close - el.appendChild(closeButton) - - return el - } - - self.setGoto = function (d) { - gotoAnything = d - } - - return self - } - - function showNodeinfo(config, infobox, gotoAnything, d) { - var el = infobox.create() - - var h2 = document.createElement("h2") - h2.textContent = d.nodeinfo.hostname - var span = document.createElement("span") - span.classList.add(d.flags.online ? "online" : "offline") - span.textContent = " (" + (d.flags.online ? "online" : "offline, " + d.lastseen.fromNow(true)) + ")" - h2.appendChild(span) - el.appendChild(h2) - - var attributes = document.createElement("table") - attributes.classList.add("attributes") - - attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null) - attributeEntry(attributes, "In der Karte", has_location(d) ? "ja" : "nein") - - 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, "Firmware", showFirmware(d)) - attributeEntry(attributes, "Uptime", showUptime(d)) - attributeEntry(attributes, "Teil des Netzes", showFirstseen(d)) - attributeEntry(attributes, "Arbeitsspeicher", showRAM(d)) - attributeEntry(attributes, "IP Adressen", showIPs(d)) - attributeEntry(attributes, "Clients", showClients(d)) - el.appendChild(attributes) - - if (d.neighbours.length > 0) { - var h3 = document.createElement("h3") - h3.textContent = "Nachbarknoten (" + 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 = "Knoten" - th1.classList.add("sort-default") - tr.appendChild(th1) - - var th2 = document.createElement("th") - th2.textContent = "TQ" - tr.appendChild(th2) - - var th3 = document.createElement("th") - th3.textContent = "Entfernung" - tr.appendChild(th3) - - thead.appendChild(tr) - table.appendChild(thead) - - var tbody = document.createElement("tbody") - - d.neighbours.forEach( function (d) { - var tr = document.createElement("tr") - - var td1 = document.createElement("td") - var a1 = document.createElement("a") - a1.classList.add("hostname") - a1.textContent = d.node.nodeinfo.hostname - a1.href = "#" - a1.onclick = gotoAnything.node(d.node) - td1.appendChild(a1) - - if (d.link.vpn) - td1.appendChild(document.createTextNode(" (VPN)")) - - if (has_location(d.node)) { - var span = document.createElement("span") - span.classList.add("icon") - span.classList.add("ion-location") - td1.appendChild(span) - } - - tr.appendChild(td1) - - var td2 = document.createElement("td") - var a2 = document.createElement("a") - a2.href = "#" - a2.textContent = showTq(d.link) - a2.onclick = gotoAnything.link(d.link) - td2.appendChild(a2) - tr.appendChild(td2) - - var td3 = document.createElement("td") - var a3 = document.createElement("a") - a3.href = "#" - a3.textContent = showDistance(d.link) - a3.onclick = gotoAnything.link(d.link) - td3.appendChild(a3) - td3.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1) - tr.appendChild(td3) - - tbody.appendChild(tr) - }) - - table.appendChild(tbody) - - 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 - - return release + " / " + base - } - - function showUptime(d) { - if (!("uptime" in d.statistics)) - return - - return moment.duration(d.statistics.uptime, "seconds").humanize() - } - - function showFirstseen(d) { - if (!("firstseen" in d)) - return - - return d.firstseen.fromNow(true) - } - - function showClients(d) { - if (!d.flags.online) - return - - return function (el) { - el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine")) - el.appendChild(document.createElement("br")) - - var span = document.createElement("span") - span.classList.add("clients") - span.textContent = " ".repeat(d.statistics.clients) - el.appendChild(span) - } - } - - function showIPs(d) { - var ips = dictGet(d.nodeinfo, ["network", "addresses"]) - if (ips === null) - return - - ips.sort() - - return function (el) { - ips.forEach( function (ip, i) { - var link = !ip.startsWith("fe80:") - - if (i > 0) - el.appendChild(document.createElement("br")) - - if (link) { - var a = document.createElement("a") - a.href = "http://[" + ip + "]/" - a.textContent = ip - el.appendChild(a) - } else - el.appendChild(document.createTextNode(ip)) - }) - } - } - - function showRAM(d) { - if (!("memory_usage" in d.statistics)) - return - - return function (el) { - el.appendChild(showBar("memory-usage", d.statistics.memory_usage)) - } - } - } - - function attributeEntry(el, label, value) { - if (value === null || value == undefined) - return - - var tr = document.createElement("tr") - var th = document.createElement("th") - th.textContent = label - 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 - } - - 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 showLinkinfo(config, infobox, gotoAnything, d) { - var el = infobox.create() - - var h2 = document.createElement("h2") - a1 = document.createElement("a") - a1.href = "#" - a1.onclick = gotoAnything.node(d.source.node) - a1.textContent = d.source.node.nodeinfo.hostname - h2.appendChild(a1) - h2.appendChild(document.createTextNode(" – ")) - a2 = document.createElement("a") - a2.href = "#" - a2.onclick = gotoAnything.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") - - attributeEntry(attributes, "TQ", showTq(d)) - attributeEntry(attributes, "Entfernung", showDistance(d)) - attributeEntry(attributes, "VPN", d.vpn ? "ja" : "nein") - - el.appendChild(attributes) - } - function pushHistory(d) { var s = "#!" @@ -609,16 +320,18 @@ require(["map"], function (Map) { } } - function gotoBuilder(config, infobox, nodes, links) { + function gotoBuilder(config, nodes) { var targets = [] var self = this - infobox.setGoto(self) + var infobox function resetView() { targets.forEach( function (t) { t.resetView() }) + + pushHistory() } function gotoNode(d, showMap, push) { @@ -629,8 +342,6 @@ require(["map"], function (Map) { t.gotoNode(d) }) - nodes(config, infobox, self, d) - if (push) pushHistory( { node: d }) @@ -645,22 +356,20 @@ require(["map"], function (Map) { t.gotoLink(d) }) - links(config, infobox, self, d) - if (push) pushHistory( { link: d }) return false } - this.node = function (d, m, p) { return function () { return gotoNode(d, m, p) }} - this.link = function (d, m, p) { return function () { return gotoLink(d, m, p) }} - this.reset = resetView - this.addMarkers = function (d) { + self.node = function (d, m, p) { return function () { return gotoNode(d, m, p) }} + self.link = function (d, m, p) { return function () { return gotoLink(d, m, p) }} + self.reset = resetView + self.addMarkers = function (d) { markers = d } - this.addTarget = function (d) { targets.push(d) } + self.addTarget = function (d) { targets.push(d) } - return this + return self } }) diff --git a/lib/infobox/link.js b/lib/infobox/link.js new file mode 100644 index 0000000..94e870b --- /dev/null +++ b/lib/infobox/link.js @@ -0,0 +1,26 @@ +define(function () { + return function (config, el, gotoAnything, d) { + var h2 = document.createElement("h2") + a1 = document.createElement("a") + a1.href = "#" + a1.onclick = gotoAnything.node(d.source.node) + a1.textContent = d.source.node.nodeinfo.hostname + h2.appendChild(a1) + h2.appendChild(document.createTextNode(" – ")) + a2 = document.createElement("a") + a2.href = "#" + a2.onclick = gotoAnything.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") + + attributeEntry(attributes, "TQ", showTq(d)) + attributeEntry(attributes, "Entfernung", showDistance(d)) + attributeEntry(attributes, "VPN", d.vpn ? "ja" : "nein") + + el.appendChild(attributes) + } +}) diff --git a/lib/infobox/main.js b/lib/infobox/main.js new file mode 100644 index 0000000..68245ac --- /dev/null +++ b/lib/infobox/main.js @@ -0,0 +1,43 @@ +define(["infobox/link", "infobox/node"], function (Link, Node) { + return function (config, sidebar, gotoAnything) { + var self = this + el = undefined + + function destroy() { + if (el && el.parentNode) { + el.parentNode.removeChild(el) + el = undefined + } + } + + function create() { + destroy() + + el = document.createElement("div") + sidebar.insertBefore(el, sidebar.firstChild) + + el.scrollIntoView(false) + el.classList.add("infobox") + el.destroy = destroy + + var closeButton = document.createElement("button") + closeButton.classList.add("close") + closeButton.onclick = gotoAnything.reset + el.appendChild(closeButton) + } + + self.resetView = destroy + + self.gotoNode = function (d) { + create() + new Node(config, el, gotoAnything, d) + } + + self.gotoLink = function (d) { + create() + new Link(config, el, gotoAnything, d) + } + + return self + } +}) diff --git a/lib/infobox/node.js b/lib/infobox/node.js new file mode 100644 index 0000000..2ad419a --- /dev/null +++ b/lib/infobox/node.js @@ -0,0 +1,196 @@ +define(function () { + return function(config, el, gotoAnything, d) { + var h2 = document.createElement("h2") + h2.textContent = d.nodeinfo.hostname + var span = document.createElement("span") + span.classList.add(d.flags.online ? "online" : "offline") + span.textContent = " (" + (d.flags.online ? "online" : "offline, " + d.lastseen.fromNow(true)) + ")" + h2.appendChild(span) + el.appendChild(h2) + + var attributes = document.createElement("table") + attributes.classList.add("attributes") + + attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null) + attributeEntry(attributes, "In der Karte", has_location(d) ? "ja" : "nein") + + 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, "Firmware", showFirmware(d)) + attributeEntry(attributes, "Uptime", showUptime(d)) + attributeEntry(attributes, "Teil des Netzes", showFirstseen(d)) + attributeEntry(attributes, "Arbeitsspeicher", showRAM(d)) + attributeEntry(attributes, "IP Adressen", showIPs(d)) + attributeEntry(attributes, "Clients", showClients(d)) + el.appendChild(attributes) + + if (d.neighbours.length > 0) { + var h3 = document.createElement("h3") + h3.textContent = "Nachbarknoten (" + 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 = "Knoten" + th1.classList.add("sort-default") + tr.appendChild(th1) + + var th2 = document.createElement("th") + th2.textContent = "TQ" + tr.appendChild(th2) + + var th3 = document.createElement("th") + th3.textContent = "Entfernung" + tr.appendChild(th3) + + thead.appendChild(tr) + table.appendChild(thead) + + var tbody = document.createElement("tbody") + + d.neighbours.forEach( function (d) { + var tr = document.createElement("tr") + + var td1 = document.createElement("td") + var a1 = document.createElement("a") + a1.classList.add("hostname") + a1.textContent = d.node.nodeinfo.hostname + a1.href = "#" + a1.onclick = gotoAnything.node(d.node) + td1.appendChild(a1) + + if (d.link.vpn) + td1.appendChild(document.createTextNode(" (VPN)")) + + if (has_location(d.node)) { + var span = document.createElement("span") + span.classList.add("icon") + span.classList.add("ion-location") + td1.appendChild(span) + } + + tr.appendChild(td1) + + var td2 = document.createElement("td") + var a2 = document.createElement("a") + a2.href = "#" + a2.textContent = showTq(d.link) + a2.onclick = gotoAnything.link(d.link) + td2.appendChild(a2) + tr.appendChild(td2) + + var td3 = document.createElement("td") + var a3 = document.createElement("a") + a3.href = "#" + a3.textContent = showDistance(d.link) + a3.onclick = gotoAnything.link(d.link) + td3.appendChild(a3) + td3.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1) + tr.appendChild(td3) + + tbody.appendChild(tr) + }) + + table.appendChild(tbody) + + 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 + + return release + " / " + base + } + + function showUptime(d) { + if (!("uptime" in d.statistics)) + return + + return moment.duration(d.statistics.uptime, "seconds").humanize() + } + + function showFirstseen(d) { + if (!("firstseen" in d)) + return + + return d.firstseen.fromNow(true) + } + + function showClients(d) { + if (!d.flags.online) + return + + return function (el) { + el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine")) + el.appendChild(document.createElement("br")) + + var span = document.createElement("span") + span.classList.add("clients") + span.textContent = " ".repeat(d.statistics.clients) + el.appendChild(span) + } + } + + function showIPs(d) { + var ips = dictGet(d.nodeinfo, ["network", "addresses"]) + if (ips === null) + return + + ips.sort() + + return function (el) { + ips.forEach( function (ip, i) { + var link = !ip.startsWith("fe80:") + + if (i > 0) + el.appendChild(document.createElement("br")) + + if (link) { + var a = document.createElement("a") + a.href = "http://[" + ip + "]/" + a.textContent = ip + el.appendChild(a) + } else + el.appendChild(document.createTextNode(ip)) + }) + } + } + + function showRAM(d) { + if (!("memory_usage" in d.statistics)) + return + + return function (el) { + el.appendChild(showBar("memory-usage", d.statistics.memory_usage)) + } + } + + 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 + } + } +}) diff --git a/lib/map.js b/lib/map.js index 35bf2ed..91407cf 100644 --- a/lib/map.js +++ b/lib/map.js @@ -55,7 +55,7 @@ define(function () { var groupOnline, group - return function (sidebar) { + return function (sidebar, gotoAnything) { var self = this var el = document.createElement("div") @@ -76,7 +76,7 @@ define(function () { var nodeDict = {} var linkDict = {} - self.setData = function (linkScale, sidebar, now, newnodes, lostnodes, onlinenodes, links, gotoAnything) { + self.setData = function (linkScale, sidebar, now, newnodes, lostnodes, onlinenodes, links) { nodeDict = {} linkDict = {}