jquery event: misc change
[jquery.git] / src / event.js
1 /*
2  * A number of helper functions used for managing events.
3  * Many of the ideas behind this code originated from
4  * Dean Edwards' addEvent library.
5  */
6 jQuery.event = {
7
8         // Bind an event to an element
9         // Original by Dean Edwards
10         add: function( elem, types, handler, data ) {
11                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
12                         return;
13                 }
14
15                 // For whatever reason, IE has trouble passing the window object
16                 // around, causing it to be cloned in the process
17                 if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
18                         elem = window;
19                 }
20
21                 // Make sure that the function being executed has a unique ID
22                 if ( !handler.guid ) {
23                         handler.guid = this.guid++;
24                 }
25
26                 // if data is passed, bind to handler
27                 if ( data !== undefined ) {
28                         // Create temporary function pointer to original handler
29                         var fn = handler;
30
31                         // Create unique handler function, wrapped around original handler
32                         handler = this.proxy( fn );
33
34                         // Store data in unique handler
35                         handler.data = data;
36                 }
37
38                 // Init the element's event structure
39                 var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ),
40                         handle = jQuery.data( elem, "handle" ) || jQuery.data( elem, "handle", function() {
41                                 // Handle the second event of a trigger and when
42                                 // an event is called after a page has unloaded
43                                 return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
44                                         jQuery.event.handle.apply( arguments.callee.elem, arguments ) :
45                                         undefined;
46                         });
47                 // Add elem as a property of the handle function
48                 // This is to prevent a memory leak with non-native
49                 // event in IE.
50                 handle.elem = elem;
51
52                 // Handle multiple events separated by a space
53                 // jQuery(...).bind("mouseover mouseout", fn);
54                 types = types.split( /\s+/ );
55                 var type, i=0;
56                 while ( (type = types[ i++ ]) ) {
57                         // Namespaced event handlers
58                         var namespaces = type.split(".");
59                         type = namespaces.shift();
60                         handler.type = namespaces.slice().sort().join(".");
61
62                         // Get the current list of functions bound to this event
63                         var handlers = events[ type ],
64                                 special = this.special[ type ] || {};
65
66                         if ( special.add ) {
67                                 var modifiedHandler = special.add.call( elem, handler, data, namespaces );
68                                 if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) {
69                                         modifiedHandler.guid = modifiedHandler.guid || handler.guid;
70                                         handler = modifiedHandler;
71                                 }
72                         }
73
74                         // Init the event handler queue
75                         if ( !handlers ) {
76                                 handlers = events[ type ] = {};
77
78                                 // Check for a special event handler
79                                 // Only use addEventListener/attachEvent if the special
80                                 // events handler returns false
81                                 if ( !special.setup || special.setup.call( elem, data, namespaces ) === false ) {
82                                         // Bind the global event handler to the element
83                                         if ( elem.addEventListener ) {
84                                                 elem.addEventListener( type, handle, false );
85                                         } else if ( elem.attachEvent ) {
86                                                 elem.attachEvent( "on" + type, handle );
87                                         }
88                                 }
89                         }
90
91                         // Add the function to the element's handler list
92                         handlers[ handler.guid ] = handler;
93
94                         // Keep track of which events have been used, for global triggering
95                         this.global[ type ] = true;
96                 }
97
98                 // Nullify elem to prevent memory leaks in IE
99                 elem = null;
100         },
101
102         guid: 1,
103         global: {},
104
105         // Detach an event or set of events from an element
106         remove: function( elem, types, handler ) {
107                 // don't do events on text and comment nodes
108                 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
109                         return;
110                 }
111
112                 var events = jQuery.data( elem, "events" ), ret, type;
113
114                 if ( events ) {
115                         // Unbind all events for the element
116                         if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
117                                 for ( type in events ) {
118                                         this.remove( elem, type + (types || "") );
119                                 }
120                         } else {
121                                 // types is actually an event object here
122                                 if ( types.type ) {
123                                         handler = types.handler;
124                                         types = types.type;
125                                 }
126
127                                 // Handle multiple events seperated by a space
128                                 // jQuery(...).unbind("mouseover mouseout", fn);
129                                 types = types.split(/\s+/);
130                                 var i = 0;
131                                 while ( (type = types[ i++ ]) ) {
132                                         // Namespaced event handlers
133                                         var namespaces = type.split(".");
134                                         type = namespaces.shift();
135                                         var all = !namespaces.length,
136                                                 namespace = new RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)"),
137                                                 special = this.special[ type ] || {};
138
139                                         if ( events[ type ] ) {
140                                                 // remove the given handler for the given type
141                                                 if ( handler ) {
142                                                         delete events[ type ][ handler.guid ];
143
144                                                 // remove all handlers for the given type
145                                                 } else {
146                                                         for ( var handle in events[ type ] ) {
147                                                                 // Handle the removal of namespaced events
148                                                                 if ( all || namespace.test( events[ type ][ handle ].type ) ) {
149                                                                         delete events[ type ][ handle ];
150                                                                 }
151                                                         }
152                                                 }
153
154                                                 if ( special.remove ) {
155                                                         special.remove.call( elem, namespaces );
156                                                 }
157
158                                                 // remove generic event handler if no more handlers exist
159                                                 for ( ret in events[ type ] ) {
160                                                         break;
161                                                 }
162                                                 if ( !ret ) {
163                                                         if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
164                                                                 if ( elem.removeEventListener ) {
165                                                                         elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
166                                                                 } else if ( elem.detachEvent ) {
167                                                                         elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
168                                                                 }
169                                                         }
170                                                         ret = null;
171                                                         delete events[ type ];
172                                                 }
173                                         }
174                                 }
175                         }
176
177                         // Remove the expando if it's no longer used
178                         for ( ret in events ) {
179                                 break;
180                         }
181                         if ( !ret ) {
182                                 var handle = jQuery.data( elem, "handle" );
183                                 if ( handle ) {
184                                         handle.elem = null;
185                                 }
186                                 jQuery.removeData( elem, "events" );
187                                 jQuery.removeData( elem, "handle" );
188                         }
189                 }
190         },
191
192         // bubbling is internal
193         trigger: function( event, data, elem /*, bubbling */ ) {
194                 // Event object or event type
195                 var type = event.type || event,
196                         bubbling = arguments[3];
197
198                 if ( !bubbling ) {
199                         event = typeof event === "object" ?
200                                 // jQuery.Event object
201                                 event[expando] ? event :
202                                 // Object literal
203                                 jQuery.extend( jQuery.Event(type), event ) :
204                                 // Just the event type (string)
205                                 jQuery.Event(type);
206
207                         if ( type.indexOf("!") >= 0 ) {
208                                 event.type = type = type.slice(0, -1);
209                                 event.exclusive = true;
210                         }
211
212                         // Handle a global trigger
213                         if ( !elem ) {
214                                 // Don't bubble custom events when global (to avoid too much overhead)
215                                 event.stopPropagation();
216                                 // Only trigger if we've ever bound an event for it
217                                 if ( this.global[ type ] ) {
218                                         jQuery.each( jQuery.cache, function() {
219                                                 if ( this.events && this.events[type] ) {
220                                                         jQuery.event.trigger( event, data, this.handle.elem );
221                                                 }
222                                         });
223                                 }
224                         }
225
226                         // Handle triggering a single element
227
228                         // don't do events on text and comment nodes
229                         if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
230                                 return undefined;
231                         }
232
233                         // Clean up in case it is reused
234                         event.result = undefined;
235                         event.target = elem;
236
237                         // Clone the incoming data, if any
238                         data = jQuery.makeArray( data );
239                         data.unshift( event );
240                 }
241
242                 event.currentTarget = elem;
243
244                 // Trigger the event, it is assumed that "handle" is a function
245                 var handle = jQuery.data( elem, "handle" );
246                 if ( handle ) {
247                         handle.apply( elem, data );
248                 }
249
250                 // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
251                 if ( (!elem[ type ] || (jQuery.nodeName(elem, 'a') && type === "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) {
252                         event.result = false;
253                 }
254
255                 // Trigger the native events (except for clicks on links)
256                 if ( !bubbling && elem[ type ] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type === "click") ) {
257                         this.triggered = true;
258                         try {
259                                 elem[ type ]();
260                         // prevent IE from throwing an error for some hidden elements
261                         } catch (e) {}
262                 }
263
264                 this.triggered = false;
265
266                 if ( !event.isPropagationStopped() ) {
267                         var parent = elem.parentNode || elem.ownerDocument;
268                         if ( parent ) {
269                                 jQuery.event.trigger( event, data, parent, true );
270                         }
271                 }
272         },
273
274         handle: function( event ) {
275                 // returned undefined or false
276                 var all, handlers;
277
278                 event = arguments[0] = jQuery.event.fix( event || window.event );
279                 event.currentTarget = this;
280
281                 // Namespaced event handlers
282                 var namespaces = event.type.split(".");
283                 event.type = namespaces.shift();
284
285                 // Cache this now, all = true means, any handler
286                 all = !namespaces.length && !event.exclusive;
287
288                 var namespace = new RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
289
290                 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
291
292                 for ( var j in handlers ) {
293                         var handler = handlers[ j ];
294
295                         // Filter the functions by class
296                         if ( all || namespace.test(handler.type) ) {
297                                 // Pass in a reference to the handler function itself
298                                 // So that we can later remove it
299                                 event.handler = handler;
300                                 event.data = handler.data;
301
302                                 var ret = handler.apply( this, arguments );
303
304                                 if ( ret !== undefined ) {
305                                         event.result = ret;
306                                         if ( ret === false ) {
307                                                 event.preventDefault();
308                                                 event.stopPropagation();
309                                         }
310                                 }
311
312                                 if ( event.isImmediatePropagationStopped() ) {
313                                         break;
314                                 }
315
316                         }
317                 }
318         },
319
320         props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
321
322         fix: function( event ) {
323                 if ( event[ expando ] ) {
324                         return event;
325                 }
326
327                 // store a copy of the original event object
328                 // and "clone" to set read-only properties
329                 var originalEvent = event;
330                 event = jQuery.Event( originalEvent );
331
332                 for ( var i = this.props.length, prop; i; ) {
333                         prop = this.props[ --i ];
334                         event[ prop ] = originalEvent[ prop ];
335                 }
336
337                 // Fix target property, if necessary
338                 if ( !event.target ) {
339                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
340                 }
341
342                 // check if target is a textnode (safari)
343                 if ( event.target.nodeType === 3 ) {
344                         event.target = event.target.parentNode;
345                 }
346
347                 // Add relatedTarget, if necessary
348                 if ( !event.relatedTarget && event.fromElement ) {
349                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
350                 }
351
352                 // Calculate pageX/Y if missing and clientX/Y available
353                 if ( event.pageX == null && event.clientX != null ) {
354                         var doc = document.documentElement, body = document.body;
355                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
356                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
357                 }
358
359                 // Add which for key events
360                 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
361                         event.which = event.charCode || event.keyCode;
362                 }
363
364                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
365                 if ( !event.metaKey && event.ctrlKey ) {
366                         event.metaKey = event.ctrlKey;
367                 }
368
369                 // Add which for click: 1 == left; 2 == middle; 3 == right
370                 // Note: button is not normalized, so don't use it
371                 if ( !event.which && event.button ) {
372                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
373                 }
374
375                 return event;
376         },
377
378         proxy: function( fn, proxy, thisObject ) {
379                 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
380                         thisObject = proxy;
381                         proxy = undefined;
382                 }
383                 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
384                 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
385                 // Set the guid of unique handler to the same of original handler, so it can be removed
386                 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
387                 // So proxy can be declared as an argument
388                 return proxy;
389         },
390
391         special: {
392                 ready: {
393                         // Make sure the ready event is setup
394                         setup: bindReady,
395                         teardown: function() {}
396                 },
397
398                 live: {
399                         add: function( proxy, data, namespaces ) {
400                                 jQuery.extend( proxy, data || {} );
401                                 proxy.guid += data.selector + data.live;
402                                 jQuery.event.add( this, data.live, liveHandler );
403                         },
404
405                         teardown: function( namespaces ) {
406                                 jQuery.event.remove( this, namespaces[0], liveHandler );
407                         }
408                 }
409         }
410 };
411
412 jQuery.Event = function( src ){
413         // Allow instantiation without the 'new' keyword
414         if ( !this.preventDefault ) {
415                 return new jQuery.Event( src );
416         }
417
418         // Event object
419         if ( src && src.type ) {
420                 this.originalEvent = src;
421                 this.type = src.type;
422         // Event type
423         } else {
424                 this.type = src;
425         }
426
427         // timeStamp is buggy for some events on Firefox(#3843)
428         // So we won't rely on the native value
429         this.timeStamp = now();
430
431         // Mark it as fixed
432         this[ expando ] = true;
433 };
434
435 function returnFalse() {
436         return false;
437 }
438 function returnTrue() {
439         return true;
440 }
441
442 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
443 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
444 jQuery.Event.prototype = {
445         preventDefault: function() {
446                 this.isDefaultPrevented = returnTrue;
447
448                 var e = this.originalEvent;
449                 if ( !e ) {
450                         return;
451                 }
452                 // if preventDefault exists run it on the original event
453                 if ( e.preventDefault ) {
454                         e.preventDefault();
455                 }
456                 // otherwise set the returnValue property of the original event to false (IE)
457                 e.returnValue = false;
458         },
459         stopPropagation: function() {
460                 this.isPropagationStopped = returnTrue;
461
462                 var e = this.originalEvent;
463                 if ( !e ) {
464                         return;
465                 }
466                 // if stopPropagation exists run it on the original event
467                 if ( e.stopPropagation ) {
468                         e.stopPropagation();
469                 }
470                 // otherwise set the cancelBubble property of the original event to true (IE)
471                 e.cancelBubble = true;
472         },
473         stopImmediatePropagation: function(){
474                 this.isImmediatePropagationStopped = returnTrue;
475                 this.stopPropagation();
476         },
477         isDefaultPrevented: returnFalse,
478         isPropagationStopped: returnFalse,
479         isImmediatePropagationStopped: returnFalse
480 };
481 // Checks if an event happened on an element within another element
482 // Used in jQuery.event.special.mouseenter and mouseleave handlers
483 var withinElement = function( event ) {
484         // Check if mouse(over|out) are still within the same parent element
485         var parent = event.relatedTarget;
486         // Traverse up the tree
487         while ( parent && parent != this ) {
488                 // Firefox sometimes assigns relatedTarget a XUL element
489                 // which we cannot access the parentNode property of
490                 try { parent = parent.parentNode; }
491                 // assuming we've left the element since we most likely mousedover a xul element
492                 catch(e) { break; }
493         }
494
495         if ( parent != this ) {
496                 // set the correct event type
497                 event.type = event.data;
498                 // handle event if we actually just moused on to a non sub-element
499                 jQuery.event.handle.apply( this, arguments );
500         }
501 };
502
503 jQuery.each({
504         mouseover: 'mouseenter',
505         mouseout: 'mouseleave'
506 }, function( orig, fix ) {
507         jQuery.event.special[ fix ] = {
508                 setup: function(){
509                         jQuery.event.add( this, orig, withinElement, fix );
510                 },
511                 teardown: function(){
512                         jQuery.event.remove( this, orig, withinElement );
513                 }
514         };
515 });
516
517 jQuery.fn.extend({
518         bind: function( type, data, fn, thisObject ) {
519                 if ( jQuery.isFunction( data ) ) {
520                         if ( fn !== undefined ) {
521                                 thisObject = fn;
522                         }
523                         fn = data;
524                         data = undefined;
525                 }
526                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
527                 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
528                         jQuery.event.add( this, type, fn, data );
529                 });
530         },
531
532         one: function( type, data, fn, thisObject ) {
533                 if ( jQuery.isFunction( data ) ) {
534                         if ( fn !== undefined ) {
535                                 thisObject = fn;
536                         }
537                         fn = data;
538                         data = undefined;
539                 }
540                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
541                 var one = jQuery.event.proxy( fn, function( event ) {
542                         jQuery( this ).unbind( event, one );
543                         return fn.apply( this, arguments );
544                 });
545                 return this.each(function() {
546                         jQuery.event.add( this, type, one, data );
547                 });
548         },
549
550         unbind: function( type, fn ) {
551                 return this.each(function() {
552                         jQuery.event.remove( this, type, fn );
553                 });
554         },
555
556         trigger: function( type, data ) {
557                 return this.each(function() {
558                         jQuery.event.trigger( type, data, this );
559                 });
560         },
561
562         triggerHandler: function( type, data ) {
563                 if ( this[0] ) {
564                         var event = jQuery.Event( type );
565                         event.preventDefault();
566                         event.stopPropagation();
567                         jQuery.event.trigger( event, data, this[0] );
568                         return event.result;
569                 }
570         },
571
572         toggle: function( fn ) {
573                 // Save reference to arguments for access in closure
574                 var args = arguments, i = 1;
575
576                 // link all the functions, so any of them can unbind this click handler
577                 while( i < args.length ) {
578                         jQuery.event.proxy( fn, args[ i++ ] );
579                 }
580
581                 return this.click( jQuery.event.proxy( fn, function( event ) {
582                         // Figure out which function to execute
583                         this.lastToggle = ( this.lastToggle || 0 ) % i;
584
585                         // Make sure that clicks stop
586                         event.preventDefault();
587
588                         // and execute the function
589                         return args[ this.lastToggle++ ].apply( this, arguments ) || false;
590                 }));
591         },
592
593         hover: function( fnOver, fnOut ) {
594                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
595         },
596
597         ready: function( fn ) {
598                 // Attach the listeners
599                 bindReady();
600
601                 // If the DOM is already ready
602                 if ( jQuery.isReady ) {
603                         // Execute the function immediately
604                         fn.call( document, jQuery );
605
606                 // Otherwise, remember the function for later
607                 } else {
608                         // Add the function to the wait list
609                         jQuery.readyList.push( fn );
610                 }
611
612                 return this;
613         },
614
615         live: function( type, data, fn, thisObject ) {
616                 if ( jQuery.isFunction( data ) ) {
617                         if ( fn !== undefined ) {
618                                 thisObject = fn;
619                         }
620                         fn = data;
621                         data = undefined;
622                 }
623                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
624                         data: data, selector: this.selector, live: type
625                 }, fn, thisObject );
626                 return this;
627         },
628
629         die: function( type, fn ) {
630                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
631                 return this;
632         }
633 });
634
635 function liveHandler( event ) {
636         var stop = true, elems = [], args = arguments;
637
638         jQuery.each( jQuery.data( this, "events" ).live || [], function( i, fn ) {
639                 if ( fn.live === event.type ) {
640                         var elem = jQuery( event.target ).closest( fn.selector )[0];
641                         if ( elem ) {
642                                 elems.push({ elem: elem, fn: fn });
643                         }
644                 }
645         });
646
647         elems.sort(function( a, b ) {
648                 return jQuery.data( a.elem, "closest" ) - jQuery.data( b.elem, "closest" );
649         });
650
651         jQuery.each(elems, function() {
652                 event.currentTarget = this.elem;
653                 event.data = this.fn.data;
654                 if ( this.fn.apply( this.elem, args ) === false ) {
655                         return (stop = false);
656                 }
657         });
658
659         return stop;
660 }
661
662 function liveConvert( type, selector ) {
663         return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
664 }
665
666 jQuery.extend({
667         isReady: false,
668         readyList: [],
669         // Handle when the DOM is ready
670         ready: function() {
671                 // Make sure that the DOM is not already loaded
672                 if ( !jQuery.isReady ) {
673                         // Remember that the DOM is ready
674                         jQuery.isReady = true;
675
676                         // If there are functions bound, to execute
677                         if ( jQuery.readyList ) {
678                                 // Execute all of them
679                                 var fn, i = 0;
680                                 while ( (fn = jQuery.readyList[ i++ ]) ) {
681                                         fn.call( document, jQuery );
682                                 }
683
684                                 // Reset the list of functions
685                                 jQuery.readyList = null;
686                         }
687
688                         // Trigger any bound ready events
689                         jQuery( document ).triggerHandler( "ready" );
690                 }
691         }
692 });
693
694 var readyBound = false;
695
696 function bindReady() {
697         if ( readyBound ) return;
698         readyBound = true;
699
700         // Mozilla, Opera and webkit nightlies currently support this event
701         if ( document.addEventListener ) {
702                 // Use the handy event callback
703                 document.addEventListener( "DOMContentLoaded", function() {
704                         document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
705                         jQuery.ready();
706                 }, false );
707
708         // If IE event model is used
709         } else if ( document.attachEvent ) {
710                 // ensure firing before onload,
711                 // maybe late but safe also for iframes
712                 document.attachEvent("onreadystatechange", function() {
713                         if ( document.readyState === "complete" ) {
714                                 document.detachEvent( "onreadystatechange", arguments.callee );
715                                 jQuery.ready();
716                         }
717                 });
718
719                 // If IE and not an iframe
720                 // continually check to see if the document is ready
721                 if ( document.documentElement.doScroll && window === window.top ) (function() {
722                         if ( jQuery.isReady ) {
723                                 return;
724                         }
725
726                         try {
727                                 // If IE is used, use the trick by Diego Perini
728                                 // http://javascript.nwbox.com/IEContentLoaded/
729                                 document.documentElement.doScroll("left");
730                         } catch( error ) {
731                                 setTimeout( arguments.callee, 0 );
732                                 return;
733                         }
734
735                         // and execute any waiting functions
736                         jQuery.ready();
737                 })();
738         }
739
740         // A fallback to window.onload, that will always work
741         jQuery.event.add( window, "load", jQuery.ready );
742 }
743
744 jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
745         "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
746         "change,select,submit,keydown,keypress,keyup,error").split(","), function( i, name ) {
747
748         // Handle event binding
749         jQuery.fn[ name ] = function( fn ) {
750                 return fn ? this.bind( name, fn ) : this.trigger( name );
751         };
752 });
753
754 // Prevent memory leaks in IE
755 // And prevent errors on refresh with events like mouseover in other browsers
756 // Window isn't included so as not to unbind existing unload events
757 // More info:
758 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
759 //  - https://bugzilla.mozilla.org/show_bug.cgi?id=252542
760 jQuery( window ).bind( 'unload', function() {
761         for ( var id in jQuery.cache ) {
762                 // Skip the window
763                 if ( id != 1 && jQuery.cache[ id ].handle ) {
764                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
765                 }
766         }
767 });