OSDN Git Service

Version 0.6.190, fix X.UI.ScrollBox & X.UI.Gesture.
[pettanr/clientJs.git] / 0.6.x / js / 20_ui / 05_XUI_Gesture.js
index ed6bfec..3aab9e1 100644 (file)
-/* 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
-       \r
-       var ELEENT_LIST = [],\r
-               HAMMER_LIST = [],\r
-               POINTERS    = [],\r
-               ABS = new Function( 'v', 'return v<0?-v:v' );\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
-                       hammer, deltaTime, deltaX, deltaY, velocity, center;\r
-               \r
-               //console.log( 'Hammer@handleEvent ' + XUI_Event.IdToName[ e.type ] + ' ' + e.pointerType + ' ' + type );\r
-               if( !type ) return;\r
-               \r
-               //console.log( e.type + ' dw:' + XUI_Event._POINTER_DOWN + ' up:' + XUI_Event._POINTER_UP + ' mv:' + XUI_Event._POINTER_MOVE );\r
-               \r
-               if( e.pointerType ){\r
-                       type |= POINTER;\r
-                       switch( e.pointerType ){\r
-                               case 'touch' :\r
-                                       type |= TOUCH; break;\r
-                               case 'pen' :\r
-                                       type |= PEN; break;\r
-                               case 'mouse' :\r
-                                       type |= MOUSE; break;\r
-                               default :\r
-                                       return;\r
-                       };\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_PREVENT_DEFAULT;\r
-               } else\r
-               // mousebutton must be down or a touch event\r
-               if( 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.button === 0 ) //(sourceEventType.match(/mouse/) && e.which === 1) // mouse is pressed\r
-               ){\r
-                       enable_detect = true;\r
-               };\r
-\r
-               //console.log( 'Hammer@handleEvent ' + IdToGestureID[ e.type ] + ' ' + e.type + ' ' + XUI_Event._POINTER_DOWN + ' ' + enable_detect );\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
-               type & TOUCH && ( 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
-\r
-                       POINTERS[ e.pointerId ] = type & END ? null : e;\r
-                       touches    = [];\r
-                       numTouches = -1;\r
-                       // we can use forEach since pointerEvents only is in IE10\r
-                       for( i in POINTERS ){\r
-                               POINTERS[ i ] && ( touches[ ++numTouches ] = POINTERS[ i ] );\r
-                       };\r
-                       ++numTouches;\r
-                       ///console.log( 'numTouches ' + numTouches );\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   : e.timeStamp,\r
-                               target      : e.target,\r
-                               touches     : touches,\r
-                               eventType   : type & EVENT_TYPE_MASK,\r
-                               pointerType : type & POINTER_TYPE_MASK\r
-                       };\r
-\r
-                       if( type & START ){\r
-                               if( !this.enabled ) return;\r
-                               // already busy with a Hammer.gesture detection on an element\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
-                       // ----------------------------------------------------------------------------------------------------------------\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
-                       // store as previous event event\r
-                       Detection.current.lastEvent = e;\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( e, hammer ) || X_CALLBACK_NONE ) );\r
-                       };\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 ) && \r
-                       Detection.register( Gestures[ name ] );\r
-               };\r
-\r
-               Hammer.EVENT_TYPES_START = [ XUI_Event._POINTER_DOWN ];\r
-               types = [ XUI_Event._POINTER_MOVE, XUI_Event._POINTER_UP, XUI_Event._POINTER_CANCEL ];\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
-               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 && ( X_UA[ 'Android' ] < 2.2 || X_UA[ 'Blink' ] || X_UA[ 'Opera' ] );\r
-       \r
-       // detect touchevents\r
-       Hammer.HAS_POINTEREVENTS = true; // navigator.pointerEnabled || navigator.msPointerEnabled;\r
-       Hammer.HAS_POINTEREVENTS && console.log( 'Hammer.HAS_POINTEREVENTS : true' );\r
-\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[ XUI_Event._POINTER_DOWN   ] = START;\r
-       IdToGestureID[ XUI_Event._POINTER_MOVE   ] = MOVE;\r
-       IdToGestureID[ XUI_Event._POINTER_UP     ] = END;\r
-       IdToGestureID[ XUI_Event._POINTER_CANCEL ] = END;\r
-       \r
-       var 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
+var XUI_GestureUtils = {\r
                /**\r
                 * get the center of all the touches\r
                 * @param   {Array}     touches\r
                 * @returns {Object}    center\r
                 */\r
-               getCenter : function getCenter(touches) {\r
+               getCenter : function( touches ){\r
                        var i = 0,\r
                                l = touches.length,\r
-                               valuesX, valuesY;\r
+                               x, y, minX, minY, maxX, maxY;\r
+\r
                        switch( l ){\r
                                case 0 :\r
                                        return {};\r
                                                pageY : ( touches[ 0 ].pageY + touches[ 1 ].pageY ) / 2\r
                                        };\r
                        };\r
-                       valuesX = [];\r
-                       valuesY = [];\r
+                       minX = minY = 1 / 0;\r
+                       maxX = maxY = - 1 / 0;\r
                        for( ; i < l; ++i ){\r
-                               valuesX[ valuesX.length ] = touches[ i ].pageX;\r
-                               valuesY[ valuesY.length ] = touches[ i ].pageY;\r
+                               x    = touches[ i ].pageX;\r
+                               minX = x < minX ? x : minX;\r
+                               maxX = maxX < x ? x : maxX;\r
+                               y    = touches[ i ].pageY;\r
+                               minY = y < minY ? y : minY;\r
+                               maxY = maxY < y ? y : maxY;\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
+                               pageX : ( minX + maxX ) / 2 | 0,\r
+                               pageY : ( minY + maxY ) / 2 | 0\r
                        };\r
                },\r
 \r
                 * @param   {Number}    deltaY\r
                 * @returns {Object}    velocity\r
                 */\r
-               getVelocity : function getVelocity( deltaTime, deltaX, deltaY ) {\r
+               getVelocity : function( deltaTime, deltaX, deltaY ) {\r
                        return {\r
-                               x : ABS( deltaX / deltaTime ) || 0,\r
-                               y : ABS( deltaY / deltaTime ) || 0\r
+                               x : Math.abs( deltaX / deltaTime ) || 0,\r
+                               y : Math.abs( deltaY / deltaTime ) || 0\r
                        };\r
                },\r
 \r
                 * @param   {Touch}     touch2\r
                 * @returns {Number}    angle\r
                 */\r
-               getAngle : function getAngle(touch1, touch2) {\r
+               getAngle : function( 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
                 * angle to direction define\r
                 * @param   {Touch}     touch1\r
                 * @param   {Touch}     touch2\r
-                * @returns {String}    direction constant, like Hammer.DIRECTION_LEFT\r
+                * @returns {String}    direction constant, like 'left'\r
                 */\r
-               getDirection : function getDirection( touch1, touch2 ){\r
+               getDirection : function( 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
+                       return Math.abs( y ) <= Math.abs( x ) ?\r
+                               ( x > 0 ? 'left' : 'right' ) :\r
+                               ( y > 0 ? 'up'   : 'down' );\r
                },\r
 \r
                /**\r
                 * @param   {Touch}     touch2\r
                 * @returns {Number}    distance\r
                 */\r
-               getDistance : function getDistance( touch1, touch2 ){\r
+               getDistance : function( touch1, touch2 ){\r
                        var x = touch2.pageX - touch1.pageX,\r
                                y = touch2.pageY - touch1.pageY;\r
                        return Math.sqrt( ( x * x ) + ( y * y ) );\r
                 * @param   {Array}     end\r
                 * @returns {Number}    scale\r
                 */\r
-               getScale : function getScale( start, end ){\r
+               getScale : function( 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
+                               XUI_GestureUtils.getDistance( end[ 0 ], end[ 1 ] ) / XUI_GestureUtils.getDistance( start[ 0 ], start[ 1 ] ) :\r
                                1;\r
                },\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
+                               XUI_GestureUtils.getAngle( end[ 1 ], end[ 0 ] ) - XUI_GestureUtils.getAngle( start[ 1 ], start[ 0 ] ) :\r
                                0;\r
                },\r
 \r
                 * @returns  {Boolean}   is_vertical\r
                 */\r
                isVertical : function isVertical( direction ){\r
-                       return direction === Hammer.DIRECTION_UP || direction === Hammer.DIRECTION_DOWN;\r
+                       return direction === 'up' || 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
+var XUI_Gesture_POINTERS = {},\r
+       XUI_Gesture_CAPTURED = {},\r
+       XUI_Gesture_DEFAULTS = {};\r
 \r
-       /*\r
-        * when touch events have been fired, this is true\r
-        * @type {Boolean}\r
-        */\r
-       var touch_triggered = false;\r
-       \r
-       var Detection = {\r
-               // contains all registred Gestures in the correct order\r
-               gestures : [],\r
+// AbstractUINode に移動\r
 \r
-               // data of the current Hammer.gesture detection session\r
-               current : null,\r
+var XUI_Gesture = Hammer = X_Class_create(\r
+       'X.UI.Gesture',\r
+       X_Class.NONE,\r
+       {\r
+               uinodeRoot    : null,\r
+               uinode        : null,\r
+               options       : null,\r
+               \r
+               activated     : null,\r
+               types         : null,\r
 \r
-               // the previous Hammer.gesture session data\r
-               // is a full clone of the previous gesture.current object\r
-               previous : null,\r
+               triggered     : null,\r
+               canceled      : null,\r
+               \r
+               currentName   : '',\r
+               startEvent    : null,\r
+               lastEvent     : null,\r
+               lastMoveEvent : null,\r
+               \r
+               'Constructor' : function( uinodeRoot, uinode, type, opt_options ){\r
+                       this.uinodeRoot = uinodeRoot;\r
+                       this.uinode     = uinode;\r
+                       this.options    = X_Object_override( X_Object_copy( XUI_Gesture_DEFAULTS ), opt_options );\r
+                       \r
+                       this.triggered  = {};\r
+                       this.canceled   = {};\r
 \r
-               // when this becomes true, no gestures are fired\r
-               stopped : false,\r
+                       uinode[ 'listen' ]( XUI_Event._POINTER_DOWN, this, XUI_Gesture_handleEvent );\r
+                       \r
+                       this[ 'listen' ]( type );\r
+               },\r
+               \r
+               trigger : function( type, gesture ){\r
+                       var e;\r
+                       \r
+                       if( !this.types[ type ] ) return X_CALLBACK_NONE;\r
+                       e = X_Object_copy( gesture );\r
+                       e.type = type;\r
+                       return this.uinode[ 'dispatch' ]( e ) || X_CALLBACK_NONE;\r
+               },\r
+               \r
+               listen : function( type ){\r
+                       var gestures = XUI_Gesture_LIST,\r
+                               i = gestures.length, g;\r
+\r
+                       for( ; i; ){\r
+                               g = gestures[ --i ];\r
+                               if( g.startID <= type && type <= g.endID ){\r
+                                       if( !this.activated ) this.activated = {};\r
+                                       if( !this.types ) this.types = {};\r
+                                       this.activated[ g.name ] = this.types[ type ] = 1;\r
+                                       break;\r
+                               };\r
+                       };\r
+               },\r
+               \r
+               unlisten : function( type ){\r
+                       var gestures = XUI_Gesture_LIST,\r
+                               i = gestures.length,\r
+                               active = this.activated, g;\r
+\r
+                       if( !active ) return;\r
+                       for( ; i; ){\r
+                               g = gestures[ --i ];\r
+                               if( g.startID <= type && type <= g.endID ){\r
+                                       if( active[ 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 active[ g.name ];\r
+                                       };\r
+                                       break;\r
+                               };\r
+                       };\r
+               }\r
+       }\r
+);\r
+\r
+function XUI_Gesture_handleEvent( e ){\r
+               var gestures   = XUI_Gesture_LIST,\r
+                       type       = e.type,\r
+                       uid        = e[ 'pointerId' ],\r
+                       isStart    = type === XUI_Event._POINTER_DOWN,\r
+                       isEnd      = type === XUI_Event._POINTER_UP || type === XUI_Event._POINTER_CANCEL || type === XUI_Event.POINTER_OUT,\r
+                       hammer     = this,\r
+                       isMouse    = e.pointerType === 'mouse',\r
+                       touches    = [], \r
+                       numTouches = 0,// count the total touches on the screen\r
+                       i, p, l, j, captured, ret, activated, gesture, startEv,\r
+                       deltaTime, deltaX, deltaY, velocity, center, startCenter;\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
+               if( !isStart && !hammer.startEvent ) return;\r
 \r
-                       // reset the current\r
-                       Detection.current = null;\r
+if( type === XUI_Event.POINTER_OUT ) console.log( 'canceled ...' + e.button )\r
 \r
-                       // stopped!\r
-                       Detection.stopped = true;\r
-               },\r
+               if( isEnd ){\r
+                       if( XUI_Gesture_POINTERS[ uid ] ){\r
+                               delete XUI_Gesture_POINTERS[ uid ];\r
+                               if( XUI_Gesture_CAPTURED[ uid ] ) delete XUI_Gesture_CAPTURED[ uid ];\r
+                       };\r
+               } else {\r
+                       XUI_Gesture_POINTERS[ uid ] = e;\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
-                               list    = Detection.gestures,\r
-                               _index, i = 0, l = list.length, index;\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
-                       if( l === 0 ){\r
-                               list[ 0 ] = gesture;\r
-                               return;\r
+               // mousebutton must be down or a touch event\r
+               if( ( isEnd || !isMouse || e.button === 0 ) ){\r
+                       numTouches = -1;\r
+\r
+                       for( i in XUI_Gesture_POINTERS ){\r
+                               if( p = XUI_Gesture_POINTERS[ i ] ){\r
+                                       // いずれかの hammer によって束縛されている場合、その束縛している hammer なら\r
+                                       captured = XUI_Gesture_CAPTURED[ p[ 'pointerId' ] ];\r
+                                       if( captured && captured !== hammer ){\r
+                                               continue;\r
+                                       };\r
+                                       touches[ ++numTouches ] = p;\r
+                               };\r
                        };\r
-                       _index = gesture.index;\r
-                       for( i = 0; i < l; ++i ){\r
-                               index = list[ i ].index;\r
-                               if( i === 0 && _index < index ){\r
-                                       list.unshift( gesture );\r
-                                       return;\r
-                               } else\r
-                               if( i === l - 1 ){\r
-                                       list[ l ] = gesture;\r
-                                       return;\r
-                               } else\r
-                               if( index <= _index && _index < list[ i + 1 ].index ){\r
-                                       list.splice( i, 0, gesture );\r
-                                       return;\r
+                       ++numTouches;\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( !numTouches ){ // no touches, force the end event\r
+                               isEnd = true;\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
+                       ( isEnd && hammer.lastMoveEvent ) ? ( e = hammer.lastMoveEvent ) : ( hammer.lastMoveEvent = e ); // store the last move event\r
+\r
+                       hammerEvent = X_Object_copy( e );\r
+                       hammerEvent.touches = touches;\r
+\r
+                       if( isStart && !hammer.startEvent ){\r
+                               console.log( '=- add -=' );\r
+                               // already busy with a Hammer.gesture detection on an element\r
+                               hammer.startEvent = hammerEvent;\r
+                               hammer.uinodeRoot[ 'listen' ]( [ XUI_Event._POINTER_MOVE, XUI_Event._POINTER_UP, XUI_Event._POINTER_CANCEL, XUI_Event.POINTER_OUT ], hammer, XUI_Gesture_handleEvent );                         \r
+                       };\r
+                       \r
+                       startEv = hammer.startEvent;\r
+                       \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
+                               j = -1;\r
+                               for( ; i < numTouches; ++i ){\r
+                                       startEv.touches[ ++j ] = touches[ i ];\r
                                };\r
                        };\r
-               }\r
-       };\r
 \r
-       var Gestures = Gestures || {};\r
+                       deltaTime   = hammerEvent.timestamp  - startEv.timestamp;\r
+                       center      = XUI_GestureUtils.getCenter( touches );\r
+                       startCenter = startEv.center;\r
+                       deltaX      = startCenter ? ( center.pageX - startCenter.pageX ) : 0;\r
+                       deltaY      = startCenter ? ( center.pageY - startCenter.pageY ) : 0;\r
+                       velocity    = XUI_GestureUtils.getVelocity( deltaTime, deltaX, deltaY );\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
+                       X_Object_override( hammerEvent, {\r
+                               type       : isEnd ? XUI_Event._POINTER_UP : type,\r
+                               \r
+                               deltaTime  : deltaTime,\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 : XUI_Event.HOLD,\r
-               endID   : XUI_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( XUI_Event.HOLD_END, e );\r
+                               deltaX     : deltaX,\r
+                               deltaY     : deltaY,\r
+\r
+                               velocityX  : velocity.x,\r
+                               velocityY  : velocity.y,\r
+\r
+                               center     : center,\r
+                               distance   : startCenter ? XUI_GestureUtils.getDistance( startCenter, center ) : 0,\r
+                               angle      : startCenter ? XUI_GestureUtils.getAngle( startCenter, center ) : 0,\r
+                               direction  : startCenter ? XUI_GestureUtils.getDirection( startCenter, center ) : 0,\r
+\r
+                               scale      : XUI_GestureUtils.getScale( startEv.touches, touches ),\r
+                               rotation   : XUI_GestureUtils.getRotation( startEv.touches, touches ),\r
+\r
+                               startEvent : startEv\r
+                       });\r
+\r
+                       // store as previous event event\r
+                       hammer.lastEvent = hammerEvent;\r
+                       activated = hammer.activated;\r
+                       \r
+                       // call Hammer.gesture handlers\r
+                       for( i = 0, l = gestures.length; i < l; ++i ){\r
+                               gesture = gestures[ i ];\r
+\r
+                               if( activated[ gesture.name ] && !hammer.canceled[ gesture.name ] ){\r
+                                       ( console.log( '... ' + i + ' ' + gesture.name ) );\r
+                                       // if a handler returns false, we stop with the detection\r
+                                       ( ret |= ( gesture.handler( hammerEvent, hammer ) || X_CALLBACK_NONE ) );\r
+                               };\r
+\r
+                               if( ret & X_CALLBACK_CAPTURE_POINTER ){\r
+                                       for( i = touches.length; i; ){\r
+                                               uid = touches[ --i ][ 'pointerId' ];\r
+                                               XUI_Gesture_CAPTURED[ uid ] = hammer;\r
+                                               //console.log( 'captured. ' + uid );\r
                                        };\r
                                        break;\r
+                               } else\r
+                               if( ret & X_CALLBACK_STOP_NOW ){\r
+                                       break;\r
+                               };\r
                        };\r
-               },\r
-               _onTimer : function( e, hammer ){\r
-                       if( Detection.current.name === 'hold' ){\r
-                               hammer.trigger( XUI_Event.HOLD, e );\r
-                               Gestures.Hold.holding = true;\r
+                       //console.log( '----' );\r
+               } else {\r
+                       \r
+               };\r
+               \r
+               if( isEnd || ( ret & X_CALLBACK_RELEASE_POINTER ) ){\r
+                       for( i = touches.length; i; ){\r
+                               uid = touches[ --i ][ 'pointerId' ];\r
+                               if( XUI_Gesture_CAPTURED[ uid ] === hammer ){\r
+                                       console.log( 'released. ' + uid );\r
+                                       delete XUI_Gesture_CAPTURED[ uid ];\r
+                               };\r
                        };\r
-               }\r
-       };\r
+               };\r
+\r
+               if( isEnd ){\r
+                       console.log( '=- clear -=' );\r
+                       hammer.uinodeRoot[ 'unlisten' ]( [ XUI_Event._POINTER_MOVE, XUI_Event._POINTER_UP, XUI_Event._POINTER_CANCEL, XUI_Event.POINTER_OUT ], hammer, XUI_Gesture_handleEvent );\r
+                       \r
+                       hammer.previous = {\r
+                               currentName   : hammer.currentName,\r
+                               startEvent    : hammer.startEvent,\r
+                               lastEvent     : hammer.lastEvent,\r
+                               lastMoveEvent : hammer.lastMoveEvent\r
+                       };\r
+                       \r
+                       X_Object_clear( hammer.triggered );\r
+                       X_Object_clear( hammer.canceled  );\r
+                       \r
+                       delete hammer.currentName;\r
+                       delete hammer.startEvent;\r
+                       delete hammer.lastEvent;\r
+                       delete hammer.lastMoveEvent;\r
+                       \r
+                       ret |= X_CALLBACK_RELEASE_POINTER;\r
+               };\r
+               \r
+               return ret;\r
+};\r
+\r
 \r
+\r
+var XUI_Gesture_LIST = [\r
        /**\r
-        * Tap/DoubleTap\r
-        * Quick touch at a place or double at the same place\r
-        * @events  tap, doubletap\r
+        * Touch\r
+        * Called as first, tells the user has touched the screen\r
+        * @events  touch\r
         */\r
-       Gestures.Tap = {\r
-               name     : 'tap',\r
-               index    : 100,\r
-               startID  : XUI_Event.TAP,\r
-               endID    : XUI_Event.DOUBLE_TAP,\r
+       {\r
+               name     : 'touch',\r
+               index    : -Infinity,\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
+                       // 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
-                               // 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( XUI_Event.DOUBLE_TAP, e );\r
-                               } else\r
-                               // do a single tap\r
-                               if( hammer.options.tap_always && Detection.current.name !== 'tap' ){ // EventFire中にalert すると mouseleave で再び呼ばれるのを防ぐ\r
-                                       Detection.current.name = 'tap';\r
-                                       return hammer.trigger( XUI_Event.TAP, e );\r
-                               };\r
+                       // disable mouse events, so only touch (or pen!) input triggers events\r
+                       prevent_mouseevents : false\r
+               },\r
+               handler : function( e, hammer ){\r
+                       if( hammer.options.prevent_mouseevents && e[ 'pointerType' ] === 'mouse' ){\r
+                               return X_CALLBACK_STOP_NOW;\r
                        };\r
-               }\r
-       };\r
 \r
+                       //hammer.options.prevent_default && e.preventDefault();\r
+\r
+                       return e.type === XUI_Event._POINTER_DOWN && hammer.trigger( XUI_Event.TOUCH, e );\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
+        * Transform\r
+        * User want to scale or rotate with 2 fingers\r
+        * @events  transform, transformstart, transformend, pinch, pinchin, pinchout, rotate\r
         */\r
-       Gestures.Swipe = {\r
-               name     : 'swipe',\r
-               index    : 40,\r
-               startID  : XUI_Event.SWIP,\r
-               endID    : XUI_Event.SWIP_DOWN,\r
+       {\r
+               name     : 'transform',\r
+               index    : 45,\r
+               startID  : XUI_Event.TRANSFORM,\r
+               endID    : XUI_Event.ROTATE,\r
                defaults : {\r
-                       // set 0 for unlimited, but this can conflict with transform\r
-                       swipe_max_touches : 1,\r
-                       swipe_velocity    : 0.7\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
-               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( XUI_Event.SWIP, e );\r
-                                       hammer.trigger(\r
-                                               e.direction === Hammer.DIRECTION_UP ?\r
-                                                       XUI_Event.SWIP_UP :\r
-                                               e.direction === Hammer.DIRECTION_DOWN ?\r
-                                                       XUI_Event.SWIP_DOWN :\r
-                                               e.direction === Hammer.DIRECTION_LEFT ?\r
-                                                       XUI_Event.SWIP_LEFT :\r
-                                                       XUI_Event.SWIP_RIGHT,\r
-                                               e\r
-                                       );\r
-                               };\r
+\r
+               handler : function( e, hammer ){\r
+                       var transform = this, ret = X_CALLBACK_NONE, scale_threshold, rotation_threshold;\r
+                       \r
+                       // current gesture isnt drag, but dragged is true\r
+                       // this means an other gesture is busy. now call dragend\r
+                       if( hammer.currentName !== transform.name && hammer.triggered[ transform.name ] ){\r
+                               ret = hammer.trigger( XUI_Event.TRANSFORM_END, e );\r
+                               delete hammer.triggered[ transform.name ];\r
+                               return ret;\r
                        };\r
-               }\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.type ){\r
+                               case XUI_Event._POINTER_DOWN :\r
+                                       //hammer.triggered[ transform.name ] = false;\r
+                                       break;\r
+\r
+                               case XUI_Event._POINTER_MOVE:\r
+                                       scale_threshold    = Math.abs( 1 - e.scale );\r
+                                       rotation_threshold = Math.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
+                                       hammer.currentName = transform.name;\r
+\r
+                                       // first time, trigger dragstart event\r
+                                       if( !hammer.triggered[ transform.name ] ){\r
+                                               ret = hammer.trigger( XUI_Event.TRANSFORM_START, e );\r
+                                               if( ret & X_CALLBACK_PREVENT_DEFAULT ){\r
+                                                       hammer.canceled[ transform.name ] = true;\r
+                                                       break;\r
+                                               };\r
+                                               hammer.triggered[ transform.name ] = true;\r
+                                               break;\r
+                                       };\r
+\r
+                                       ret |= hammer.trigger( XUI_Event.TRANSFORM, e );\r
+                                       // basic transform event\r
+\r
+                                       // trigger rotate event\r
+                                       if( hammer.options.transform_min_rotation < rotation_threshold ){\r
+                                               ret |= hammer.trigger( XUI_Event.ROTATE, e );\r
+                                       };\r
+                                       \r
+                                       // trigger pinch event\r
+                                       if( scale_threshold > hammer.options.transform_min_scale ){\r
+                                               ret |= hammer.trigger( XUI_Event.PINCH, e );\r
+                                               ret |= hammer.trigger( e.scale < 1 ? XUI_Event.PINCH_IN : XUI_Event.PINCH_OUT, e );\r
+                                       };\r
+                                       break;\r
+\r
+                               case XUI_Event.POINTER_OUT :\r
+                               case XUI_Event._POINTER_CANCEL :\r
+                               case XUI_Event._POINTER_UP :\r
+                                       // trigger dragend\r
+                                       ret = hammer.triggered[ transform.name ] && hammer.trigger( XUI_Event.TRANSFORM_END, e );\r
+                                       hammer.triggered[ transform.name ] = false;\r
+                                       break;\r
+                       };\r
+                       return ret;\r
+               }\r
+       },\r
+       \r
        /**\r
         * Drag\r
         * Move with x fingers (default 1) around on the page. Blocking the scrolling when\r
         * you disable scrolling on that area.\r
         * @events  drag, dragstart, dragend, drapleft, dragright, dragup, dragdown\r
         */\r
-       Gestures.Drag = {\r
+       {\r
                name     : 'drag',\r
                index    : 50,\r
                startID  : XUI_Event.DRAG,\r
                endID    : XUI_Event.DRAG_DOWN,\r
+               \r
                defaults : {\r
                        drag_min_distance : 10,\r
                        // set 0 for unlimited, but this can conflict with transform\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
+\r
+               handler : function( e, hammer ){\r
+                       var drag = this, last_direction, ret;\r
+\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( XUI_Event.DRAG_END, e );\r
-                               this.triggered = false;\r
-                               return;\r
+                       if( hammer.currentName !== drag.name && hammer.triggered[ drag.name ] ){\r
+                               ret = hammer.trigger( XUI_Event.DRAG_END, e );\r
+                               hammer.triggered[ drag.name ] = false;\r
+                               return ret;\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
+                       \r
+                       switch( e.type ){\r
+                               case XUI_Event._POINTER_DOWN :\r
+                                       hammer.triggered[ drag.name ] = false;\r
                                        break;\r
 \r
-                               case MOVE :\r
+                               case XUI_Event._POINTER_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
+                                       if( e.distance < hammer.options.drag_min_distance && hammer.currentName !== drag.name ) return;\r
 \r
                                        // we are dragging!\r
-                                       Detection.current.name = this.name;\r
+                                       hammer.currentName = drag.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
+                                       if( hammer.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
+                                       last_direction = hammer.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
+                                               e.direction = XUI_GestureUtils.isVertical( last_direction ) ?\r
+                                                       ( e.deltaY < 0 ? 'up'   : 'down' ) :\r
+                                                       ( e.deltaX < 0 ? 'left' : 'right' );\r
                                        };\r
 \r
+                                       ret = X_CALLBACK_NONE;\r
+\r
                                        // first time, trigger dragstart event\r
-                                       if( !this.triggered ){\r
-                                               hammer.trigger( XUI_Event.DRAG_START, e );\r
-                                               this.triggered = true;\r
+                                       if( !hammer.triggered[ drag.name ] ){\r
+                                               ret = hammer.trigger( XUI_Event.DRAG_START, e );\r
+                                               //if( ret & X_CALLBACK_PREVENT_DEFAULT ){\r
+                                               //      hammer.canceled[ drag.name ] = true;\r
+                                               //      break;\r
+                                               //};\r
+                                               ret |= X_CALLBACK_CAPTURE_POINTER;\r
+                                               console.log( '----- drag start ....' + e.type );\r
+                                               hammer.triggered[ drag.name ] = true;\r
+                                               break;\r
                                        };\r
 \r
+                                       //console.log( '----- drag ....' + e.type );\r
                                        // trigger normal event\r
-                                       hammer.trigger( XUI_Event.DRAG, e );\r
+                                       ret = hammer.trigger( XUI_Event.DRAG, e ) | X_CALLBACK_CAPTURE_POINTER;\r
 \r
                                        // direction event, like dragdown\r
-                                       hammer.trigger(\r
-                                               e.direction === Hammer.DIRECTION_UP ?\r
+                                       ret |= hammer.trigger(\r
+                                               e.direction === 'up' ?\r
                                                        XUI_Event.DRAG_UP :\r
-                                               e.direction === Hammer.DIRECTION_DOWN ?\r
+                                               e.direction === 'down' ?\r
                                                        XUI_Event.DRAG_DOWN :\r
-                                               e.direction === Hammer.DIRECTION_LEFT ?\r
+                                               e.direction === 'left' ?\r
                                                        XUI_Event.DRAG_LEFT :\r
                                                        XUI_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
+                                       /* (\r
+                                               ( hammer.options.drag_block_vertical   &&  XUI_GestureUtils.isVertical( e.direction ) ) ||\r
+                                               ( hammer.options.drag_block_horizontal && !XUI_GestureUtils.isVertical( e.direction ) )\r
+                                       ) && e.preventDefault(); */\r
                                        break;\r
 \r
-                               case END:\r
+                               case XUI_Event.POINTER_OUT :\r
+                                       console.log( 'cancel!!' );\r
+                               case XUI_Event._POINTER_CANCEL :\r
+                               case XUI_Event._POINTER_UP:\r
                                        // trigger dragend\r
-                                       this.triggered && hammer.trigger( XUI_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  : XUI_Event.TRANSFORM,\r
-               endID    : XUI_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( XUI_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( XUI_Event.TRANSFORM_START, e );\r
-                                               this.triggered = true;\r
+                                       if( hammer.triggered[ drag.name ] ){\r
+                                               ret = hammer.trigger( XUI_Event.DRAG_END, e ) | X_CALLBACK_CAPTURE_POINTER;\r
+                                               console.log( '----- drag end ....' + e.type );\r
+                                               hammer.triggered[ drag.name ] = false;\r
                                        };\r
-\r
-                                       hammer.trigger( XUI_Event.TRANSFORM, e );\r
-                                       // basic transform event\r
-\r
-                                       // trigger rotate event\r
-                                       hammer.options.transform_min_rotation < rotation_threshold && hammer.trigger( XUI_Event.ROTATE, e );\r
-\r
-                                       // trigger pinch event\r
-                                       if( scale_threshold > hammer.options.transform_min_scale ){\r
-                                               hammer.trigger( XUI_Event.PINCH, e );\r
-                                               hammer.trigger( e.scale < 1 ? XUI_Event.PINCH_IN : XUI_Event.PINCH_OUT, e );\r
-                                       };\r
-                                       break;\r
-\r
-                               case END:\r
-                                       // trigger dragend\r
-                                       this.triggered && hammer.trigger( XUI_Event.TRANSFORM_END, e );\r
-                                       this.triggered = false;\r
                                        break;\r
                        };\r
+                       return ret;\r
                }\r
-       };\r
+       },\r
 \r
        /**\r
-        * Touch\r
-        * Called as first, tells the user has touched the screen\r
-        * @events  touch\r
+        * Tap/DoubleTap\r
+        * Quick touch at a place or double at the same place\r
+        * @events  tap, doubletap\r
         */\r
-       Gestures.Touch = {\r
-               name     : 'touch',\r
-               index    : -Infinity,\r
+       {\r
+               name     : 'tap',\r
+               index    : 100,\r
+               startID  : XUI_Event.TAP,\r
+               endID    : XUI_Event.DOUBLE_TAP,\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
+                       tap_min_touchtime  : 75,\r
+                       tap_max_touchtime  : 250,\r
+                       tap_max_distance   : 3,\r
+                       tap_always         : true,\r
+                       doubletap_distance : 20,\r
+                       doubletap_interval : 300\r
                },\r
-               handler : function touchGesture( e, hammer ){\r
-                       if( hammer.options.prevent_mouseevents && e.pointerType === MOUSE ){\r
-                               Detection.stopDetect();\r
-                               return;\r
+               handler : function( e, hammer ){\r
+                       // previous gesture, for the double tap since these are two different gesture detections\r
+                       var prev = hammer.previous;\r
+                       \r
+                       if( e.type === XUI_Event._POINTER_UP ){\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
+//console.log( ' rap? ' + e.deltaTime + 'ms' );\r
+                               //if( e.deltaTime < hammer.options.tap_min_touchtime ) return;\r
+\r
+                               // check if double tap\r
+                               if( prev && prev.currentName === 'tap' && ( e.timestamp - prev.lastEvent.timestamp ) < hammer.options.doubletap_interval && e.distance < hammer.options.doubletap_distance ){\r
+                                       return hammer.trigger( XUI_Event.DOUBLE_TAP, e );\r
+                               } else\r
+                               // do a single tap\r
+                               if( hammer.options.tap_always && hammer.currentName !== 'tap' ){ // EventFire中にalert すると mouseleave で再び呼ばれるのを防ぐ\r
+                                       //hammer.currentName = 'tap';\r
+                                       console.log( 'tap! ' + e.deltaTime + 'ms' );\r
+                                       return hammer.trigger( XUI_Event.TAP, e );\r
+                               };\r
                        };\r
+               }\r
+       }\r
 \r
-                       hammer.options.prevent_default && e.preventDefault();\r
+];\r
 \r
-                       e.eventType === START && hammer.trigger( this.name, e );\r
-               }\r
+(function( i, g ){\r
+       for( ; i; ){\r
+               g = XUI_Gesture_LIST[ --i ];\r
+               X_Object_override( XUI_Gesture_DEFAULTS, g.defaults );\r
+               delete g.defaults;\r
        };\r
+})( XUI_Gesture_LIST.length );\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