1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Custom events and shortcuts.
7 define( [ "jquery", "./jquery.mobile.core", "./jquery.mobile.support", "./jquery.mobile.vmouse" ], function( $ ) {
8 //>>excludeEnd("jqmBuildExclude");
9 (function( $, window, undefined ) {
11 // add new event shortcuts
12 $.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
13 "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {
15 $.fn[ name ] = function( fn ) {
16 return fn ? this.bind( name, fn ) : this.trigger( name );
19 $.attrFn[ name ] = true;
22 var supportTouch = $.support.touch,
23 scrollEvent = "touchmove scroll",
24 touchStartEvent = supportTouch ? "touchstart" : "mousedown",
25 touchStopEvent = supportTouch ? "touchend" : "mouseup",
26 touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
28 function triggerCustomEvent( obj, eventType, event ) {
29 var originalType = event.type;
30 event.type = eventType;
31 $.event.handle.call( obj, event );
32 event.type = originalType;
35 // also handles scrollstop
36 $.event.special.scrollstart = {
42 var thisObject = this,
43 $this = $( thisObject ),
47 function trigger( event, state ) {
49 triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
52 // iPhone triggers scroll after a small delay; use touchmove instead
53 $this.bind( scrollEvent, function( event ) {
55 if ( !$.event.special.scrollstart.enabled ) {
60 trigger( event, true );
63 clearTimeout( timer );
64 timer = setTimeout(function() {
65 trigger( event, false );
71 // also handles taphold
72 $.event.special.tap = {
74 var thisObject = this,
75 $this = $( thisObject );
77 $this.bind( "vmousedown", function( event ) {
79 if ( event.which && event.which !== 1 ) {
83 var origTarget = event.target,
84 origEvent = event.originalEvent,
87 function clearTapTimer() {
88 clearTimeout( timer );
91 function clearTapHandlers() {
94 $this.unbind( "vclick", clickHandler )
95 .unbind( "vmouseup", clearTapTimer );
96 $( document ).unbind( "vmousecancel", clearTapHandlers );
99 function clickHandler(event) {
102 // ONLY trigger a 'tap' event if the start target is
103 // the same as the stop target.
104 if ( origTarget == event.target ) {
105 triggerCustomEvent( thisObject, "tap", event );
109 $this.bind( "vmouseup", clearTapTimer )
110 .bind( "vclick", clickHandler );
111 $( document ).bind( "vmousecancel", clearTapHandlers );
113 timer = setTimeout(function() {
114 triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
120 // also handles swipeleft, swiperight
121 $.event.special.swipe = {
122 scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.
124 durationThreshold: 1000, // More time than this, and it isn't a swipe.
126 horizontalDistanceThreshold: 30, // Swipe horizontal displacement must be more than this.
128 verticalDistanceThreshold: 75, // Swipe vertical displacement must be less than this.
131 var thisObject = this,
132 $this = $( thisObject );
134 $this.bind( touchStartEvent, function( event ) {
135 var data = event.originalEvent.touches ?
136 event.originalEvent.touches[ 0 ] : event,
138 time: ( new Date() ).getTime(),
139 coords: [ data.pageX, data.pageY ],
140 origin: $( event.target )
144 function moveHandler( event ) {
150 var data = event.originalEvent.touches ?
151 event.originalEvent.touches[ 0 ] : event;
154 time: ( new Date() ).getTime(),
155 coords: [ data.pageX, data.pageY ]
159 if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
160 event.preventDefault();
164 $this.bind( touchMoveEvent, moveHandler )
165 .one( touchStopEvent, function( event ) {
166 $this.unbind( touchMoveEvent, moveHandler );
168 if ( start && stop ) {
169 if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
170 Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
171 Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
173 start.origin.trigger( "swipe" )
174 .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
177 start = stop = undefined;
183 (function( $, window ) {
184 // "Cowboy" Ben Alman
186 var win = $( window ),
190 initial_orientation_is_landscape,
191 initial_orientation_is_default,
192 portrait_map = { "0": true, "180": true };
194 // It seems that some device/browser vendors use window.orientation values 0 and 180 to
195 // denote the "default" orientation. For iOS devices, and most other smart-phones tested,
196 // the default orientation is always "portrait", but in some Android and RIM based tablets,
197 // the default orientation is "landscape". The following code attempts to use the window
198 // dimensions to figure out what the current orientation is, and then makes adjustments
199 // to the to the portrait_map if necessary, so that we can properly decode the
200 // window.orientation value whenever get_orientation() is called.
202 // Note that we used to use a media query to figure out what the orientation the browser
205 // initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");
207 // but there was an iPhone/iPod Touch bug beginning with iOS 4.2, up through iOS 5.1,
208 // where the browser *ALWAYS* applied the landscape media query. This bug does not
211 if ( $.support.orientation ) {
213 // Check the window width and height to figure out what the current orientation
214 // of the device is at this moment. Note that we've initialized the portrait map
215 // values to 0 and 180, *AND* we purposely check for landscape so that if we guess
216 // wrong, , we default to the assumption that portrait is the default orientation.
217 // We use a threshold check below because on some platforms like iOS, the iPhone
218 // form-factor can report a larger width than height if the user turns on the
219 // developer console. The actual threshold value is somewhat arbitrary, we just
220 // need to make sure it is large enough to exclude the developer console case.
222 var ww = window.innerWidth || $( window ).width(),
223 wh = window.innerHeight || $( window ).height(),
224 landscape_threshold = 50;
226 initial_orientation_is_landscape = ww > wh && ( ww - wh ) > landscape_threshold;
229 // Now check to see if the current window.orientation is 0 or 180.
230 initial_orientation_is_default = portrait_map[ window.orientation ];
232 // If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR*
233 // if the initial orientation is portrait, but window.orientation reports 90 or -90, we
234 // need to flip our portrait_map values because landscape is the default orientation for
235 // this device/browser.
236 if ( ( initial_orientation_is_landscape && initial_orientation_is_default ) || ( !initial_orientation_is_landscape && !initial_orientation_is_default ) ) {
237 portrait_map = { "-90": true, "90": true };
241 $.event.special.orientationchange = special_event = {
243 // If the event is supported natively, return false so that jQuery
244 // will bind to the event using DOM methods.
245 if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
249 // Get the current orientation to avoid initial double-triggering.
250 last_orientation = get_orientation();
252 // Because the orientationchange event doesn't exist, simulate the
253 // event by testing window dimensions on resize.
254 win.bind( "throttledresize", handler );
256 teardown: function(){
257 // If the event is not supported natively, return false so that
258 // jQuery will unbind the event using DOM methods.
259 if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
263 // Because the orientationchange event doesn't exist, unbind the
264 // resize event handler.
265 win.unbind( "throttledresize", handler );
267 add: function( handleObj ) {
268 // Save a reference to the bound event handler.
269 var old_handler = handleObj.handler;
272 handleObj.handler = function( event ) {
273 // Modify event object, adding the .orientation property.
274 event.orientation = get_orientation();
276 // Call the originally-bound event handler and return its result.
277 return old_handler.apply( this, arguments );
282 // If the event is not supported natively, this handler will be bound to
283 // the window resize event to simulate the orientationchange event.
285 // Get the current orientation.
286 var orientation = get_orientation();
288 if ( orientation !== last_orientation ) {
289 // The orientation has changed, so trigger the orientationchange event.
290 last_orientation = orientation;
291 win.trigger( "orientationchange" );
295 // Get the current page orientation. This method is exposed publicly, should it
296 // be needed, as jQuery.event.special.orientationchange.orientation()
297 $.event.special.orientationchange.orientation = get_orientation = function() {
298 var isPortrait = true, elem = document.documentElement;
300 // prefer window orientation to the calculation based on screensize as
301 // the actual screen resize takes place before or after the orientation change event
302 // has been fired depending on implementation (eg android 2.3 is before, iphone after).
303 // More testing is required to determine if a more reliable method of determining the new screensize
304 // is possible when orientationchange is fired. (eg, use media queries + element + opacity)
305 if ( $.support.orientation ) {
306 // if the window orientation registers as 0 or 180 degrees report
307 // portrait, otherwise landscape
308 isPortrait = portrait_map[ window.orientation ];
310 isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
313 return isPortrait ? "portrait" : "landscape";
316 })( jQuery, window );
319 // throttled resize event
322 $.event.special.throttledresize = {
324 $( this ).bind( "resize", handler );
326 teardown: function(){
327 $( this ).unbind( "resize", handler );
332 handler = function() {
333 curr = ( new Date() ).getTime();
334 diff = curr - lastCall;
336 if ( diff >= throttle ) {
339 $( this ).trigger( "throttledresize" );
344 clearTimeout( heldCall );
347 // Promise a held call will still execute
348 heldCall = setTimeout( handler, throttle - diff );
359 scrollstop: "scrollstart",
363 }, function( event, sourceEvent ) {
365 $.event.special[ event ] = {
367 $( this ).bind( sourceEvent, $.noop );
373 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
375 //>>excludeEnd("jqmBuildExclude");