1 /**
  2  * @license
  3  * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
  4  * MIT-licenced: https://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;
175