Merge pull request #176 from kberg/add-per-series-to-tests
authorRobert Konigsberg <konigsberg@gmail.com>
Thu, 22 Nov 2012 16:29:05 +0000 (08:29 -0800)
committerRobert Konigsberg <konigsberg@gmail.com>
Thu, 22 Nov 2012 16:29:05 +0000 (08:29 -0800)
Add missing tests to local.html, organize.

auto_tests/misc/local.html
auto_tests/tests/multiple_axes.js
dygraph-dev.js
dygraph-options.js [new file with mode: 0644]
dygraph.js

index e35d18d..ae0b6b3 100644 (file)
@@ -8,7 +8,7 @@
   <![endif]-->
   <script type="text/javascript" src="../../dygraph-dev.js"></script>
 
-  <!-- Scripts for automated tests -->
+  <!-- Scripts for library support -->
   <script type="text/javascript" src="../lib/jquery-1.4.2.js"></script>
   <script type="text/javascript" src="../lib/Asserts.js"></script>
   <script type="text/javascript" src="fake-jstestdriver.js"></script>
@@ -16,6 +16,8 @@
   <script type="text/javascript" src="../tests/CanvasAssertions.js"></script>
   <script type="text/javascript" src="../tests/DygraphOps.js"></script>
   <script type="text/javascript" src="../tests/PixelSampler.js"></script>
+
+  <!-- Scripts for automated tests -->
   <script type="text/javascript" src="../tests/annotations.js"></script>
   <script type="text/javascript" src="../tests/axis_labels.js"></script>
   <script type="text/javascript" src="../tests/callback.js"></script>
   <script type="text/javascript" src="../tests/formats.js"></script>
   <script type="text/javascript" src="../tests/interaction_model.js"></script>
   <script type="text/javascript" src="../tests/missing_points.js"></script>
-  <script type="text/javascript" src="../tests/multiple_axes.js"></script>
   <script type="text/javascript" src="../tests/multi_csv.js"></script>
+  <script type="text/javascript" src="../tests/multiple_axes.js"></script>
   <script type="text/javascript" src="../tests/no_hours.js"></script>
+  <script type="text/javascript" src="../tests/parser.js"></script>
   <script type="text/javascript" src="../tests/pathological_cases.js"></script>
-
+  <script type="text/javascript" src="../tests/per-series.js"></script>
+  <script type="text/javascript" src="../tests/range_selector.js"></script>
   <script type="text/javascript" src="../tests/range_tests.js"></script>
   <script type="text/javascript" src="../tests/rolling_average.js"></script>
   <script type="text/javascript" src="../tests/sanity.js"></script>
-  <script type="text/javascript" src="../tests/selection.js"></script>
   <script type="text/javascript" src="../tests/scientific_notation.js"></script>
   <script type="text/javascript" src="../tests/scrolling_div.js"></script>
+  <script type="text/javascript" src="../tests/selection.js"></script>
   <script type="text/javascript" src="../tests/simple_drawing.js"></script>
+  <script type="text/javascript" src="../tests/stacked.js"></script>
   <!--
   <script type="text/javascript" src="../tests/tickers.js"></script>
   -->
   <script type="text/javascript" src="../tests/to_dom_coords.js"></script>
-  <script type="text/javascript" src="../tests/update_while_panning.js"></script>
-  <script type="text/javascript" src="../tests/stacked.js"></script>
-  <script type="text/javascript" src="../tests/per_series.js"></script>
-  <script type="text/javascript" src="../tests/parser.js"></script>
   <script type="text/javascript" src="../tests/update_options.js"></script>
+  <script type="text/javascript" src="../tests/update_while_panning.js"></script>
   <script type="text/javascript" src="../tests/utils_test.js"></script>
 
 <style type="text/css">
index 05e93c7..d08ff50 100644 (file)
@@ -290,4 +290,47 @@ MultipleAxesTestCase.prototype.testValueRangePerAxisOptions = function() {
   );
   assertEquals(["40", "45", "50", "55", "60", "65", "70", "75"], getYLabelsForAxis("1"));
   assertEquals(["1M", "1.02M", "1.05M", "1.08M", "1.1M", "1.13M", "1.15M", "1.18M"], getYLabelsForAxis("2"));
-};
\ No newline at end of file
+};
+
+MultipleAxesTestCase.prototype.testDrawPointCallback = function() {
+  var data = MultipleAxesTestCase.getData();
+
+  var results = { y : {}, y2 : {}};
+  var firstCallback = function(g, seriesName, ctx, canvasx, canvasy, color, radius) {
+    results.y[seriesName] = 1; 
+    Dygraph.Circles.DEFAULT(g, seriesName, ctx, canvasx, canvasy, color, radius);
+
+  };
+  var secondCallback = function(g, seriesName, ctx, canvasx, canvasy, color, radius) {
+    results.y2[seriesName] = 1; 
+    Dygraph.Circles.TRIANGLE(g, seriesName, ctx, canvasx, canvasy, color, radius);
+  };
+
+  g = new Dygraph(
+    document.getElementById("graph"),
+    data,
+    {
+      labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
+      drawPoints : true,
+      pointSize : 3,
+      'Y3': {
+        axis: {
+        }
+      },
+      'Y4': {
+        axis: 'Y3'  // use the same y-axis as series Y3
+      },
+      axes: {
+        y2: {
+          drawPointCallback: secondCallback
+        }
+      },
+      drawPointCallback: firstCallback
+    }
+  );
+
+  assertEquals(1, results.y["Y1"]);
+  assertEquals(1, results.y["Y2"]);
+  assertEquals(1, results.y2["Y3"]);
+  assertEquals(1, results.y2["Y4"]);
+};
index 4ca6bb3..b90796c 100644 (file)
@@ -20,6 +20,7 @@
     "rgbcolor/rgbcolor.js",
     "stacktrace.js",
     "dashed-canvas.js",
+    "dygraph-options.js",
     "dygraph-layout.js",
     "dygraph-canvas.js",
     "dygraph.js",
diff --git a/dygraph-options.js b/dygraph-options.js
new file mode 100644 (file)
index 0000000..cc8c770
--- /dev/null
@@ -0,0 +1,143 @@
+/**
+ * @fileoverview DygraphOptions is responsible for parsing and returning information about options.
+ *
+ * Still tightly coupled to Dygraphs, we could remove some of that, you know.
+ */
+
+"use strict";
+
+/*
+ * Interesting member variables:
+ * dygraph_ - the graph.
+ * global - global attributes (common among all graphs, AIUI)
+ * user - attributes set by the user
+ * axes
+ * series - { seriesName -> { idx, yAxis, options }
+ * labels - used as mapping from index to series name.
+ */
+
+/**
+ * @constructor
+ *
+ * This parses attributes into an object that can be easily queried.
+ *
+ * @param {Dyraph} dygraph The chart to which these options belong.
+ */
+var DygraphOptions = function(dygraph) {
+  this.dygraph_ = dygraph;
+  this.axes_ = [];
+  this.series_ = {};
+
+  // Once these two objects are initialized, you can call find();
+  this.global_ = this.dygraph_.attrs_;
+  this.user_ = this.dygraph_.user_attrs_ || {};
+
+  this.highlightSeries_ = this.find("highlightSeriesOpts") || {};
+  // Get a list of series names.
+
+  var labels = this.find("labels");
+  if (!labels) {
+    return; // -- can't do more for now, will parse after getting the labels.
+  };
+
+  this.reparseSeries();
+}
+
+DygraphOptions.prototype.reparseSeries = function() {
+  this.labels = this.find("labels").slice(1);
+
+  this.axes_ = [ {} ]; // 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];
+
+    var optionsForSeries = this.user_[seriesName] || {};
+    var yAxis = 0;
+
+    var axis = optionsForSeries["axis"];
+    if (typeof(axis) == 'object') {
+      yAxis = ++axisId;
+      this.axes_[yAxis] = axis;
+    }
+    this.series_[seriesName] = { idx: idx, yAxis: yAxis, options : optionsForSeries };
+  }
+
+  // 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;
+    }
+  }
+
+  // This doesn't support reading from the 'x' axis, only 'y' and 'y2.
+  // 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);
+    }
+
+    if (axis_opts.hasOwnProperty("y2")) {
+      this.axes_[1] = this.axes_[1] || {};
+      Dygraph.update(this.axes_[1], axis_opts.y2);
+    }
+  }
+};
+
+DygraphOptions.prototype.find = function(name) {
+  if (this.user_.hasOwnProperty(name)) {
+    return this.user_[name];
+  }
+  if (this.global_.hasOwnProperty(name)) {
+    return this.global_[name];
+  }
+  return null;
+}
+
+DygraphOptions.prototype.findForAxis = function(name, axis) {
+  var axisIdx = (axis == "y2" || axis == "y2" || axis == 1) ? 1 : 0;
+
+  var axisOptions = this.axes_[axisIdx];
+  if (axisOptions.hasOwnProperty(name)) {
+    return axisOptions[name];
+  }
+  return this.find(name);
+}
+
+DygraphOptions.prototype.findForSeries = function(name, series) {
+  // Honors indexes as series.
+  var seriesName = (typeof(series) == "number") ? this.labels[series] : series;
+
+  if (seriesName === this.dygraph_.highlightSet_) {
+    if (this.highlightSeries_.hasOwnProperty(name)) {
+      return this.highlightSeries_[name];
+    }
+  }
+
+  if (!this.series_.hasOwnProperty(seriesName)) {
+    throw "Unknown series: " + series;
+  }
+
+  var seriesObj = this.series_[seriesName];
+  var seriesOptions = seriesObj["options"];
+  if (seriesOptions.hasOwnProperty(name)) {
+    return seriesOptions[name];
+  }
+
+  return this.findForAxis(name, seriesObj["yAxis"]);
+}
+
index b81789b..c7f7087 100644 (file)
@@ -437,6 +437,8 @@ Dygraph.prototype.__init__ = function(div, file, attrs) {
   this.registeredEvents_ = [];
   this.eventListeners_ = {};
 
+  this.attributes_ = new DygraphOptions(this);
+
   // Create the containing DIV and other interactive elements
   this.createInterface_();
 
@@ -568,6 +570,12 @@ Dygraph.prototype.attr_ = function(name, seriesName) {
   }
 // </REMOVE_FOR_COMBINED>
 
+  // Building an array which we peruse in backwards order to find the correct value.
+  // Options are checked in this order:
+  // series, axis, user attrs, global attrs.
+  // TODO(konigsberg): Can this be made faster by starting with the series and working outward,
+  // rather than building an array?
+
   var sources = [];
   sources.push(this.attrs_);
   if (this.user_attrs_) {
@@ -576,6 +584,8 @@ Dygraph.prototype.attr_ = function(name, seriesName) {
       if (this.user_attrs_.hasOwnProperty(seriesName)) {
         sources.push(this.user_attrs_[seriesName]);
       }
+
+      // TODO(konigsberg): This special case ought to be documented.
       if (seriesName === this.highlightSet_ &&
           this.user_attrs_.hasOwnProperty('highlightSeriesOpts')) {
         sources.push(this.user_attrs_.highlightSeriesOpts);
@@ -591,7 +601,14 @@ Dygraph.prototype.attr_ = function(name, seriesName) {
       break;
     }
   }
-  return ret;
+
+  var computedValue = seriesName ? this.attributes_.findForSeries(name, seriesName) : this.attributes_.find(name);
+  if (ret !== computedValue) {
+    console.log("Mismatch", name, seriesName, ret, computedValue);
+  }
+
+  var USE_NEW_VALUE = true;
+  return USE_NEW_VALUE ? computedValue : ret;
 };
 
 /**
@@ -2187,7 +2204,8 @@ Dygraph.prototype.predraw_ = function() {
   // rolling averages.
   this.rolledSeries_ = [null];  // x-axis is the first series and it's special
   for (var i = 1; i < this.numColumns(); i++) {
-    var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong
+    // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too.
+    var logScale = this.attr_('logscale');
     var series = this.extractSeries_(this.rawData_, i, logScale);
     series = this.rollingAverage(series, this.rollPeriod_);
     this.rolledSeries_.push(series);
@@ -2960,6 +2978,7 @@ Dygraph.prototype.parseCSV_ = function(data) {
     // User hasn't explicitly set labels, so they're (presumably) in the CSV.
     start = 1;
     this.attrs_.labels = lines[0].split(delim);  // NOTE: _not_ user_attrs_.
+    this.attributes_.reparseSeries();
   }
   var line_no = 0;
 
@@ -3096,8 +3115,9 @@ Dygraph.prototype.parseArray_ = function(data) {
               "in the options parameter");
     this.attrs_.labels = [ "X" ];
     for (i = 1; i < data[0].length; i++) {
-      this.attrs_.labels.push("Y" + i);
+      this.attrs_.labels.push("Y" + i); // Not user_attrs_.
     }
+    this.attributes_.reparseSeries();
   } else {
     var num_labels = this.attr_("labels");
     if (num_labels.length != data[0].length) {