Merge pull request #204 from sauter-hq/55eb8485b406174f15dcee0e97abc4c466de7733
authorDan Vanderkam <danvdk@gmail.com>
Fri, 8 Feb 2013 20:21:52 +0000 (12:21 -0800)
committerDan Vanderkam <danvdk@gmail.com>
Fri, 8 Feb 2013 20:21:52 +0000 (12:21 -0800)
Added support for stepPlot per-series

auto_tests/misc/local.html
auto_tests/tests/step_plot_per_series.js [new file with mode: 0644]
dygraph-canvas.js
dygraph-options-reference.js
tests/steps.html

index ea529fe..74f57e6 100644 (file)
@@ -47,6 +47,7 @@
   <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/step_plot_per_series.js"></script>
   <script type="text/javascript" src="../tests/stacked.js"></script>
   <!--
   <script type="text/javascript" src="../tests/tickers.js"></script>
diff --git a/auto_tests/tests/step_plot_per_series.js b/auto_tests/tests/step_plot_per_series.js
new file mode 100644 (file)
index 0000000..bf25948
--- /dev/null
@@ -0,0 +1,421 @@
+/**
+ * @fileoverview Test cases for the option "stepPlot" especially for the scenario where the option is not set for the whole graph but for single series.
+ *
+ * @author julian.eichstaedt@ch.sauter-bc.com (Fr. Sauter AG)
+ */
+var StepTestCase = TestCase("step_plot_per_series");
+
+StepTestCase.prototype.setUp = function() {
+  document.body.innerHTML = "<div id='graph'></div>";
+};
+
+StepTestCase.origFunc = Dygraph.getContext;
+
+StepTestCase.prototype.setUp = function() {
+  document.body.innerHTML = "<div id='graph'></div>";
+  Dygraph.getContext = function(canvas) {
+    return new Proxy(StepTestCase.origFunc(canvas));
+  };
+};
+
+StepTestCase.prototype.tearDown = function() {
+  Dygraph.getContext = StepTestCase.origFunc;
+};
+
+StepTestCase.prototype.testMixedModeStepAndLineFilled = function() {
+  var opts = {
+    width: 480,
+    height: 320,
+    drawXGrid: false,
+    drawYGrid: false,
+    drawXAxis: false,
+    drawYAxis: false,
+    errorBars: false,
+    labels: ["X", "Idle", "Used"],
+    series: {
+      Idle: {stepPlot: false},
+      Used: {stepPlot: true}
+    },
+    fillGraph: true,
+    stackedGraph: false,
+    includeZero: true
+  };
+
+  var data = [
+               [1, 70,30],
+               [2, 12,88],
+               [3, 88,12],
+               [4, 63,37],
+               [5, 35,65]
+             ];
+
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, data, opts);
+
+  htx = g.hidden_ctx_;
+
+  var attrs = {};  
+
+
+  for (var i = 0; i < data.length - 1; i++) {
+
+    var x1 = data[i][0];
+    var x2 = data[i + 1][0];
+
+    var y1 = data[i][1];
+    var y2 = data[i + 1][1];
+
+    // First series (line)
+    var xy1 = g.toDomCoords(x1, y1);
+    var xy2 = g.toDomCoords(x2, y2);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    y1 = data[i][2];
+    y2 = data[i + 1][2];
+
+    // Seconds series (step)
+    // Horizontal line
+    xy1 = g.toDomCoords(x1, y1);
+    xy2 = g.toDomCoords(x2, y1);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // Vertical line
+    xy1 = g.toDomCoords(x2, y1);
+    xy2 = g.toDomCoords(x2, y2);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+  }
+};
+
+StepTestCase.prototype.testMixedModeStepAndLineStackedAndFilled = function() {
+  var opts = {
+    width: 480,
+    height: 320,
+    drawXGrid: false,
+    drawYGrid: false,
+    drawXAxis: false,
+    drawYAxis: false,
+    errorBars: false,
+    labels: ["X", "Idle", "Used", "NotUsed", "Active"],
+    series: {
+      Idle: {stepPlot: false},
+      Used: {stepPlot: true},
+      NotUsed: {stepPlot: false},
+      Active: {stepPlot: true}
+    },
+    fillGraph: true,
+    stackedGraph: true,
+    includeZero: true
+  };
+
+  var data = [
+               [1, 60,30,5,5],
+               [2, 12,73,5,10],
+               [3, 38,12,30,20],
+               [4, 50,17,23,10],
+               [5, 35,25,35,5]
+             ];
+
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, data, opts);
+
+  htx = g.hidden_ctx_;
+
+  var attrs = {};  
+
+
+  for (var i = 0; i < data.length - 1; i++) {
+    
+    var x1 = data[i][0];
+    var x2 = data[i + 1][0];
+    var y1base = 0;
+    var y2base = 0;
+    var y1 = data[i][4];
+    var y2 = data[i + 1][4];
+
+    // Fourth series (step)
+    // Test lines
+    // Horizontal line
+    var xy1 = g.toDomCoords(x1, y1);
+    var xy2 = g.toDomCoords(x2, y1);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // Vertical line
+    xy1 = g.toDomCoords(x2, y1);
+    xy2 = g.toDomCoords(x2, y2);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // Test edges of areas (also drawn by dygraphs as lines)
+    xy1 = g.toDomCoords(x1, y1);
+    xy2 = g.toDomCoords(x2, y1);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x2, y2base);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x1, y1base);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // The last edge can not be tested via assertLineDrawn since it wasn't drawn as a line but via clossePath.
+    // But a rectangle is completely tested with three of its four edges.
+    
+    y1base = y1;
+    y2base = y1;
+    y1 += data[i][3];
+    y2 += data[i + 1][3];
+    
+    // Third series (line)
+    // Test lines
+    xy1 = g.toDomCoords(x1, y1);
+    xy2 = g.toDomCoords(x2, y2);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // Test edges of areas (also drawn by dygraphs as lines)
+    xy1 = g.toDomCoords(x1, y1);
+    xy2 = g.toDomCoords(x2, y2);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x2, y2base);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x1, y1base);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // The last edge can not be tested via assertLineDrawn since it wasn't drawn as a line but via clossePath.
+    // But a rectangle is completely tested with three of its four edges.
+    
+    y1base = y1;
+    y2base = y2;
+    y1 += data[i][2];
+    y2 += data[i + 1][2];
+
+    // Second series (step)
+    // Test lines
+    // Horizontal line
+    xy1 = g.toDomCoords(x1, y1);
+    xy2 = g.toDomCoords(x2, y1);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // Vertical line
+    xy1 = g.toDomCoords(x2, y1);
+    xy2 = g.toDomCoords(x2, y2);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // Test edges of areas (also drawn by dygraphs as lines)
+    xy1 = g.toDomCoords(x1, y1);
+    xy2 = g.toDomCoords(x2, y1);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x2, y2base);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x1, y1base);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // The last edge can not be tested via assertLineDrawn since it wasn't drawn as a line but via clossePath.
+    // But a rectangle is completely tested with three of its four edges.
+    
+    y1base = y1;
+    y2base = y1;
+    y1 += data[i][1];
+    y2 += data[i + 1][1];
+
+    // First series (line)
+    // Test lines
+    xy1 = g.toDomCoords(x1, y1);
+    xy2 = g.toDomCoords(x2, y2);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // Test edges of areas (also drawn by dygraphs as lines)
+    xy1 = g.toDomCoords(x1, y1);
+    xy2 = g.toDomCoords(x2, y2);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x2, y2base);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x1, y1base);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // The last edge can not be tested via assertLineDrawn since it wasn't drawn as a line but via clossePath.
+    // But a rectangle is completely tested with three of its four edges.
+  }
+};
+
+StepTestCase.prototype.testMixedModeStepAndLineErrorBars = function() {
+  var opts = {
+    width: 480,
+    height: 320,
+    drawXGrid: false,
+    drawYGrid: false,
+    drawXAxis: false,
+    drawYAxis: false,
+    errorBars: true,
+    sigma: 1,
+    labels: ["X", "Data1", "Data2"],
+    series: {
+      Data1: {stepPlot: true}, 
+      Data2: {stepPlot: false} 
+    }
+  };
+  var data = [
+               [1, [75, 2], [50, 3]],
+               [2, [70, 5], [90, 4]],
+               [3, [80, 7], [112, 5]],
+               [4, [55, 3], [100, 2]],
+               [5, [69, 4], [85, 6]]
+             ];
+
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, data, opts);
+
+  htx = g.hidden_ctx_;
+
+  var attrs = {};  
+
+  // Test first series (step)
+  for (var i = 0; i < data.length - 1; i++) {
+    var x1 = data[i][0];
+    var x2 = data[i + 1][0];
+    
+    var y1_middle = data[i][1][0];
+    var y2_middle = data[i + 1][1][0];
+    
+    var y1_top = y1_middle + data[i][1][1];
+    var y2_top = y2_middle + data[i + 1][1][1];
+    
+    var y1_bottom = y1_middle - data[i][1][1];
+    var y2_bottom = y2_middle - data[i + 1][1][1];
+    // Bottom line
+    var xy1 = g.toDomCoords(x1, y1_bottom);
+    var xy2 = g.toDomCoords(x2, y1_bottom);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // Top line
+    xy1 = g.toDomCoords(x1, y1_top);
+    xy2 = g.toDomCoords(x2, y1_top);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // Middle line
+    xy1 = g.toDomCoords(x1, y1_middle);
+    xy2 = g.toDomCoords(x2, y1_middle);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    
+    // Test edges of error bar areas(also drawn by dygraphs as lines)
+    xy1 = g.toDomCoords(x1, y1_top);
+    xy2 = g.toDomCoords(x2, y1_top);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x2, y1_bottom);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x1, y1_bottom);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // The last edge can not be tested via assertLineDrawn since it wasn't drawn as a line but via clossePath.
+    // But a rectangle is completely tested with three of its four edges.
+  }
+
+  // Test second series (line)  
+  for (var i = 0; i < data.length - 1; i++) {
+    // bottom line
+    var xy1 = g.toDomCoords(data[i][0], (data[i][2][0] - data[i][2][1]));
+    var xy2 = g.toDomCoords(data[i + 1][0], (data[i + 1][2][0] - data[i + 1][2][1]));
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // top line
+    xy1 = g.toDomCoords(data[i][0], data[i][2][0] + data[i][2][1]);
+    xy2 = g.toDomCoords(data[i + 1][0], data[i + 1][2][0] + data[i + 1][2][1]);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // middle line
+    xy1 = g.toDomCoords(data[i][0], data[i][2][0]);
+    xy2 = g.toDomCoords(data[i + 1][0], data[i + 1][2][0]);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+  }
+
+};
+
+StepTestCase.prototype.testMixedModeStepAndLineCustomBars = function() {
+  var opts = {
+    width: 480,
+    height: 320,
+    drawXGrid: false,
+    drawYGrid: false,
+    drawXAxis: false,
+    drawYAxis: false,
+    customBars: true,
+       labels: ["X", "Data1", "Data2"],
+    series: {
+      Data1: {stepPlot: true}, 
+      Data2: {stepPlot: false} 
+    }
+  };
+  var data = [
+               [1, [73, 75, 78], [50, 55, 70]],
+               [2, [65, 70, 75], [83, 91, 99]],
+               [3, [75, 85, 90], [98, 107, 117]],
+               [4, [55, 58, 61], [93, 102, 105]],
+               [5, [69, 73, 85], [80, 85, 87]]
+             ];
+
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, data, opts);
+
+  htx = g.hidden_ctx_;
+
+  var attrs = {};  
+
+  // Test first series (step)
+  for (var i = 0; i < data.length - 1; i++) {
+
+    var x1 = data[i][0];
+    var x2 = data[i + 1][0];
+    
+    var y1_middle = data[i][1][1];
+    var y2_middle = data[i + 1][1][1];
+    
+    var y1_top = data[i][1][2];
+    var y2_top = data[i + 1][1][2];
+    
+    var y1_bottom = data[i][1][0];
+    var y2_bottom = data[i + 1][1][0];
+    
+    // Bottom line
+    var xy1 = g.toDomCoords(x1, y1_bottom);
+    var xy2 = g.toDomCoords(x2, y1_bottom);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    
+    // Top line
+    xy1 = g.toDomCoords(x1, y1_top);
+    xy2 = g.toDomCoords(x2, y1_top);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    
+    // Middle line
+    xy1 = g.toDomCoords(x1, y1_middle);
+    xy2 = g.toDomCoords(x2, y1_middle);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    
+    // Test edges of custom bar areas(also drawn by dygraphs as lines)
+    xy1 = g.toDomCoords(x1, y1_top);
+    xy2 = g.toDomCoords(x2, y1_top);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x2, y1_bottom);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    xy1 = xy2;
+    xy2 = g.toDomCoords(x1, y1_bottom);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+    // The last edge can not be tested via assertLineDrawn since it wasn't drawn as a line but via clossePath.
+    // But a rectangle is completely tested with three of its four edges.
+  }
+  
+  // Test second series (line)
+  for (var i = 0; i < data.length - 1; i++) {
+    // Bottom line
+    var xy1 = g.toDomCoords(data[i][0], data[i][2][0]);
+    var xy2 = g.toDomCoords(data[i + 1][0], data[i + 1][2][0]);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // Top line
+    xy1 = g.toDomCoords(data[i][0], data[i][2][2]);
+    xy2 = g.toDomCoords(data[i + 1][0], data[i + 1][2][2]);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+
+    // Middle line
+    xy1 = g.toDomCoords(data[i][0], data[i][2][1]);
+    xy2 = g.toDomCoords(data[i + 1][0], data[i + 1][2][1]);
+    CanvasAssertions.assertLineDrawn(htx, xy1, xy2, attrs);
+  }
+};
index 89d28b4..fa0dd80 100644 (file)
@@ -261,7 +261,8 @@ DygraphCanvasRenderer._drawStyledLine = function(e,
     drawPointCallback, pointSize) {
   var g = e.dygraph;
   // TODO(konigsberg): Compute attributes outside this method call.
-  var stepPlot = g.getOption("stepPlot");  // TODO(danvk): per-series
+  var stepPlot = g.getOption("stepPlot", e.setName);
+
   if (!Dygraph.isArrayLike(strokePattern)) {
     strokePattern = null;
   }
@@ -591,7 +592,7 @@ DygraphCanvasRenderer._errorPlotter = function(e) {
   var ctx = e.drawingContext;
   var color = e.color;
   var fillAlpha = g.getOption('fillAlpha', setName);
-  var stepPlot = g.getOption('stepPlot');  // TODO(danvk): per-series
+  var stepPlot = g.getOption("stepPlot", setName);
   var points = e.points;
 
   var iter = Dygraph.createIterator(points, 0, points.length,
@@ -691,18 +692,19 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
   var setCount = sets.length;
 
   var fillAlpha = g.getOption('fillAlpha');
-  var stepPlot = g.getOption('stepPlot');
   var stackedGraph = g.getOption("stackedGraph");
   var colors = g.getColors();
 
   var baseline = {};  // for stacked graphs: baseline for filling
   var currBaseline;
+  var prevStepPlot;  // for different line drawing modes (line/step) per series
 
   // process sets in reverse order (needed for stacked graphs)
   for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
     var setName = setNames[setIdx];
     if (!g.getOption('fillGraph', setName)) continue;
-
+    
+    var stepPlot = g.getOption('stepPlot', setName);
     var color = colors[setIdx];
     var axis = g.axisPropertiesForSeries(setName);
     var axisY = 1.0 + axis.minyval * axis.yscale;
@@ -737,7 +739,7 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
         if (currBaseline === undefined) {
           lastY = axisY;
         } else {
-          if(stepPlot) {
+          if(prevStepPlot) {
             lastY = currBaseline[0];
           } else {
             lastY = currBaseline;
@@ -762,17 +764,18 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
       }
       if (!isNaN(prevX)) {
         ctx.moveTo(prevX, prevYs[0]);
-
+        
+        // Move to top fill point
         if (stepPlot) {
           ctx.lineTo(point.canvasx, prevYs[0]);
-          if(currBaseline) {
-            // Draw to the bottom of the baseline
-            ctx.lineTo(point.canvasx, currBaseline[1]);
-          } else {
-            ctx.lineTo(point.canvasx, newYs[1]);
-          }
         } else {
           ctx.lineTo(point.canvasx, newYs[0]);
+        }
+        // Move to bottom fill point
+        if (prevStepPlot && currBaseline) {
+          // Draw to the bottom of the baseline
+          ctx.lineTo(point.canvasx, currBaseline[1]);
+        } else {
           ctx.lineTo(point.canvasx, newYs[1]);
         }
 
@@ -782,6 +785,7 @@ DygraphCanvasRenderer._fillPlotter = function(e) {
       prevYs = newYs;
       prevX = point.canvasx;
     }
+    prevStepPlot = stepPlot;
     ctx.fill();
   }
 };
index a3392d9..9d4a485 100644 (file)
@@ -469,7 +469,7 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "default": "false",
     "labels": ["Data Line display"],
     "type": "boolean",
-    "description": "When set, display the graph as a step plot instead of a line plot."
+    "description": "When set, display the graph as a step plot instead of a line plot. This option may either be set for the whole graph or for single series."
   },
   "labelsKMB": {
     "default": "false",
index 42b7362..8c58811 100644 (file)
                           includeZero: true
                        });
     </script>
+       
+       <p>9: Mixed mode - step and line:</p>
+    <div id="graphdiv9"></div>
+    <script type="text/javascript">
+      g8 = new Dygraph(document.getElementById("graphdiv9"),
+                      "Date,Idle,Used\n" +
+                      "2008-05-07,70,30\n" +
+                      "2008-05-08,12,88\n" +
+                      "2008-05-09,88,12\n" +
+                      "2008-05-10,63,37\n" +
+                      "2008-05-11,35,65\n",
+                      {
+                               series: {
+                                                 Idle: {stepPlot: true},
+                                                 Used: {stepPlot: false}
+                                               },
+                                                 
+                        fillGraph: false,
+                        stackedGraph: false,
+                        includeZero: true
+                      });
+    </script>
+       
+       
+       <p>10: Mixed mode - step and line (filled):</p>
+    <div id="graphdiv10"></div>
+    <script type="text/javascript">
+      g8 = new Dygraph(document.getElementById("graphdiv10"),
+                      "Date,Idle,Used\n" +
+                      "2008-05-07,70,30\n" +
+                      "2008-05-08,12,88\n" +
+                      "2008-05-09,88,12\n" +
+                      "2008-05-10,63,37\n" +
+                      "2008-05-11,35,65\n",
+                      {
+                               series: {
+                                                 Idle: {stepPlot: false},
+                                                 Used: {stepPlot: true}
+                                               },
+                                                 
+                        fillGraph: true,
+                        stackedGraph: false,
+                        includeZero: true
+                      });
+    </script>
+
+       <p>11: Mixed mode - step and line (stacked and filled):</p>
+    <div id="graphdiv11"></div>
+    <script type="text/javascript">
+      g8 = new Dygraph(document.getElementById("graphdiv11"),
+                      "Date,Idle,Used,NotUsed,Active\n" +
+                      "2008-05-07,60,30,5,5\n" +
+                      "2008-05-08,12,73,5,10\n" +
+                      "2008-05-09,38,12,30,20\n" +
+                      "2008-05-10,50,17,23,10\n" +
+                      "2008-05-11,35,25,35,5\n",
+                      {
+                               series: {
+                                                 Idle: {stepPlot: false},
+                                                 Used: {stepPlot: true},
+                                                 NotUsed: {stepPlot: false},
+                                                 Active: {stepPlot: true}                                                   
+                        },
+                        fillGraph: true,
+                        stackedGraph: true,
+                        includeZero: true
+                      });
+    </script>
+       
+       <p>12: Mixed mode - step and line (errorbars):</p>
+    <div id="graphdiv12"></div>
+    <script type="text/javascript">
+      g8 = new Dygraph(document.getElementById("graphdiv12"),
+        [
+          [1, [75, 2], [50, 3]],
+          [2, [70, 5], [90, 4]],
+          [3, [80, 7], [112, 5]],
+          [4, [55, 3], [100, 2]],
+          [5, [69, 4], [85, 6]]
+        ],     
+        {                 
+          errorBars: true,
+          labels: ["X", "Data1", "Data2"],
+                 series: {
+                   Data1: {stepPlot: true},    
+                   Data2: {stepPlot: false} 
+                 }
+        }
+         );
+    </script>
+       
+       <p>13: Mixed mode - step and line (custombars):</p>
+    <div id="graphdiv13"></div>
+    <script type="text/javascript">
+      g8 = new Dygraph(document.getElementById("graphdiv13"),
+        [
+          [1, [73, 75, 78], [50, 55, 70]],
+          [2, [65, 70, 75], [83, 91, 99]],
+          [3, [75, 85, 90], [98, 107, 117]],
+          [4, [55, 58, 61], [93, 102, 105]],
+          [5, [69, 73, 85], [80, 85, 87]]
+        ],     
+        {
+          customBars: true,
+          labels: ["X", "Data1", "Data2"],
+                 series: {
+                   Data1: {stepPlot: true},    
+                   Data2: {stepPlot: false} 
+                 }
+        }
+         );
+    </script>
 
   </body>
 </html>