Nils Schneider 9 年 前
コミット
35c4690ad6
5 ファイル変更132 行追加3 行削除
  1. 1 0
      app.js
  2. 2 1
      bower.json
  3. 6 2
      lib/gui.js
  4. 98 0
      lib/proportions.js
  5. 25 0
      style.css

+ 1 - 0
app.js

@@ -7,6 +7,7 @@ require.config({
     "moment": "../bower_components/moment/min/moment-with-locales.min",
     "tablesort": "../bower_components/tablesort/tablesort.min",
     "tablesort.numeric": "../bower_components/tablesort/src/sorts/tablesort.numeric",
+    "chartjs": "../bower_components/chartjs/Chart",
     "helper": "../helper"
   },
   shim: {

+ 2 - 1
bower.json

@@ -19,7 +19,8 @@
     "roboto-slab-fontface": "*",
     "es6-shim": "~0.27.1",
     "almond": "~0.3.1",
-    "r.js": "~2.1.16"
+    "r.js": "~2.1.16",
+    "chartjs": "~1.0.2"
   },
   "authors": [
     "Nils Schneider <nils@nilsschneider.net>"

+ 6 - 2
lib/gui.js

@@ -1,7 +1,8 @@
 define([ "chroma-js", "map", "sidebar", "tabs", "container", "meshstats",
-         "linklist", "nodelist", "simplenodelist", "infobox/main" ],
+         "linklist", "nodelist", "simplenodelist", "infobox/main",
+         "proportions" ],
 function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Linklist,
-          Nodelist, SimpleNodelist, Infobox) {
+          Nodelist, SimpleNodelist, Infobox, Proportions) {
   return function (config, router) {
     var self = this
     var dataTargets = []
@@ -20,6 +21,7 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Linklist,
     var lostnodeslist = new SimpleNodelist(config, "lost", "lastseen", router, "Verschwundene Knoten")
     var nodelist = new Nodelist(router)
     var linklist = new Linklist(linkScale, router)
+    var statistics = new Proportions()
 
     dataTargets.push(map)
     dataTargets.push(meshstats)
@@ -27,6 +29,7 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Linklist,
     dataTargets.push(lostnodeslist)
     dataTargets.push(nodelist)
     dataTargets.push(linklist)
+    dataTargets.push(statistics)
 
     overview.add(meshstats)
     overview.add(newnodeslist)
@@ -36,6 +39,7 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Linklist,
     tabs.add("Übersicht", overview)
     tabs.add("Alle Knoten", nodelist)
     tabs.add("Verbindungen", linklist)
+    tabs.add("Statistiken", statistics)
 
     router.addTarget(infobox)
     router.addTarget(map)

+ 98 - 0
lib/proportions.js

@@ -0,0 +1,98 @@
+define(["chartjs", "chroma-js"], function (ChartJS, Chroma) {
+  return function () {
+    var self = this
+    var fwTable, hwTable, autoTable
+    var scale = Chroma.scale("YlGnBu").mode("lab")
+
+    function count(nodes, key, def, f) {
+      var dict = {}
+
+      nodes.forEach( function (d) {
+        var v = dictGet(d, key.slice(0))
+
+        if (f !== undefined)
+          v = f(v)
+
+        if (v === null)
+          v = def
+
+        dict[v] = 1 + (v in dict ? dict[v] : 0)
+      })
+
+      return Object.keys(dict).map(function (d) { return [d, dict[d]] })
+    }
+
+    function fillTable(table, data) {
+      var max = 0
+      data.forEach(function (d) {
+        if (d[1] > max)
+          max = d[1]
+      })
+
+      data.forEach(function (d) {
+        var v = d[1] / max
+        var row = document.createElement("tr")
+        var th = document.createElement("th")
+        var td = document.createElement("td")
+        var span = document.createElement("span")
+        th.textContent = d[0]
+        span.style.width = Math.round(v * 100) + "%"
+        span.style.backgroundColor = scale(v).hex()
+        var c1 = Chroma.contrast(scale(v), "white")
+        var c2 = Chroma.contrast(scale(v), "black")
+        span.style.color = c1 > c2 ? "white" : "black"
+        span.textContent = d[1]
+        td.appendChild(span)
+        row.appendChild(th)
+        row.appendChild(td)
+        table.appendChild(row)
+      })
+    }
+
+    self.setData = function (data) {
+      var nodes = data.nodes.all.filter(online).concat(data.nodes.lost)
+
+      var fwDict = count(nodes, ["nodeinfo", "software", "firmware", "release"], "n/a")
+      var hwDict = count(nodes, ["nodeinfo", "hardware", "model"], "n/a")
+      var autoDict = count(nodes, ["nodeinfo", "software", "autoupdater"], "deaktiviert", function (d) {
+        if (d === null || !d.enabled)
+          return null
+        else
+          return d.branch
+      })
+
+      fillTable(fwTable, fwDict.sort(function (a, b) { return b[1] - a[1] }))
+      fillTable(hwTable, hwDict.sort(function (a, b) { return b[1] - a[1] }))
+      fillTable(autoTable, autoDict.sort(function (a, b) { return b[1] - a[1] }))
+    }
+
+    self.render = function (el) {
+      var h2
+      h2 = document.createElement("h2")
+      h2.textContent = "Firmwareversionen"
+      el.appendChild(h2)
+
+      fwTable = document.createElement("table")
+      fwTable.classList.add("proportion")
+      el.appendChild(fwTable)
+
+      h2 = document.createElement("h2")
+      h2.textContent = "Hardwaremodelle"
+      el.appendChild(h2)
+
+      hwTable = document.createElement("table")
+      hwTable.classList.add("proportion")
+      el.appendChild(hwTable)
+
+      h2 = document.createElement("h2")
+      h2.textContent = "Autoupdater"
+      el.appendChild(h2)
+
+      autoTable = document.createElement("table")
+      autoTable.classList.add("proportion")
+      el.appendChild(autoTable)
+    }
+
+    return self
+  }
+})

+ 25 - 0
style.css

@@ -237,6 +237,31 @@ button.close:after {
   animation-iteration-count: infinite;
 }
 
+.proportion th {
+  font-weight: normal;
+  text-align: right !important;
+  font-size: 0.95em;
+}
+
+.proportion td {
+  text-align: left !important;
+  width: 100%;
+}
+
+.proportion td, .proportion th {
+  white-space: nowrap;
+}
+
+.proportion span {
+  display: inline-block;
+  height: 1.4em;
+  background: black;
+  padding: 0 0.5em;
+  font-weight: bold;
+  min-width: 1.5em;
+  box-sizing: border-box;
+}
+
 @-webkit-keyframes blink {
   0%   { opacity: 1.0; }
   80%  { opacity: 1.0; }