3 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
4 * MIT-licensed (http://opensource.org/licenses/MIT)
6 /*global Dygraph:false */
8 Dygraph
.Plugins
.ChartLabels
= (function() {
12 // TODO(danvk): move chart label options out of dygraphs and into the plugin.
13 // TODO(danvk): only tear down & rebuild the DIVs when it's necessary.
15 var chart_labels
= function() {
16 this.title_div_
= null;
17 this.xlabel_div_
= null;
18 this.ylabel_div_
= null;
19 this.y2label_div_
= null;
22 chart_labels
.prototype.toString
= function() {
23 return "ChartLabels Plugin";
26 chart_labels
.prototype.activate
= function(g
) {
29 // clearChart: this.clearChart,
30 didDrawChart
: this.didDrawChart
34 // QUESTION: should there be a plugin-utils.js?
35 var createDivInRect
= function(r
) {
36 var div
= document
.createElement('div');
37 div
.style
.position
= 'absolute';
38 div
.style
.left
= r
.x
+ 'px';
39 div
.style
.top
= r
.y
+ 'px';
40 div
.style
.width
= r
.w
+ 'px';
41 div
.style
.height
= r
.h
+ 'px';
45 // Detach and null out any existing nodes.
46 chart_labels
.prototype.detachLabels_
= function() {
47 var els
= [ this.title_div_
,
51 for (var i
= 0; i
< els
.length
; i
++) {
54 if (el
.parentNode
) el
.parentNode
.removeChild(el
);
57 this.title_div_
= null;
58 this.xlabel_div_
= null;
59 this.ylabel_div_
= null;
60 this.y2label_div_
= null;
63 var createRotatedDiv
= function(g
, box
, axis
, classes
, html
) {
64 // TODO(danvk): is this outer div actually necessary?
65 var div
= document
.createElement("div");
66 div
.style
.position
= 'absolute';
68 // NOTE: this is cheating. Should be positioned relative to the box.
69 div
.style
.left
= '0px';
71 div
.style
.left
= box
.x
+ 'px';
73 div
.style
.top
= box
.y
+ 'px';
74 div
.style
.width
= box
.w
+ 'px';
75 div
.style
.height
= box
.h
+ 'px';
76 div
.style
.fontSize
= (g
.getOption('yLabelWidth') - 2) + 'px';
78 var inner_div
= document
.createElement("div");
79 inner_div
.style
.position
= 'absolute';
80 inner_div
.style
.width
= box
.h
+ 'px';
81 inner_div
.style
.height
= box
.w
+ 'px';
82 inner_div
.style
.top
= (box
.h
/ 2 - box.w / 2) + 'px';
83 inner_div
.style
.left
= (box
.w
/ 2 - box.h / 2) + 'px';
84 inner_div
.style
.textAlign
= 'center';
86 // CSS rotation is an HTML5 feature which is not standardized. Hence every
87 // browser has its own name for the CSS style.
88 var val
= 'rotate(' + (axis
== 1 ? '-' : '') + '90deg)';
89 inner_div
.style
.transform
= val
; // HTML5
90 inner_div
.style
.WebkitTransform
= val
; // Safari/Chrome
91 inner_div
.style
.MozTransform
= val
; // Firefox
92 inner_div
.style
.OTransform
= val
; // Opera
93 inner_div
.style
.msTransform
= val
; // IE9
95 if (typeof(document
.documentMode
) !== 'undefined' &&
96 document
.documentMode
< 9) {
97 // We're dealing w/ an old version of IE
, so we have to rotate the text
98 // using a BasicImage transform. This uses a different origin of rotation
99 // than HTML5 rotation (top left of div vs. its center).
100 inner_div
.style
.filter
=
101 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' +
102 (axis
== 1 ? '3' : '1') + ')';
103 inner_div
.style
.left
= '0px';
104 inner_div
.style
.top
= '0px';
107 var class_div
= document
.createElement("div");
108 class_div
.className
= classes
;
109 class_div
.innerHTML
= html
;
111 inner_div
.appendChild(class_div
);
112 div
.appendChild(inner_div
);
116 chart_labels
.prototype.layout
= function(e
) {
117 this.detachLabels_();
120 var div
= e
.chart_div
;
121 if (g
.getOption('title')) {
122 // QUESTION: should this return an absolutely-positioned div instead?
123 var title_rect
= e
.reserveSpaceTop(g
.getOption('titleHeight'));
124 this.title_div_
= createDivInRect(title_rect
);
125 this.title_div_
.style
.textAlign
= 'center';
126 this.title_div_
.style
.fontSize
= (g
.getOption('titleHeight') - 8) + 'px';
127 this.title_div_
.style
.fontWeight
= 'bold';
128 this.title_div_
.style
.zIndex
= 10;
130 var class_div
= document
.createElement("div");
131 class_div
.className
= 'dygraph-label dygraph-title';
132 class_div
.innerHTML
= g
.getOption('title');
133 this.title_div_
.appendChild(class_div
);
134 div
.appendChild(this.title_div_
);
137 if (g
.getOption('xlabel')) {
138 var x_rect
= e
.reserveSpaceBottom(g
.getOption('xLabelHeight'));
139 this.xlabel_div_
= createDivInRect(x_rect
);
140 this.xlabel_div_
.style
.textAlign
= 'center';
141 this.xlabel_div_
.style
.fontSize
= (g
.getOption('xLabelHeight') - 2) + 'px';
143 var class_div
= document
.createElement("div");
144 class_div
.className
= 'dygraph-label dygraph-xlabel';
145 class_div
.innerHTML
= g
.getOption('xlabel');
146 this.xlabel_div_
.appendChild(class_div
);
147 div
.appendChild(this.xlabel_div_
);
150 if (g
.getOption('ylabel')) {
151 // It would make sense to shift the chart here to make room for the y-axis
152 // label, but the default yAxisLabelWidth is large enough that this results
153 // in overly-padded charts. The y-axis label should fit fine. If it
154 // doesn't, the yAxisLabelWidth option can be increased.
155 var y_rect
= e
.reserveSpaceLeft(0);
157 this.ylabel_div_
= createRotatedDiv(
159 1, // primary (left) y-axis
160 'dygraph-label dygraph-ylabel',
161 g
.getOption('ylabel'));
162 div
.appendChild(this.ylabel_div_
);
165 if (g
.getOption('y2label') && g
.numAxes() == 2) {
166 // same logic applies here as for ylabel.
167 var y2_rect
= e
.reserveSpaceRight(0);
168 this.y2label_div_
= createRotatedDiv(
170 2, // secondary (right) y-axis
171 'dygraph-label dygraph-y2label',
172 g
.getOption('y2label'));
173 div
.appendChild(this.y2label_div_
);
177 chart_labels
.prototype.didDrawChart
= function(e
) {
179 if (this.title_div_
) {
180 this.title_div_
.children
[0].innerHTML
= g
.getOption('title');
182 if (this.xlabel_div_
) {
183 this.xlabel_div_
.children
[0].innerHTML
= g
.getOption('xlabel');
185 if (this.ylabel_div_
) {
186 this.ylabel_div_
.children
[0].children
[0].innerHTML
= g
.getOption('ylabel');
188 if (this.y2label_div_
) {
189 this.y2label_div_
.children
[0].children
[0].innerHTML
= g
.getOption('y2label');
193 chart_labels
.prototype.clearChart
= function() {
196 chart_labels
.prototype.destroy
= function() {
197 this.detachLabels_();