--- /dev/null
+/**
+ * @fileoverview Regression test for a bug involving data update while panning.
+ *
+ * See http://stackoverflow.com/questions/9528173
+ *
+ * @author dan@dygraphs.com (Dan Vanderkam)
+ */
+var updateWhilePanningTestCase = TestCase("update-while-panning");
+
+updateWhilePanningTestCase.prototype.setUp = function() {
+ document.body.innerHTML = "<div id='graph'></div>";
+};
+
+updateWhilePanningTestCase.prototype.tearDown = function() {
+};
+
+// This tests the following sequence:
+// 1. Begin dragging a chart (x-panning)
+// 2. Do a data update (updateOptions({file: ...}))
+// 3. Verify that the y-axis is still well-defined.
+updateWhilePanningTestCase.prototype.testUpdateWhilePanning = function() {
+ var sinewave = function(start, limit, step) {
+ var data = [];
+ for (var x = start; x < limit; x += step) {
+ data.push([x, Math.sin(x)]);
+ }
+ return data;
+ };
+
+ var opts = {
+ width: 480,
+ height: 320,
+ valueRange: [-2, 2]
+ };
+
+ var graph = document.getElementById("graph");
+
+ var g = new Dygraph(graph, sinewave(0, 6, 0.1), opts);
+ assertEquals([-2, 2], g.yAxisRange());
+
+ // Start a pan, but don't finish it yet.
+ DygraphOps.dispatchMouseDown_Point(g, 200, 100, {shiftKey: true});
+ DygraphOps.dispatchMouseMove_Point(g, 100, 100, {shiftKey: true});
+ assertEquals([-2, 2], g.yAxisRange());
+
+ // Now do a data update. y-axis should remain the same.
+ g.updateOptions({file: sinewave(0, 7, 0.1)});
+ assertEquals([-2, 2], g.yAxisRange());
+
+ // Keep the pan going.
+ DygraphOps.dispatchMouseMove_Point(g, 50, 100, {shiftKey: true});
+ assertEquals([-2, 2], g.yAxisRange());
+
+ // Now finish the pan.
+ DygraphOps.dispatchMouseUp_Point(g, 100, 100, {shiftKey: true});
+ assertEquals([-2, 2], g.yAxisRange());
+};
+
// Record the range of each y-axis at the start of the drag.
// If any axis has a valueRange or valueWindow, then we want a 2D pan.
+ // We can't store data directly in g.axes_, because it does not belong to us
+ // and could change out from under us during a pan (say if there's a data
+ // update).
context.is2DPan = false;
+ context.axes = [];
for (i = 0; i < g.axes_.length; i++) {
axis = g.axes_[i];
+ var axis_data = {};
var yRange = g.yAxisRange(i);
// TODO(konigsberg): These values should be in |context|.
// In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale.
if (axis.logscale) {
- axis.initialTopValue = Dygraph.log10(yRange[1]);
- axis.dragValueRange = Dygraph.log10(yRange[1]) - Dygraph.log10(yRange[0]);
+ axis_data.initialTopValue = Dygraph.log10(yRange[1]);
+ axis_data.dragValueRange = Dygraph.log10(yRange[1]) - Dygraph.log10(yRange[0]);
} else {
- axis.initialTopValue = yRange[1];
- axis.dragValueRange = yRange[1] - yRange[0];
+ axis_data.initialTopValue = yRange[1];
+ axis_data.dragValueRange = yRange[1] - yRange[0];
}
- axis.unitsPerPixel = axis.dragValueRange / (g.plotter_.area.h - 1);
+ axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1);
+ context.axes.push(axis_data);
// While calculating axes, set 2dpan.
if (axis.valueWindow || axis.valueRange) context.is2DPan = true;
// Adjust each axis appropriately.
for (var i = 0; i < g.axes_.length; i++) {
var axis = g.axes_[i];
+ var axis_data = context.axes[i];
var pixelsDragged = context.dragEndY - context.dragStartY;
- var unitsDragged = pixelsDragged * axis.unitsPerPixel;
+ var unitsDragged = pixelsDragged * axis_data.unitsPerPixel;
var boundedValue = context.boundedValues ? context.boundedValues[i] : null;
// In log scale, maxValue and minValue are the logs of those values.
- var maxValue = axis.initialTopValue + unitsDragged;
+ var maxValue = axis_data.initialTopValue + unitsDragged;
if (boundedValue) {
maxValue = Math.min(maxValue, boundedValue[1]);
}
- var minValue = maxValue - axis.dragValueRange;
+ var minValue = maxValue - axis_data.dragValueRange;
if (boundedValue) {
if (minValue < boundedValue[0]) {
// Adjust maxValue, and recompute minValue.
maxValue = maxValue - (minValue - boundedValue[0]);
- minValue = maxValue - axis.dragValueRange;
+ minValue = maxValue - axis_data.dragValueRange;
}
}
if (axis.logscale) {
Dygraph.Interaction.treatMouseOpAsClick(g, event, context);
}
- // TODO(konigsberg): Clear the context data from the axis.
- // (replace with "context = {}" ?)
// TODO(konigsberg): mouseup should just delete the
// context object, and mousedown should create a new one.
context.isPanning = false;
context.valueRange = null;
context.boundedDates = null;
context.boundedValues = null;
+ context.axes = null;
};
/**