OSDN Git Service

Version 0.6.145, fix X.Audio, fix VIEW_ACTIVATE Event @X.ViewPort.
[pettanr/clientJs.git] / 0.6.x / js / 20_ui / 05_XUI_Gesture.js
1 /* original:\r
2  *  Hammer.JS - v1.0.5 - 2013-04-07\r
3  *  http://eightmedia.github.com/hammer.js\r
4  *  Jorik Tangelder <j.tangelder@gmail.com>, MIT license\r
5  **/\r
6 \r
7         \r
8         var ELEENT_LIST = [],\r
9                 HAMMER_LIST = [],\r
10                 POINTERS    = [],\r
11                 ABS = new Function( 'v', 'return v<0?-v:v' );\r
12         \r
13         function Hammer( uinodeRoot, uinode, type ){\r
14                 this.uinode  = uinode;\r
15                 this.enabled = true;\r
16                 \r
17                 Hammer.startup && Hammer.startup( uinodeRoot );\r
18 \r
19                 this.options = Hammer.defaults;\r
20 \r
21                 // start detection on touchstart\r
22                 Utils.addEvents( uinode, Hammer.EVENT_TYPES_START, this );\r
23                 \r
24                 this[ 'listen' ]( type );\r
25         };\r
26         \r
27         Hammer.defaults = {};\r
28         \r
29         Hammer.prototype.handleEvent = function( e ){\r
30                 //var sourceEventType = e.type.toLowerCase();\r
31 \r
32                 var type       = IdToGestureID[ e.type ],\r
33                         gestures   = Detection.gestures,\r
34                         numTouches = 0,// count the total touches on the screen\r
35                         pointerType, i, l, touches, ret, active, gesture, startEv,\r
36                         hammer, deltaTime, deltaX, deltaY, velocity, center;\r
37                 \r
38                 //console.log( 'Hammer@handleEvent ' + XUI_Event.IdToName[ e.type ] + ' ' + e.pointerType + ' ' + type );\r
39                 if( !type ) return;\r
40                 \r
41                 //console.log( e.type + ' dw:' + XUI_Event._POINTER_DOWN + ' up:' + XUI_Event._POINTER_UP + ' mv:' + XUI_Event._POINTER_MOVE );\r
42                 \r
43                 if( e.pointerType ){\r
44                         type |= POINTER;\r
45                         switch( e.pointerType ){\r
46                                 case 'touch' :\r
47                                         type |= TOUCH; break;\r
48                                 case 'pen' :\r
49                                         type |= PEN; break;\r
50                                 case 'mouse' :\r
51                                         type |= MOUSE; break;\r
52                                 default :\r
53                                         return;\r
54                         };\r
55                 };\r
56                 \r
57                 // onmouseup, but when touchend has been fired we do nothing.\r
58                 // this is for touchdevices which also fire a mouseup on touchend\r
59                 if( type & MOUSE && touch_triggered ){\r
60                         return X_Callback_STOP_NOW | X_Callback_PREVENT_DEFAULT;\r
61                 } else\r
62                 // mousebutton must be down or a touch event\r
63                 if( type & TOUCH || //sourceEventType.match(/touch/) || // touch events are always on screen\r
64                         ( type & POINTER && type & START ) || //sourceEventType.match(/pointerdown/) || // pointerevents touch\r
65                         ( type & MOUSE   && e.button === 0 ) //(sourceEventType.match(/mouse/) && e.which === 1) // mouse is pressed\r
66                 ){\r
67                         enable_detect = true;\r
68                 };\r
69 \r
70                 //console.log( 'Hammer@handleEvent ' + IdToGestureID[ e.type ] + ' ' + e.type + ' ' + XUI_Event._POINTER_DOWN + ' ' + enable_detect );\r
71 \r
72                 // we are in a touch event, set the touch triggered bool to true,\r
73                 // this for the conflicts that may occur on ios and android\r
74                 //type & ( TOUCH | POINTER ) && ( touch_triggered = true );\r
75                 type & TOUCH && ( touch_triggered = true );\r
76                 //if (sourceEventType.match(/touch|pointer/)) { touch_triggered = true;}\r
77 \r
78                 // when touch has been triggered in this detection session\r
79                 // and we are now handling a mouse event, we stop that to prevent conflicts\r
80                 if( enable_detect ){\r
81                         // update pointerevent\r
82 \r
83                         POINTERS[ e.pointerId ] = type & END ? null : e;\r
84                         touches    = [];\r
85                         numTouches = -1;\r
86                         // we can use forEach since pointerEvents only is in IE10\r
87                         for( i in POINTERS ){\r
88                                 POINTERS[ i ] && ( touches[ ++numTouches ] = POINTERS[ i ] );\r
89                         };\r
90                         ++numTouches;\r
91                         ///console.log( 'numTouches ' + numTouches );\r
92 \r
93                         // if we are in a end event, but when we remove one touch and\r
94                         // we still have enough, set eventType to move\r
95                         if( 0 < numTouches && type & END ){ // eventType === Hammer.EVENT_END ){\r
96                                 type = type & POINTER_TYPE_MASK | MOVE;\r
97                                 //eventType = Hammer.EVENT_MOVE;\r
98                         } else if( !numTouches ){\r
99                         // no touches, force the end event\r
100                                 type = type & POINTER_TYPE_MASK | END;\r
101                                 //eventType = Hammer.EVENT_END;\r
102                         };\r
103 \r
104                         // because touchend has no touches, and we often want to use these in our gestures,\r
105                         // we send the last move event as our eventData in touchend\r
106                         ( !numTouches && last_move_event !== null ) ?\r
107                                 ( e = last_move_event ):\r
108                                 ( last_move_event = e ); // store the last move event\r
109 \r
110                         e = {\r
111                                 center      : Utils.getCenter( touches ),\r
112                                 timeStamp   : e.timeStamp,\r
113                                 target      : e.target,\r
114                                 touches     : touches,\r
115                                 eventType   : type & EVENT_TYPE_MASK,\r
116                                 pointerType : type & POINTER_TYPE_MASK\r
117                         };\r
118 \r
119                         if( type & START ){\r
120                                 if( !this.enabled ) return;\r
121                                 // already busy with a Hammer.gesture detection on an element\r
122                                 if( Detection.current ) return;\r
123                                 Detection.current = {\r
124                                         hammer     : this, // reference to HammerInstance we're working for\r
125                                         startEvent : Utils.extend( {}, e ), // start eventData for distances, timing etc\r
126                                         lastEvent  : false, // last eventData\r
127                                         name       : '' // current gesture we're in/detected, can be 'tap', 'hold' etc\r
128                                 };\r
129                                 Detection.stopped = false;\r
130                                 hammer = this;\r
131                                 active = hammer.activeGesture;\r
132                         } else\r
133                         if( !Detection.current || Detection.stopped ){\r
134                                 return;\r
135                         } else {\r
136                                 hammer = Detection.current.hammer;\r
137                                 active = hammer.activeGesture;\r
138                         };\r
139                         \r
140                         // ----------------------------------------------------------------------------------------------------------------\r
141                         // ret = Detection.detect( e );\r
142 \r
143                         // ----------------------------------------------------------------------------------------------------------------\r
144                         // extend event data with calculations about scale, distance etc\r
145                         // e = Detection.extendEventData( e );\r
146                         startEv = Detection.current.startEvent;\r
147                         center  = e.center;\r
148 \r
149                         // if the touches change, set the new touches over the startEvent touches\r
150                         // this because touchevents don't have all the touches on touchstart, or the\r
151                         // user must place his fingers at the EXACT same time on the screen, which is not realistic\r
152                         // but, sometimes it happens that both fingers are touching at the EXACT same time\r
153                         if( startEv && ( numTouches !== startEv.touches.length || touches === startEv.touches ) ){\r
154                                 // extend 1 level deep to get the touchlist with the touch objects\r
155                                 startEv.touches.length = i = 0;\r
156                                 for( ; i < numTouches; ++i ){\r
157                                         startEv.touches[ startEv.touches.length ] = Utils.extend( {}, touches[ i ] );\r
158                                 };\r
159                         };\r
160 \r
161                         deltaTime = e.timeStamp  - startEv.timeStamp;\r
162                         deltaX    = center.pageX - startEv.center.pageX;\r
163                         deltaY    = center.pageY - startEv.center.pageY;\r
164                         velocity  = Utils.getVelocity( deltaTime, deltaX, deltaY );\r
165 \r
166                         Utils.extend( e, {\r
167                                 deltaTime  : deltaTime,\r
168 \r
169                                 deltaX     : deltaX,\r
170                                 deltaY     : deltaY,\r
171 \r
172                                 velocityX  : velocity.x,\r
173                                 velocityY  : velocity.y,\r
174 \r
175                                 distance   : Utils.getDistance( startEv.center, center ),\r
176                                 angle      : Utils.getAngle( startEv.center, center ),\r
177                                 direction  : Utils.getDirection( startEv.center, center ),\r
178 \r
179                                 scale      : Utils.getScale( startEv.touches, touches ),\r
180                                 rotation   : Utils.getRotation( startEv.touches, touches ),\r
181 \r
182                                 startEvent : startEv\r
183                         });\r
184 \r
185                         // store as previous event event\r
186                         Detection.current.lastEvent = e;\r
187                         \r
188                         // call Hammer.gesture handlers\r
189                         for( i = 0, l = gestures.length; i < l; ++i ){\r
190                                 gesture = gestures[ i ];\r
191                                 if( Detection.stopped ) break;\r
192                                 //if( active[ gesture.name ] ) console.log( gesture.name );\r
193                                 // only when the instance options have enabled this gesture\r
194                                 active[ gesture.name ] &&\r
195                                         // if a handler returns false, we stop with the detection\r
196                                         ( ret |= ( gesture.handler( e, hammer ) || X_Callback_NONE ) );\r
197                         };\r
198 \r
199                         // endevent, but not the last touch, so dont stop\r
200                         type & END && numTouches === 0 && Detection.stopDetect();\r
201                         \r
202                         // ----------------------------------------------------------------------------------------------------------------\r
203                         // trigger the handler\r
204                         //handler.call( context, HamEvent.collectEventData( element, eventType, e ) );\r
205 \r
206                         // remove pointerevent from list\r
207                         if( Hammer.HAS_POINTEREVENTS && type & END ){ // eventType === Hammer.EVENT_END ){\r
208                                 numTouches = 0;\r
209                         };\r
210                 };\r
211 \r
212                 //debug(sourceEventType +" "+ eventType);\r
213 \r
214                 // on the end we reset everything\r
215                 if( numTouches === 0 ){\r
216                         last_move_event = null;\r
217                         enable_detect   = false;\r
218                         touch_triggered = false;\r
219                         POINTERS.length = 0;\r
220                 };\r
221                 \r
222                 return ret;\r
223         };\r
224         \r
225         Hammer.startup = function( uinodeRoot ){\r
226                 // find what eventtypes we add listeners to\r
227                 /**\r
228                  * we have different events for each device/browser\r
229                  * determine what we need and set them in the Hammer.EVENT_TYPES constant\r
230                  */\r
231                 // determine the eventtype we want to set\r
232                 // for non pointer events browsers and mixed browsers,\r
233                 // like chrome on windows8 touch laptop         \r
234                 var types, name;\r
235 \r
236                 // Register all gestures inside Gestures\r
237                 for( name in Gestures ){\r
238                         //Gestures.hasOwnProperty( name ) && \r
239                         Detection.register( Gestures[ name ] );\r
240                 };\r
241 \r
242                 Hammer.EVENT_TYPES_START = [ XUI_Event._POINTER_DOWN ];\r
243                 types = [ XUI_Event._POINTER_MOVE, XUI_Event._POINTER_UP, XUI_Event._POINTER_CANCEL ];\r
244 \r
245                 // Add touch events on the document\r
246                 Utils.addEvents( uinodeRoot, types, Hammer.prototype.handleEvent );\r
247 \r
248                 // Hammer is ready...!\r
249                 delete Hammer.startup;\r
250         };\r
251         \r
252         Hammer.prototype.trigger = function( type, gesture ){\r
253                 if( !this.types[ type ] ) return;\r
254                 var e = Utils.extend( {}, gesture );\r
255                 e.type = type;\r
256                 return this.uinode[ 'dispatch' ]( e );\r
257         };\r
258         \r
259         Hammer.prototype.listen = function( type ){\r
260                 var gestures = Detection.gestures,\r
261                         i = gestures.length, g;\r
262                 for( ; i; ){\r
263                         g = gestures[ --i ];\r
264                         if( g.startID <= type && type <= g.endID ){\r
265                                 if( !this.activeGesture ) this.activeGesture = {};\r
266                                 if( !this.types ) this.types = {};\r
267                                 this.activeGesture[ g.name ] = this.types[ type ] = 1;\r
268                                 return;\r
269                         };\r
270                 };\r
271         };\r
272         \r
273         Hammer.prototype.unlisten = function( type ){\r
274                 var gestures = Detection.gestures,\r
275                         i = gestures.length, g;\r
276                 if( !this.activeGesture ) return;\r
277                 for( ; i; ){\r
278                         g = gestures[ --i ];\r
279                         if( g.startID <= type && type <= g.endID ){\r
280                                 if( this.activeGesture[ g.name ] ){\r
281                                         if( this.types[ type ] ) delete this.types[ type ];\r
282                                         for( i = g.startID; i <= g.endID; ++i ){\r
283                                                 if( this.types[ i ] ) return;\r
284                                         };\r
285                                         delete this.activeGesture[ g.name ];\r
286                                 };\r
287                                 return;\r
288                         };\r
289                 };\r
290         };\r
291         \r
292         /*\r
293          *  "Android version < 2.2" return ev.touches.length === 1 when touchend, others return ev.touches.length === 0\r
294          */\r
295         Hammer.DO_TOUCHES_FIX = Hammer.HAS_TOUCHEVENTS && ( X_UA[ 'Android' ] < 2.2 || X_UA[ 'Blink' ] || X_UA[ 'Opera' ] );\r
296         \r
297         // detect touchevents\r
298         Hammer.HAS_POINTEREVENTS = true; // navigator.pointerEnabled || navigator.msPointerEnabled;\r
299         Hammer.HAS_POINTEREVENTS && console.log( 'Hammer.HAS_POINTEREVENTS : true' );\r
300 \r
301 \r
302         // eventtypes per touchevent (start, move, end)\r
303         // are filled by HamEvent.determineEventTypes on setup\r
304         Hammer.EVENT_TYPES_START = null;\r
305 \r
306         // direction defines\r
307         Hammer.DIRECTION_DOWN  = 'down';\r
308         Hammer.DIRECTION_LEFT  = 'left';\r
309         Hammer.DIRECTION_UP    = 'up';\r
310         Hammer.DIRECTION_RIGHT = 'right';\r
311 \r
312         // plugins namespace\r
313         Hammer.plugins = {};\r
314 \r
315         var POINTER     = 1,\r
316                 TOUCH       = 2,\r
317                 PEN         = 8, //4,\r
318                 MOUSE       = 8,\r
319                 START       = 16,\r
320                 MOVE        = 32,\r
321                 END         = 64,\r
322                 CANCEL      = 128,\r
323                 EVENT_TYPE_MASK   = START | MOVE | END,\r
324                 POINTER_TYPE_MASK = POINTER | TOUCH | MOUSE | PEN,\r
325                 IdToGestureID = {};\r
326         IdToGestureID[ XUI_Event._POINTER_DOWN   ] = START;\r
327         IdToGestureID[ XUI_Event._POINTER_MOVE   ] = MOVE;\r
328         IdToGestureID[ XUI_Event._POINTER_UP     ] = END;\r
329         IdToGestureID[ XUI_Event._POINTER_CANCEL ] = END;\r
330         \r
331         var Utils = {\r
332                 \r
333                 /**\r
334                  * touch events with mouse fallback\r
335                  * @param   {HTMLElement}   element\r
336                  * @param   {String}        eventType        like Hammer.EVENT_MOVE\r
337                  * @param   {Function}      handler\r
338                  */\r
339                 addEvents : function( uinode, types, context ){\r
340                         for( var i = 0; i < types.length; ++i ){\r
341                                 uinode[ 'listen' ]( types[ i ], context );\r
342                         };\r
343                 },\r
344                 \r
345                 /**\r
346                  * extend method,\r
347                  * also used for cloning when dest is an empty object\r
348                  * @param   {Object}    dest\r
349                  * @param   {Object}    src\r
350                  * @parm        {Boolean}       merge           do a merge\r
351                  * @returns {Object}    dest\r
352                  */\r
353                 extend : function extend( dest, src, merge ){\r
354                         for( var key in src ){\r
355                                 if( dest[ key ] !== undefined && merge ) continue;\r
356                                 dest[ key ] = src[ key ];\r
357                         };\r
358                         return dest;\r
359                 },\r
360 \r
361                 /**\r
362                  * find if a node is in the given parent\r
363                  * used for event delegation tricks\r
364                  * @param   {HTMLElement}   node\r
365                  * @param   {HTMLElement}   parent\r
366                  * @returns {boolean}       has_parent\r
367                  */\r
368                 hasParent : function( node, parent ){\r
369                         while( node && node.tagName ){ /* tagName for ie */\r
370                                 if( node === parent ) return true;\r
371                                 node = node.parentNode;\r
372                         };\r
373                         return false;\r
374                 },\r
375 \r
376                 /**\r
377                  * get the center of all the touches\r
378                  * @param   {Array}     touches\r
379                  * @returns {Object}    center\r
380                  */\r
381                 getCenter : function getCenter(touches) {\r
382                         var i = 0,\r
383                                 l = touches.length,\r
384                                 valuesX, valuesY;\r
385                         switch( l ){\r
386                                 case 0 :\r
387                                         return {};\r
388                                 case 1 :\r
389                                         return {\r
390                                                 pageX : touches[ 0 ].pageX,\r
391                                                 pageY : touches[ 0 ].pageY\r
392                                         };\r
393                                 case 2 :\r
394                                         return {\r
395                                                 pageX : ( touches[ 0 ].pageX + touches[ 1 ].pageX ) / 2,\r
396                                                 pageY : ( touches[ 0 ].pageY + touches[ 1 ].pageY ) / 2\r
397                                         };\r
398                         };\r
399                         valuesX = [];\r
400                         valuesY = [];\r
401                         for( ; i < l; ++i ){\r
402                                 valuesX[ valuesX.length ] = touches[ i ].pageX;\r
403                                 valuesY[ valuesY.length ] = touches[ i ].pageY;\r
404                         };\r
405                         return {\r
406                                 pageX : ( ( Math.min.apply( null, valuesX ) + Math.max.apply( null, valuesX ) ) / 2 ),\r
407                                 pageY : ( ( Math.min.apply( null, valuesY ) + Math.max.apply( null, valuesY ) ) / 2 )\r
408                         };\r
409                 },\r
410 \r
411                 /**\r
412                  * calculate the velocity between two points\r
413                  * @param   {Number}    deltaTime\r
414                  * @param   {Number}    deltaX\r
415                  * @param   {Number}    deltaY\r
416                  * @returns {Object}    velocity\r
417                  */\r
418                 getVelocity : function getVelocity( deltaTime, deltaX, deltaY ) {\r
419                         return {\r
420                                 x : ABS( deltaX / deltaTime ) || 0,\r
421                                 y : ABS( deltaY / deltaTime ) || 0\r
422                         };\r
423                 },\r
424 \r
425                 /**\r
426                  * calculate the angle between two coordinates\r
427                  * @param   {Touch}     touch1\r
428                  * @param   {Touch}     touch2\r
429                  * @returns {Number}    angle\r
430                  */\r
431                 getAngle : function getAngle(touch1, touch2) {\r
432                         var y = touch2.pageY - touch1.pageY,\r
433                                 x = touch2.pageX - touch1.pageX;\r
434                         return Math.atan2( y, x ) * 180 / Math.PI;\r
435                 },\r
436 \r
437                 /**\r
438                  * angle to direction define\r
439                  * @param   {Touch}     touch1\r
440                  * @param   {Touch}     touch2\r
441                  * @returns {String}    direction constant, like Hammer.DIRECTION_LEFT\r
442                  */\r
443                 getDirection : function getDirection( touch1, touch2 ){\r
444                         var x = touch1.pageX - touch2.pageX,\r
445                                 y = touch1.pageY - touch2.pageY;\r
446                         return ABS( y ) <= ABS( x ) ?\r
447                                 ( x > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT ) :\r
448                                 ( y > 0 ? Hammer.DIRECTION_UP   : Hammer.DIRECTION_DOWN );\r
449                 },\r
450 \r
451                 /**\r
452                  * calculate the distance between two touches\r
453                  * @param   {Touch}     touch1\r
454                  * @param   {Touch}     touch2\r
455                  * @returns {Number}    distance\r
456                  */\r
457                 getDistance : function getDistance( touch1, touch2 ){\r
458                         var x = touch2.pageX - touch1.pageX,\r
459                                 y = touch2.pageY - touch1.pageY;\r
460                         return Math.sqrt( ( x * x ) + ( y * y ) );\r
461                 },\r
462 \r
463                 /**\r
464                  * calculate the scale factor between two touchLists (fingers)\r
465                  * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out\r
466                  * @param   {Array}     start\r
467                  * @param   {Array}     end\r
468                  * @returns {Number}    scale\r
469                  */\r
470                 getScale : function getScale( start, end ){\r
471                         // need two fingers...\r
472                         return ( 2 <= start.length && 2 <= end.length ) ?\r
473                                 Utils.getDistance( end[ 0 ], end[ 1 ] ) / Utils.getDistance( start[ 0 ], start[ 1 ] ) :\r
474                                 1;\r
475                 },\r
476 \r
477                 /**\r
478                  * calculate the rotation degrees between two touchLists (fingers)\r
479                  * @param   {Array}     start\r
480                  * @param   {Array}     end\r
481                  * @returns {Number}    rotation\r
482                  */\r
483                 getRotation : function getRotation( start, end ){\r
484                         // need two fingers\r
485                         return ( 2 <= start.length && 2 <= end.length ) ?\r
486                                 Utils.getAngle( end[ 1 ], end[ 0 ] ) - Utils.getAngle( start[ 1 ], start[ 0 ] ) :\r
487                                 0;\r
488                 },\r
489 \r
490                 /**\r
491                  * boolean if the direction is vertical\r
492                  * @param    {String}    direction\r
493                  * @returns  {Boolean}   is_vertical\r
494                  */\r
495                 isVertical : function isVertical( direction ){\r
496                         return direction === Hammer.DIRECTION_UP || direction === Hammer.DIRECTION_DOWN;\r
497                 }\r
498         };\r
499         \r
500         /*\r
501          * this holds the last move event,\r
502          * used to fix empty touchend issue\r
503          * see the onTouch event for an explanation\r
504          * @type {Object}\r
505          */\r
506         var last_move_event = null;\r
507 \r
508         /*\r
509          * when the mouse is hold down, this is true\r
510          * @type {Boolean}\r
511          */\r
512         var enable_detect = false;\r
513 \r
514         /*\r
515          * when touch events have been fired, this is true\r
516          * @type {Boolean}\r
517          */\r
518         var touch_triggered = false;\r
519         \r
520         var Detection = {\r
521                 // contains all registred Gestures in the correct order\r
522                 gestures : [],\r
523 \r
524                 // data of the current Hammer.gesture detection session\r
525                 current : null,\r
526 \r
527                 // the previous Hammer.gesture session data\r
528                 // is a full clone of the previous gesture.current object\r
529                 previous : null,\r
530 \r
531                 // when this becomes true, no gestures are fired\r
532                 stopped : false,\r
533 \r
534                 /**\r
535                  * clear the Hammer.gesture vars\r
536                  * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected\r
537                  * to stop other Gestures from being fired\r
538                  */\r
539                 stopDetect : function stopDetect() {\r
540                         // clone current data to the store as the previous gesture\r
541                         // used for the double tap gesture, since this is an other gesture detect session\r
542                         Detection.previous = Utils.extend( {}, Detection.current );\r
543 \r
544                         // reset the current\r
545                         Detection.current = null;\r
546 \r
547                         // stopped!\r
548                         Detection.stopped = true;\r
549                 },\r
550 \r
551                 /**\r
552                  * register new gesture\r
553                  * @param   {Object}    gesture object, see gestures.js for documentation\r
554                  * @returns {Array}     gestures\r
555                  */\r
556                 register : function( gesture ){\r
557                         // add an enable gesture options if there is no given\r
558                         var options = gesture.defaults || {},\r
559                                 list    = Detection.gestures,\r
560                                 _index, i = 0, l = list.length, index;\r
561                         if( options[ gesture.name ] === undefined ) options[ gesture.name ] = true;\r
562 \r
563                         // extend Hammer default options with the Hammer.gesture options\r
564                         Utils.extend( Hammer.defaults, options, true );\r
565 \r
566                         // set its index\r
567                         gesture.index = gesture.index || 1000;\r
568 \r
569                         // add Hammer.gesture to the list\r
570                         //Detection.gestures.push( gesture );\r
571 \r
572                         // sort the list by index\r
573                         //Detection.gestures.sort( function( a, b ){\r
574                         //      return\r
575                         //              a.index < b.index ? -1 :\r
576                         //              a.index > b.index ? 1 : 0;\r
577                         //});\r
578                         if( l === 0 ){\r
579                                 list[ 0 ] = gesture;\r
580                                 return;\r
581                         };\r
582                         _index = gesture.index;\r
583                         for( i = 0; i < l; ++i ){\r
584                                 index = list[ i ].index;\r
585                                 if( i === 0 && _index < index ){\r
586                                         list.unshift( gesture );\r
587                                         return;\r
588                                 } else\r
589                                 if( i === l - 1 ){\r
590                                         list[ l ] = gesture;\r
591                                         return;\r
592                                 } else\r
593                                 if( index <= _index && _index < list[ i + 1 ].index ){\r
594                                         list.splice( i, 0, gesture );\r
595                                         return;\r
596                                 };\r
597                         };\r
598                 }\r
599         };\r
600 \r
601         var Gestures = Gestures || {};\r
602 \r
603         /**\r
604          * Custom gestures\r
605          * ==============================\r
606          *\r
607          * Gesture object\r
608          * --------------------\r
609          * The object structure of a gesture:\r
610          *\r
611          * { name: 'mygesture',\r
612          *   index: 1337,\r
613          *   defaults: {\r
614          *     mygesture_option: true\r
615          *   }\r
616          *   handler: function(type, e, inst) {\r
617          *     // trigger gesture event\r
618          *     inst.trigger(this.name, e );\r
619          *   }\r
620          * }\r
621 \r
622          * @param   {String}    name\r
623          * this should be the name of the gesture, lowercase\r
624          * it is also being used to disable/enable the gesture per instance config.\r
625          *\r
626          * @param   {Number}    [index=1000]\r
627          * the index of the gesture, where it is going to be in the stack of gestures detection\r
628          * like when you build an gesture that depends on the drag gesture, it is a good\r
629          * idea to place it after the index of the drag gesture.\r
630          *\r
631          * @param   {Object}    [defaults={}]\r
632          * the default settings of the gesture. these are added to the instance settings,\r
633          * and can be overruled per instance. you can also add the name of the gesture,\r
634          * but this is also added by default (and set to true).\r
635          *\r
636          * @param   {Function}  handler\r
637          * this handles the gesture detection of your custom gesture and receives the\r
638          * following arguments:\r
639          *\r
640          *      @param  {Object}    eventData\r
641          *      event data containing the following properties:\r
642          *          timeStamp   {Number}        time the event occurred\r
643          *          target      {HTMLElement}   target element\r
644          *          touches     {Array}         touches (fingers, pointers, mouse) on the screen\r
645          *          pointerType {String}        kind of pointer that was used. matches Hammer.POINTER_MOUSE|TOUCH\r
646          *          center      {Object}        center position of the touches. contains pageX and pageY\r
647          *          deltaTime   {Number}        the total time of the touches in the screen\r
648          *          deltaX      {Number}        the delta on x axis we haved moved\r
649          *          deltaY      {Number}        the delta on y axis we haved moved\r
650          *          velocityX   {Number}        the velocity on the x\r
651          *          velocityY   {Number}        the velocity on y\r
652          *          angle       {Number}        the angle we are moving\r
653          *          direction   {String}        the direction we are moving. matches Hammer.DIRECTION_UP|DOWN|LEFT|RIGHT\r
654          *          distance    {Number}        the distance we haved moved\r
655          *          scale       {Number}        scaling of the touches, needs 2 touches\r
656          *          rotation    {Number}        rotation of the touches, needs 2 touches *\r
657          *          eventType   {String}        matches Hammer.EVENT_START|MOVE|END\r
658          *          srcEvent    {Object}        the source event, like TouchStart or MouseDown *\r
659          *          startEvent  {Object}        contains the same properties as above,\r
660          *                                      but from the first touch. this is used to calculate\r
661          *                                      distances, deltaTime, scaling etc\r
662          *\r
663          *      @param  {Hammer.Instance}    inst\r
664          *      the instance we are doing the detection for. you can get the options from\r
665          *      the inst.options object and trigger the gesture event by calling inst.trigger\r
666          *\r
667          *\r
668          * Handle gestures\r
669          * --------------------\r
670          * inside the handler you can get/set Detection.current. This is the current\r
671          * detection session. It has the following properties\r
672          *      @param  {String}    name\r
673          *      contains the name of the gesture we have detected. it has not a real function,\r
674          *      only to check in other gestures if something is detected.\r
675          *      like in the drag gesture we set it to 'drag' and in the swipe gesture we can\r
676          *      check if the current gesture is 'drag' by accessing Detection.current.name\r
677          *\r
678          *      @readonly\r
679          *      @param  {Hammer.Instance}    inst\r
680          *      the instance we do the detection for\r
681          *\r
682          *      @readonly\r
683          *      @param  {Object}    startEvent\r
684          *      contains the properties of the first gesture detection in this session.\r
685          *      Used for calculations about timing, distance, etc.\r
686          *\r
687          *      @readonly\r
688          *      @param  {Object}    lastEvent\r
689          *      contains all the properties of the last gesture detect in this session.\r
690          *\r
691          * after the gesture detection session has been completed (user has released the screen)\r
692          * the Detection.current object is copied into Detection.previous,\r
693          * this is usefull for gestures like doubletap, where you need to know if the\r
694          * previous gesture was a tap\r
695          *\r
696          * options that have been set by the instance can be received by calling inst.options\r
697          *\r
698          * You can trigger a gesture event by calling inst.trigger("mygesture", event).\r
699          * The first param is the name of your gesture, the second the event argument\r
700          *\r
701          *\r
702          * Register gestures\r
703          * --------------------\r
704          * When an gesture is added to the Gestures object, it is auto registered\r
705          * at the setup of the first Hammer instance. You can also call Detection.register\r
706          * manually and pass your gesture object as a param\r
707          *\r
708          */\r
709 \r
710         /**\r
711          * Hold\r
712          * Touch stays at the same place for x time\r
713          * @events  hold holdend\r
714          */\r
715         Gestures.Hold = {\r
716                 name    : 'hold',\r
717                 index   : 10,\r
718                 startID : XUI_Event.HOLD,\r
719                 endID   : XUI_Event.HOLD_END,\r
720                 defaults : {\r
721                         hold_timeout   : 500,\r
722                         hold_threshold : 1\r
723                 },\r
724                 timerID : null,\r
725                 holding : false,\r
726                 handler : function holdGesture( e, hammer ){\r
727                         switch( e.eventType ){\r
728                                 case START :\r
729                                         // clear any running timers\r
730                                         this.timerID && X_Timer_remove( this.timerID );\r
731 \r
732                                         // set the gesture so we can check in the timeout if it still is\r
733                                         Detection.current.name = this.name;\r
734                                         Gestures.Hold.holding = false;\r
735                                         \r
736                                         // set timer and if after the timeout it still is hold,\r
737                                         // we trigger the hold event\r
738                                         this.timerID = X_Timer_add( hammer.options.hold_timeout, 1, Gestures.Hold._onTimer, [ e, hammer ] );\r
739                                         return;\r
740 \r
741                                 // when you move or end we clear the timer\r
742                                 case MOVE :\r
743                                         if( e.distance <= hammer.options.hold_threshold ) return;\r
744                                 case END :\r
745                                         this.timerID && X_Timer_remove( this.timerID );\r
746                                         if( Gestures.Hold.holding === true ){\r
747                                                 Gestures.Hold.holding = false;\r
748                                                 return hammer.trigger( XUI_Event.HOLD_END, e );\r
749                                         };\r
750                                         break;\r
751                         };\r
752                 },\r
753                 _onTimer : function( e, hammer ){\r
754                         if( Detection.current.name === 'hold' ){\r
755                                 hammer.trigger( XUI_Event.HOLD, e );\r
756                                 Gestures.Hold.holding = true;\r
757                         };\r
758                 }\r
759         };\r
760 \r
761         /**\r
762          * Tap/DoubleTap\r
763          * Quick touch at a place or double at the same place\r
764          * @events  tap, doubletap\r
765          */\r
766         Gestures.Tap = {\r
767                 name     : 'tap',\r
768                 index    : 100,\r
769                 startID  : XUI_Event.TAP,\r
770                 endID    : XUI_Event.DOUBLE_TAP,\r
771                 defaults : {\r
772                         tap_max_touchtime  : 250,\r
773                         tap_max_distance   : 10,\r
774                         tap_always         : true,\r
775                         doubletap_distance : 20,\r
776                         doubletap_interval : 300\r
777                 },\r
778                 handler : function tapGesture( e, hammer ){\r
779                         // previous gesture, for the double tap since these are two different gesture detections\r
780                         var prev = Detection.previous;\r
781                         if( e.eventType === END ){\r
782                                 // when the touchtime is higher then the max touch time\r
783                                 // or when the moving distance is too much\r
784                                 if( hammer.options.tap_max_touchtime < e.deltaTime || hammer.options.tap_max_distance < e.distance ) return;\r
785 \r
786                                 // check if double tap\r
787                                 if( prev && prev.name === 'tap' && ( e.timeStamp - prev.lastEvent.timeStamp ) < hammer.options.doubletap_interval && e.distance < hammer.options.doubletap_distance ){\r
788                                         return hammer.trigger( XUI_Event.DOUBLE_TAP, e );\r
789                                 } else\r
790                                 // do a single tap\r
791                                 if( hammer.options.tap_always && Detection.current.name !== 'tap' ){ // EventFire中にalert すると mouseleave で再び呼ばれるのを防ぐ\r
792                                         Detection.current.name = 'tap';\r
793                                         return hammer.trigger( XUI_Event.TAP, e );\r
794                                 };\r
795                         };\r
796                 }\r
797         };\r
798 \r
799         /**\r
800          * Swipe\r
801          * triggers swipe events when the end velocity is above the threshold\r
802          * @events  swipe, swipeleft, swiperight, swipeup, swipedown\r
803          */\r
804         Gestures.Swipe = {\r
805                 name     : 'swipe',\r
806                 index    : 40,\r
807                 startID  : XUI_Event.SWIP,\r
808                 endID    : XUI_Event.SWIP_DOWN,\r
809                 defaults : {\r
810                         // set 0 for unlimited, but this can conflict with transform\r
811                         swipe_max_touches : 1,\r
812                         swipe_velocity    : 0.7\r
813                 },\r
814                 handler : function swipeGesture(e, hammer) {\r
815                         if( e.eventType === END ){\r
816                                 // max touches\r
817                                 if( 0 < hammer.options.swipe_max_touches && hammer.options.swipe_max_touches < e.touches.length ) return;\r
818 \r
819                                 // when the distance we moved is too small we skip this gesture\r
820                                 // or we can be already in dragging\r
821                                 if( hammer.options.swipe_velocity < e.velocityX || hammer.options.swipe_velocity < e.velocityY ){\r
822                                         // trigger swipe events\r
823                                         hammer.trigger( XUI_Event.SWIP, e );\r
824                                         hammer.trigger(\r
825                                                 e.direction === Hammer.DIRECTION_UP ?\r
826                                                         XUI_Event.SWIP_UP :\r
827                                                 e.direction === Hammer.DIRECTION_DOWN ?\r
828                                                         XUI_Event.SWIP_DOWN :\r
829                                                 e.direction === Hammer.DIRECTION_LEFT ?\r
830                                                         XUI_Event.SWIP_LEFT :\r
831                                                         XUI_Event.SWIP_RIGHT,\r
832                                                 e\r
833                                         );\r
834                                 };\r
835                         };\r
836                 }\r
837         };\r
838 \r
839         /**\r
840          * Drag\r
841          * Move with x fingers (default 1) around on the page. Blocking the scrolling when\r
842          * moving left and right is a good practice. When all the drag events are blocking\r
843          * you disable scrolling on that area.\r
844          * @events  drag, dragstart, dragend, drapleft, dragright, dragup, dragdown\r
845          */\r
846         Gestures.Drag = {\r
847                 name     : 'drag',\r
848                 index    : 50,\r
849                 startID  : XUI_Event.DRAG,\r
850                 endID    : XUI_Event.DRAG_DOWN,\r
851                 defaults : {\r
852                         drag_min_distance : 10,\r
853                         // set 0 for unlimited, but this can conflict with transform\r
854                         drag_max_touches : 1,\r
855                         // prevent default browser behavior when dragging occurs\r
856                         // be careful with it, it makes the element a blocking element\r
857                         // when you are using the drag gesture, it is a good practice to set this true\r
858                         drag_block_horizontal : false,\r
859                         drag_block_vertical : false,\r
860                         // drag_lock_to_axis keeps the drag gesture on the axis that it started on,\r
861                         // It disallows vertical directions if the initial direction was horizontal, and vice versa.\r
862                         drag_lock_to_axis : false,\r
863                         // drag lock only kicks in when distance > drag_lock_min_distance\r
864                         // This way, locking occurs only when the distance has become large enough to reliably determine the direction\r
865                         drag_lock_min_distance : 25\r
866                 },\r
867                 triggered : false,\r
868                 handler : function dragGesture( e, hammer ){\r
869                         var last_direction;\r
870                         // current gesture isnt drag, but dragged is true\r
871                         // this means an other gesture is busy. now call dragend\r
872                         if( Detection.current.name !== this.name && this.triggered ){\r
873                                 hammer.trigger( XUI_Event.DRAG_END, e );\r
874                                 this.triggered = false;\r
875                                 return;\r
876                         };\r
877 \r
878                         // max touches\r
879                         if( 0 < hammer.options.drag_max_touches && hammer.options.drag_max_touches < e.touches.length ) return;\r
880 \r
881                         switch( e.eventType ){\r
882                                 case START:\r
883                                         this.triggered = false;\r
884                                         break;\r
885 \r
886                                 case MOVE :\r
887                                         // when the distance we moved is too small we skip this gesture\r
888                                         // or we can be already in dragging\r
889                                         if( e.distance < hammer.options.drag_min_distance && Detection.current.name !== this.name ) return;\r
890 \r
891                                         // we are dragging!\r
892                                         Detection.current.name = this.name;\r
893 \r
894                                         // lock drag to axis?\r
895                                         if( Detection.current.lastEvent.drag_locked_to_axis || ( hammer.options.drag_lock_to_axis && hammer.options.drag_lock_min_distance <= e.distance ) ){\r
896                                                 e.drag_locked_to_axis = true;\r
897                                         };\r
898                                         last_direction = Detection.current.lastEvent.direction;\r
899                                         if( e.drag_locked_to_axis && last_direction !== e.direction ){\r
900                                                 // keep direction on the axis that the drag gesture started on\r
901                                                 e.direction = Utils.isVertical( last_direction ) ?\r
902                                                         ( e.deltaY < 0 ? Hammer.DIRECTION_UP   : Hammer.DIRECTION_DOWN ) :\r
903                                                         ( e.deltaX < 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT );\r
904                                         };\r
905 \r
906                                         // first time, trigger dragstart event\r
907                                         if( !this.triggered ){\r
908                                                 hammer.trigger( XUI_Event.DRAG_START, e );\r
909                                                 this.triggered = true;\r
910                                         };\r
911 \r
912                                         // trigger normal event\r
913                                         hammer.trigger( XUI_Event.DRAG, e );\r
914 \r
915                                         // direction event, like dragdown\r
916                                         hammer.trigger(\r
917                                                 e.direction === Hammer.DIRECTION_UP ?\r
918                                                         XUI_Event.DRAG_UP :\r
919                                                 e.direction === Hammer.DIRECTION_DOWN ?\r
920                                                         XUI_Event.DRAG_DOWN :\r
921                                                 e.direction === Hammer.DIRECTION_LEFT ?\r
922                                                         XUI_Event.DRAG_LEFT :\r
923                                                         XUI_Event.DRAG_RIGHT,\r
924                                                 e\r
925                                         );\r
926 \r
927                                         // block the browser events\r
928                                         (\r
929                                                 ( hammer.options.drag_block_vertical   &&  Utils.isVertical( e.direction ) ) ||\r
930                                                 ( hammer.options.drag_block_horizontal && !Utils.isVertical( e.direction ) )\r
931                                         ) && e.preventDefault();\r
932                                         break;\r
933 \r
934                                 case END:\r
935                                         // trigger dragend\r
936                                         this.triggered && hammer.trigger( XUI_Event.DRAG_END, e );\r
937                                         this.triggered = false;\r
938                                         break;\r
939                         }\r
940                 }\r
941         };\r
942 \r
943         /**\r
944          * Transform\r
945          * User want to scale or rotate with 2 fingers\r
946          * @events  transform, transformstart, transformend, pinch, pinchin, pinchout, rotate\r
947          */\r
948         Gestures.Transform = {\r
949                 name     : 'transform',\r
950                 index    : 45,\r
951                 startID  : XUI_Event.TRANSFORM,\r
952                 endID    : XUI_Event.ROTATE,\r
953                 defaults : {\r
954                         // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1\r
955                         transform_min_scale : 0.01,\r
956                         // rotation in degrees\r
957                         transform_min_rotation : 1,\r
958                         // prevent default browser behavior when two touches are on the screen\r
959                         // but it makes the element a blocking element\r
960                         // when you are using the transform gesture, it is a good practice to set this true\r
961                         transform_always_block : false\r
962                 },\r
963                 triggered : false,\r
964                 handler : function transformGesture( e, hammer ){\r
965                         // current gesture isnt drag, but dragged is true\r
966                         // this means an other gesture is busy. now call dragend\r
967                         if( Detection.current.name !== this.name && this.triggered ){\r
968                                 hammer.trigger( XUI_Event.TRANSFORM_END, e );\r
969                                 this.triggered = false;\r
970                                 return;\r
971                         };\r
972 \r
973                         // atleast multitouch\r
974                         if( e.touches.length < 2 ) return;\r
975 \r
976                         // prevent default when two fingers are on the screen\r
977                         hammer.options.transform_always_block && e.preventDefault();\r
978 \r
979                         switch(e.eventType) {\r
980                                 case START:\r
981                                         this.triggered = false;\r
982                                         break;\r
983 \r
984                                 case MOVE:\r
985                                         var scale_threshold    = ABS( 1 - e.scale ),\r
986                                                 rotation_threshold = ABS( e.rotation );\r
987 \r
988                                         // when the distance we moved is too small we skip this gesture\r
989                                         // or we can be already in dragging\r
990                                         if( scale_threshold < hammer.options.transform_min_scale && rotation_threshold < hammer.options.transform_min_rotation ) return;\r
991 \r
992                                         // we are transforming!\r
993                                         Detection.current.name = this.name;\r
994 \r
995                                         // first time, trigger dragstart event\r
996                                         if( !this.triggered ){\r
997                                                 hammer.trigger( XUI_Event.TRANSFORM_START, e );\r
998                                                 this.triggered = true;\r
999                                         };\r
1000 \r
1001                                         hammer.trigger( XUI_Event.TRANSFORM, e );\r
1002                                         // basic transform event\r
1003 \r
1004                                         // trigger rotate event\r
1005                                         hammer.options.transform_min_rotation < rotation_threshold && hammer.trigger( XUI_Event.ROTATE, e );\r
1006 \r
1007                                         // trigger pinch event\r
1008                                         if( scale_threshold > hammer.options.transform_min_scale ){\r
1009                                                 hammer.trigger( XUI_Event.PINCH, e );\r
1010                                                 hammer.trigger( e.scale < 1 ? XUI_Event.PINCH_IN : XUI_Event.PINCH_OUT, e );\r
1011                                         };\r
1012                                         break;\r
1013 \r
1014                                 case END:\r
1015                                         // trigger dragend\r
1016                                         this.triggered && hammer.trigger( XUI_Event.TRANSFORM_END, e );\r
1017                                         this.triggered = false;\r
1018                                         break;\r
1019                         };\r
1020                 }\r
1021         };\r
1022 \r
1023         /**\r
1024          * Touch\r
1025          * Called as first, tells the user has touched the screen\r
1026          * @events  touch\r
1027          */\r
1028         Gestures.Touch = {\r
1029                 name     : 'touch',\r
1030                 index    : -Infinity,\r
1031                 defaults : {\r
1032                         // call preventDefault at touchstart, and makes the element blocking by\r
1033                         // disabling the scrolling of the page, but it improves gestures like\r
1034                         // transforming and dragging.\r
1035                         // be careful with using this, it can be very annoying for users to be stuck\r
1036                         // on the page\r
1037                         prevent_default : false,\r
1038 \r
1039                         // disable mouse events, so only touch (or pen!) input triggers events\r
1040                         prevent_mouseevents : false\r
1041                 },\r
1042                 handler : function touchGesture( e, hammer ){\r
1043                         if( hammer.options.prevent_mouseevents && e.pointerType === MOUSE ){\r
1044                                 Detection.stopDetect();\r
1045                                 return;\r
1046                         };\r
1047 \r
1048                         hammer.options.prevent_default && e.preventDefault();\r
1049 \r
1050                         e.eventType === START && hammer.trigger( this.name, e );\r
1051                 }\r
1052         };\r
1053 \r
1054         /**\r
1055          * Release\r
1056          * Called as last, tells the user has released the screen\r
1057          * @events  release\r
1058          */\r
1059         Gestures.Release = {\r
1060                 name    : 'release',\r
1061                 index   : Infinity,\r
1062                 handler : function releaseGesture( e, hammer ){\r
1063                         e.eventType === END && hammer.trigger( this.name, e );\r
1064                 }\r
1065         };\r