X.Dom.Event = function( e ){
this._event = e;
this.type = e.type;
- this.target = e.target;
- this.currentTarget = e.currentTarget;
- this.relatedTarget = e.relatedTarget;
+ this.target = e.target; // xnode
+ this.currentTarget = e.currentTarget; // xnode
+ this.relatedTarget = e.relatedTarget; // xnode
this.eventPhase = e.eventPhase;
this.clientX = e.clientX;
this.clientY = e.clientY;
- this.screenX = e.screenX;
- this.screenY = e.screenY;
+ //this.screenX = e.screenX;
+ //this.screenY = e.screenY;
+ this.pageX = e.pageX;
+ this.pageY = e.pageY;
+ this.offsetX = e.offsetX || e.layerX;
+ this.offsetY = e.offsetY || e.layerY;
this.keyCode = e.keyCode;
this.altKey = e.altKey;
this.ctrlKey = e.ctrlKey;
this.shiftKey = e.shiftKey;
+ // http://www.programming-magic.com/20090127231544/
+ this.which = e.which || ( e.button + 1 ); // 左:1, 中:2, 右:3
+
this.wheelDelta = e.wheelDelta;
+
+ if( e.constructor === window.TouchEvent ){
+ // TouchEvent
+ this.touches = e.touches;
+ this.changedTouches = e.changedTouches;
+ this.targetTouches = e.targetTouches;
+ //this.altKey = e.altKey;
+ //this.ctrlKey = e.ctrlKey;
+ this.metaKey = e.metaKey;
+ //this.shiftKey = e.shiftKey;
+ //this.type = e.type;
+ //this.target = e.target;
+ } else
+ if( e.constructor === window.PointerEvent ){
+ // PointerEvent;
+ this.currentPoint = e.currentPoint;
+ this.width = e.width;
+ this.height = e.height;
+ this.timeStamp = e.timeStamp;
+ this.hwTimestamp = e.hwTimestamp;
+ this.intermediatePoints = e.intermediatePoints;
+ this.isPrimary = e.isPrimary;
+ this.pointerId = e.pointerId;
+ this.pointerType = e.pointerType;
+ this.pressure = e.pressure;
+ this.tiltX = e.tiltX;
+ this.tiltY = e.tiltY;
+ };
};
} else {
X.Dom.Event = function( e, element ){
+ var btn;
+
this._event = e;
this.type = e.type;
- this.target = e.srcElement;
- this.currentTarget = element;
- this.relatedTarget = e.formElement ? e.formElement : e.toElement;
+ this.target = e.srcElement; // xnode
+ this.currentTarget = element; // xnode
+ this.relatedTarget = e.formElement ? e.formElement : e.toElement; // xnode
this.eventPhase = e.srcElement === element ? 2: 3;
this.clientX = e.clientX;
this.clientY = e.clientY;
- this.screenX = e.screenX;
- this.screenY = e.screenY;
+ //this.screenX = e.screenX;
+ //this.screenY = e.screenY;
+ this.pageX = e.clientX + document.body.scrollLeft;
+ this.pageY = e.clientY + document.body.scrollTop;
+ this.offsetX = e.offsetX;
+ this.offsetY = e.offsetY;
this.keyCode = e.keyCode;
this.altKey = e.altKey;
this.ctrlKey = e.ctrlKey;
this.shiftKey = e.shiftKey;
+ // http://www.programming-magic.com/20090127231544/
+ switch( this.type ){
+ case 'click' :
+ case 'dblclick' :
+ this.which = 1;
+ break;
+ case 'contextmenu' :
+ this.which = 3;
+ break;
+ default :
+ btn = e.button;
+ this.which =
+ btn & 1 ? 1 :
+ btn & 4 ? 2 :
+ btn & 2 ? 3 : 0; // 左:1(click:0), 中:4, 右:2
+ };
+
this.wheelDelta = e.wheelDelta;
};
};
+X.Dom.Event._Helper =
+ document.addEventListener ?
+ (function( elm, type ){
+ elm.addEventListener( type, this, false );
+ }) :
+ document.attachEvent ?
+ (function( elm, type ){
+ elm.attachEvent( 'on' + type, ( this.callback = X.Callback.create( this ) ) );
+ }) :
+ (function( elm, type ){
+ this.callback = elm[ 'on' + type ] = X.Callback.create( this );
+ });
+
+X.Dom.Event._Helper.prototype.removeEvent =
+ document.removeEventListener ?
+ (function( migrate ){
+ this.type && this.elm.removeEventListener( this.type, this, false );
+ if( migrate ) return;
+ delete this.elm;
+ delete this.type;
+ delete this.list;
+ }) :
+ document.detachEvent ?
+ (function( migrate ){
+ this.type && this.elm.detachEvent( 'on' + this.type, this.callback );
+ X.Callback._correct( this.callback );
+ if( migrate ) return;
+ delete this.elm;
+ delete this.type;
+ delete this.list;
+ delete this.callback;
+ }) :
+ (function( migrate ){
+ this.elm[ 'on' + this.type ] = X.Dom.Event.emptyFunc;
+ this.elm[ 'on' + this.type ] = '';
+ X.Callback._correct( this.callback );
+ if( migrate ) return;
+ delete this.elm;
+ delete this.type;
+ delete this.list;
+ delete this.callback;
+ });
+
+/*
+ * eventDispatch 中の Event.remove への対処.
+ * 現在 dispatch 中の function より以前の function が抜かれた場合の対策
+ */
+X.Dom.Event._Helper.prototype.handleEvent =
+ document.removeEventListener ?
+ (function( e ){
+ var list = this.list,
+ e = new X.Dom.Event( e ),
+ i = 0,
+ ret = 0,
+ p, f, r;
+ list.removed = 0;
+ for( ; i < list.length; ){
+ list.pointer = i - list.removed;
+ f = list[ list.pointer ];
+ r = f( e );
+ ret |= ( r || 0 );
+ if( r & X.Callback.UN_LISTEN ){
+ list.splice( list.pointer, 1 );
+ X.Callback._correct( f );
+ if( list.length === 0 ){
+ this.removeEvent();
+ break;
+ };
+ } else {
+ ++i;
+ };
+ if( r & X.Callback.STOP_NOW ) break;
+ };
+ delete list.pointer;
+ delete list.removed;
+ if( ret & X.Callback.STOP_PROPAGATION ){
+ event.cancelBubble = true;
+ };
+ if( ret & X.Callback.PREVENT_DEFAULT ){
+ return event.returnValue = false;
+ };
+ }) :
+ (function(){
+ var list = this.list,
+ e = new X.Dom.Event( event, this.elm ),
+ i = 0,
+ ret = 0,
+ p, f, r;
+ list.removed = 0;
+ for( ; i < list.length; ){
+ list.pointer = i - list.removed;
+ f = list[ list.pointer ];
+ r = f( e );
+ ret |= ( r || 0 );
+ if( r & X.Callback.UN_LISTEN ){
+ list.splice( list.pointer, 1 );
+ X.Callback._correct( f );
+ if( list.length === 0 ){
+ this.removeEvent();
+ break;
+ };
+ } else {
+ ++i;
+ };
+ if( r & X.Callback.STOP_NOW ) break;
+ };
+ delete list.pointer;
+ delete list.removed;
+ if( ret & X.Callback.STOP_PROPAGATION ){
+ e.stopPropagation();
+ };
+ if( ret & X.Callback.PREVENT_DEFAULT ){
+ e.preventDefault();
+ return false;
+ };
+ });
+
X.Dom.Event._chashe = [ {}, {}, null, {} ]; // window, document, documentElement, body
X.Dom.Event._chashe2 = [];
hash = XEvent._getHash( element, true ),
helper = hash[ type ],
callback;
- callback = typeof arg2 === 'function' ? X.Callback.create( element, arg2, arg3 ) : X.Callback.create( arg2, arg3, arg4 );
+ if( typeof arg2 === 'function' ){
+ callback = X.Callback.create( element, arg2, arg3 );
+ } else {
+ callback = X.Callback.create( arg2, arg3, arg4 );
+ };
if( helper ){
list = helper.list;
list.indexOf( callback ) === -1 && ( list[ list.length ] = callback );
if( uid = parseFloat( element.getAttribute( '_UID' ) ) ){
return this._chashe2[ uid ];
};
- element.setAttrivute( '_UID', uid = this._chashe2.length );
- if( create ) return this._chashe2[ uid ] = {};
+ if( create ){
+ uid = this._chashe2.length;
+ element.setAttrivute( '_UID', '' + uid );
+ return this._chashe2[ uid ] = {};
+ };
//
} else {
if( uid = 2 + element.UID ){
if( uid = element._UID ){
return this._chashe2[ uid ];
};
- element._UID = uid = this._chashe2.length;
- if( create ) return this._chashe2[ uid ] = {};
+ if( create ){
+ element._UID = uid = this._chashe2.length;
+ return this._chashe2[ uid ] = {};
+ };
//
};
};
-X.Dom.Event.remove = function( element, type, arg2, arg3 ){
- var XEvent = X.Dom.Event,
- hash = XEvent._getHash( element ),
+X.Dom.Event.remove = function( /* element, type, arg2, arg3, arg4 */ ){
+ var element = arguments[ 0 ],
+ type = arguments[ 1 ],
+ arg2, arg3, arg4,
+ hash = X.Dom.Event._getHash( element ),
helper, callback, i;
if( !hash ) return;
- // element のみ指定
- if( type === void 0 ){
- for( type in hash ){
- XEvent.remove( element, type );
- };
- XEvent._chashe.splice( XEvent._chashe.indexOf( hash ), 1 );
- return;
- };
- // element, type まで指定
- if( arg2 === void 0 ){
- if( !( helper = hash[ type ] ) ) return;
- list = helper.list;
- i = list.length;
- for( ; i; ){
- callback = list[ --i ];
- X.Callback._correct( callback );
- };
- list.length = 0;
- helper.removeEvent();
- delete hash[ type ];
- return;
- };
- if( !( helper = hash[ type ] ) ){
- list = helper.list;
- i = list.indexOf( callback );
- if( i !== -1 ){
- list.splice( i, 1 );
- if( list.pointer && i <= list.pointer ){
- ++list.removed;
+ switch( arguments.length ){
+ case 0 :
+ return;
+ case 1 :
+ for( type in hash ){
+ X.Dom.Event.remove( element, type );
};
- X.Callback._correct( callback );
- if( list.length === 0 ){
+ X.Dom.Event._chashe.splice( X.Dom.Event._chashe.indexOf( hash ), 1 );
+ break;
+ case 2 :
+ if( helper = hash[ type ] ){
+ list = helper.list;
+ i = list.length;
+ for( ; i; ){
+ callback = list[ --i ];
+ X.Callback._correct( callback );
+ };
+ list.length = 0;
helper.removeEvent();
delete hash[ type ];
+ };
+ break;
+ default :
+ arg2 = arguments[ 2 ];
+ arg3 = arguments[ 3 ];
+ arg4 = arguments[ 4 ];
+ callback = typeof arg2 === 'function' ? X.Callback.create( element, arg2, arg3 ) : X.Callback.create( arg2, arg3, arg4 );
+ list = helper.list;
+ i = list.indexOf( callback );
+ if( i !== -1 ){
+ list.splice( i, 1 );
+ if( list.pointer && i <= list.pointer ){
+ ++list.removed;
+ };
+ X.Callback._correct( callback );
+ if( list.length === 0 ){
+ helper.removeEvent();
+ delete hash[ type ];
+ };
};
- };
};
};
};
};
-X.Dom.Event._Helper =
- document.addEventListener ?
- (function( elm, type ){
- elm.addEventListener( type, this, false );
- }) :
- document.attachEvent ?
- (function( elm, type ){
- elm.attachEvent( 'on' + type, ( this.callback = X.Callback.create( this ) ) );
- }) :
- (function( elm, type ){
- this.callback = elm[ 'on' + type ] = X.Callback.create( this );
- });
-
-/*
- * eventDispatch 中の Event.remove への対処.
- * 現在 dispatch 中の function より以前の function が抜かれた場合の対策
- */
-X.Dom.Event._currentList = void 0;
-
-// eventDispatch 内で remove, add されていたら?
-X.Dom.Event._Helper.prototype.handleEvent = function( e ){
- var list = X.Dom.Event._currentList = this.list,
- e = window.addEventListener ?
- new X.Dom.Event( e ) :
- new X.Dom.Event( event, this.elm ),
- me = X.Dom.Event,
- i = 0,
- ret = 0,
- p, f, r;
- list.removed = 0;
- for( ; i < list.length; ){
- me._currentList = list;
- list.pointer = p = i - list.removed;
- f = list[ p ];
- r = f( e ) || 0;
- me._currentList = null;
- ret |= r;
- if( r & X.Callback.UN_LISTEN ){
- list.splice( p, 1 );
- X.Callback._correct( f );
- if( list.length === 0 ){
- this.removeEvent();
- break;
- };
- } else {
- ++i;
- };
- if( r & X.Callback.CANCEL_NOW ) break;
- };
- delete me._currentList;
- if( window.addEventListener ){
- if( ret & X.Callback.STOP_PROPAGATION ){
- e.stopPropagation();
- };
- if( ret & X.Callback.PREVENT_DEFAULT ){
- e.preventDefault();
- return false;
- };
- } else {
- if( ret & X.Callback.STOP_PROPAGATION ){
- event.cancelBubble = true;
- };
- if( ret & X.Callback.PREVENT_DEFAULT ){
- return event.returnValue = false;
- };
- };
-};
-
-X.Dom.Event._Helper.prototype.removeEvent =
- document.removeEventListener ?
- (function( migrate ){
- this.type && this.elm.removeEventListener( this.type, this, false );
- if( migrate ) return;
- delete this.elm;
- delete this.type;
- delete this.list;
- }) :
- document.detachEvent ?
- (function( migrate ){
- this.type && this.elm.detachEvent( 'on' + this.type, this.callback );
- X.Callback._correct( this.callback );
- if( migrate ) return;
- delete this.elm;
- delete this.type;
- delete this.list;
- delete this.callback;
- }) :
- (function( migrate ){
- this.elm[ 'on' + this.type ] = X.Dom.Event.emptyFunc;
- this.elm[ 'on' + this.type ] = '';
- X.Callback._correct( this.callback );
- if( migrate ) return;
- delete this.elm;
- delete this.type;
- delete this.list;
- delete this.callback;
- });
-
if( !document.removeEventListener && !document.detachEvent ){
X.Dom.Event.emptyFunc = new Function();
};
\ No newline at end of file
--- /dev/null
+/* original:\r
+ * Hammer.JS - v1.0.5 - 2013-04-07\r
+ * http://eightmedia.github.com/hammer.js\r
+ * Jorik Tangelder <j.tangelder@gmail.com>, MIT license\r
+ **/\r
+\r
+X.UI = X.UI || {};\r
+\r
+( function( Math, window, document, undefined ){\r
+ \r
+ var ELEENT_LIST = [],\r
+ HAMMER_LIST = [],\r
+ POINTERS = [],\r
+ ABS = new Function( 'v', 'return v<0?-v:v' );\r
+ \r
+ X.UI.Gesture = Hammer;\r
+ \r
+ function Hammer( uinodeRoot, uinode, type ){\r
+ this.uinode = uinode;\r
+ this.enabled = true;\r
+ \r
+ Hammer.startup && Hammer.startup( uinodeRoot );\r
+\r
+ this.options = Hammer.defaults;\r
+\r
+ // start detection on touchstart\r
+ Utils.addEvents( uinode, Hammer.EVENT_TYPES_START, this );\r
+ \r
+ this.listen( type );\r
+ };\r
+ \r
+ Hammer.defaults = {};\r
+ \r
+ Hammer.prototype.handleEvent = function( e ){\r
+ //var sourceEventType = e.type.toLowerCase();\r
+\r
+ var type = IdToGestureID[ e.type ],\r
+ gestures = Detection.gestures,\r
+ numTouches = 0,// count the total touches on the screen\r
+ pointerType, i, l, touches, ret, active, gesture, startEv,\r
+ deltaTime, deltaX, deltaY, velocity;\r
+ console.log( '1 : ' + type + ' .............. ' + e.type )\r
+ if( !type ) return;\r
+ \r
+ if( e.pointerType ){\r
+ type |= POINTER;\r
+ switch( e.pointerType ){\r
+ case 'touch' :\r
+ case e.MSPOINTER_TYPE_TOUCH :\r
+ type |= TOUCH; break;\r
+ case 'pen' :\r
+ case e.MSPOINTER_TYPE_PEN :\r
+ type |= PEN; break;\r
+ case 'mouse' :\r
+ case e.MSPOINTER_TYPE_MOUSE :\r
+ type |= MOUSE; break;\r
+ default :\r
+ return;\r
+ };\r
+ } else\r
+ if( e.touches ){\r
+ type |= TOUCH;\r
+ } else {\r
+ type |= MOUSE;\r
+ };\r
+ \r
+ // onmouseup, but when touchend has been fired we do nothing.\r
+ // this is for touchdevices which also fire a mouseup on touchend\r
+ if( type & MOUSE && touch_triggered ){\r
+ return X.Callback.STOP_NOW | X.Callback.STOP_PROPAGATION | X.Callback.PREVENT_DEFAULT;\r
+ }\r
+ // mousebutton must be down or a touch event\r
+ else if (\r
+ type & TOUCH || //sourceEventType.match(/touch/) || // touch events are always on screen\r
+ ( type & POINTER && type & START ) || //sourceEventType.match(/pointerdown/) || // pointerevents touch\r
+ ( type & MOUSE && e.which === 1 ) //(sourceEventType.match(/mouse/) && e.which === 1) // mouse is pressed\r
+ ){\r
+ enable_detect = true;\r
+ };\r
+\r
+ // we are in a touch event, set the touch triggered bool to true,\r
+ // this for the conflicts that may occur on ios and android\r
+ type & ( TOUCH | POINTER ) && ( touch_triggered = true );\r
+ //if (sourceEventType.match(/touch|pointer/)) { touch_triggered = true;}\r
+\r
+ // when touch has been triggered in this detection session\r
+ // and we are now handling a mouse event, we stop that to prevent conflicts\r
+ if( enable_detect ){\r
+ // update pointerevent\r
+ if( Hammer.HAS_POINTEREVENTS ){ //eventType !== Hammer.EVENT_END ){\r
+ POINTERS[ e.identifier = e.pointerId ] = type & END ? null : e;\r
+ touches = [];\r
+ // we can use forEach since pointerEvents only is in IE10\r
+ for( i = 0, l = POINTERS.length; i < l; ++i ){\r
+ POINTERS[ i ] && ( touches[ touches.length ] = POINTERS[ i ] );\r
+ };\r
+ numTouches = touches.length;\r
+ }\r
+ // touch\r
+ else if ( type & TOUCH ){ //sourceEventType.match(/touch/)) {\r
+ touches = Hammer.DO_TOUCHES_FIX && type & END ? [] : e.touches;\r
+ numTouches = touches.length;\r
+ }\r
+ // mouse\r
+ else if( !touch_triggered ){\r
+ numTouches = ( type & END ) ? 0 : 1;\r
+ touches = numTouches === 0 ? [] : [{\r
+ identifier : 1,\r
+ pageX : e.pageX,\r
+ pageY : e.pageY,\r
+ target : e.target\r
+ }];\r
+ };\r
+\r
+ // if we are in a end event, but when we remove one touch and\r
+ // we still have enough, set eventType to move\r
+ if( 0 < numTouches && type & END ){ // eventType === Hammer.EVENT_END ){\r
+ type = type & POINTER_TYPE_MASK | MOVE;\r
+ //eventType = Hammer.EVENT_MOVE;\r
+ } else if( !numTouches ){\r
+ // no touches, force the end event\r
+ type = type & POINTER_TYPE_MASK | END;\r
+ //eventType = Hammer.EVENT_END;\r
+ };\r
+\r
+ // because touchend has no touches, and we often want to use these in our gestures,\r
+ // we send the last move event as our eventData in touchend\r
+ ( !numTouches && last_move_event !== null ) ?\r
+ ( e = last_move_event ):\r
+ ( last_move_event = e ); // store the last move event\r
+\r
+ e = {\r
+ center : Utils.getCenter( touches ),\r
+ timeStamp : Date.now ? Date.now() : +new Date,\r
+ target : e.target,\r
+ touches : touches,\r
+ eventType : type & EVENT_TYPE_MASK,\r
+ pointerType : type & POINTER_TYPE_MASK\r
+ };\r
+\r
+ console.log( '2 : ' + type + ' .............. ' + last_move_event.type )\r
+\r
+ if( type & START ){\r
+ console.log( '2a : ' + type + ' .............. ' + last_move_event.type )\r
+ if( !this.enabled ) return;\r
+ // already busy with a Hammer.gesture detection on an element\r
+ console.log( '2b : ' + type + ' .............. ' + last_move_event.type )\r
+ if( Detection.current ) return;\r
+ Detection.current = {\r
+ hammer : this, // reference to HammerInstance we're working for\r
+ startEvent : Utils.extend( {}, e ), // start eventData for distances, timing etc\r
+ lastEvent : false, // last eventData\r
+ name : '' // current gesture we're in/detected, can be 'tap', 'hold' etc\r
+ };\r
+ Detection.stopped = false;\r
+ hammer = this;\r
+ active = hammer.activeGesture;\r
+ } else\r
+ if( !Detection.current || Detection.stopped ){\r
+ return;\r
+ } else {\r
+ hammer = Detection.current.hammer;\r
+ active = hammer.activeGesture;\r
+ };\r
+ \r
+ console.log( '3 : ' + type + ' .............. ' + last_move_event.type )\r
+ \r
+ // ----------------------------------------------------------------------------------------------------------------\r
+ // ret = Detection.detect( e );\r
+\r
+ // ----------------------------------------------------------------------------------------------------------------\r
+ // extend event data with calculations about scale, distance etc\r
+ // e = Detection.extendEventData( e );\r
+ startEv = Detection.current.startEvent;\r
+ center = e.center;\r
+\r
+ // if the touches change, set the new touches over the startEvent touches\r
+ // this because touchevents don't have all the touches on touchstart, or the\r
+ // user must place his fingers at the EXACT same time on the screen, which is not realistic\r
+ // but, sometimes it happens that both fingers are touching at the EXACT same time\r
+ if( startEv && ( numTouches !== startEv.touches.length || touches === startEv.touches ) ){\r
+ // extend 1 level deep to get the touchlist with the touch objects\r
+ startEv.touches.length = i = 0;\r
+ for( ; i < numTouches; ++i ){\r
+ startEv.touches[ startEv.touches.length ] = Utils.extend( {}, touches[ i ] );\r
+ };\r
+ };\r
+\r
+ deltaTime = e.timeStamp - startEv.timeStamp;\r
+ deltaX = center.pageX - startEv.center.pageX;\r
+ deltaY = center.pageY - startEv.center.pageY;\r
+ velocity = Utils.getVelocity( deltaTime, deltaX, deltaY );\r
+\r
+ Utils.extend( e, {\r
+ deltaTime : deltaTime,\r
+\r
+ deltaX : deltaX,\r
+ deltaY : deltaY,\r
+\r
+ velocityX : velocity.x,\r
+ velocityY : velocity.y,\r
+\r
+ distance : Utils.getDistance( startEv.center, center ),\r
+ angle : Utils.getAngle( startEv.center, center ),\r
+ direction : Utils.getDirection( startEv.center, center ),\r
+\r
+ scale : Utils.getScale( startEv.touches, touches ),\r
+ rotation : Utils.getRotation( startEv.touches, touches ),\r
+\r
+ startEvent : startEv\r
+ });\r
+\r
+\r
+ // call Hammer.gesture handlers\r
+ for( i = 0, l = gestures.length; i < l; ++i ){\r
+ gesture = gestures[ i ];\r
+ if( Detection.stopped ) break;\r
+ if( active[ gesture.name ] ) console.log( gesture.name );\r
+ // only when the instance options have enabled this gesture\r
+ active[ gesture.name ] &&\r
+ // if a handler returns false, we stop with the detection\r
+ ( ret |= ( gesture.handler.call( gesture, e, hammer ) || X.Callback.NONE ) );\r
+ };\r
+\r
+ // store as previous event event\r
+ Detection.current.lastEvent = e;\r
+\r
+ // endevent, but not the last touch, so dont stop\r
+ type & END && numTouches === 0 && Detection.stopDetect();\r
+ \r
+ // ----------------------------------------------------------------------------------------------------------------\r
+ // trigger the handler\r
+ //handler.call( context, HamEvent.collectEventData( element, eventType, e ) );\r
+\r
+ // remove pointerevent from list\r
+ if( Hammer.HAS_POINTEREVENTS && type & END ){ // eventType === Hammer.EVENT_END ){\r
+ numTouches = 0;\r
+ };\r
+ };\r
+\r
+ //debug(sourceEventType +" "+ eventType);\r
+\r
+ // on the end we reset everything\r
+ if( numTouches === 0 ){\r
+ last_move_event = null;\r
+ enable_detect = false;\r
+ touch_triggered = false;\r
+ POINTERS.length = 0;\r
+ };\r
+ \r
+ return ret;\r
+ };\r
+ \r
+ Hammer.startup = function( uinodeRoot ){\r
+ // find what eventtypes we add listeners to\r
+ /**\r
+ * we have different events for each device/browser\r
+ * determine what we need and set them in the Hammer.EVENT_TYPES constant\r
+ */\r
+ // determine the eventtype we want to set\r
+ // for non pointer events browsers and mixed browsers,\r
+ // like chrome on windows8 touch laptop \r
+ var types, name;\r
+\r
+ // Register all gestures inside Gestures\r
+ for( name in Gestures ){\r
+ Gestures.hasOwnProperty( name ) && Detection.register( Gestures[ name ] );\r
+ };\r
+\r
+ if( navigator.pointerEnabled || navigator.msPointerEnabled ){\r
+ Hammer.EVENT_TYPES_START = [ X.View.Event._POINTER_DOWN ];\r
+ types = [ X.View.Event._POINTER_MOVE, X.View.Event._POINTER_UP, X.View.Event._POINTER_CANCEL ];\r
+ } else\r
+ if( window.ontouchstart !== void 0 ){\r
+ Hammer.EVENT_TYPES_START = [ X.View.Event._TOUCH_START ];\r
+ types = [ X.View.Event._TOUCH_MOVE, X.View.Event._MOUSE_MOVE, X.View.Event._TOUCH_END, X.View.Event._TOUCH_CANCEL ];\r
+ } else {\r
+ Hammer.EVENT_TYPES_START = [ X.View.Event._MOUSE_DOWN ];\r
+ types = [ X.View.Event._MOUSE_MOVE, X.View.Event._MOUSE_UP, X.View.Event._MOUSE_CANCEL ];\r
+ };\r
+\r
+ // Add touch events on the document\r
+ Utils.addEvents( uinodeRoot, types, Hammer.prototype.handleEvent );\r
+\r
+ // Hammer is ready...!\r
+ delete Hammer.startup;\r
+ };\r
+ \r
+ Hammer.prototype.trigger = function( type, gesture ){\r
+ if( !this.types[ type ] ) return;\r
+ var e = Utils.extend( {}, gesture );\r
+ e.type = type;\r
+ console.log( 'trigger : ' + type )\r
+ return this.uinode.dispatch( e );\r
+ };\r
+ \r
+ Hammer.prototype.listen = function( type ){\r
+ var gestures = Detection.gestures,\r
+ i = gestures.length, g;\r
+ for( ; i; ){\r
+ g = gestures[ --i ];\r
+ if( g.startID <= type && type <= g.endID ){\r
+ if( !this.activeGesture ) this.activeGesture = {};\r
+ if( !this.types ) this.types = {};\r
+ this.activeGesture[ g.name ] = this.types[ type ] = 1;\r
+ return;\r
+ };\r
+ };\r
+ };\r
+ \r
+ Hammer.prototype.unlisten = function( type ){\r
+ var gestures = Detection.gestures,\r
+ i = gestures.length, g;\r
+ if( !this.activeGesture ) return;\r
+ for( ; i; ){\r
+ g = gestures[ --i ];\r
+ if( g.startID <= type && type <= g.endID ){\r
+ if( this.activeGesture[ g.name ] ){\r
+ if( this.types[ type ] ) delete this.types[ type ];\r
+ for( i = g.startID; i <= g.endID; ++i ){\r
+ if( this.types[ i ] ) return;\r
+ };\r
+ delete this.activeGesture[ g.name ];\r
+ };\r
+ return;\r
+ };\r
+ };\r
+ };\r
+ \r
+ /*\r
+ * "Android version < 2.2" return ev.touches.length === 1 when touchend, others return ev.touches.length === 0\r
+ */\r
+ Hammer.DO_TOUCHES_FIX = Hammer.HAS_TOUCHEVENTS && ( function( ua, i ){\r
+ if( ( i = ua.indexOf('android') ) === -1 ) return false;\r
+ return ( parseFloat( ua.substr( i + 8 ) ) || 0 ) < 2.2;\r
+ })( navigator.userAgent.toLowerCase() );\r
+ \r
+ // detect touchevents\r
+ Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled;\r
+\r
+ // eventtypes per touchevent (start, move, end)\r
+ // are filled by HamEvent.determineEventTypes on setup\r
+ Hammer.EVENT_TYPES_START = null;\r
+\r
+ // direction defines\r
+ Hammer.DIRECTION_DOWN = 'down';\r
+ Hammer.DIRECTION_LEFT = 'left';\r
+ Hammer.DIRECTION_UP = 'up';\r
+ Hammer.DIRECTION_RIGHT = 'right';\r
+\r
+ // plugins namespace\r
+ Hammer.plugins = {};\r
+\r
+ var POINTER = 1,\r
+ TOUCH = 2,\r
+ PEN = 8, //4,\r
+ MOUSE = 8,\r
+ START = 16,\r
+ MOVE = 32,\r
+ END = 64,\r
+ CANCEL = 128,\r
+ EVENT_TYPE_MASK = START | MOVE | END,\r
+ POINTER_TYPE_MASK = POINTER | TOUCH | MOUSE | PEN,\r
+ IdToGestureID = {};\r
+ IdToGestureID[ X.View.Event._POINTER_DOWN ] = START;\r
+ IdToGestureID[ X.View.Event._POINTER_MOVE ] = MOVE;\r
+ IdToGestureID[ X.View.Event._POINTER_UP ] = END;\r
+ IdToGestureID[ X.View.Event._POINTER_CANCEL ] = END;\r
+ \r
+ IdToGestureID[ X.View.Event._TOUCH_START ] = START;\r
+ IdToGestureID[ X.View.Event._TOUCH_MOVE ] = MOVE;\r
+ IdToGestureID[ X.View.Event._TOUCH_END ] = END;\r
+ IdToGestureID[ X.View.Event._TOUCH_CANCEL ] = END;\r
+ \r
+ IdToGestureID[ X.View.Event._MOUSE_DOWN ] = START;\r
+ IdToGestureID[ X.View.Event._MOUSE_MOVE ] = MOVE;\r
+ IdToGestureID[ X.View.Event._MOUSE_UP ] = END;\r
+ IdToGestureID[ X.View.Event._MOUSE_CANCEL ] = END;\r
+ \r
+ Utils = {\r
+ \r
+ /**\r
+ * touch events with mouse fallback\r
+ * @param {HTMLElement} element\r
+ * @param {String} eventType like Hammer.EVENT_MOVE\r
+ * @param {Function} handler\r
+ */\r
+ addEvents : function( uinode, types, context ){\r
+ for( var i = 0; i < types.length; ++i ){\r
+ uinode.listen( types[ i ], context );\r
+ };\r
+ },\r
+ \r
+ /**\r
+ * extend method,\r
+ * also used for cloning when dest is an empty object\r
+ * @param {Object} dest\r
+ * @param {Object} src\r
+ * @parm {Boolean} merge do a merge\r
+ * @returns {Object} dest\r
+ */\r
+ extend : function extend( dest, src, merge ){\r
+ for( var key in src ){\r
+ if( dest[ key ] !== undefined && merge ) continue;\r
+ dest[ key ] = src[ key ];\r
+ };\r
+ return dest;\r
+ },\r
+\r
+ /**\r
+ * find if a node is in the given parent\r
+ * used for event delegation tricks\r
+ * @param {HTMLElement} node\r
+ * @param {HTMLElement} parent\r
+ * @returns {boolean} has_parent\r
+ */\r
+ hasParent : function( node, parent ){\r
+ while( node && node.tagName ){ /* tagName for ie */\r
+ if( node === parent ) return true;\r
+ node = node.parentNode;\r
+ };\r
+ return false;\r
+ },\r
+\r
+ /**\r
+ * get the center of all the touches\r
+ * @param {Array} touches\r
+ * @returns {Object} center\r
+ */\r
+ getCenter : function getCenter(touches) {\r
+ var i = 0,\r
+ l = touches.length,\r
+ valuesX, valuesY;\r
+ switch( l ){\r
+ case 0 :\r
+ return {};\r
+ case 1 :\r
+ return {\r
+ pageX : touches[ 0 ].pageX,\r
+ pageY : touches[ 0 ].pageY\r
+ };\r
+ case 2 :\r
+ return {\r
+ pageX : ( touches[ 0 ].pageX + touches[ 1 ].pageX ) / 2,\r
+ pageY : ( touches[ 0 ].pageY + touches[ 1 ].pageY ) / 2\r
+ };\r
+ };\r
+ valuesX = [];\r
+ valuesY = [];\r
+ for( ; i < l; ++i ){\r
+ valuesX[ valuesX.length ] = touches[ i ].pageX;\r
+ valuesY[ valuesY.length ] = touches[ i ].pageY;\r
+ };\r
+ return {\r
+ pageX : ( ( Math.min.apply( null, valuesX ) + Math.max.apply( null, valuesX ) ) / 2 ),\r
+ pageY : ( ( Math.min.apply( null, valuesY ) + Math.max.apply( null, valuesY ) ) / 2 )\r
+ };\r
+ },\r
+\r
+ /**\r
+ * calculate the velocity between two points\r
+ * @param {Number} deltaTime\r
+ * @param {Number} deltaX\r
+ * @param {Number} deltaY\r
+ * @returns {Object} velocity\r
+ */\r
+ getVelocity : function getVelocity( deltaTime, deltaX, deltaY ) {\r
+ return {\r
+ x : ABS( deltaX / deltaTime ) || 0,\r
+ y : ABS( deltaY / deltaTime ) || 0\r
+ };\r
+ },\r
+\r
+ /**\r
+ * calculate the angle between two coordinates\r
+ * @param {Touch} touch1\r
+ * @param {Touch} touch2\r
+ * @returns {Number} angle\r
+ */\r
+ getAngle : function getAngle(touch1, touch2) {\r
+ var y = touch2.pageY - touch1.pageY,\r
+ x = touch2.pageX - touch1.pageX;\r
+ return Math.atan2( y, x ) * 180 / Math.PI;\r
+ },\r
+\r
+ /**\r
+ * angle to direction define\r
+ * @param {Touch} touch1\r
+ * @param {Touch} touch2\r
+ * @returns {String} direction constant, like Hammer.DIRECTION_LEFT\r
+ */\r
+ getDirection : function getDirection( touch1, touch2 ){\r
+ var x = touch1.pageX - touch2.pageX,\r
+ y = touch1.pageY - touch2.pageY;\r
+ return ABS( y ) <= ABS( x ) ?\r
+ ( x > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT ) :\r
+ ( y > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN );\r
+ },\r
+\r
+ /**\r
+ * calculate the distance between two touches\r
+ * @param {Touch} touch1\r
+ * @param {Touch} touch2\r
+ * @returns {Number} distance\r
+ */\r
+ getDistance : function getDistance( touch1, touch2 ){\r
+ var x = touch2.pageX - touch1.pageX,\r
+ y = touch2.pageY - touch1.pageY;\r
+ return Math.sqrt( ( x * x ) + ( y * y ) );\r
+ },\r
+\r
+ /**\r
+ * calculate the scale factor between two touchLists (fingers)\r
+ * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out\r
+ * @param {Array} start\r
+ * @param {Array} end\r
+ * @returns {Number} scale\r
+ */\r
+ getScale : function getScale( start, end ){\r
+ // need two fingers...\r
+ return ( 2 <= start.length && 2 <= end.length ) ?\r
+ Utils.getDistance( end[ 0 ], end[ 1 ] ) / Utils.getDistance( start[ 0 ], start[ 1 ] ) :\r
+ 1;\r
+ },\r
+\r
+ /**\r
+ * calculate the rotation degrees between two touchLists (fingers)\r
+ * @param {Array} start\r
+ * @param {Array} end\r
+ * @returns {Number} rotation\r
+ */\r
+ getRotation : function getRotation( start, end ){\r
+ // need two fingers\r
+ return ( 2 <= start.length && 2 <= end.length ) ?\r
+ Utils.getAngle( end[ 1 ], end[ 0 ] ) - Utils.getAngle( start[ 1 ], start[ 0 ] ) :\r
+ 0;\r
+ },\r
+\r
+ /**\r
+ * boolean if the direction is vertical\r
+ * @param {String} direction\r
+ * @returns {Boolean} is_vertical\r
+ */\r
+ isVertical : function isVertical( direction ){\r
+ return direction === Hammer.DIRECTION_UP || direction === Hammer.DIRECTION_DOWN;\r
+ }\r
+ };\r
+ \r
+ /**\r
+ * this holds the last move event,\r
+ * used to fix empty touchend issue\r
+ * see the onTouch event for an explanation\r
+ * @type {Object}\r
+ */\r
+ var last_move_event = null;\r
+\r
+ /**\r
+ * when the mouse is hold down, this is true\r
+ * @type {Boolean}\r
+ */\r
+ var enable_detect = false;\r
+\r
+ /**\r
+ * when touch events have been fired, this is true\r
+ * @type {Boolean}\r
+ */\r
+ var touch_triggered = false;\r
+ \r
+ Detection = {\r
+ // contains all registred Gestures in the correct order\r
+ gestures : [],\r
+\r
+ // data of the current Hammer.gesture detection session\r
+ current : null,\r
+\r
+ // the previous Hammer.gesture session data\r
+ // is a full clone of the previous gesture.current object\r
+ previous : null,\r
+\r
+ // when this becomes true, no gestures are fired\r
+ stopped : false,\r
+\r
+ /**\r
+ * clear the Hammer.gesture vars\r
+ * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected\r
+ * to stop other Gestures from being fired\r
+ */\r
+ stopDetect : function stopDetect() {\r
+ // clone current data to the store as the previous gesture\r
+ // used for the double tap gesture, since this is an other gesture detect session\r
+ Detection.previous = Utils.extend( {}, Detection.current );\r
+\r
+ // reset the current\r
+ Detection.current = null;\r
+\r
+ // stopped!\r
+ Detection.stopped = true;\r
+ },\r
+\r
+ /**\r
+ * register new gesture\r
+ * @param {Object} gesture object, see gestures.js for documentation\r
+ * @returns {Array} gestures\r
+ */\r
+ register : function( gesture ){\r
+ // add an enable gesture options if there is no given\r
+ var options = gesture.defaults || {};\r
+ if( options[ gesture.name ] === undefined ) options[ gesture.name ] = true;\r
+\r
+ // extend Hammer default options with the Hammer.gesture options\r
+ Utils.extend( Hammer.defaults, options, true );\r
+\r
+ // set its index\r
+ gesture.index = gesture.index || 1000;\r
+\r
+ // add Hammer.gesture to the list\r
+ Detection.gestures.push( gesture );\r
+\r
+ // sort the list by index\r
+ Detection.gestures.sort( function( a, b ){\r
+ return\r
+ a.index < b.index ? -1 :\r
+ a.index > b.index ? 1 : 0;\r
+ });\r
+ }\r
+ };\r
+\r
+ var Gestures = Gestures || {};\r
+\r
+ /**\r
+ * Custom gestures\r
+ * ==============================\r
+ *\r
+ * Gesture object\r
+ * --------------------\r
+ * The object structure of a gesture:\r
+ *\r
+ * { name: 'mygesture',\r
+ * index: 1337,\r
+ * defaults: {\r
+ * mygesture_option: true\r
+ * }\r
+ * handler: function(type, e, inst) {\r
+ * // trigger gesture event\r
+ * inst.trigger(this.name, e );\r
+ * }\r
+ * }\r
+\r
+ * @param {String} name\r
+ * this should be the name of the gesture, lowercase\r
+ * it is also being used to disable/enable the gesture per instance config.\r
+ *\r
+ * @param {Number} [index=1000]\r
+ * the index of the gesture, where it is going to be in the stack of gestures detection\r
+ * like when you build an gesture that depends on the drag gesture, it is a good\r
+ * idea to place it after the index of the drag gesture.\r
+ *\r
+ * @param {Object} [defaults={}]\r
+ * the default settings of the gesture. these are added to the instance settings,\r
+ * and can be overruled per instance. you can also add the name of the gesture,\r
+ * but this is also added by default (and set to true).\r
+ *\r
+ * @param {Function} handler\r
+ * this handles the gesture detection of your custom gesture and receives the\r
+ * following arguments:\r
+ *\r
+ * @param {Object} eventData\r
+ * event data containing the following properties:\r
+ * timeStamp {Number} time the event occurred\r
+ * target {HTMLElement} target element\r
+ * touches {Array} touches (fingers, pointers, mouse) on the screen\r
+ * pointerType {String} kind of pointer that was used. matches Hammer.POINTER_MOUSE|TOUCH\r
+ * center {Object} center position of the touches. contains pageX and pageY\r
+ * deltaTime {Number} the total time of the touches in the screen\r
+ * deltaX {Number} the delta on x axis we haved moved\r
+ * deltaY {Number} the delta on y axis we haved moved\r
+ * velocityX {Number} the velocity on the x\r
+ * velocityY {Number} the velocity on y\r
+ * angle {Number} the angle we are moving\r
+ * direction {String} the direction we are moving. matches Hammer.DIRECTION_UP|DOWN|LEFT|RIGHT\r
+ * distance {Number} the distance we haved moved\r
+ * scale {Number} scaling of the touches, needs 2 touches\r
+ * rotation {Number} rotation of the touches, needs 2 touches *\r
+ * eventType {String} matches Hammer.EVENT_START|MOVE|END\r
+ * srcEvent {Object} the source event, like TouchStart or MouseDown *\r
+ * startEvent {Object} contains the same properties as above,\r
+ * but from the first touch. this is used to calculate\r
+ * distances, deltaTime, scaling etc\r
+ *\r
+ * @param {Hammer.Instance} inst\r
+ * the instance we are doing the detection for. you can get the options from\r
+ * the inst.options object and trigger the gesture event by calling inst.trigger\r
+ *\r
+ *\r
+ * Handle gestures\r
+ * --------------------\r
+ * inside the handler you can get/set Detection.current. This is the current\r
+ * detection session. It has the following properties\r
+ * @param {String} name\r
+ * contains the name of the gesture we have detected. it has not a real function,\r
+ * only to check in other gestures if something is detected.\r
+ * like in the drag gesture we set it to 'drag' and in the swipe gesture we can\r
+ * check if the current gesture is 'drag' by accessing Detection.current.name\r
+ *\r
+ * @readonly\r
+ * @param {Hammer.Instance} inst\r
+ * the instance we do the detection for\r
+ *\r
+ * @readonly\r
+ * @param {Object} startEvent\r
+ * contains the properties of the first gesture detection in this session.\r
+ * Used for calculations about timing, distance, etc.\r
+ *\r
+ * @readonly\r
+ * @param {Object} lastEvent\r
+ * contains all the properties of the last gesture detect in this session.\r
+ *\r
+ * after the gesture detection session has been completed (user has released the screen)\r
+ * the Detection.current object is copied into Detection.previous,\r
+ * this is usefull for gestures like doubletap, where you need to know if the\r
+ * previous gesture was a tap\r
+ *\r
+ * options that have been set by the instance can be received by calling inst.options\r
+ *\r
+ * You can trigger a gesture event by calling inst.trigger("mygesture", event).\r
+ * The first param is the name of your gesture, the second the event argument\r
+ *\r
+ *\r
+ * Register gestures\r
+ * --------------------\r
+ * When an gesture is added to the Gestures object, it is auto registered\r
+ * at the setup of the first Hammer instance. You can also call Detection.register\r
+ * manually and pass your gesture object as a param\r
+ *\r
+ */\r
+\r
+ /**\r
+ * Hold\r
+ * Touch stays at the same place for x time\r
+ * @events hold holdend\r
+ */\r
+ Gestures.Hold = {\r
+ name : 'hold',\r
+ index : 10,\r
+ startID : X.View.Event.HOLD,\r
+ endID : X.View.Event.HOLD_END,\r
+ defaults : {\r
+ hold_timeout : 500,\r
+ hold_threshold : 1\r
+ },\r
+ timerID : null,\r
+ holding : false,\r
+ handler : function holdGesture( e, hammer ){\r
+ switch( e.eventType ){\r
+ case START :\r
+ // clear any running timers\r
+ this.timerID && X.Timer.remove( this.timerID );\r
+\r
+ // set the gesture so we can check in the timeout if it still is\r
+ Detection.current.name = this.name;\r
+ Gestures.Hold.holding = false;\r
+ \r
+ // set timer and if after the timeout it still is hold,\r
+ // we trigger the hold event\r
+ this.timerID = X.Timer.add( hammer.options.hold_timeout, 1, Gestures.Hold._onTimer, [ e, hammer ] );\r
+ return;\r
+\r
+ // when you move or end we clear the timer\r
+ case MOVE :\r
+ if( e.distance <= hammer.options.hold_threshold ) return;\r
+ case END :\r
+ this.timerID && X.Timer.remove( this.timerID );\r
+ if( Gestures.Hold.holding === true ){\r
+ Gestures.Hold.holding = false;\r
+ return hammer.trigger( X.View.Event.HOLD_END, e );\r
+ };\r
+ break;\r
+ };\r
+ },\r
+ _onTimer : function( e, hammer ){\r
+ if( Detection.current.name === 'hold' ){\r
+ hammer.trigger( X.View.Event.HOLD, e );\r
+ Gestures.Hold.holding = true;\r
+ };\r
+ }\r
+ };\r
+\r
+ /**\r
+ * Tap/DoubleTap\r
+ * Quick touch at a place or double at the same place\r
+ * @events tap, doubletap\r
+ */\r
+ Gestures.Tap = {\r
+ name : 'tap',\r
+ index : 100,\r
+ startID : X.View.Event.TAP,\r
+ endID : X.View.Event.DOUBLE_TAP,\r
+ defaults : {\r
+ tap_max_touchtime : 250,\r
+ tap_max_distance : 10,\r
+ tap_always : true,\r
+ doubletap_distance : 20,\r
+ doubletap_interval : 300\r
+ },\r
+ handler : function tapGesture( e, hammer ){\r
+ // previous gesture, for the double tap since these are two different gesture detections\r
+ var prev = Detection.previous;\r
+ if( e.eventType === END ){\r
+ // when the touchtime is higher then the max touch time\r
+ // or when the moving distance is too much\r
+ if( hammer.options.tap_max_touchtime < e.deltaTime || hammer.options.tap_max_distance < e.distance ) return;\r
+\r
+ // check if double tap\r
+ if( prev && prev.name === 'tap' && ( e.timeStamp - prev.lastEvent.timeStamp ) < hammer.options.doubletap_interval && e.distance < hammer.options.doubletap_distance ){\r
+ return hammer.trigger( X.View.Event.DOUBLE_TAP, e );\r
+ } else\r
+ // do a single tap\r
+ if( hammer.options.tap_always ){\r
+ Detection.current.name = 'tap';\r
+ return hammer.trigger( X.View.Event.TAP, e );\r
+ };\r
+ };\r
+ }\r
+ };\r
+\r
+ /**\r
+ * Swipe\r
+ * triggers swipe events when the end velocity is above the threshold\r
+ * @events swipe, swipeleft, swiperight, swipeup, swipedown\r
+ */\r
+ Gestures.Swipe = {\r
+ name : 'swipe',\r
+ index : 40,\r
+ startID : X.View.Event.SWIP,\r
+ endID : X.View.Event.SWIP_DOWN,\r
+ defaults : {\r
+ // set 0 for unlimited, but this can conflict with transform\r
+ swipe_max_touches : 1,\r
+ swipe_velocity : 0.7\r
+ },\r
+ handler : function swipeGesture(e, hammer) {\r
+ if( e.eventType === END ){\r
+ // max touches\r
+ if( 0 < hammer.options.swipe_max_touches && hammer.options.swipe_max_touches < e.touches.length ) return;\r
+\r
+ // when the distance we moved is too small we skip this gesture\r
+ // or we can be already in dragging\r
+ if( hammer.options.swipe_velocity < e.velocityX || hammer.options.swipe_velocity < e.velocityY ){\r
+ // trigger swipe events\r
+ hammer.trigger( X.View.Event.SWIP, e );\r
+ hammer.trigger(\r
+ e.direction === Hammer.DIRECTION_UP ?\r
+ X.View.Event.SWIP_UP :\r
+ e.direction === Hammer.DIRECTION_DOWN ?\r
+ X.View.Event.SWIP_DOWN :\r
+ e.direction === Hammer.DIRECTION_LEFT ?\r
+ X.View.Event.SWIP_LEFT :\r
+ X.View.Event.SWIP_RIGHT,\r
+ e\r
+ );\r
+ };\r
+ };\r
+ }\r
+ };\r
+\r
+ /**\r
+ * Drag\r
+ * Move with x fingers (default 1) around on the page. Blocking the scrolling when\r
+ * moving left and right is a good practice. When all the drag events are blocking\r
+ * you disable scrolling on that area.\r
+ * @events drag, dragstart, dragend, drapleft, dragright, dragup, dragdown\r
+ */\r
+ Gestures.Drag = {\r
+ name : 'drag',\r
+ index : 50,\r
+ startID : X.View.Event.DRAG,\r
+ endID : X.View.Event.DRAG_DOWN,\r
+ defaults : {\r
+ drag_min_distance : 10,\r
+ // set 0 for unlimited, but this can conflict with transform\r
+ drag_max_touches : 1,\r
+ // prevent default browser behavior when dragging occurs\r
+ // be careful with it, it makes the element a blocking element\r
+ // when you are using the drag gesture, it is a good practice to set this true\r
+ drag_block_horizontal : false,\r
+ drag_block_vertical : false,\r
+ // drag_lock_to_axis keeps the drag gesture on the axis that it started on,\r
+ // It disallows vertical directions if the initial direction was horizontal, and vice versa.\r
+ drag_lock_to_axis : false,\r
+ // drag lock only kicks in when distance > drag_lock_min_distance\r
+ // This way, locking occurs only when the distance has become large enough to reliably determine the direction\r
+ drag_lock_min_distance : 25\r
+ },\r
+ triggered : false,\r
+ handler : function dragGesture( e, hammer ){\r
+ var last_direction;\r
+ // current gesture isnt drag, but dragged is true\r
+ // this means an other gesture is busy. now call dragend\r
+ if( Detection.current.name !== this.name && this.triggered ){\r
+ hammer.trigger( X.View.Event.DRAG_END, e );\r
+ this.triggered = false;\r
+ return;\r
+ };\r
+\r
+ // max touches\r
+ if( 0 < hammer.options.drag_max_touches && hammer.options.drag_max_touches < e.touches.length ) return;\r
+\r
+ switch( e.eventType ){\r
+ case START:\r
+ this.triggered = false;\r
+ break;\r
+\r
+ case MOVE :\r
+ // when the distance we moved is too small we skip this gesture\r
+ // or we can be already in dragging\r
+ if( e.distance < hammer.options.drag_min_distance && Detection.current.name !== this.name ) return;\r
+\r
+ // we are dragging!\r
+ Detection.current.name = this.name;\r
+\r
+ // lock drag to axis?\r
+ if( Detection.current.lastEvent.drag_locked_to_axis || ( hammer.options.drag_lock_to_axis && hammer.options.drag_lock_min_distance <= e.distance ) ){\r
+ e.drag_locked_to_axis = true;\r
+ };\r
+ last_direction = Detection.current.lastEvent.direction;\r
+ if( e.drag_locked_to_axis && last_direction !== e.direction ){\r
+ // keep direction on the axis that the drag gesture started on\r
+ e.direction = Utils.isVertical( last_direction ) ?\r
+ ( e.deltaY < 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN ) :\r
+ ( e.deltaX < 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT );\r
+ };\r
+\r
+ // first time, trigger dragstart event\r
+ if( !this.triggered ){\r
+ hammer.trigger( X.View.Event.DRAG_START, e );\r
+ this.triggered = true;\r
+ };\r
+\r
+ // trigger normal event\r
+ hammer.trigger( X.View.Event.DRAG, e );\r
+\r
+ // direction event, like dragdown\r
+ hammer.trigger(\r
+ e.direction === Hammer.DIRECTION_UP ?\r
+ X.View.Event.DRAG_UP :\r
+ e.direction === Hammer.DIRECTION_DOWN ?\r
+ X.View.Event.DRAG_DOWN :\r
+ e.direction === Hammer.DIRECTION_LEFT ?\r
+ X.View.Event.DRAG_LEFT :\r
+ X.View.Event.DRAG_RIGHT,\r
+ e\r
+ );\r
+\r
+ // block the browser events\r
+ (\r
+ ( hammer.options.drag_block_vertical && Utils.isVertical( e.direction ) ) ||\r
+ ( hammer.options.drag_block_horizontal && !Utils.isVertical( e.direction ) )\r
+ ) && e.preventDefault();\r
+ break;\r
+\r
+ case END:\r
+ // trigger dragend\r
+ this.triggered && hammer.trigger( X.View.Event.DRAG_END, e );\r
+ this.triggered = false;\r
+ break;\r
+ }\r
+ }\r
+ };\r
+\r
+ /**\r
+ * Transform\r
+ * User want to scale or rotate with 2 fingers\r
+ * @events transform, transformstart, transformend, pinch, pinchin, pinchout, rotate\r
+ */\r
+ Gestures.Transform = {\r
+ name : 'transform',\r
+ index : 45,\r
+ startID : X.View.Event.TRANSFORM,\r
+ endID : X.View.Event.ROTATE,\r
+ defaults : {\r
+ // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1\r
+ transform_min_scale : 0.01,\r
+ // rotation in degrees\r
+ transform_min_rotation : 1,\r
+ // prevent default browser behavior when two touches are on the screen\r
+ // but it makes the element a blocking element\r
+ // when you are using the transform gesture, it is a good practice to set this true\r
+ transform_always_block : false\r
+ },\r
+ triggered : false,\r
+ handler : function transformGesture( e, hammer ){\r
+ // current gesture isnt drag, but dragged is true\r
+ // this means an other gesture is busy. now call dragend\r
+ if( Detection.current.name !== this.name && this.triggered ){\r
+ hammer.trigger( X.View.Event.TRANSFORM_END, e );\r
+ this.triggered = false;\r
+ return;\r
+ };\r
+\r
+ // atleast multitouch\r
+ if( e.touches.length < 2 ) return;\r
+\r
+ // prevent default when two fingers are on the screen\r
+ hammer.options.transform_always_block && e.preventDefault();\r
+\r
+ switch(e.eventType) {\r
+ case START:\r
+ this.triggered = false;\r
+ break;\r
+\r
+ case MOVE:\r
+ var scale_threshold = ABS( 1 - e.scale ),\r
+ rotation_threshold = ABS( e.rotation );\r
+\r
+ // when the distance we moved is too small we skip this gesture\r
+ // or we can be already in dragging\r
+ if( scale_threshold < hammer.options.transform_min_scale && rotation_threshold < hammer.options.transform_min_rotation ) return;\r
+\r
+ // we are transforming!\r
+ Detection.current.name = this.name;\r
+\r
+ // first time, trigger dragstart event\r
+ if( !this.triggered ){\r
+ hammer.trigger( X.View.Event.TRANSFORM_START, e );\r
+ this.triggered = true;\r
+ };\r
+\r
+ hammer.trigger( X.View.Event.TRANSFORM, e );\r
+ // basic transform event\r
+\r
+ // trigger rotate event\r
+ hammer.options.transform_min_rotation < rotation_threshold && hammer.trigger( X.View.Event.ROTATE, e );\r
+\r
+ // trigger pinch event\r
+ if( scale_threshold > hammer.options.transform_min_scale ){\r
+ hammer.trigger( X.View.Event.PINCH, e );\r
+ hammer.trigger( e.scale < 1 ? X.View.Event.PINCH_IN : X.View.Event.PINCH_OUT, e );\r
+ };\r
+ break;\r
+\r
+ case END:\r
+ // trigger dragend\r
+ this.triggered && hammer.trigger( X.View.Event.TRANSFORM_END, e );\r
+ this.triggered = false;\r
+ break;\r
+ };\r
+ }\r
+ };\r
+\r
+ /**\r
+ * Touch\r
+ * Called as first, tells the user has touched the screen\r
+ * @events touch\r
+ */\r
+ Gestures.Touch = {\r
+ name : 'touch',\r
+ index : -Infinity,\r
+ defaults : {\r
+ // call preventDefault at touchstart, and makes the element blocking by\r
+ // disabling the scrolling of the page, but it improves gestures like\r
+ // transforming and dragging.\r
+ // be careful with using this, it can be very annoying for users to be stuck\r
+ // on the page\r
+ prevent_default : false,\r
+\r
+ // disable mouse events, so only touch (or pen!) input triggers events\r
+ prevent_mouseevents : false\r
+ },\r
+ handler : function touchGesture( e, hammer ){\r
+ if( hammer.options.prevent_mouseevents && e.pointerType === MOUSE ){\r
+ Detection.stopDetect();\r
+ return;\r
+ };\r
+\r
+ hammer.options.prevent_default && e.preventDefault();\r
+\r
+ e.eventType === START && hammer.trigger( this.name, e );\r
+ }\r
+ };\r
+\r
+ /**\r
+ * Release\r
+ * Called as last, tells the user has released the screen\r
+ * @events release\r
+ */\r
+ Gestures.Release = {\r
+ name : 'release',\r
+ index : Infinity,\r
+ handler : function releaseGesture( e, hammer ){\r
+ e.eventType === END && hammer.trigger( this.name, e );\r
+ }\r
+ };\r
+ \r
+})( Math, window, document );
\ No newline at end of file