+};
+
+/**
+ * Returns the currently-visible x-range. This can be affected by zooming,
+ * panning or a call to updateOptions.
+ * Returns a two-element array: [left, right].
+ * If the Dygraph has dates on the x-axis, these will be millis since epoch.
+ */
+Dygraph.prototype.xAxisRange = function() {
+ if (this.dateWindow_) return this.dateWindow_;
+
+ // The entire chart is visible.
+ var left = this.rawData_[0][0];
+ var right = this.rawData_[this.rawData_.length - 1][0];
+ return [left, right];
+};
+
+/**
+ * Returns the currently-visible y-range for an axis. This can be affected by
+ * zooming, panning or a call to updateOptions. Axis indices are zero-based. If
+ * called with no arguments, returns the range of the first axis.
+ * Returns a two-element array: [bottom, top].
+ */
+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] ];
+};
+
+/**
+ * Returns the currently-visible y-ranges for each axis. This can be affected by
+ * zooming, panning, calls to updateOptions, etc.
+ * Returns an array of [bottom, top] pairs, one for each y-axis.
+ */
+Dygraph.prototype.yAxisRanges = function() {
+ var ret = [];
+ for (var i = 0; i < this.axes_.length; i++) {
+ ret.push(this.yAxisRange(i));
+ }
+ return ret;
+};
+
+// TODO(danvk): use these functions throughout dygraphs.
+/**
+ * Convert from data coordinates to canvas/div X/Y coordinates.
+ * If specified, do this conversion for the coordinate system of a particular
+ * axis. Uses the first axis by default.
+ * Returns a two-element array: [X, Y]
+ */
+Dygraph.prototype.toDomCoords = function(x, y, axis) {
+ var ret = [null, null];
+ var area = this.plotter_.area;
+ if (x !== null) {
+ var xRange = this.xAxisRange();
+ ret[0] = area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w;
+ }
+
+ if (y !== null) {
+ var yRange = this.yAxisRange(axis);
+ ret[1] = area.y + (yRange[1] - y) / (yRange[1] - yRange[0]) * area.h;
+ }
+
+ return ret;
+};
+
+/**
+ * Convert from canvas/div coords to data coordinates.
+ * If specified, do this conversion for the coordinate system of a particular
+ * axis. Uses the first axis by default.
+ * Returns a two-element array: [X, Y]
+ */
+Dygraph.prototype.toDataCoords = function(x, y, axis) {
+ var ret = [null, null];
+ var area = this.plotter_.area;
+ if (x !== null) {
+ var xRange = this.xAxisRange();
+ ret[0] = xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]);
+ }
+
+ if (y !== null) {
+ var yRange = this.yAxisRange(axis);
+ ret[1] = yRange[0] + (area.h - y) / area.h * (yRange[1] - yRange[0]);
+ }
+
+ return ret;
+};
+
+/**
+ * Returns the number of columns (including the independent variable).
+ */
+Dygraph.prototype.numColumns = function() {
+ return this.rawData_[0].length;
+};
+
+/**
+ * Returns the number of rows (excluding any header/label row).
+ */
+Dygraph.prototype.numRows = function() {
+ return this.rawData_.length;
+};
+
+/**
+ * Returns the value in the given row and column. If the row and column exceed
+ * the bounds on the data, returns null. Also returns null if the value is
+ * missing.
+ */
+Dygraph.prototype.getValue = function(row, col) {
+ if (row < 0 || row > this.rawData_.length) return null;
+ if (col < 0 || col > this.rawData_[row].length) return null;
+
+ return this.rawData_[row][col];
+};
+
+Dygraph.addEvent = function(el, evt, fn) {
+ var normed_fn = function(e) {
+ if (!e) var e = window.event;
+ fn(e);
+ };
+ if (window.addEventListener) { // Mozilla, Netscape, Firefox
+ el.addEventListener(evt, normed_fn, false);
+ } else { // IE
+ el.attachEvent('on' + evt, normed_fn);
+ }
+};
+
+
+// Based on the article at
+// http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel
+Dygraph.cancelEvent = function(e) {
+ e = e ? e : window.event;
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ }
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ e.cancelBubble = true;
+ e.cancel = true;
+ e.returnValue = false;
+ return false;