jquery.mobile.custom.js 24 KB

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