Missing consistent use of getYRanges(). Fixed zoom.html to use yRanges in API.
[dygraphs.git] / dygraph.js
index 8fbe9fa..0dba534 100644 (file)
@@ -166,6 +166,16 @@ Dygraph.prototype.__old_init__ = function(div, file, labels, attrs) {
  * @private
  */
 Dygraph.prototype.__init__ = function(div, file, attrs) {
+  // Hack for IE: if we're using excanvas and the document hasn't finished
+  // loading yet (and hence may not have initialized whatever it needs to
+  // initialize), then keep calling this routine periodically until it has.
+  if (/MSIE/.test(navigator.userAgent) && !window.opera &&
+      typeof(G_vmlCanvasManager) != 'undefined' &&
+      document.readyState != 'complete') {
+    var self = this;
+    setTimeout(function() { self.__init__(div, file, attrs) }, 100);
+  }
+
   // Support two-argument constructor
   if (attrs == null) { attrs = {}; }
 
@@ -189,10 +199,10 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
   // If the div isn't already sized then inherit from our attrs or
   // give it a default size.
   if (div.style.width == '') {
-    div.style.width = attrs.width || Dygraph.DEFAULT_WIDTH + "px";
+    div.style.width = (attrs.width || Dygraph.DEFAULT_WIDTH) + "px";
   }
   if (div.style.height == '') {
-    div.style.height = attrs.height || Dygraph.DEFAULT_HEIGHT + "px";
+    div.style.height = (attrs.height || Dygraph.DEFAULT_HEIGHT) + "px";
   }
   this.width_ = parseInt(div.style.width, 10);
   this.height_ = parseInt(div.style.height, 10);
@@ -691,7 +701,7 @@ Dygraph.prototype.positionLabelsDiv_ = function() {
 
   var area = this.plotter_.area;
   var div = this.attr_("labelsDiv");
-  div.style.left = area.x + area.w - this.attr_("labelsDivWidth") + "px";
+  div.style.left = area.x + area.w - this.attr_("labelsDivWidth") - 1 + "px";
 };
 
 /**
@@ -843,6 +853,14 @@ Dygraph.prototype.createDragInterface_ = function() {
 
   // Track the beginning of drag events
   Dygraph.addEvent(this.mouseEventElement_, 'mousedown', function(event) {
+    // prevents mouse drags from selecting page text.
+    if (event.preventDefault) {
+      event.preventDefault();  // Firefox, Chrome, etc.
+    } else {
+      event.returnValue = false;  // IE
+      event.cancelBubble = true;  
+    }
+
     px = Dygraph.findPosX(self.canvas_);
     py = Dygraph.findPosY(self.canvas_);
     dragStartX = getX(event);
@@ -1069,7 +1087,7 @@ Dygraph.prototype.doZoomXDates_ = function(minDate, maxDate) {
   this.drawGraph_();
   if (this.attr_("zoomCallback")) {
     var yRange = this.yAxisRange();
-    this.attr_("zoomCallback")(minDate, maxDate, yRange[0], yRange[1]);
+    this.attr_("zoomCallback")(minDate, maxDate, this.yAxisRanges());
   }
 };
 
@@ -1152,6 +1170,8 @@ Dygraph.prototype.mouseMove_ = function(event) {
   var minDist = 1e+100;
   var idx = -1;
   for (var i = 0; i < points.length; i++) {
+    var point = points[i];
+    if (point == null) continue;
     var dist = Math.abs(points[i].canvasx - canvasx);
     if (dist > minDist) continue;
     minDist = dist;
@@ -1159,7 +1179,8 @@ Dygraph.prototype.mouseMove_ = function(event) {
   }
   if (idx >= 0) lastx = points[idx].xval;
   // Check that you can really highlight the last day's data
-  if (canvasx > points[points.length-1].canvasx)
+  var last = points[points.length-1];
+  if (last != null && canvasx > last.canvasx)
     lastx = points[points.length-1].xval;
 
   // Extract the points we've selected
@@ -1192,7 +1213,7 @@ Dygraph.prototype.mouseMove_ = function(event) {
     var px = this.lastx_;
     if (px !== null && lastx != px) {
       // only fire if the selected point has changed.
-      this.attr_("highlightCallback")(event, lastx, this.selPoints_);
+      this.attr_("highlightCallback")(event, lastx, this.selPoints_, this.idxToRow_(idx));
     }
   }
 
@@ -1203,6 +1224,24 @@ Dygraph.prototype.mouseMove_ = function(event) {
 };
 
 /**
+ * Transforms layout_.points index into data row number.
+ * @param int layout_.points index
+ * @return int row number, or -1 if none could be found.
+ * @private
+ */
+Dygraph.prototype.idxToRow_ = function(idx) {
+  if (idx < 0) return -1;
+
+  for (var i in this.layout_.datasets) {
+    if (idx < this.layout_.datasets[i].length) {
+      return this.boundaryIds_[0][0]+idx;
+    }
+    idx -= this.layout_.datasets[i].length;
+  }
+  return -1;
+};
+
+/**
  * Draw dots over the selectied points in the data series. This function
  * takes care of cleanup of previously-drawn dots.
  * @private
@@ -2374,7 +2413,8 @@ Dygraph.prototype.parseCSV_ = function(data) {
   // Parse the x as a float or return null if it's not a number.
   var parseFloatOrNull = function(x) {
     var val = parseFloat(x);
-    return isNaN(val) ? null : val;
+    // isFinite() returns false for NaN and +/-Infinity.
+    return isFinite(val) ? val : null;
   };
 
   var xParser;
@@ -2606,6 +2646,11 @@ Dygraph.prototype.parseDataTable_ = function(data) {
     if (ret.length > 0 && row[0] < ret[ret.length - 1][0]) {
       outOfOrder = true;
     }
+
+    // Strip out infinities, which give dygraphs problems later on.
+    for (var j = 0; j < row.length; j++) {
+      if (!isFinite(row[j])) row[j] = null;
+    }
     ret.push(row);
   }
 
@@ -2820,7 +2865,7 @@ Dygraph.prototype.visibility = function() {
  */
 Dygraph.prototype.setVisibility = function(num, value) {
   var x = this.visibility();
-  if (num < 0 && num >= x.length) {
+  if (num < 0 || num >= x.length) {
     this.warn("invalid series number in setVisibility: " + num);
   } else {
     x[num] = value;
@@ -2863,30 +2908,36 @@ Dygraph.prototype.indexFromSetName = function(name) {
 Dygraph.addAnnotationRule = function() {
   if (Dygraph.addedAnnotationCSS) return;
 
-  var mysheet;
-  if (document.styleSheets.length > 0) {
-    mysheet = document.styleSheets[0];
-  } else {
-    var styleSheetElement = document.createElement("style");
-    styleSheetElement.type = "text/css";
-    document.getElementsByTagName("head")[0].appendChild(styleSheetElement);
-    for(i = 0; i < document.styleSheets.length; i++) {
-      if (document.styleSheets[i].disabled) continue;
-      mysheet = document.styleSheets[i];
-    }
-  }
-
   var rule = "border: 1px solid black; " +
              "background-color: white; " +
              "text-align: center;";
-  if (mysheet.insertRule) {  // Firefox
-    var idx = mysheet.cssRules ? mysheet.cssRules.length : 0;
-    mysheet.insertRule(".dygraphDefaultAnnotation { " + rule + " }", idx);
-  } else if (mysheet.addRule) {  // IE
-    mysheet.addRule(".dygraphDefaultAnnotation", rule);
+
+  var styleSheetElement = document.createElement("style");
+  styleSheetElement.type = "text/css";
+  document.getElementsByTagName("head")[0].appendChild(styleSheetElement);
+
+  // Find the first style sheet that we can access.
+  // We may not add a rule to a style sheet from another domain for security
+  // reasons. This sometimes comes up when using gviz, since the Google gviz JS
+  // adds its own style sheets from google.com.
+  for (var i = 0; i < document.styleSheets.length; i++) {
+    if (document.styleSheets[i].disabled) continue;
+    var mysheet = document.styleSheets[i];
+    try {
+      if (mysheet.insertRule) {  // Firefox
+        var idx = mysheet.cssRules ? mysheet.cssRules.length : 0;
+        mysheet.insertRule(".dygraphDefaultAnnotation { " + rule + " }", idx);
+      } else if (mysheet.addRule) {  // IE
+        mysheet.addRule(".dygraphDefaultAnnotation", rule);
+      }
+      Dygraph.addedAnnotationCSS = true;
+      return;
+    } catch(err) {
+      // Was likely a security exception.
+    }
   }
 
-  Dygraph.addedAnnotationCSS = true;
+  this.warn("Unable to add default annotation CSS rule; display may be off.");
 }
 
 /**
@@ -2914,7 +2965,14 @@ Dygraph.GVizChart = function(container) {
 }
 
 Dygraph.GVizChart.prototype.draw = function(data, options) {
+  // Clear out any existing dygraph.
+  // TODO(danvk): would it make more sense to simply redraw using the current
+  // date_graph object?
   this.container.innerHTML = '';
+  if (typeof(this.date_graph) != 'undefined') {
+    this.date_graph.destroy();
+  }
+
   this.date_graph = new Dygraph(this.container, data, options);
 }