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