Administrator
2023-04-21 195945efc5db921a4c9eb8cf9421c172273293f5
提交 | 用户 | 时间
58d006 1 /*
A 2 * jQuery Mobile v1.4.5
3 * http://jquerymobile.com
4 *
5 * Copyright 2010, 2014 jQuery Foundation, Inc. and other contributors
6 * Released under the MIT license.
7 * http://jquery.org/license
8 *
9 */
10
11 (function ( root, doc, factory ) {
12     if ( typeof define === "function" && define.amd ) {
13         // AMD. Register as an anonymous module.
14         define( [ "jquery" ], function ( $ ) {
15             factory( $, root, doc );
16             return $.mobile;
17         });
18     } else {
19         // Browser globals
20         factory( root.jQuery, root, doc );
21     }
22 }( this, document, function ( jQuery, window, document, undefined ) {// This plugin is an experiment for abstracting away the touch and mouse
23 // events so that developers don't have to worry about which method of input
24 // the device their document is loaded on supports.
25 //
26 // The idea here is to allow the developer to register listeners for the
27 // basic mouse events, such as mousedown, mousemove, mouseup, and click,
28 // and the plugin will take care of registering the correct listeners
29 // behind the scenes to invoke the listener at the fastest possible time
30 // for that device, while still retaining the order of event firing in
31 // the traditional mouse environment, should multiple handlers be registered
32 // on the same element for different events.
33 //
34 // The current version exposes the following virtual events to jQuery bind methods:
35 // "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
36
37 (function( $, window, document, undefined ) {
38
39 var dataPropertyName = "virtualMouseBindings",
40     touchTargetPropertyName = "virtualTouchID",
41     virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
42     touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
43     mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
44     mouseEventProps = $.event.props.concat( mouseHookProps ),
45     activeDocHandlers = {},
46     resetTimerID = 0,
47     startX = 0,
48     startY = 0,
49     didScroll = false,
50     clickBlockList = [],
51     blockMouseTriggers = false,
52     blockTouchTriggers = false,
53     eventCaptureSupported = "addEventListener" in document,
54     $document = $( document ),
55     nextTouchID = 1,
56     lastTouchID = 0, threshold,
57     i;
58
59 $.vmouse = {
60     moveDistanceThreshold: 10,
61     clickDistanceThreshold: 10,
62     resetTimerDuration: 1500
63 };
64
65 function getNativeEvent( event ) {
66
67     while ( event && typeof event.originalEvent !== "undefined" ) {
68         event = event.originalEvent;
69     }
70     return event;
71 }
72
73 function createVirtualEvent( event, eventType ) {
74
75     var t = event.type,
76         oe, props, ne, prop, ct, touch, i, j, len;
77
78     event = $.Event( event );
79     event.type = eventType;
80
81     oe = event.originalEvent;
82     props = $.event.props;
83
84     // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
85     // https://github.com/jquery/jquery-mobile/issues/3280
86     if ( t.search( /^(mouse|click)/ ) > -1 ) {
87         props = mouseEventProps;
88     }
89
90     // copy original event properties over to the new event
91     // this would happen if we could call $.event.fix instead of $.Event
92     // but we don't have a way to force an event to be fixed multiple times
93     if ( oe ) {
94         for ( i = props.length, prop; i; ) {
95             prop = props[ --i ];
96             event[ prop ] = oe[ prop ];
97         }
98     }
99
100     // make sure that if the mouse and click virtual events are generated
101     // without a .which one is defined
102     if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ) {
103         event.which = 1;
104     }
105
106     if ( t.search(/^touch/) !== -1 ) {
107         ne = getNativeEvent( oe );
108         t = ne.touches;
109         ct = ne.changedTouches;
110         touch = ( t && t.length ) ? t[0] : ( ( ct && ct.length ) ? ct[ 0 ] : undefined );
111
112         if ( touch ) {
113             for ( j = 0, len = touchEventProps.length; j < len; j++) {
114                 prop = touchEventProps[ j ];
115                 event[ prop ] = touch[ prop ];
116             }
117         }
118     }
119
120     return event;
121 }
122
123 function getVirtualBindingFlags( element ) {
124
125     var flags = {},
126         b, k;
127
128     while ( element ) {
129
130         b = $.data( element, dataPropertyName );
131
132         for (  k in b ) {
133             if ( b[ k ] ) {
134                 flags[ k ] = flags.hasVirtualBinding = true;
135             }
136         }
137         element = element.parentNode;
138     }
139     return flags;
140 }
141
142 function getClosestElementWithVirtualBinding( element, eventType ) {
143     var b;
144     while ( element ) {
145
146         b = $.data( element, dataPropertyName );
147
148         if ( b && ( !eventType || b[ eventType ] ) ) {
149             return element;
150         }
151         element = element.parentNode;
152     }
153     return null;
154 }
155
156 function enableTouchBindings() {
157     blockTouchTriggers = false;
158 }
159
160 function disableTouchBindings() {
161     blockTouchTriggers = true;
162 }
163
164 function enableMouseBindings() {
165     lastTouchID = 0;
166     clickBlockList.length = 0;
167     blockMouseTriggers = false;
168
169     // When mouse bindings are enabled, our
170     // touch bindings are disabled.
171     disableTouchBindings();
172 }
173
174 function disableMouseBindings() {
175     // When mouse bindings are disabled, our
176     // touch bindings are enabled.
177     enableTouchBindings();
178 }
179
180 function startResetTimer() {
181     clearResetTimer();
182     resetTimerID = setTimeout( function() {
183         resetTimerID = 0;
184         enableMouseBindings();
185     }, $.vmouse.resetTimerDuration );
186 }
187
188 function clearResetTimer() {
189     if ( resetTimerID ) {
190         clearTimeout( resetTimerID );
191         resetTimerID = 0;
192     }
193 }
194
195 function triggerVirtualEvent( eventType, event, flags ) {
196     var ve;
197
198     if ( ( flags && flags[ eventType ] ) ||
199                 ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
200
201         ve = createVirtualEvent( event, eventType );
202
203         $( event.target).trigger( ve );
204     }
205
206     return ve;
207 }
208
209 function mouseEventCallback( event ) {
210     var touchID = $.data( event.target, touchTargetPropertyName ),
211         ve;
212
213     if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ) {
214         ve = triggerVirtualEvent( "v" + event.type, event );
215         if ( ve ) {
216             if ( ve.isDefaultPrevented() ) {
217                 event.preventDefault();
218             }
219             if ( ve.isPropagationStopped() ) {
220                 event.stopPropagation();
221             }
222             if ( ve.isImmediatePropagationStopped() ) {
223                 event.stopImmediatePropagation();
224             }
225         }
226     }
227 }
228
229 function handleTouchStart( event ) {
230
231     var touches = getNativeEvent( event ).touches,
232         target, flags, t;
233
234     if ( touches && touches.length === 1 ) {
235
236         target = event.target;
237         flags = getVirtualBindingFlags( target );
238
239         if ( flags.hasVirtualBinding ) {
240
241             lastTouchID = nextTouchID++;
242             $.data( target, touchTargetPropertyName, lastTouchID );
243
244             clearResetTimer();
245
246             disableMouseBindings();
247             didScroll = false;
248
249             t = getNativeEvent( event ).touches[ 0 ];
250             startX = t.pageX;
251             startY = t.pageY;
252
253             triggerVirtualEvent( "vmouseover", event, flags );
254             triggerVirtualEvent( "vmousedown", event, flags );
255         }
256     }
257 }
258
259 function handleScroll( event ) {
260     if ( blockTouchTriggers ) {
261         return;
262     }
263
264     if ( !didScroll ) {
265         triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
266     }
267
268     didScroll = true;
269     startResetTimer();
270 }
271
272 function handleTouchMove( event ) {
273     if ( blockTouchTriggers ) {
274         return;
275     }
276
277     var t = getNativeEvent( event ).touches[ 0 ],
278         didCancel = didScroll,
279         moveThreshold = $.vmouse.moveDistanceThreshold,
280         flags = getVirtualBindingFlags( event.target );
281
282         didScroll = didScroll ||
283             ( Math.abs( t.pageX - startX ) > moveThreshold ||
284                 Math.abs( t.pageY - startY ) > moveThreshold );
285
286     if ( didScroll && !didCancel ) {
287         triggerVirtualEvent( "vmousecancel", event, flags );
288     }
289
290     triggerVirtualEvent( "vmousemove", event, flags );
291     startResetTimer();
292 }
293
294 function handleTouchEnd( event ) {
295     if ( blockTouchTriggers ) {
296         return;
297     }
298
299     disableTouchBindings();
300
301     var flags = getVirtualBindingFlags( event.target ),
302         ve, t;
303     triggerVirtualEvent( "vmouseup", event, flags );
304
305     if ( !didScroll ) {
306         ve = triggerVirtualEvent( "vclick", event, flags );
307         if ( ve && ve.isDefaultPrevented() ) {
308             // The target of the mouse events that follow the touchend
309             // event don't necessarily match the target used during the
310             // touch. This means we need to rely on coordinates for blocking
311             // any click that is generated.
312             t = getNativeEvent( event ).changedTouches[ 0 ];
313             clickBlockList.push({
314                 touchID: lastTouchID,
315                 x: t.clientX,
316                 y: t.clientY
317             });
318
319             // Prevent any mouse events that follow from triggering
320             // virtual event notifications.
321             blockMouseTriggers = true;
322         }
323     }
324     triggerVirtualEvent( "vmouseout", event, flags);
325     didScroll = false;
326
327     startResetTimer();
328 }
329
330 function hasVirtualBindings( ele ) {
331     var bindings = $.data( ele, dataPropertyName ),
332         k;
333
334     if ( bindings ) {
335         for ( k in bindings ) {
336             if ( bindings[ k ] ) {
337                 return true;
338             }
339         }
340     }
341     return false;
342 }
343
344 function dummyMouseHandler() {}
345
346 function getSpecialEventObject( eventType ) {
347     var realType = eventType.substr( 1 );
348
349     return {
350         setup: function(/* data, namespace */) {
351             // If this is the first virtual mouse binding for this element,
352             // add a bindings object to its data.
353
354             if ( !hasVirtualBindings( this ) ) {
355                 $.data( this, dataPropertyName, {} );
356             }
357
358             // If setup is called, we know it is the first binding for this
359             // eventType, so initialize the count for the eventType to zero.
360             var bindings = $.data( this, dataPropertyName );
361             bindings[ eventType ] = true;
362
363             // If this is the first virtual mouse event for this type,
364             // register a global handler on the document.
365
366             activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
367
368             if ( activeDocHandlers[ eventType ] === 1 ) {
369                 $document.bind( realType, mouseEventCallback );
370             }
371
372             // Some browsers, like Opera Mini, won't dispatch mouse/click events
373             // for elements unless they actually have handlers registered on them.
374             // To get around this, we register dummy handlers on the elements.
375
376             $( this ).bind( realType, dummyMouseHandler );
377
378             // For now, if event capture is not supported, we rely on mouse handlers.
379             if ( eventCaptureSupported ) {
380                 // If this is the first virtual mouse binding for the document,
381                 // register our touchstart handler on the document.
382
383                 activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1;
384
385                 if ( activeDocHandlers[ "touchstart" ] === 1 ) {
386                     $document.bind( "touchstart", handleTouchStart )
387                         .bind( "touchend", handleTouchEnd )
388
389                         // On touch platforms, touching the screen and then dragging your finger
390                         // causes the window content to scroll after some distance threshold is
391                         // exceeded. On these platforms, a scroll prevents a click event from being
392                         // dispatched, and on some platforms, even the touchend is suppressed. To
393                         // mimic the suppression of the click event, we need to watch for a scroll
394                         // event. Unfortunately, some platforms like iOS don't dispatch scroll
395                         // events until *AFTER* the user lifts their finger (touchend). This means
396                         // we need to watch both scroll and touchmove events to figure out whether
397                         // or not a scroll happenens before the touchend event is fired.
398
399                         .bind( "touchmove", handleTouchMove )
400                         .bind( "scroll", handleScroll );
401                 }
402             }
403         },
404
405         teardown: function(/* data, namespace */) {
406             // If this is the last virtual binding for this eventType,
407             // remove its global handler from the document.
408
409             --activeDocHandlers[ eventType ];
410
411             if ( !activeDocHandlers[ eventType ] ) {
412                 $document.unbind( realType, mouseEventCallback );
413             }
414
415             if ( eventCaptureSupported ) {
416                 // If this is the last virtual mouse binding in existence,
417                 // remove our document touchstart listener.
418
419                 --activeDocHandlers[ "touchstart" ];
420
421                 if ( !activeDocHandlers[ "touchstart" ] ) {
422                     $document.unbind( "touchstart", handleTouchStart )
423                         .unbind( "touchmove", handleTouchMove )
424                         .unbind( "touchend", handleTouchEnd )
425                         .unbind( "scroll", handleScroll );
426                 }
427             }
428
429             var $this = $( this ),
430                 bindings = $.data( this, dataPropertyName );
431
432             // teardown may be called when an element was
433             // removed from the DOM. If this is the case,
434             // jQuery core may have already stripped the element
435             // of any data bindings so we need to check it before
436             // using it.
437             if ( bindings ) {
438                 bindings[ eventType ] = false;
439             }
440
441             // Unregister the dummy event handler.
442
443             $this.unbind( realType, dummyMouseHandler );
444
445             // If this is the last virtual mouse binding on the
446             // element, remove the binding data from the element.
447
448             if ( !hasVirtualBindings( this ) ) {
449                 $this.removeData( dataPropertyName );
450             }
451         }
452     };
453 }
454
455 // Expose our custom events to the jQuery bind/unbind mechanism.
456
457 for ( i = 0; i < virtualEventNames.length; i++ ) {
458     $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
459 }
460
461 // Add a capture click handler to block clicks.
462 // Note that we require event capture support for this so if the device
463 // doesn't support it, we punt for now and rely solely on mouse events.
464 if ( eventCaptureSupported ) {
465     document.addEventListener( "click", function( e ) {
466         var cnt = clickBlockList.length,
467             target = e.target,
468             x, y, ele, i, o, touchID;
469
470         if ( cnt ) {
471             x = e.clientX;
472             y = e.clientY;
473             threshold = $.vmouse.clickDistanceThreshold;
474
475             // The idea here is to run through the clickBlockList to see if
476             // the current click event is in the proximity of one of our
477             // vclick events that had preventDefault() called on it. If we find
478             // one, then we block the click.
479             //
480             // Why do we have to rely on proximity?
481             //
482             // Because the target of the touch event that triggered the vclick
483             // can be different from the target of the click event synthesized
484             // by the browser. The target of a mouse/click event that is synthesized
485             // from a touch event seems to be implementation specific. For example,
486             // some browsers will fire mouse/click events for a link that is near
487             // a touch event, even though the target of the touchstart/touchend event
488             // says the user touched outside the link. Also, it seems that with most
489             // browsers, the target of the mouse/click event is not calculated until the
490             // time it is dispatched, so if you replace an element that you touched
491             // with another element, the target of the mouse/click will be the new
492             // element underneath that point.
493             //
494             // Aside from proximity, we also check to see if the target and any
495             // of its ancestors were the ones that blocked a click. This is necessary
496             // because of the strange mouse/click target calculation done in the
497             // Android 2.1 browser, where if you click on an element, and there is a
498             // mouse/click handler on one of its ancestors, the target will be the
499             // innermost child of the touched element, even if that child is no where
500             // near the point of touch.
501
502             ele = target;
503
504             while ( ele ) {
505                 for ( i = 0; i < cnt; i++ ) {
506                     o = clickBlockList[ i ];
507                     touchID = 0;
508
509                     if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
510                                 $.data( ele, touchTargetPropertyName ) === o.touchID ) {
511                         // XXX: We may want to consider removing matches from the block list
512                         //      instead of waiting for the reset timer to fire.
513                         e.preventDefault();
514                         e.stopPropagation();
515                         return;
516                     }
517                 }
518                 ele = ele.parentNode;
519             }
520         }
521     }, true);
522 }
523 })( jQuery, window, document );
524
525 (function( $ ) {
526     $.mobile = {};
527 }( jQuery ));
528
529     (function( $, undefined ) {
530         var support = {
531             touch: "ontouchend" in document
532         };
533
534         $.mobile.support = $.mobile.support || {};
535         $.extend( $.support, support );
536         $.extend( $.mobile.support, support );
537     }( jQuery ));
538
539
540 (function( $, window, undefined ) {
541     var $document = $( document ),
542         supportTouch = $.mobile.support.touch,
543         scrollEvent = "touchmove scroll",
544         touchStartEvent = supportTouch ? "touchstart" : "mousedown",
545         touchStopEvent = supportTouch ? "touchend" : "mouseup",
546         touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
547
548     // setup new event shortcuts
549     $.each( ( "touchstart touchmove touchend " +
550         "tap taphold " +
551         "swipe swipeleft swiperight " +
552         "scrollstart scrollstop" ).split( " " ), function( i, name ) {
553
554         $.fn[ name ] = function( fn ) {
555             return fn ? this.bind( name, fn ) : this.trigger( name );
556         };
557
558         // jQuery < 1.8
559         if ( $.attrFn ) {
560             $.attrFn[ name ] = true;
561         }
562     });
563
564     function triggerCustomEvent( obj, eventType, event, bubble ) {
565         var originalType = event.type;
566         event.type = eventType;
567         if ( bubble ) {
568             $.event.trigger( event, undefined, obj );
569         } else {
570             $.event.dispatch.call( obj, event );
571         }
572         event.type = originalType;
573     }
574
575     // also handles scrollstop
576     $.event.special.scrollstart = {
577
578         enabled: true,
579         setup: function() {
580
581             var thisObject = this,
582                 $this = $( thisObject ),
583                 scrolling,
584                 timer;
585
586             function trigger( event, state ) {
587                 scrolling = state;
588                 triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
589             }
590
591             // iPhone triggers scroll after a small delay; use touchmove instead
592             $this.bind( scrollEvent, function( event ) {
593
594                 if ( !$.event.special.scrollstart.enabled ) {
595                     return;
596                 }
597
598                 if ( !scrolling ) {
599                     trigger( event, true );
600                 }
601
602                 clearTimeout( timer );
603                 timer = setTimeout( function() {
604                     trigger( event, false );
605                 }, 50 );
606             });
607         },
608         teardown: function() {
609             $( this ).unbind( scrollEvent );
610         }
611     };
612
613     // also handles taphold
614     $.event.special.tap = {
615         tapholdThreshold: 750,
616         emitTapOnTaphold: true,
617         setup: function() {
618             var thisObject = this,
619                 $this = $( thisObject ),
620                 isTaphold = false;
621
622             $this.bind( "vmousedown", function( event ) {
623                 isTaphold = false;
624                 if ( event.which && event.which !== 1 ) {
625                     return false;
626                 }
627
628                 var origTarget = event.target,
629                     timer;
630
631                 function clearTapTimer() {
632                     clearTimeout( timer );
633                 }
634
635                 function clearTapHandlers() {
636                     clearTapTimer();
637
638                     $this.unbind( "vclick", clickHandler )
639                         .unbind( "vmouseup", clearTapTimer );
640                     $document.unbind( "vmousecancel", clearTapHandlers );
641                 }
642
643                 function clickHandler( event ) {
644                     clearTapHandlers();
645
646                     // ONLY trigger a 'tap' event if the start target is
647                     // the same as the stop target.
648                     if ( !isTaphold && origTarget === event.target ) {
649                         triggerCustomEvent( thisObject, "tap", event );
650                     } else if ( isTaphold ) {
651                         event.preventDefault();
652                     }
653                 }
654
655                 $this.bind( "vmouseup", clearTapTimer )
656                     .bind( "vclick", clickHandler );
657                 $document.bind( "vmousecancel", clearTapHandlers );
658
659                 timer = setTimeout( function() {
660                     if ( !$.event.special.tap.emitTapOnTaphold ) {
661                         isTaphold = true;
662                     }
663                     triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
664                 }, $.event.special.tap.tapholdThreshold );
665             });
666         },
667         teardown: function() {
668             $( this ).unbind( "vmousedown" ).unbind( "vclick" ).unbind( "vmouseup" );
669             $document.unbind( "vmousecancel" );
670         }
671     };
672
673     // Also handles swipeleft, swiperight
674     $.event.special.swipe = {
675
676         // More than this horizontal displacement, and we will suppress scrolling.
677         scrollSupressionThreshold: 30,
678
679         // More time than this, and it isn't a swipe.
680         durationThreshold: 1000,
681
682         // Swipe horizontal displacement must be more than this.
683         horizontalDistanceThreshold: 30,
684
685         // Swipe vertical displacement must be less than this.
686         verticalDistanceThreshold: 30,
687
688         getLocation: function ( event ) {
689             var winPageX = window.pageXOffset,
690                 winPageY = window.pageYOffset,
691                 x = event.clientX,
692                 y = event.clientY;
693
694             if ( event.pageY === 0 && Math.floor( y ) > Math.floor( event.pageY ) ||
695                 event.pageX === 0 && Math.floor( x ) > Math.floor( event.pageX ) ) {
696
697                 // iOS4 clientX/clientY have the value that should have been
698                 // in pageX/pageY. While pageX/page/ have the value 0
699                 x = x - winPageX;
700                 y = y - winPageY;
701             } else if ( y < ( event.pageY - winPageY) || x < ( event.pageX - winPageX ) ) {
702
703                 // Some Android browsers have totally bogus values for clientX/Y
704                 // when scrolling/zooming a page. Detectable since clientX/clientY
705                 // should never be smaller than pageX/pageY minus page scroll
706                 x = event.pageX - winPageX;
707                 y = event.pageY - winPageY;
708             }
709
710             return {
711                 x: x,
712                 y: y
713             };
714         },
715
716         start: function( event ) {
717             var data = event.originalEvent.touches ?
718                     event.originalEvent.touches[ 0 ] : event,
719                 location = $.event.special.swipe.getLocation( data );
720             return {
721                         time: ( new Date() ).getTime(),
722                         coords: [ location.x, location.y ],
723                         origin: $( event.target )
724                     };
725         },
726
727         stop: function( event ) {
728             var data = event.originalEvent.touches ?
729                     event.originalEvent.touches[ 0 ] : event,
730                 location = $.event.special.swipe.getLocation( data );
731             return {
732                         time: ( new Date() ).getTime(),
733                         coords: [ location.x, location.y ]
734                     };
735         },
736
737         handleSwipe: function( start, stop, thisObject, origTarget ) {
738             if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
739                 Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
740                 Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
741                 var direction = start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight";
742
743                 triggerCustomEvent( thisObject, "swipe", $.Event( "swipe", { target: origTarget, swipestart: start, swipestop: stop }), true );
744                 triggerCustomEvent( thisObject, direction,$.Event( direction, { target: origTarget, swipestart: start, swipestop: stop } ), true );
745                 return true;
746             }
747             return false;
748
749         },
750
751         // This serves as a flag to ensure that at most one swipe event event is
752         // in work at any given time
753         eventInProgress: false,
754
755         setup: function() {
756             var events,
757                 thisObject = this,
758                 $this = $( thisObject ),
759                 context = {};
760
761             // Retrieve the events data for this element and add the swipe context
762             events = $.data( this, "mobile-events" );
763             if ( !events ) {
764                 events = { length: 0 };
765                 $.data( this, "mobile-events", events );
766             }
767             events.length++;
768             events.swipe = context;
769
770             context.start = function( event ) {
771
772                 // Bail if we're already working on a swipe event
773                 if ( $.event.special.swipe.eventInProgress ) {
774                     return;
775                 }
776                 $.event.special.swipe.eventInProgress = true;
777
778                 var stop,
779                     start = $.event.special.swipe.start( event ),
780                     origTarget = event.target,
781                     emitted = false;
782
783                 context.move = function( event ) {
784                     if ( !start || event.isDefaultPrevented() ) {
785                         return;
786                     }
787
788                     stop = $.event.special.swipe.stop( event );
789                     if ( !emitted ) {
790                         emitted = $.event.special.swipe.handleSwipe( start, stop, thisObject, origTarget );
791                         if ( emitted ) {
792
793                             // Reset the context to make way for the next swipe event
794                             $.event.special.swipe.eventInProgress = false;
795                         }
796                     }
797                     // prevent scrolling
798                     if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
799                         event.preventDefault();
800                     }
801                 };
802
803                 context.stop = function() {
804                         emitted = true;
805
806                         // Reset the context to make way for the next swipe event
807                         $.event.special.swipe.eventInProgress = false;
808                         $document.off( touchMoveEvent, context.move );
809                         context.move = null;
810                 };
811
812                 $document.on( touchMoveEvent, context.move )
813                     .one( touchStopEvent, context.stop );
814             };
815             $this.on( touchStartEvent, context.start );
816         },
817
818         teardown: function() {
819             var events, context;
820
821             events = $.data( this, "mobile-events" );
822             if ( events ) {
823                 context = events.swipe;
824                 delete events.swipe;
825                 events.length--;
826                 if ( events.length === 0 ) {
827                     $.removeData( this, "mobile-events" );
828                 }
829             }
830
831             if ( context ) {
832                 if ( context.start ) {
833                     $( this ).off( touchStartEvent, context.start );
834                 }
835                 if ( context.move ) {
836                     $document.off( touchMoveEvent, context.move );
837                 }
838                 if ( context.stop ) {
839                     $document.off( touchStopEvent, context.stop );
840                 }
841             }
842         }
843     };
844     $.each({
845         scrollstop: "scrollstart",
846         taphold: "tap",
847         swipeleft: "swipe.left",
848         swiperight: "swipe.right"
849     }, function( event, sourceEvent ) {
850
851         $.event.special[ event ] = {
852             setup: function() {
853                 $( this ).bind( sourceEvent, $.noop );
854             },
855             teardown: function() {
856                 $( this ).unbind( sourceEvent );
857             }
858         };
859     });
860
861 })( jQuery, this );
862
863
864 }));