2018-08-22 17:27:12 +08:00

1534 lines
58 KiB
JavaScript
Raw Blame History

/*jslint undef: true, browser: true, continue: true, eqeq: true, vars: true, forin: true, white: true, newcap: false, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
/**
* jGestures: a jQuery plugin for gesture events
* Copyright 2010-2011 Neue Digitale / Razorfish GmbH
* Copyright 2011-2012, Razorfish GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @fileOverview
* Razorfish GmbH javascript library: add touch events such as 'pinch',
* 'rotate', 'swipe', 'tap' and 'orientationchange' on capable user agents.
* For incapable devices there's a basic event substitution: a "tapone" event
* can be triggered by "clicking", a "swipeone" by performing a swipe-ish
* gesture using the mouse (buttondown - mousemove - buttonup).
*
* This is still a beta version, bugfixes and improvements appreciated.
*
* @author martin.krause@razorfish.de
* @version 0.90-shake
*
* @requires
* jQuery JavaScript Library v1.4.2 - http://jquery.com/
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* @example jQuery('#swipe').bind('swipeone',eventHandler);
*
* Notification on native events:
* On every native touchstart, touchend, gesturestart and gestureend-event,
* jgestures triggers a corresponding custom event
* ('jGestures.touchstart', 'jGestures.touchend;start', 'jGestures.touchend;processed',
* 'jGestures.gesturestart', 'jGestures.gestureend;start', 'jGestures.gestureend;processed') on the event-element.
* The eventhandler's second argument represents the original touch event (yes: including all touchpoints).
* Use this if you need very detailed control e.g. kinetic scrolling or implementing additional gestures.
*
* Every jGesture-eventhandler receives a custom object as second argument
* containing the original event (originalEvent property) and processed
* information (such as delta values and timesptamp).
* Example:{
* type: eventtype e.g. "swipe","pinch",
* originalEvent: {DOM-Event},
* // default: just one entry on the delta-array - the first touchpoint
* // the first touchpoint is the reference point for every gesture,
* // because moving touchpoints in various directions would result in
* // a gesture.
* // delta and direction details are just provided for touch not for gesture / motion events
* delta : [
* {
* lastX:{Number} , // x-axis: relative to the last touchevent (e.g. touchmove!)
* lastY:{Number}, // y-axis: relative to the last touchevent (e.g. touchmove!)
* moved: {Number}, // distance: relative to the original touchpoint
* startX: {Number} , // relative to the original touchpoint
* startY: {Number} ,// relative to the original touchpoint
* } ],
* // based on the first touchpoint
* direction : { // relative to the last touchevent (e.g. touchmove!)
* vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
* orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
* lastX : {Number}, // -1,0,+1 || null (orientationchange) // relative to the last touchevent (e.g. touchmove!)
* lastY : {Number}, // -1,0,+1 || null (orientationchange)// relative to the last touchevent (e.g. touchmove!)
* startX: {Number} , // relative to the original touchpoint
* startY: {Number} ,// relative to the original touchpoint
* },
* rotation: {Number} || null, // gestureonly: amount of rotation relative to the current position NOT the original
* scale: {Number} || null, // gestureonly: amount of scaling relative to the current position NOT the original
* duration: {Number}, // ms: relative to the original touchpoint
* description : {String} // details as String: {TYPE *}:{TOUCHES 1|2|3|4}:{X-AXIS 'right'|'left'|'steady'}:{Y-AXIS 'down'|'up'|'steady'} e.g. "swipe:1:left:steady" relative to the last touchpoint
* };
*
* Available jGesture-events can be grouped into:
*
*
* Device events:
* The jGesture-Events in this group are triggered by the device.
*
* @event 'orientationchange'
* The device is turned clockwise or counterclockwise. This event is triggered
* by the device and might use an internal gyroscope.
* obj.description:
* orientationchange:landscape:clockwise:-90
* orientationchange:portrait:default:0
* orientationchange:landscape:counterclockwise|portrait:90
* orientationchange:portrait:upsidedown:180
*
*
* Move events:
* The jGesture-Events in this group are triggered during the touch/gesture
* execution whenever a touchpoint changes.
* In contrast to touchend/gestureend-events which are triggered after
* the touch/gesture has completed.
*
* @event 'pinch'
* Is triggered during a pinch gesture (two fingers moving away from or
* towards each other).
* obj.description:
* pinch:-1:close
* pinch:+1:open
*
* @event 'rotate'
* Is triggered during a rotation gesture (two fingers rotating clockwise
* or counterclockwise).
* obj.description:
* rotate:-1:counterclockwise
* rotate:+1:+clockwise
*
* @event 'swipemove'
* Is triggered during a swipe move gesture (finger(s) being moved around
* the device, e.g. dragging)
* obj.description:
* swipemove:1:left:down
* swipemove:1:left:up
* swipemove:1:left:steady
* swipemove:1:right:down
* swipemove:1:right:up
* swipemove:1:right:steady
* swipemove:2:left:down
* swipemove:2:left:up
* swipemove:2:left:steady
* swipemove:2:right:down
* swipemove:2:right:up
* swipemove:2:right:steady
* swipemove:2:left:down
* swipemove:3:left:up
* swipemove:3:left:steady
* swipemove:3:right:down
* swipemove:3:right:up
* swipemove:3:right:steady
* swipemove:3:left:down
* swipemove:4:left:up
* swipemove:4:left:steady
* swipemove:4:right:down
* swipemove:4:right:up
* swipemove:4:right:steady
*
*
* Toucheend events:
* The jGesture-Events in this group are triggered after the touch/gesture
* has completed.
* In contrast to touchmove-events which are triggered during the touch/gesture
* execution whenever a touchpoint changes.
*
* @event 'swipeone'
* Is triggered after a swipe move gesture with one touchpoint (one finger
* was moved around the device)
* obj.description:
* swipeone:1:left:down
* swipeone:1:left:up
* swipeone:1:left:steady
* swipeone:1:right:down
* swipeone:1:right:up
* swipeone:1:right:steady
*
* @event 'swipetwo'
* Is triggered after a swipe move gesture with two touchpoints (two fingers
* were moved around the device)
* obj.description:
* swipetwo:2:left:down
* swipetwo:2:left:up
* swipetwo:2:left:steady
* swipetwo:2:right:down
* swipetwo:2:right:up
* swipetwo:2:right:steady
*
* @event 'swipethree'
* Is triggered after a swipe move gesture with three touchpoints (three
* fingers were moved around the device)
* obj.description:
* swipethree:3:left:down
* swipethree:3:left:up
* swipethree:3:left:steady
* swipethree:3:right:down
* swipethree:3:right:up
* swipethree:3:right:steady
*
* @event 'swipefour'
* Is triggered after a swipe move gesture with four touchpoints (four
* fingers were moved around the device)
* obj.description:
* swipefour:4:left:down
* swipefour:4:left:up
* swipefour:4:left:steady
* swipefour:4:right:down
* swipefour:4:right:up
* swipefour:4:right:steady
*
*
* @event 'swipeup'
* Is triggered after an strict upwards swipe move gesture
* obj.description:
* swipe:1:steady:up
* swipe:2:steady:up
* swipe:3:steady:up
* swipe:4:steady:up
*
* @event 'swiperightup'
* Is triggered after a rightwards and upwards swipe move gesture
* obj.description:
* swipe:1:right:up
* swipe:2:right:up
* swipe:3:right:up
* swipe:4:right:up
*
* @event 'swiperight'
* Is triggered after a strict rightwards swipe move gesture
* obj.description:
* swipe:1:right:steady
* swipe:2:right:steady
* swipe:3:right:steady
* swipe:4:right:steady
*
* @event 'swiperightdown'
* Is triggered after a rightwards and downwards swipe move gesture
* obj.description:
* swipe:1:right:down
* swipe:2:right:down
* swipe:3:right:down
* swipe:4:right:down
*
* @event 'swipedown'
* Is triggered after a strict downwards swipe move gesture
* obj.description:
* swipe:1:steady:down
* swipe:2:steady:down
* swipe:3:steady:down
* swipe:4:steady:down
*
* @event 'swipeleftdown'
* Is triggered after a leftwards and downwards swipe move gesture
* obj.description:
* swipe:1:left:down
* swipe:2:left:down
* swipe:3:left:down
* swipe:4:left:down
*
* @event 'swipeleft'
* Is triggered after a strict leftwards swipe move gesture
* obj.description:
* swipe:1:left:steady
* swipe:2:left:steady
* swipe:3:left:steady
* swipe:4:left:steady
*
* @event 'swipeleftup'
* Is triggered after a leftwards and upwards swipe move gesture
* obj.description:
* swipe:1:left:up
* swipe:2:left:up
* swipe:3:left:up
* swipe:4:left:up
*
* @event 'tapone'
* Is triggered after a single (one finger) tap gesture
* obj.description:
* tapone
*
* @event 'taptwo'
* Is triggered after a double (two finger) tap gesture
* obj.description:
* taptwo
* *
* @event 'tapthree'
* Is triggered after a tripple (three finger) tap gesture
* obj.description:
* tapthree
*
*
* Gestureend events:
* A gesture is an interpretation of different touchpoints.
* The jGesture-Events in this group are triggered when a gesture has finished
* and the touchpoints are removed from the device.
*
* @event 'pinchopen'
* Is triggered when a pinchopen gesture (two fingers moving away from each
* other) occured and the touchpoints (fingers) are removed the device.
* obj.description:
* pinch:+1:open
*
* @event 'pinchclose'
* Is triggered when a pinchclose gesture (two fingers moving towards each
* other) occured and the touchpoints (fingers) are removed the device.
* obj.description:
* pinch:-1:close
*
* @event 'rotatecw'
* Is triggered when a clockwise rotation gesture (two fingers rotating
* clockwise) occured and the touchpoints (fingers) are removed the device.
* obj.description:
* rotate:+1:+clockwise
*
* @event 'rotateccw'
* Is triggered when a counterclockwise rotation gesture (two fingers
* rotating counterclockwise) occured and the touchpoints (fingers) are
* removed the device.
* obj.description:
* rotate:-1:+counterclockwise
*
*
* Motion events:
* A "motion event" is an interpretation of changes in space, e.g. a "shaking motion"
* consists of a specified number of acceleration changes in a given interval.
* For understanding "directions", place your mobile device on a table with the bottom
* (home button) close to you:
* - x-axis: horizontal left / right
* - y-axis: horizontal front / back (through the home button)
* - z-axis: vertical through your device
*
* Note: Devicemotion / deviceorientation don't send custom event (such as: jGestures.touchstart).
* Note: Devicemotion should be bound on the "window-element" - because the whole device moves
*
* @event 'shake'
* Is triggered when a shaking motion is detected
* obj.description:
* shake:leftright:x-axisfrontback:y-axis:updown:z-axis
*
* @event 'shakefrontback'
* Is triggered when a shaking motion is detected and the gesture can be interpreted as a mainly front-back movement.
* obj.description:
* shakefrontback:shakefrontback:y-axis
*
* @event 'shakeleftright'
* Is triggered when a shaking motion is detected and the gesture can be interpreted as a mainly left-right movement.
* Additional major movements are mentioned in the obj.description.
* obj.description:
* shakeleftright:leftright:x-axis
*
* @event 'shakeupdown'
* Is triggered when a shaking motion is detected and the gesture can be interpreted as a mainly up-down movement.
* Additional major movements are mentioned in the obj.description.
* obj.description:
* shake:shakeupdown:updown:z-axis
*
* @example
* .bind( eventType, [ eventData ], handler(eventObject) )
* jQuery('body').bind('tapone',function(){alert(arguments[1].description);})
*
*/
(function($) {
/**
* General thresholds.
*/
// @TODO: move to $...defaults
// @TODO: shake to defaults freeze etc
// change of x deg in y ms
$.jGestures = {};
$.jGestures.defaults = {};
$.jGestures.defaults.thresholdShake = {
requiredShakes : 10,
freezeShakes: 100,
frontback : {
sensitivity: 10
},
leftright : {
sensitivity: 10
},
updown : {
sensitivity: 10
}
};
$.jGestures.defaults.thresholdPinchopen = 0.05;
$.jGestures.defaults.thresholdPinchmove = 0.05;
$.jGestures.defaults.thresholdPinch = 0.05;
$.jGestures.defaults.thresholdPinchclose = 0.05;
$.jGestures.defaults.thresholdRotatecw = 5; //deg
$.jGestures.defaults.thresholdRotateccw = 5; // deg
// a tap becomes a swipe if x/y values changes are above this threshold
$.jGestures.defaults.thresholdMove = 20;
$.jGestures.defaults.thresholdSwipe = 100;
// get capable user agents
$.jGestures.data = {};
$.jGestures.data.capableDevicesInUserAgentString = ['iPad','iPhone','iPod','Mobile Safari']; // basic functionality such as swipe, pinch, rotate, tap should work on every mobile safari, e.g. GalaxyTab
$.jGestures.data.hasGestures = (function () { var _i; for(_i = 0; _i < $.jGestures.data.capableDevicesInUserAgentString.length; _i++ ) { if (navigator.userAgent.indexOf($.jGestures.data.capableDevicesInUserAgentString[_i]) !== -1 ) {return true;} } return false; } )();
$.hasGestures = $.jGestures.data.hasGestures;
$.jGestures.events = {
touchstart : 'jGestures.touchstart',
touchendStart: 'jGestures.touchend;start',
touchendProcessed: 'jGestures.touchend;processed',
gesturestart: 'jGestures.gesturestart',
gestureendStart: 'jGestures.gestureend;start',
gestureendProcessed: 'jGestures.gestureend;processed'
};
jQuery
.each({
// "first domevent necessary"_"touch event+counter" : "exposed as"
// event: orientationchange
orientationchange_orientationchange01: "orientationchange",
// event: gestures
gestureend_pinchopen01: "pinchopen",
gestureend_pinchclose01: "pinchclose",
gestureend_rotatecw01 : 'rotatecw',
gestureend_rotateccw01 : 'rotateccw',
// move events
gesturechange_pinch01: 'pinch',
gesturechange_rotate01: 'rotate',
touchstart_swipe13: 'swipemove',
// event: touches
touchstart_swipe01: "swipeone",
touchstart_swipe02: "swipetwo",
touchstart_swipe03: "swipethree",
touchstart_swipe04: "swipefour",
touchstart_swipe05: 'swipeup',
touchstart_swipe06: 'swiperightup',
touchstart_swipe07: 'swiperight',
touchstart_swipe08: 'swiperightdown',
touchstart_swipe09: 'swipedown',
touchstart_swipe10: 'swipeleftdown',
touchstart_swipe11: 'swipeleft',
touchstart_swipe12: 'swipeleftup',
touchstart_tap01: 'tapone',
touchstart_tap02: 'taptwo',
touchstart_tap03: 'tapthree',
touchstart_tap04: 'tapfour',
devicemotion_shake01: 'shake',
devicemotion_shake02: 'shakefrontback',
devicemotion_shake03: 'shakeleftright',
devicemotion_shake04: 'shakeupdown'
},
/**
* Add gesture events inside the jQuery.event.special namespace
*/
function( sInternal_, sPublicFN_ ) {
// add as funciton to jQuery.event.special.sPublicFN_
jQuery.event.special[ sPublicFN_ ] = {
/**
* When the first event handler is bound, jQuery executes the setup function.
* This plugin just uses one eventhandler per element, regardless of the number of bound events.
* All Events are stored internally as properties on the dom-element using the $.data api.
* The setup-function adds the eventlistener, acting as a proxy function for the internal events.
* $.data.ojQueryGestures[_sDOMEvent ('tap') ] = {Boolean}
* @return {Void}
*/
setup: function () {
// split the arguments to necessary controll arguements
var _aSplit = sInternal_.split('_');
var _sDOMEvent = _aSplit[0]; //
// get the associated gesture event and strip the counter: necessary for distinguisihng similliar events such as tapone-tapfour
var _sGestureEvent = _aSplit[1].slice(0,_aSplit[1].length-2);
var _$element = jQuery(this);
var _oDatajQueryGestures ;
var oObj;
// bind the event handler on the first $.bind() for a gestureend-event, set marker
if (!_$element.data('ojQueryGestures') || !_$element.data('ojQueryGestures')[_sDOMEvent]) {
// setup pseudo event
_oDatajQueryGestures = _$element.data('ojQueryGestures') || {};
oObj = {};
// marker for: domEvent being set on this element
// e.g.: $.data.oGestureInternals['touchstart'] = true;
// since they're grouped, i'm just marking the first one being added
oObj[_sDOMEvent] = true;
$.extend(true,_oDatajQueryGestures,oObj);
_$element.data('ojQueryGestures' ,_oDatajQueryGestures);
// add gesture events
if($.hasGestures) {
switch(_sGestureEvent) {
// event: orientationchange
case 'orientationchange':
_$element.get(0).addEventListener('orientationchange', _onOrientationchange, false);
break;
// event:
// - shake
// - tilt
case 'shake':
case 'shakefrontback':
case 'shakeleftright':
case 'shakeupdown':
case 'tilt':
//$.hasGyroscope = true //!window.DeviceOrientationEvent;
//_$element.get(0).addEventListener('devicemotion', _onDevicemotion, false);
//_$element.get(0).addEventListener('deviceorientation', _onDeviceorientation, false);
_$element.get(0).addEventListener('devicemotion', _onDevicemotion, false);
break;
// event:
// - touchstart
// - touchmove
// - touchend
case 'tap':
case 'swipe':
case 'swipeup':
case 'swiperightup':
case 'swiperight':
case 'swiperightdown':
case 'swipedown':
case 'swipeleftdown':
case 'swipeleft':
_$element.get(0).addEventListener('touchstart', _onTouchstart, false);
break;
// event: gestureend
case 'pinchopen':
case 'pinchclose' :
case 'rotatecw' :
case 'rotateccw' :
_$element.get(0).addEventListener('gesturestart', _onGesturestart, false);
_$element.get(0).addEventListener('gestureend', _onGestureend, false);
break;
// event: gesturechange
case 'pinch':
case 'rotate':
_$element.get(0).addEventListener('gesturestart', _onGesturestart, false);
_$element.get(0).addEventListener('gesturechange', _onGesturechange, false);
break;
}
}
// create substitute for gesture events
else {
switch(_sGestureEvent) {
// event substitutes:
// - touchstart: mousedown
// - touchmove: none
// - touchend: mouseup
case 'tap':
case 'swipe':
// _$element.get(0).addEventListener('mousedown', _onTouchstart, false);
_$element.bind('mousedown', _onTouchstart);
break;
// no substitution
case 'orientationchange':
case 'pinchopen':
case 'pinchclose' :
case 'rotatecw' :
case 'rotateccw' :
case 'pinch':
case 'rotate':
case 'shake':
case 'tilt':
break;
}
}
}
return false;
},
/**
* For every $.bind(GESTURE) the add-function will be called.
* Instead of binding an actual eventlister, the event is stored as $.data on the element.
* The handler will be triggered using $.triggerHandler(GESTURE) if the internal
* eventhandler (proxy being bound on setup()) detects a GESTURE event
* @param {Object} event_ jQuery-Event-Object being passed by $.bind()
* @return {Void}
*/
add : function(event_) {
// add pseudo event: properties on $.data
var _$element = jQuery(this);
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
// _oDatajQueryGestures[event_.type] = { 'originalType' : event_.type , 'threshold' : event_.data.threshold, 'preventDefault' : event_.data.preventDefault } ;
_oDatajQueryGestures[event_.type] = { 'originalType' : event_.type } ;
return false;
},
/**
* For every $.unbind(GESTURE) the remove-function will be called.
* Instead of removing the actual eventlister, the event is removed from $.data on the element.
* @param {Object} event_ jQuery-Event-Object being passed by $.bind()
* @return {Void}
*/
remove : function(event_) {
// remove pseudo event: properties on $.data
var _$element = jQuery(this);
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
_oDatajQueryGestures[event_.type] = false;
_$element.data('ojQueryGestures' ,_oDatajQueryGestures );
return false;
},
/**
* The last $.unbind()-call on the domElement triggers the teardown function
* removing the eventlistener
* @return {Void}
*/
// @TODO: maybe rework teardown to work with event type?!
teardown : function() {
// split the arguments to necessary controll arguements
var _aSplit = sInternal_.split('_');
var _sDOMEvent = _aSplit[0]; //
// get the associated gesture event and strip the counter: necessary for distinguisihng similliar events such as tapone-tapfour
var _sGestureEvent = _aSplit[1].slice(0,_aSplit[1].length-2);
var _$element = jQuery(this);
var _oDatajQueryGestures;
var oObj;
// bind the event handler on the first $.bind() for a gestureend-event, set marker
if (!_$element.data('ojQueryGestures') || !_$element.data('ojQueryGestures')[_sDOMEvent]) {
// setup pseudo event
_oDatajQueryGestures = _$element.data('ojQueryGestures') || {};
oObj = {};
// remove marker for: domEvent being set on this element
oObj[_sDOMEvent] = false;
$.extend(true,_oDatajQueryGestures,oObj);
_$element.data('ojQueryGestures' ,_oDatajQueryGestures);
// remove gesture events
if($.hasGestures) {
switch(_sGestureEvent) {
// event: orientationchange
case 'orientationchange':
_$element.get(0).removeEventListener('orientationchange', _onOrientationchange, false);
break;
case 'shake':
case 'shakefrontback':
case 'shakeleftright':
case 'shakeupdown':
case 'tilt':
_$element.get(0).removeEventListener('devicemotion', _onDevicemotion, false);
break;
// event :
// - touchstart
// - touchmove
// - touchend
case 'tap':
case 'swipe':
case 'swipeup':
case 'swiperightup':
case 'swiperight':
case 'swiperightdown':
case 'swipedown':
case 'swipeleftdown':
case 'swipeleft':
case 'swipeleftup':
_$element.get(0).removeEventListener('touchstart', _onTouchstart, false);
_$element.get(0).removeEventListener('touchmove', _onTouchmove, false);
_$element.get(0).removeEventListener('touchend', _onTouchend, false);
break;
// event: gestureend
case 'pinchopen':
case 'pinchclose' :
case 'rotatecw' :
case 'rotateccw' :
_$element.get(0).removeEventListener('gesturestart', _onGesturestart, false);
_$element.get(0).removeEventListener('gestureend', _onGestureend, false);
break;
// event: gesturechange
case 'pinch':
case 'rotate':
_$element.get(0).removeEventListener('gesturestart', _onGesturestart, false);
_$element.get(0).removeEventListener('gesturechange', _onGesturechange, false);
break;
}
}
// remove substitute for gesture events
else {
switch(_sGestureEvent) {
// event substitutes:
// - touchstart: mousedown
// - touchmove: none
// - touchend: mouseup
case 'tap':
case 'swipe':
// _$element.get(0).removeEventListener('mousedown', _onTouchstart, false);
// _$element.get(0).removeEventListener('mousemove', _onTouchmove, false);
// _$element.get(0).removeEventListener('mouseup', _onTouchend, false);
_$element.unbind('mousedown', _onTouchstart);
_$element.unbind('mousemove', _onTouchmove);
_$element.unbind('mouseup', _onTouchend);
break;
// no substitution
case 'orientationchange':
case 'pinchopen':
case 'pinchclose' :
case 'rotatecw' :
case 'rotateccw' :
case 'pinch':
case 'rotate':
case 'shake':
case 'tilt':
break;
}
}
}
return false;
}
};
});
/**
* Creates the object that ist passed as second argument to the $element.triggerHandler function.
* This object contains detailed informations about the gesture event.
* @param {Object} oOptions_ {type: {String}, touches: {String}, deltaY: {String},deltaX : {String}, startMove: {Object}, event:{DOM-Event}, timestamp:{String},vector: {Number}}
* @example _createOptions (
* {
* type: 'swipemove',
* touches: '1',
* deltaY: _iDeltaY,
* deltaX : _iDeltaX,
* startMove: _oDatajQueryGestures.oStartTouch,
* event:event_,
* timestamp:_oEventData.timestamp,
* vector: -1
* }
* );
* @returns {Object}
* {
* type: eventtype e.g. "swipe","pinch",
* originalEvent: {DOM-Event},
* // default: just one entry on the delta-array - the first touchpoint
* // the first touchpoint is the reference point for every gesture,
* // because moving touchpoints in various directions would result in
* // a gesture.
* // delta and direction details are just provided for touch not for gesture / motion events
* delta : [
* {
* lastX:{Number} , // x-axis: relative to the last touchevent (e.g. touchmove!)
* lastY:{Number}, // y-axis: relative to the last touchevent (e.g. touchmove!)
* moved: {Number}, // distance: relative to the original touchpoint
* startX: {Number} , // relative to the original touchpoint
* startY: {Number} ,// relative to the original touchpoint
* } ],
* // based on the first touchpoint
* direction : { // relative to the last touchevent (e.g. touchmove!)
* vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
* orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
* lastX : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
* lastY : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
* startX: {Number} , //-1,0,+1 relative to the original touchpoint
* startY: {Number} ,// -1,0,+1 relative to the original touchpoint
* },
* rotation: {Number} || null, // gestureonly: amount of rotation relative to the current position NOT the original
* scale: {Number} || null, // gestureonly: amount of scaling relative to the current position NOT the original
* duration: {Number}, // ms: relative to the original touchpoint
* description : {String} // details as String: {TYPE *}:{TOUCHES 1|2|3|4}:{X-AXIS 'right'|'left'|'steady'}:{Y-AXIS 'down'|'up'|'steady'} e.g. "swipe:1:left:steady" relative to the last touchpoint
* };
*/
function _createOptions(oOptions_) {
// force properties
oOptions_.startMove = (oOptions_.startMove) ? oOptions_.startMove : {startX: null,startY:null,timestamp:null} ;
var _iNow = new Date().getTime();
var _oDirection;
var _oDelta;
// calculate touch differences
if (oOptions_.touches) {
// store delta values
_oDelta = [
{
lastX: oOptions_.deltaX ,
lastY: oOptions_.deltaY,
moved: null,
startX: oOptions_.screenX - oOptions_.startMove.screenX ,
startY: oOptions_.screenY - oOptions_.startMove.screenY
}
];
_oDirection = {
vector: oOptions_.vector || null,
orientation : window.orientation || null,
lastX : ((_oDelta[0].lastX > 0) ? +1 : ( (_oDelta[0].lastX < 0) ? -1 : 0 ) ),
lastY : ((_oDelta[0].lastY > 0) ? +1 : ( (_oDelta[0].lastY < 0) ? -1 : 0 ) ),
startX : ((_oDelta[0].startX > 0) ? +1 : ( (_oDelta[0].startX < 0) ? -1 : 0 ) ),
startY : ((_oDelta[0].startY > 0) ? +1 : ( (_oDelta[0].startY < 0) ? -1 : 0 ) )
};
// calculate distance traveled using the pythagorean theorem
_oDelta[0].moved = Math.sqrt(Math.pow(Math.abs(_oDelta[0].startX), 2) + Math.pow(Math.abs(_oDelta[0].startY), 2));
}
return {
type: oOptions_.type || null,
originalEvent: oOptions_.event || null,
delta : _oDelta || null,
direction : _oDirection || { orientation : window.orientation || null, vector: oOptions_.vector || null},
duration: (oOptions_.duration) ? oOptions_.duration : ( oOptions_.startMove.timestamp ) ? _iNow - oOptions_.timestamp : null,
rotation: oOptions_.rotation || null,
scale: oOptions_.scale || null,
description : oOptions_.description || [
oOptions_.type,
':',
oOptions_.touches,
':',
((_oDelta[0].lastX != 0) ? ((_oDelta[0].lastX > 0) ? 'right' : 'left') : 'steady'),
':',
((_oDelta[0].lastY != 0) ? ( (_oDelta[0].lastY > 0) ? 'down' : 'up') :'steady')
].join('')
};
}
/**
* DOM-event handlers
*/
/**
* Handler: orientationchange
* Triggers the bound orientationchange handler on the window element
* The "orientationchange" handler will receive an object with additional information
* about the event.
* {
* direction : {
* orientation: {-90|0|90|180}
* },
* description : [
* 'orientationchange:{landscape:clockwise:|portrait:default|landscape:counterclockwise|portrait:upsidedown}:{-90|0|90|180}' // e.g. 'orientation:landscape:clockwise:-90
* }
* @param {DOM-Event} event_
* @return {Void}
*/
function _onOrientationchange(event_) {
// window.orientation: -90,0,90,180
var _aDict = ['landscape:clockwise:','portrait:default:','landscape:counterclockwise:','portrait:upsidedown:'];
$(window).triggerHandler('orientationchange',
{
direction : {orientation: window.orientation},
description : [
'orientationchange:',
_aDict[( (window.orientation / 90) +1)],
window.orientation
].join('')
});
}
/**
* Handler: devicemotion
* Calculates "motion events" such as shake, tilt, wiggle by observing "changes in space"
* For understanding "directions", place your mobile device on a table with the bottom
* (home button) close to you:
* - x-axis: horizontal left / right
* - y-axis: horizontal front / back (through the home button)
* - z-axis: vertical through your device
* @param {DOM-Event} event_
* @returns {Object}
* {
* type: eventtype e.g. "shake",
* originalEvent: {DOM-Event},
* // delta and direction details are just provided for touch not for gesture / motion events
* delta : null,
* direction :{
* vector: null,
* orientation: -90,0,90,180 || null (window.orienntation)
* }
* rotation: {Number} , // amount of rotation relative to the current position NOT the original
* scale: {Number} , // amount of scaling relative to the current position NOT the original
* duration: {Number}, // ms: duration of the motion
* description : {String} // details as String: pinch:{'close'|'open'} e.g. "pinch:-1:close" || rotate:{'counterclockwise'|'clockwise'} e.g. "rotate:-1:counterclockwise"
* };
* @param {DOM-Event} event_
* @return {Void}
*/
function _onDevicemotion(event_) {
var _sType;
var _$element = jQuery(window);
//var _bHasGyroscope = $.hasGyroscope;
// skip custom notification: devicemotion is triggered every 0.05s regardlesse of any gesture
// get options
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
var _oThreshold = $.jGestures.defaults.thresholdShake;
// get last position or set initital values
var _oLastDevicePosition = _oDatajQueryGestures.oDeviceMotionLastDevicePosition || {
accelerationIncludingGravity : {
x: 0,
y: 0,
z: 0
},
shake : {
eventCount: 0,
intervalsPassed: 0,
intervalsFreeze: 0
},
shakeleftright : {
eventCount: 0,
intervalsPassed: 0,
intervalsFreeze: 0
},
shakefrontback : {
eventCount: 0,
intervalsPassed: 0,
intervalsFreeze: 0
},
shakeupdown : {
eventCount: 0,
intervalsPassed: 0,
intervalsFreeze: 0
}
};
// cache current values
var _oCurrentDevicePosition = {
accelerationIncludingGravity : {
x: event_.accelerationIncludingGravity.x,
y: event_.accelerationIncludingGravity.y,
z: event_.accelerationIncludingGravity.z
},
shake: {
eventCount: _oLastDevicePosition.shake.eventCount,
intervalsPassed: _oLastDevicePosition.shake.intervalsPassed,
intervalsFreeze: _oLastDevicePosition.shake.intervalsFreeze
},
shakeleftright: {
eventCount: _oLastDevicePosition.shakeleftright.eventCount,
intervalsPassed: _oLastDevicePosition.shakeleftright.intervalsPassed,
intervalsFreeze: _oLastDevicePosition.shakeleftright.intervalsFreeze
},
shakefrontback: {
eventCount: _oLastDevicePosition.shakefrontback.eventCount,
intervalsPassed: _oLastDevicePosition.shakefrontback.intervalsPassed,
intervalsFreeze: _oLastDevicePosition.shakefrontback.intervalsFreeze
},
shakeupdown: {
eventCount: _oLastDevicePosition.shakeupdown.eventCount,
intervalsPassed: _oLastDevicePosition.shakeupdown.intervalsPassed,
intervalsFreeze: _oLastDevicePosition.shakeupdown.intervalsFreeze
}
};
// options
var _aType;
var _aDescription;
var _oObj;
// trigger events for all bound pseudo events on this element
for (_sType in _oDatajQueryGestures) {
// get current pseudo event
// trigger bound events on this element
switch(_sType) {
case 'shake':
case 'shakeleftright':
case 'shakefrontback':
case 'shakeupdown':
// options
_aType = [];
_aDescription = [];
_aType.push(_sType);
// freeze shake - prevent multiple shake events on one shaking motion (user won't stop shaking immediately)
if (++_oCurrentDevicePosition[_sType].intervalsFreeze > _oThreshold.freezeShakes && _oCurrentDevicePosition[_sType].intervalsFreeze < (2*_oThreshold.freezeShakes) ) { break; }
// set control values
_oCurrentDevicePosition[_sType].intervalsFreeze = 0;
_oCurrentDevicePosition[_sType].intervalsPassed++;
// check for shaking motions: massive acceleration changes in every direction
if ( ( _sType === 'shake' ||_sType === 'shakeleftright' ) && ( _oCurrentDevicePosition.accelerationIncludingGravity.x > _oThreshold.leftright.sensitivity || _oCurrentDevicePosition.accelerationIncludingGravity.x < (-1* _oThreshold.leftright.sensitivity) ) ) {
_aType.push('leftright');
_aType.push('x-axis');
}
if ( ( _sType === 'shake' ||_sType === 'shakefrontback' ) && (_oCurrentDevicePosition.accelerationIncludingGravity.y > _oThreshold.frontback.sensitivity || _oCurrentDevicePosition.accelerationIncludingGravity.y < (-1 * _oThreshold.frontback.sensitivity) ) ) {
_aType.push('frontback');
_aType.push('y-axis');
}
if ( ( _sType === 'shake' ||_sType === 'shakeupdown' ) && ( _oCurrentDevicePosition.accelerationIncludingGravity.z+9.81 > _oThreshold.updown.sensitivity || _oCurrentDevicePosition.accelerationIncludingGravity.z+9.81 < (-1 * _oThreshold.updown.sensitivity) ) ) {
_aType.push('updown');
_aType.push('z-axis');
}
// at least one successful shaking event
if (_aType.length > 1) {
// minimum number of shaking motions during the defined "time" (messured by events - device event interval: 0.05s)
if (++_oCurrentDevicePosition[_sType].eventCount == _oThreshold.requiredShakes && (_oCurrentDevicePosition[_sType].intervalsPassed) < _oThreshold.freezeShakes ) {
// send event
_$element.triggerHandler(_sType, _createOptions ({type: _sType, description: _aType.join(':'), event:event_,duration:_oCurrentDevicePosition[_sType].intervalsPassed*5 }) );
// reset
_oCurrentDevicePosition[_sType].eventCount = 0;
_oCurrentDevicePosition[_sType].intervalsPassed = 0;
// freeze shake
_oCurrentDevicePosition[_sType].intervalsFreeze = _oThreshold.freezeShakes+1;
}
// too slow, reset
else if (_oCurrentDevicePosition[_sType].eventCount == _oThreshold.requiredShakes && (_oCurrentDevicePosition[_sType].intervalsPassed) > _oThreshold.freezeShakes ) {
_oCurrentDevicePosition[_sType].eventCount = 0 ;
_oCurrentDevicePosition[_sType].intervalsPassed = 0;
}
}
break;
}
// refresh pseudo events
_oObj = {};
_oObj.oDeviceMotionLastDevicePosition = _oCurrentDevicePosition;
_$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
}
}
/**
* Handler: touchstart or mousedown
* Setup pseudo-event by storing initial values such as :
* screenX : {Number}
* screenY : {Number}
* timestamp: {Number}
* on the pseudo gesture event and
* sets up additional eventlisteners for handling touchmove events.
* @param {DOM-Event} event_
* @return {Void}
*/
function _onTouchstart(event_) {
// ignore bubbled handlers
// if ( event_.currentTarget !== event_.target ) { return; }
var _$element = jQuery(event_.currentTarget);
// var _$element = jQuery(event_.target);
// trigger custom notification
_$element.triggerHandler($.jGestures.events.touchstart,event_);
// set the necessary touch events
if($.hasGestures) {
event_.currentTarget.addEventListener('touchmove', _onTouchmove, false);
event_.currentTarget.addEventListener('touchend', _onTouchend, false);
}
// event substitution
else {
// event_.currentTarget.addEventListener('mousemove', _onTouchmove, false);
// event_.currentTarget.addEventListener('mouseup', _onTouchend, false);
_$element.bind('mousemove', _onTouchmove);
_$element.bind('mouseup', _onTouchend);
}
// get stored pseudo event
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
// var _oEventData = _oDatajQueryGestures[_sType];
var _eventBase = (event_.touches) ? event_.touches[0] : event_;
// store current values for calculating relative values (changes between touchmoveevents)
var _oObj = {};
_oObj.oLastSwipemove = { screenX : _eventBase.screenX, screenY : _eventBase.screenY, timestamp:new Date().getTime()};
_oObj.oStartTouch = { screenX : _eventBase.screenX, screenY : _eventBase.screenY, timestamp:new Date().getTime()};
_$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
}
/**
* Handler: touchmove or mousemove
* Calculates the x/y changes since the last event,
* compares it to $.jGestures.defaults.thresholdMove and triggers
* an swipemove event if the distance exceed the
* threshold.
* Custom-event argument object:
* {Object}
* {
* type: e.g. 'swipemove',
* <09><>: {DOM-Event},
* // default: just one entry on the delta-array - the first touchpoint
* // the first touchpoint is the reference point for every gesture,
* // because moving touchpoints in various directions would result in
* // a gesture.
* // delta and direction details are just provided for touch not for gesture / motion events
* delta : [
* {
* lastX:{Number} , // x-axis: relative to the last touchevent (e.g. touchmove!)
* lastY:{Number}, // y-axis: relative to the last touchevent (e.g. touchmove!)
* moved: {Number}, // distance: relative to the original touchpoint
* startX: {Number} , // relative to the original touchpoint
* startY: {Number} ,// relative to the original touchpoint
* } ],
* // based on the first touchpoint
* direction : { // relative to the last touchevent (e.g. touchmove!)
* vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
* orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
* lastX : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
* lastY : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
* startX: {Number} , //-1,0,+1 relative to the original touchpoint
* startY: {Number} ,// -1,0,+1 relative to the original touchpoint
* },
* rotation: null, // gestureonly: amount of rotation relative to the current position NOT the original
* scale: null, // gestureonly: amount of scaling relative to the current position NOT the original
* duration: {Number}, // ms: relative to the original touchpoint
* description : {String} // details as String: {TYPE *}:{TOUCHES 1|2|3|4}:{X-AXIS 'right'|'left'|'steady'}:{Y-AXIS 'down'|'up'|'steady'} e.g. "swipe:1:left:steady" relative to the last touchpoint
* };
*
* @param {DOM-Event} event_
* @return {Void}
*/
function _onTouchmove(event_) {
var _$element = jQuery(event_.currentTarget);
// var _$element = jQuery(event_.target);
// get stored pseudo event
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
var _bHasTouches = !!event_.touches;
var _iScreenX = (_bHasTouches) ? event_.changedTouches[0].screenX : event_.screenX;
var _iScreenY = (_bHasTouches) ? event_.changedTouches[0].screenY : event_.screenY;
//relative to the last event
var _oEventData = _oDatajQueryGestures.oLastSwipemove;
var _iDeltaX = _iScreenX - _oEventData.screenX ;
var _iDeltaY = _iScreenY - _oEventData.screenY;
var _oDetails;
// there's a swipemove set (not the first occurance), trigger event
if (!!_oDatajQueryGestures.oLastSwipemove) {
// check
_oDetails = _createOptions({type: 'swipemove', touches: (_bHasTouches) ? event_.touches.length: '1', screenY: _iScreenY,screenX:_iScreenX ,deltaY: _iDeltaY,deltaX : _iDeltaX, startMove:_oEventData, event:event_, timestamp:_oEventData.timestamp});
_$element.triggerHandler(_oDetails.type,_oDetails);
}
// store the new values
var _oObj = {};
var _eventBase = (event_.touches) ? event_.touches[0] : event_;
_oObj.oLastSwipemove = { screenX : _eventBase.screenX, screenY : _eventBase.screenY, timestamp:new Date().getTime()};
_$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
}
/**
* Handler: touchend or mouseup
* Removes the additional handlers (move/end)
* Calculates the x/y changes since the touchstart event
* not in relation to the last move event.
* Triggers the
* swipeone|swipetwo|swipethree|swipefour|
* swipeup|swiperightup|swiperight|swiperightdown|swipedown|
* swipeleftdown|swipeleft|swipeleftup|
* tapone|taptwo|tapthree|tapfour
* event.
* {Object}
* {
* type: eventtype e.g. "swipeone","swipeleftdown",
* originalEvent: {DOM-Event},
* // default: just one entry on the delta-array - the first touchpoint
* // the first touchpoint is the reference point for every gesture,
* // because moving touchpoints in various directions would result in
* // a gesture.
* // delta and direction details are just provided for touch not for gesture / motion events
* delta : [
* {
* lastX:{Number} , // x-axis: relative to the last touchevent (e.g. touchmove!)
* lastY:{Number}, // y-axis: relative to the last touchevent (e.g. touchmove!)
* moved: {Number}, // distance: relative to the original touchpoint
* startX: {Number} , // relative to the original touchpoint
* startY: {Number} ,// relative to the original touchpoint
* } ],
* // based on the first touchpoint
* direction : { // relative to the last touchevent (e.g. touchmove!)
* vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
* orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
* lastX : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
* lastY : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
* startX: {Number} , //-1,0,+1 relative to the original touchpoint
* startY: {Number} ,// -1,0,+1 relative to the original touchpoint
* },
* rotation: null,
* scale: null ,
* duration: {Number}, // ms: relative to the original touchpoint
* description : {String} // details as String: {TYPE *}:{TOUCHES 1|2|3|4}:{X-AXIS 'right'|'left'|'steady'}:{Y-AXIS 'down'|'up'|'steady'} e.g. "swipe:1:left:steady" relative to the last touchpoint
* };
* @param {DOM-Event} event_
* @return {Void}
*/
function _onTouchend(event_) {
// ignore bubbled handlers
// if ( event_.currentTarget !== event_.target ) { return; }
var _$element = jQuery(event_.currentTarget);
var _bHasTouches = !!event_.changedTouches;
var _iTouches = (_bHasTouches) ? event_.changedTouches.length : '1';
var _iScreenX = (_bHasTouches) ? event_.changedTouches[0].screenX : event_.screenX;
var _iScreenY = (_bHasTouches) ? event_.changedTouches[0].screenY : event_.screenY;
// trigger custom notification
_$element.triggerHandler($.jGestures.events.touchendStart,event_);
// var _$element = jQuery(event_.target);
// remove events
if($.hasGestures) {
event_.currentTarget.removeEventListener('touchmove', _onTouchmove, false);
event_.currentTarget.removeEventListener('touchend', _onTouchend, false);
}
// event substitution
else {
// event_.currentTarget.removeEventListener('mousemove', _onTouchmove, false);
// event_.currentTarget.removeEventListener('mouseup', _onTouchend, false);
_$element.unbind('mousemove', _onTouchmove);
_$element.unbind('mouseup', _onTouchend);
}
// get all bound pseudo events
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
// if the current change on the x/y position is above the defined threshold for moving an element set the moved flag
// to distinguish between a moving gesture and a shaking finger trying to tap
var _bHasMoved = (
Math.abs(_oDatajQueryGestures.oStartTouch.screenX - _iScreenX) > $.jGestures.defaults.thresholdMove ||
Math.abs(_oDatajQueryGestures.oStartTouch.screenY - _iScreenY) > $.jGestures.defaults.thresholdMove
) ? true : false;
// if the current change on the x/y position is above the defined threshold for swiping set the moved flag
// to indicate we're dealing with a swipe gesture
var _bHasSwipeGesture = (
Math.abs(_oDatajQueryGestures.oStartTouch.screenX - _iScreenX) > $.jGestures.defaults.thresholdSwipe ||
Math.abs(_oDatajQueryGestures.oStartTouch.screenY - _iScreenY) > $.jGestures.defaults.thresholdSwipe
) ? true : false;
var _sType;
var _oEventData ;
var _oDelta;
// calculate distances in relation to the touchstart position not the last touchmove event!
var _iDeltaX;
var _iDeltaY;
var _oDetails;
var _aDict = ['zero','one','two','three','four'];
// swipe marker
var _bIsSwipe;
// trigger events for all bound pseudo events on this element
for (_sType in _oDatajQueryGestures) {
// get current pseudo event
_oEventData = _oDatajQueryGestures.oStartTouch;
_oDelta = {};
_iScreenX = (_bHasTouches) ? event_.changedTouches[0].screenX : event_.screenX;
_iScreenY = (_bHasTouches) ? event_.changedTouches[0].screenY : event_.screenY;
// calculate distances in relation to the touchstart position not the last touchmove event!
_iDeltaX = _iScreenX - _oEventData.screenX ;
_iDeltaY = _iScreenY - _oEventData.screenY;
_oDetails = _createOptions({type: 'swipe', touches: _iTouches, screenY: _iScreenY,screenX:_iScreenX ,deltaY: _iDeltaY,deltaX : _iDeltaX, startMove:_oEventData, event:event_, timestamp: _oEventData.timestamp });
// swipe marker
_bIsSwipe = false;
// trigger bound events on this element
switch(_sType) {
case 'swipeone':
if( _bHasTouches === false && _iTouches == 1 && _bHasMoved === false){
// trigger tapone!
break;
}
if (_bHasTouches===false || ( _iTouches == 1 && _bHasMoved === true && _bHasSwipeGesture===true)) {
_bIsSwipe = true;
_oDetails.type = ['swipe',_aDict[_iTouches]].join('');
_$element.triggerHandler(_oDetails.type,_oDetails);
}
break;
case 'swipetwo':
if (( _bHasTouches && _iTouches== 2 && _bHasMoved === true && _bHasSwipeGesture===true)) {
_bIsSwipe = true;
_oDetails.type = ['swipe',_aDict[_iTouches]].join('');
_$element.triggerHandler(_oDetails.type,_oDetails);
}
break;
case 'swipethree':
if ( ( _bHasTouches && _iTouches == 3 && _bHasMoved === true && _bHasSwipeGesture===true)) {
_bIsSwipe = true;
_oDetails.type = ['swipe',_aDict[_iTouches]].join('');
_$element.triggerHandler(_oDetails.type,_oDetails);
}
break;
case 'swipefour':
if ( ( _bHasTouches && _iTouches == 4 && _bHasMoved === true && _bHasSwipeGesture===true)) {
_bIsSwipe = true;
_oDetails.type = ['swipe',_aDict[_iTouches]].join('');
_$element.triggerHandler(_oDetails.type,_oDetails);
}
break;
case 'swipeup':
case 'swiperightup':
case 'swiperight':
case 'swiperightdown':
case 'swipedown':
case 'swipeleftdown':
case 'swipeleft':
case 'swipeleftup':
if ( _bHasTouches && _bHasMoved === true && _bHasSwipeGesture===true) {
_bIsSwipe = true;
_oDetails.type = [
'swipe',
((_oDetails.delta[0].lastX != 0) ? ((_oDetails.delta[0].lastX > 0) ? 'right' : 'left') : ''),
((_oDetails.delta[0].lastY != 0) ? ((_oDetails.delta[0].lastY > 0) ? 'down' : 'up') :'')
].join('');
_$element.triggerHandler(_oDetails.type, _oDetails);
}
break;
case 'tapone':
case 'taptwo':
case 'tapthree':
case 'tapfour':
if (( /* _bHasTouches && */ _bHasMoved !== true && _bIsSwipe !==true) && (_aDict[_iTouches] ==_sType.slice(3)) ) {
_oDetails.description = ['tap',_aDict[_iTouches]].join('');
_oDetails.type = ['tap',_aDict[_iTouches]].join('');
_$element.triggerHandler(_oDetails.type,_oDetails);
}
break;
}
// refresh pseudo events
var _oObj = {};
// _oObj[_sType] = false;
// _oObj.hasTouchmoved = false;
_$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
_$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
}
_$element.triggerHandler($.jGestures.events.touchendProcessed,event_);
}
/**
* Handler: gesturestart
* Setup pseudo-event by storing initial values such as :
* timestamp: {Number}
* on the pseudo gesture event
* Since the gesture-event doesn't supply event.touches no tuchpoints will be calculated
* @param {DOM-Event} event_
* @return {Void}
*/
function _onGesturestart(event_) {
// ignore bubbled handlers
// if ( event_.currentTarget !== event_.target ) { return; }
var _$element = jQuery(event_.currentTarget);
// var _$element = jQuery(event_.target);
// trigger custom notification
_$element.triggerHandler($.jGestures.events.gesturestart,event_);
// get stored pseudo event
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
// var _oEventData = _oDatajQueryGestures[_sType];
// store current values for calculating relative values (changes between touchmoveevents)
var _oObj = {};
_oObj.oStartTouch = {timestamp:new Date().getTime()};
_$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
}
/**
* Handler: gesturechange
* Read the event_.scale / event_.rotate values,
* an triggers a pinch|rotate event if necessary.
* Since the gesture-event doesn't supply event.touches no tuchpoints will be calculated
* @returns {Object}
* {
* type: eventtype e.g. "pinch","rotate",
* originalEvent: {DOM-Event},
* // delta and direction details are just provided for touch not for gesture / motion events
* delta : null,
* direction : {
* vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
* orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
* },
* rotation: {Number} , // amount of rotation relative to the current position NOT the original
* scale: {Number} , // amount of scaling relative to the current position NOT the original
* duration: {Number}, // ms: relative to the original touchpoint
* description : {String} // details as String: pinch:{'close'|'open'} e.g. "pinch:-1:close" || rotate:{'counterclockwise'|'clockwise'} e.g. "rotate:-1:counterclockwise"
* };
* @param {DOM-Event} event_
* @return {Void}
*/
function _onGesturechange(event_) {
// ignore bubbled handlers
// if ( event_.currentTarget !== event_.target ) { return; }
var _$element = jQuery(event_.currentTarget);
// var _$element = jQuery(event_.target);
var _iDelta,_iDirection,_sDesc,_oDetails;
// get all pseudo events
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
// trigger events for all bound pseudo events on this element
var _sType;
for (_sType in _oDatajQueryGestures) {
// trigger a specific bound event
switch(_sType) {
case 'pinch':
_iDelta = event_.scale;
if ( ( ( _iDelta < 1 ) && (_iDelta % 1) < (1 - $.jGestures.defaults.thresholdPinchclose) ) || ( ( _iDelta > 1 ) && (_iDelta % 1) > ($.jGestures.defaults.thresholdPinchopen) ) ) {
_iDirection = (_iDelta < 1 ) ? -1 : +1 ;
_oDetails = _createOptions({type: 'pinch', scale: _iDelta, touches: null,startMove:_oDatajQueryGestures.oStartTouch, event:event_, timestamp: _oDatajQueryGestures.oStartTouch.timestamp, vector:_iDirection, description: ['pinch:',_iDirection,':' , ( (_iDelta < 1 ) ? 'close' : 'open' )].join('') });
_$element.triggerHandler(_oDetails.type, _oDetails);
}
break;
case 'rotate':
_iDelta = event_.rotation;
if ( ( ( _iDelta < 1 ) && ( -1*(_iDelta) > $.jGestures.defaults.thresholdRotateccw ) ) || ( ( _iDelta > 1 ) && (_iDelta > $.jGestures.defaults.thresholdRotatecw) ) ) {
_iDirection = (_iDelta < 1 ) ? -1 : +1 ;
_oDetails = _createOptions({type: 'rotate', rotation: _iDelta, touches: null, startMove:_oDatajQueryGestures.oStartTouch, event:event_, timestamp: _oDatajQueryGestures.oStartTouch.timestamp, vector:_iDirection, description: ['rotate:',_iDirection,':' , ( (_iDelta < 1 ) ? 'counterclockwise' : 'clockwise' )].join('') });
_$element.triggerHandler(_oDetails.type, _oDetails);
}
break;
}
}
}
/**
* Handler: gestureend
* Read the event_.scale / event_.rotate values,
* compares it to $.jGestures.defaults.threshold* and triggers
* a pinchclose|pinchclose|rotatecw|rotateccw event if the distance exceed the
* Since the gesture-event doesn't supply event.touches no tuchpoints will be calculated
* * Custom-event argument object:
* @returns {Object}
* {
* type: eventtype e.g. "pinchclose","pinchopen", "rotatecw", "rotateccw",
* originalEvent: {DOM-Event},
* // delta and direction details are just provided for touch not for gesture / motion events
* delta : null,
* // based on the first touchpoint
* direction : {
* vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
* orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
* },
* rotation: {Number} , // amount of rotation relative to the current position NOT the original
* scale: {Number} , // amount of scaling relative to the current position NOT the original
* duration: {Number}, // ms: relative to the original touchpoint
* description : {String} // details as String: pinch:{'close'|'open'} e.g. "pinch:-1:close" || rotate:{'counterclockwise'|'clockwise'} e.g. "rotate:-1:counterclockwise"
* };
* @param {DOM-Event} event_
* @return {Void}
*/
function _onGestureend(event_) {
// ignore bubbled handlers
// if ( event_.currentTarget !== event_.target ) { return; }
var _$element = jQuery(event_.currentTarget);
// var _$element = jQuery(event_.target);
// trigger custom notification
_$element.triggerHandler($.jGestures.events.gestureendStart,event_);
var _iDelta;
var _oDatajQueryGestures = _$element.data('ojQueryGestures');
// trigger handler for every bound event
var _sType;
for (_sType in _oDatajQueryGestures) {
switch(_sType) {
case 'pinchclose':
_iDelta = event_.scale;
if (( _iDelta < 1 ) && (_iDelta % 1) < (1 - $.jGestures.defaults.thresholdPinchclose)) {
_$element.triggerHandler('pinchclose', _createOptions ({type: 'pinchclose', scale:_iDelta, vector: -1, touches: null, startMove: _oDatajQueryGestures.oStartTouch, event:event_, timestamp:_oDatajQueryGestures.oStartTouch.timestamp,description: 'pinch:-1:close' }) );
}
break;
case 'pinchopen':
_iDelta = event_.scale;
if ( ( _iDelta > 1 ) && (_iDelta % 1) > ($.jGestures.defaults.thresholdPinchopen) ) {
_$element.triggerHandler('pinchopen', _createOptions ({type: 'pinchopen', scale:_iDelta, vector: +1, touches: null, startMove: _oDatajQueryGestures.oStartTouch, event:event_, timestamp:_oDatajQueryGestures.oStartTouch.timestamp,description: 'pinch:+1:open'}) );
}
break;
case 'rotatecw':
_iDelta = event_.rotation;
if ( ( _iDelta > 1 ) && (_iDelta > $.jGestures.defaults.thresholdRotatecw) ) {
_$element.triggerHandler('rotatecw', _createOptions ({type: 'rotatecw', rotation:_iDelta, vector: +1, touches: null, startMove: _oDatajQueryGestures.oStartTouch, event:event_, timestamp:_oDatajQueryGestures.oStartTouch.timestamp,description: 'rotate:+1:clockwise'}) );
}
break;
case 'rotateccw':
_iDelta = event_.rotation;
if ( ( _iDelta < 1 ) && ( -1*(_iDelta) > $.jGestures.defaults.thresholdRotateccw ) ) {
_$element.triggerHandler('rotateccw', _createOptions ({type: 'rotateccw', rotation:_iDelta, vector: -1, touches: null, startMove: _oDatajQueryGestures.oStartTouch, event:event_, timestamp:_oDatajQueryGestures.oStartTouch.timestamp,description: 'rotate:-1:counterclockwise'}) );
}
break;
}
}
_$element.triggerHandler($.jGestures.events.gestureendProcessed,event_);
}
}
)(jQuery);