Merge branch 'PR723'
[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 "use strict";
9
10 // TODO(danvk): move chart label options out of dygraphs and into the plugin.
11 // TODO(danvk): only tear down & rebuild the DIVs when it's necessary.
12
13 var chart_labels = function() {
14 this.title_div_ = null;
15 this.xlabel_div_ = null;
16 this.ylabel_div_ = null;
17 this.y2label_div_ = null;
18 };
19
20 chart_labels.prototype.toString = function() {
21 return "ChartLabels Plugin";
22 };
23
24 chart_labels.prototype.activate = function(g) {
25 return {
26 layout: this.layout,
27 // clearChart: this.clearChart,
28 didDrawChart: this.didDrawChart
29 };
30 };
31
32 // QUESTION: should there be a plugin-utils.js?
33 var createDivInRect = function(r) {
34 var div = document.createElement('div');
35 div.style.position = 'absolute';
36 div.style.left = r.x + 'px';
37 div.style.top = r.y + 'px';
38 div.style.width = r.w + 'px';
39 div.style.height = r.h + 'px';
40 return div;
41 };
42
43 // Detach and null out any existing nodes.
44 chart_labels.prototype.detachLabels_ = function() {
45 var els = [ this.title_div_,
46 this.xlabel_div_,
47 this.ylabel_div_,
48 this.y2label_div_ ];
49 for (var i = 0; i < els.length; i++) {
50 var el = els[i];
51 if (!el) continue;
52 if (el.parentNode) el.parentNode.removeChild(el);
53 }
54
55 this.title_div_ = null;
56 this.xlabel_div_ = null;
57 this.ylabel_div_ = null;
58 this.y2label_div_ = null;
59 };
60
61 var createRotatedDiv = function(g, box, axis, classes, html) {
62 // TODO(danvk): is this outer div actually necessary?
63 var div = document.createElement("div");
64 div.style.position = 'absolute';
65 if (axis == 1) {
66 // NOTE: this is cheating. Should be positioned relative to the box.
67 div.style.left = '0px';
68 } else {
69 div.style.left = box.x + 'px';
70 }
71 div.style.top = box.y + 'px';
72 div.style.width = box.w + 'px';
73 div.style.height = box.h + 'px';
74 div.style.fontSize = (g.getOption('yLabelWidth') - 2) + 'px';
75
76 var inner_div = document.createElement("div");
77 inner_div.style.position = 'absolute';
78 inner_div.style.width = box.h + 'px';
79 inner_div.style.height = box.w + 'px';
80 inner_div.style.top = (box.h / 2 - box.w / 2) + 'px';
81 inner_div.style.left = (box.w / 2 - box.h / 2) + 'px';
82 // TODO: combine inner_div and class_div.
83 inner_div.className = 'dygraph-label-rotate-' + (axis == 1 ? 'right' : 'left');
84
85 var class_div = document.createElement("div");
86 class_div.className = classes;
87 class_div.innerHTML = html;
88
89 inner_div.appendChild(class_div);
90 div.appendChild(inner_div);
91 return div;
92 };
93
94 chart_labels.prototype.layout = function(e) {
95 this.detachLabels_();
96
97 var g = e.dygraph;
98 var div = e.chart_div;
99 if (g.getOption('title')) {
100 // QUESTION: should this return an absolutely-positioned div instead?
101 var title_rect = e.reserveSpaceTop(g.getOption('titleHeight'));
102 this.title_div_ = createDivInRect(title_rect);
103 this.title_div_.style.fontSize = (g.getOption('titleHeight') - 8) + 'px';
104
105 var class_div = document.createElement("div");
106 class_div.className = 'dygraph-label dygraph-title';
107 class_div.innerHTML = g.getOption('title');
108 this.title_div_.appendChild(class_div);
109 div.appendChild(this.title_div_);
110 }
111
112 if (g.getOption('xlabel')) {
113 var x_rect = e.reserveSpaceBottom(g.getOption('xLabelHeight'));
114 this.xlabel_div_ = createDivInRect(x_rect);
115 this.xlabel_div_.style.fontSize = (g.getOption('xLabelHeight') - 2) + 'px';
116
117 var class_div = document.createElement("div");
118 class_div.className = 'dygraph-label dygraph-xlabel';
119 class_div.innerHTML = g.getOption('xlabel');
120 this.xlabel_div_.appendChild(class_div);
121 div.appendChild(this.xlabel_div_);
122 }
123
124 if (g.getOption('ylabel')) {
125 // It would make sense to shift the chart here to make room for the y-axis
126 // label, but the default yAxisLabelWidth is large enough that this results
127 // in overly-padded charts. The y-axis label should fit fine. If it
128 // doesn't, the yAxisLabelWidth option can be increased.
129 var y_rect = e.reserveSpaceLeft(0);
130
131 this.ylabel_div_ = createRotatedDiv(
132 g, y_rect,
133 1, // primary (left) y-axis
134 'dygraph-label dygraph-ylabel',
135 g.getOption('ylabel'));
136 div.appendChild(this.ylabel_div_);
137 }
138
139 if (g.getOption('y2label') && g.numAxes() == 2) {
140 // same logic applies here as for ylabel.
141 var y2_rect = e.reserveSpaceRight(0);
142 this.y2label_div_ = createRotatedDiv(
143 g, y2_rect,
144 2, // secondary (right) y-axis
145 'dygraph-label dygraph-y2label',
146 g.getOption('y2label'));
147 div.appendChild(this.y2label_div_);
148 }
149 };
150
151 chart_labels.prototype.didDrawChart = function(e) {
152 var g = e.dygraph;
153 if (this.title_div_) {
154 this.title_div_.children[0].innerHTML = g.getOption('title');
155 }
156 if (this.xlabel_div_) {
157 this.xlabel_div_.children[0].innerHTML = g.getOption('xlabel');
158 }
159 if (this.ylabel_div_) {
160 this.ylabel_div_.children[0].children[0].innerHTML = g.getOption('ylabel');
161 }
162 if (this.y2label_div_) {
163 this.y2label_div_.children[0].children[0].innerHTML = g.getOption('y2label');
164 }
165 };
166
167 chart_labels.prototype.clearChart = function() {
168 };
169
170 chart_labels.prototype.destroy = function() {
171 this.detachLabels_();
172 };
173
174 export default chart_labels;