basic filter support

This commit is contained in:
Nils Schneider 2015-07-08 00:36:57 +02:00
parent aeae866998
commit 09bdb7d61a
8 changed files with 246 additions and 38 deletions

76
lib/datadistributor.js Normal file
View file

@ -0,0 +1,76 @@
define([], function () {
return function () {
var targets = []
var filterObservers = []
var filters = []
var filteredData
var data
function remove(d) {
targets = targets.filter( function (e) { return d !== e } )
}
function add(d) {
targets.push(d)
if (filteredData !== undefined)
d.setData(filteredData)
}
function setData(d) {
data = d
refresh()
}
function refresh() {
if (data === undefined)
return
filteredData = filters.reduce( function (a, f) {
return f.run(a)
}, data)
targets.forEach( function (t) {
t.setData(filteredData)
})
}
function notifyObservers() {
filterObservers.forEach( function (d) {
d.filtersChanged(filters)
})
}
function addFilter(d) {
filters.push(d)
notifyObservers()
d.setRefresh(refresh)
refresh()
}
function removeFilter(d) {
filters = filters.filter( function (e) { return d !== e } )
notifyObservers()
refresh()
}
function watchFilters(d) {
filterObservers.push(d)
d.filtersChanged(filters)
return function () {
filterObservers = filterObservers.filter( function (e) { return d !== e })
}
}
return { add: add,
remove: remove,
setData: setData,
addFilter: addFilter,
removeFilter: removeFilter,
watchFilters: watchFilters,
refresh: refresh
}
}
})

34
lib/filters/filtergui.js Normal file
View file

@ -0,0 +1,34 @@
define([], function () {
return function (distributor) {
var container = document.createElement("ul")
container.classList.add("filters")
function render(el) {
el.appendChild(container)
}
function filtersChanged(filters) {
while (container.firstChild)
container.removeChild(container.firstChild)
filters.forEach( function (d) {
var li = document.createElement("li")
var div = document.createElement("div")
container.appendChild(li)
li.appendChild(div)
d.render(div)
var button = document.createElement("button")
button.textContent = ""
button.onclick = function () {
distributor.removeFilter(d)
}
li.appendChild(button)
})
}
return { render: render,
filtersChanged: filtersChanged
}
}
})

View file

@ -0,0 +1,32 @@
define(["filters/nodefilter"], function (nodefilter) {
return function (name, key, value, f) {
function run(d) {
var o = dictGet(d, key.slice(0))
if (f)
o = f(o)
return o === value
}
function setRefresh() {
}
function render(el) {
var label = document.createElement("label")
label.textContent = name + " "
var strong = document.createElement("strong")
strong.textContent = value
label.appendChild(strong)
el.appendChild(label)
}
return { run: nodefilter(run),
setRefresh: setRefresh,
render: render
}
}
})

33
lib/filters/nodefilter.js Normal file
View file

@ -0,0 +1,33 @@
define([], function () {
return function (filter) {
return function (data) {
var n = Object.create(data)
n.nodes = {}
for (var key in data.nodes) {
n.nodes[key] = data.nodes[key].filter(filter)
}
var filteredIds = new Set()
n.graph = {}
n.graph.nodes = data.graph.nodes.filter( function (d) {
if (!d.node)
return true
var r = filter(d.node)
if (r)
filteredIds.add(d.id)
return r
})
n.graph.links = data.graph.links.filter( function (d) {
return filteredIds.has(d.source.id) && filteredIds.has(d.target.id)
})
return n
}
}
})

View file

@ -1,13 +1,13 @@
define([ "chroma-js", "map", "sidebar", "tabs", "container", "meshstats", define([ "chroma-js", "map", "sidebar", "tabs", "container", "meshstats",
"legend", "linklist", "nodelist", "simplenodelist", "infobox/main", "legend", "linklist", "nodelist", "simplenodelist", "infobox/main",
"proportions", "forcegraph", "title", "about" ], "proportions", "forcegraph", "title", "about", "datadistributor",
"filters/filtergui" ],
function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist, function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph, Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph,
Title, About) { Title, About, DataDistributor, FilterGUI) {
return function (config, router) { return function (config, router) {
var self = this var self = this
var dataTargets = [] var fanout = new DataDistributor()
var latestData
var content var content
var contentDiv var contentDiv
@ -17,16 +17,13 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
var buttons = document.createElement("div") var buttons = document.createElement("div")
buttons.classList.add("buttons") buttons.classList.add("buttons")
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) fanout.remove(content)
content.destroy() content.destroy()
content = null content = null
@ -38,10 +35,7 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
content = new K(config, linkScale, sidebar.getWidth, router, buttons) content = new K(config, linkScale, sidebar.getWidth, router, buttons)
content.render(contentDiv) content.render(contentDiv)
if (latestData) fanout.add(content)
content.setData(latestData)
dataTargets.push(content)
router.addTarget(content) router.addTarget(content)
} }
@ -82,15 +76,15 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
var lostnodeslist = new SimpleNodelist("lost", "lastseen", router, "Verschwundene Knoten") var lostnodeslist = new SimpleNodelist("lost", "lastseen", router, "Verschwundene Knoten")
var nodelist = new Nodelist(router) var nodelist = new Nodelist(router)
var linklist = new Linklist(linkScale, router) var linklist = new Linklist(linkScale, router)
var statistics = new Proportions(config) var statistics = new Proportions(config, fanout)
var about = new About() var about = new About()
dataTargets.push(meshstats) fanout.add(meshstats)
dataTargets.push(newnodeslist) fanout.add(newnodeslist)
dataTargets.push(lostnodeslist) fanout.add(lostnodeslist)
dataTargets.push(nodelist) fanout.add(nodelist)
dataTargets.push(linklist) fanout.add(linklist)
dataTargets.push(statistics) fanout.add(statistics)
sidebar.add(header) sidebar.add(header)
header.add(meshstats) header.add(meshstats)
@ -99,6 +93,10 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
overview.add(newnodeslist) overview.add(newnodeslist)
overview.add(lostnodeslist) overview.add(lostnodeslist)
var filterGUI = new FilterGUI(fanout)
fanout.watchFilters(filterGUI)
header.add(filterGUI)
sidebar.add(tabs) sidebar.add(tabs)
tabs.add("Aktuelles", overview) tabs.add("Aktuelles", overview)
tabs.add("Knoten", nodelist) tabs.add("Knoten", nodelist)
@ -114,13 +112,7 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
router.view("m") router.view("m")
self.setData = function (data) { self.setData = fanout.setData
latestData = data
dataTargets.forEach(function (d) {
d.setData(data)
})
}
return self return self
} }

View file

@ -1,7 +1,7 @@
define(["chroma-js", "virtual-dom", "numeral-intl", "vercomp" ], define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "vercomp" ],
function (Chroma, V, numeral, vercomp) { function (Chroma, V, numeral, Filter, vercomp) {
return function (config) { return function (config, filterManager) {
var self = this var self = this
var scale = Chroma.scale("YlGnBu").mode("lab") var scale = Chroma.scale("YlGnBu").mode("lab")
@ -68,10 +68,18 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "vercomp" ],
dict[v] = 1 + (v in dict ? dict[v] : 0) dict[v] = 1 + (v in dict ? dict[v] : 0)
}) })
return Object.keys(dict).map(function (d) { return [d, dict[d]] }) return Object.keys(dict).map(function (d) { return [d, dict[d], key, f] })
} }
function fillTable(table, data) { function addFilter(filter) {
return function () {
filterManager.addFilter(filter)
return false
}
}
function fillTable(name, table, data) {
if (!table.last) if (!table.last)
table.last = V.h("table") table.last = V.h("table")
@ -86,7 +94,11 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "vercomp" ],
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")
var th = V.h("th", d[0]) var filter = new Filter(name, d[2], d[0], d[3])
var a = V.h("a", { href: "#", onclick: addFilter(filter) }, d[0])
var th = V.h("th", a)
var td = V.h("td", V.h("span", {style: { var td = V.h("td", V.h("span", {style: {
width: Math.round(v * 100) + "%", width: Math.round(v * 100) + "%",
backgroundColor: scale(v).hex(), backgroundColor: scale(v).hex(),
@ -127,11 +139,11 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "vercomp" ],
return "(deaktiviert)" return "(deaktiviert)"
}) })
fillTable(statusTable, statusDict.sort(function (a, b) { return b[1] - a[1] })) fillTable("Status", statusTable, statusDict.sort(function (a, b) { return b[1] - a[1] }))
fillTable(fwTable, fwDict.sort(function (a, b) { return vercomp(b[0], a[0]) })) fillTable("Firmware", fwTable, fwDict.sort(function (a, b) { return vercomp(b[0], a[0]) }))
fillTable(hwTable, hwDict.sort(function (a, b) { return b[1] - a[1] })) fillTable("Hardware", hwTable, hwDict.sort(function (a, b) { return b[1] - a[1] }))
fillTable(geoTable, geoDict.sort(function (a, b) { return b[1] - a[1] })) fillTable("Koordinaten", geoTable, geoDict.sort(function (a, b) { return b[1] - a[1] }))
fillTable(autoTable, autoDict.sort(function (a, b) { return b[1] - a[1] })) fillTable("Autom. Updates", autoTable, autoDict.sort(function (a, b) { return b[1] - a[1] }))
} }
self.render = function (el) { self.render = function (el) {

28
scss/_filters.scss Normal file
View file

@ -0,0 +1,28 @@
.filters {
margin: 0;
padding: 0 !important;
li {
display: flex;
padding: 6pt 0 6pt 12pt;
align-items: center;
& > div {
flex-grow: 1;
}
@include shadow(1);
}
button {
box-shadow: none;
margin: 0;
padding: 0;
background: none;
font-size: 1.41em;
&:hover {
box-shadow: none !important;
}
}
}

View file

@ -3,6 +3,7 @@
@import '_base'; @import '_base';
@import '_leaflet'; @import '_leaflet';
@import '_leaflet.label'; @import '_leaflet.label';
@import '_filters';
$minscreenwidth: 630pt; $minscreenwidth: 630pt;
$sidebarwidth: 420pt; $sidebarwidth: 420pt;