summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--public/js/mv/chart.js44
-rw-r--r--public/js/mv/connect.js26
-rw-r--r--public/js/util/trellis-chart.js71
3 files changed, 111 insertions, 30 deletions
diff --git a/public/js/mv/chart.js b/public/js/mv/chart.js
index d2eedd4..ebb62da 100644
--- a/public/js/mv/chart.js
+++ b/public/js/mv/chart.js
@@ -14,6 +14,21 @@ var mv = function(mv) {
.xUnits(d3.time.hours)
.xAxisPadding(2)
;
+ mv.charts.date.filterCompare = function(l, r) {
+ return ((l instanceof Date) ? l : new Date(l))
+ == ((r instanceof Date) ? r : new Date(r));
+ };
+ // Remember the old date filter
+ // FIXME: Find a more elegant way to do this
+ var innerDateFilter = mv.charts.date.filter;
+ mv.charts.date.filter = function(_) {
+ if (!arguments.length) return innerDateFilter();
+ if (!_) return innerDateFilter(_);
+ _ = _ instanceof Array
+ ? _.map(function(d) { return d instanceof Date ? d : new Date(d); })
+ : (_ instanceof Date ? _ : new Date(_));
+ return innerDateFilter(_);
+ }
/* dc's default date format is M/D/Y, which is confusing and not ISO 8901 */
dc.dateFormat = d3.time.format("%Y-%m-%d %H:%M");
mv.charts.blvl = bar(monoGroup(med(dc.barChart("#blvl-chart")), "blvl"))
@@ -52,7 +67,34 @@ var mv = function(mv) {
.title(function(d) { return "Map " + d.key + ":" + d.value.r; })
.renderTitle(true)
;
- mv.charts.stats = trellisChart("#stat-chart", ["str", "agi", "vit", "dex", "int", "luk"].map(function(d) { mv.heap[d].name = d; return mv.heap[d]; }));
+ var attrs = ["str", "agi", "vit", "dex", "int", "luk"];
+ mv.charts.stats = trellisChart("#stat-chart", attrs.map(function(d) { mv.heap[d].name = d; return mv.heap[d]; }));
+ mv.charts.stats.filterCompare = function(l, r) {
+ /* Compare each attribute in turn. FIXME: Duplicated code with connect.js */
+ if (l == null && r == null)
+ return true;
+ if (l == null || r == null)
+ return false;
+ for (var key in attrs) {
+ var attr = attrs[key];
+ if (attr in l && attr in r) {
+ if (l[attr] instanceof Array) {
+ /* Range filter */
+ if (!(r[attr] instanceof Array)
+ || l[attr][0] != r[attr][0]
+ || l[attr][1] != r[attr][1]) {
+ return false;
+ }
+ } else if ((r[attr] instanceof Array) || l[attr] != r[attr]) {
+ /* Exact filter */
+ return false;
+ }
+ } else if (attr in l || attr in r) {
+ return false;
+ }
+ }
+ return true;
+ }
mv.charts.type.filter("KILLXP");
var killxpShown = true;
var killxpCharts = d3.select("#killxp-charts");
diff --git a/public/js/mv/connect.js b/public/js/mv/connect.js
index 6f3101f..a17febb 100644
--- a/public/js/mv/connect.js
+++ b/public/js/mv/connect.js
@@ -105,30 +105,30 @@ var mv = function(mv) {
/* See if there's any difference - if there isn't, don't update. */
var change = false;
var key;
+ function filterCompare(key, l, r) {
+ return ("filterCompare" in mv.charts[key]) ? mv.charts[key].filterCompare(l, r) : l == r;
+ }
/* Check for keys in the filters to apply which are not in our charts. */
for (key in filters) {
- if (key == "date") {
- /*
- * Special case! FIXME: Find a more elegant way to handle this
- */
- filters[key][0] = new Date(filters[key][0]);
- filters[key][1] = new Date(filters[key][1]);
- }
if (!(key in mv.charts))
continue;
- var filter = mv.charts[key].filter();
- if (typeof(filter) == "array") {
+ /* The two filters to compare. */
+ var l = mv.charts[key].filter();
+ var r = filters[key];
+ if (l instanceof Array) {
/* Crossfilter uses arrays to filter ranges. Exactly the first two elements are significant. */
- if (filter[0] == filters[key][0] &&
- filter[1] == filters[key][1]) {
+ if ((r instanceof Array)
+ && filterCompare(key, l[0], r[0])
+ && filterCompare(key, l[1], r[1])) {
continue;
}
- } else if (filter == filters[key]) {
+ } else if (!(r instanceof Array) && filterCompare(key, l, r)) {
+ /* Exact filter */
continue;
}
/* This filter differs. Apply it. */
change = true;
- mv.charts[key].filter(filters[key]);
+ mv.charts[key].filter(r);
}
/* Check for keys in our charts which are not in the filters to apply. */
for (key in mv.charts) {
diff --git a/public/js/util/trellis-chart.js b/public/js/util/trellis-chart.js
index 8b92344..473d7d3 100644
--- a/public/js/util/trellis-chart.js
+++ b/public/js/util/trellis-chart.js
@@ -31,14 +31,15 @@ function trellisChart(anchor, monoGroups) {
var g = anchor.select("g");
/* Main columns, one for each attr */
- var columns;
var colBodies;
+ var brushContainers;
var _chart = function() {
if (g.empty()) {
renderBase();
}
colBodies.each(redrawCells);
+ brushContainers.each(redrawBrush);
}
function renderBase() {
@@ -157,19 +158,32 @@ function trellisChart(anchor, monoGroups) {
;
})
;
- dimLabelsG.selectAll("g.brush-container")
+ brushContainers = dimLabelsG.selectAll("g.brush-container")
.data(monoGroups)
.enter().append("g").attr("class", "brush-container")
.attr("transform", function(d, i) { return "translate(" + (i * subChartLength) + ",0)"; })
.each(function (d, i) {
d.id = i;
d.brush = d3.svg.brush();
+ d.brushRedrawNeeded = false;
d.filter = function(_) {
if (!arguments.length) return d._filter;
- d.dim.filter(_);
- d._filter = _;
+ if (_ != null) {
+ d._filter = _;
+ d.brush.extent(_);
+ d.dim.filter(_);
+ d.brushRedrawNeeded = true;
+ } else {
+ d.filterAll();
+ }
return d;
};
+ d.filterAll = function() {
+ d._filter = null;
+ d.brush.clear();
+ d.dim.filterAll();
+ d.brushRedrawNeeded = true;
+ }
})
.each(renderBrush)
;
@@ -179,7 +193,7 @@ function trellisChart(anchor, monoGroups) {
* monoGroups is an array of each stat dimension. We can consider each column to have data in the following format:
* { group: function, dim: function, name: stat }
*/
- columns = g.selectAll(".column")
+ var columns = g.selectAll(".column")
.data(monoGroups);
var colE = columns.enter().append("g").attr("class", "column")
@@ -253,7 +267,8 @@ function trellisChart(anchor, monoGroups) {
}
function renderBrush(d, i) {
- var columnG = d3.select(this);
+ var columnGDOM = this;
+ var columnG = d3.select(columnGDOM);
var brushG = columnG.select(".brush");
d.brush
@@ -261,7 +276,7 @@ function trellisChart(anchor, monoGroups) {
.on("brush", function () {
var extent = extendBrush(d, brushG);
- redrawBrush(d, i, brushG);
+ redrawBrush.call(columnGDOM, d, i);
if (brushIsEmpty(extent, d.brush)) {
dc.events.trigger(function () {
@@ -287,13 +302,20 @@ function trellisChart(anchor, monoGroups) {
}
}
- function redrawBrush(d, i, brushG) {
- if (d.filter() && d.brush.empty())
+ function redrawBrush(d, i) {
+ if (!d.brushRedrawNeeded) {
+ return;
+ }
+ if (d.filter() && d.brush.empty()) {
d.brush.extent(d.filter());
+ }
+ var brushG = d3.select(this).select(".brush");
brushG.call(d.brush.x(_scale));
brushG.selectAll("rect").attr("height", chartLen);
+ d.brushRedrawNeeded = false;
+
// TODO: fade the deselected area
}
@@ -326,18 +348,35 @@ function trellisChart(anchor, monoGroups) {
+ "V" + (2 * y - 8);
}
+ /*
+ * Interface assumptions:
+ * - filter with one argument uses this argument as a filter in a manner defined by the chart.
+ * - filter with no arguments returns a description of the chart's active filter(s) suitable for applying to a filter() function of the same chart type.
+ *
+ * Most chart's filter() uses the data type of crossfilter's filter(). The trellis chart, using multiple dimensions, cannot.
+ * Therefore, it uses the format { [name] -> filter } where filter is the format used by crossfilter's filter.
+ */
_chart.filter = function(_) {
- /*
- * TODO:
- * This is going to be interesting. As the chart is not charting a single
- * monogroup, and most code is built around this assumption, this might
- * well end up being a messy special case.
- */
if (!arguments.length) {
- return null;
+ var ret = null;
+ for (var key in monoGroups) {
+ if (monoGroups[key].filter()) {
+ (ret || (ret = {}))[attrs[key]] = monoGroups[key].filter();
+ }
+ }
+ return ret;
+ }
+ for (key in _) {
+ monoGroups[attrsIdByName[key]].filter(_[key]);
}
return _chart;
}
+ _chart.filterAll = function(_) {
+ for (var key in monoGroups) {
+ monoGroups[key].filterAll();
+ }
+ }
+
return _chart;
}