Copy from pr/528
[dygraphs.git] / plugins / range-selector.js
index 8842546..4c1e938 100644 (file)
@@ -12,7 +12,6 @@
 
 Dygraph.Plugins.RangeSelector = (function() {
 
-/*jshint globalstrict: true */
 /*global Dygraph:false */
 "use strict";
 
@@ -29,7 +28,6 @@ rangeSelector.prototype.toString = function() {
 
 rangeSelector.prototype.activate = function(dygraph) {
   this.dygraph_ = dygraph;
-  this.isUsingExcanvas_ = dygraph.isUsingExcanvas_;
   if (this.getOption_('showRangeSelector')) {
     this.createInterface_();
   }
@@ -45,7 +43,6 @@ rangeSelector.prototype.destroy = function() {
   this.fgcanvas_ = null;
   this.leftZoomHandle_ = null;
   this.rightZoomHandle_ = null;
-  this.iePanOverlay_ = null;
 };
 
 //------------------------------------------------------------------
@@ -57,7 +54,7 @@ rangeSelector.prototype.getOption_ = function(name, opt_series) {
 };
 
 rangeSelector.prototype.setDefaultOption_ = function(name, value) {
-  return this.dygraph_.attrs_[name] = value;
+  this.dygraph_.attrs_[name] = value;
 };
 
 /**
@@ -66,9 +63,6 @@ rangeSelector.prototype.setDefaultOption_ = function(name, value) {
  */
 rangeSelector.prototype.createInterface_ = function() {
   this.createCanvases_();
-  if (this.isUsingExcanvas_) {
-    this.createIEPanOverlay_();
-  }
   this.createZoomHandles_();
   this.initInteraction_();
 
@@ -166,17 +160,23 @@ rangeSelector.prototype.updateVisibility_ = function() {
  * Resizes the range selector.
  */
 rangeSelector.prototype.resize_ = function() {
-  function setElementRect(canvas, rect) {
+  function setElementRect(canvas, context, rect) {
+    var canvasScale = Dygraph.getContextPixelRatio(context);
+
     canvas.style.top = rect.y + 'px';
     canvas.style.left = rect.x + 'px';
-    canvas.width = rect.w;
-    canvas.height = rect.h;
-    canvas.style.width = canvas.width + 'px';    // for IE
-    canvas.style.height = canvas.height + 'px';  // for IE
+    canvas.width = rect.w * canvasScale;
+    canvas.height = rect.h * canvasScale;
+    canvas.style.width = rect.w + 'px';
+    canvas.style.height = rect.h + 'px';
+
+    if(canvasScale != 1) {
+      context.scale(canvasScale, canvasScale);
+    }
   }
 
   var plotArea = this.dygraph_.layout_.getPlotArea();
-  
+
   var xAxisLabelHeight = 0;
   if (this.dygraph_.getOptionForAxis('drawAxis', 'x')) {
     xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'));
@@ -188,8 +188,8 @@ rangeSelector.prototype.resize_ = function() {
     h: this.getOption_('rangeSelectorHeight')
   };
 
-  setElementRect(this.bgcanvas_, this.canvasRect_);
-  setElementRect(this.fgcanvas_, this.canvasRect_);
+  setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_);
+  setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_);
 };
 
 /**
@@ -213,20 +213,6 @@ rangeSelector.prototype.createCanvases_ = function() {
 
 /**
  * @private
- * Creates overlay divs for IE/Excanvas so that mouse events are handled properly.
- */
-rangeSelector.prototype.createIEPanOverlay_ = function() {
-  this.iePanOverlay_ = document.createElement("div");
-  this.iePanOverlay_.style.position = 'absolute';
-  this.iePanOverlay_.style.backgroundColor = 'white';
-  this.iePanOverlay_.style.filter = 'alpha(opacity=0)';
-  this.iePanOverlay_.style.display = 'none';
-  this.iePanOverlay_.style.cursor = 'move';
-  this.fgcanvas_.appendChild(this.iePanOverlay_);
-};
-
-/**
- * @private
  * Creates the zoom handle elements.
  */
 rangeSelector.prototype.createZoomHandles_ = function() {
@@ -236,7 +222,7 @@ rangeSelector.prototype.createZoomHandles_ = function() {
   img.style.zIndex = 10;
   img.style.visibility = 'hidden'; // Initially hidden so they don't show up in the wrong place.
   img.style.cursor = 'col-resize';
-
+//TODO: change image to more options
   if (/MSIE 7/.test(navigator.userAgent)) { // IE7 doesn't support embedded src data.
     img.width = 7;
     img.height = 14;
@@ -273,7 +259,7 @@ rangeSelector.prototype.initInteraction_ = function() {
   var handle = null;
   var isZooming = false;
   var isPanning = false;
-  var dynamic = !this.isMobileDevice_ && !this.isUsingExcanvas_;
+  var dynamic = !this.isMobileDevice_;
 
   // We cover iframes during mouse interactions. See comments in
   // dygraph-utils.js for more info on why this is a good idea.
@@ -338,7 +324,7 @@ rangeSelector.prototype.initInteraction_ = function() {
     handle.style.left = (newPos - halfHandleWidth) + 'px';
     self.drawInteractiveLayer_();
 
-    // Zoom on the fly (if not using excanvas).
+    // Zoom on the fly.
     if (dynamic) {
       doZoom();
     }
@@ -355,7 +341,7 @@ rangeSelector.prototype.initInteraction_ = function() {
     Dygraph.removeEvent(topElem, 'mouseup', onZoomEnd);
     self.fgcanvas_.style.cursor = 'default';
 
-    // If using excanvas, Zoom now.
+    // If on a slower device, zoom now.
     if (!dynamic) {
       doZoom();
     }
@@ -378,15 +364,11 @@ rangeSelector.prototype.initInteraction_ = function() {
   };
 
   isMouseInPanZone = function(e) {
-    if (self.isUsingExcanvas_) {
-        return e.srcElement == self.iePanOverlay_;
-    } else {
-      var rect = self.leftZoomHandle_.getBoundingClientRect();
-      var leftHandleClientX = rect.left + rect.width/2;
-      rect = self.rightZoomHandle_.getBoundingClientRect();
-      var rightHandleClientX = rect.left + rect.width/2;
-      return (e.clientX > leftHandleClientX && e.clientX < rightHandleClientX);
-    }
+    var rect = self.leftZoomHandle_.getBoundingClientRect();
+    var leftHandleClientX = rect.left + rect.width/2;
+    rect = self.rightZoomHandle_.getBoundingClientRect();
+    var rightHandleClientX = rect.left + rect.width/2;
+    return (e.clientX > leftHandleClientX && e.clientX < rightHandleClientX);
   };
 
   onPanStart = function(e) {
@@ -436,7 +418,7 @@ rangeSelector.prototype.initInteraction_ = function() {
     self.rightZoomHandle_.style.left = (rightHandlePos - halfHandleWidth) + 'px';
     self.drawInteractiveLayer_();
 
-    // Do pan on the fly (if not using excanvas).
+    // Do pan on the fly.
     if (dynamic) {
       doPan();
     }
@@ -450,7 +432,7 @@ rangeSelector.prototype.initInteraction_ = function() {
     isPanning = false;
     Dygraph.removeEvent(topElem, 'mousemove', onPan);
     Dygraph.removeEvent(topElem, 'mouseup', onPanEnd);
-    // If using excanvas, do pan now.
+    // If on a slower device, do pan now.
     if (!dynamic) {
       doPan();
     }
@@ -519,12 +501,8 @@ rangeSelector.prototype.initInteraction_ = function() {
   this.dygraph_.addAndTrackEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart);
   this.dygraph_.addAndTrackEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart);
 
-  if (this.isUsingExcanvas_) {
-    this.dygraph_.addAndTrackEvent(this.iePanOverlay_, 'mousedown', onPanStart);
-  } else {
-    this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart);
-    this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover);
-  }
+  this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart);
+  this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover);
 
   // Touch events
   if (this.hasTouchInterface_) {
@@ -548,8 +526,8 @@ rangeSelector.prototype.drawStaticLayer_ = function() {
   }
 
   var margin = 0.5;
-  this.bgcanvas_ctx_.lineWidth = 1;
-  ctx.strokeStyle = 'gray';
+  this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorBackgroundLineWidth');
+  ctx.strokeStyle = this.getOption_('rangeSelectorBackgroundStrokeColor');
   ctx.beginPath();
   ctx.moveTo(margin, margin);
   ctx.lineTo(margin, this.canvasRect_.h-margin);
@@ -565,6 +543,7 @@ rangeSelector.prototype.drawStaticLayer_ = function() {
  */
 rangeSelector.prototype.drawMiniPlot_ = function() {
   var fillStyle = this.getOption_('rangeSelectorPlotFillColor');
+  var fillGradientStyle = this.getOption_('rangeSelectorPlotFillGradientColor');
   var strokeStyle = this.getOption_('rangeSelectorPlotStrokeColor');
   if (!fillStyle && !strokeStyle) {
     return;
@@ -594,6 +573,13 @@ rangeSelector.prototype.drawMiniPlot_ = function() {
     var dataPoint = combinedSeriesData.data[i];
     var x = ((dataPoint[0] !== null) ? ((dataPoint[0] - xExtremes[0])*xFact) : NaN);
     var y = ((dataPoint[1] !== null) ? (canvasHeight - (dataPoint[1] - combinedSeriesData.yMin)*yFact) : NaN);
+
+    // Skip points that don't change the x-value. Overly fine-grained points
+    // can cause major slowdowns with the ctx.fill() call below.
+    if (!stepPlot && prevX !== null && Math.round(x) == Math.round(prevX)) {
+      continue;
+    }
+
     if (isFinite(x) && isFinite(y)) {
       if(prevX === null) {
         ctx.lineTo(x, canvasHeight);
@@ -623,7 +609,9 @@ rangeSelector.prototype.drawMiniPlot_ = function() {
 
   if (fillStyle) {
     var lingrad = this.bgcanvas_ctx_.createLinearGradient(0, 0, 0, canvasHeight);
-    lingrad.addColorStop(0, 'white');
+    if (fillGradientStyle) {
+      lingrad.addColorStop(0, fillGradientStyle);
+    }
     lingrad.addColorStop(1, fillStyle);
     this.bgcanvas_ctx_.fillStyle = lingrad;
     ctx.fill();
@@ -631,7 +619,7 @@ rangeSelector.prototype.drawMiniPlot_ = function() {
 
   if (strokeStyle) {
     this.bgcanvas_ctx_.strokeStyle = strokeStyle;
-    this.bgcanvas_ctx_.lineWidth = 1.5;
+    this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorPlotLineWidth');
     ctx.stroke();
   }
 };
@@ -673,7 +661,7 @@ rangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() {
     if (g.rollPeriod() > 1) {
       series = dataHandler.rollingAverage(series, g.rollPeriod(), options);
     }
-    
+
     rolledSeries.push(series);
   }
 
@@ -761,7 +749,8 @@ rangeSelector.prototype.drawInteractiveLayer_ = function() {
   var height = this.canvasRect_.h - margin;
   var zoomHandleStatus = this.getZoomHandleStatus_();
 
-  ctx.strokeStyle = 'black';
+  ctx.strokeStyle = this.getOption_('rangeSelectorForegroundStrokeColor');
+  ctx.lineWidth = this.getOption_('rangeSelectorForegroundLineWidth');
   if (!zoomHandleStatus.isZoomed) {
     ctx.beginPath();
     ctx.moveTo(margin, margin);
@@ -769,14 +758,11 @@ rangeSelector.prototype.drawInteractiveLayer_ = function() {
     ctx.lineTo(width, height);
     ctx.lineTo(width, margin);
     ctx.stroke();
-    if (this.iePanOverlay_) {
-      this.iePanOverlay_.style.display = 'none';
-    }
   } else {
     var leftHandleCanvasPos = Math.max(margin, zoomHandleStatus.leftHandlePos - this.canvasRect_.x);
     var rightHandleCanvasPos = Math.min(width, zoomHandleStatus.rightHandlePos - this.canvasRect_.x);
 
-    ctx.fillStyle = 'rgba(240, 240, 240, 0.6)';
+    ctx.fillStyle = 'rgba(240, 240, 240, ' + this.getOption_('rangeSelectorAlpha').toString() + ')';
     ctx.fillRect(0, 0, leftHandleCanvasPos, this.canvasRect_.h);
     ctx.fillRect(rightHandleCanvasPos, 0, this.canvasRect_.w - rightHandleCanvasPos, this.canvasRect_.h);
 
@@ -788,13 +774,6 @@ rangeSelector.prototype.drawInteractiveLayer_ = function() {
     ctx.lineTo(rightHandleCanvasPos, margin);
     ctx.lineTo(width, margin);
     ctx.stroke();
-
-    if (this.isUsingExcanvas_) {
-      this.iePanOverlay_.style.width = (rightHandleCanvasPos - leftHandleCanvasPos) + 'px';
-      this.iePanOverlay_.style.left = leftHandleCanvasPos + 'px';
-      this.iePanOverlay_.style.height = height + 'px';
-      this.iePanOverlay_.style.display = 'inline';
-    }
   }
 };