Merge branch 'autoreload'
This commit is contained in:
commit
88d0aa2f92
6
app.js
6
app.js
@ -10,6 +10,7 @@ require.config({
|
|||||||
"d3": "../bower_components/d3/d3.min",
|
"d3": "../bower_components/d3/d3.min",
|
||||||
"numeral": "../bower_components/numeraljs/min/numeral.min",
|
"numeral": "../bower_components/numeraljs/min/numeral.min",
|
||||||
"numeral-intl": "../bower_components/numeraljs/min/languages.min",
|
"numeral-intl": "../bower_components/numeraljs/min/languages.min",
|
||||||
|
"virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom",
|
||||||
"helper": "../helper"
|
"helper": "../helper"
|
||||||
},
|
},
|
||||||
shim: {
|
shim: {
|
||||||
@ -17,7 +18,10 @@ require.config({
|
|||||||
"tablesort": {
|
"tablesort": {
|
||||||
exports: "Tablesort"
|
exports: "Tablesort"
|
||||||
},
|
},
|
||||||
"numeral-intl": ["numeral"],
|
"numeral-intl": {
|
||||||
|
deps: ["numeral"],
|
||||||
|
exports: "numeral"
|
||||||
|
},
|
||||||
"tablesort.numeric": ["tablesort"],
|
"tablesort.numeric": ["tablesort"],
|
||||||
"helper": ["numeral-intl"]
|
"helper": ["numeral-intl"]
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
"r.js": "~2.1.16",
|
"r.js": "~2.1.16",
|
||||||
"d3": "~3.5.5",
|
"d3": "~3.5.5",
|
||||||
"numeraljs": "~1.5.3",
|
"numeraljs": "~1.5.3",
|
||||||
"roboto-fontface": "~0.3.0"
|
"roboto-fontface": "~0.3.0",
|
||||||
|
"virtual-dom": "~2.0.1"
|
||||||
},
|
},
|
||||||
"authors": [
|
"authors": [
|
||||||
"Nils Schneider <nils@nilsschneider.net>"
|
"Nils Schneider <nils@nilsschneider.net>"
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
define(["d3"], function (d3) {
|
define(["d3"], function (d3) {
|
||||||
return function (config, linkScale, sidebar, router) {
|
return function (config, linkScale, sidebar, router) {
|
||||||
var self = this
|
var self = this
|
||||||
var nodes, links
|
|
||||||
var svg, vis, link, node
|
var svg, vis, link, node
|
||||||
var nodesDict, linksDict
|
var nodesDict, linksDict
|
||||||
var zoomBehavior
|
var zoomBehavior
|
||||||
var force
|
var force
|
||||||
var el
|
var el
|
||||||
var doAnimation = false
|
var doAnimation = false
|
||||||
|
var intNodes = []
|
||||||
|
var highlight
|
||||||
|
|
||||||
var LINK_DISTANCE = 70
|
var LINK_DISTANCE = 70
|
||||||
|
|
||||||
@ -19,18 +20,18 @@ define(["d3"], function (d3) {
|
|||||||
if (!localStorageTest())
|
if (!localStorageTest())
|
||||||
return
|
return
|
||||||
|
|
||||||
var save = nodes.map( function (d) {
|
var save = intNodes.map( function (d) {
|
||||||
return { id: d.id, x: d.x, y: d.y }
|
return { id: d.o.id, x: d.x, y: d.y }
|
||||||
})
|
})
|
||||||
|
|
||||||
localStorage.setItem("graph/nodeposition", JSON.stringify(save))
|
localStorage.setItem("graph/nodeposition", JSON.stringify(save))
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeName(d) {
|
function nodeName(d) {
|
||||||
if (d.node && d.node.nodeinfo)
|
if (d.o.node && d.o.node.nodeinfo)
|
||||||
return d.node.nodeinfo.hostname
|
return d.o.node.nodeinfo.hostname
|
||||||
else
|
else
|
||||||
return d.id
|
return d.o.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function dragstart(d) {
|
function dragstart(d) {
|
||||||
@ -97,6 +98,48 @@ define(["d3"], function (d3) {
|
|||||||
animatePanzoom(translate, scale)
|
animatePanzoom(translate, scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateHighlight(nopanzoom) {
|
||||||
|
if (highlight !== undefined)
|
||||||
|
if (highlight.type === "node") {
|
||||||
|
var n = nodesDict[highlight.o.nodeinfo.node_id]
|
||||||
|
|
||||||
|
if (n) {
|
||||||
|
link.classed("highlight", false)
|
||||||
|
node.classed("highlight", function (e) {
|
||||||
|
return e.o.node === n.o.node && n.o.node !== undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!nopanzoom)
|
||||||
|
panzoomTo([n.x, n.y], [n.x, n.y])
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
} else if (highlight.type === "link") {
|
||||||
|
var l = linksDict[highlight.o.id]
|
||||||
|
|
||||||
|
if (l) {
|
||||||
|
node.classed("highlight", false)
|
||||||
|
link.classed("highlight", function (e) {
|
||||||
|
return e.o === l.o && l.o !== undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!nopanzoom) {
|
||||||
|
var x = d3.extent([l.source, l.target], function (d) { return d.x })
|
||||||
|
var y = d3.extent([l.source, l.target], function (d) { return d.y })
|
||||||
|
panzoomTo([x[0], y[0]], [x[1], y[1]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node.classed("highlight", false)
|
||||||
|
link.classed("highlight", false)
|
||||||
|
|
||||||
|
if (!nopanzoom)
|
||||||
|
panzoomTo([0, 0], force.size())
|
||||||
|
}
|
||||||
|
|
||||||
function tickEvent() {
|
function tickEvent() {
|
||||||
link.selectAll("line")
|
link.selectAll("line")
|
||||||
.attr("x1", function(d) { return d.source.x })
|
.attr("x1", function(d) { return d.source.x })
|
||||||
@ -132,7 +175,7 @@ define(["d3"], function (d3) {
|
|||||||
.gravity(0.05)
|
.gravity(0.05)
|
||||||
.linkDistance(LINK_DISTANCE)
|
.linkDistance(LINK_DISTANCE)
|
||||||
.linkStrength(function (d) {
|
.linkStrength(function (d) {
|
||||||
return 1 / d.tq
|
return 1 / d.o.tq
|
||||||
})
|
})
|
||||||
.on("tick", tickEvent)
|
.on("tick", tickEvent)
|
||||||
.on("end", savePositions)
|
.on("end", savePositions)
|
||||||
@ -145,24 +188,44 @@ define(["d3"], function (d3) {
|
|||||||
.on("dragend", dragend)
|
.on("dragend", dragend)
|
||||||
|
|
||||||
self.setData = function (data) {
|
self.setData = function (data) {
|
||||||
var nodePositions = {}
|
var oldNodes = {}
|
||||||
|
|
||||||
if (localStorageTest()) {
|
intNodes.forEach( function (d) {
|
||||||
var save = JSON.parse(localStorage.getItem("graph/nodeposition"))
|
oldNodes[d.o.id] = d
|
||||||
|
})
|
||||||
|
|
||||||
if (save)
|
intNodes = data.graph.nodes.map( function (d) {
|
||||||
save.forEach( function (d) {
|
var e
|
||||||
nodePositions[d.id] = d
|
if (d.id in oldNodes)
|
||||||
})
|
e = oldNodes[d.id]
|
||||||
}
|
else
|
||||||
|
e = {}
|
||||||
|
|
||||||
links = data.graph.links.filter( function (d) {
|
e.o = d
|
||||||
|
|
||||||
|
return e
|
||||||
|
})
|
||||||
|
|
||||||
|
var newNodesDict = {}
|
||||||
|
|
||||||
|
intNodes.forEach( function (d) {
|
||||||
|
newNodesDict[d.o.id] = d
|
||||||
|
})
|
||||||
|
|
||||||
|
var intLinks = data.graph.links.filter( function (d) {
|
||||||
return !d.vpn
|
return !d.vpn
|
||||||
|
}).map( function (d) {
|
||||||
|
var source = newNodesDict[d.source.id]
|
||||||
|
var target = newNodesDict[d.target.id]
|
||||||
|
|
||||||
|
return {o: d, source: source, target: target}
|
||||||
})
|
})
|
||||||
|
|
||||||
link = vis.select("g.links")
|
link = vis.select("g.links")
|
||||||
.selectAll("g.link")
|
.selectAll("g.link")
|
||||||
.data(links, function (d) { return d.id })
|
.data(intLinks, function (d) { return d.o.id })
|
||||||
|
|
||||||
|
link.exit().remove()
|
||||||
|
|
||||||
var linkEnter = link.enter().append("g")
|
var linkEnter = link.enter().append("g")
|
||||||
.attr("class", "link")
|
.attr("class", "link")
|
||||||
@ -175,34 +238,34 @@ define(["d3"], function (d3) {
|
|||||||
.append("title")
|
.append("title")
|
||||||
|
|
||||||
link.selectAll("line")
|
link.selectAll("line")
|
||||||
.style("stroke", function (d) { return linkScale(d.tq) })
|
.style("stroke", function (d) { return linkScale(d.o.tq).hex() })
|
||||||
|
|
||||||
link.selectAll("title").text(showTq)
|
link.selectAll("title").text(function (d) { return showTq(d.o) })
|
||||||
|
|
||||||
linksDict = {}
|
linksDict = {}
|
||||||
|
|
||||||
link.each( function (d) {
|
link.each( function (d) {
|
||||||
if (d.source.node && d.target.node)
|
if (d.o.source.node && d.o.target.node)
|
||||||
linksDict[d.id] = d
|
linksDict[d.o.id] = d
|
||||||
})
|
})
|
||||||
|
|
||||||
nodes = data.graph.nodes
|
|
||||||
|
|
||||||
node = vis.select("g.nodes")
|
node = vis.select("g.nodes")
|
||||||
.selectAll(".node")
|
.selectAll(".node")
|
||||||
.data(nodes, function(d) { return d.id })
|
.data(intNodes, function(d) { return d.o.id })
|
||||||
|
|
||||||
|
node.exit().remove()
|
||||||
|
|
||||||
var nodeEnter = node.enter().append("circle")
|
var nodeEnter = node.enter().append("circle")
|
||||||
.attr("r", 8)
|
.attr("r", 8)
|
||||||
.on("click", function (d) {
|
.on("click", function (d) {
|
||||||
if (!d3.event.defaultPrevented)
|
if (!d3.event.defaultPrevented)
|
||||||
router.node(d.node)()
|
router.node(d.o.node)()
|
||||||
})
|
})
|
||||||
.call(draggableNode)
|
.call(draggableNode)
|
||||||
|
|
||||||
node.attr("class", function (d) {
|
node.attr("class", function (d) {
|
||||||
var s = ["node"]
|
var s = ["node"]
|
||||||
if (!d.node)
|
if (!d.o.node)
|
||||||
s.push("unknown")
|
s.push("unknown")
|
||||||
|
|
||||||
return s.join(" ")
|
return s.join(" ")
|
||||||
@ -211,65 +274,59 @@ define(["d3"], function (d3) {
|
|||||||
nodesDict = {}
|
nodesDict = {}
|
||||||
|
|
||||||
node.each( function (d) {
|
node.each( function (d) {
|
||||||
if (d.node)
|
if (d.o.node)
|
||||||
nodesDict[d.node.nodeinfo.node_id] = d
|
nodesDict[d.o.node.nodeinfo.node_id] = d
|
||||||
})
|
})
|
||||||
|
|
||||||
nodeEnter.append("title")
|
nodeEnter.append("title")
|
||||||
nodeEnter.each( function (d) {
|
|
||||||
if (nodePositions[d.id]) {
|
if (localStorageTest()) {
|
||||||
d.x = nodePositions[d.id].x
|
var save = JSON.parse(localStorage.getItem("graph/nodeposition"))
|
||||||
d.y = nodePositions[d.id].y
|
|
||||||
|
if (save) {
|
||||||
|
var nodePositions = {}
|
||||||
|
save.forEach( function (d) {
|
||||||
|
nodePositions[d.id] = d
|
||||||
|
})
|
||||||
|
|
||||||
|
nodeEnter.each( function (d) {
|
||||||
|
if (nodePositions[d.o.id]) {
|
||||||
|
d.x = nodePositions[d.o.id].x
|
||||||
|
d.y = nodePositions[d.o.id].y
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
node.selectAll("title").text(nodeName)
|
node.selectAll("title").text(nodeName)
|
||||||
|
|
||||||
var diameter = graphDiameter(nodes)
|
var diameter = graphDiameter(intNodes)
|
||||||
|
|
||||||
force.nodes(nodes)
|
force.nodes(intNodes)
|
||||||
.links(links)
|
.links(intLinks)
|
||||||
.size([diameter, diameter])
|
.size([diameter, diameter])
|
||||||
.start()
|
|
||||||
|
updateHighlight(true)
|
||||||
|
|
||||||
|
if (node.enter().size() + link.enter().size() > 0)
|
||||||
|
force.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.resetView = function () {
|
self.resetView = function () {
|
||||||
node.classed("highlight", false)
|
highlight = undefined
|
||||||
link.classed("highlight", false)
|
updateHighlight()
|
||||||
|
|
||||||
panzoomTo([0, 0], force.size())
|
|
||||||
|
|
||||||
doAnimation = true
|
doAnimation = true
|
||||||
}
|
}
|
||||||
|
|
||||||
self.gotoNode = function (d) {
|
self.gotoNode = function (d) {
|
||||||
link.classed("highlight", false)
|
highlight = {type: "node", o: d}
|
||||||
node.classed("highlight", function (e) {
|
updateHighlight()
|
||||||
return e.node === d && d !== undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
var n = nodesDict[d.nodeinfo.node_id]
|
|
||||||
|
|
||||||
if (n)
|
|
||||||
panzoomTo([n.x, n.y], [n.x, n.y])
|
|
||||||
|
|
||||||
doAnimation = true
|
doAnimation = true
|
||||||
}
|
}
|
||||||
|
|
||||||
self.gotoLink = function (d) {
|
self.gotoLink = function (d) {
|
||||||
node.classed("highlight", false)
|
highlight = {type: "link", o: d}
|
||||||
link.classed("highlight", function (e) {
|
updateHighlight()
|
||||||
return e === d && d !== undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
var l = linksDict[d.id]
|
|
||||||
|
|
||||||
if (l) {
|
|
||||||
var x = d3.extent([l.source, l.target], function (d) { return d.x })
|
|
||||||
var y = d3.extent([l.source, l.target], function (d) { return d.y })
|
|
||||||
panzoomTo([x[0], y[0]], [x[1], y[1]])
|
|
||||||
}
|
|
||||||
|
|
||||||
doAnimation = true
|
doAnimation = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,11 +13,16 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Linklist,
|
|||||||
var linkScale = chroma.scale(chroma.interpolate.bezier(["green", "yellow", "red"])).domain([1, 5])
|
var linkScale = chroma.scale(chroma.interpolate.bezier(["green", "yellow", "red"])).domain([1, 5])
|
||||||
var sidebar
|
var sidebar
|
||||||
|
|
||||||
|
function dataTargetRemove(d) {
|
||||||
|
dataTargets = dataTargets.filter( function (e) { return d !== e })
|
||||||
|
}
|
||||||
|
|
||||||
function removeContent() {
|
function removeContent() {
|
||||||
if (!content)
|
if (!content)
|
||||||
return
|
return
|
||||||
|
|
||||||
router.removeTarget(content)
|
router.removeTarget(content)
|
||||||
|
dataTargetRemove(content)
|
||||||
content.destroy()
|
content.destroy()
|
||||||
contentDiv.removeChild(content.div)
|
contentDiv.removeChild(content.div)
|
||||||
content = null
|
content = null
|
||||||
|
107
lib/linklist.js
107
lib/linklist.js
@ -1,75 +1,56 @@
|
|||||||
define(["tablesort", "tablesort.numeric"], function (Tablesort) {
|
define(["sorttable", "virtual-dom"], function (SortTable, V) {
|
||||||
return function(linkScale, router) {
|
function linkName(d) {
|
||||||
var self = this
|
return d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname
|
||||||
var el
|
}
|
||||||
|
|
||||||
self.render = function (d) {
|
var headings = [{ name: "Knoten",
|
||||||
el = document.createElement("div")
|
sort: function (a, b) {
|
||||||
d.appendChild(el)
|
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) {
|
this.render = function (d) {
|
||||||
if (data.graph.links.length === 0)
|
var el = document.createElement("div")
|
||||||
return
|
el.last = V.h("div")
|
||||||
|
d.appendChild(el)
|
||||||
|
|
||||||
var h2 = document.createElement("h2")
|
var h2 = document.createElement("h2")
|
||||||
h2.textContent = "Verbindungen"
|
h2.textContent = "Verbindungen"
|
||||||
el.appendChild(h2)
|
el.appendChild(h2)
|
||||||
|
|
||||||
var table = document.createElement("table")
|
el.appendChild(table.el)
|
||||||
var thead = document.createElement("thead")
|
}
|
||||||
|
|
||||||
var tr = document.createElement("tr")
|
this.setData = function (d) {
|
||||||
var th1 = document.createElement("th")
|
table.setData(d.graph.links)
|
||||||
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"
|
|
||||||
th3.classList.add("sort-default")
|
|
||||||
tr.appendChild(th3)
|
|
||||||
|
|
||||||
thead.appendChild(tr)
|
|
||||||
|
|
||||||
table.appendChild(thead)
|
|
||||||
|
|
||||||
var tbody = document.createElement("tbody")
|
|
||||||
|
|
||||||
data.graph.links.forEach( function (d) {
|
|
||||||
var row = document.createElement("tr")
|
|
||||||
var td1 = document.createElement("td")
|
|
||||||
var a = document.createElement("a")
|
|
||||||
a.textContent = d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname
|
|
||||||
a.href = "#"
|
|
||||||
a.onclick = router.link(d)
|
|
||||||
td1.appendChild(a)
|
|
||||||
row.appendChild(td1)
|
|
||||||
|
|
||||||
if (d.vpn)
|
|
||||||
td1.appendChild(document.createTextNode(" (VPN)"))
|
|
||||||
|
|
||||||
var td2 = document.createElement("td")
|
|
||||||
td2.textContent = showTq(d)
|
|
||||||
td2.style.color = linkScale(d.tq)
|
|
||||||
row.appendChild(td2)
|
|
||||||
|
|
||||||
var td3 = document.createElement("td")
|
|
||||||
td3.textContent = showDistance(d)
|
|
||||||
td3.setAttribute("data-sort", d.distance !== undefined ? -d.distance : 1)
|
|
||||||
row.appendChild(td3)
|
|
||||||
|
|
||||||
tbody.appendChild(row)
|
|
||||||
})
|
|
||||||
|
|
||||||
table.appendChild(tbody)
|
|
||||||
|
|
||||||
new Tablesort(table)
|
|
||||||
|
|
||||||
el.appendChild(table)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
14
lib/main.js
14
lib/main.js
@ -88,14 +88,24 @@ function (config, moment, Router, L, GUI, numeral) {
|
|||||||
var urls = [ config.dataPath + "nodes.json",
|
var urls = [ config.dataPath + "nodes.json",
|
||||||
config.dataPath + "graph.json"
|
config.dataPath + "graph.json"
|
||||||
]
|
]
|
||||||
|
function update() {
|
||||||
|
return Promise.all(urls.map(getJSON))
|
||||||
|
.then(handleData)
|
||||||
|
}
|
||||||
|
|
||||||
Promise.all(urls.map(getJSON))
|
update()
|
||||||
.then(handleData)
|
|
||||||
.then(function (d) {
|
.then(function (d) {
|
||||||
var gui = new GUI(config, router)
|
var gui = new GUI(config, router)
|
||||||
gui.setData(d)
|
gui.setData(d)
|
||||||
router.setData(d)
|
router.setData(d)
|
||||||
router.start()
|
router.start()
|
||||||
|
|
||||||
|
window.setInterval(function () {
|
||||||
|
update().then(function (d) {
|
||||||
|
gui.setData(d)
|
||||||
|
router.setData(d)
|
||||||
|
})
|
||||||
|
}, 60000)
|
||||||
})
|
})
|
||||||
.catch(function (e) {
|
.catch(function (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
|
144
lib/map.js
144
lib/map.js
@ -57,6 +57,7 @@ define(["d3", "leaflet", "moment", "leaflet.label"], function (d3, L, moment) {
|
|||||||
return function (config, linkScale, sidebar, router) {
|
return function (config, linkScale, sidebar, router) {
|
||||||
var self = this
|
var self = this
|
||||||
var barycenter
|
var barycenter
|
||||||
|
var groupOnline, groupOffline, groupNew, groupLost, groupLines
|
||||||
|
|
||||||
var el = document.createElement("div")
|
var el = document.createElement("div")
|
||||||
el.classList.add("map")
|
el.classList.add("map")
|
||||||
@ -73,6 +74,62 @@ define(["d3", "leaflet", "moment", "leaflet.label"], function (d3, L, moment) {
|
|||||||
|
|
||||||
var nodeDict = {}
|
var nodeDict = {}
|
||||||
var linkDict = {}
|
var linkDict = {}
|
||||||
|
var highlight
|
||||||
|
|
||||||
|
function resetMarkerStyles(nodes, links) {
|
||||||
|
Object.keys(nodes).forEach( function (d) {
|
||||||
|
nodes[d].resetStyle()
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.keys(links).forEach( function (d) {
|
||||||
|
links[d].resetStyle()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function setView(bounds) {
|
||||||
|
map.fitBounds(bounds, {paddingTopLeft: [sidebar.getWidth(), 0]})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetZoom() {
|
||||||
|
setView(barycenter.getBounds())
|
||||||
|
}
|
||||||
|
|
||||||
|
function goto(m) {
|
||||||
|
var bounds
|
||||||
|
|
||||||
|
if ("getBounds" in m)
|
||||||
|
bounds = m.getBounds()
|
||||||
|
else
|
||||||
|
bounds = L.latLngBounds([m.getLatLng()])
|
||||||
|
|
||||||
|
setView(bounds)
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateView(nopanzoom) {
|
||||||
|
resetMarkerStyles(nodeDict, linkDict)
|
||||||
|
var m
|
||||||
|
|
||||||
|
if (highlight !== undefined)
|
||||||
|
if (highlight.type === "node") {
|
||||||
|
m = nodeDict[highlight.o.nodeinfo.node_id]
|
||||||
|
|
||||||
|
if (m)
|
||||||
|
m.setStyle({ fillColor: m.options.color, color: "orange", weight: 20, fillOpacity: 1, opacity: 0.7 })
|
||||||
|
} else if (highlight.type === "link") {
|
||||||
|
m = linkDict[highlight.o.id]
|
||||||
|
|
||||||
|
if (m)
|
||||||
|
m.setStyle({ weight: 7, opacity: 1, dashArray: "10, 10" })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nopanzoom)
|
||||||
|
if (m)
|
||||||
|
goto(m)
|
||||||
|
else
|
||||||
|
resetZoom()
|
||||||
|
}
|
||||||
|
|
||||||
function calcBarycenter(nodes) {
|
function calcBarycenter(nodes) {
|
||||||
nodes = nodes.map(function (d) { return d.nodeinfo.location })
|
nodes = nodes.map(function (d) { return d.nodeinfo.location })
|
||||||
@ -94,8 +151,23 @@ define(["d3", "leaflet", "moment", "leaflet.label"], function (d3, L, moment) {
|
|||||||
nodeDict = {}
|
nodeDict = {}
|
||||||
linkDict = {}
|
linkDict = {}
|
||||||
|
|
||||||
|
if (groupOffline)
|
||||||
|
groupOffline.clearLayers()
|
||||||
|
|
||||||
|
if (groupOnline)
|
||||||
|
groupOnline.clearLayers()
|
||||||
|
|
||||||
|
if (groupNew)
|
||||||
|
groupNew.clearLayers()
|
||||||
|
|
||||||
|
if (groupLost)
|
||||||
|
groupLost.clearLayers()
|
||||||
|
|
||||||
|
if (groupLines)
|
||||||
|
groupLines.clearLayers()
|
||||||
|
|
||||||
var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router)
|
var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router)
|
||||||
L.featureGroup(lines).addTo(map)
|
groupLines = L.featureGroup(lines).addTo(map)
|
||||||
|
|
||||||
barycenter = calcBarycenter(data.nodes.all.filter(has_location))
|
barycenter = calcBarycenter(data.nodes.all.filter(has_location))
|
||||||
|
|
||||||
@ -119,71 +191,27 @@ define(["d3", "leaflet", "moment", "leaflet.label"], function (d3, L, moment) {
|
|||||||
return iconLost
|
return iconLost
|
||||||
}, router))
|
}, router))
|
||||||
|
|
||||||
L.featureGroup(markersOffline).addTo(map)
|
groupOffline = L.featureGroup(markersOffline).addTo(map)
|
||||||
L.featureGroup(markersOnline).addTo(map)
|
groupOnline = L.featureGroup(markersOnline).addTo(map)
|
||||||
L.featureGroup(markersNew).addTo(map)
|
groupNew = L.featureGroup(markersNew).addTo(map)
|
||||||
L.featureGroup(markersLost).addTo(map)
|
groupLost = L.featureGroup(markersLost).addTo(map)
|
||||||
|
|
||||||
|
updateView(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetMarkerStyles(nodes, links) {
|
self.resetView = function () {
|
||||||
Object.keys(nodes).forEach( function (d) {
|
highlight = undefined
|
||||||
nodes[d].resetStyle()
|
updateView()
|
||||||
})
|
|
||||||
|
|
||||||
Object.keys(links).forEach( function (d) {
|
|
||||||
links[d].resetStyle()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setView(bounds) {
|
|
||||||
map.fitBounds(bounds, {paddingTopLeft: [sidebar.getWidth(), 0]})
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetView() {
|
|
||||||
resetMarkerStyles(nodeDict, linkDict)
|
|
||||||
|
|
||||||
setView(barycenter.getBounds())
|
|
||||||
}
|
|
||||||
|
|
||||||
function goto(dict, id) {
|
|
||||||
var m = dict[id]
|
|
||||||
if (m === undefined)
|
|
||||||
return undefined
|
|
||||||
|
|
||||||
var bounds
|
|
||||||
|
|
||||||
if ("getBounds" in m)
|
|
||||||
bounds = m.getBounds()
|
|
||||||
else
|
|
||||||
bounds = L.latLngBounds([m.getLatLng()])
|
|
||||||
|
|
||||||
setView(bounds)
|
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
self.resetView = resetView
|
|
||||||
|
|
||||||
self.gotoNode = function (d) {
|
self.gotoNode = function (d) {
|
||||||
resetMarkerStyles(nodeDict, linkDict)
|
highlight = {type: "node", o: d}
|
||||||
|
updateView()
|
||||||
var m = goto(nodeDict, d.nodeinfo.node_id)
|
|
||||||
|
|
||||||
if (m)
|
|
||||||
m.setStyle({ fillColor: m.options.color, color: "orange", weight: 20, fillOpacity: 1, opacity: 0.7 })
|
|
||||||
else
|
|
||||||
resetView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.gotoLink = function (d) {
|
self.gotoLink = function (d) {
|
||||||
resetMarkerStyles(nodeDict, linkDict)
|
highlight = {type: "link", o: d}
|
||||||
|
updateView()
|
||||||
var m = goto(linkDict, d.id)
|
|
||||||
|
|
||||||
if (m)
|
|
||||||
m.setStyle({ weight: 7, opacity: 1, dashArray: "10, 10" })
|
|
||||||
else
|
|
||||||
resetView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.destroy = function () {
|
self.destroy = function () {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
define(function () {
|
define(function () {
|
||||||
return function () {
|
return function () {
|
||||||
var self = this
|
var self = this
|
||||||
var p
|
var stats, timestamp
|
||||||
|
|
||||||
self.setData = function (d) {
|
self.setData = function (d) {
|
||||||
var totalNodes = sum(d.nodes.all.filter(online).map(one))
|
var totalNodes = sum(d.nodes.all.filter(online).map(one))
|
||||||
@ -12,12 +12,11 @@ define(function () {
|
|||||||
return d.flags.gateway
|
return d.flags.gateway
|
||||||
}).map(one))
|
}).map(one))
|
||||||
|
|
||||||
p.textContent = totalNodes + " Knoten (online), " +
|
stats.textContent = totalNodes + " Knoten (online), " +
|
||||||
totalClients + " Clients, " +
|
totalClients + " Clients, " +
|
||||||
totalGateways + " Gateways"
|
totalGateways + " Gateways"
|
||||||
|
|
||||||
p.appendChild(document.createElement("br"))
|
timestamp.textContent = "Diese Daten sind von " + d.timestamp.format("LLLL") + "."
|
||||||
p.appendChild(document.createTextNode("Diese Daten sind von " + d.timestamp.format("LLLL") + "."))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.render = function (el) {
|
self.render = function (el) {
|
||||||
@ -25,8 +24,13 @@ define(function () {
|
|||||||
h2.textContent = "Übersicht"
|
h2.textContent = "Übersicht"
|
||||||
el.appendChild(h2)
|
el.appendChild(h2)
|
||||||
|
|
||||||
p = document.createElement("p")
|
var p = document.createElement("p")
|
||||||
el.appendChild(p)
|
el.appendChild(p)
|
||||||
|
stats = document.createTextNode("")
|
||||||
|
p.appendChild(stats)
|
||||||
|
p.appendChild(document.createElement("br"))
|
||||||
|
timestamp = document.createTextNode("")
|
||||||
|
p.appendChild(timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
145
lib/nodelist.js
145
lib/nodelist.js
@ -1,99 +1,82 @@
|
|||||||
define(["tablesort", "tablesort.numeric"], function (Tablesort) {
|
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) {
|
return function(router) {
|
||||||
function showUptime(el, now, d) {
|
function renderRow(d) {
|
||||||
var uptime
|
var td1Content = []
|
||||||
if (d.flags.online && "uptime" in d.statistics)
|
var aClass = ["hostname", d.flags.online ? "online" : "offline"]
|
||||||
uptime = Math.round(d.statistics.uptime / 3600)
|
|
||||||
else if (!d.flags.online && "lastseen" in d)
|
|
||||||
uptime = Math.round(-(now - d.lastseen) / 3600000)
|
|
||||||
|
|
||||||
var s = ""
|
td1Content.push(V.h("a", { className: aClass.join(" "),
|
||||||
|
onclick: router.node(d),
|
||||||
|
href: "#"
|
||||||
|
}, d.nodeinfo.hostname))
|
||||||
|
|
||||||
if (uptime !== undefined)
|
if (has_location(d))
|
||||||
if (Math.abs(uptime) >= 24)
|
td1Content.push(V.h("span", {className: "icon ion-location"}))
|
||||||
s = Math.round(uptime / 24) + "d"
|
|
||||||
else
|
|
||||||
s = uptime + "h"
|
|
||||||
|
|
||||||
el.textContent = s
|
var td1 = V.h("td", td1Content)
|
||||||
el.setAttribute("data-sort", uptime !== undefined ? -uptime : 0)
|
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 table = new SortTable(headings, 0, renderRow)
|
||||||
var el
|
|
||||||
|
|
||||||
self.render = function (d) {
|
this.render = function (d) {
|
||||||
el = document.createElement("div")
|
var el = document.createElement("div")
|
||||||
d.appendChild(el)
|
d.appendChild(el)
|
||||||
}
|
|
||||||
|
|
||||||
self.setData = function (data) {
|
|
||||||
if (data.nodes.all.length === 0)
|
|
||||||
return
|
|
||||||
|
|
||||||
var h2 = document.createElement("h2")
|
var h2 = document.createElement("h2")
|
||||||
h2.textContent = "Alle Knoten"
|
h2.textContent = "Alle Knoten"
|
||||||
el.appendChild(h2)
|
el.appendChild(h2)
|
||||||
|
|
||||||
var table = document.createElement("table")
|
el.appendChild(table.el)
|
||||||
var thead = document.createElement("thead")
|
}
|
||||||
|
|
||||||
var tr = document.createElement("tr")
|
this.setData = function (d) {
|
||||||
var th1 = document.createElement("th")
|
var data = d.nodes.all.map(function (e) {
|
||||||
th1.textContent = "Knoten"
|
var n = Object.create(e)
|
||||||
th1.classList.add("sort-default")
|
n.uptime = getUptime(d.now, e)
|
||||||
tr.appendChild(th1)
|
return n
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
var tbody = document.createElement("tbody")
|
|
||||||
|
|
||||||
data.nodes.all.forEach( function (d) {
|
|
||||||
var row = document.createElement("tr")
|
|
||||||
|
|
||||||
var td1 = document.createElement("td")
|
|
||||||
var a = document.createElement("a")
|
|
||||||
a.textContent = d.nodeinfo.hostname
|
|
||||||
a.href = "#"
|
|
||||||
a.onclick = router.node(d)
|
|
||||||
a.classList.add("hostname")
|
|
||||||
a.classList.add(d.flags.online ? "online" : "offline")
|
|
||||||
td1.appendChild(a)
|
|
||||||
row.appendChild(td1)
|
|
||||||
|
|
||||||
if (has_location(d)) {
|
|
||||||
var span = document.createElement("span")
|
|
||||||
span.classList.add("icon")
|
|
||||||
span.classList.add("ion-location")
|
|
||||||
td1.appendChild(span)
|
|
||||||
}
|
|
||||||
|
|
||||||
var td2 = document.createElement("td")
|
|
||||||
showUptime(td2, data.now, d)
|
|
||||||
row.appendChild(td2)
|
|
||||||
|
|
||||||
var td3 = document.createElement("td")
|
|
||||||
td3.textContent = "clients" in d.statistics ? d.statistics.clients : ""
|
|
||||||
row.appendChild(td3)
|
|
||||||
|
|
||||||
tbody.appendChild(row)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
table.appendChild(tbody)
|
table.setData(data)
|
||||||
|
}
|
||||||
new Tablesort(table)
|
|
||||||
|
|
||||||
el.appendChild(table)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
define(["chroma-js"], function (Chroma) {
|
define(["chroma-js", "virtual-dom", "numeral-intl"],
|
||||||
|
function (Chroma, V, numeral) {
|
||||||
|
|
||||||
return function () {
|
return function () {
|
||||||
var self = this
|
var self = this
|
||||||
var fwTable, hwTable, autoTable, gwTable
|
var fwTable, hwTable, autoTable, gwTable
|
||||||
@ -23,30 +25,33 @@ define(["chroma-js"], function (Chroma) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fillTable(table, data) {
|
function fillTable(table, data) {
|
||||||
|
if (!table.last)
|
||||||
|
table.last = V.h("table")
|
||||||
|
|
||||||
var max = 0
|
var max = 0
|
||||||
data.forEach(function (d) {
|
data.forEach(function (d) {
|
||||||
if (d[1] > max)
|
if (d[1] > max)
|
||||||
max = d[1]
|
max = d[1]
|
||||||
})
|
})
|
||||||
|
|
||||||
data.forEach(function (d) {
|
var items = data.map(function (d) {
|
||||||
var v = d[1] / max
|
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 c1 = Chroma.contrast(scale(v), "white")
|
||||||
var c2 = Chroma.contrast(scale(v), "black")
|
var c2 = Chroma.contrast(scale(v), "black")
|
||||||
span.style.color = c1 > c2 ? "white" : "black"
|
|
||||||
span.textContent = d[1]
|
var th = V.h("th", d[0])
|
||||||
td.appendChild(span)
|
var td = V.h("td", V.h("span", {style: {
|
||||||
row.appendChild(th)
|
width: Math.round(v * 100) + "%",
|
||||||
row.appendChild(td)
|
backgroundColor: scale(v).hex(),
|
||||||
table.appendChild(row)
|
color: c1 > c2 ? "white" : "black"
|
||||||
|
}}, numeral(d[1]).format("0,0")))
|
||||||
|
|
||||||
|
return V.h("tr", [th, td])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var tableNew = V.h("table", items)
|
||||||
|
table = V.patch(table, V.diff(table.last, tableNew))
|
||||||
|
table.last = tableNew
|
||||||
}
|
}
|
||||||
|
|
||||||
self.setData = function (data) {
|
self.setData = function (data) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
define(["moment"], function (moment) {
|
define(["moment", "virtual-dom"], function (moment, V) {
|
||||||
return function(config, nodes, field, router, title) {
|
return function(config, nodes, field, router, title) {
|
||||||
var self = this
|
var self = this
|
||||||
var el
|
var el, tbody
|
||||||
|
|
||||||
self.render = function (d) {
|
self.render = function (d) {
|
||||||
el = document.createElement("div")
|
el = document.createElement("div")
|
||||||
@ -11,52 +11,54 @@ define(["moment"], function (moment) {
|
|||||||
self.setData = function (data) {
|
self.setData = function (data) {
|
||||||
var list = data.nodes[nodes]
|
var list = data.nodes[nodes]
|
||||||
|
|
||||||
if (list.length === 0)
|
if (list.length === 0) {
|
||||||
|
while (el.firstChild)
|
||||||
|
el.removeChild(el.firstChild)
|
||||||
|
|
||||||
|
tbody = null
|
||||||
|
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var h2 = document.createElement("h2")
|
if (!tbody) {
|
||||||
h2.textContent = title
|
var h2 = document.createElement("h2")
|
||||||
el.appendChild(h2)
|
h2.textContent = title
|
||||||
var table = document.createElement("table")
|
el.appendChild(h2)
|
||||||
el.appendChild(table)
|
|
||||||
|
|
||||||
var tbody = document.createElement("tbody")
|
var table = document.createElement("table")
|
||||||
|
el.appendChild(table)
|
||||||
|
|
||||||
list.forEach( function (d) {
|
tbody = document.createElement("tbody")
|
||||||
var time = moment(d[field]).fromNow()
|
tbody.last = V.h("tbody")
|
||||||
|
table.appendChild(tbody)
|
||||||
|
}
|
||||||
|
|
||||||
var row = document.createElement("tr")
|
var items = list.map( function (d) {
|
||||||
var td1 = document.createElement("td")
|
var time = moment(d[field]).from(data.now)
|
||||||
var a = document.createElement("a")
|
var td1Content = []
|
||||||
a.classList.add("hostname")
|
|
||||||
a.classList.add(d.flags.online ? "online" : "offline")
|
|
||||||
a.textContent = d.nodeinfo.hostname
|
|
||||||
a.href = "#"
|
|
||||||
a.onclick = router.node(d)
|
|
||||||
td1.appendChild(a)
|
|
||||||
|
|
||||||
if (has_location(d)) {
|
var aClass = ["hostname", d.flags.online ? "online" : "offline"]
|
||||||
var span = document.createElement("span")
|
|
||||||
span.classList.add("icon")
|
|
||||||
span.classList.add("ion-location")
|
|
||||||
td1.appendChild(span)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("owner" in d.nodeinfo && config.showContact) {
|
td1Content.push(V.h("a", { className: aClass.join(" "),
|
||||||
var contact = d.nodeinfo.owner.contact
|
onclick: router.node(d),
|
||||||
td1.appendChild(document.createTextNode(" – " + contact + ""))
|
href: "#"
|
||||||
}
|
}, d.nodeinfo.hostname))
|
||||||
|
|
||||||
var td2 = document.createElement("td")
|
if (has_location(d))
|
||||||
td2.textContent = time
|
td1Content.push(V.h("span", {className: "icon ion-location"}))
|
||||||
|
|
||||||
row.appendChild(td1)
|
if ("owner" in d.nodeinfo && config.showContact)
|
||||||
row.appendChild(td2)
|
td1Content.push(" - " + d.nodeinfo.owner.contact)
|
||||||
tbody.appendChild(row)
|
|
||||||
|
var td1 = V.h("td", td1Content)
|
||||||
|
var td2 = V.h("td", time)
|
||||||
|
|
||||||
|
return V.h("tr", [td1, td2])
|
||||||
})
|
})
|
||||||
|
|
||||||
table.appendChild(tbody)
|
var tbodyNew = V.h("tbody", items)
|
||||||
el.appendChild(table)
|
tbody = V.patch(tbody, V.diff(tbody.last, tbodyNew))
|
||||||
|
tbody.last = tbodyNew
|
||||||
}
|
}
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
57
lib/sorttable.js
Normal file
57
lib/sorttable.js
Normal file
@ -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
|
||||||
|
}
|
||||||
|
})
|
@ -19,7 +19,8 @@ module.exports = function (grunt) {
|
|||||||
"strict": [2, "never"],
|
"strict": [2, "never"],
|
||||||
"no-multi-spaces": 0,
|
"no-multi-spaces": 0,
|
||||||
"no-new": 0,
|
"no-new": 0,
|
||||||
"no-shadow": 0
|
"no-shadow": 0,
|
||||||
|
"no-use-before-define": [1, "nofunc"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sources: {
|
sources: {
|
||||||
|
Loading…
Reference in New Issue
Block a user