Merge pull request #590 from danvk/540-fixup
[dygraphs.git] / src / extras / rebase.js
diff --git a/src/extras/rebase.js b/src/extras/rebase.js
new file mode 100644 (file)
index 0000000..1c518a8
--- /dev/null
@@ -0,0 +1,139 @@
+/**
+ * @license
+ * Copyright 2015 Petr Shevtsov (petr.shevtsov@gmail.com)
+ * MIT-licensed (http://opensource.org/licenses/MIT)
+ *
+ * Rebase plugin
+ *
+ * On pan/zoom event, each series will rebase to a specified value (e.g. 100) at the
+ * start of the displayed period.
+ *
+ * See http://stats.oecd.org/glossary/detail.asp?ID=2249
+ *
+ * Options:
+ *  Value to rebase. Must be either Number or 'percent' or null.
+ *
+ * See tests/straw-broom.html for demo.
+ */
+
+/*global Dygraph:false */
+
+(function() {
+  "use strict";
+
+  Dygraph.DataHandlers.RebaseHandler = function(baseOpt) {
+    this.baseOpt = baseOpt;
+  };
+
+  var RebaseHandler =  Dygraph.DataHandlers.RebaseHandler;
+  RebaseHandler.prototype = new Dygraph.DataHandlers.DefaultHandler();
+
+  RebaseHandler.rebase = function(value, initial, base) {
+    if (base === "percent") {
+      return (value / initial - 1) * 100;
+    }
+    return value * base / initial;
+  };
+
+  RebaseHandler.prototype.getExtremeYValues = function(series, dateWindow, options) {
+    var minY = null, maxY = null, y;
+    var firstIdx = 0, lastIdx = series.length - 1;
+    var initial = series[firstIdx][1];
+
+    for (var j = firstIdx; j <= lastIdx; j++) {
+      if (j === firstIdx) {
+        y = (this.baseOpt === "percent") ? 0 : this.baseOpt;
+      } else {
+        y = RebaseHandler.rebase(series[j][1], initial, this.baseOpt);
+      }
+      if (y === null || isNaN(y))
+        continue;
+      if (maxY === null || y > maxY) {
+        maxY = y;
+      }
+      if (minY === null || y < minY) {
+        minY = y;
+      }
+    }
+    return [ minY, maxY ];
+  };
+
+  RebaseHandler.prototype.seriesToPoints = function(series, setName, boundaryIdStart){
+    var points = [];
+    var firstIdx = 0;
+    var lastIdx = series.length - 1;
+    var initial = series[firstIdx][1]; // TODO: check for null
+    for (var i = 0; i <= lastIdx; ++i) {
+      var item = series[i];
+      var yraw = item[1];
+      var yval = yraw === null ? null : Dygraph.DataHandler.parseFloat(yraw);
+      if (yval !== null) {
+        if (i === firstIdx) {
+          yval = (this.baseOpt === "percent") ? 0 : this.baseOpt;
+        } else {
+          yval = RebaseHandler.rebase(yval, initial, this.baseOpt);
+        }
+      }
+      var point = {
+        x: NaN,
+        y: NaN,
+        xval: Dygraph.DataHandler.parseFloat(item[0]),
+        yval: yval,
+        name: setName,
+        idx: i + boundaryIdStart
+      };
+      points.push(point);
+    }
+    this.onPointsCreated_(series, points);
+    return points;
+  };
+
+  Dygraph.Plugins.Rebase = (function() {
+    var rebase = function(baseOpt) {
+      var isNum = function(v) {
+        return !isNaN(v) && (typeof v === 'number' || {}.toString.call(v) === '[object Number]');
+      };
+      if (baseOpt === "percent" || isNum(baseOpt)) {
+        this.baseOpt_ = baseOpt;
+      } else {
+        this.baseOpt_ = null;
+      }
+    };
+
+    rebase.prototype.toString = function() {
+      return "Rebase Plugin";
+    };
+
+    rebase.prototype.activate = function(g) {
+      if (this.baseOpt_ === null) {
+        return;
+      }
+      return {
+        predraw: this.predraw
+      };
+    };
+
+    rebase.prototype.predraw = function(e) {
+      var g = e.dygraph;
+
+      if (this.baseOpt_ === "percent") {
+        g.updateOptions({
+          axes: {
+            y: {
+              axisLabelFormatter: function(y) {
+                return y + '%';
+              },
+              valueFormatter: function(y) {
+                return Math.round(y * 100) / 100 + '%';
+              }
+            }
+          }
+        }, true);
+      }
+
+      g.dataHandler_ = new Dygraph.DataHandlers.RebaseHandler(this.baseOpt_);
+    };
+
+    return rebase;
+  })();
+})();