Rename test and test methods so they do not conflict with multiple-axes.js.
[dygraphs.git] / dygraph-range-selector.js
index fd50bd0..b777470 100644 (file)
@@ -1,5 +1,8 @@
-// Copyright 2011 Paul Felix (paul.eric.felix@gmail.com)
-// All Rights Reserved.
+/*
+ * @license
+ * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com)
+ * MIT-licensed (http://opensource.org/licenses/MIT)
+ */
 
 /**
  * @fileoverview This file contains the DygraphRangeSelector class used to provide
@@ -20,7 +23,7 @@ var DygraphRangeSelector = function(dygraph) {
   this.isUsingExcanvas_ = dygraph.isUsingExcanvas_;
   this.dygraph_ = dygraph;
   this.hasTouchInterface_ = typeof(TouchEvent) != 'undefined';
-  this.isMobileDevice_ =  Math.min(screen.width, screen.height) < 480;
+  this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion);
   this.createCanvases_();
   if (this.isUsingExcanvas_) {
     this.createIEPanOverlay_();
@@ -77,7 +80,7 @@ DygraphRangeSelector.prototype.resize_ = function() {
   }
 
   var plotArea = this.layout_.getPlotArea();
-  var xAxisLabelHeight = this.attr_('axisLabelFontSize') + 2 * this.attr_('axisTickSize');
+  var xAxisLabelHeight = this.attr_('xAxisHeight') || (this.attr_('axisLabelFontSize') + 2 * this.attr_('axisTickSize'));
   this.canvasRect_ = {
     x: plotArea.x,
     y: plotArea.y + plotArea.h + xAxisLabelHeight + 4,
@@ -154,11 +157,7 @@ DygraphRangeSelector.prototype.createZoomHandles_ = function() {
 'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII=';
   }
 
-  var minScreenDim = Math.min(screen.width, screen.height);
-  if (minScreenDim < 480) {
-    img.width *= 3;
-    img.height *= 3;
-  } else if (minScreenDim < 768) {
+  if (this.isMobileDevice_) {
     img.width *= 2;
     img.height *= 2;
   }
@@ -180,10 +179,14 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
   var isPanning = false;
   var dynamic = !this.isMobileDevice_ && !this.isUsingExcanvas_;
 
+  // We cover iframes during mouse interactions. See comments in
+  // dygraph-utils.js for more info on why this is a good idea.
+  var tarp = new Dygraph.IFrameTarp();
+
   // functions, defined below.  Defining them this way (rather than with
   // "function foo() {...}" makes JSHint happy.
   var toXDataWindow, onZoomStart, onZoom, onZoomEnd, doZoom, isMouseInPanZone,
-      onPanStart, onPan, onPanEnd, doPan, onCanvasMouseMove;
+      onPanStart, onPan, onPanEnd, doPan, onCanvasMouseMove, applyBrowserZoomLevel;
 
   // Touch event functions
   var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents;
@@ -196,6 +199,15 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     return [xDataMin, xDataMax];
   };
 
+  applyBrowserZoomLevel = function(delX) {
+    var zoom = window.outerWidth/document.documentElement.clientWidth;
+    if (!isNaN(zoom)) {
+      return delX/zoom;
+    } else {
+      return delX;
+    }
+  };
+
   onZoomStart = function(e) {
     Dygraph.cancelEvent(e);
     isZooming = true;
@@ -204,17 +216,24 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     self.dygraph_.addEvent(topElem, 'mousemove', onZoom);
     self.dygraph_.addEvent(topElem, 'mouseup', onZoomEnd);
     self.fgcanvas_.style.cursor = 'col-resize';
+    tarp.cover();
+    return true;
   };
 
   onZoom = function(e) {
     if (!isZooming) {
-      return;
+      return false;
     }
+    Dygraph.cancelEvent(e);
     var delX = e.screenX - xLast;
-    if (Math.abs(delX) < 4) {
-      return;
+    if (Math.abs(delX) < 4 || e.screenX === 0) {
+      // First iPad move event seems to have screenX = 0
+      return true;
     }
     xLast = e.screenX;
+    delX = applyBrowserZoomLevel(delX);
+
+    // Move handle.
     var zoomHandleStatus = self.getZoomHandleStatus_();
     var newPos;
     if (handle == self.leftZoomHandle_) {
@@ -234,13 +253,15 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     if (dynamic) {
       doZoom();
     }
+    return true;
   };
 
   onZoomEnd = function(e) {
     if (!isZooming) {
-      return;
+      return false;
     }
     isZooming = false;
+    tarp.uncover();
     Dygraph.removeEvent(topElem, 'mousemove', onZoom);
     Dygraph.removeEvent(topElem, 'mouseup', onZoomEnd);
     self.fgcanvas_.style.cursor = 'default';
@@ -249,6 +270,7 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     if (!dynamic) {
       doZoom();
     }
+    return true;
   };
 
   doZoom = function() {
@@ -285,20 +307,23 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
       xLast = e.screenX;
       self.dygraph_.addEvent(topElem, 'mousemove', onPan);
       self.dygraph_.addEvent(topElem, 'mouseup', onPanEnd);
+      return true;
     }
+    return false;
   };
 
   onPan = function(e) {
     if (!isPanning) {
-      return;
+      return false;
     }
     Dygraph.cancelEvent(e);
 
     var delX = e.screenX - xLast;
     if (Math.abs(delX) < 4) {
-      return;
+      return true;
     }
     xLast = e.screenX;
+    delX = applyBrowserZoomLevel(delX);
 
     // Move range view
     var zoomHandleStatus = self.getZoomHandleStatus_();
@@ -324,11 +349,12 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     if (dynamic) {
       doPan();
     }
+    return true;
   };
 
   onPanEnd = function(e) {
     if (!isPanning) {
-      return;
+      return false;
     }
     isPanning = false;
     Dygraph.removeEvent(topElem, 'mousemove', onPan);
@@ -337,6 +363,7 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
     if (!dynamic) {
       doPan();
     }
+    return true;
   };
 
   doPan = function() {
@@ -360,22 +387,28 @@ DygraphRangeSelector.prototype.initInteraction_ = function() {
   };
 
   onZoomHandleTouchEvent = function(e) {
-    e.preventDefault();
-    if (e.type == 'touchstart') {
-      onZoomStart(e.targetTouches[0]);
-    } else if (e.type == 'touchmove') {
-      onZoom(e.targetTouches[0]);
+    if (e.type == 'touchstart' && e.targetTouches.length == 1) {
+      if (onZoomStart(e.targetTouches[0])) {
+        Dygraph.cancelEvent(e);
+      }
+    } else if (e.type == 'touchmove' && e.targetTouches.length == 1) {
+      if (onZoom(e.targetTouches[0])) {
+        Dygraph.cancelEvent(e);
+      }
     } else {
       onZoomEnd(e);
     }
   };
 
   onCanvasTouchEvent = function(e) {
-    e.preventDefault();
-    if (e.type == 'touchstart') {
-      onPanStart(e.targetTouches[0]);
-    } else if (e.type == 'touchmove') {
-      onPan(e.targetTouches[0]);
+    if (e.type == 'touchstart' && e.targetTouches.length == 1) {
+      if (onPanStart(e.targetTouches[0])) {
+        Dygraph.cancelEvent(e);
+      }
+    } else if (e.type == 'touchmove' && e.targetTouches.length == 1) {
+      if (onPan(e.targetTouches[0])) {
+        Dygraph.cancelEvent(e);
+      }
     } else {
       onPanEnd(e);
     }
@@ -447,6 +480,8 @@ DygraphRangeSelector.prototype.drawMiniPlot_ = function() {
     return;
   }
 
+  var stepPlot = this.attr_('stepPlot');
+
   var combinedSeriesData = this.computeCombinedSeriesAndLimits_();
   var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin;
 
@@ -461,14 +496,36 @@ DygraphRangeSelector.prototype.drawMiniPlot_ = function() {
   var canvasWidth = this.canvasRect_.w - margin;
   var canvasHeight = this.canvasRect_.h - margin;
 
+  var prevX = null, prevY = null;
+
   ctx.beginPath();
   ctx.moveTo(margin, canvasHeight);
   for (var i = 0; i < combinedSeriesData.data.length; i++) {
     var dataPoint = combinedSeriesData.data[i];
-    var x = (dataPoint[0] - xExtremes[0])*xFact;
-    var y = canvasHeight - (dataPoint[1] - combinedSeriesData.yMin)*yFact;
+    var x = ((dataPoint[0] !== null) ? ((dataPoint[0] - xExtremes[0])*xFact) : NaN);
+    var y = ((dataPoint[1] !== null) ? (canvasHeight - (dataPoint[1] - combinedSeriesData.yMin)*yFact) : NaN);
     if (isFinite(x) && isFinite(y)) {
+      if(prevX === null) {
+        ctx.lineTo(x, canvasHeight);
+      }
+      else if (stepPlot) {
+        ctx.lineTo(x, prevY);
+      }
       ctx.lineTo(x, y);
+      prevX = x;
+      prevY = y;
+    }
+    else {
+      if(prevX !== null) {
+        if (stepPlot) {
+          ctx.lineTo(x, prevY);
+          ctx.lineTo(x, canvasHeight);
+        }
+        else {
+          ctx.lineTo(prevX, canvasHeight);
+        }
+      }
+      prevX = prevY = null;
     }
   }
   ctx.lineTo(canvasWidth, canvasHeight);