      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