Browse Source

nodelist/linklist: sortable tables

Nils Schneider 9 years ago
parent
commit
88bc4aafc5
4 changed files with 166 additions and 138 deletions
  1. 43 60
      lib/linklist.js
  2. 64 77
      lib/nodelist.js
  3. 57 0
      lib/sorttable.js
  4. 2 1
      tasks/linting.js

+ 43 - 60
lib/linklist.js

@@ -1,73 +1,56 @@
-define(["virtual-dom"],
-  function (V) {
-  return function(linkScale, router) {
-    var self = this
-    var el, tbody
-
-    self.render = function (d)  {
-      el = document.createElement("div")
-      d.appendChild(el)
-    }
-
-    self.setData = function (data) {
-      if (data.graph.links.length === 0)
-        return
-
-      if (!tbody) {
-        var h2 = document.createElement("h2")
-        h2.textContent = "Verbindungen"
-        el.appendChild(h2)
-
-        var table = document.createElement("table")
-        el.appendChild(table)
-
-        var thead = document.createElement("thead")
-
-        var tr = document.createElement("tr")
-        var th1 = document.createElement("th")
-        th1.textContent = "Knoten"
-        tr.appendChild(th1)
-
-        var th2 = document.createElement("th")
-        th2.textContent = "TQ"
-        tr.appendChild(th2)
+define(["sorttable", "virtual-dom"], function (SortTable, V) {
+  function linkName(d) {
+    return d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname
+  }
 
-        var th3 = document.createElement("th")
-        th3.textContent = "Entfernung"
-        tr.appendChild(th3)
+  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: "Entfernung",
+                    sort: function (a, b) {
+                      return (a.distance === undefined ? -1 : a.distance) -
+                             (b.distance === undefined ? -1 : b.distance)
+                    },
+                    reverse: true
+                  }]
 
-        thead.appendChild(tr)
-        table.appendChild(thead)
+  return function(linkScale, router) {
+    var table = new SortTable(headings, 2, renderRow)
 
-        tbody = document.createElement("tbody")
-        tbody.last = V.h("tbody")
-        table.appendChild(tbody)
-      }
+    function renderRow(d) {
+      var td1Content = [V.h("a", {href: "#", onclick: router.link(d)}, linkName(d))]
 
-      var links = data.graph.links.slice(0).sort( function (a, b) {
-        a = a.distance === undefined ? -1 : a.distance
-        b = b.distance === undefined ? -1 : b.distance
+      if (d.vpn)
+        td1Content.push(" (VPN)")
 
-        return b - a
-      })
+      var td1 = V.h("td", td1Content)
+      var td2 = V.h("td", {style: {color: linkScale(d.tq).hex()}}, showTq(d))
+      var td3 = V.h("td", showDistance(d))
 
-      var items = links.map( function (d) {
-        var name = d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname
-        var td1Content = [V.h("a", {href: "#", onclick: router.link(d)}, name)]
+      return V.h("tr", [td1, td2, td3])
+    }
 
-        if (d.vpn)
-          td1Content.push(" (VPN)")
+    this.render = function (d)  {
+      var el = document.createElement("div")
+      el.last = V.h("div")
+      d.appendChild(el)
 
-        var td1 = V.h("td", td1Content)
-        var td2 = V.h("td", {style: {color: linkScale(d.tq).hex()}}, showTq(d))
-        var td3 = V.h("td", showDistance(d))
+      var h2 = document.createElement("h2")
+      h2.textContent = "Verbindungen"
+      el.appendChild(h2)
 
-        return V.h("tr", [td1, td2, td3])
-      })
+      el.appendChild(table.el)
+    }
 
-      var tbodyNew = V.h("tbody", items)
-      tbody = V.patch(tbody, V.diff(tbody.last, tbodyNew))
-      tbody.last = tbodyNew
+    this.setData = function (d) {
+      table.setData(d.graph.links)
     }
   }
 })

+ 64 - 77
lib/nodelist.js

@@ -1,95 +1,82 @@
-define(["virtual-dom"],
-  function (V) {
-  return function(router) {
-    function showUptime(now, d) {
-      var uptime
-      if (d.flags.online && "uptime" in d.statistics)
-        uptime = Math.round(d.statistics.uptime / 3600)
-      else if (!d.flags.online && "lastseen" in d)
-        uptime = Math.round(-(now - d.lastseen) / 3600000)
-
-      var s = ""
-
-      if (uptime !== undefined)
-        if (Math.abs(uptime) >= 24)
-          s = Math.round(uptime / 24) + "d"
-        else
-          s = uptime + "h"
-
-      return {v: s, sort: uptime !== undefined ? -uptime : 0}
-    }
-
-    var self = this
-    var el, tbody
-
-    self.render = function (d) {
-      el = document.createElement("div")
-      d.appendChild(el)
-    }
-
-    self.setData = function (data) {
-      if (data.nodes.all.length === 0)
-        return
-
-      if (!tbody) {
-        var h2 = document.createElement("h2")
-        h2.textContent = "Alle Knoten"
-        el.appendChild(h2)
+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 / 3600)
+    else if (!d.flags.online && "lastseen" in d)
+      return Math.round(-(now - d.lastseen) / 3600000)
+  }
 
-        var table = document.createElement("table")
-        el.appendChild(table)
+  function showUptime(uptime) {
+    var s = ""
 
-        var thead = document.createElement("thead")
+    if (uptime !== undefined)
+      if (Math.abs(uptime) >= 24)
+        s = Math.round(uptime / 24) + "d"
+      else
+        s = uptime + "h"
 
-        var tr = document.createElement("tr")
-        var th1 = document.createElement("th")
-        th1.textContent = "Knoten"
-        tr.appendChild(th1)
+    return s
+  }
 
-        var th2 = document.createElement("th")
-        th2.textContent = "Uptime"
-        tr.appendChild(th2)
+  var headings = [{ name: "Knoten",
+                    sort: function (a, b) {
+                      return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname)
+                    },
+                    reverse: false
+                  },
+                  { name: "Uptime",
+                    sort: function (a, b) { return a.uptime - b.uptime},
+                    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 th3 = document.createElement("th")
-        th3.textContent = "Clients"
-        tr.appendChild(th3)
+  return function(router) {
+    function renderRow(d) {
+      var td1Content = []
+      var aClass = ["hostname", d.flags.online ? "online" : "offline"]
 
-        thead.appendChild(tr)
-        table.appendChild(thead)
+      td1Content.push(V.h("a", { className: aClass.join(" "),
+                                 onclick: router.node(d),
+                                 href: "#"
+                               }, d.nodeinfo.hostname))
 
-        tbody = document.createElement("tbody")
-        tbody.last = V.h("tbody")
-        table.appendChild(tbody)
-      }
+      if (has_location(d))
+        td1Content.push(V.h("span", {className: "icon ion-location"}))
 
-      var nodes = data.nodes.all.slice(0).sort( function (a, b) {
-        return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname)
-      })
+      var td1 = V.h("td", td1Content)
+      var td2 = V.h("td", showUptime(d.uptime))
+      var td3 = V.h("td", numeral("clients" in d.statistics ? d.statistics.clients : "").format("0,0"))
 
-      var items = nodes.map( function (d) {
-        var td1Content = []
-        var aClass = ["hostname", d.flags.online ? "online" : "offline"]
+      return V.h("tr", [td1, td2, td3])
+    }
 
-        td1Content.push(V.h("a", { className: aClass.join(" "),
-                                   onclick: router.node(d),
-                                   href: "#"
-                                 }, d.nodeinfo.hostname))
+    var table = new SortTable(headings, 0, renderRow)
 
-        if (has_location(d))
-          td1Content.push(V.h("span", {className: "icon ion-location"}))
+    this.render = function (d) {
+      var el = document.createElement("div")
+      d.appendChild(el)
 
-        var uptime = showUptime(data.now, d)
+      var h2 = document.createElement("h2")
+      h2.textContent = "Alle Knoten"
+      el.appendChild(h2)
 
-        var td1 = V.h("td", td1Content)
-        var td2 = V.h("td", uptime.v)
-        var td3 = V.h("td", "clients" in d.statistics ? d.statistics.clients : "")
+      el.appendChild(table.el)
+    }
 
-        return V.h("tr", [td1, td2, td3])
+    this.setData = function (d) {
+      var data = d.nodes.all.map(function (e) {
+        var n = Object.create(e)
+        n.uptime = getUptime(d.now, e)
+        return n
       })
 
-      var tbodyNew = V.h("tbody", items)
-      tbody = V.patch(tbody, V.diff(tbody.last, tbodyNew))
-      tbody.last = tbodyNew
-   }
+      table.setData(data)
+    }
   }
 })

+ 57 - 0
lib/sorttable.js

@@ -0,0 +1,57 @@
+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")
+
+    function sortTable(i) {
+      sortReverse = i === sortIndex ? !sortReverse : false
+      sortIndex = i
+
+      updateView()
+    }
+
+    function sortTableHandler(i) {
+      return function () { sortTable(i) }
+    }
+
+    function updateView() {
+      var children = []
+
+      if (data.length !== 0) {
+        var th = headings.map(function (d, i) {
+          var properties = { onclick: sortTableHandler(i),
+                             className: "sort-header"
+                           }
+
+          if (sortIndex === i)
+            properties.className += sortReverse ? " sort-up" : " sort-down"
+
+          return V.h("th", properties, d.name)
+        })
+
+        var links = data.slice(0).sort(headings[sortIndex].sort)
+
+        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)))
+      }
+
+      var elNew = V.h("table", children)
+      el = V.patch(el, V.diff(elLast, elNew))
+      elLast = elNew
+    }
+
+    this.setData = function (d) {
+      data = d
+      updateView()
+    }
+
+    this.el = el
+
+    return this
+  }
+})

+ 2 - 1
tasks/linting.js

@@ -19,7 +19,8 @@ module.exports = function (grunt) {
           "strict": [2, "never"],
           "no-multi-spaces": 0,
           "no-new": 0,
-          "no-shadow": 0
+          "no-shadow": 0,
+          "no-use-before-define": [1, "nofunc"]
         }
       },
       sources: {