+ var that = this;
+ Dygraph.repeatAndCleanup(function(step) {
+ if (valueRanges.length) {
+ for (var i = 0; i < that.axes_.length; i++) {
+ var w = valueRanges[step][i];
+ that.axes_[i].valueWindow = [w[0], w[1]];
+ }
+ }
+ if (windows.length) {
+ that.dateWindow_ = windows[step];
+ }
+ that.drawGraph_();
+ }, steps, Dygraph.ANIMATION_DURATION / steps, callback);
+};
+
+/**
+ * Get the current graph's area object.
+ *
+ * Returns: {x, y, w, h}
+ */
+Dygraph.prototype.getArea = function() {
+ return this.plotter_.area;
+};
+
+/**
+ * Convert a mouse event to DOM coordinates relative to the graph origin.
+ *
+ * Returns a two-element array: [X, Y].
+ */
+Dygraph.prototype.eventToDomCoords = function(event) {
+ var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(this.mouseEventElement_);
+ var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(this.mouseEventElement_);
+ return [canvasx, canvasy];
+};
+
+/**
+ * Given a canvas X coordinate, find the closest row.
+ * @param {Number} domX graph-relative DOM X coordinate
+ * Returns: row number, integer
+ * @private
+ */
+Dygraph.prototype.findClosestRow = function(domX) {
+ var minDistX = Infinity;
+ var pointIdx = -1, setIdx = -1;
+ var sets = this.layout_.points;
+ for (var i = 0; i < sets.length; i++) {
+ var points = sets[i];
+ var len = points.length;
+ for (var j = 0; j < len; j++) {
+ var point = points[j];
+ if (!Dygraph.isValidPoint(point, true)) continue;
+ var dist = Math.abs(point.canvasx - domX);
+ if (dist < minDistX) {
+ minDistX = dist;
+ setIdx = i;
+ pointIdx = j;
+ }
+ }
+ }
+
+ // TODO(danvk): remove this function; it's trivial and has only one use.
+ return this.idxToRow_(setIdx, pointIdx);
+};
+
+/**
+ * Given canvas X,Y coordinates, find the closest point.
+ *
+ * This finds the individual data point across all visible series
+ * that's closest to the supplied DOM coordinates using the standard
+ * Euclidean X,Y distance.
+ *
+ * @param {Number} domX graph-relative DOM X coordinate
+ * @param {Number} domY graph-relative DOM Y coordinate
+ * Returns: {row, seriesName, point}
+ * @private
+ */
+Dygraph.prototype.findClosestPoint = function(domX, domY) {
+ var minDist = Infinity;
+ var idx = -1;
+ var dist, dx, dy, point, closestPoint, closestSeries;
+ for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
+ var points = this.layout_.points[setIdx];
+ for (var i = 0; i < points.length; ++i) {
+ var point = points[i];
+ if (!Dygraph.isValidPoint(point)) continue;
+ dx = point.canvasx - domX;
+ dy = point.canvasy - domY;
+ dist = dx * dx + dy * dy;
+ if (dist < minDist) {
+ minDist = dist;
+ closestPoint = point;
+ closestSeries = setIdx;
+ idx = i;
+ }
+ }
+ }
+ var name = this.layout_.setNames[closestSeries];
+ return {
+ row: idx + this.getLeftBoundary_(),
+ seriesName: name,
+ point: closestPoint
+ };
+};
+
+/**
+ * Given canvas X,Y coordinates, find the touched area in a stacked graph.
+ *
+ * This first finds the X data point closest to the supplied DOM X coordinate,
+ * then finds the series which puts the Y coordinate on top of its filled area,
+ * using linear interpolation between adjacent point pairs.
+ *
+ * @param {Number} domX graph-relative DOM X coordinate
+ * @param {Number} domY graph-relative DOM Y coordinate
+ * Returns: {row, seriesName, point}
+ * @private
+ */
+Dygraph.prototype.findStackedPoint = function(domX, domY) {
+ var row = this.findClosestRow(domX);
+ var boundary = this.getLeftBoundary_();
+ var rowIdx = row - boundary;
+ var sets = this.layout_.points;
+ var closestPoint, closestSeries;
+ for (var setIdx = 0; setIdx < this.layout_.datasets.length; ++setIdx) {
+ var points = this.layout_.points[setIdx];
+ if (rowIdx >= points.length) continue;
+ var p1 = points[rowIdx];
+ if (!Dygraph.isValidPoint(p1)) continue;
+ var py = p1.canvasy;
+ if (domX > p1.canvasx && rowIdx + 1 < points.length) {
+ // interpolate series Y value using next point
+ var p2 = points[rowIdx + 1];
+ if (Dygraph.isValidPoint(p2)) {
+ var dx = p2.canvasx - p1.canvasx;
+ if (dx > 0) {
+ var r = (domX - p1.canvasx) / dx;
+ py += r * (p2.canvasy - p1.canvasy);
+ }
+ }
+ } else if (domX < p1.canvasx && rowIdx > 0) {
+ // interpolate series Y value using previous point
+ var p0 = points[rowIdx - 1];
+ if (Dygraph.isValidPoint(p0)) {
+ var dx = p1.canvasx - p0.canvasx;
+ if (dx > 0) {
+ var r = (p1.canvasx - domX) / dx;
+ py += r * (p0.canvasy - p1.canvasy);
+ }
+ }
+ }
+ // Stop if the point (domX, py) is above this series' upper edge
+ if (setIdx == 0 || py < domY) {
+ closestPoint = p1;
+ closestSeries = setIdx;