fix issue #494 & expand xAxisLabelWidth; lots of failing tests
[dygraphs.git] / dygraph.js
index c8a1ccf..ab44fac 100644 (file)
@@ -46,7 +46,7 @@
 // For "production" code, this gets set to false by uglifyjs.
 if (typeof(DEBUG) === 'undefined') DEBUG=true;
 
-/*jshint globalstrict: true */
+var Dygraph = (function() {
 /*global DygraphLayout:false, DygraphCanvasRenderer:false, DygraphOptions:false, G_vmlCanvasManager:false,ActiveXObject:false */
 "use strict";
 
@@ -117,10 +117,8 @@ Dygraph.KMG2_SMALL_LABELS = [ 'm', 'u', 'n', 'p', 'f', 'a', 'z', 'y' ];
  * and maxNumberWidth options.
  * @param {number} x The number to be formatted
  * @param {Dygraph} opts An options view
- * @param {string} name The name of the point's data series
- * @param {Dygraph} g The dygraph object
  */
-Dygraph.numberValueFormatter = function(x, opts, pt, g) {
+Dygraph.numberValueFormatter = function(x, opts) {
   var sigFigs = opts('sigFigs');
 
   if (sigFigs !== null) {
@@ -191,8 +189,8 @@ Dygraph.numberValueFormatter = function(x, opts, pt, g) {
  * variant for use as an axisLabelFormatter.
  * @private
  */
-Dygraph.numberAxisLabelFormatter = function(x, granularity, opts, g) {
-  return Dygraph.numberValueFormatter(x, opts, g);
+Dygraph.numberAxisLabelFormatter = function(x, granularity, opts) {
+  return Dygraph.numberValueFormatter(x, opts);
 };
 
 /**
@@ -228,12 +226,12 @@ Dygraph.dateAxisLabelFormatter = function(date, granularity, opts) {
   if (granularity >= Dygraph.DECADAL) {
     return '' + year;
   } else if (granularity >= Dygraph.MONTHLY) {
-    return Dygraph.SHORT_MONTH_NAMES_[month] + ' ' + year;
+    return Dygraph.SHORT_MONTH_NAMES_[month] + ' ' + year;
   } else {
     var frac = hours * 3600 + mins * 60 + secs + 1e-3 * millis;
     if (frac === 0 || granularity >= Dygraph.DAILY) {
-      // e.g. '21Jan' (%d%b)
-      return Dygraph.zeropad(day) + Dygraph.SHORT_MONTH_NAMES_[month];
+      // e.g. '21 Jan' (%d%b)
+      return Dygraph.zeropad(day) + ' ' + Dygraph.SHORT_MONTH_NAMES_[month];
     } else {
       return Dygraph.hmsString_(hours, mins, secs);
     }
@@ -292,8 +290,6 @@ Dygraph.DEFAULT_ATTRS = {
 
   axisTickSize: 3,
   axisLabelFontSize: 14,
-  xAxisLabelWidth: 50,
-  yAxisLabelWidth: 50,
   rightGap: 5,
 
   showRoller: false,
@@ -334,7 +330,6 @@ Dygraph.DEFAULT_ATTRS = {
   axisLineWidth: 0.3,
   gridLineWidth: 0.3,
   axisLabelColor: "black",
-  axisLabelFont: "Arial",  // TODO(danvk): is this implemented?
   axisLabelWidth: 50,
   drawYGrid: true,
   drawXGrid: true,
@@ -364,6 +359,7 @@ Dygraph.DEFAULT_ATTRS = {
   axes: {
     x: {
       pixelsPerLabel: 60,
+      axisLabelWidth: 58,  // TODO(danvk): make these 10-20px different again.
       axisLabelFormatter: Dygraph.dateAxisLabelFormatter,
       valueFormatter: Dygraph.dateValueFormatter,
       drawGrid: true,
@@ -372,6 +368,7 @@ Dygraph.DEFAULT_ATTRS = {
       ticker: null  // will be set in dygraph-tickers.js
     },
     y: {
+      axisLabelWidth: 50,
       pixelsPerLabel: 30,
       valueFormatter: Dygraph.numberValueFormatter,
       axisLabelFormatter: Dygraph.numberAxisLabelFormatter,
@@ -381,10 +378,11 @@ Dygraph.DEFAULT_ATTRS = {
       ticker: null  // will be set in dygraph-tickers.js
     },
     y2: {
+      axisLabelWidth: 50,
       pixelsPerLabel: 30,
       valueFormatter: Dygraph.numberValueFormatter,
       axisLabelFormatter: Dygraph.numberAxisLabelFormatter,
-      drawAxis: false,
+      drawAxis: true,  // only applies when there are two axes of data.
       drawGrid: false,
       independentTicks: false,
       ticker: null  // will be set in dygraph-tickers.js
@@ -555,6 +553,7 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
 
     var handlers = pluginInstance.activate(this);
     for (var eventName in handlers) {
+      if (!handlers.hasOwnProperty(eventName)) continue;
       // TODO(danvk): validate eventName.
       pluginDict.events[eventName] = handlers[eventName];
     }
@@ -1079,7 +1078,7 @@ Dygraph.prototype.toPercentXCoord = function(x) {
   var xRange = this.xAxisRange();
   var pct;
   var logscale = this.attributes_.getForAxis("logscale", 'x') ;
-  if (logscale == true) { // logscale can be null so we test for true explicitly.
+  if (logscale === true) {  // logscale can be null so we test for true explicitly.
     var logr0 = Dygraph.log10(xRange[0]);
     var logr1 = Dygraph.log10(xRange[1]);
     pct = (Dygraph.log10(x) - logr0) / (logr1 - logr0);
@@ -1230,6 +1229,12 @@ Dygraph.prototype.destroy = function() {
   this.canvas_ctx_.restore();
   this.hidden_ctx_.restore();
 
+  // Destroy any plugins, in the reverse order that they were registered.
+  for (var i = this.plugins_.length - 1; i >= 0; i--) {
+    var p = this.plugins_.pop();
+    if (p.plugin.destroy) p.plugin.destroy();
+  }
+
   var removeRecursive = function(node) {
     while (node.hasChildNodes()) {
       removeRecursive(node.firstChild);
@@ -1244,7 +1249,7 @@ Dygraph.prototype.destroy = function() {
   Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_);
 
   // remove window handlers
-  Dygraph.removeEvent(window,'resize',this.resizeHandler_);
+  Dygraph.removeEvent(window,'resize', this.resizeHandler_);
   this.resizeHandler_ = null;
 
   removeRecursive(this.maindiv_);
@@ -1483,6 +1488,26 @@ Dygraph.prototype.createDragInterface_ = function() {
       contextB.dragStartY = Dygraph.dragGetY_(event, contextB);
       contextB.cancelNextDblclick = false;
       contextB.tarp.cover();
+    },
+    destroy: function() {
+      var context = this;
+      if (context.isZooming || context.isPanning) {
+        context.isZooming = false;
+        context.dragStartX = null;
+        context.dragStartY = null;
+      }
+
+      if (context.isPanning) {
+        context.isPanning = false;
+        context.draggingDate = null;
+        context.dateRange = null;
+        for (var i = 0; i < self.axes_.length; i++) {
+          delete self.axes_[i].draggingValue;
+          delete self.axes_[i].dragValueRange;
+        }
+      }
+
+      context.tarp.uncover();
     }
   };
 
@@ -1506,27 +1531,13 @@ Dygraph.prototype.createDragInterface_ = function() {
 
   // If the user releases the mouse button during a drag, but not over the
   // canvas, then it doesn't count as a zooming action.
-  var mouseUpHandler = function(event) {
-    if (context.isZooming || context.isPanning) {
-      context.isZooming = false;
-      context.dragStartX = null;
-      context.dragStartY = null;
-    }
-
-    if (context.isPanning) {
-      context.isPanning = false;
-      context.draggingDate = null;
-      context.dateRange = null;
-      for (var i = 0; i < self.axes_.length; i++) {
-        delete self.axes_[i].draggingValue;
-        delete self.axes_[i].dragValueRange;
-      }
-    }
-
-    context.tarp.uncover();
-  };
+  if (!interactionModel.willDestroyContextMyself) {
+    var mouseUpHandler = function(event) {
+      context.destroy();
+    };
 
-  this.addAndTrackEvent(document, 'mouseup', mouseUpHandler);
+    this.addAndTrackEvent(document, 'mouseup', mouseUpHandler);
+  }
 };
 
 /**
@@ -1591,7 +1602,7 @@ Dygraph.prototype.drawZoomRect_ = function(direction, startX, endX, startY,
  */
 Dygraph.prototype.clearZoomRect_ = function() {
   this.currentZoomRectArgs_ = null;
-  this.canvas_ctx_.clearRect(0, 0, this.canvas_.width, this.canvas_.height);
+  this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);
 };
 
 /**
@@ -2307,7 +2318,7 @@ Dygraph.prototype.addXTicks_ = function() {
   var xTicks = xAxisOptionsView('ticker')(
       range[0],
       range[1],
-      this.width_,  // TODO(danvk): should be area.width
+      this.plotter_.area.w,  // TODO(danvk): should be area.width
       xAxisOptionsView,
       this);
   // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks);
@@ -2358,12 +2369,6 @@ Dygraph.prototype.predraw_ = function() {
   // TODO(danvk): move more computations out of drawGraph_ and into here.
   this.computeYAxes_();
 
-  // Create a new plotter.
-  if (this.plotter_) {
-    this.cascadeEvents_('clearChart');
-    this.plotter_.clear();
-  }
-
   if (!this.is_initial_draw_) {
     this.canvas_ctx_.restore();
     this.hidden_ctx_.restore();
@@ -2372,6 +2377,7 @@ Dygraph.prototype.predraw_ = function() {
   this.canvas_ctx_.save();
   this.hidden_ctx_.save();
 
+  // Create a new plotter.
   this.plotter_ = new DygraphCanvasRenderer(this,
                                             this.hidden_,
                                             this.hidden_ctx_,
@@ -2718,8 +2724,7 @@ Dygraph.prototype.renderGraph_ = function(is_initial_draw) {
 
   // TODO(danvk): is this a performance bottleneck when panning?
   // The interaction canvas should already be empty in that situation.
-  this.canvas_.getContext('2d').clearRect(0, 0, this.canvas_.width,
-                                          this.canvas_.height);
+  this.canvas_.getContext('2d').clearRect(0, 0, this.width_, this.height_);
 
   if (this.getFunctionOption("drawCallback") !== null) {
     this.getFunctionOption("drawCallback")(this, is_initial_draw);
@@ -2967,7 +2972,7 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
       var ticker = opts('ticker');
       axis.ticks = ticker(axis.computedValueRange[0],
               axis.computedValueRange[1],
-              this.height_,  // TODO(danvk): should be area.height
+              this.plotter_.area.h,
               opts,
               this);
       // Define the first independent axis as primary axis.
@@ -2998,7 +3003,7 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
 
       axis.ticks = ticker(axis.computedValueRange[0],
                           axis.computedValueRange[1],
-                          this.height_,  // TODO(danvk): should be area.height
+                          this.plotter_.area.h,
                           opts,
                           this,
                           tick_values);
@@ -3549,6 +3554,7 @@ Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) {
 Dygraph.mapLegacyOptions_ = function(attrs) {
   var my_attrs = {};
   for (var k in attrs) {
+    if (!attrs.hasOwnProperty(k)) continue;
     if (k == 'file') continue;
     if (attrs.hasOwnProperty(k)) my_attrs[k] = attrs[k];
   }
@@ -3582,6 +3588,8 @@ Dygraph.mapLegacyOptions_ = function(attrs) {
   map('drawXAxis', 'x', 'drawAxis');
   map('drawYGrid', 'y', 'drawGrid');
   map('drawYAxis', 'y', 'drawAxis');
+  map('xAxisLabelWidth', 'x', 'axisLabelWidth');
+  map('yAxisLabelWidth', 'y', 'axisLabelWidth');
   return my_attrs;
 };
 
@@ -3793,3 +3801,7 @@ Dygraph.addAnnotationRule = function() {
 
   console.warn("Unable to add default annotation CSS rule; display may be off.");
 };
+
+return Dygraph;
+
+})();