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