.bind() now accepts an optional thisObject as the last argument which is used to...
[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
197                 if ( !bubbling ) {
198                         event = typeof event === "object" ?
199                                 // jQuery.Event object
200                                 event[expando] ? event :
201                                 // Object literal
202                                 jQuery.extend( jQuery.Event(type), event ) :
203                                 // Just the event type (string)
204                                 jQuery.Event(type);
205
206                         if ( type.indexOf("!") >= 0 ) {
207                                 event.type = type = type.slice(0, -1);
208                                 event.exclusive = true;
209                         }
210
211                         // Handle a global trigger
212                         if ( !elem ) {
213                                 // Don't bubble custom events when global (to avoid too much overhead)
214                                 event.stopPropagation();
215                                 // Only trigger if we've ever bound an event for it
216                                 if ( this.global[ type ] ) {
217                                         jQuery.each( jQuery.cache, function() {
218                                                 if ( this.events && this.events[type] ) {
219                                                         jQuery.event.trigger( event, data, this.handle.elem );
220                                                 }
221                                         });
222                                 }
223                         }
224
225                         // Handle triggering a single element
226
227                         // don't do events on text and comment nodes
228                         if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
229                                 return undefined;
230                         }
231
232                         // Clean up in case it is reused
233                         event.result = undefined;
234                         event.target = elem;
235
236                         // Clone the incoming data, if any
237                         data = jQuery.makeArray( data );
238                         data.unshift( event );
239                 }
240
241                 event.currentTarget = elem;
242
243                 // Trigger the event, it is assumed that "handle" is a function
244                 var handle = jQuery.data( elem, "handle" );
245                 if ( handle ) {
246                         handle.apply( elem, data );
247                 }
248
249                 // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
250                 if ( (!elem[ type ] || (jQuery.nodeName(elem, 'a') && type === "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) {
251                         event.result = false;
252                 }
253
254                 // Trigger the native events (except for clicks on links)
255                 if ( !bubbling && elem[ type ] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type === "click") ) {
256                         this.triggered = true;
257                         try {
258                                 elem[ type ]();
259                         // prevent IE from throwing an error for some hidden elements
260                         } catch (e) {}
261                 }
262
263                 this.triggered = false;
264
265                 if ( !event.isPropagationStopped() ) {
266                         var parent = elem.parentNode || elem.ownerDocument;
267                         if ( parent ) {
268                                 jQuery.event.trigger( event, data, parent, true );
269                         }
270                 }
271         },
272
273         handle: function( event ) {
274                 // returned undefined or false
275                 var all, handlers;
276
277                 event = arguments[0] = jQuery.event.fix( event || window.event );
278                 event.currentTarget = this;
279
280                 // Namespaced event handlers
281                 var namespaces = event.type.split(".");
282                 event.type = namespaces.shift();
283
284                 // Cache this now, all = true means, any handler
285                 all = !namespaces.length && !event.exclusive;
286
287                 var namespace = new RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
288
289                 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
290
291                 for ( var j in handlers ) {
292                         var handler = handlers[ j ];
293
294                         // Filter the functions by class
295                         if ( all || namespace.test(handler.type) ) {
296                                 // Pass in a reference to the handler function itself
297                                 // So that we can later remove it
298                                 event.handler = handler;
299                                 event.data = handler.data;
300
301                                 var ret = handler.apply( this, arguments );
302
303                                 if ( ret !== undefined ) {
304                                         event.result = ret;
305                                         if ( ret === false ) {
306                                                 event.preventDefault();
307                                                 event.stopPropagation();
308                                         }
309                                 }
310
311                                 if ( event.isImmediatePropagationStopped() ) {
312                                         break;
313                                 }
314
315                         }
316                 }
317         },
318
319         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(" "),
320
321         fix: function( event ) {
322                 if ( event[ expando ] ) {
323                         return event;
324                 }
325
326                 // store a copy of the original event object
327                 // and "clone" to set read-only properties
328                 var originalEvent = event;
329                 event = jQuery.Event( originalEvent );
330
331                 for ( var i = this.props.length, prop; i; ) {
332                         prop = this.props[ --i ];
333                         event[ prop ] = originalEvent[ prop ];
334                 }
335
336                 // Fix target property, if necessary
337                 if ( !event.target ) {
338                         event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
339                 }
340
341                 // check if target is a textnode (safari)
342                 if ( event.target.nodeType === 3 ) {
343                         event.target = event.target.parentNode;
344                 }
345
346                 // Add relatedTarget, if necessary
347                 if ( !event.relatedTarget && event.fromElement ) {
348                         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
349                 }
350
351                 // Calculate pageX/Y if missing and clientX/Y available
352                 if ( event.pageX == null && event.clientX != null ) {
353                         var doc = document.documentElement, body = document.body;
354                         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
355                         event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
356                 }
357
358                 // Add which for key events
359                 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
360                         event.which = event.charCode || event.keyCode;
361                 }
362
363                 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
364                 if ( !event.metaKey && event.ctrlKey ) {
365                         event.metaKey = event.ctrlKey;
366                 }
367
368                 // Add which for click: 1 == left; 2 == middle; 3 == right
369                 // Note: button is not normalized, so don't use it
370                 if ( !event.which && event.button ) {
371                         event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
372                 }
373
374                 return event;
375         },
376
377         proxy: function( fn, proxy, thisObject ) {
378                 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
379                         thisObject = proxy;
380                         proxy = undefined;
381                 }
382                 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
383                 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
384                 // Set the guid of unique handler to the same of original handler, so it can be removed
385                 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
386                 // So proxy can be declared as an argument
387                 return proxy;
388         },
389
390         special: {
391                 ready: {
392                         // Make sure the ready event is setup
393                         setup: bindReady,
394                         teardown: function() {}
395                 },
396
397                 live: {
398                         add: function( proxy, data, namespaces ) {
399                                 jQuery.extend( proxy, data || {} );
400                                 proxy.guid += data.selector + data.live;
401                                 jQuery.event.add( this, data.live, liveHandler );
402                         },
403
404                         teardown: function( namespaces ) {
405                                 jQuery.event.remove( this, namespaces[0], liveHandler );
406                         }
407                 }
408         }
409 };
410
411 jQuery.Event = function( src ){
412         // Allow instantiation without the 'new' keyword
413         if ( !this.preventDefault ) {
414                 return new jQuery.Event( src );
415         }
416
417         // Event object
418         if ( src && src.type ) {
419                 this.originalEvent = src;
420                 this.type = src.type;
421         // Event type
422         } else {
423                 this.type = src;
424         }
425
426         // timeStamp is buggy for some events on Firefox(#3843)
427         // So we won't rely on the native value
428         this.timeStamp = now();
429
430         // Mark it as fixed
431         this[ expando ] = true;
432 };
433
434 function returnFalse() {
435         return false;
436 }
437 function returnTrue() {
438         return true;
439 }
440
441 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
442 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
443 jQuery.Event.prototype = {
444         preventDefault: function() {
445                 this.isDefaultPrevented = returnTrue;
446
447                 var e = this.originalEvent;
448                 if ( !e ) {
449                         return;
450                 }
451                 // if preventDefault exists run it on the original event
452                 if ( e.preventDefault ) {
453                         e.preventDefault();
454                 }
455                 // otherwise set the returnValue property of the original event to false (IE)
456                 e.returnValue = false;
457         },
458         stopPropagation: function() {
459                 this.isPropagationStopped = returnTrue;
460
461                 var e = this.originalEvent;
462                 if ( !e ) {
463                         return;
464                 }
465                 // if stopPropagation exists run it on the original event
466                 if ( e.stopPropagation ) {
467                         e.stopPropagation();
468                 }
469                 // otherwise set the cancelBubble property of the original event to true (IE)
470                 e.cancelBubble = true;
471         },
472         stopImmediatePropagation: function(){
473                 this.isImmediatePropagationStopped = returnTrue;
474                 this.stopPropagation();
475         },
476         isDefaultPrevented: returnFalse,
477         isPropagationStopped: returnFalse,
478         isImmediatePropagationStopped: returnFalse
479 };
480 // Checks if an event happened on an element within another element
481 // Used in jQuery.event.special.mouseenter and mouseleave handlers
482 var withinElement = function( event ) {
483         // Check if mouse(over|out) are still within the same parent element
484         var parent = event.relatedTarget;
485         // Traverse up the tree
486         while ( parent && parent != this ) {
487                 // Firefox sometimes assigns relatedTarget a XUL element
488                 // which we cannot access the parentNode property of
489                 try { parent = parent.parentNode; }
490                 // assuming we've left the element since we most likely mousedover a xul element
491                 catch(e) { break; }
492         }
493
494         if ( parent != this ) {
495                 // set the correct event type
496                 event.type = event.data;
497                 // handle event if we actually just moused on to a non sub-element
498                 jQuery.event.handle.apply( this, arguments );
499         }
500 };
501
502 jQuery.each({
503         mouseover: 'mouseenter',
504         mouseout: 'mouseleave'
505 }, function( orig, fix ) {
506         jQuery.event.special[ fix ] = {
507                 setup: function(){
508                         jQuery.event.add( this, orig, withinElement, fix );
509                 },
510                 teardown: function(){
511                         jQuery.event.remove( this, orig, withinElement );
512                 }
513         };
514 });
515
516 jQuery.fn.extend({
517         bind: function( type, data, fn, thisObject ) {
518                 if ( jQuery.isFunction( data ) ) {
519                         if ( fn !== undefined ) {
520                                 thisObject = fn;
521                         }
522                         fn = data;
523                         data = undefined;
524                 }
525                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
526                 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
527                         jQuery.event.add( this, type, fn, data, thisObject );
528                 });
529         },
530
531         one: function( type, data, fn, thisObject ) {
532                 if ( jQuery.isFunction( data ) ) {
533                         if ( fn !== undefined ) {
534                                 thisObject = fn;
535                         }
536                         fn = data;
537                         data = undefined;
538                 }
539                 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
540                 var one = jQuery.event.proxy( fn, function( event ) {
541                         jQuery( this ).unbind( event, one );
542                         return fn.apply( this, arguments );
543                 });
544                 return this.each(function() {
545                         jQuery.event.add( this, type, one, data, thisObject );
546                 });
547         },
548
549         unbind: function( type, fn ) {
550                 return this.each(function() {
551                         jQuery.event.remove( this, type, fn );
552                 });
553         },
554
555         trigger: function( type, data ) {
556                 return this.each(function() {
557                         jQuery.event.trigger( type, data, this );
558                 });
559         },
560
561         triggerHandler: function( type, data ) {
562                 if ( this[0] ) {
563                         var event = jQuery.Event( type );
564                         event.preventDefault();
565                         event.stopPropagation();
566                         jQuery.event.trigger( event, data, this[0] );
567                         return event.result;
568                 }
569         },
570
571         toggle: function( fn ) {
572                 // Save reference to arguments for access in closure
573                 var args = arguments, i = 1;
574
575                 // link all the functions, so any of them can unbind this click handler
576                 while( i < args.length ) {
577                         jQuery.event.proxy( fn, args[ i++ ] );
578                 }
579
580                 return this.click( jQuery.event.proxy( fn, function( event ) {
581                         // Figure out which function to execute
582                         this.lastToggle = ( this.lastToggle || 0 ) % i;
583
584                         // Make sure that clicks stop
585                         event.preventDefault();
586
587                         // and execute the function
588                         return args[ this.lastToggle++ ].apply( this, arguments ) || false;
589                 }));
590         },
591
592         hover: function( fnOver, fnOut ) {
593                 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
594         },
595
596         ready: function( fn ) {
597                 // Attach the listeners
598                 bindReady();
599
600                 // If the DOM is already ready
601                 if ( jQuery.isReady ) {
602                         // Execute the function immediately
603                         fn.call( document, jQuery );
604
605                 // Otherwise, remember the function for later
606                 } else {
607                         // Add the function to the wait list
608                         jQuery.readyList.push( fn );
609                 }
610
611                 return this;
612         },
613
614         live: function( type, data, fn, thisObject ) {
615                 if ( jQuery.isFunction( data ) ) {
616                         if ( fn !== undefined ) {
617                                 thisObject = fn;
618                         }
619                         fn = data;
620                         data = undefined;
621                 }
622                 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
623                         data: data, selector: this.selector, live: type
624                 }, fn, thisObject );
625                 return this;
626         },
627
628         die: function( type, fn ) {
629                 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
630                 return this;
631         }
632 });
633
634 function liveHandler( event ) {
635         var stop = true, elems = [], args = arguments;
636
637         jQuery.each( jQuery.data( this, "events" ).live || [], function( i, fn ) {
638                 if ( fn.live === event.type ) {
639                         var elem = jQuery( event.target ).closest( fn.selector )[0];
640                         if ( elem ) {
641                                 elems.push({ elem: elem, fn: fn });
642                         }
643                 }
644         });
645
646         elems.sort(function( a, b ) {
647                 return jQuery.data( a.elem, "closest" ) - jQuery.data( b.elem, "closest" );
648         });
649
650         jQuery.each(elems, function() {
651                 event.currentTarget = this.elem;
652                 event.data = this.fn.data;
653                 if ( this.fn.apply( this.elem, args ) === false ) {
654                         return (stop = false);
655                 }
656         });
657
658         return stop;
659 }
660
661 function liveConvert( type, selector ) {
662         return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
663 }
664
665 jQuery.extend({
666         isReady: false,
667         readyList: [],
668         // Handle when the DOM is ready
669         ready: function() {
670                 // Make sure that the DOM is not already loaded
671                 if ( !jQuery.isReady ) {
672                         // Remember that the DOM is ready
673                         jQuery.isReady = true;
674
675                         // If there are functions bound, to execute
676                         if ( jQuery.readyList ) {
677                                 // Execute all of them
678                                 var fn, i = 0;
679                                 while ( (fn = jQuery.readyList[ i++ ]) ) {
680                                         fn.call( document, jQuery );
681                                 }
682
683                                 // Reset the list of functions
684                                 jQuery.readyList = null;
685                         }
686
687                         // Trigger any bound ready events
688                         jQuery( document ).triggerHandler( "ready" );
689                 }
690         }
691 });
692
693 var readyBound = false;
694
695 function bindReady() {
696         if ( readyBound ) return;
697         readyBound = true;
698
699         // Mozilla, Opera and webkit nightlies currently support this event
700         if ( document.addEventListener ) {
701                 // Use the handy event callback
702                 document.addEventListener( "DOMContentLoaded", function() {
703                         document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
704                         jQuery.ready();
705                 }, false );
706
707         // If IE event model is used
708         } else if ( document.attachEvent ) {
709                 // ensure firing before onload,
710                 // maybe late but safe also for iframes
711                 document.attachEvent("onreadystatechange", function() {
712                         if ( document.readyState === "complete" ) {
713                                 document.detachEvent( "onreadystatechange", arguments.callee );
714                                 jQuery.ready();
715                         }
716                 });
717
718                 // If IE and not an iframe
719                 // continually check to see if the document is ready
720                 if ( document.documentElement.doScroll && window === window.top ) (function() {
721                         if ( jQuery.isReady ) {
722                                 return;
723                         }
724
725                         try {
726                                 // If IE is used, use the trick by Diego Perini
727                                 // http://javascript.nwbox.com/IEContentLoaded/
728                                 document.documentElement.doScroll("left");
729                         } catch( error ) {
730                                 setTimeout( arguments.callee, 0 );
731                                 return;
732                         }
733
734                         // and execute any waiting functions
735                         jQuery.ready();
736                 })();
737         }
738
739         // A fallback to window.onload, that will always work
740         jQuery.event.add( window, "load", jQuery.ready );
741 }
742
743 jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
744         "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
745         "change,select,submit,keydown,keypress,keyup,error").split(","), function( i, name ) {
746
747         // Handle event binding
748         jQuery.fn[ name ] = function( fn ) {
749                 return fn ? this.bind( name, fn ) : this.trigger( name );
750         };
751 });
752
753 // Prevent memory leaks in IE
754 // And prevent errors on refresh with events like mouseover in other browsers
755 // Window isn't included so as not to unbind existing unload events
756 // More info:
757 //  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
758 //  - https://bugzilla.mozilla.org/show_bug.cgi?id=252542
759 jQuery( window ).bind( 'unload', function() {
760         for ( var id in jQuery.cache ) {
761                 // Skip the window
762                 if ( id != 1 && jQuery.cache[ id ].handle ) {
763                         jQuery.event.remove( jQuery.cache[ id ].handle.elem );
764                 }
765         }
766 });