2.0_beta sync to rsa
[framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.1.0 / js / jquery.mobile.event.js
1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Custom events and shortcuts.
3 //>>label: Events
4 //>>group: Core
5 //>>required: true
6
7 define( [ "jquery", "./jquery.mobile.core", "./jquery.mobile.support", "./jquery.mobile.vmouse" ], function( $ ) {
8 //>>excludeEnd("jqmBuildExclude");
9 (function( $, window, undefined ) {
10
11 // add new event shortcuts
12 $.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
13                                         "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {
14
15         $.fn[ name ] = function( fn ) {
16                 return fn ? this.bind( name, fn ) : this.trigger( name );
17         };
18
19         $.attrFn[ name ] = true;
20 });
21
22 var supportTouch = $.support.touch,
23         scrollEvent = "touchmove scroll",
24         touchStartEvent = supportTouch ? "touchstart" : "mousedown",
25         touchStopEvent = supportTouch ? "touchend" : "mouseup",
26         touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
27
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;
33 }
34
35 // also handles scrollstop
36 $.event.special.scrollstart = {
37
38         enabled: true,
39
40         setup: function() {
41
42                 var thisObject = this,
43                         $this = $( thisObject ),
44                         scrolling,
45                         timer;
46
47                 function trigger( event, state ) {
48                         scrolling = state;
49                         triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
50                 }
51
52                 // iPhone triggers scroll after a small delay; use touchmove instead
53                 $this.bind( scrollEvent, function( event ) {
54
55                         if ( !$.event.special.scrollstart.enabled ) {
56                                 return;
57                         }
58
59                         if ( !scrolling ) {
60                                 trigger( event, true );
61                         }
62
63                         clearTimeout( timer );
64                         timer = setTimeout(function() {
65                                 trigger( event, false );
66                         }, 50 );
67                 });
68         }
69 };
70
71 // also handles taphold
72 $.event.special.tap = {
73         setup: function() {
74                 var thisObject = this,
75                         $this = $( thisObject );
76
77                 $this.bind( "vmousedown", function( event ) {
78
79                         if ( event.which && event.which !== 1 ) {
80                                 return false;
81                         }
82
83                         var origTarget = event.target,
84                                 origEvent = event.originalEvent,
85                                 timer;
86
87                         function clearTapTimer() {
88                                 clearTimeout( timer );
89                         }
90
91                         function clearTapHandlers() {
92                                 clearTapTimer();
93
94                                 $this.unbind( "vclick", clickHandler )
95                                         .unbind( "vmouseup", clearTapTimer );
96                                 $( document ).unbind( "vmousecancel", clearTapHandlers );
97                         }
98
99                         function clickHandler(event) {
100                                 clearTapHandlers();
101
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 );
106                                 }
107                         }
108
109                         $this.bind( "vmouseup", clearTapTimer )
110                                 .bind( "vclick", clickHandler );
111                         $( document ).bind( "vmousecancel", clearTapHandlers );
112
113                         timer = setTimeout(function() {
114                                         triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
115                         }, 750 );
116                 });
117         }
118 };
119
120 // also handles swipeleft, swiperight
121 $.event.special.swipe = {
122         scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.
123
124         durationThreshold: 1000, // More time than this, and it isn't a swipe.
125
126         horizontalDistanceThreshold: 30,  // Swipe horizontal displacement must be more than this.
127
128         verticalDistanceThreshold: 75,  // Swipe vertical displacement must be less than this.
129
130         setup: function() {
131                 var thisObject = this,
132                         $this = $( thisObject );
133
134                 $this.bind( touchStartEvent, function( event ) {
135                         var data = event.originalEvent.touches ?
136                                                                 event.originalEvent.touches[ 0 ] : event,
137                                 start = {
138                                         time: ( new Date() ).getTime(),
139                                         coords: [ data.pageX, data.pageY ],
140                                         origin: $( event.target )
141                                 },
142                                 stop;
143
144                         function moveHandler( event ) {
145
146                                 if ( !start ) {
147                                         return;
148                                 }
149
150                                 var data = event.originalEvent.touches ?
151                                                 event.originalEvent.touches[ 0 ] : event;
152
153                                 stop = {
154                                         time: ( new Date() ).getTime(),
155                                         coords: [ data.pageX, data.pageY ]
156                                 };
157
158                                 // prevent scrolling
159                                 if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
160                                         event.preventDefault();
161                                 }
162                         }
163
164                         $this.bind( touchMoveEvent, moveHandler )
165                                 .one( touchStopEvent, function( event ) {
166                                         $this.unbind( touchMoveEvent, moveHandler );
167
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 ) {
172
173                                                         start.origin.trigger( "swipe" )
174                                                                 .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
175                                                 }
176                                         }
177                                         start = stop = undefined;
178                                 });
179                 });
180         }
181 };
182
183 (function( $, window ) {
184         // "Cowboy" Ben Alman
185
186         var win = $( window ),
187                 special_event,
188                 get_orientation,
189                 last_orientation,
190                 initial_orientation_is_landscape,
191                 initial_orientation_is_default,
192                 portrait_map = { "0": true, "180": true };
193
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.
201         //
202         // Note that we used to use a media query to figure out what the orientation the browser
203         // thinks it is in:
204         //
205         //     initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");
206         //
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
209         // happen on iPad.
210
211         if ( $.support.orientation ) {
212
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.
221
222                 var ww = window.innerWidth || $( window ).width(),
223                         wh = window.innerHeight || $( window ).height(),
224                         landscape_threshold = 50;
225
226                 initial_orientation_is_landscape = ww > wh && ( ww - wh ) > landscape_threshold;
227
228
229                 // Now check to see if the current window.orientation is 0 or 180.
230                 initial_orientation_is_default = portrait_map[ window.orientation ];
231
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 };
238                 }
239         }
240
241         $.event.special.orientationchange = special_event = {
242                 setup: function() {
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 ) {
246                                 return false;
247                         }
248
249                         // Get the current orientation to avoid initial double-triggering.
250                         last_orientation = get_orientation();
251
252                         // Because the orientationchange event doesn't exist, simulate the
253                         // event by testing window dimensions on resize.
254                         win.bind( "throttledresize", handler );
255                 },
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 ) {
260                                 return false;
261                         }
262
263                         // Because the orientationchange event doesn't exist, unbind the
264                         // resize event handler.
265                         win.unbind( "throttledresize", handler );
266                 },
267                 add: function( handleObj ) {
268                         // Save a reference to the bound event handler.
269                         var old_handler = handleObj.handler;
270
271
272                         handleObj.handler = function( event ) {
273                                 // Modify event object, adding the .orientation property.
274                                 event.orientation = get_orientation();
275
276                                 // Call the originally-bound event handler and return its result.
277                                 return old_handler.apply( this, arguments );
278                         };
279                 }
280         };
281
282         // If the event is not supported natively, this handler will be bound to
283         // the window resize event to simulate the orientationchange event.
284         function handler() {
285                 // Get the current orientation.
286                 var orientation = get_orientation();
287
288                 if ( orientation !== last_orientation ) {
289                         // The orientation has changed, so trigger the orientationchange event.
290                         last_orientation = orientation;
291                         win.trigger( "orientationchange" );
292                 }
293         }
294
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;
299
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 ];
309                 } else {
310                         isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
311                 }
312
313                 return isPortrait ? "portrait" : "landscape";
314         };
315
316 })( jQuery, window );
317
318
319 // throttled resize event
320 (function() {
321
322         $.event.special.throttledresize = {
323                 setup: function() {
324                         $( this ).bind( "resize", handler );
325                 },
326                 teardown: function(){
327                         $( this ).unbind( "resize", handler );
328                 }
329         };
330
331         var throttle = 250,
332                 handler = function() {
333                         curr = ( new Date() ).getTime();
334                         diff = curr - lastCall;
335
336                         if ( diff >= throttle ) {
337
338                                 lastCall = curr;
339                                 $( this ).trigger( "throttledresize" );
340
341                         } else {
342
343                                 if ( heldCall ) {
344                                         clearTimeout( heldCall );
345                                 }
346
347                                 // Promise a held call will still execute
348                                 heldCall = setTimeout( handler, throttle - diff );
349                         }
350                 },
351                 lastCall = 0,
352                 heldCall,
353                 curr,
354                 diff;
355 })();
356
357
358 $.each({
359         scrollstop: "scrollstart",
360         taphold: "tap",
361         swipeleft: "swipe",
362         swiperight: "swipe"
363 }, function( event, sourceEvent ) {
364
365         $.event.special[ event ] = {
366                 setup: function() {
367                         $( this ).bind( sourceEvent, $.noop );
368                 }
369         };
370 });
371
372 })( jQuery, this );
373 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
374 });
375 //>>excludeEnd("jqmBuildExclude");