From 88bc4aafc5624bcf2950281592b961337c6004fd Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Tue, 7 Apr 2015 17:41:17 +0200 Subject: [PATCH] nodelist/linklist: sortable tables --- lib/linklist.js | 111 +++++++++++++++-------------------- lib/nodelist.js | 149 +++++++++++++++++++++-------------------------- lib/sorttable.js | 57 ++++++++++++++++++ tasks/linting.js | 3 +- 4 files changed, 174 insertions(+), 146 deletions(-) create mode 100644 lib/sorttable.js diff --git a/lib/linklist.js b/lib/linklist.js index bdcf01a..1484721 100644 --- a/lib/linklist.js +++ b/lib/linklist.js @@ -1,73 +1,56 @@ -define(["virtual-dom"], - function (V) { - return function(linkScale, router) { - var self = this - var el, tbody +define(["sorttable", "virtual-dom"], function (SortTable, V) { + function linkName(d) { + return d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + } - self.render = function (d) { - el = document.createElement("div") - d.appendChild(el) + 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 + }] + + 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))] + + if (d.vpn) + td1Content.push(" (VPN)") + + 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)) + + return V.h("tr", [td1, td2, td3]) } - self.setData = function (data) { - if (data.graph.links.length === 0) - return + this.render = function (d) { + var el = document.createElement("div") + el.last = V.h("div") + d.appendChild(el) - if (!tbody) { - var h2 = document.createElement("h2") - h2.textContent = "Verbindungen" - el.appendChild(h2) + var h2 = document.createElement("h2") + h2.textContent = "Verbindungen" + el.appendChild(h2) - var table = document.createElement("table") - el.appendChild(table) + el.appendChild(table.el) + } - 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) - - var th3 = document.createElement("th") - th3.textContent = "Entfernung" - tr.appendChild(th3) - - thead.appendChild(tr) - table.appendChild(thead) - - tbody = document.createElement("tbody") - tbody.last = V.h("tbody") - table.appendChild(tbody) - } - - 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 - - return b - a - }) - - 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)] - - if (d.vpn) - td1Content.push(" (VPN)") - - 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)) - - return V.h("tr", [td1, td2, td3]) - }) - - 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) } } }) diff --git a/lib/nodelist.js b/lib/nodelist.js index 93ef47b..1ab4113 100644 --- a/lib/nodelist.js +++ b/lib/nodelist.js @@ -1,95 +1,82 @@ -define(["virtual-dom"], - function (V) { +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) + } + + function showUptime(uptime) { + var s = "" + + if (uptime !== undefined) + if (Math.abs(uptime) >= 24) + s = Math.round(uptime / 24) + "d" + else + s = uptime + "h" + + return s + } + + 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 + }] + 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) + function renderRow(d) { + var td1Content = [] + var aClass = ["hostname", d.flags.online ? "online" : "offline"] - var s = "" + td1Content.push(V.h("a", { className: aClass.join(" "), + onclick: router.node(d), + href: "#" + }, d.nodeinfo.hostname)) - if (uptime !== undefined) - if (Math.abs(uptime) >= 24) - s = Math.round(uptime / 24) + "d" - else - s = uptime + "h" + if (has_location(d)) + td1Content.push(V.h("span", {className: "icon ion-location"})) - return {v: s, sort: uptime !== undefined ? -uptime : 0} + 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")) + + return V.h("tr", [td1, td2, td3]) } - var self = this - var el, tbody + var table = new SortTable(headings, 0, renderRow) - self.render = function (d) { - el = document.createElement("div") + this.render = function (d) { + var el = document.createElement("div") d.appendChild(el) + + var h2 = document.createElement("h2") + h2.textContent = "Alle Knoten" + el.appendChild(h2) + + el.appendChild(table.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) - - 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 = "Uptime" - tr.appendChild(th2) - - var th3 = document.createElement("th") - th3.textContent = "Clients" - tr.appendChild(th3) - - thead.appendChild(tr) - table.appendChild(thead) - - tbody = document.createElement("tbody") - tbody.last = V.h("tbody") - table.appendChild(tbody) - } - - var nodes = data.nodes.all.slice(0).sort( function (a, b) { - return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname) + 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 items = nodes.map( function (d) { - var td1Content = [] - var aClass = ["hostname", d.flags.online ? "online" : "offline"] - - td1Content.push(V.h("a", { className: aClass.join(" "), - onclick: router.node(d), - href: "#" - }, d.nodeinfo.hostname)) - - if (has_location(d)) - td1Content.push(V.h("span", {className: "icon ion-location"})) - - var uptime = showUptime(data.now, d) - - 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 : "") - - return V.h("tr", [td1, td2, td3]) - }) - - var tbodyNew = V.h("tbody", items) - tbody = V.patch(tbody, V.diff(tbody.last, tbodyNew)) - tbody.last = tbodyNew - } + table.setData(data) + } } }) diff --git a/lib/sorttable.js b/lib/sorttable.js new file mode 100644 index 0000000..d6a39a1 --- /dev/null +++ b/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 + } +}) diff --git a/tasks/linting.js b/tasks/linting.js index 974029d..8355fde 100644 --- a/tasks/linting.js +++ b/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: {