Merge pull request #673 from danvk/track-code-size
[dygraphs.git] / src / plugins / chart-labels.js
1 /**
2 * @license
3 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
4 * MIT-licensed (http://opensource.org/licenses/MIT)
5 */
6 /*global Dygraph:false */
7
8 Dygraph.Plugins.ChartLabels = (function() {
9
10 "use strict";
11
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.
14
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;
20 };
21
22 chart_labels.prototype.toString = function() {
23 return "ChartLabels Plugin";
24 };
25
26 chart_labels.prototype.activate = function(g) {
27 return {
28 layout: this.layout,
29 // clearChart: this.clearChart,
30 didDrawChart: this.didDrawChart
31 };
32 };
33
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';
42 return div;
43 };
44
45 // Detach and null out any existing nodes.
46 chart_labels.prototype.detachLabels_ = function() {
47 var els = [ this.title_div_,
48 this.xlabel_div_,
49 this.ylabel_div_,
50 this.y2label_div_ ];
51 for (var i = 0; i < els.length; i++) {
52 var el = els[i];
53 if (!el) continue;
54 if (el.parentNode) el.parentNode.removeChild(el);
55 }
56
57 this.title_div_ = null;
58 this.xlabel_div_ = null;
59 this.ylabel_div_ = null;
60 this.y2label_div_ = null;
61 };
62
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';
67 if (axis == 1) {
68 // NOTE: this is cheating. Should be positioned relative to the box.
69 div.style.left = '0px';
70 } else {
71 div.style.left = box.x + 'px';
72 }
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';
77
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';
85
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
94
95 var class_div = document.createElement("div");
96 class_div.className = classes;
97 class_div.innerHTML = html;
98
99 inner_div.appendChild(class_div);
100 div.appendChild(inner_div);
101 return div;
102 };
103
104 chart_labels.prototype.layout = function(e) {
105 this.detachLabels_();
106
107 var g = e.dygraph;
108 var div = e.chart_div;
109 if (g.getOption('title')) {
110 // QUESTION: should this return an absolutely-positioned div instead?
111 var title_rect = e.reserveSpaceTop(g.getOption('titleHeight'));
112 this.title_div_ = createDivInRect(title_rect);
113 this.title_div_.style.textAlign = 'center';
114 this.title_div_.style.fontSize = (g.getOption('titleHeight') - 8) + 'px';
115 this.title_div_.style.fontWeight = 'bold';
116 this.title_div_.style.zIndex = 10;
117
118 var class_div = document.createElement("div");
119 class_div.className = 'dygraph-label dygraph-title';
120 class_div.innerHTML = g.getOption('title');
121 this.title_div_.appendChild(class_div);
122 div.appendChild(this.title_div_);
123 }
124
125 if (g.getOption('xlabel')) {
126 var x_rect = e.reserveSpaceBottom(g.getOption('xLabelHeight'));
127 this.xlabel_div_ = createDivInRect(x_rect);
128 this.xlabel_div_.style.textAlign = 'center';
129 this.xlabel_div_.style.fontSize = (g.getOption('xLabelHeight') - 2) + 'px';
130
131 var class_div = document.createElement("div");
132 class_div.className = 'dygraph-label dygraph-xlabel';
133 class_div.innerHTML = g.getOption('xlabel');
134 this.xlabel_div_.appendChild(class_div);
135 div.appendChild(this.xlabel_div_);
136 }
137
138 if (g.getOption('ylabel')) {
139 // It would make sense to shift the chart here to make room for the y-axis
140 // label, but the default yAxisLabelWidth is large enough that this results
141 // in overly-padded charts. The y-axis label should fit fine. If it
142 // doesn't, the yAxisLabelWidth option can be increased.
143 var y_rect = e.reserveSpaceLeft(0);
144
145 this.ylabel_div_ = createRotatedDiv(
146 g, y_rect,
147 1, // primary (left) y-axis
148 'dygraph-label dygraph-ylabel',
149 g.getOption('ylabel'));
150 div.appendChild(this.ylabel_div_);
151 }
152
153 if (g.getOption('y2label') && g.numAxes() == 2) {
154 // same logic applies here as for ylabel.
155 var y2_rect = e.reserveSpaceRight(0);
156 this.y2label_div_ = createRotatedDiv(
157 g, y2_rect,
158 2, // secondary (right) y-axis
159 'dygraph-label dygraph-y2label',
160 g.getOption('y2label'));
161 div.appendChild(this.y2label_div_);
162 }
163 };
164
165 chart_labels.prototype.didDrawChart = function(e) {
166 var g = e.dygraph;
167 if (this.title_div_) {
168 this.title_div_.children[0].innerHTML = g.getOption('title');
169 }
170 if (this.xlabel_div_) {
171 this.xlabel_div_.children[0].innerHTML = g.getOption('xlabel');
172 }
173 if (this.ylabel_div_) {
174 this.ylabel_div_.children[0].children[0].innerHTML = g.getOption('ylabel');
175 }
176 if (this.y2label_div_) {
177 this.y2label_div_.children[0].children[0].innerHTML = g.getOption('y2label');
178 }
179 };
180
181 chart_labels.prototype.clearChart = function() {
182 };
183
184 chart_labels.prototype.destroy = function() {
185 this.detachLabels_();
186 };
187
188
189 return chart_labels;
190 })();