Clear graph when numAxes goes from 1 -> 2 in updateOptions
[dygraphs.git] / src / dygraph.js
index d1629db..4676a2e 100644 (file)
  And error bars will be calculated automatically using a binomial distribution.
 
  For further documentation and examples, see http://dygraphs.com/
-
  */
 
-// Polyfills
-import 'core-js/es6/symbol';
-import 'core-js/fn/symbol/iterator';
-
 import DygraphLayout from './dygraph-layout';
 import DygraphCanvasRenderer from './dygraph-canvas';
 import DygraphOptions from './dygraph-options';
@@ -94,7 +89,7 @@ var Dygraph = function(div, data, opts) {
 };
 
 Dygraph.NAME = "Dygraph";
-Dygraph.VERSION = "1.1.0";
+Dygraph.VERSION = "2.1.0";
 
 // Various default values
 Dygraph.DEFAULT_ROLL_PERIOD = 1;
@@ -371,14 +366,16 @@ Dygraph.prototype.toString = function() {
  */
 Dygraph.prototype.attr_ = function(name, seriesName) {
   // For "production" code, this gets removed by uglifyjs.
-  if (process.env.NODE_ENV != 'production') {
-    if (typeof(OPTIONS_REFERENCE) === 'undefined') {
-      console.error('Must include options reference JS for testing');
-    } else if (!OPTIONS_REFERENCE.hasOwnProperty(name)) {
-      console.error('Dygraphs is using property ' + name + ', which has no ' +
-                    'entry in the Dygraphs.OPTIONS_REFERENCE listing.');
-      // Only log this error once.
-      OPTIONS_REFERENCE[name] = true;
+  if (typeof(process) !== 'undefined') {
+    if (process.env.NODE_ENV != 'production') {
+      if (typeof(OPTIONS_REFERENCE) === 'undefined') {
+        console.error('Must include options reference JS for testing');
+      } else if (!OPTIONS_REFERENCE.hasOwnProperty(name)) {
+        console.error('Dygraphs is using property ' + name + ', which has no ' +
+                      'entry in the Dygraphs.OPTIONS_REFERENCE listing.');
+        // Only log this error once.
+        OPTIONS_REFERENCE[name] = true;
+      }
     }
   }
   return seriesName ? this.attributes_.getForSeries(name, seriesName) : this.attributes_.get(name);
@@ -864,7 +861,9 @@ Dygraph.prototype.resizeElements_ = function() {
   this.graphDiv.style.width = this.width_ + "px";
   this.graphDiv.style.height = this.height_ + "px";
 
-  var canvasScale = utils.getContextPixelRatio(this.canvas_ctx_);
+  var pixelRatioOption = this.getNumericOption('pixelRatio')
+
+  var canvasScale = pixelRatioOption || utils.getContextPixelRatio(this.canvas_ctx_);
   this.canvas_.width = this.width_ * canvasScale;
   this.canvas_.height = this.height_ * canvasScale;
   this.canvas_.style.width = this.width_ + "px";    // for IE
@@ -873,7 +872,7 @@ Dygraph.prototype.resizeElements_ = function() {
     this.canvas_ctx_.scale(canvasScale, canvasScale);
   }
 
-  var hiddenScale = utils.getContextPixelRatio(this.hidden_ctx_);
+  var hiddenScale = pixelRatioOption || utils.getContextPixelRatio(this.hidden_ctx_);
   this.hidden_.width = this.width_ * hiddenScale;
   this.hidden_.height = this.height_ * hiddenScale;
   this.hidden_.style.width = this.width_ + "px";    // for IE
@@ -1353,11 +1352,12 @@ Dygraph.prototype.resetZoom = function() {
   const zoomCallback = this.getFunctionOption('zoomCallback');
 
   // TODO(danvk): merge this block w/ the code below.
+  // TODO(danvk): factor out a generic, public zoomTo method.
   if (!animatedZooms) {
     this.dateWindow_ = null;
-    for (const axis of this.axes_) {
+    this.axes_.forEach(axis => {
       if (axis.valueRange) delete axis.valueRange;
-    }
+    });
 
     this.drawGraph_();
     if (zoomCallback) {
@@ -1380,9 +1380,9 @@ Dygraph.prototype.resetZoom = function() {
   this.doAnimatedZoom(oldWindow, newWindow, oldValueRanges, newValueRanges,
       () => {
         this.dateWindow_ = null;
-        for (const axis of this.axes_) {
+        this.axes_.forEach(axis => {
           if (axis.valueRange) delete axis.valueRange;
-        }
+        });
         if (zoomCallback) {
           zoomCallback.call(this, minDate, maxDate, this.yAxisRanges());
         }
@@ -2441,9 +2441,8 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
     //
     // - backwards compatible (yRangePad not set):
     //   10% padding for automatic Y ranges, but not for user-supplied
-    //   ranges, and move a close-to-zero edge to zero except if
-    //   avoidMinZero is set, since drawing at the edge results in
-    //   invisible lines. Unfortunately lines drawn at the edge of a
+    //   ranges, and move a close-to-zero edge to zero, since drawing at the edge
+    //   results in invisible lines. Unfortunately lines drawn at the edge of a
     //   user-supplied range will still be invisible. If logscale is
     //   set, add a variable amount of padding at the top but
     //   none at the bottom.
@@ -2516,11 +2515,9 @@ Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
           minAxisY = minY - ypad * span;
 
           // Backwards-compatible behavior: Move the span to start or end at zero if it's
-          // close to zero, but not if avoidMinZero is set.
-          if (!this.getBooleanOption("avoidMinZero")) {
-            if (minAxisY < 0 && minY >= 0) minAxisY = 0;
-            if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0;
-          }
+          // close to zero.
+          if (minAxisY < 0 && minY >= 0) minAxisY = 0;
+          if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0;
         }
       }
       axis.extremeRange = [minAxisY, maxAxisY];
@@ -2783,6 +2780,23 @@ Dygraph.prototype.parseCSV_ = function(data) {
   return ret;
 };
 
+// In native format, all values must be dates or numbers.
+// This check isn't perfect but will catch most mistaken uses of strings.
+function validateNativeFormat(data) {
+  const firstRow = data[0];
+  const firstX = firstRow[0];
+  if (typeof firstX !== 'number' && !utils.isDateLike(firstX)) {
+    throw new Error(`Expected number or date but got ${typeof firstX}: ${firstX}.`);
+  }
+  for (let i = 1; i < firstRow.length; i++) {
+    const val = firstRow[i];
+    if (val === null || val === undefined) continue;
+    if (typeof val === 'number') continue;
+    if (utils.isArrayLike(val)) continue;  // e.g. error bars or custom bars.
+    throw new Error(`Expected number or array but got ${typeof val}: ${val}.`);
+  }
+}
+
 /**
  * The user has provided their data as a pre-packaged JS array. If the x values
  * are numeric, this is the same as dygraphs' internal format. If the x values
@@ -2802,6 +2816,8 @@ Dygraph.prototype.parseArray_ = function(data) {
     return null;
   }
 
+  validateNativeFormat(data);
+
   var i;
   if (this.attr_("labels") === null) {
     console.warn("Using default labels. Set labels explicitly via 'labels' " +
@@ -2997,6 +3013,7 @@ Dygraph.prototype.parseDataTable_ = function(data) {
 /**
  * Signals to plugins that the chart data has updated.
  * This happens after the data has updated but before the chart has redrawn.
+ * @private
  */
 Dygraph.prototype.cascadeDataDidUpdateEvent_ = function() {
   // TODO(danvk): there are some issues checking xAxisRange() and using
@@ -3086,6 +3103,7 @@ Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) {
   // copyUserAttrs_ drops the "file" parameter as a convenience to us.
   var file = input_attrs.file;
   var attrs = Dygraph.copyUserAttrs_(input_attrs);
+  var prevNumAxes = this.attributes_.numAxes();
 
   // TODO(danvk): this is a mess. Move these options into attr_.
   if ('rollPeriod' in attrs) {
@@ -3109,9 +3127,10 @@ Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) {
 
   this.attributes_.reparseSeries();
 
+  if (prevNumAxes < this.attributes_.numAxes()) this.plotter_.clear();
   if (file) {
     // This event indicates that the data is about to change, but hasn't yet.
-    // TODO(danvk): support cancelation of the update via this event.
+    // TODO(danvk): support cancellation of the update via this event.
     this.cascadeEvents_('dataWillUpdate', {});
 
     this.file_ = file;
@@ -3129,6 +3148,7 @@ Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) {
 
 /**
  * Make a copy of input attributes, removing file as a convenience.
+ * @private
  */
 Dygraph.copyUserAttrs_ = function(attrs) {
   var my_attrs = {};