/js/jquery.mobile.event.js

https://github.com/dannyc/jquery-mobile · JavaScript · 318 lines · 211 code · 68 blank · 39 comment · 23 complexity · 0e590728bf6f11c49d726aec9801a8d4 MD5 · raw file

  1. /*
  2. * "events" plugin - Handles events
  3. */
  4. (function( $, window, undefined ) {
  5. // add new event shortcuts
  6. $.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
  7. "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {
  8. $.fn[ name ] = function( fn ) {
  9. return fn ? this.bind( name, fn ) : this.trigger( name );
  10. };
  11. $.attrFn[ name ] = true;
  12. });
  13. var supportTouch = $.support.touch,
  14. scrollEvent = "touchmove scroll",
  15. touchStartEvent = supportTouch ? "touchstart" : "mousedown",
  16. touchStopEvent = supportTouch ? "touchend" : "mouseup",
  17. touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
  18. function triggerCustomEvent( obj, eventType, event ) {
  19. var originalType = event.type;
  20. event.type = eventType;
  21. $.event.handle.call( obj, event );
  22. event.type = originalType;
  23. }
  24. // also handles scrollstop
  25. $.event.special.scrollstart = {
  26. enabled: true,
  27. setup: function() {
  28. var thisObject = this,
  29. $this = $( thisObject ),
  30. scrolling,
  31. timer;
  32. function trigger( event, state ) {
  33. scrolling = state;
  34. triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
  35. }
  36. // iPhone triggers scroll after a small delay; use touchmove instead
  37. $this.bind( scrollEvent, function( event ) {
  38. if ( !$.event.special.scrollstart.enabled ) {
  39. return;
  40. }
  41. if ( !scrolling ) {
  42. trigger( event, true );
  43. }
  44. clearTimeout( timer );
  45. timer = setTimeout(function() {
  46. trigger( event, false );
  47. }, 50 );
  48. });
  49. }
  50. };
  51. // also handles taphold
  52. $.event.special.tap = {
  53. setup: function() {
  54. var thisObject = this,
  55. $this = $( thisObject );
  56. $this.bind( "vmousedown", function( event ) {
  57. if ( event.which && event.which !== 1 ) {
  58. return false;
  59. }
  60. var origTarget = event.target,
  61. origEvent = event.originalEvent,
  62. timer;
  63. function clearTapTimer() {
  64. clearTimeout( timer );
  65. }
  66. function clearTapHandlers() {
  67. clearTapTimer();
  68. $this.unbind( "vclick", clickHandler )
  69. .unbind( "vmouseup", clearTapTimer )
  70. .unbind( "vmousecancel", clearTapHandlers );
  71. }
  72. function clickHandler(event) {
  73. clearTapHandlers();
  74. // ONLY trigger a 'tap' event if the start target is
  75. // the same as the stop target.
  76. if ( origTarget == event.target ) {
  77. triggerCustomEvent( thisObject, "tap", event );
  78. }
  79. }
  80. $this.bind( "vmousecancel", clearTapHandlers )
  81. .bind( "vmouseup", clearTapTimer )
  82. .bind( "vclick", clickHandler );
  83. timer = setTimeout(function() {
  84. triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
  85. }, 750 );
  86. });
  87. }
  88. };
  89. // also handles swipeleft, swiperight
  90. $.event.special.swipe = {
  91. scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.
  92. durationThreshold: 1000, // More time than this, and it isn't a swipe.
  93. horizontalDistanceThreshold: 30, // Swipe horizontal displacement must be more than this.
  94. verticalDistanceThreshold: 75, // Swipe vertical displacement must be less than this.
  95. setup: function() {
  96. var thisObject = this,
  97. $this = $( thisObject );
  98. $this.bind( touchStartEvent, function( event ) {
  99. var data = event.originalEvent.touches ?
  100. event.originalEvent.touches[ 0 ] : event,
  101. start = {
  102. time: ( new Date() ).getTime(),
  103. coords: [ data.pageX, data.pageY ],
  104. origin: $( event.target )
  105. },
  106. stop;
  107. function moveHandler( event ) {
  108. if ( !start ) {
  109. return;
  110. }
  111. var data = event.originalEvent.touches ?
  112. event.originalEvent.touches[ 0 ] : event;
  113. stop = {
  114. time: ( new Date() ).getTime(),
  115. coords: [ data.pageX, data.pageY ]
  116. };
  117. // prevent scrolling
  118. if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
  119. event.preventDefault();
  120. }
  121. }
  122. $this.bind( touchMoveEvent, moveHandler )
  123. .one( touchStopEvent, function( event ) {
  124. $this.unbind( touchMoveEvent, moveHandler );
  125. if ( start && stop ) {
  126. if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
  127. Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
  128. Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
  129. start.origin.trigger( "swipe" )
  130. .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
  131. }
  132. }
  133. start = stop = undefined;
  134. });
  135. });
  136. }
  137. };
  138. (function( $, window ) {
  139. // "Cowboy" Ben Alman
  140. var win = $( window ),
  141. special_event,
  142. get_orientation,
  143. last_orientation;
  144. $.event.special.orientationchange = special_event = {
  145. setup: function() {
  146. // If the event is supported natively, return false so that jQuery
  147. // will bind to the event using DOM methods.
  148. if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
  149. return false;
  150. }
  151. // Get the current orientation to avoid initial double-triggering.
  152. last_orientation = get_orientation();
  153. // Because the orientationchange event doesn't exist, simulate the
  154. // event by testing window dimensions on resize.
  155. win.bind( "throttledresize", handler );
  156. },
  157. teardown: function(){
  158. // If the event is not supported natively, return false so that
  159. // jQuery will unbind the event using DOM methods.
  160. if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
  161. return false;
  162. }
  163. // Because the orientationchange event doesn't exist, unbind the
  164. // resize event handler.
  165. win.unbind( "throttledresize", handler );
  166. },
  167. add: function( handleObj ) {
  168. // Save a reference to the bound event handler.
  169. var old_handler = handleObj.handler;
  170. handleObj.handler = function( event ) {
  171. // Modify event object, adding the .orientation property.
  172. event.orientation = get_orientation();
  173. // Call the originally-bound event handler and return its result.
  174. return old_handler.apply( this, arguments );
  175. };
  176. }
  177. };
  178. // If the event is not supported natively, this handler will be bound to
  179. // the window resize event to simulate the orientationchange event.
  180. function handler() {
  181. // Get the current orientation.
  182. var orientation = get_orientation();
  183. if ( orientation !== last_orientation ) {
  184. // The orientation has changed, so trigger the orientationchange event.
  185. last_orientation = orientation;
  186. win.trigger( "orientationchange" );
  187. }
  188. }
  189. // Get the current page orientation. This method is exposed publicly, should it
  190. // be needed, as jQuery.event.special.orientationchange.orientation()
  191. $.event.special.orientationchange.orientation = get_orientation = function() {
  192. var isPortrait = true, elem = document.documentElement;
  193. // prefer window orientation to the calculation based on screensize as
  194. // the actual screen resize takes place before or after the orientation change event
  195. // has been fired depending on implementation (eg android 2.3 is before, iphone after).
  196. // More testing is required to determine if a more reliable method of determining the new screensize
  197. // is possible when orientationchange is fired. (eg, use media queries + element + opacity)
  198. if ( $.support.orientation ) {
  199. // if the window orientation registers as 0 or 180 degrees report
  200. // portrait, otherwise landscape
  201. isPortrait = window.orientation % 180 == 0;
  202. } else {
  203. isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
  204. }
  205. return isPortrait ? "portrait" : "landscape";
  206. };
  207. })( jQuery, window );
  208. // throttled resize event
  209. (function() {
  210. $.event.special.throttledresize = {
  211. setup: function() {
  212. $( this ).bind( "resize", handler );
  213. },
  214. teardown: function(){
  215. $( this ).unbind( "resize", handler );
  216. }
  217. };
  218. var throttle = 250,
  219. handler = function() {
  220. curr = ( new Date() ).getTime();
  221. diff = curr - lastCall;
  222. if ( diff >= throttle ) {
  223. lastCall = curr;
  224. $( this ).trigger( "throttledresize" );
  225. } else {
  226. if ( heldCall ) {
  227. clearTimeout( heldCall );
  228. }
  229. // Promise a held call will still execute
  230. heldCall = setTimeout( handler, throttle - diff );
  231. }
  232. },
  233. lastCall = 0,
  234. heldCall,
  235. curr,
  236. diff;
  237. })();
  238. $.each({
  239. scrollstop: "scrollstart",
  240. taphold: "tap",
  241. swipeleft: "swipe",
  242. swiperight: "swipe"
  243. }, function( event, sourceEvent ) {
  244. $.event.special[ event ] = {
  245. setup: function() {
  246. $( this ).bind( sourceEvent, $.noop );
  247. }
  248. };
  249. });
  250. })( jQuery, this );