Merge branch 'master' of https://github.com/danvk/dygraphs into new-series-option
authorRobert Konigsberg <konigsberg@google.com>
Thu, 29 Nov 2012 20:13:14 +0000 (15:13 -0500)
committerRobert Konigsberg <konigsberg@google.com>
Thu, 29 Nov 2012 20:13:14 +0000 (15:13 -0500)
Conflicts:
Makefile
dygraph.js

auto_tests/tests/per_series.js
dygraph-options-reference.js
dygraph-options.js
dygraph.js
tests/custom-circles.html
tests/isolated-points.html
tests/per-series.html
tests/two-axes-vr.html

index 4831ded..8d81c7b 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * @fileoverview FILL THIS IN
+ * @fileoverview Tests for per-series options.
  *
  * @author danvk@google.com (Dan Vanderkam)
  */
@@ -12,7 +12,6 @@ perSeriesTestCase.prototype.setUp = function() {
 perSeriesTestCase.prototype.tearDown = function() {
 };
 
-
 perSeriesTestCase.prototype.testPerSeriesFill = function() {
   var opts = {
     width: 480,
@@ -48,3 +47,102 @@ perSeriesTestCase.prototype.testPerSeriesFill = function() {
   assertEquals([255,0,0,38], sampler.colorAtCoordinate(6.5, 0.5));
 };
 
+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 = {
+    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);
+
+  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 : {
+      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"));
+};
index 5aa7f05..9110879 100644 (file)
@@ -759,6 +759,12 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "labels": ["Data Line display"],
     "type": "array or function",
     "description": "A function (or array of functions) which plot each data series on the chart. TODO(danvk): more details! May be set per-series."
+  },
+  "series": {
+    "default": "null",
+    "labels": ["Series"],
+    "type": "Object",
+    "description": "Defines per-series options. Its keys match the y-axis label names, and the values are dictionaries themselves that contain options specific to that series. When this option is missing, it falls back on the old-style of per-series options comingled with global options."
   }
 }
 ;  // </JSON>
@@ -787,6 +793,7 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
    'Legend',
    'Overall display',
    'Rolling Averages',
+   'Series',
    'Value display/formatting',
    'Zooming',
    'Debugging',
index 9a4f6bf..f734c5d 100644 (file)
@@ -9,11 +9,11 @@
 /*
  * Interesting member variables:
  * dygraph_ - the graph.
- * global - global attributes (common among all graphs, AIUI)
+ * global_ - global attributes (common among all graphs, AIUI)
  * user - attributes set by the user
- * axes - map of options specific to the axis.
- * series - { seriesName -> { idx, yAxis, options }
- * labels - used as mapping from index to series name.
+ * axes_ - array of axis index to { series : [ series names ] , options : { axis-specific options. }
+ * series_ - { seriesName -> { idx, yAxis, options }}
+ * labels_ - used as mapping from index to series name.
  */
 
 /**
@@ -46,6 +46,39 @@ var DygraphOptions = function(dygraph) {
   this.reparseSeries();
 };
 
+/*
+ * Not optimal, but does the trick when you're only using two axes.
+ * If we move to more axes, this can just become a function.
+ */
+DygraphOptions.AXIS_STRING_MAPPINGS_ = {
+  'y' : 0,
+  'Y' : 0,
+  'y1' : 0,
+  'Y1' : 0,
+  'y2' : 1,
+  'Y2' : 1
+}
+
+DygraphOptions.axisToIndex_ = function(axis) {
+  if (typeof(axis) == "string") {
+    if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) {
+      return DygraphOptions.AXIS_STRING_MAPPINGS_[axis];
+    }
+    throw "Unknown axis : " + text;
+  }
+  if (typeof(axis) == "number") {
+    if (axis == 0 || axis == 1) {
+      return axis;
+    }
+    throw "Dygraphs only supports two y-axes, indexed from 0-1."
+  }
+  if (axis) {
+    throw "Unknown axis : " + axis;
+  }
+  // No axis specification means axis 0.
+  return 0;
+};
+
 /**
  * Reparses options that are all related to series. This typically occurs when
  * options are either updated, or source data has been made avaialble.
@@ -55,56 +88,103 @@ var DygraphOptions = function(dygraph) {
 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] = { 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] = axis;
+      this.series_[seriesName] = { idx: idx, yAxis: yAxis, options : optionsForSeries };
     }
-    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;
+  
+    // 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;
+        }
+        var yAxis = this.series_[axis].yAxis;
+        this.series_[seriesName].yAxis = yAxis;
+        this.axes_[yAxis].series.push(seriesName);
       }
-      this.series_[seriesName].yAxis = this.series_[axis].yAxis;
     }
-  }
+  } else {
+    var maxYAxis = 0;
 
-  // 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;
+    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"]);
 
-    if (axis_opts.hasOwnProperty("y")) {
-      Dygraph.update(this.axes_[0], axis_opts.y);
-    }
+      maxYAxis = Math.max(yAxis, maxYAxis);
 
-    if (axis_opts.hasOwnProperty("y2")) {
-      this.axes_[1] = this.axes_[1] || {};
-      Dygraph.update(this.axes_[1], axis_opts.y2);
+      this.series_[seriesName] = {
+        idx: idx,
+        yAxis: yAxis,
+        options : optionsForSeries };
+
+      if (!this.axes_[yAxis]) {
+        this.axes_[yAxis] =  { series : [ seriesName ], options : {} };
+      } else {
+        this.axes_[yAxis].series.push(seriesName);
+      }
     }
   }
+
+  // This doesn't support reading from the 'x' axis, only 'y' and '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"] || {});   
+  }
 };
 
 /**
@@ -139,7 +219,7 @@ DygraphOptions.prototype.getForAxis = function(name, axis) {
     axisIdx = (axis == "y2") ? 1 : 0;
   }
 
-  var axisOptions = this.axes_[axisIdx];
+  var axisOptions = this.axes_[axisIdx].options;
   if (axisOptions.hasOwnProperty(name)) {
     return axisOptions[name];
   }
@@ -177,3 +257,47 @@ DygraphOptions.prototype.getForSeries = function(name, series) {
   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;
+}
index d42bb9f..4bef1e4 100644 (file)
@@ -576,7 +576,6 @@ Dygraph.prototype.attr_ = function(name, seriesName) {
     Dygraph.OPTIONS_REFERENCE[name] = true;
   }
 // </REMOVE_FOR_COMBINED>
-
   return seriesName ? this.attributes_.getForSeries(name, seriesName) : this.attributes_.get(name);
 };
 
@@ -1140,7 +1139,7 @@ Dygraph.prototype.getPropertiesForSeries = function(series_name) {
     column: idx,
     visible: this.visibility()[idx - 1],
     color: this.colorsMap_[series_name],
-    axis: 1 + this.seriesToAxisMap_[series_name]
+    axis: 1 + this.attributes_.axisForSeries(series_name)
   };
 };
 
@@ -2423,9 +2422,8 @@ Dygraph.prototype.renderGraph_ = function(is_initial_draw) {
  * currently being displayed. This includes things like the number of axes and
  * the style of the axes. It does not include the range of each axis and its
  * tick marks.
- * This fills in this.axes_ and this.seriesToAxisMap_.
+ * This fills in this.axes_.
  * axes_ = [ { options } ]
- * seriesToAxisMap_ = { seriesName: 0, seriesName2: 1, ... }
  *   indices are into the axes_ array.
  */
 Dygraph.prototype.computeYAxes_ = function() {
@@ -2439,70 +2437,16 @@ Dygraph.prototype.computeYAxes_ = function() {
     }
   }
 
-  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);
-
-  // all options which could be applied per-axis:
-  var axisOptions = [
-    'includeZero',
-    'valueRange',
-    'labelsKMB',
-    'labelsKMG2',
-    'pixelsPerYLabel',
-    'yAxisLabelWidth',
-    'axisLabelFontSize',
-    'axisTickSize',
-    'logscale'
-  ];
-
-  // Copy global axis options over to the first axis.
-  for (i = 0; i < axisOptions.length; i++) {
-    var k = axisOptions[i];
-    v = this.attr_(k);
-    if (v) this.axes_[0][k] = v;
-  }
-
+  // this.axes_ doesn't match this.attributes_.axes_.options. It's used for
+  // data computation as well as options storage.
   // 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.axes_ = [];
+  
+  for (axis = 0; axis < this.attributes_.numAxes(); axis++) {
+    // Add a new axis, making a copy of its per-axis options.
+    opts = { g : this };
+    Dygraph.update(opts, this.attributes_.axisOptions(axis));
+    this.axes_[axis] = opts;
   }
 
   if (valueWindows !== undefined) {
@@ -2525,7 +2469,6 @@ Dygraph.prototype.computeYAxes_ = function() {
       }
     }
   }
-
 };
 
 /**
@@ -2533,13 +2476,7 @@ Dygraph.prototype.computeYAxes_ = function() {
  * @return {Number} the number of axes.
  */
 Dygraph.prototype.numAxes = function() {
-  var last_axis = 0;
-  for (var series in this.seriesToAxisMap_) {
-    if (!this.seriesToAxisMap_.hasOwnProperty(series)) continue;
-    var idx = this.seriesToAxisMap_[series];
-    if (idx > last_axis) last_axis = idx;
-  }
-  return 1 + last_axis;
+  return this.attributes_.numAxes();
 };
 
 /**
@@ -2551,7 +2488,7 @@ Dygraph.prototype.numAxes = function() {
  */
 Dygraph.prototype.axisPropertiesForSeries = function(series) {
   // TODO(danvk): handle errors.
-  return this.axes_[this.seriesToAxisMap_[series]];
+  return this.axes_[this.attributes_.axisForSeries(series)];
 };
 
 /**
@@ -2561,25 +2498,20 @@ Dygraph.prototype.axisPropertiesForSeries = function(series) {
  * 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 series;
+  var numAxes = this.attributes_.numAxes();
 
   // 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;
@@ -3363,6 +3295,8 @@ Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) {
 
   Dygraph.updateDeep(this.user_attrs_, attrs);
 
+  this.attributes_.reparseSeries();
+
   if (file) {
     this.file_ = file;
     if (!block_redraw) this.start_();
index f2ef094..0e6d62f 100644 (file)
             drawPoints : true,
             pointSize : 5,
             highlightCircleSize: 8,
-            A : {
-              drawPointCallback : Dygraph.Circles.TRIANGLE,
-              drawHighlightPointCallback : Dygraph.Circles.TRIANGLE
-            },
-            B : {
-              drawPointCallback : Dygraph.Circles.HEXAGON,
-              drawHighlightPointCallback : Dygraph.Circles.HEXAGON
+            series : {
+              A : {
+                drawPointCallback : Dygraph.Circles.TRIANGLE,
+                drawHighlightPointCallback : Dygraph.Circles.TRIANGLE
+              },
+              B : {
+                drawPointCallback : Dygraph.Circles.HEXAGON,
+                drawHighlightPointCallback : Dygraph.Circles.HEXAGON
+              }
             }
         });
 
index f507fda..1c1da06 100644 (file)
       ],
       {
         labels: ["X", "S1", "S2" ],
-        S1: {
-          drawGapEdgePoints: true
+        series: {
+          S1: {
+            drawGapEdgePoints: true
+          }
         },
         pointSize: 4,
         showRoller: true
index 98501ac..84730b1 100644 (file)
@@ -18,6 +18,8 @@
     <div id="demodiv"></div>
     <h2>Chart with per-series properties with legend.</h2>
     <div id="demodiv2"></div>
+    <h2>First graph, using old-style series specification.</h2>
+    <div id="demodiv3"></div>
     <script type="text/javascript">
       data = function() {
         var zp = function(x) { if (x < 10) return "0"+x; else return x; };
               data,
               {
                 strokeWidth: 2,
+               series : {
+                  'parabola': {
+                    strokeWidth: 0.0,
+                    drawPoints: true,
+                    pointSize: 4,
+                    highlightCircleSize: 6
+                  },
+                  'line': {
+                    strokeWidth: 1.0,
+                    drawPoints: true,
+                    pointSize: 1.5
+                  },
+                  'sine wave': {
+                    strokeWidth: 3,
+                    highlightCircleSize: 10
+                  },
+                  'sine wave2': {
+                    strokePattern: [10, 2, 5, 2],
+                    strokeWidth: 2,
+                    highlightCircleSize: 3
+                  }
+                }
+              }
+          );
+      g2 = new Dygraph(
+              document.getElementById("demodiv2"),
+              data,
+              {
+                legend: 'always',
+                strokeWidth: 2,
                 'parabola': {
-                  strokeWidth: 0.0,
+                  strokePattern: null,
                   drawPoints: true,
                   pointSize: 4,
                   highlightCircleSize: 6
                 },
                 'line': {
+                  strokePattern: Dygraph.DASHED_LINE,
                   strokeWidth: 1.0,
                   drawPoints: true,
                   pointSize: 1.5
                 },
+                'another line': {
+                  strokePattern: [25, 5]
+                },
                 'sine wave': {
+                  strokePattern: Dygraph.DOTTED_LINE,
                   strokeWidth: 3,
                   highlightCircleSize: 10
                 },
                 'sine wave2': {
-                  strokePattern: [10, 2, 5, 2],
+                  strokePattern: Dygraph.DOT_DASH_LINE,
                   strokeWidth: 2,
                   highlightCircleSize: 3
                 }
               }
           );
-      g2 = new Dygraph(
-              document.getElementById("demodiv2"),
+      g3 = new Dygraph(
+              document.getElementById("demodiv3"),
               data,
               {
-                legend: 'always',
                 strokeWidth: 2,
                 'parabola': {
-                  strokePattern: null,
+                  strokeWidth: 0.0,
                   drawPoints: true,
                   pointSize: 4,
                   highlightCircleSize: 6
                 },
                 'line': {
-                  strokePattern: Dygraph.DASHED_LINE,
                   strokeWidth: 1.0,
                   drawPoints: true,
                   pointSize: 1.5
                 },
-                'another line': {
-                  strokePattern: [25, 5]
-                },
                 'sine wave': {
-                  strokePattern: Dygraph.DOTTED_LINE,
                   strokeWidth: 3,
                   highlightCircleSize: 10
                 },
                 'sine wave2': {
-                  strokePattern: Dygraph.DOT_DASH_LINE,
+                  strokePattern: [10, 2, 5, 2],
                   strokeWidth: 2,
                   highlightCircleSize: 3
                 }
index 5814435..0646b9a 100644 (file)
           data,
           {
             labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
-            'Y3': {
-              axis: {
+            series : {
+              'Y3': {
+                axis: {
+                }
+              },
+              'Y4': {
+                axis: 'Y3'  // use the same y-axis as series Y3
               }
             },
-            'Y4': {
-              axis: 'Y3'  // use the same y-axis as series Y3
-            },
             valueRange: [40, 70],
             axes: {
               y2: {
           data,
           {
             labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
-            'Y3': {
-              axis: {
+            series : {
+              'Y3': {
+                axis: {
+                }
+              },
+              'Y4': {
+                axis: 'Y3'  // use the same y-axis as series Y3
               }
             },
-            'Y4': {
-              axis: 'Y3'  // use the same y-axis as series Y3
-            },
             axes: {
               y: {
                 valueRange: [40, 80]
           data,
           {
             labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
-            'Y3': {
-              axis: {
+            series : {
+              'Y3': {
+                axis: {
+                }
+              },
+              'Y4': {
+                axis: 'Y3'  // use the same y-axis as series Y3
               }
             },
-            'Y4': {
-              axis: 'Y3'  // use the same y-axis as series Y3
-            },
             axes: {
               y: {
                 valueRange: [40, 80]