1 /** 2 * @license 3 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) 4 * MIT-licenced: https://opensource.org/licenses/MIT 5 */ 6 7 /*global Dygraph:false */ 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 var annotations = function() { 24 this.annotations_ = []; 25 }; 26 27 annotations.prototype.toString = function() { 28 return "Annotations Plugin"; 29 }; 30 31 annotations.prototype.activate = function(g) { 32 return { 33 clearChart: this.clearChart, 34 didDrawChart: this.didDrawChart 35 }; 36 }; 37 38 annotations.prototype.detachLabels = function() { 39 for (var i = 0; i < this.annotations_.length; i++) { 40 var a = this.annotations_[i]; 41 if (a.parentNode) a.parentNode.removeChild(a); 42 this.annotations_[i] = null; 43 } 44 this.annotations_ = []; 45 }; 46 47 annotations.prototype.clearChart = function(e) { 48 this.detachLabels(); 49 }; 50 51 annotations.prototype.didDrawChart = function(e) { 52 var g = e.dygraph; 53 54 // Early out in the (common) case of zero annotations. 55 var points = g.layout_.annotated_points; 56 if (!points || points.length === 0) return; 57 58 var containerDiv = e.canvas.parentNode; 59 60 var bindEvt = function(eventName, classEventName, pt) { 61 return function(annotation_event) { 62 var a = pt.annotation; 63 if (a.hasOwnProperty(eventName)) { 64 a[eventName](a, pt, g, annotation_event); 65 } else if (g.getOption(classEventName)) { 66 g.getOption(classEventName)(a, pt, g, annotation_event ); 67 } 68 }; 69 }; 70 71 // Add the annotations one-by-one. 72 var area = e.dygraph.getArea(); 73 74 // x-coord to sum of previous annotation's heights (used for stacking). 75 var xToUsedHeight = {}; 76 77 for (var i = 0; i < points.length; i++) { 78 var p = points[i]; 79 if (p.canvasx < area.x || p.canvasx > area.x + area.w || 80 p.canvasy < area.y || p.canvasy > area.y + area.h) { 81 continue; 82 } 83 84 var a = p.annotation; 85 var tick_height = 6; 86 if (a.hasOwnProperty("tickHeight")) { 87 tick_height = a.tickHeight; 88 } 89 90 // TODO: deprecate axisLabelFontSize in favor of CSS 91 var div = document.createElement("div"); 92 div.style['fontSize'] = g.getOption('axisLabelFontSize') + "px"; 93 var className = 'dygraph-annotation'; 94 if (!a.hasOwnProperty('icon')) { 95 // camelCase class names are deprecated. 96 className += ' dygraphDefaultAnnotation dygraph-default-annotation'; 97 } 98 if (a.hasOwnProperty('cssClass')) { 99 className += " " + a.cssClass; 100 } 101 div.className = className; 102 103 var width = a.hasOwnProperty('width') ? a.width : 16; 104 var height = a.hasOwnProperty('height') ? a.height : 16; 105 if (a.hasOwnProperty('icon')) { 106 var img = document.createElement("img"); 107 img.src = a.icon; 108 img.width = width; 109 img.height = height; 110 div.appendChild(img); 111 } else if (p.annotation.hasOwnProperty('shortText')) { 112 div.appendChild(document.createTextNode(p.annotation.shortText)); 113 } 114 var left = p.canvasx - width / 2; 115 div.style.left = left + "px"; 116 var divTop = 0; 117 if (a.attachAtBottom) { 118 var y = (area.y + area.h - height - tick_height); 119 if (xToUsedHeight[left]) { 120 y -= xToUsedHeight[left]; 121 } else { 122 xToUsedHeight[left] = 0; 123 } 124 xToUsedHeight[left] += (tick_height + height); 125 divTop = y; 126 } else { 127 divTop = p.canvasy - height - tick_height; 128 } 129 div.style.top = divTop + "px"; 130 div.style.width = width + "px"; 131 div.style.height = height + "px"; 132 div.title = p.annotation.text; 133 div.style.color = g.colorsMap_[p.name]; 134 div.style.borderColor = g.colorsMap_[p.name]; 135 a.div = div; 136 137 g.addAndTrackEvent(div, 'click', 138 bindEvt('clickHandler', 'annotationClickHandler', p, this)); 139 g.addAndTrackEvent(div, 'mouseover', 140 bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this)); 141 g.addAndTrackEvent(div, 'mouseout', 142 bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this)); 143 g.addAndTrackEvent(div, 'dblclick', 144 bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this)); 145 146 containerDiv.appendChild(div); 147 this.annotations_.push(div); 148 149 var ctx = e.drawingContext; 150 ctx.save(); 151 ctx.strokeStyle = a.hasOwnProperty('tickColor') ? a.tickColor : g.colorsMap_[p.name]; 152 ctx.lineWidth = a.hasOwnProperty('tickWidth') ? a.tickWidth : g.getOption('strokeWidth'); 153 ctx.beginPath(); 154 if (!a.attachAtBottom) { 155 ctx.moveTo(p.canvasx, p.canvasy); 156 ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height); 157 } else { 158 var y = divTop + height; 159 ctx.moveTo(p.canvasx, y); 160 ctx.lineTo(p.canvasx, y + tick_height); 161 } 162 ctx.closePath(); 163 ctx.stroke(); 164 ctx.restore(); 165 } 166 }; 167 168 annotations.prototype.destroy = function() { 169 this.detachLabels(); 170 }; 171 172 export default annotations; 173