merge upstream changes
authorDan Vanderkam <dan@dygraphs.com>
Mon, 6 Jun 2011 13:15:53 +0000 (09:15 -0400)
committerDan Vanderkam <dan@dygraphs.com>
Mon, 6 Jun 2011 13:15:53 +0000 (09:15 -0400)
1  2 
dygraph-canvas.js
dygraph.js

   */
  
  /**
 + * This class determines the charting area (in pixel coordinates), maps the
 + * percentage coordinates in the DygraphLayout to pixels and draws them.
 + * It's also responsible for creating chart DOM elements, i.e. annotations,
 + * tick mark labels, the title and the x/y-axis labels.
 + * This class is based on PlotKit.CanvasRenderer.
 + *
+  * Creates a new DygraphLayout object.
+  * @return {Object} The DygraphLayout object
+  */
+ DygraphLayout = function(dygraph) {
+   this.dygraph_ = dygraph;
+   this.datasets = new Array();
+   this.annotations = new Array();
+   this.yAxes_ = null;
+   // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs, but xticks and
+   // yticks are outputs. Clean this up.
+   this.xTicks_ = null;
+   this.yTicks_ = null;
+ };
+ DygraphLayout.prototype.attr_ = function(name) {
+   return this.dygraph_.attr_(name);
+ };
+ DygraphLayout.prototype.addDataset = function(setname, set_xy) {
+   this.datasets[setname] = set_xy;
+ };
+ DygraphLayout.prototype.setAnnotations = function(ann) {
+   // The Dygraph object's annotations aren't parsed. We parse them here and
+   // save a copy. If there is no parser, then the user must be using raw format.
+   this.annotations = [];
+   var parse = this.attr_('xValueParser') || function(x) { return x; };
+   for (var i = 0; i < ann.length; i++) {
+     var a = {};
+     if (!ann[i].xval && !ann[i].x) {
+       this.dygraph_.error("Annotations must have an 'x' property");
+       return;
+     }
+     if (ann[i].icon &&
+         !(ann[i].hasOwnProperty('width') &&
+           ann[i].hasOwnProperty('height'))) {
+       this.dygraph_.error("Must set width and height when setting " +
+                           "annotation.icon property");
+       return;
+     }
+     Dygraph.update(a, ann[i]);
+     if (!a.xval) a.xval = parse(a.x, this.dygraph_);
+     this.annotations.push(a);
+   }
+ };
+ DygraphLayout.prototype.setXTicks = function(xTicks) {
+   this.xTicks_ = xTicks;
+ };
+ // TODO(danvk): add this to the Dygraph object's API or move it into Layout.
+ DygraphLayout.prototype.setYAxes = function (yAxes) {
+   this.yAxes_ = yAxes;
+ };
+ DygraphLayout.prototype.setDateWindow = function(dateWindow) {
+   this.dateWindow_ = dateWindow;
+ };
+ DygraphLayout.prototype.evaluate = function() {
+   this._evaluateLimits();
+   this._evaluateLineCharts();
+   this._evaluateLineTicks();
+   this._evaluateAnnotations();
+ };
+ DygraphLayout.prototype._evaluateLimits = function() {
+   this.minxval = this.maxxval = null;
+   if (this.dateWindow_) {
+     this.minxval = this.dateWindow_[0];
+     this.maxxval = this.dateWindow_[1];
+   } else {
+     for (var name in this.datasets) {
+       if (!this.datasets.hasOwnProperty(name)) continue;
+       var series = this.datasets[name];
+       if (series.length > 1) {
+         var x1 = series[0][0];
+         if (!this.minxval || x1 < this.minxval) this.minxval = x1;
+   
+         var x2 = series[series.length - 1][0];
+         if (!this.maxxval || x2 > this.maxxval) this.maxxval = x2;
+       }
+     }
+   }
+   this.xrange = this.maxxval - this.minxval;
+   this.xscale = (this.xrange != 0 ? 1/this.xrange : 1.0);
+   for (var i = 0; i < this.yAxes_.length; i++) {
+     var axis = this.yAxes_[i];
+     axis.minyval = axis.computedValueRange[0];
+     axis.maxyval = axis.computedValueRange[1];
+     axis.yrange = axis.maxyval - axis.minyval;
+     axis.yscale = (axis.yrange != 0 ? 1.0 / axis.yrange : 1.0);
+     if (axis.g.attr_("logscale")) {
+       axis.ylogrange = Dygraph.log10(axis.maxyval) - Dygraph.log10(axis.minyval);
+       axis.ylogscale = (axis.ylogrange != 0 ? 1.0 / axis.ylogrange : 1.0);
+       if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) {
+         axis.g.error('axis ' + i + ' of graph at ' + axis.g +
+             ' can\'t be displayed in log scale for range [' +
+             axis.minyval + ' - ' + axis.maxyval + ']');
+       }
+     }
+   }
+ };
+ DygraphLayout.prototype._evaluateLineCharts = function() {
+   // add all the rects
+   this.points = new Array();
+   for (var setName in this.datasets) {
+     if (!this.datasets.hasOwnProperty(setName)) continue;
+     var dataset = this.datasets[setName];
+     var axis = this.dygraph_.axisPropertiesForSeries(setName);
+     for (var j = 0; j < dataset.length; j++) {
+       var item = dataset[j];
+       var yval;
+       if (axis.logscale) {
+         yval = 1.0 - ((Dygraph.log10(parseFloat(item[1])) - Dygraph.log10(axis.minyval)) * axis.ylogscale); // really should just be yscale.
+       } else {
+         yval = 1.0 - ((parseFloat(item[1]) - axis.minyval) * axis.yscale);
+       }
+       var point = {
+         // TODO(danvk): here
+         x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
+         y: yval,
+         xval: parseFloat(item[0]),
+         yval: parseFloat(item[1]),
+         name: setName
+       };
+       this.points.push(point);
+     }
+   }
+ };
+ DygraphLayout.prototype._evaluateLineTicks = function() {
+   this.xticks = new Array();
+   for (var i = 0; i < this.xTicks_.length; i++) {
+     var tick = this.xTicks_[i];
+     var label = tick.label;
+     var pos = this.xscale * (tick.v - this.minxval);
+     if ((pos >= 0.0) && (pos <= 1.0)) {
+       this.xticks.push([pos, label]);
+     }
+   }
+   this.yticks = new Array();
+   for (var i = 0; i < this.yAxes_.length; i++ ) {
+     var axis = this.yAxes_[i];
+     for (var j = 0; j < axis.ticks.length; j++) {
+       var tick = axis.ticks[j];
+       var label = tick.label;
+       var pos = this.dygraph_.toPercentYCoord(tick.v, i);
+       if ((pos >= 0.0) && (pos <= 1.0)) {
+         this.yticks.push([i, pos, label]);
+       }
+     }
+   }
+ };
+ /**
+  * Behaves the same way as PlotKit.Layout, but also copies the errors
+  * @private
+  */
+ DygraphLayout.prototype.evaluateWithError = function() {
+   this.evaluate();
+   if (!(this.attr_('errorBars') || this.attr_('customBars'))) return;
+   // Copy over the error terms
+   var i = 0; // index in this.points
+   for (var setName in this.datasets) {
+     if (!this.datasets.hasOwnProperty(setName)) continue;
+     var j = 0;
+     var dataset = this.datasets[setName];
+     for (var j = 0; j < dataset.length; j++, i++) {
+       var item = dataset[j];
+       var xv = parseFloat(item[0]);
+       var yv = parseFloat(item[1]);
+       if (xv == this.points[i].xval &&
+           yv == this.points[i].yval) {
+         this.points[i].errorMinus = parseFloat(item[2]);
+         this.points[i].errorPlus = parseFloat(item[3]);
+       }
+     }
+   }
+ };
+ DygraphLayout.prototype._evaluateAnnotations = function() {
+   // Add the annotations to the point to which they belong.
+   // Make a map from (setName, xval) to annotation for quick lookups.
+   var annotations = {};
+   for (var i = 0; i < this.annotations.length; i++) {
+     var a = this.annotations[i];
+     annotations[a.xval + "," + a.series] = a;
+   }
+   this.annotated_points = [];
+   for (var i = 0; i < this.points.length; i++) {
+     var p = this.points[i];
+     var k = p.xval + "," + p.name;
+     if (k in annotations) {
+       p.annotation = annotations[k];
+       this.annotated_points.push(p);
+     }
+   }
+ };
+ /**
+  * Convenience function to remove all the data sets from a graph
+  */
+ DygraphLayout.prototype.removeAllDatasets = function() {
+   delete this.datasets;
+   this.datasets = new Array();
+ };
+ /**
+  * Return a copy of the point at the indicated index, with its yval unstacked.
+  * @param int index of point in layout_.points
+  */
+ DygraphLayout.prototype.unstackPointAtIndex = function(idx) {
+   var point = this.points[idx];
+   
+   // Clone the point since we modify it
+   var unstackedPoint = {};  
+   for (var i in point) {
+     unstackedPoint[i] = point[i];
+   }
+   
+   if (!this.attr_("stackedGraph")) {
+     return unstackedPoint;
+   }
+   
+   // The unstacked yval is equal to the current yval minus the yval of the 
+   // next point at the same xval.
+   for (var i = idx+1; i < this.points.length; i++) {
+     if (this.points[i].xval == point.xval) {
+       unstackedPoint.yval -= this.points[i].yval; 
+       break;
+     }
+   }
+   
+   return unstackedPoint;
+ }  
+ /**
+  * The DygraphCanvasRenderer class does the actual rendering of the chart onto
+  * a canvas. It's based on PlotKit.CanvasRenderer.
++>>>>>>> master
   * @param {Object} element The canvas to attach to
   * @param {Object} elementContext The 2d context of the canvas (injected so it
   * can be mocked for testing.)
diff --cc dygraph.js
Simple merge