X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph-range-selector.js;h=b777470e69ad656977b5ec9cf99e2e7d7a49ec65;hb=600da35ccf7fda98f16ba16037c80451ebca9cf5;hp=9ae04d0270c2f0c5cff74fdeaa49464ba6c29bb9;hpb=2996a18eb00729b2624d8d75e8d24b056c5732bc;p=dygraphs.git diff --git a/dygraph-range-selector.js b/dygraph-range-selector.js index 9ae04d0..b777470 100644 --- a/dygraph-range-selector.js +++ b/dygraph-range-selector.js @@ -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 @@ -19,6 +22,8 @@ var DygraphRangeSelector = function(dygraph) { this.isIE_ = /MSIE/.test(navigator.userAgent) && !window.opera; this.isUsingExcanvas_ = dygraph.isUsingExcanvas_; this.dygraph_ = dygraph; + this.hasTouchInterface_ = typeof(TouchEvent) != 'undefined'; + this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion); this.createCanvases_(); if (this.isUsingExcanvas_) { this.createIEPanOverlay_(); @@ -75,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, @@ -135,15 +140,16 @@ DygraphRangeSelector.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'; + if (/MSIE 7/.test(navigator.userAgent)) { // IE7 doesn't support embedded src data. - img.width = 7; - img.height = 14; - img.style.backgroundColor = 'white'; - img.style.border = '1px solid #333333'; // Just show box in IE7. + img.width = 7; + img.height = 14; + img.style.backgroundColor = 'white'; + img.style.border = '1px solid #333333'; // Just show box in IE7. } else { - img.width = 9; - img.height = 16; - img.src = 'data:image/png;base64,' + + img.width = 9; + img.height = 16; + img.src = 'data:image/png;base64,' + 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAA' + 'zwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENv' + 'bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl' + @@ -151,6 +157,11 @@ DygraphRangeSelector.prototype.createZoomHandles_ = function() { 'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII='; } + if (this.isMobileDevice_) { + img.width *= 2; + img.height *= 2; + } + this.leftZoomHandle_ = img; this.rightZoomHandle_ = img.cloneNode(false); }; @@ -166,11 +177,19 @@ DygraphRangeSelector.prototype.initInteraction_ = function() { var handle = null; var isZooming = false; 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; toXDataWindow = function(zoomHandleStatus) { var xDataLimits = self.dygraph_.xAxisExtremes(); @@ -180,25 +199,41 @@ 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; xLast = e.screenX; handle = e.target ? e.target : e.srcElement; - Dygraph.addEvent(topElem, 'mousemove', onZoom); - Dygraph.addEvent(topElem, 'mouseup', onZoomEnd); + 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_) { @@ -215,24 +250,27 @@ DygraphRangeSelector.prototype.initInteraction_ = function() { self.drawInteractiveLayer_(); // Zoom on the fly (if not using excanvas). - if (!self.isUsingExcanvas_) { + 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'; // If using excanvas, Zoom now. - if (self.isUsingExcanvas_) { + if (!dynamic) { doZoom(); } + return true; }; doZoom = function() { @@ -254,15 +292,11 @@ DygraphRangeSelector.prototype.initInteraction_ = function() { if (self.isUsingExcanvas_) { return e.srcElement == self.iePanOverlay_; } else { - // Getting clientX directly from the event is not accurate enough :( - var clientX; - if (e.offsetX != undefined) { - clientX = self.canvasRect_.x + e.offsetX; - } else { - clientX = e.clientX; - } - var zoomHandleStatus = self.getZoomHandleStatus_(); - return (clientX > zoomHandleStatus.leftHandlePos && clientX < zoomHandleStatus.rightHandlePos); + 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); } }; @@ -271,22 +305,25 @@ DygraphRangeSelector.prototype.initInteraction_ = function() { Dygraph.cancelEvent(e); isPanning = true; xLast = e.screenX; - Dygraph.addEvent(topElem, 'mousemove', onPan); - Dygraph.addEvent(topElem, 'mouseup', onPanEnd); + 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_(); @@ -309,22 +346,24 @@ DygraphRangeSelector.prototype.initInteraction_ = function() { self.drawInteractiveLayer_(); // Do pan on the fly (if not using excanvas). - if (!self.isUsingExcanvas_) { + if (dynamic) { doPan(); } + return true; }; onPanEnd = function(e) { if (!isPanning) { - return; + return false; } isPanning = false; Dygraph.removeEvent(topElem, 'mousemove', onPan); Dygraph.removeEvent(topElem, 'mouseup', onPanEnd); // If using excanvas, do pan now. - if (self.isUsingExcanvas_) { + if (!dynamic) { doPan(); } + return true; }; doPan = function() { @@ -347,19 +386,61 @@ DygraphRangeSelector.prototype.initInteraction_ = function() { } }; + onZoomHandleTouchEvent = function(e) { + 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) { + 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); + } + }; + + addTouchEvents = function(elem, fn) { + var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel']; + for (var i = 0; i < types.length; i++) { + self.dygraph_.addEvent(elem, types[i], fn); + } + }; + this.dygraph_.attrs_.interactionModel = Dygraph.Interaction.dragIsPanInteractionModel; this.dygraph_.attrs_.panEdgeFraction = 0.0001; var dragStartEvent = window.opera ? 'mousedown' : 'dragstart'; - Dygraph.addEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart); - Dygraph.addEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart); + this.dygraph_.addEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart); + this.dygraph_.addEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart); if (this.isUsingExcanvas_) { - Dygraph.addEvent(this.iePanOverlay_, 'mousedown', onPanStart); + this.dygraph_.addEvent(this.iePanOverlay_, 'mousedown', onPanStart); } else { - Dygraph.addEvent(this.fgcanvas_, 'mousedown', onPanStart); - Dygraph.addEvent(this.fgcanvas_, 'mousemove', onCanvasMouseMove); + this.dygraph_.addEvent(this.fgcanvas_, 'mousedown', onPanStart); + this.dygraph_.addEvent(this.fgcanvas_, 'mousemove', onCanvasMouseMove); + } + + // Touch events + if (this.hasTouchInterface_) { + addTouchEvents(this.leftZoomHandle_, onZoomHandleTouchEvent); + addTouchEvents(this.rightZoomHandle_, onZoomHandleTouchEvent); + addTouchEvents(this.fgcanvas_, onCanvasTouchEvent); } }; @@ -399,6 +480,8 @@ DygraphRangeSelector.prototype.drawMiniPlot_ = function() { return; } + var stepPlot = this.attr_('stepPlot'); + var combinedSeriesData = this.computeCombinedSeriesAndLimits_(); var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin; @@ -413,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); @@ -454,14 +559,14 @@ DygraphRangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() { var combinedSeries = []; var sum; var count; - var yVal, y; var mutipleValues; var i, j, k; + var xVal, yVal; // Find out if data has multiple values per datapoint. // Go to first data point that actually has values (see http://code.google.com/p/dygraphs/issues/detail?id=246) for (i = 0; i < data.length; i++) { - if (data[i].length > 1 && data[i][1] != null) { + if (data[i].length > 1 && data[i][1] !== null) { mutipleValues = typeof data[i][1] != 'number'; if (mutipleValues) { sum = []; @@ -477,7 +582,7 @@ DygraphRangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() { for (i = 0; i < data.length; i++) { var dataPoint = data[i]; - var xVal = dataPoint[0]; + xVal = dataPoint[0]; if (mutipleValues) { for (k = 0; k < sum.length; k++) { @@ -489,6 +594,7 @@ DygraphRangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() { for (j = 1; j < dataPoint.length; j++) { if (this.dygraph_.visibility()[j-1]) { + var y; if (mutipleValues) { for (k = 0; k < sum.length; k++) { y = dataPoint[j][k];