Fix bug 382. Simplify resizing by not recreating everything from scratch. This makes...
[dygraphs.git] / plugins / annotations.js
index a3a13c3..8576104 100644 (file)
@@ -4,12 +4,23 @@
  * MIT-licensed (http://opensource.org/licenses/MIT)
  */
 
+/*global Dygraph:false */
+
 Dygraph.Plugins.Annotations = (function() {
 
+"use strict";
+
 /**
 Current bits of jankiness:
 - Uses dygraph.layout_ to get the parsed annotations.
 - Uses dygraph.plotter_.area
+
+It would be nice if the plugin didn't require so much special support inside
+the core dygraphs classes, but annotations involve quite a bit of parsing and
+layout.
+
+TODO(danvk): cache DOM elements.
+
 */
 
 var annotations = function() {
@@ -23,7 +34,7 @@ annotations.prototype.toString = function() {
 annotations.prototype.activate = function(g) {
   return {
     clearChart: this.clearChart,
-    drawChart: this.drawChart
+    didDrawChart: this.didDrawChart
   };
 };
 
@@ -40,12 +51,12 @@ annotations.prototype.clearChart = function(e) {
   this.detachLabels();
 };
 
-annotations.prototype.drawChart = function(e) {
+annotations.prototype.didDrawChart = function(e) {
   var g = e.dygraph;
 
   // Early out in the (common) case of zero annotations.
   var points = g.layout_.annotated_points;
-  if (!points || points.length == 0) return;
+  if (!points || points.length === 0) return;
 
   var containerDiv = e.canvas.parentNode;
   var annotationStyle = {
@@ -68,6 +79,10 @@ annotations.prototype.drawChart = function(e) {
 
   // Add the annotations one-by-one.
   var area = e.dygraph.plotter_.area;
+
+  // x-coord to sum of previous annotation's heights (used for stacking).
+  var xToUsedHeight = {};
+
   for (var i = 0; i < points.length; i++) {
     var p = points[i];
     if (p.canvasx < area.x || p.canvasx > area.x + area.w ||
@@ -105,12 +120,22 @@ annotations.prototype.drawChart = function(e) {
     } else if (p.annotation.hasOwnProperty('shortText')) {
       div.appendChild(document.createTextNode(p.annotation.shortText));
     }
-    div.style.left = (p.canvasx - width / 2) + "px";
+    var left = p.canvasx - width / 2;
+    div.style.left = left + "px";
+    var divTop = 0;
     if (a.attachAtBottom) {
-      div.style.top = (area.h - height - tick_height) + "px";
+      var y = (area.y + area.h - height - tick_height);
+      if (xToUsedHeight[left]) {
+        y -= xToUsedHeight[left];
+      } else {
+        xToUsedHeight[left] = 0;
+      }
+      xToUsedHeight[left] += (tick_height + height);
+      divTop = y;
     } else {
-      div.style.top = (p.canvasy - height - tick_height) + "px";
+      divTop = p.canvasy - height - tick_height;
     }
+    div.style.top = divTop + "px";
     div.style.width = width + "px";
     div.style.height = height + "px";
     div.title = p.annotation.text;
@@ -118,13 +143,13 @@ annotations.prototype.drawChart = function(e) {
     div.style.borderColor = g.colorsMap_[p.name];
     a.div = div;
 
-    g.addEvent(div, 'click',
+    g.addAndTrackEvent(div, 'click',
         bindEvt('clickHandler', 'annotationClickHandler', p, this));
-    g.addEvent(div, 'mouseover',
+    g.addAndTrackEvent(div, 'mouseover',
         bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this));
-    g.addEvent(div, 'mouseout',
+    g.addAndTrackEvent(div, 'mouseout',
         bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this));
-    g.addEvent(div, 'dblclick',
+    g.addAndTrackEvent(div, 'dblclick',
         bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this));
 
     containerDiv.appendChild(div);
@@ -138,8 +163,9 @@ annotations.prototype.drawChart = function(e) {
       ctx.moveTo(p.canvasx, p.canvasy);
       ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height);
     } else {
-      ctx.moveTo(p.canvasx, area.h);
-      ctx.lineTo(p.canvasx, area.h - 2 - tick_height);
+      var y = divTop + height;
+      ctx.moveTo(p.canvasx, y);
+      ctx.lineTo(p.canvasx, y + tick_height);
     }
     ctx.closePath();
     ctx.stroke();