diff --git a/history.html b/history.html index f5b25e3..d840de0 100644 --- a/history.html +++ b/history.html @@ -12,11 +12,33 @@ font-size: 11pt; } + table.attributes { + width: auto !important; + } + + table.attributes th { + text-align: left; + font-weight: bold; + vertical-align: top; + padding-right: 1em; + white-space: nowrap; + } + + table.attributes td { + text-align: left !important; + } + + #nodeinfo .clients { + font-family: "ionicons"; + color: #1566A9; + word-spacing: -0.2em; + } + #nodeinfo { position: relative; box-shadow: 0px 0.5px 3px rgba(0, 0, 0, 0.16), 0px 0.5px 2px rgba(0, 0, 0, 0.24); background: rgba(0, 0, 0, 0.02); - padding-top: 0.25em; + padding: 0.25em 0; } #nodeinfo.hidden { @@ -58,11 +80,11 @@ content: "\f12a"; } - #sidebar h2 { + #sidebar h2, #sidebar h3 { padding: 0 10pt; } - #sidebar p, #sidebar table, #sidebar pre { + #sidebar p, #sidebar table, #sidebar pre, #sidebar ul { padding: 0 10pt 1em; } @@ -86,11 +108,11 @@ .hostname { } - .hostname.online { + .online { color: #558020 !important; } - .hostname.offline { + .offline { color: #D43E2A !important; } @@ -135,6 +157,28 @@ color: #1566A9; } + .bar { + display: inline-block; + width: 100%; + height: 1.4em; + background: rgba(85, 128, 32, 0.5); + position: relative; + } + + .bar span { + display: inline-block; + height: 1.4em; + background: rgba(85, 128, 32, 0.8); + } + + .bar label { + font-weight: bold; + white-space: nowrap; + color: white; + position: absolute; + right: 0.5em; + } + @media screen and (max-width: 80em) { #sidebar { font-size: 0.8em; diff --git a/history.js b/history.js index 1d672d8..a4b0c67 100644 --- a/history.js +++ b/history.js @@ -392,17 +392,146 @@ function showNodeinfo(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 pre = document.createElement("pre") - pre.textContent = JSON.stringify(d, null, ' ') - el.appendChild(pre) + var attributes = document.createElement("table") + attributes.classList.add("attributes") + + attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null) + attributeEntry(attributes, "In der Karte", "location" in d.nodeinfo ? "ja" : "nein") + 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) + function destroy() { el.classList.add("hidden") while (el.hasChildNodes()) el.removeChild(el.childNodes[0]) } + + 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 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 } function showLinkinfo(d) { @@ -431,3 +560,15 @@ function gotoBuilder(markers, nodes, links) { link: function (d) { return function () { return gotoLink(d) }} } } + +function dictGet(dict, key) { + var k = key.shift() + + if (!(k in dict)) + return null + + if (key.length == 0) + return dict[k] + + return dictGet(dict[k], key) +}