From 97583b90be3e80c95654d010db8a4b3f8813bb7a Mon Sep 17 00:00:00 2001 From: Dan Vanderkam Date: Sun, 4 Mar 2012 09:46:37 -0500 Subject: [PATCH] Fix a bug involving calls to updateOptions() while panning. This showed up with data that gets updated periodically, i.e. the once/second updates in dynamic-update.html. Previously the chart would disappear whenever an update occurred. --- auto_tests/misc/local.html | 1 + auto_tests/tests/error_bars.js | 2 +- auto_tests/tests/update_while_panning.js | 58 ++++++++++++++++++++++++++++++++ dygraph-interaction-model.js | 28 +++++++++------ 4 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 auto_tests/tests/update_while_panning.js diff --git a/auto_tests/misc/local.html b/auto_tests/misc/local.html index 8e4cfdd..ae39e7f 100644 --- a/auto_tests/misc/local.html +++ b/auto_tests/misc/local.html @@ -36,6 +36,7 @@ + diff --git a/auto_tests/tests/error_bars.js b/auto_tests/tests/error_bars.js index 0d8b3ba..8bc20e3 100644 --- a/auto_tests/tests/error_bars.js +++ b/auto_tests/tests/error_bars.js @@ -21,7 +21,7 @@ errorBarsTestCase.prototype.tearDown = function() { Dygraph.getContext = _origFunc; }; -errorBarsTestCase.prototype.testNameGoesHere = function() { +errorBarsTestCase.prototype.testErrorBarsDrawn = function() { var opts = { width: 480, height: 320, diff --git a/auto_tests/tests/update_while_panning.js b/auto_tests/tests/update_while_panning.js new file mode 100644 index 0000000..8634bd0 --- /dev/null +++ b/auto_tests/tests/update_while_panning.js @@ -0,0 +1,58 @@ +/** + * @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 = "
"; +}; + +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()); +}; + diff --git a/dygraph-interaction-model.js b/dygraph-interaction-model.js index a3de6f7..c57ad25 100644 --- a/dygraph-interaction-model.js +++ b/dygraph-interaction-model.js @@ -72,20 +72,26 @@ Dygraph.Interaction.startPan = function(event, g, context) { // 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; @@ -130,23 +136,24 @@ Dygraph.Interaction.movePan = function(event, g, context) { // 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) { @@ -186,8 +193,6 @@ Dygraph.Interaction.endPan = function(event, g, context) { 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; @@ -197,6 +202,7 @@ Dygraph.Interaction.endPan = function(event, g, context) { context.valueRange = null; context.boundedDates = null; context.boundedValues = null; + context.axes = null; }; /** -- 2.7.4