fix lingering hover dot on zoom bug
[dygraphs.git] / dygraph.js
index 90c93a9..888abd9 100644 (file)
@@ -85,11 +85,10 @@ Dygraph.toString = function() {
 Dygraph.DEFAULT_ROLL_PERIOD = 1;
 Dygraph.DEFAULT_WIDTH = 480;
 Dygraph.DEFAULT_HEIGHT = 320;
-Dygraph.AXIS_LINE_WIDTH = 0.3;
 
 Dygraph.LOG_SCALE = 10;
 Dygraph.LN_TEN = Math.log(Dygraph.LOG_SCALE);
-/** @private (shut up, jsdoc!) */
+/** @private */
 Dygraph.log10 = function(x) {
   return Math.log(x) / Dygraph.LN_TEN;
 }
@@ -154,6 +153,18 @@ Dygraph.DEFAULT_ATTRS = {
   xLabelHeight: 18,
   yLabelWidth: 18,
 
+  drawXAxis: true,
+  drawYAxis: true,
+  axisLineColor: "black",
+  axisLineWidth: 0.3,
+  gridLineWidth: 0.3,
+  axisLabelColor: "black",
+  axisLabelFont: "Arial",  // TODO(danvk): is this implemented?
+  axisLabelWidth: 50,
+  drawYGrid: true,
+  drawXGrid: true,
+  gridLineColor: "rgb(128,128,128)",
+
   interactionModel: null  // will be set to Dygraph.defaultInteractionModel.
 };
 
@@ -389,17 +400,17 @@ Dygraph.prototype.log = function(severity, message) {
   }
 };
 
-/** @private (shut up, jsdoc!) */
+/** @private */
 Dygraph.prototype.info = function(message) {
   this.log(Dygraph.INFO, message);
 };
 
-/** @private (shut up, jsdoc!) */
+/** @private */
 Dygraph.prototype.warn = function(message) {
   this.log(Dygraph.WARNING, message);
 };
 
-/** @private (shut up, jsdoc!) */
+/** @private */
 Dygraph.prototype.error = function(message) {
   this.log(Dygraph.ERROR, message);
 };
@@ -758,21 +769,7 @@ Dygraph.prototype.createInterface_ = function() {
   });
 
   // Create the grapher
-  // TODO(danvk): why does the Layout need its own set of options?
-  this.layoutOptions_ = { 'xOriginIsZero': false };
-  Dygraph.update(this.layoutOptions_, this.attrs_);
-  Dygraph.update(this.layoutOptions_, this.user_attrs_);
-  Dygraph.update(this.layoutOptions_, {
-    'errorBars': (this.attr_("errorBars") || this.attr_("customBars")) });
-
-  this.layout_ = new DygraphLayout(this, this.layoutOptions_);
-
-  // TODO(danvk): why does the Renderer need its own set of options?
-  this.renderOptions_ = { colorScheme: this.colors_,
-                          strokeColor: null,
-                          axisLineWidth: Dygraph.AXIS_LINE_WIDTH };
-  Dygraph.update(this.renderOptions_, this.attrs_);
-  Dygraph.update(this.renderOptions_, this.user_attrs_);
+  this.layout_ = new DygraphLayout(this);
 
   this.createStatusMessage_();
   this.createDragInterface_();
@@ -878,8 +875,6 @@ Dygraph.hsvToRGB = function (hue, saturation, value) {
  * @private
  */
 Dygraph.prototype.setColors_ = function() {
-  // TODO(danvk): compute this directly into this.attrs_['colorScheme'] and do
-  // away with this.renderOptions_.
   var num = this.attr_("labels").length - 1;
   this.colors_ = [];
   var colors = this.attr_('colors');
@@ -902,11 +897,7 @@ Dygraph.prototype.setColors_ = function() {
     }
   }
 
-  // TODO(danvk): update this w/r/t/ the new options system.
-  this.renderOptions_.colorScheme = this.colors_;
-  Dygraph.update(this.plotter_.options, this.renderOptions_);
-  Dygraph.update(this.layoutOptions_, this.user_attrs_);
-  Dygraph.update(this.layoutOptions_, this.attrs_);
+  this.plotter_.setColors(this.colors_);
 };
 
 /**
@@ -1097,6 +1088,12 @@ Dygraph.prototype.dragGetY_ = function(e, context) {
 };
 
 /**
+ * A collection of functions to facilitate build custom interaction models.
+ * @class
+ */
+Dygraph.Interaction = {};
+
+/**
  * Called in response to an interaction model operation that
  * should start the default panning behavior.
  *
@@ -1109,7 +1106,7 @@ Dygraph.prototype.dragGetY_ = function(e, context) {
  * @param { Object} context The dragging context object (with
  * dragStartX/dragStartY/etc. properties). This function modifies the context.
  */
-Dygraph.startPan = function(event, g, context) {
+Dygraph.Interaction.startPan = function(event, g, context) {
   context.isPanning = true;
   var xRange = g.xAxisRange();
   context.dateRange = xRange[1] - xRange[0];
@@ -1180,7 +1177,7 @@ Dygraph.startPan = function(event, g, context) {
  * @param { Object} context The dragging context object (with
  * dragStartX/dragStartY/etc. properties). This function modifies the context.
  */
-Dygraph.movePan = function(event, g, context) {
+Dygraph.Interaction.movePan = function(event, g, context) {
   context.dragEndX = g.dragGetX_(event, context);
   context.dragEndY = g.dragGetY_(event, context);
 
@@ -1249,7 +1246,7 @@ Dygraph.movePan = function(event, g, context) {
  * @param { Object} context The dragging context object (with
  * dragStartX/dragStartY/etc. properties). This function modifies the context.
  */
-Dygraph.endPan = function(event, g, context) {
+Dygraph.Interaction.endPan = function(event, g, context) {
   // TODO(konigsberg): Clear the context data from the axis.
   // TODO(konigsberg): mouseup should just delete the
   // context object, and mousedown should create a new one.
@@ -1275,7 +1272,7 @@ Dygraph.endPan = function(event, g, context) {
  * @param { Object} context The dragging context object (with
  * dragStartX/dragStartY/etc. properties). This function modifies the context.
  */
-Dygraph.startZoom = function(event, g, context) {
+Dygraph.Interaction.startZoom = function(event, g, context) {
   context.isZooming = true;
 };
 
@@ -1292,7 +1289,7 @@ Dygraph.startZoom = function(event, g, context) {
  * @param { Object} context The dragging context object (with
  * dragStartX/dragStartY/etc. properties). This function modifies the context.
  */
-Dygraph.moveZoom = function(event, g, context) {
+Dygraph.Interaction.moveZoom = function(event, g, context) {
   context.dragEndX = g.dragGetX_(event, context);
   context.dragEndY = g.dragGetY_(event, context);
 
@@ -1331,7 +1328,7 @@ Dygraph.moveZoom = function(event, g, context) {
  * @param { Object} context The dragging context object (with
  * dragStartX/dragStartY/etc. properties). This function modifies the context.
  */
-Dygraph.endZoom = function(event, g, context) {
+Dygraph.Interaction.endZoom = function(event, g, context) {
   // TODO(konigsberg): Refactor or rename this fn -- it deals with clicks, too.
   context.isZooming = false;
   context.dragEndX = g.dragGetX_(event, context);
@@ -1389,7 +1386,7 @@ Dygraph.endZoom = function(event, g, context) {
  *   }
  * } );
  */
-Dygraph.defaultInteractionModel = {
+Dygraph.Interaction.defaultModel = {
   // Track the beginning of drag events
   mousedown: function(event, g, context) {
     context.initializeMouseDown(event, g, context);
@@ -1437,7 +1434,16 @@ Dygraph.defaultInteractionModel = {
   }
 };
 
-Dygraph.DEFAULT_ATTRS.interactionModel = Dygraph.defaultInteractionModel;
+Dygraph.DEFAULT_ATTRS.interactionModel = Dygraph.Interaction.defaultModel;
+
+// old ways of accessing these methods/properties
+Dygraph.defaultInteractionModel = Dygraph.Interaction.defaultModel;
+Dygraph.endZoom = Dygraph.Interaction.endZoom;
+Dygraph.moveZoom = Dygraph.Interaction.moveZoom;
+Dygraph.startZoom = Dygraph.Interaction.startZoom;
+Dygraph.endPan = Dygraph.Interaction.endPan;
+Dygraph.movePan = Dygraph.Interaction.movePan;
+Dygraph.startPan = Dygraph.Interaction.startPan;
 
 /**
  * Set up all the mouse handlers needed to capture dragging behavior for zoom
@@ -1675,6 +1681,9 @@ Dygraph.prototype.doUnzoom_ = function() {
     }
   }
 
+  // Clear any selection, since it's likely to be drawn in the wrong place.
+  this.clearSelection();
+
   if (dirty) {
     // Putting the drawing operation before the callback because it resets
     // yAxisRange.
@@ -2173,7 +2182,7 @@ Dygraph.prototype.addXTicks_ = function() {
   }
 
   var xTicks = this.attr_('xTicker')(range[0], range[1], this);
-  this.layout_.updateOptions({xTicks: xTicks});
+  this.layout_.setXTicks(xTicks);
 };
 
 // Time granularity enumeration
@@ -2636,8 +2645,7 @@ Dygraph.prototype.predraw_ = function() {
   this.plotter_ = new DygraphCanvasRenderer(this,
                                             this.hidden_,
                                             this.hidden_ctx_,
-                                            this.layout_,
-                                            this.renderOptions_);
+                                            this.layout_);
 
   // The roller sits in the bottom left corner of the chart. We don't know where
   // this will be until the options are available, so it's positioned here.
@@ -2782,15 +2790,14 @@ Dygraph.prototype.drawGraph_ = function() {
   }
 
   this.computeYAxisRanges_(extremes);
-  this.layout_.updateOptions( { yAxes: this.axes_,
-                                seriesToAxisMap: this.seriesToAxisMap_
-                              } );
+  this.layout_.setYAxes(this.axes_);
+
   this.addXTicks_();
 
-  // Save the X axis zoomed status as the updateOptions call will tend to set it errorneously
+  // Save the X axis zoomed status as the updateOptions call will tend to set it erroneously
   var tmp_zoomed_x = this.zoomed_x_;
   // Tell PlotKit to use this new data and render itself
-  this.layout_.updateOptions({dateWindow: this.dateWindow_});
+  this.layout_.setDateWindow(this.dateWindow_);
   this.zoomed_x_ = tmp_zoomed_x;
   this.layout_.evaluateWithError();
   this.plotter_.clear();
@@ -2803,8 +2810,10 @@ Dygraph.prototype.drawGraph_ = function() {
     this.setLegendHTML_();
   } else {
     if (typeof(this.selPoints_) !== 'undefined' && this.selPoints_.length) {
-      this.lastx_ = this.selPoints_[0].xval;
-      this.updateSelection_();
+      // We should select the point nearest the page x/y here, but it's easier
+      // to just clear the selection. This prevents erroneous hover dots from
+      // being displayed.
+      this.clearSelection();
     } else {
       this.clearSelection();
     }
@@ -2921,6 +2930,18 @@ Dygraph.prototype.numAxes = function() {
 
 /**
  * @private
+ * Returns axis properties for the given series.
+ * @param { String } setName The name of the series for which to get axis
+ * properties, e.g. 'Y1'.
+ * @return { Object } The axis properties.
+ */
+Dygraph.prototype.axisPropertiesForSeries = function(series) {
+  // TODO(danvk): handle errors.
+  return this.axes_[this.seriesToAxisMap_[series]];
+};
+
+/**
+ * @private
  * Determine the value range and tick marks for each axis.
  * @param {Object} extremes A mapping from seriesName -> [low, high]
  * This fills in the valueRange and ticks fields in each entry of this.axes_.
@@ -3742,12 +3763,9 @@ Dygraph.prototype.updateOptions = function(attrs) {
   // highlightCircleSize
 
   Dygraph.update(this.user_attrs_, attrs);
-  Dygraph.update(this.renderOptions_, attrs);
 
   this.labelsFromCSV_ = (this.attr_("labels") == null);
 
-  // TODO(danvk): this doesn't match the constructor logic
-  this.layout_.updateOptions({ 'errorBars': this.attr_("errorBars") });
   if (attrs['file']) {
     this.file_ = attrs['file'];
     this.start_();
@@ -4265,14 +4283,14 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
   "colorSaturation": {
     "default": "1.0",
     "labels": ["Data Series Colors"],
-    "type": "0.0 - 1.0",
+    "type": "float (0.0 - 1.0)",
     "description": "If <strong>colors</strong> is not specified, saturation of the automatically-generated data series colors."
   },
   "yAxisLabelWidth": {
     "default": "50",
     "labels": ["Axis display"],
     "type": "integer",
-    "description": "Width, in pixels, of the y-axis labels."
+    "description": "Width, in pixels, of the y-axis labels. This also affects the amount of space available for a y-axis chart label."
   },
   "hideOverlayOnMouseOut": {
     "default": "true",
@@ -4439,6 +4457,66 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "type": "boolean",
     "description" : "When this option is passed to updateOptions() along with either the <code>dateWindow</code> or <code>valueRange</code> options, the zoom flags are not changed to reflect a zoomed state. This is primarily useful for when the display area of a chart is changed programmatically and also where manual zooming is allowed and use is made of the <code>isZoomed</code> method to determine this."
   },
+  "drawXGrid": {
+    "default": "true",
+    "labels": ["Grid"],
+    "type": "boolean",
+    "description" : "Whether to display vertical gridlines under the chart."
+  },
+  "drawYGrid": {
+    "default": "true",
+    "labels": ["Grid"],
+    "type": "boolean",
+    "description" : "Whether to display horizontal gridlines under the chart."
+  },
+  "drawXAxis": {
+    "default": "true",
+    "labels": ["Axis display"],
+    "type": "boolean",
+    "description" : "Whether to draw the x-axis. Setting this to false also prevents x-axis ticks from being drawn and reclaims the space for the chart grid/lines."
+  },
+  "drawYAxis": {
+    "default": "true",
+    "labels": ["Axis display"],
+    "type": "boolean",
+    "description" : "Whether to draw the y-axis. Setting this to false also prevents y-axis ticks from being drawn and reclaims the space for the chart grid/lines."
+  },
+  "gridLineWidth": {
+    "default": "0.3",
+    "labels": ["Grid"],
+    "type": "float",
+    "description" : "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawXGrid and drawYGrid options."
+  },
+  "axisLineWidth": {
+    "default": "0.3",
+    "labels": ["Axis display"],
+    "type": "float",
+    "description" : "Thickness (in pixels) of the x- and y-axis lines."
+  },
+  "axisLineColor": {
+    "default": "black",
+    "labels": ["Axis display"],
+    "type": "string",
+    "description" : "Color of the x- and y-axis lines. Accepts any value which the HTML canvas strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'."
+  },
+  "fillAlpha": {
+    "default": "0.15",
+    "labels": ["Error bars", "Data Series Colors"],
+    "type": "float (0.0 - 1.0)",
+    "description" : "Error bars (or custom bars) for each series are drawn in the same color as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the error bars will not be drawn, whereas a value of 1.0 means that the error bars will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point."
+  },
+  "axisLabelColor": {
+    "default": "black",
+    "labels": ["Axis display"],
+    "type": "string",
+    "description" : "Color for x- and y-axis labels. This is a CSS color string."
+  },
+  "axisLabelWidth": {
+    "default": "50",
+    "labels": ["Axis display", "Chart labels"],
+    "type": "integer",
+    "description" : "Width (in pixels) of the containing divs for x- and y-axis labels. For the y-axis, this also controls "
+  },
   "sigFigs" : {
     "default": "null",
     "labels": ["Value display/formatting"],
@@ -4456,6 +4534,12 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "labels": ["Value display/formatting"],
     "type": "integer",
     "description": "When displaying numbers in normal (not scientific) mode, large numbers will be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This can lead to unwieldy y-axis labels. If there are more than <code>maxNumberWidth</code> digits to the left of the decimal in a number, dygraphs will switch to scientific notation, even when not operating in scientific mode. If you'd like to see all those digits, set this to something large, like 20 or 30."
+  },
+  "file": {
+    "default": "(set when constructed)",
+    "labels": ["Data"],
+    "type": "string (URL of CSV or CSV), GViz DataTable or 2D Array",
+    "description": "Sets the data being displayed in the chart. This can only be set when calling updateOptions; it cannot be set from the constructor. For a full description of valid data formats, see the <a href='http://dygraphs.com/data.html'>Data Formats</a> page."
   }
 }
 ;  // </JSON>
@@ -4474,6 +4558,7 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
    'Chart labels',
    'CSV parsing',
    'Callbacks',
+   'Data',
    'Data Line display',
    'Data Series Colors',
    'Error Bars',