summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/geo/charting/TouchInteractionSupport.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/geo/charting/TouchInteractionSupport.js')
-rw-r--r--js/dojo/dojox/geo/charting/TouchInteractionSupport.js313
1 files changed, 313 insertions, 0 deletions
diff --git a/js/dojo/dojox/geo/charting/TouchInteractionSupport.js b/js/dojo/dojox/geo/charting/TouchInteractionSupport.js
new file mode 100644
index 0000000..6e2b7cd
--- /dev/null
+++ b/js/dojo/dojox/geo/charting/TouchInteractionSupport.js
@@ -0,0 +1,313 @@
+//>>built
+define("dojox/geo/charting/TouchInteractionSupport", ["dojo/_base/lang","dojo/_base/declare","dojo/_base/event", "dojo/_base/connect","dojo/_base/window"],
+ function(lang,declare,event,connect,win) {
+
+return declare("dojox.geo.charting.TouchInteractionSupport",null, {
+ // summary:
+ // class to handle touch interactions on a dojox.geo.charting.Map widget
+ // tags:
+ // private
+
+ _map : null,
+ _centerTouchLocation : null,
+ _touchMoveListener: null,
+ _touchEndListener: null,
+ _touchEndTapListener: null,
+ _touchStartListener: null,
+ _initialFingerSpacing: null,
+ _initialScale: null,
+ _tapCount: null,
+ _tapThreshold: null,
+ _lastTap: null,
+ _doubleTapPerformed:false,
+ _oneFingerTouch:false,
+ _tapCancel:false,
+
+ constructor : function(/* dojox.geo.charting.Map */map,options) {
+ // summary:
+ // Constructs a new _TouchInteractionSupport instance
+ // map: dojox.geo.charting.Map
+ // the Map widget this class provides touch navigation for.
+ this._map = map;
+ this._centerTouchLocation = {x: 0,y: 0};
+
+ this._tapCount = 0;
+ this._lastTap = {x: 0,y: 0};
+ this._tapThreshold = 100; // square distance in pixels
+ },
+
+ connect: function() {
+ // summary:
+ // install touch listeners
+ _touchStartListener = this._map.surface.connect("touchstart", this, this._touchStartHandler);
+ },
+
+ disconnect: function() {
+ // summary:
+ // disconnects any installed listeners. Must be called only when disposing of this instance
+ if (this._touchStartListener) {
+ connect.disconnect(this._touchStartListener);
+ this._touchStartListener = null;
+ }
+ },
+
+ _getTouchBarycenter: function(touchEvent) {
+ // summary:
+ // returns the midpoint of the two first fingers (or the first finger location if only one)
+ // touchEvent: a touch event
+ // returns: dojox.gfx.Point
+ // the midpoint
+ // tags:
+ // private
+ var touches = touchEvent.touches;
+ var firstTouch = touches[0];
+ var secondTouch = null;
+ if (touches.length > 1) {
+ secondTouch = touches[1];
+ } else {
+ secondTouch = touches[0];
+ }
+ var containerBounds = this._map._getContainerBounds();
+ var middleX = (firstTouch.pageX + secondTouch.pageX) / 2.0 - containerBounds.x;
+ var middleY = (firstTouch.pageY + secondTouch.pageY) / 2.0 - containerBounds.y;
+ return {x: middleX,y: middleY};
+ },
+
+ _getFingerSpacing: function(touchEvent) {
+ // summary:
+ // computes the distance between the first two fingers
+ // touchEvent: a touch event
+ // returns: float
+ // a distance. -1 if less that 2 fingers
+ // tags:
+ // private
+ var touches = touchEvent.touches;
+ var spacing = -1;
+ if (touches.length >= 2) {
+ var dx = (touches[1].pageX - touches[0].pageX);
+ var dy = (touches[1].pageY - touches[0].pageY);
+ spacing = Math.sqrt(dx*dx + dy*dy);
+ }
+ return spacing;
+ },
+
+ _isDoubleTap: function(touchEvent) {
+ // summary:
+ // checks whether the specified touchStart event is a double tap
+ // (i.e. follows closely a previous touchStart at approximately the same location)
+ // touchEvent: a touch event
+ // returns: boolean
+ // true if this event is considered a double tap
+ // tags:
+ // private
+ var isDoubleTap = false;
+ var touches = touchEvent.touches;
+ if ((this._tapCount > 0) && touches.length == 1) {
+ // test distance from last tap
+ var dx = (touches[0].pageX - this._lastTap.x);
+ var dy = (touches[0].pageY - this._lastTap.y);
+ var distance = dx*dx + dy*dy;
+ if (distance < this._tapThreshold) {
+ isDoubleTap = true;
+ } else {
+ this._tapCount = 0;
+ }
+ }
+ this._tapCount++;
+ this._lastTap.x = touches[0].pageX;
+ this._lastTap.y = touches[0].pageY;
+ setTimeout(lang.hitch(this,function() {
+ this._tapCount = 0;}),300);
+ return isDoubleTap;
+ },
+
+ _doubleTapHandler: function(touchEvent) {
+ // summary:
+ // action performed on the map when a double tap was triggered
+ // touchEvent: a touch event
+ // tags:
+ // private
+ var feature = this._getFeatureFromTouchEvent(touchEvent);
+ if (feature) {
+ this._map.fitToMapArea(feature._bbox, 15, true);
+ } else {
+ // perform a basic 2x zoom on touch
+ var touches = touchEvent.touches;
+ var containerBounds = this._map._getContainerBounds();
+ var offX = touches[0].pageX - containerBounds.x;
+ var offY = touches[0].pageY - containerBounds.y;
+ // clicked map point before zooming
+ var mapPoint = this._map.screenCoordsToMapCoords(offX,offY);
+ // zoom increment power
+ this._map.setMapCenterAndScale(mapPoint.x, mapPoint.y,this._map.getMapScale()*2,true);
+ }
+ },
+
+ _getFeatureFromTouchEvent: function(touchEvent) {
+ // summary:
+ // utility function to return the feature located at this touch event location
+ // touchEvent: a touch event
+ // returns: dojox.geo.charting.Feature
+ // the feature found if any, null otherwise.
+ // tags:
+ // private
+ var feature = null;
+ if (touchEvent.gfxTarget && touchEvent.gfxTarget.getParent) {
+ feature = this._map.mapObj.features[touchEvent.gfxTarget.getParent().id];
+ }
+ return feature;
+ },
+
+ _touchStartHandler: function(touchEvent){
+ // summary:
+ // action performed on the map when a touch start was triggered
+ // touchEvent: a touch event
+ // tags:
+ // private
+ event.stop(touchEvent);
+ this._oneFingerTouch = (touchEvent.touches.length == 1);
+ this._tapCancel = !this._oneFingerTouch;
+ // test double tap
+ this._doubleTapPerformed = false;
+ if (this._isDoubleTap(touchEvent)) {
+ //console.log("double tap recognized");
+ this._doubleTapHandler(touchEvent);
+ this._doubleTapPerformed = true;
+ return;
+ }
+ // compute map midpoint between fingers
+ var middlePoint = this._getTouchBarycenter(touchEvent);
+ var mapPoint = this._map.screenCoordsToMapCoords(middlePoint.x,middlePoint.y);
+ this._centerTouchLocation.x = mapPoint.x;
+ this._centerTouchLocation.y = mapPoint.y;
+ // store initial finger spacing to compute zoom later
+ this._initialFingerSpacing = this._getFingerSpacing(touchEvent);
+ // store initial map scale
+ this._initialScale = this._map.getMapScale();
+ // install touch move and up listeners (if not done by other fingers before)
+ if (!this._touchMoveListener)
+ this._touchMoveListener = connect.connect(win.global,"touchmove",this,this._touchMoveHandler);
+ if (!this._touchEndTapListener)
+ this._touchEndTapListener = this._map.surface.connect("touchend", this, this._touchEndTapHandler);
+ if (!this._touchEndListener)
+ this._touchEndListener = connect.connect(win.global,"touchend",this, this._touchEndHandler);
+ },
+
+ _touchEndTapHandler: function(touchEvent) {
+ // summary:
+ // action performed on the map when a tap was triggered
+ // touchEvent: a touch event
+ // tags:
+ // private
+ var touches = touchEvent.touches;
+ if (touches.length == 0) {
+
+ // test potential tap ?
+ if (this._oneFingerTouch && !this._tapCancel) {
+ this._oneFingerTouch = false;
+ setTimeout(lang.hitch(this,function() {
+ // wait to check if double tap
+ // perform test for single tap
+ //console.log("double tap was performed ? " + this._doubleTapPerformed);
+ if (!this._doubleTapPerformed) {
+ // test distance from last tap
+ var dx = (touchEvent.changedTouches[0].pageX - this._lastTap.x);
+ var dy = (touchEvent.changedTouches[0].pageY - this._lastTap.y);
+ var distance = dx*dx + dy*dy;
+ if (distance < this._tapThreshold) {
+ // single tap ok
+ this._singleTapHandler(touchEvent);
+ }
+ }
+ }),350);
+ }
+ this._tapCancel = false;
+
+ }
+ },
+
+ _touchEndHandler: function(touchEvent) {
+ // summary:
+ // action performed on the map when a touch end was triggered
+ // touchEvent: a touch event
+ // tags:
+ // private
+ event.stop(touchEvent);
+ var touches = touchEvent.touches;
+ if (touches.length == 0) {
+ // disconnect listeners only when all fingers are up
+ if (this._touchMoveListener) {
+ connect.disconnect(this._touchMoveListener);
+ this._touchMoveListener = null;
+ }
+ if (this._touchEndListener) {
+ connect.disconnect(this._touchEndListener);
+ this._touchEndListener = null;
+ }
+ } else {
+ // recompute touch center
+ var middlePoint = this._getTouchBarycenter(touchEvent);
+ var mapPoint = this._map.screenCoordsToMapCoords(middlePoint.x,middlePoint.y);
+ this._centerTouchLocation.x = mapPoint.x;
+ this._centerTouchLocation.y = mapPoint.y;
+ }
+ },
+
+ _singleTapHandler: function(touchEvent) {
+ // summary:
+ // action performed on the map when a single tap was triggered
+ // touchEvent: a touch event
+ // tags:
+ // private
+ var feature = this._getFeatureFromTouchEvent(touchEvent);
+ if (feature) {
+ // call feature handler
+ feature._onclickHandler(touchEvent);
+ } else {
+ // unselect all
+ for (var name in this._map.mapObj.features){
+ this._map.mapObj.features[name].select(false);
+ }
+ this._map.onFeatureClick(null);
+ }
+ },
+
+ _touchMoveHandler: function(touchEvent){
+ // summary:
+ // action performed on the map when a touch move was triggered
+ // touchEvent: a touch event
+ // tags:
+ // private
+
+ // prevent browser interaction
+ event.stop(touchEvent);
+
+ // cancel tap if moved too far from first touch location
+ if (!this._tapCancel) {
+ var dx = (touchEvent.touches[0].pageX - this._lastTap.x),
+ dy = (touchEvent.touches[0].pageY - this._lastTap.y);
+ var distance = dx*dx + dy*dy;
+ if (distance > this._tapThreshold) {
+ this._tapCancel = true;
+ }
+ }
+ var middlePoint = this._getTouchBarycenter(touchEvent);
+ // compute map offset
+ var mapPoint = this._map.screenCoordsToMapCoords(middlePoint.x,middlePoint.y),
+ mapOffsetX = mapPoint.x - this._centerTouchLocation.x,
+ mapOffsetY = mapPoint.y - this._centerTouchLocation.y;
+ // compute scale factor
+ var scaleFactor = 1;
+ var touches = touchEvent.touches;
+ if (touches.length >= 2) {
+ var fingerSpacing = this._getFingerSpacing(touchEvent);
+ scaleFactor = fingerSpacing / this._initialFingerSpacing;
+ // scale map
+ this._map.setMapScale(this._initialScale*scaleFactor);
+ }
+ // adjust map center on barycentre
+ var currentMapCenter = this._map.getMapCenter();
+ this._map.setMapCenter(currentMapCenter.x - mapOffsetX, currentMapCenter.y - mapOffsetY);
+ }
+});
+});