Checkpoint: annotations fully ported to plugin system. All tests pass.
[dygraphs.git] / dygraph.js
index 47c6d2c..aea8830 100644 (file)
@@ -233,6 +233,7 @@ Dygraph.DEFAULT_ATTRS = {
 
   stepPlot: false,
   avoidMinZero: false,
+  drawAxesAtZero: false,
 
   // Sizes of the various chart labels.
   titleHeight: 28,
@@ -424,16 +425,11 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
       pluginOptions: {}
     };
 
-    var registerer = (function(pluginDict) {
-      return {
-        addEventListener: function(eventName, callback) {
-          // TODO(danvk): validate eventName.
-          pluginDict.events[eventName] = callback;
-        }
-      };
-    })(pluginDict);
-    pluginInstance.activate(this, registerer);
-    // TODO(danvk): prevent activate() from holding a reference to registerer.
+    var handlers = pluginInstance.activate(this);
+    for (var eventName in handlers) {
+      // TODO(danvk): validate eventName.
+      pluginDict.events[eventName] = handlers[eventName];
+    }
 
     this.plugins_.push(pluginDict);
   }
@@ -485,11 +481,13 @@ Dygraph.prototype.cascadeEvents_ = function(name, extra_props) {
   Dygraph.update(e, extra_props);
 
   var callback_plugin_pairs = this.eventListeners_[name];
-  for (var i = callback_plugin_pairs.length - 1; i >= 0; i--) {
-    var plugin = callback_plugin_pairs[i][0];
-    var callback = callback_plugin_pairs[i][1];
-    callback.call(plugin, e);
-    if (e.propagationStopped) break;
+  if (callback_plugin_pairs) {
+    for (var i = callback_plugin_pairs.length - 1; i >= 0; i--) {
+      var plugin = callback_plugin_pairs[i][0];
+      var callback = callback_plugin_pairs[i][1];
+      callback.call(plugin, e);
+      if (e.propagationStopped) break;
+    }
   }
   return e.defaultPrevented;
 };
@@ -988,14 +986,14 @@ Dygraph.prototype.destroy = function() {
  
   for (var idx = 0; idx < this.registeredEvents_.length; idx++) {
     var reg = this.registeredEvents_[idx];
-    this.removeEvent(reg.elem, reg.type, reg.fn);
+    Dygraph.removeEvent(reg.elem, reg.type, reg.fn);
   }
   this.registeredEvents_ = [];
 
   // remove mouse event handlers (This may not be necessary anymore)
-  this.removeEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler);
-  this.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler);
-  this.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseUpHandler_);
+  Dygraph.removeEvent(this.mouseEventElement_, 'mouseout', this.mouseOutHandler);
+  Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler);
+  Dygraph.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseUpHandler_);
   removeRecursive(this.maindiv_);
 
   var nullOut = function(obj) {
@@ -1006,7 +1004,7 @@ Dygraph.prototype.destroy = function() {
     }
   };
   // remove event handlers
-  this.removeEvent(window,'resize',this.resizeHandler);
+  Dygraph.removeEvent(window,'resize',this.resizeHandler);
   this.resizeHandler = null;
   // These may not all be necessary, but it can't hurt...
   nullOut(this.layout_);
@@ -1065,8 +1063,10 @@ Dygraph.prototype.createMouseEventElement_ = function() {
  * @private
  */
 Dygraph.prototype.setColors_ = function() {
-  var num = this.attr_("labels").length - 1;
+  var labels = this.getLabels();
+  var num = labels.length - 1;
   this.colors_ = [];
+  this.colorsMap_ = {};
   var colors = this.attr_('colors');
   var i;
   if (!colors) {
@@ -1078,13 +1078,16 @@ Dygraph.prototype.setColors_ = function() {
       // alternate colors for high contrast.
       var idx = i % 2 ? Math.ceil(i / 2) : (half + i / 2);
       var hue = (1.0 * idx/ (1 + num));
-      this.colors_.push(Dygraph.hsvToRGB(hue, sat, val));
+      var colorStr = Dygraph.hsvToRGB(hue, sat, val);
+      this.colors_.push(colorStr);
+      this.colorsMap_[labels[i]] = colorStr;
     }
   } else {
     for (i = 0; i < num; i++) {
       if (!this.visibility()[i]) continue;
       var colorStr = colors[i % colors.length];
       this.colors_.push(colorStr);
+      this.colorsMap_[labels[1 + i]] = colorStr;
     }
   }
 
@@ -2104,7 +2107,10 @@ Dygraph.prototype.predraw_ = function() {
   this.computeYAxes_();
 
   // Create a new plotter.
-  if (this.plotter_) this.plotter_.clear();
+  if (this.plotter_) {
+    this.cascadeEvents_('clearChart');
+    this.plotter_.clear();
+  }
   this.plotter_ = new DygraphCanvasRenderer(this,
                                             this.hidden_,
                                             this.hidden_ctx_,
@@ -2332,8 +2338,13 @@ Dygraph.prototype.drawGraph_ = function() {
  * @private
  */
 Dygraph.prototype.renderGraph_ = function(is_initial_draw) {
+  this.cascadeEvents_('clearChart');
   this.plotter_.clear();
+
   this.plotter_.render();
+
+  // 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);
 
@@ -2343,7 +2354,10 @@ Dygraph.prototype.renderGraph_ = function(is_initial_draw) {
     this.rangeSelector_.renderInteractiveLayer();
   }
 
-  this.cascadeEvents_('drawChart');
+  this.cascadeEvents_('drawChart', {
+    canvas: this.hidden_,
+    drawingContext: this.hidden_ctx_,
+  });
   if (this.attr_("drawCallback") !== null) {
     this.attr_("drawCallback")(this, is_initial_draw);
   }
@@ -3490,6 +3504,7 @@ Dygraph.prototype.datasetIndexFromSetName_ = function(name) {
  * called once -- all calls after the first will return immediately.
  */
 Dygraph.addAnnotationRule = function() {
+  // TODO(danvk): move this function into plugins/annotations.js?
   if (Dygraph.addedAnnotationCSS) return;
 
   var rule = "border: 1px solid black; " +