Merge commit '9f890c23ad80924d0a30f3a14f8680b7c2d6318e' into fix-for-issue-451
authorDavid Eberlein <david.eberlein@ch.sauter-bc.com>
Thu, 2 May 2013 08:50:24 +0000 (10:50 +0200)
committerDavid Eberlein <david.eberlein@ch.sauter-bc.com>
Thu, 2 May 2013 08:50:24 +0000 (10:50 +0200)
Conflicts:
dygraph-layout.js

17 files changed:
auto_tests/tests/missing_points.js
auto_tests/tests/range_selector.js
closure-todo.txt
docs/index.html
dygraph-canvas.js
dygraph-externs.js
dygraph-options-reference.js
dygraph-options.js
dygraph-tickers.js
dygraph-utils.js
dygraph.js
plugins/axes.js
plugins/range-selector.js
test.sh
tests/missing-data.html
tests/range-selector.html
tests/two-axes.html

index 68a5024..3c52dd0 100644 (file)
@@ -50,7 +50,7 @@ MissingPointsTestCase.prototype.testSeparatedPointsDontDraw = function() {
   var htx = g.hidden_ctx_;
   assertEquals(2, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
   assertEquals(0, CanvasAssertions.numLinesDrawn(htx, '#0000ff'));
-}
+};
 
 MissingPointsTestCase.prototype.testSeparatedPointsDontDraw_expanded = function() {
   var graph = document.getElementById("graph");
@@ -69,7 +69,7 @@ MissingPointsTestCase.prototype.testSeparatedPointsDontDraw_expanded = function(
       { strokeStyle: '#0000ff', });
   CanvasAssertions.assertLineDrawn(htx, [370, 87], [475, 25],
       { strokeStyle: '#0000ff', });
-}
+};
 
 MissingPointsTestCase.prototype.testSeparatedPointsDontDraw_expanded_connected = function() {
   var graph = document.getElementById("graph");
@@ -88,7 +88,7 @@ MissingPointsTestCase.prototype.testSeparatedPointsDontDraw_expanded_connected =
   CanvasAssertions.assertConsecutiveLinesDrawn(htx, 
       [[56, 275], [161, 212], [370, 87], [475, 25]],
       { strokeStyle: '#0000ff' });
-}
+};
 
 /**
  * At the time of writing this test, the blue series is only points, and not lines.
@@ -122,7 +122,7 @@ MissingPointsTestCase.prototype.testConnectSeparatedPoints = function() {
   CanvasAssertions.assertConsecutiveLinesDrawn(htx, 
       [[140, 275], [307, 125], [475, 225]],
       { strokeStyle: '#ff0000' });
-}
+};
 
 /**
  * At the time of writing this test, the blue series is only points, and not lines.
@@ -158,7 +158,7 @@ MissingPointsTestCase.prototype.testConnectSeparatedPointsWithNan = function() {
   CanvasAssertions.assertConsecutiveLinesDrawn(htx, 
       [[56, 244], [149, 181], [242, 118]],
       { strokeStyle: '#0000ff' });
-}
+};
 
 /* These lines contain awesome powa!  
   var lines = CanvasAssertions.getLinesDrawn(htx, {strokeStyle: "#0000ff"});
@@ -167,3 +167,143 @@ MissingPointsTestCase.prototype.testConnectSeparatedPointsWithNan = function() {
     console.log(line[0].args, line[1].args, line[0].properties.strokeStyle);
   }
 */
+
+MissingPointsTestCase.prototype.testErrorBarsWithMissingPoints = function() {
+  var data = [
+              [1, [2,1]],
+              [2, [3,1]],
+              [3, null],
+              [4, [5,1]],
+              [5, [4,1]],
+              [6, [null,null]],
+             ];
+  var g = new Dygraph(
+    document.getElementById("graph"),
+    data,
+    {
+      errorBars: true,
+      colors: ['red']
+    }
+  );
+
+  var htx = g.hidden_ctx_;
+
+  assertEquals(8, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+
+  var p0 = g.toDomCoords(data[0][0], data[0][1][0]);
+  var p1 = g.toDomCoords(data[1][0], data[1][1][0]);
+  var p2 = g.toDomCoords(data[3][0], data[3][1][0]);
+  var p3 = g.toDomCoords(data[4][0], data[4][1][0]);
+  CanvasAssertions.assertConsecutiveLinesDrawn(htx, 
+      [p0, p1], { strokeStyle: '#ff0000' });
+  CanvasAssertions.assertConsecutiveLinesDrawn(htx, 
+      [p2, p3], { strokeStyle: '#ff0000' });
+};
+
+MissingPointsTestCase.prototype.testErrorBarsWithMissingPointsConnected = function() {
+  var data = [
+              [1, [null,1]],
+              [2, [2,1]],
+              [3, null],
+              [4, [5,1]],
+              [5, [null,null]],
+              [6, [3,1]]
+             ];
+  var g = new Dygraph(
+    document.getElementById("graph"),
+    data,
+    {
+      connectSeparatedPoints: true,
+      drawPoints: true,
+      errorBars: true,
+      colors: ['red']
+    }
+  );
+
+  var htx = g.hidden_ctx_;
+  
+  assertEquals(8, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+
+  var p1 = g.toDomCoords(data[1][0], data[1][1][0]);
+  var p2 = g.toDomCoords(data[3][0], data[3][1][0]);
+  var p3 = g.toDomCoords(data[5][0], data[5][1][0]);
+  CanvasAssertions.assertConsecutiveLinesDrawn(htx, 
+      [p1, p2, p3],
+      { strokeStyle: '#ff0000' });
+};
+MissingPointsTestCase.prototype.testCustomBarsWithMissingPoints = function() {
+  var data = [
+              [1, [1,2,3]],
+              [2, [2,3,4]],
+              [3, null],
+              [4, [4,5,6]],
+              [5, [3,4,5]],
+              [6, [null,null,null]],
+              [7, [2,3,4]],
+              [8, [1,2,3]],
+              [9, NaN],
+              [10, [2,3,4]],
+              [11, [3,4,5]],
+              [12, [NaN,NaN,NaN]]
+             ];
+  var g = new Dygraph(
+    document.getElementById("graph"),
+    data,
+    {
+      customBars: true,
+      colors: ['red']
+    }
+  );
+
+  var htx = g.hidden_ctx_;
+
+  assertEquals(16, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+
+  var p0 = g.toDomCoords(data[0][0], data[0][1][1]);
+  var p1 = g.toDomCoords(data[1][0], data[1][1][1]);
+  CanvasAssertions.assertLineDrawn(htx, p0, p1, { strokeStyle: '#ff0000' });
+  
+  p0 = g.toDomCoords(data[3][0], data[3][1][1]);
+  p1 = g.toDomCoords(data[4][0], data[4][1][1]);
+  CanvasAssertions.assertLineDrawn(htx, p0, p1, { strokeStyle: '#ff0000' });
+
+  p0 = g.toDomCoords(data[6][0], data[6][1][1]);
+  p1 = g.toDomCoords(data[7][0], data[7][1][1]);
+  CanvasAssertions.assertLineDrawn(htx, p0, p1, { strokeStyle: '#ff0000' });;
+
+  p0 = g.toDomCoords(data[9][0], data[9][1][1]);
+  p1 = g.toDomCoords(data[10][0], data[10][1][1]);
+  CanvasAssertions.assertLineDrawn(htx, p0, p1, { strokeStyle: '#ff0000' });
+};
+
+MissingPointsTestCase.prototype.testCustomBarsWithMissingPointsConnected = function() {
+  var data = [
+              [1, [1,null,1]],
+              [2, [1,2,3]],
+              [3, null],
+              [4, [4,5,6]],
+              [5, [null,null,null]],
+              [6, [2,3,4]]
+             ];
+  var g = new Dygraph(
+    document.getElementById("graph"),
+    data,
+    {
+      connectSeparatedPoints: true,
+      drawPoints: true,
+      customBars: true,
+      colors: ['red']
+    }
+  );
+
+  var htx = g.hidden_ctx_;
+  
+  assertEquals(8, CanvasAssertions.numLinesDrawn(htx, '#ff0000'));
+
+  var p1 = g.toDomCoords(data[1][0], data[1][1][1]);
+  var p2 = g.toDomCoords(data[3][0], data[3][1][1]);
+  var p3 = g.toDomCoords(data[5][0], data[5][1][1]);
+  CanvasAssertions.assertConsecutiveLinesDrawn(htx, 
+      [p1, p2, p3],
+      { strokeStyle: '#ff0000' });
+};
index a27cad7..34e3574 100644 (file)
@@ -318,6 +318,32 @@ RangeSelectorTestCase.prototype.testRangeSelectorInteraction = function() {
   assert(newXRange[1]+'<'+xRange[1], newXRange[1] < xRange[1]);
 };
 
+
+RangeSelectorTestCase.prototype.testRangeSelectorPositionIfXAxisNotDrawn = function() {
+  var opts = {
+    width: 480,
+    height: 100,
+    xAxisHeight: 30,
+    drawXAxis: false,
+    showRangeSelector: true,
+    rangeSelectorHeight: 30
+  };
+  var data = [
+               [0, 1],
+               [10, 1]
+             ];
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, data, opts);
+  
+  //assert, that the range selector is at top position 70 since the 30px of the
+  // xAxis shouldn't be reserved since it isn't drawn.
+  this.assertGraphExistence(g, graph);
+  var bgcanvas = graph.getElementsByClassName('dygraph-rangesel-bgcanvas')[0];
+  assertEquals("Range selector is not at the expected position.","70px", bgcanvas.style.top);
+  var fgcanvas = graph.getElementsByClassName('dygraph-rangesel-fgcanvas')[0];
+  assertEquals("Range selector is not at the expected position.","70px", fgcanvas.style.top);
+};
+
 RangeSelectorTestCase.prototype.assertGraphExistence = function(g, graph) {
   assertNotNull(g);
   var zoomhandles = graph.getElementsByClassName('dygraph-rangesel-zoomhandle');
index c9d42b7..5c61495 100644 (file)
@@ -12,7 +12,7 @@ Core:
 - dygraph-canvas.js
 - dygraph-interaction-model.js
 - dygraph-layout.js
-- dygraph-options.js
+x dygraph-options.js
 - dygraph-range-selector.js
 - dygraph.js
 x dygraph-gviz.js
@@ -32,6 +32,6 @@ Plugins:
 
 Here's a command that can be used to build dygraphs using the closure
 compiler:
-java -jar ../../closure-compiler-read-only/build/compiler.jar --js=dygraph-utils.js --js=dashed-canvas.js --js=dygraph-options-reference.js --js=dygraph-tickers.js --js=dygraph-gviz.js --js_output_file=/tmp/out.js --compilation_level ADVANCED_OPTIMIZATIONS --warning_level VERBOSE --externs dygraph-externs.js
+java -jar ../../closure-compiler-read-only/build/compiler.jar --js=dygraph-utils.js --js=dashed-canvas.js --js=dygraph-options-reference.js --js=dygraph-tickers.js --js=dygraph-gviz.js --js=dygraph-options.js --js_output_file=/tmp/out.js --compilation_level ADVANCED_OPTIMIZATIONS --warning_level VERBOSE --externs dygraph-externs.js
 
 As each file is closurized, it can be added as a "--js" parameter.
index 7c1dcdc..cc3d1be 100644 (file)
       <h1>dygraphs JavaScript Visualization Library</h1>
 
       <p><a href="http://github.com/danvk/dygraphs">http://github.com/danvk/dygraphs</a></p>
-      <p>See <a href="http://blog.dygraphs.com/">blog</a>, <a href="http://groups.google.com/group/dygraphs-users">mailing list</a>, <a href="http://github.com/danvk/dygraphs/downloads/">downloads</a>, <a href="tests/">demos</a> and <a href="http://code.google.com/p/dygraphs/issues/">open issues</a></p>
+      <p>For help, <a
+        href="http://stackoverflow.com/questions/ask?tags=dygraphs+javascript">ask
+        a question</a> on StackOverflow. You may also be interested in the <a
+        href="http://blog.dygraphs.com/">blog</a>, <a
+        href="http://groups.google.com/group/dygraphs-users">mailing list</a>, <a
+        href="tests/">demos</a> and <a
+        href="http://code.google.com/p/dygraphs/issues/">open issues</a>.</p>
 
       <p>dygraphs is an open source JavaScript library that produces produces interactive, zoomable charts of time series. It is designed to display dense data sets and enable users to explore and interpret them.</p>
 
index 90ef027..ef6cf1f 100644 (file)
@@ -39,8 +39,8 @@
  * The chart canvas has already been created by the Dygraph object. The
  * renderer simply gets a drawing context.
  *
- * @param {Dyraph} dygraph The chart to which this renderer belongs.
- * @param {Canvas} element The &lt;canvas&gt; DOM element on which to draw.
+ * @param {Dygraph} dygraph The chart to which this renderer belongs.
+ * @param {HTMLCanvasElement} element The &lt;canvas&gt; DOM element on which to draw.
  * @param {CanvasRenderingContext2D} elementContext The drawing context.
  * @param {DygraphLayout} layout The chart's DygraphLayout object.
  *
@@ -58,6 +58,7 @@ var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) {
   this.width = this.element.width;
 
   // --- check whether everything is ok before we return
+  // NOTE(konigsberg): isIE is never defined in this object. Bug of some sort.
   if (!this.isIE && !(DygraphCanvasRenderer.isSupported(this.element)))
       throw "Canvas is not supported.";
 
@@ -433,14 +434,16 @@ DygraphCanvasRenderer.prototype._updatePoints = function() {
 
 /**
  * Add canvas Actually draw the lines chart, including error bars.
- * If opt_seriesName is specified, only that series will be drawn.
- * (This is used for expedited redrawing with highlightSeriesOpts)
- * Lines are typically drawn in the non-interactive dygraph canvas. If opt_ctx
- * is specified, they can be drawn elsewhere.
  *
  * This function can only be called if DygraphLayout's points array has been
  * updated with canvas{x,y} attributes, i.e. by
  * DygraphCanvasRenderer._updatePoints.
+ *
+ * @param {string=} opt_seriesName when specified, only that series will
+ *     be drawn. (This is used for expedited redrawing with highlightSeriesOpts)
+ * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing
+ *     context.  However, lines are typically drawn on the object's
+ *     elementContext.
  * @private
  */
 DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ctx) {
index 6e487fd..ed5de4c 100644 (file)
@@ -72,13 +72,22 @@ function DygraphLayout() {}
  */
 DygraphLayout.prototype.datasets;
 
+// TODO: DygraphOptions should not reach inside Dygraph private data like this.
+/** @type {Object} */
+Dygraph.prototype.attrs_;
+/** @type {Object} */
+Dygraph.prototype.user_attrs_;
+
 /**
  * @type {DygraphLayout}
  */
 Dygraph.prototype.layout_;
 
+/** @type {function(): string} */
+Dygraph.prototype.getHighlightSeries;
+
 /** @type {Array.<{elem:Element,type:string,fn:function(!Event):(boolean|undefined|null)}>} */
 Dygraph.prototype.registeredEvents_;
 
-/** @type {Object} */
+/** @type {{axes: Object}} */
 Dygraph.DEFAULT_ATTRS;
index a4e262c..9010385 100644 (file)
@@ -60,7 +60,7 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
       [ "cx" , "center x coordinate" ],
       [ "cy" , "center y coordinate" ],
       [ "color" , "series color" ],
-      [ "pointSize" , "the radius of the image." ]
+      [ "pointSize" , "the radius of the image." ],
       [ "idx" , "the row-index of the point in the data."]
     ],
     "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy).  Also see <a href='#drawHighlightPointCallback'>drawHighlightPointCallback</a>"
@@ -130,7 +130,7 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
       [ "cx" , "center x coordinate" ],
       [ "cy" , "center y coordinate" ],
       [ "color" , "series color" ],
-      [ "pointSize" , "the radius of the image." ]
+      [ "pointSize" , "the radius of the image." ],
       [ "idx" , "the row-index of the point in the data."]
     ],
     "description": "Draw a custom item when a point is highlighted.  Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see <a href='#drawPointCallback'>drawPointCallback</a>"
index 6889a09..0f90086 100644 (file)
@@ -17,14 +17,10 @@ var DygraphOptions = (function() {
 "use strict";
 
 /*
- * Interesting member variables:
- * dygraph_ - the graph.
+ * Interesting member variables: (REMOVING THIS LIST AS I CLOSURIZE)
  * global_ - global attributes (common among all graphs, AIUI)
  * user - attributes set by the user
- * yAxes_ - array of axis index to { series : [ series names ] , options : { axis-specific options. }
- * xAxis_ - { options : { axis-specific options. }
  * series_ - { seriesName -> { idx, yAxis, options }}
- * labels_ - used as mapping from index to series name.
  */
 
 /**
@@ -34,12 +30,28 @@ var DygraphOptions = (function() {
  * if labels are not yet available, since those drive details of the per-series
  * and per-axis options.
  *
- * @param {Dyraph} dygraph The chart to which these options belong.
+ * @param {Dygraph} dygraph The chart to which these options belong.
  * @constructor
  */
 var DygraphOptions = function(dygraph) {
+  /**
+   * The dygraph.
+   * @type {!Dygraph}
+   */
   this.dygraph_ = dygraph;
+
+  /**
+   * Array of axis index to { series : [ series names ] , options : { axis-specific options. }
+   * @type {Array.<{series : Array.<string>, options : Object}>} @private
+   */
   this.yAxes_ = [];
+
+  /**
+   * Contains x-axis specific options, which are stored in the options key.
+   * This matches the yAxes_ object structure (by being a dictionary with an
+   * options element) allowing for shared code.
+   * @type {options: Object} @private
+   */
   this.xAxis_ = {};
   this.series_ = {};
 
@@ -47,13 +59,22 @@ var DygraphOptions = function(dygraph) {
   this.global_ = this.dygraph_.attrs_;
   this.user_ = this.dygraph_.user_attrs_ || {};
 
+  /**
+   * A list of series in columnar order.
+   * @type {Array.<string>}
+   */
+  this.labels_ = [];
+
   this.highlightSeries_ = this.get("highlightSeriesOpts") || {};
   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.
+ *
+ * @type {Object.<number>}
+ * @private
  */
 DygraphOptions.AXIS_STRING_MAPPINGS_ = {
   'y' : 0,
@@ -64,6 +85,10 @@ DygraphOptions.AXIS_STRING_MAPPINGS_ = {
   'Y2' : 1
 };
 
+/**
+ * @param {string|number} axis
+ * @private
+ */
 DygraphOptions.axisToIndex_ = function(axis) {
   if (typeof(axis) == "string") {
     if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) {
@@ -77,10 +102,6 @@ DygraphOptions.axisToIndex_ = function(axis) {
     }
     throw "Dygraphs only supports two y-axes, indexed from 0-1.";
   }
-  if (typeof(axis) == "object") {
-    throw "Using objects for axis specification " +
-        "is not supported inside the 'series' option.";
-  }
   if (axis) {
     throw "Unknown axis : " + axis;
   }
@@ -162,9 +183,9 @@ DygraphOptions.prototype.reparseSeries = function() {
 
       if (typeof(axis) == 'string') {
         if (!this.series_.hasOwnProperty(axis)) {
-          this.dygraph_.error("Series " + seriesName + " wants to share a y-axis with " +
+          Dygraph.error("Series " + seriesName + " wants to share a y-axis with " +
                      "series " + axis + ", which does not define its own axis.");
-          return null;
+          return;
         }
         var yAxis = this.series_[axis].yAxis;
         this.series_[seriesName].yAxis = yAxis;
@@ -293,7 +314,7 @@ DygraphOptions.prototype.getForAxis = function(name, axis) {
  */
 DygraphOptions.prototype.getForSeries = function(name, series) {
   // Honors indexes as series.
-  if (series === this.dygraph_.highlightSet_) {
+  if (series === this.dygraph_.getHighlightSeries()) {
     if (this.highlightSeries_.hasOwnProperty(name)) {
       return this.highlightSeries_[name];
     }
@@ -314,7 +335,7 @@ DygraphOptions.prototype.getForSeries = function(name, series) {
 
 /**
  * Returns the number of y-axes on the chart.
- * @return {Number} the number of axes.
+ * @return {number} the number of axes.
  */
 DygraphOptions.prototype.numAxes = function() {
   return this.yAxes_.length;
index 4909909..f4778b1 100644 (file)
@@ -259,6 +259,39 @@ Dygraph.SHORT_SPACINGS[Dygraph.SIX_HOURLY]      = 1000 * 3600 * 6;
 Dygraph.SHORT_SPACINGS[Dygraph.DAILY]           = 1000 * 86400;
 Dygraph.SHORT_SPACINGS[Dygraph.WEEKLY]          = 1000 * 604800;
 
+/** 
+ * A collection of objects specifying where it is acceptable to place tick
+ * marks for granularities larger than WEEKLY.  
+ * 'months' is an array of month indexes on which to place tick marks.
+ * 'year_mod' ticks are placed when year % year_mod = 0.
+ * @type {Array.<Object>} 
+ */
+Dygraph.LONG_TICK_PLACEMENTS = [];
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.MONTHLY] = {
+  months : [0,1,2,3,4,5,6,7,8,9,10,11], 
+  year_mod : 1
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.QUARTERLY] = {
+  months: [0,3,6,9], 
+  year_mod: 1
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.BIANNUAL] = {
+  months: [0,6], 
+  year_mod: 1
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.ANNUAL] = {
+  months: [0], 
+  year_mod: 1
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.DECADAL] = {
+  months: [0], 
+  year_mod: 10
+};
+Dygraph.LONG_TICK_PLACEMENTS[Dygraph.CENTENNIAL] = {
+  months: [0], 
+  year_mod: 100
+};
+
 /**
  * This is a list of human-friendly values at which to show tick marks on a log
  * scale. It is k * 10^n, where k=1..9 and n=-39..+39, so:
@@ -312,17 +345,11 @@ Dygraph.numDateTicks = function(start_time, end_time, granularity) {
     var spacing = Dygraph.SHORT_SPACINGS[granularity];
     return Math.floor(0.5 + 1.0 * (end_time - start_time) / spacing);
   } else {
-    var year_mod = 1;  // e.g. to only print one point every 10 years.
-    var num_months = 12;
-    if (granularity == Dygraph.QUARTERLY) num_months = 3;
-    if (granularity == Dygraph.BIANNUAL) num_months = 2;
-    if (granularity == Dygraph.ANNUAL) num_months = 1;
-    if (granularity == Dygraph.DECADAL) { num_months = 1; year_mod = 10; }
-    if (granularity == Dygraph.CENTENNIAL) { num_months = 1; year_mod = 100; }
+    var tickPlacement = Dygraph.LONG_TICK_PLACEMENTS[granularity];
 
     var msInYear = 365.2524 * 24 * 3600 * 1000;
     var num_years = 1.0 * (end_time - start_time) / msInYear;
-    return Math.floor(0.5 + 1.0 * num_years * num_months / year_mod);
+    return Math.floor(0.5 + 1.0 * num_years * tickPlacement.months.length / tickPlacement.year_mod);
   }
 };
 
@@ -386,7 +413,7 @@ Dygraph.getDateAxis = function(start_time, end_time, granularity, opts, dg) {
     var check_dst = (spacing >= Dygraph.SHORT_SPACINGS[Dygraph.TWO_HOURLY]);
 
     for (t = start_time; t <= end_time; t += spacing) {
-      var d = new Date(t);
+      d = new Date(t);
 
       // This ensures that we stay on the same hourly "rhythm" across
       // daylight savings transitions. Without this, the ticks could get off
@@ -420,20 +447,9 @@ Dygraph.getDateAxis = function(start_time, end_time, granularity, opts, dg) {
     var months;
     var year_mod = 1;  // e.g. to only print one point every 10 years.
 
-    if (granularity == Dygraph.MONTHLY) {
-      months = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
-    } else if (granularity == Dygraph.QUARTERLY) {
-      months = [ 0, 3, 6, 9 ];
-    } else if (granularity == Dygraph.BIANNUAL) {
-      months = [ 0, 6 ];
-    } else if (granularity == Dygraph.ANNUAL) {
-      months = [ 0 ];
-    } else if (granularity == Dygraph.DECADAL) {
-      months = [ 0 ];
-      year_mod = 10;
-    } else if (granularity == Dygraph.CENTENNIAL) {
-      months = [ 0 ];
-      year_mod = 100;
+    if (granularity < Dygraph.NUM_GRANULARITIES) {
+      months = Dygraph.LONG_TICK_PLACEMENTS[granularity].months;
+      year_mod = Dygraph.LONG_TICK_PLACEMENTS[granularity].year_mod;
     } else {
       Dygraph.warn("Span of dates is too long");
     }
index 035aba6..223360b 100644 (file)
@@ -135,7 +135,6 @@ Dygraph.prototype.warn = Dygraph.warn;
 
 /**
  * @param {string} message
- * @private
  */
 Dygraph.error = function(message) {
   Dygraph.log(Dygraph.ERROR, message);
@@ -634,7 +633,6 @@ Dygraph.dateStrToMillis = function(str) {
  * @param {!Object} self
  * @param {!Object} o
  * @return {!Object}
- * @private
  */
 Dygraph.update = function(self, o) {
   if (typeof(o) != 'undefined' && o !== null) {
@@ -818,7 +816,7 @@ Dygraph.Iterator.prototype.next = function() {
 };
 
 /**
- * Returns a new iterator over array, between indexes start and 
+ * Returns a new iterator over array, between indexes start and
  * start + length, and only returns entries that pass the accept function
  *
  * @param {!Array} array the array to iterate over.
@@ -908,7 +906,7 @@ Dygraph.repeatAndCleanup = function(repeatFn, maxFrames, framePeriodInMillis,
  * This function will scan the option list and determine if they
  * require us to recalculate the pixel positions of each point.
  * @param {!Array.<string>} labels a list of options to check.
- * @param {!Object} attrs 
+ * @param {!Object} attrs
  * @return {boolean} true if the graph needs new points else false.
  * @private
  */
@@ -1012,7 +1010,7 @@ Dygraph.isPixelChangingOptionList = function(labels, attrs) {
 
 /**
  * Compares two arrays to see if they are equal. If either parameter is not an
- * array it will return false. Does a shallow compare 
+ * array it will return false. Does a shallow compare
  * Dygraph.compareArrays([[1,2], [3, 4]], [[1,2], [3,4]]) === false.
  * @param {!Array.<T>} array1 first array
  * @param {!Array.<T>} array2 second array
@@ -1057,7 +1055,7 @@ Dygraph.regularShape_ = function(
   var computeCoordinates = function() {
     var x = cx + (Math.sin(angle) * radius);
     var y = cy + (-Math.cos(angle) * radius);
-    return [x, y]; 
+    return [x, y];
   };
 
   var initialCoordinates = computeCoordinates();
@@ -1165,7 +1163,7 @@ Dygraph.Circles = {
  *   };
  *   window.addEventListener('mouseup', mouseUpHandler);
  * };
- * 
+ *
  * @constructor
  */
 Dygraph.IFrameTarp = function() {
@@ -1236,20 +1234,21 @@ Dygraph.detectLineDelimiter = function(data) {
 };
 
 /**
- * Is one element contained by another?
- * @param {Element} containee The contained element.
- * @param {Element} container The container element.
+ * Is one node contained by another?
+ * @param {Node} containee The contained node.
+ * @param {Node} container The container node.
  * @return {boolean} Whether containee is inside (or equal to) container.
  * @private
  */
-Dygraph.isElementContainedBy = function(containee, container) {
+Dygraph.isNodeContainedBy = function(containee, container) {
   if (container === null || containee === null) {
     return false;
   }
-  while (containee && containee !== container) {
-    containee = containee.parentNode;
+  var containeeNode = /** @type {Node} */ (containee);
+  while (containeeNode && containeeNode !== container) {
+    containeeNode = containeeNode.parentNode;
   }
-  return (containee === container);
+  return (containeeNode === container);
 };
 
 
index defa173..7ad3cdb 100644 (file)
@@ -1019,8 +1019,8 @@ Dygraph.prototype.createInterface_ = function() {
     // 2. e.relatedTarget is outside the chart
     var target = e.target || e.fromElement;
     var relatedTarget = e.relatedTarget || e.toElement;
-    if (Dygraph.isElementContainedBy(target, dygraph.graphDiv) &&
-        !Dygraph.isElementContainedBy(relatedTarget, dygraph.graphDiv)) {
+    if (Dygraph.isNodeContainedBy(target, dygraph.graphDiv) &&
+        !Dygraph.isNodeContainedBy(relatedTarget, dygraph.graphDiv)) {
       dygraph.mouseOut_(e);
     }
   };
@@ -2797,6 +2797,8 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
 Dygraph.prototype.extractSeries_ = function(rawData, i, logScale) {
   // TODO(danvk): pre-allocate series here.
   var series = [];
+  var errorBars = this.attr_("errorBars");
+  var customBars =  this.attr_("customBars");
   for (var j = 0; j < rawData.length; j++) {
     var x = rawData[j][0];
     var point = rawData[j][i];
@@ -2807,7 +2809,12 @@ Dygraph.prototype.extractSeries_ = function(rawData, i, logScale) {
         point = null;
       }
     }
-    series.push([x, point]);
+    // Fix null points to fit the display type standard.
+    if(point !== null) {
+      series.push([x, point]);
+    } else {
+      series.push([x, errorBars ? [null, null] : customBars ? [null, null, null] : point]);
+    }
   }
   return series;
 };
index f7865c8..cacda86 100644 (file)
@@ -71,9 +71,12 @@ axes.prototype.layout = function(e) {
   }
 
   if (g.numAxes() == 2) {
-    // TODO(danvk): per-axis setting.
-    var w = g.getOption('yAxisLabelWidth') + 2 * g.getOption('axisTickSize');
-    e.reserveSpaceRight(w);
+    // TODO(danvk): introduce a 'drawAxis' per-axis property.
+    if (g.getOption('drawYAxis')) {
+      // TODO(danvk): per-axis setting.
+      var w = g.getOption('yAxisLabelWidth') + 2 * g.getOption('axisTickSize');
+      e.reserveSpaceRight(w);
+    }
   } else if (g.numAxes() > 2) {
     g.error("Only two y-axes are supported at this time. (Trying " +
             "to use " + g.numAxes() + ")");
index df8f867..d45a29a 100644 (file)
@@ -176,7 +176,11 @@ rangeSelector.prototype.resize_ = function() {
   }
 
   var plotArea = this.dygraph_.layout_.getPlotArea();
-  var xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'));
+  
+  var xAxisLabelHeight = 0;
+  if(this.getOption_('drawXAxis')){
+    xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'));
+  }
   this.canvasRect_ = {
     x: plotArea.x,
     y: plotArea.y + plotArea.h + xAxisLabelHeight + 4,
diff --git a/test.sh b/test.sh
index 3d3bfbb..f54d6e0 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -9,4 +9,11 @@ if [ $? != 0 ]; then
   exit 1
 fi
 
+# Don't run tests if the documentation doesn't parse.
+./generate-documentation.py > /dev/null
+if [ $? != 0 ]; then
+  echo Failed to generate documentation. Fix this before running tests.
+  exit 1
+fi
+
 phantomjs phantom-driver.js
index fd0bfbd..615a732 100644 (file)
@@ -21,6 +21,9 @@
     </td><td valign=top>
     <div id="graph3"></div>
     <div id="graph4"></div>
+    </td><td valign=top>
+    <div id="graph5"></div>
+    <div id="graph6"></div>
     </td></tr>
     </table>
 
@@ -61,7 +64,7 @@
         [2, [12, 2], [20, 3]],
         [3, [ 8, 2], [20, 3]],
         [4, [null, 2], [20, 3]],
-        [5, [null, 2], [null, 3]],
+        [5, [null, 2], null],
         [6, [ 9, 2], [20, 3]],
         [7, [10, 2], [20, 3]]
       ],
@@ -77,8 +80,8 @@
         [1, [10, 2], [20, 3]],
         [2, [12, 2], [20, 3]],
         [3, [ 8, 2], [20, 3]],
-        [4, [null, 2], [20, 3]],
-        [5, [null, 2], [null, 3]],
+        [4, null, [20, 3]],
+        [5, null, [null, 3]],
         [6, [ 9, 2], [20, 3]],
         [7, [10, 2], [20, 3]]
       ],
         labels: [ "X", "Series1", "Series2" ]
       }
     );
+      
+    g5 = new Dygraph(
+        document.getElementById("graph5"),
+        [
+          [1, [10, 2], [20, 3]],
+          [2, [12, 2], [20, 3]],
+          [3, [ 8, 2], [20, 3]],
+          [4, [2, null], null],
+          [5, null, [null, 3]],
+          [6, [ 9, 2], [20, 3]],
+          [7, [10, 2], [20, 3]]
+        ],
+        {
+          errorBars: true,
+          connectSeparatedPoints: false,
+          labels: [ "X", "Series1", "Series2" ]
+        }
+    );
+      
+    g6 = new Dygraph(
+        document.getElementById("graph6"),
+        [
+          [1, [8, 10,12],null],
+          [2, [3, 5,7],[4,6,7]],
+          [3, null,[1,2,4]],
+          [4, [ 9,null, 2],[3,4,8]],
+          [5, [null,2, null],[6,8,9]],
+          [6, [2,3, 6],[2,3,5]]
+        ],
+        {
+          customBars: true,
+          connectSeparatedPoints: false,
+          labels: [ "X", "Series1", "Series2"]
+        }
+    );
     </script>
   </body>
 </html>
index 3ad6a26..74a38d6 100644 (file)
       Roll period of 14 timesteps, custom range selector height and plot color.
     </p>
     <div id="roll14" style="width:800px; height:320px;"></div>
+    <p>
+      Demo of range selecor without the chart. (interesting if multiple charts should be synced with one range selector).
+    </p>
+    <div id="nochart" style="width:800px; height:30px;"></div>
     <script type="text/javascript">
       g1 = new Dygraph(
           document.getElementById("noroll"),
             rangeSelectorPlotFillColor: 'lightyellow'
           }
       );
+      g3 = new Dygraph(
+          document.getElementById("nochart"),
+          [[0,1],[10,1]],
+          {
+            xAxisHeight: 30,
+            drawXAxis: false,
+            showRangeSelector: true,
+            rangeSelectorHeight: 30,
+          }
+      );
     </script>
   </body>
 </html>
index 3f40b07..cfc36a5 100644 (file)
@@ -67,7 +67,7 @@
             labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
             labelsKMB: true,
             ylabel: 'Primary y-axis',
-            y2label: 'Secondary y-axis',
+            y2label: 'Secondary y-axis'
           }
       );