Merge branch 'remove-old-options' into new-series-option, make new style axis specifi...
authorRobert Konigsberg <konigsberg@gmail.com>
Sun, 25 Nov 2012 04:11:14 +0000 (23:11 -0500)
committerRobert Konigsberg <konigsberg@gmail.com>
Sun, 25 Nov 2012 04:11:14 +0000 (23:11 -0500)
Conflicts:
dygraph-options.js

1  2 
auto_tests/tests/per_series.js
dygraph-options.js
dygraph.js

@@@ -47,96 -48,3 +47,102 @@@ perSeriesTestCase.prototype.testPerSeri
    assertEquals([255,0,0,38], sampler.colorAtCoordinate(6.5, 0.5));
  };
  
-     logscale: true,
 +perSeriesTestCase.prototype.testOldStyleSeries = function() {
 +  var opts = {
 +    pointSize : 5,
 +    Y: { pointSize : 4 },
 +  };
 +  var graph = document.getElementById("graph");
 +  var data = "X,Y,Z\n1,0,0\n";
 +  g = new Dygraph(graph, data, opts);
 +
 +  assertEquals(5, g.getOption("pointSize"));
 +  assertEquals(4, g.getOption("pointSize", "Y"));
 +  assertEquals(5, g.getOption("pointSize", "Z"));
 +};
 +
 +perSeriesTestCase.prototype.testNewStyleSeries = function() {
 +  var opts = {
 +    pointSize : 5,
 +    series : {
 +      Y: { pointSize : 4 }
 +    },
 +  };
 +  var graph = document.getElementById("graph");
 +  var data = "X,Y,Z\n1,0,0\n";
 +  g = new Dygraph(graph, data, opts);
 +
 +  assertEquals(5, g.getOption("pointSize"));
 +  assertEquals(4, g.getOption("pointSize", "Y"));
 +  assertEquals(5, g.getOption("pointSize", "Z"));
 +};
 +
 +perSeriesTestCase.prototype.testNewStyleSeriesTrumpsOldStyle = function() {
 +  var opts = {
 +    pointSize : 5,
 +    Z : { pointSize : 6 },
 +    series : {
 +      Y: { pointSize : 4 }
 +    },
 +  };
 +  var graph = document.getElementById("graph");
 +  var data = "X,Y,Z\n1,0,0\n";
 +  g = new Dygraph(graph, data, opts);
 +
 +  assertEquals(5, g.getOption("pointSize"));
 +  assertEquals(4, g.getOption("pointSize", "Y"));
 +  assertEquals(5, g.getOption("pointSize", "Z"));
 +
 +  // Erase the series object, and Z will become visible again.
 +  g.updateOptions({ series : undefined });
 +  assertEquals(5, g.getOption("pointSize"));
 +  assertEquals(6, g.getOption("pointSize", "Z"));
 +  assertEquals(5, g.getOption("pointSize", "Y"));
 +};
 +
++// TODO(konigsberg): move to multiple_axes.js
 +perSeriesTestCase.prototype.testAxisInNewSeries = function() {
 +  var opts = {
-   assertEquals(5, g.getOption("pointSize"));
-   assertEquals(4, g.getOption("pointSize", "Y"));
-   assertEquals(5, g.getOption("pointSize", "Z"));
 +    series : {
 +      D : { axis : 'y2' },
 +      C : { axis : 1 },
 +      B : { axis : 0 },
 +      E : { axis : 'y' }
 +    }
 +  };
 +  var graph = document.getElementById("graph");
 +  var data = "X,A,B,C,D,E\n0,1,2,3,4,5\n";
 +  g = new Dygraph(graph, data, opts);
 +
-       y : {},
-       y2 : {}
++  assertEquals(["A", "B", "E"], g.attributes_.seriesForAxis(0));
++  assertEquals(["C", "D"], g.attributes_.seriesForAxis(1));
 +};
 +
++// TODO(konigsberg): move to multiple_axes.js
 +perSeriesTestCase.prototype.testAxisInNewSeries_withAxes = function() {
 +  var opts = {
 +    series : {
 +      D : { axis : 'y2' },
 +      C : { axis : 1 },
 +      B : { axis : 0 },
 +      E : { axis : 'y' }
 +    },
 +    axes : {
-   assertEquals(5, g.getOption("pointSize"));
-   assertEquals(4, g.getOption("pointSize", "Y"));
-   assertEquals(5, g.getOption("pointSize", "Z"));
++      y : { pointSize : 7 },
++      y2 : { pointSize  : 6 }
 +    }
 +  };
 +  var graph = document.getElementById("graph");
 +  var data = "X,A,B,C,D,E\n0,1,2,3,4,5\n";
 +  g = new Dygraph(graph, data, opts);
 +
++  assertEquals(["A", "B", "E"], g.attributes_.seriesForAxis(0));
++  assertEquals(["C", "D"], g.attributes_.seriesForAxis(1));
++
++  assertEquals(1.5, g.getOption("pointSize"));
++  assertEquals(7, g.getOption("pointSize", "A"));
++  assertEquals(7, g.getOption("pointSize", "B"));
++  assertEquals(6, g.getOption("pointSize", "C"));
++  assertEquals(6, g.getOption("pointSize", "D"));
++  assertEquals(7, g.getOption("pointSize", "E"));
 +};
@@@ -88,99 -55,63 +88,102 @@@ DygraphOptions.axisToIndex_ = function(
  DygraphOptions.prototype.reparseSeries = function() {
    this.labels = this.get("labels").slice(1);
  
-   this.axes_ = [ {} ]; // Always one axis at least.
+   this.axes_ = [ { series : [], options : {}} ]; // Always one axis at least.
    this.series_ = {};
  
 -  var axisId = 0; // 0-offset; there's always one.
 -  // Go through once, add all the series, and for those with {} axis options, add a new axis.
 -  for (var idx = 0; idx < this.labels.length; idx++) {
 -    var seriesName = this.labels[idx];
 +  // Traditionally, per-series options were specified right up there with the options. For instance
 +  // {
 +  //   labels: [ "X", "foo", "bar" ],
 +  //   pointSize: 3,
 +  //   foo : {}, // options for foo
 +  //   bar : {} // options for bar
 +  // }
 +  //
 +  // Moving forward, series really should be specified in the series element, separating them.
 +  // like so:
 +  //
 +  // {
 +  //   labels: [ "X", "foo", "bar" ],
 +  //   pointSize: 3,
 +  //   series : {
 +  //     foo : {}, // options for foo
 +  //     bar : {} // options for bar
 +  //   }
 +  // }
 +  //
 +  // So, if series is found, it's expected to contain per-series data, otherwise we fall
 +  // back.
 +  var oldStyleSeries = !this.user_["series"];
 +  
 +  if (oldStyleSeries) {
 +    var axisId = 0; // 0-offset; there's always one.
 +    // Go through once, add all the series, and for those with {} axis options, add a new axis.
 +    for (var idx = 0; idx < this.labels.length; idx++) {
 +      var seriesName = this.labels[idx];
 +  
 +      var optionsForSeries = this.user_[seriesName] || {};
 +  
 +      var yAxis = 0;
 +      var axis = optionsForSeries["axis"];
 +      if (typeof(axis) == 'object') {
 +        yAxis = ++axisId;
-         this.axes_[yAxis] = axis;
++        this.axes_[yAxis] = { series : [ seriesName ], options : axis };
 +      }
 -    var optionsForSeries = this.user_[seriesName] || {};
 -    var yAxis = 0;
++      // Associate series without axis options with axis 0.
++      if (!axis) { // undefined
++        this.axes_[0].series.push(seriesName);
++      }
 -    var axis = optionsForSeries["axis"];
 -    if (typeof(axis) == 'object') {
 -      yAxis = ++axisId;
 -      this.axes_[yAxis] = { series : [ seriesName ], options : axis };
 +      this.series_[seriesName] = { idx: idx, yAxis: yAxis, options : optionsForSeries };
      }
 -
 -    // Associate series without axis options with axis 0.
 -    if (!axis) { // undefined
 -      this.axes_[0].series.push(seriesName);
 +  
 +    // Go through one more time and assign series to an axis defined by another
 +    // series, e.g. { 'Y1: { axis: {} }, 'Y2': { axis: 'Y1' } }
 +    for (var idx = 0; idx < this.labels.length; idx++) {
 +      var seriesName = this.labels[idx];
 +      var optionsForSeries = this.series_[seriesName]["options"]; 
 +      var axis = optionsForSeries["axis"];
 +  
 +      if (typeof(axis) == 'string') {
 +        if (!this.series_.hasOwnProperty(axis)) {
 +          this.dygraph_.error("Series " + seriesName + " wants to share a y-axis with " +
 +                     "series " + axis + ", which does not define its own axis.");
 +          return null;
 +        }
-         this.series_[seriesName].yAxis = this.series_[axis].yAxis;
++        var yAxis = this.series_[axis].yAxis;
++        this.series_[seriesName].yAxis = yAxis;
++        this.axes_[yAxis].series.push(seriesName);
 +      }
      }
 +  } else {
 +    var maxYAxis = 0;
  
 -    this.series_[seriesName] = { idx: idx, yAxis: yAxis, options : optionsForSeries };
 -  }
 +    for (var idx = 0; idx < this.labels.length; idx++) {
 +      var seriesName = this.labels[idx];
 +      var optionsForSeries = this.user_.series[seriesName] || {};
 +      var yAxis = DygraphOptions.axisToIndex_(optionsForSeries["axis"]);
 +
 +      maxYAxis = Math.max(yAxis, maxYAxis);
  
 -  // Go through one more time and assign series to an axis defined by another
 -  // series, e.g. { 'Y1: { axis: {} }, 'Y2': { axis: 'Y1' } }
 -  for (var idx = 0; idx < this.labels.length; idx++) {
 -    var seriesName = this.labels[idx];
 -    var optionsForSeries = this.series_[seriesName]["options"]; 
 -    var axis = optionsForSeries["axis"];
 -
 -    if (typeof(axis) == 'string') {
 -      if (!this.series_.hasOwnProperty(axis)) {
 -        this.dygraph_.error("Series " + seriesName + " wants to share a y-axis with " +
 -                   "series " + axis + ", which does not define its own axis.");
 -        return null;
 +      this.series_[seriesName] = {
 +        idx: idx,
 +        yAxis: yAxis,
 +        options : optionsForSeries };
-     }
 +
-     for (; maxYAxis >= 0; maxYAxis--) {
-       this.axes_[maxYAxis] = {};
++      if (!this.axes_[yAxis]) {
++        this.axes_[yAxis] =  { series : [ seriesName ], options : {} };
++      } else {
++        this.axes_[yAxis].series.push(seriesName);
+       }
 -      var yAxis = this.series_[axis].yAxis;
 -      this.series_[seriesName].yAxis = yAxis;
 -      this.axes_[yAxis].series.push(seriesName);
      }
    }
  
    // This doesn't support reading from the 'x' axis, only 'y' and 'y2.
-   if (this.user_["axes"]) {
 -  // Read from the global "axes" option.
 -  if (this.user_.hasOwnProperty("axes")) {
--    var axis_opts = this.user_.axes;
--
--    if (axis_opts.hasOwnProperty("y")) {
-       Dygraph.update(this.axes_[0], axis_opts.y);
 -      Dygraph.update(this.axes_[0].options, axis_opts.y);
--    }
--
--    if (axis_opts.hasOwnProperty("y2")) {
-       this.axes_[1] = this.axes_[1] || {};
-       Dygraph.update(this.axes_[1], axis_opts.y2);
 -      this.axes_[1] = this.axes_[1] || {}; // FIX
 -      Dygraph.update(this.axes_[1].options, axis_opts.y2);
--    }
++  var axis_opts = this.user_["axes"] || {};
++  Dygraph.update(this.axes_[0].options, axis_opts["y"] || {});
++  if (this.axes_.length > 1) {
++    Dygraph.update(this.axes_[1].options, axis_opts["y2"] || {});   
    }
  };
  
@@@ -253,3 -184,32 +256,48 @@@ DygraphOptions.prototype.getForSeries 
  
    return this.getForAxis(name, seriesObj["yAxis"]);
  };
+ /**
+  * Returns the number of y-axes on the chart.
+  * @return {Number} the number of axes.
+  */
+ DygraphOptions.prototype.numAxes = function() {
+   return this.axes_.length;
+ }
+ /**
+  * Return the y-axis for a given series, specified by name.
+  */
+ DygraphOptions.prototype.axisForSeries = function(seriesName) {
+   return this.series_[seriesName].yAxis;
+ }
+ /**
+  * Returns the options for the specified axis.
+  */
+ DygraphOptions.prototype.axisOptions = function(yAxis) {
+   return this.axes_[yAxis].options;
+ }
+ /**
+  * Return the series associated with an axis.
+  */
+ DygraphOptions.prototype.seriesForAxis = function(yAxis) {
+   return this.axes_[yAxis].series;
+ }
++
++/**
++ * Return the list of all series, in their columnar order.
++ */
++DygraphOptions.prototype.seriesNames = function() {
++  return this.labels_;
++}
++
++/* Are we using this? */ 
++/**
++ * Return the index of the specified series.
++ * @param {string} series the series name.
++ */
++DygraphOptions.prototype.indexOfSeries = function(series) {
++  return this.series_[series].idx;
++}
diff --cc dygraph.js
@@@ -2436,16 -2434,15 +2434,16 @@@ Dygraph.prototype.computeYAxes_ = funct
      }
    }
  
--  this.axes_ = [{ yAxisId : 0, g : this }];  // always have at least one y-axis.
-   this.seriesToAxisMap_ = {};
--
--  // Get a list of series names.
--  var labels = this.attr_("labels");
--  var series = {};
--  for (i = 1; i < labels.length; i++) series[labels[i]] = (i - 1);
++  // this.axes_ doesn't match this.attributes_.axes_.options. It's used for
++  // data computation as well as options storage.
++  this.axes_ = [];
++  for (axis = 0; axis < this.attributes_.numAxes(); axis++) {
++    this.axes_.push({ yAxisId : i, g : this });
++  }
  
    // all options which could be applied per-axis:
--  var axisOptions = [
++  // TODO(konigsberg)
++  var globalAxisOptions = [
      'includeZero',
      'valueRange',
      'labelsKMB',
    ];
  
    // Copy global axis options over to the first axis.
--  for (i = 0; i < axisOptions.length; i++) {
--    var k = axisOptions[i];
++  for (i = 0; i < globalAxisOptions.length; i++) {
++    var k = globalAxisOptions[i];
      v = this.attr_(k);
      if (v) this.axes_[0][k] = v;
    }
  
    // Go through once and add all the axes.
--  for (seriesName in series) {
--    if (!series.hasOwnProperty(seriesName)) continue;
--    axis = this.attr_("axis", seriesName);
--    if (axis === null) {
-       this.seriesToAxisMap_[seriesName] = 0;
--      continue;
--    }
--    if (typeof(axis) == 'object') {
--      // Add a new axis, making a copy of its per-axis options.
--      opts = {};
--      Dygraph.update(opts, this.axes_[0]);
--      Dygraph.update(opts, { valueRange: null });  // shouldn't inherit this.
--      var yAxisId = this.axes_.length;
--      opts.yAxisId = yAxisId;
--      opts.g = this;
--      Dygraph.update(opts, axis);
--      this.axes_.push(opts);
-       this.seriesToAxisMap_[seriesName] = yAxisId;
-     }
-   }
-   // Go through one more time and assign series to an axis defined by another
-   // series, e.g. { 'Y1: { axis: {} }, 'Y2': { axis: 'Y1' } }
-   for (seriesName in series) {
-     if (!series.hasOwnProperty(seriesName)) continue;
-     axis = this.attr_("axis", seriesName);
-     if (typeof(axis) == 'string') {
-       if (!this.seriesToAxisMap_.hasOwnProperty(axis)) {
-         this.error("Series " + seriesName + " wants to share a y-axis with " +
-                    "series " + axis + ", which does not define its own axis.");
-         return null;
-       }
-       var idx = this.seriesToAxisMap_[axis];
-       this.seriesToAxisMap_[seriesName] = idx;
--    }
++  
++  // This seems to be right - starting at 1. I think this gets simpler now.
++  for (axis = 1; axis < this.attributes_.numAxes(); axis++) {
++    // Add a new axis, making a copy of its per-axis options.
++    opts = {};
++    Dygraph.update(opts, this.axes_[0]);
++    Dygraph.update(opts, { valueRange: null });  // shouldn't inherit this.
++    Dygraph.update(opts, this.attributes_.axisOptions(axis));
++    this.axes_[axis] = opts;
    }
  
    if (valueWindows !== undefined) {
        }
      }
    }
--
  };
  
  /**
@@@ -2559,25 -2532,24 +2524,20 @@@ Dygraph.prototype.axisPropertiesForSeri
   * This fills in the valueRange and ticks fields in each entry of this.axes_.
   */
  Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
--  // Build a map from axis number -> [list of series names]
-   var seriesForAxis = [], series;
-   for (series in this.seriesToAxisMap_) {
-     if (!this.seriesToAxisMap_.hasOwnProperty(series)) continue;
-     var idx = this.seriesToAxisMap_[series];
-     while (seriesForAxis.length <= idx) seriesForAxis.push([]);
-     seriesForAxis[idx].push(series);
-   }
 -  var seriesForAxis = [];
+   var series;
+   var numAxes = this.attributes_.numAxes();
 -  for (var yAxis = 0; yAxis < numAxes; yAxis++) {
 -    seriesForAxis[yAxis] = this.attributes_.seriesForAxis(yAxis);
 -  }
  
    // Compute extreme values, a span and tick marks for each axis.
-   for (var i = 0; i < this.axes_.length; i++) {
+   for (var i = 0; i < numAxes; i++) {
      var axis = this.axes_[i];
  
--    if (!seriesForAxis[i]) {
++    series = this.attributes_.seriesForAxis(i);
++
++    if (series.length == 0) {
        // If no series are defined or visible then use a reasonable default
        axis.extremeRange = [0, 1];
      } else {
        // Calculate the extremes of extremes.
--      series = seriesForAxis[i];
        var minY = Infinity;  // extremes[series[0]][0];
        var maxY = -Infinity;  // extremes[series[0]][1];
        var extremeMinY, extremeMaxY;