Add tests (which currently fail) that ensure that drawPointCallback and drawHighlight...
[dygraphs.git] / plugins / annotations.js
1 /**
2 * @license
3 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
4 * MIT-licensed (http://opensource.org/licenses/MIT)
5 */
6
7 Dygraph.Plugins.Annotations = (function() {
8
9 "use strict";
10
11 /**
12 Current bits of jankiness:
13 - Uses dygraph.layout_ to get the parsed annotations.
14 - Uses dygraph.plotter_.area
15
16 It would be nice if the plugin didn't require so much special support inside
17 the core dygraphs classes, but annotations involve quite a bit of parsing and
18 layout.
19
20 TODO(danvk): cache DOM elements.
21
22 */
23
24 var annotations = function() {
25 this.annotations_ = [];
26 };
27
28 annotations.prototype.toString = function() {
29 return "Annotations Plugin";
30 };
31
32 annotations.prototype.activate = function(g) {
33 return {
34 clearChart: this.clearChart,
35 didDrawChart: this.didDrawChart
36 };
37 };
38
39 annotations.prototype.detachLabels = function() {
40 for (var i = 0; i < this.annotations_.length; i++) {
41 var a = this.annotations_[i];
42 if (a.parentNode) a.parentNode.removeChild(a);
43 this.annotations_[i] = null;
44 }
45 this.annotations_ = [];
46 };
47
48 annotations.prototype.clearChart = function(e) {
49 this.detachLabels();
50 };
51
52 annotations.prototype.didDrawChart = function(e) {
53 var g = e.dygraph;
54
55 // Early out in the (common) case of zero annotations.
56 var points = g.layout_.annotated_points;
57 if (!points || points.length === 0) return;
58
59 var containerDiv = e.canvas.parentNode;
60 var annotationStyle = {
61 "position": "absolute",
62 "fontSize": g.getOption('axisLabelFontSize') + "px",
63 "zIndex": 10,
64 "overflow": "hidden"
65 };
66
67 var bindEvt = function(eventName, classEventName, pt) {
68 return function(annotation_event) {
69 var a = pt.annotation;
70 if (a.hasOwnProperty(eventName)) {
71 a[eventName](a, pt, g, annotation_event);
72 } else if (g.getOption(classEventName)) {
73 g.getOption(classEventName)(a, pt, g, annotation_event );
74 }
75 };
76 };
77
78 // Add the annotations one-by-one.
79 var area = e.dygraph.plotter_.area;
80 for (var i = 0; i < points.length; i++) {
81 var p = points[i];
82 if (p.canvasx < area.x || p.canvasx > area.x + area.w ||
83 p.canvasy < area.y || p.canvasy > area.y + area.h) {
84 continue;
85 }
86
87 var a = p.annotation;
88 var tick_height = 6;
89 if (a.hasOwnProperty("tickHeight")) {
90 tick_height = a.tickHeight;
91 }
92
93 var div = document.createElement("div");
94 for (var name in annotationStyle) {
95 if (annotationStyle.hasOwnProperty(name)) {
96 div.style[name] = annotationStyle[name];
97 }
98 }
99 if (!a.hasOwnProperty('icon')) {
100 div.className = "dygraphDefaultAnnotation";
101 }
102 if (a.hasOwnProperty('cssClass')) {
103 div.className += " " + a.cssClass;
104 }
105
106 var width = a.hasOwnProperty('width') ? a.width : 16;
107 var height = a.hasOwnProperty('height') ? a.height : 16;
108 if (a.hasOwnProperty('icon')) {
109 var img = document.createElement("img");
110 img.src = a.icon;
111 img.width = width;
112 img.height = height;
113 div.appendChild(img);
114 } else if (p.annotation.hasOwnProperty('shortText')) {
115 div.appendChild(document.createTextNode(p.annotation.shortText));
116 }
117 div.style.left = (p.canvasx - width / 2) + "px";
118 if (a.attachAtBottom) {
119 div.style.top = (area.h - height - tick_height) + "px";
120 } else {
121 div.style.top = (p.canvasy - height - tick_height) + "px";
122 }
123 div.style.width = width + "px";
124 div.style.height = height + "px";
125 div.title = p.annotation.text;
126 div.style.color = g.colorsMap_[p.name];
127 div.style.borderColor = g.colorsMap_[p.name];
128 a.div = div;
129
130 g.addEvent(div, 'click',
131 bindEvt('clickHandler', 'annotationClickHandler', p, this));
132 g.addEvent(div, 'mouseover',
133 bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this));
134 g.addEvent(div, 'mouseout',
135 bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this));
136 g.addEvent(div, 'dblclick',
137 bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this));
138
139 containerDiv.appendChild(div);
140 this.annotations_.push(div);
141
142 var ctx = e.drawingContext;
143 ctx.save();
144 ctx.strokeStyle = g.colorsMap_[p.name];
145 ctx.beginPath();
146 if (!a.attachAtBottom) {
147 ctx.moveTo(p.canvasx, p.canvasy);
148 ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height);
149 } else {
150 ctx.moveTo(p.canvasx, area.h);
151 ctx.lineTo(p.canvasx, area.h - 2 - tick_height);
152 }
153 ctx.closePath();
154 ctx.stroke();
155 ctx.restore();
156 }
157 };
158
159 annotations.prototype.destroy = function() {
160 this.detachLabels();
161 };
162
163 return annotations;
164
165 })();