+};
+
+/**
+ * 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. This can be affected by zooming,
+ * panning or a call to updateOptions.
+ * Returns a two-element array: [bottom, top].
+ */
+Dygraph.prototype.yAxisRange = function() {
+ return this.displayedYRange_;
+};
+
+/**
+ * Convert from data coordinates to canvas/div X/Y coordinates.
+ * Returns a two-element array: [X, Y]
+ */
+Dygraph.prototype.toDomCoords = function(x, y) {
+ 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();
+ ret[1] = area.y + (yRange[1] - y) / (yRange[1] - yRange[0]) * area.h;
+ }
+
+ return ret;
+};
+
+// TODO(danvk): use these functions throughout dygraphs.
+/**
+ * Convert from canvas/div coords to data coordinates.
+ * Returns a two-element array: [X, Y]
+ */
+Dygraph.prototype.toDataCoords = function(x, y) {
+ 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();
+ 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);
+ }
+};
+
+Dygraph.clipCanvas_ = function(cnv, clip) {
+ var ctx = cnv.getContext("2d");
+ ctx.beginPath();
+ ctx.rect(clip.left, clip.top, clip.width, clip.height);
+ ctx.clip();
+};