<script type="text/javascript" src="../tests/axis_labels.js"></script>
<script type="text/javascript" src="../tests/multi_csv.js"></script>
<script type="text/javascript" src="../tests/to_dom_coords.js"></script>
+ <script type="text/javascript" src="../tests/interaction_model.js"></script>
</head>
<body>
<div id='graph'></div>
g.canvas_.dispatchEvent(event);
};
-DygraphOps.dispatchMouseDown = function(g, x, y, custom) {
- var px = Dygraph.findPosX(g.canvas_);
- var py = Dygraph.findPosY(g.canvas_);
-
- var pageX = px + g.toDomXCoord(x);
- var pageY = py + g.toDomYCoord(y);
+DygraphOps.dispatchMouseDown_Point = function(g, x, y, custom) {
+ var pageX = Dygraph.findPosX(g.canvas_) + x;
+ var pageY = Dygraph.findPosY(g.canvas_) + y;
var opts = {
type : 'mousedown',
var event = DygraphOps.createEvent_(opts, custom);
g.canvas_.dispatchEvent(event);
-};
-
-DygraphOps.dispatchMouseMove = function(g, x, y, custom) {
- var px = Dygraph.findPosX(g.canvas_);
- var py = Dygraph.findPosY(g.canvas_);
+}
- var pageX = px + g.toDomXCoord(x);
- var pageY = py + g.toDomYCoord(y);
+DygraphOps.dispatchMouseMove_Point = function(g, x, y, custom) {
+ var pageX = Dygraph.findPosX(g.canvas_) + x;
+ var pageY = Dygraph.findPosY(g.canvas_) + y;
var opts = {
type : 'mousemove',
g.canvas_.dispatchEvent(event);
};
-DygraphOps.dispatchMouseUp = function(g, x, y, custom) {
- var px = Dygraph.findPosX(g.canvas_);
- var py = Dygraph.findPosY(g.canvas_);
-
- var pageX = px + g.toDomXCoord(x);
- var pageY = py + g.toDomYCoord(y);
+DygraphOps.dispatchMouseUp_Point = function(g, x, y, custom) {
+ var pageX = Dygraph.findPosX(g.canvas_) + x;
+ var pageY = Dygraph.findPosY(g.canvas_) + y;
var opts = {
type : 'mouseup',
var event = DygraphOps.createEvent_(opts, custom);
g.canvas_.dispatchEvent(event);
};
+
+/**
+ * Dispatches a mouse down using the graph's data coordinate system.
+ * (The y value mapped to the first axis.)
+ */
+DygraphOps.dispatchMouseDown = function(g, x, y, custom) {
+ DygraphOps.dispatchMouseDown_Point(
+ g,
+ g.toDomXCoord(x),
+ g.toDomYCoord(y),
+ custom);
+};
+
+/**
+ * Dispatches a mouse move using the graph's data coordinate system.
+ * (The y value mapped to the first axis.)
+ */
+DygraphOps.dispatchMouseMove = function(g, x, y, custom) {
+ DygraphOps.dispatchMouseMove_Point(
+ g,
+ g.toDomXCoord(x),
+ g.toDomYCoord(y),
+ custom);
+};
+
+/**
+ * Dispatches a mouse up using the graph's data coordinate system.
+ * (The y value mapped to the first axis.)
+ */
+DygraphOps.dispatchMouseUp = function(g, x, y, custom) {
+ DygraphOps.dispatchMouseUp_Point(
+ g,
+ g.toDomXCoord(x),
+ g.toDomYCoord(y),
+ custom);
+};
+
--- /dev/null
+/**
+ * @fileoverview Test cases for the interaction model.
+ *
+ * @author konigsberg@google.com (Robert Konigsbrg)
+ */
+var InteractionModelTestCase = TestCase("interaction-model");
+
+InteractionModelTestCase.prototype.setUp = function() {
+ document.body.innerHTML = "<div id='graph'></div>";
+};
+
+InteractionModelTestCase.prototype.tearDown = function() {
+};
+
+var data1 = "X,Y\n" +
+ "20,-1\n" +
+ "21,0\n" +
+ "22,1\n" +
+ "23,0\n";
+
+var data2 =
+ [[1, 10],
+ [2, 20],
+ [3, 30],
+ [4, 40],
+ [5, 120],
+ [6, 50],
+ [7, 70],
+ [8, 90],
+ [9, 50]];
+
+function getXLabels() {
+ var x_labels = document.getElementsByClassName("dygraph-axis-label-x");
+ var ary = [];
+ for (var i = 0; i < x_labels.length; i++) {
+ ary.push(x_labels[i].innerHTML);
+ }
+ return ary;
+}
+
+InteractionModelTestCase.prototype.pan = function(g, xRange, yRange) {
+ var originalXRange = g.xAxisRange();
+ var originalYRange = g.yAxisRange(0);
+
+ DygraphOps.dispatchMouseDown(g, xRange[0], yRange[0]);
+ DygraphOps.dispatchMouseMove(g, xRange[1], yRange[0]); // this is really necessary.
+ DygraphOps.dispatchMouseUp(g, xRange[1], yRange[0]);
+
+ assertEqualsDelta(xRange, g.xAxisRange(), 0.2);
+ // assertEqualsDelta(originalYRange, g.yAxisRange(0), 0.2); // Not true, it's something in the middle.
+
+ var midX = (xRange[1] - xRange[0]) / 2;
+ DygraphOps.dispatchMouseDown(g, midX, yRange[0]);
+ DygraphOps.dispatchMouseMove(g, midX, yRange[1]); // this is really necessary.
+ DygraphOps.dispatchMouseUp(g, midX, yRange[1]);
+
+ assertEqualsDelta(xRange, g.xAxisRange(), 0.2);
+ assertEqualsDelta(yRange, g.yAxisRange(0), 0.2);
+}
+
+/**
+ * This tests that when changing the interaction model so pan is used instead
+ * of zoom as the default behavior, a standard click method is still called.
+ */
+InteractionModelTestCase.prototype.testClickCallbackIsCalled = function() {
+ var clicked;
+
+ var clickCallback = function(event, x) {
+ clicked = x;
+ };
+
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data1,
+ {
+ width: 100,
+ height : 100,
+ clickCallback : clickCallback
+ });
+
+ DygraphOps.dispatchMouseDown_Point(g, 10, 10);
+ DygraphOps.dispatchMouseMove_Point(g, 10, 10);
+ DygraphOps.dispatchMouseUp_Point(g, 10, 10);
+
+ assertEquals(20, clicked);
+};
+
+/**
+ * This tests that when changing the interaction model so pan is used instead
+ * of zoom as the default behavior, a standard click method is still called.
+ */
+InteractionModelTestCase.prototype.testClickCallbackIsCalledOnCustomPan = function() {
+ var clicked;
+
+ var clickCallback = function(event, x) {
+ clicked = x;
+ };
+
+ function customDown(event, g, context) {
+ context.initializeMouseDown(event, g, context);
+ Dygraph.startPan(event, g, context);
+ }
+
+ function customMove(event, g, context) {
+ Dygraph.movePan(event, g, context);
+ }
+
+ function customUp(event, g, context) {
+ Dygraph.endPan(event, g, context);
+ }
+
+ var opts = {
+ width: 100,
+ height : 100,
+ clickCallback : clickCallback,
+ interactionModel : {
+ 'mousedown' : customDown,
+ 'mousemove' : customMove,
+ 'mouseup' : customUp,
+ }
+ };
+
+ var graph = document.getElementById("graph");
+ var g = new Dygraph(graph, data1, opts);
+
+ DygraphOps.dispatchMouseDown_Point(g, 10, 10);
+ DygraphOps.dispatchMouseMove_Point(g, 10, 10);
+ DygraphOps.dispatchMouseUp_Point(g, 10, 10);
+
+ assertEquals(20, clicked);
+};
+
+InteractionModelTestCase.clickAt = function(g, x, y) {
+ DygraphOps.dispatchMouseDown(g, x, y);
+ DygraphOps.dispatchMouseMove(g, x, y);
+ DygraphOps.dispatchMouseUp(g, x, y);
+}
+
+/**
+ * A sanity test to ensure pointClickCallback is called.
+ */
+InteractionModelTestCase.prototype.testPointClickCallback = function() {
+ var clicked;
+ var g = new Dygraph(document.getElementById("graph"), data2, {
+ pointClickCallback : function(event, point) {
+ clicked = point;
+ }
+ });
+
+ InteractionModelTestCase.clickAt(g, 4, 40);
+
+ assertEquals(4, clicked.xval);
+ assertEquals(40, clicked.yval);
+};
+
+/**
+ * A sanity test to ensure pointClickCallback is not called when out of range.
+ */
+InteractionModelTestCase.prototype.testNoPointClickCallbackWhenOffPoint = function() {
+ var clicked;
+ var g = new Dygraph(document.getElementById("graph"), data2, {
+ pointClickCallback : function(event, point) {
+ clicked = point;
+ }
+ });
+
+ InteractionModelTestCase.clickAt(g, 5, 40);
+
+ assertUndefined(clicked);
+};
+
+/**
+ * Ensures pointClickCallback circle size is taken into account.
+ */
+InteractionModelTestCase.prototype.testPointClickCallback_circleSize = function() {
+ // TODO(konigsberg): Implement.
+};
+
+/**
+ * Ensures that pointClickCallback is called prior to clickCallback
+ */
+InteractionModelTestCase.prototype.testPointClickCallbackCalledPriorToClickCallback = function() {
+ var counter = 0;
+ var pointClicked;
+ var clicked;
+ var g = new Dygraph(document.getElementById("graph"), data2, {
+ pointClickCallback : function(event, point) {
+ counter++;
+ pointClicked = counter;
+ },
+ clickCallback : function(event, point) {
+ counter++;
+ clicked = counter;
+ }
+ });
+
+ InteractionModelTestCase.clickAt(g, 4, 40);
+ assertEquals(1, pointClicked);
+ assertEquals(2, clicked);
+};
+
+/**
+ * Ensures that when there's no pointClickCallback, clicking on a point still calls
+ * clickCallback
+ */
+InteractionModelTestCase.prototype.testClickCallback_clickOnPoint = function() {
+ var clicked;
+ var g = new Dygraph(document.getElementById("graph"), data2, {
+ clickCallback : function(event, point) {
+ clicked = 1;
+ }
+ });
+
+ InteractionModelTestCase.clickAt(g, 4, 40);
+ assertEquals(1, clicked);
+};
+
var originalXRange = g.xAxisRange();
var originalYRange = g.yAxisRange(0);
- // Editing e.shiftKey post construction doesn't work for Firefox. Damn.
DygraphOps.dispatchMouseDown(g, xRange[0], yRange[0]);
DygraphOps.dispatchMouseMove(g, xRange[1], yRange[0]); // this is really necessary.
DygraphOps.dispatchMouseUp(g, xRange[1], yRange[0]);
g.updateOptions({});
- // This currently fails.
- // See http://code.google.com/p/dygraphs/issues/detail?id=192
assertEqualsDelta([11, 18], g.xAxisRange(), 0.1);
- // assertEqualsDelta([35, 40], g.yAxisRange(0), 0.2);
+ assertEqualsDelta([35, 40], g.yAxisRange(0), 0.2);
}
/**
* tick mark labels, the title and the x/y-axis labels.
* This class is based on PlotKit.CanvasRenderer.
*
+ * Creates a new DygraphLayout object.
+ * @return {Object} The DygraphLayout object
+ */
+DygraphLayout = function(dygraph) {
+ this.dygraph_ = dygraph;
+ this.datasets = new Array();
+ this.annotations = new Array();
+ this.yAxes_ = null;
+
+ // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs, but xticks and
+ // yticks are outputs. Clean this up.
+ this.xTicks_ = null;
+ this.yTicks_ = null;
+};
+
+DygraphLayout.prototype.attr_ = function(name) {
+ return this.dygraph_.attr_(name);
+};
+
+DygraphLayout.prototype.addDataset = function(setname, set_xy) {
+ this.datasets[setname] = set_xy;
+};
+
+DygraphLayout.prototype.setAnnotations = function(ann) {
+ // The Dygraph object's annotations aren't parsed. We parse them here and
+ // save a copy. If there is no parser, then the user must be using raw format.
+ this.annotations = [];
+ var parse = this.attr_('xValueParser') || function(x) { return x; };
+ for (var i = 0; i < ann.length; i++) {
+ var a = {};
+ if (!ann[i].xval && !ann[i].x) {
+ this.dygraph_.error("Annotations must have an 'x' property");
+ return;
+ }
+ if (ann[i].icon &&
+ !(ann[i].hasOwnProperty('width') &&
+ ann[i].hasOwnProperty('height'))) {
+ this.dygraph_.error("Must set width and height when setting " +
+ "annotation.icon property");
+ return;
+ }
+ Dygraph.update(a, ann[i]);
+ if (!a.xval) a.xval = parse(a.x, this.dygraph_);
+ this.annotations.push(a);
+ }
+};
+
+DygraphLayout.prototype.setXTicks = function(xTicks) {
+ this.xTicks_ = xTicks;
+};
+
+// TODO(danvk): add this to the Dygraph object's API or move it into Layout.
+DygraphLayout.prototype.setYAxes = function (yAxes) {
+ this.yAxes_ = yAxes;
+};
+
+DygraphLayout.prototype.setDateWindow = function(dateWindow) {
+ this.dateWindow_ = dateWindow;
+};
+
+DygraphLayout.prototype.evaluate = function() {
+ this._evaluateLimits();
+ this._evaluateLineCharts();
+ this._evaluateLineTicks();
+ this._evaluateAnnotations();
+};
+
+DygraphLayout.prototype._evaluateLimits = function() {
+ this.minxval = this.maxxval = null;
+ if (this.dateWindow_) {
+ this.minxval = this.dateWindow_[0];
+ this.maxxval = this.dateWindow_[1];
+ } else {
+ for (var name in this.datasets) {
+ if (!this.datasets.hasOwnProperty(name)) continue;
+ var series = this.datasets[name];
+ if (series.length > 1) {
+ var x1 = series[0][0];
+ if (!this.minxval || x1 < this.minxval) this.minxval = x1;
+
+ var x2 = series[series.length - 1][0];
+ if (!this.maxxval || x2 > this.maxxval) this.maxxval = x2;
+ }
+ }
+ }
+ this.xrange = this.maxxval - this.minxval;
+ this.xscale = (this.xrange != 0 ? 1/this.xrange : 1.0);
+
+ for (var i = 0; i < this.yAxes_.length; i++) {
+ var axis = this.yAxes_[i];
+ axis.minyval = axis.computedValueRange[0];
+ axis.maxyval = axis.computedValueRange[1];
+ axis.yrange = axis.maxyval - axis.minyval;
+ axis.yscale = (axis.yrange != 0 ? 1.0 / axis.yrange : 1.0);
+
+ if (axis.g.attr_("logscale")) {
+ axis.ylogrange = Dygraph.log10(axis.maxyval) - Dygraph.log10(axis.minyval);
+ axis.ylogscale = (axis.ylogrange != 0 ? 1.0 / axis.ylogrange : 1.0);
+ if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) {
+ axis.g.error('axis ' + i + ' of graph at ' + axis.g +
+ ' can\'t be displayed in log scale for range [' +
+ axis.minyval + ' - ' + axis.maxyval + ']');
+ }
+ }
+ }
+};
+
+DygraphLayout.prototype._evaluateLineCharts = function() {
+ // add all the rects
+ this.points = new Array();
+ for (var setName in this.datasets) {
+ if (!this.datasets.hasOwnProperty(setName)) continue;
+
+ var dataset = this.datasets[setName];
+ var axis = this.dygraph_.axisPropertiesForSeries(setName);
+
+ for (var j = 0; j < dataset.length; j++) {
+ var item = dataset[j];
+
+ var yval;
+ if (axis.logscale) {
+ yval = 1.0 - ((Dygraph.log10(parseFloat(item[1])) - Dygraph.log10(axis.minyval)) * axis.ylogscale); // really should just be yscale.
+ } else {
+ yval = 1.0 - ((parseFloat(item[1]) - axis.minyval) * axis.yscale);
+ }
+ var point = {
+ // TODO(danvk): here
+ x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
+ y: yval,
+ xval: parseFloat(item[0]),
+ yval: parseFloat(item[1]),
+ name: setName
+ };
+
+ this.points.push(point);
+ }
+ }
+};
+
+DygraphLayout.prototype._evaluateLineTicks = function() {
+ this.xticks = new Array();
+ for (var i = 0; i < this.xTicks_.length; i++) {
+ var tick = this.xTicks_[i];
+ var label = tick.label;
+ var pos = this.xscale * (tick.v - this.minxval);
+ if ((pos >= 0.0) && (pos <= 1.0)) {
+ this.xticks.push([pos, label]);
+ }
+ }
+
+ this.yticks = new Array();
+ for (var i = 0; i < this.yAxes_.length; i++ ) {
+ var axis = this.yAxes_[i];
+ for (var j = 0; j < axis.ticks.length; j++) {
+ var tick = axis.ticks[j];
+ var label = tick.label;
+ var pos = this.dygraph_.toPercentYCoord(tick.v, i);
+ if ((pos >= 0.0) && (pos <= 1.0)) {
+ this.yticks.push([i, pos, label]);
+ }
+ }
+ }
+};
+
+
+/**
+ * Behaves the same way as PlotKit.Layout, but also copies the errors
+ * @private
+ */
+DygraphLayout.prototype.evaluateWithError = function() {
+ this.evaluate();
+ if (!(this.attr_('errorBars') || this.attr_('customBars'))) return;
+
+ // Copy over the error terms
+ var i = 0; // index in this.points
+ for (var setName in this.datasets) {
+ if (!this.datasets.hasOwnProperty(setName)) continue;
+ var j = 0;
+ var dataset = this.datasets[setName];
+ for (var j = 0; j < dataset.length; j++, i++) {
+ var item = dataset[j];
+ var xv = parseFloat(item[0]);
+ var yv = parseFloat(item[1]);
+
+ if (xv == this.points[i].xval &&
+ yv == this.points[i].yval) {
+ this.points[i].errorMinus = parseFloat(item[2]);
+ this.points[i].errorPlus = parseFloat(item[3]);
+ }
+ }
+ }
+};
+
+DygraphLayout.prototype._evaluateAnnotations = function() {
+ // Add the annotations to the point to which they belong.
+ // Make a map from (setName, xval) to annotation for quick lookups.
+ var annotations = {};
+ for (var i = 0; i < this.annotations.length; i++) {
+ var a = this.annotations[i];
+ annotations[a.xval + "," + a.series] = a;
+ }
+
+ this.annotated_points = [];
+ for (var i = 0; i < this.points.length; i++) {
+ var p = this.points[i];
+ var k = p.xval + "," + p.name;
+ if (k in annotations) {
+ p.annotation = annotations[k];
+ this.annotated_points.push(p);
+ }
+ }
+};
+
+/**
+ * Convenience function to remove all the data sets from a graph
+ */
+DygraphLayout.prototype.removeAllDatasets = function() {
+ delete this.datasets;
+ this.datasets = new Array();
+};
+
+/**
+ * Return a copy of the point at the indicated index, with its yval unstacked.
+ * @param int index of point in layout_.points
+ */
+DygraphLayout.prototype.unstackPointAtIndex = function(idx) {
+ var point = this.points[idx];
+
+ // Clone the point since we modify it
+ var unstackedPoint = {};
+ for (var i in point) {
+ unstackedPoint[i] = point[i];
+ }
+
+ if (!this.attr_("stackedGraph")) {
+ return unstackedPoint;
+ }
+
+ // The unstacked yval is equal to the current yval minus the yval of the
+ // next point at the same xval.
+ for (var i = idx+1; i < this.points.length; i++) {
+ if (this.points[i].xval == point.xval) {
+ unstackedPoint.yval -= this.points[i].yval;
+ break;
+ }
+ }
+
+ return unstackedPoint;
+}
+
+/**
+ * The DygraphCanvasRenderer class does the actual rendering of the chart onto
+ * a canvas. It's based on PlotKit.CanvasRenderer.
+>>>>>>> master
* @param {Object} element The canvas to attach to
* @param {Object} elementContext The 2d context of the canvas (injected so it
* can be mocked for testing.)
*/
Dygraph.prototype.yAxisRange = function(idx) {
if (typeof(idx) == "undefined") idx = 0;
- if (idx < 0 || idx >= this.axes_.length) return null;
- return [ this.axes_[idx].computedValueRange[0],
- this.axes_[idx].computedValueRange[1] ];
+ if (idx < 0 || idx >= this.axes_.length) {
+ return null;
+ }
+ var axis = this.axes_[idx];
+ return [ axis.computedValueRange[0], axis.computedValueRange[1] ];
};
/**
}
}
- g.drawGraph_();
+ g.drawGraph_(false);
};
/**
* dragStartX/dragStartY/etc. properties). This function modifies the context.
*/
Dygraph.Interaction.endPan = function(event, g, context) {
+ context.dragEndX = g.dragGetX_(event, context);
+ context.dragEndY = g.dragGetY_(event, context);
+
+ var regionWidth = Math.abs(context.dragEndX - context.dragStartX);
+ var regionHeight = Math.abs(context.dragEndY - context.dragStartY);
+
+ if (regionWidth < 2 && regionHeight < 2 &&
+ g.lastx_ != undefined && g.lastx_ != -1) {
+ 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.prevDragDirection = context.dragDirection;
};
+Dygraph.Interaction.treatMouseOpAsClick = function(g, event, context) {
+ var clickCallback = g.attr_('clickCallback');
+ var pointClickCallback = g.attr_('pointClickCallback');
+
+ var selectedPoint = null;
+
+ // Find out if the click occurs on a point. This only matters if there's a pointClickCallback.
+ if (pointClickCallback) {
+ var closestIdx = -1;
+ var closestDistance = Number.MAX_VALUE;
+
+ // check if the click was on a particular point.
+ for (var i = 0; i < g.selPoints_.length; i++) {
+ var p = g.selPoints_[i];
+ var distance = Math.pow(p.canvasx - context.dragEndX, 2) +
+ Math.pow(p.canvasy - context.dragEndY, 2);
+ if (closestIdx == -1 || distance < closestDistance) {
+ closestDistance = distance;
+ closestIdx = i;
+ }
+ }
+
+ // Allow any click within two pixels of the dot.
+ var radius = g.attr_('highlightCircleSize') + 2;
+ if (closestDistance <= radius * radius) {
+ selectedPoint = g.selPoints_[closestIdx];
+ }
+ }
+
+ if (selectedPoint) {
+ pointClickCallback(event, selectedPoint);
+ }
+
+ // TODO(danvk): pass along more info about the points, e.g. 'x'
+ if (clickCallback) {
+ clickCallback(event, g.lastx_, g.selPoints_);
+ }
+};
+
/**
* Called in response to an interaction model operation that
* responds to an event that performs a zoom based on previously defined
* dragStartX/dragStartY/etc. properties). This function modifies the context.
*/
Dygraph.Interaction.endZoom = function(event, g, context) {
- // TODO(konigsberg): Refactor or rename this fn -- it deals with clicks, too.
context.isZooming = false;
context.dragEndX = g.dragGetX_(event, context);
context.dragEndY = g.dragGetY_(event, context);
if (regionWidth < 2 && regionHeight < 2 &&
g.lastx_ != undefined && g.lastx_ != -1) {
- // TODO(danvk): pass along more info about the points, e.g. 'x'
- if (g.attr_('clickCallback') != null) {
- g.attr_('clickCallback')(event, g.lastx_, g.selPoints_);
- }
- if (g.attr_('pointClickCallback')) {
- // check if the click was on a particular point.
- var closestIdx = -1;
- var closestDistance = 0;
- for (var i = 0; i < g.selPoints_.length; i++) {
- var p = g.selPoints_[i];
- var distance = Math.pow(p.canvasx - context.dragEndX, 2) +
- Math.pow(p.canvasy - context.dragEndY, 2);
- if (closestIdx == -1 || distance < closestDistance) {
- closestDistance = distance;
- closestIdx = i;
- }
- }
-
- // Allow any click within two pixels of the dot.
- var radius = g.attr_('highlightCircleSize') + 2;
- if (closestDistance <= 5 * 5) {
- g.attr_('pointClickCallback')(event, g.selPoints_[closestIdx]);
- }
- }
+ Dygraph.Interaction.treatMouseOpAsClick(g, event, context);
}
if (regionWidth >= 10 && context.dragDirection == Dygraph.HORIZONTAL) {
* Update the graph with new data. This method is called when the viewing area
* has changed. If the underlying data or options have changed, predraw_ will
* be called before drawGraph_ is called.
+ *
+ * clearSelection, when undefined or true, causes this.clearSelection to be
+ * called at the end of the draw operation. This should rarely be defined,
+ * and never true (that is it should be undefined most of the time, and
+ * rarely false.)
+ *
* @private
*/
-Dygraph.prototype.drawGraph_ = function() {
+Dygraph.prototype.drawGraph_ = function(clearSelection) {
+ if (typeof(clearSelection) === 'undefined') {
+ clearSelection = true;
+ }
+
var data = this.rawData_;
// This is used to set the second parameter to drawCallback, below.
// Generate a static legend before any particular point is selected.
this.setLegendHTML_();
} else {
- if (typeof(this.selPoints_) !== 'undefined' && this.selPoints_.length) {
- // We should select the point nearest the page x/y here, but it's easier
- // to just clear the selection. This prevents erroneous hover dots from
- // being displayed.
- this.clearSelection();
- } else {
- this.clearSelection();
+ if (clearSelection) {
+ if (typeof(this.selPoints_) !== 'undefined' && this.selPoints_.length) {
+ // We should select the point nearest the page x/y here, but it's easier
+ // to just clear the selection. This prevents erroneous hover dots from
+ // being displayed.
+ this.clearSelection();
+ } else {
+ this.clearSelection();
+ }
}
}
* indices are into the axes_ array.
*/
Dygraph.prototype.computeYAxes_ = function() {
+ // Preserve valueWindow settings if they exist, and if the user hasn't
+ // specified a new valueRange.
+ var valueWindows;
+ if (this.axes_ != undefined && this.user_attrs_.hasOwnProperty("valueRange") == false) {
+ valueWindows = [];
+ for (var index = 0; index < this.axes_.length; index++) {
+ valueWindows.push(this.axes_[index].valueWindow);
+ }
+ }
+
+
this.axes_ = [{ yAxisId : 0, g : this }]; // always have at least one y-axis.
this.seriesToAxisMap_ = {};
if (vis[i - 1]) seriesToAxisFiltered[s] = this.seriesToAxisMap_[s];
}
this.seriesToAxisMap_ = seriesToAxisFiltered;
+
+ if (valueWindows != undefined) {
+ // Restore valueWindow settings.
+ for (var index = 0; index < valueWindows.length; index++) {
+ this.axes_[index].valueWindow = valueWindows[index];
+ }
+ }
};
/**
// These functions are all based on MochiKit.
/**
+ * Copies all the properties from o to self.
+ *
* @private
*/
Dygraph.update = function (self, o) {