actually draw the graph
[dygraphs.git] / dygraph.js
index 4fc2fdc..8c3bc59 100644 (file)
@@ -91,6 +91,7 @@ Dygraph.DEFAULT_ATTRS = {
   },
   labelsSeparateLines: false,
   labelsKMB: false,
+  labelsKMG2: false,
 
   strokeWidth: 1.0,
 
@@ -191,30 +192,6 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
   // Create the containing DIV and other interactive elements
   this.createInterface_();
 
-  // Create the PlotKit grapher
-  // TODO(danvk): why does the Layout need its own set of options?
-  this.layoutOptions_ = { 'xOriginIsZero': false };
-  Dygraph.update(this.layoutOptions_, this.attrs_);
-  Dygraph.update(this.layoutOptions_, this.user_attrs_);
-  Dygraph.update(this.layoutOptions_, {
-    'errorBars': (this.attr_("errorBars") || this.attr_("customBars")) });
-
-  this.layout_ = new DygraphLayout(this, this.layoutOptions_);
-
-  // TODO(danvk): why does the Renderer need its own set of options?
-  this.renderOptions_ = { colorScheme: this.colors_,
-                          strokeColor: null,
-                          axisLineWidth: Dygraph.AXIS_LINE_WIDTH };
-  Dygraph.update(this.renderOptions_, this.attrs_);
-  Dygraph.update(this.renderOptions_, this.user_attrs_);
-  this.plotter_ = new DygraphCanvasRenderer(this,
-                                            this.hidden_, this.layout_,
-                                            this.renderOptions_);
-
-  this.createStatusMessage_();
-  this.createRollInterface_();
-  this.createDragInterface_();
-
   this.start_();
 };
 
@@ -280,7 +257,7 @@ Dygraph.addEvent = function(el, evt, fn) {
 /**
  * Generates interface elements for the Dygraph: a containing div, a div to
  * display the current point, and a textbox to adjust the rolling average
- * period.
+ * period. Also creates the Renderer/Layout elements.
  * @private
  */
 Dygraph.prototype.createInterface_ = function() {
@@ -312,6 +289,30 @@ Dygraph.prototype.createInterface_ = function() {
   Dygraph.addEvent(this.hidden_, 'mouseout', function(e) {
     dygraph.mouseOut_(e);
   });
+
+  // Create the grapher
+  // TODO(danvk): why does the Layout need its own set of options?
+  this.layoutOptions_ = { 'xOriginIsZero': false };
+  Dygraph.update(this.layoutOptions_, this.attrs_);
+  Dygraph.update(this.layoutOptions_, this.user_attrs_);
+  Dygraph.update(this.layoutOptions_, {
+    'errorBars': (this.attr_("errorBars") || this.attr_("customBars")) });
+
+  this.layout_ = new DygraphLayout(this, this.layoutOptions_);
+
+  // TODO(danvk): why does the Renderer need its own set of options?
+  this.renderOptions_ = { colorScheme: this.colors_,
+                          strokeColor: null,
+                          axisLineWidth: Dygraph.AXIS_LINE_WIDTH };
+  Dygraph.update(this.renderOptions_, this.attrs_);
+  Dygraph.update(this.renderOptions_, this.user_attrs_);
+  this.plotter_ = new DygraphCanvasRenderer(this,
+                                            this.hidden_, this.layout_,
+                                            this.renderOptions_);
+
+  this.createStatusMessage_();
+  this.createRollInterface_();
+  this.createDragInterface_();
 }
 
 /**
@@ -451,7 +452,9 @@ Dygraph.prototype.createStatusMessage_ = function(){
     Dygraph.update(messagestyle, this.attr_('labelsDivStyles'));
     var div = document.createElement("div");
     for (var name in messagestyle) {
-      div.style[name] = messagestyle[name];
+      if (messagestyle.hasOwnProperty(name)) {
+        div.style[name] = messagestyle[name];
+      }
     }
     this.graphDiv.appendChild(div);
     this.attrs_.labelsDiv = div;
@@ -476,7 +479,9 @@ Dygraph.prototype.createRollInterface_ = function() {
   roller.size = "2";
   roller.value = this.rollPeriod_;
   for (var name in textAttr) {
-    roller.style[name] = textAttr[name];
+    if (textAttr.hasOwnProperty(name)) {
+      roller.style[name] = textAttr[name];
+    }
   }
 
   var pa = this.graphDiv;
@@ -896,7 +901,7 @@ Dygraph.SHORT_SPACINGS[Dygraph.MINUTELY]        = 1000 * 60;
 Dygraph.SHORT_SPACINGS[Dygraph.TEN_MINUTELY]    = 1000 * 60 * 10;
 Dygraph.SHORT_SPACINGS[Dygraph.THIRTY_MINUTELY] = 1000 * 60 * 30;
 Dygraph.SHORT_SPACINGS[Dygraph.HOURLY]          = 1000 * 3600;
-Dygraph.SHORT_SPACINGS[Dygraph.HOURLY]          = 1000 * 3600 * 6;
+Dygraph.SHORT_SPACINGS[Dygraph.SIX_HOURLY]      = 1000 * 3600 * 6;
 Dygraph.SHORT_SPACINGS[Dygraph.DAILY]           = 1000 * 86400;
 Dygraph.SHORT_SPACINGS[Dygraph.WEEKLY]          = 1000 * 604800;
 
@@ -1045,17 +1050,30 @@ Dygraph.numericTicks = function(minV, maxV, self) {
 
   // Construct labels for the ticks
   var ticks = [];
+  var k;
+  var k_labels = [];
+  if (self.attr_("labelsKMB")) {
+    k = 1000;
+    k_labels = [ "K", "M", "B", "T" ];
+  }
+  if (self.attr_("labelsKMG2")) {
+    if (k) self.warn("Setting both labelsKMB and labelsKMG2. Pick one!");
+    k = 1024;
+    k_labels = [ "k", "M", "G", "T" ];
+  }
+
   for (var i = 0; i < nTicks; i++) {
     var tickV = low_val + i * scale;
+    var absTickV = Math.abs(tickV);
     var label = self.round_(tickV, 2);
-    if (self.attr_("labelsKMB")) {
-      var k = 1000;
-      if (tickV >= k*k*k) {
-        label = self.round_(tickV/(k*k*k), 1) + "B";
-      } else if (tickV >= k*k) {
-        label = self.round_(tickV/(k*k), 1) + "M";
-      } else if (tickV >= k) {
-        label = self.round_(tickV/k, 1) + "K";
+    if (k_labels.length) {
+      // Round up to an appropriate unit.
+      var n = k*k*k*k;
+      for (var j = 3; j >= 0; j--, n /= k) {
+        if (absTickV >= n) {
+          label = self.round_(tickV / n, 1) + k_labels[j];
+          break;
+        }
       }
     }
     ticks.push( {label: label, v: tickV} );
@@ -1607,7 +1625,9 @@ Dygraph.prototype.parseDataTable_ = function(data) {
 Dygraph.update = function (self, o) {
   if (typeof(o) != 'undefined' && o !== null) {
     for (var k in o) {
-      self[k] = o[k];
+      if (o.hasOwnProperty(k)) {
+        self[k] = o[k];
+      }
     }
   }
   return self;
@@ -1723,6 +1743,39 @@ Dygraph.prototype.updateOptions = function(attrs) {
 };
 
 /**
+ * Resizes the dygraph. If no parameters are specified, resizes to fill the
+ * containing div (which has presumably changed size since the dygraph was
+ * instantiated. If the width/height are specified, the div will be resized.
+ *
+ * This is far more efficient than destroying and re-instantiating a
+ * Dygraph, since it doesn't have to reparse the underlying data.
+ *
+ * @param {Number} width Width (in pixels)
+ * @param {Number} height Height (in pixels)
+ */
+Dygraph.prototype.resize = function(width, height) {
+  if ((width === null) != (height === null)) {
+    this.warn("Dygraph.resize() should be called with zero parameters or " +
+              "two non-NULL parameters. Pretending it was zero.");
+    width = height = null;
+  }
+
+  this.maindiv_.innerHTML = "";
+  if (width) {
+    this.maindiv_.style.width = width + "px";
+    this.maindiv_.style.height = height + "px";
+    this.width_ = width;
+    this.height_ = height;
+  } else {
+    this.width_ = this.maindiv_.offsetWidth;
+    this.height_ = this.maindiv_.offsetHeight;
+  }
+
+  this.createInterface_();
+  this.drawGraph_(this.rawData_);
+};
+
+/**
  * Adjusts the number of days in the rolling average. Updates the graph to
  * reflect the new averaging period.
  * @param {Number} length Number of days over which to average the data.