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.
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 ) {
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 ) ) {
21 // Make sure that the function being executed has a unique ID
22 if ( !handler.guid ) {
23 handler.guid = this.guid++;
26 // if data is passed, bind to handler
27 if ( data !== undefined ) {
28 // Create temporary function pointer to original handler
31 // Create unique handler function, wrapped around original handler
32 handler = this.proxy( fn );
34 // Store data in unique handler
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 ) :
47 // Add elem as a property of the handle function
48 // This is to prevent a memory leak with non-native
52 // Handle multiple events separated by a space
53 // jQuery(...).bind("mouseover mouseout", fn);
54 types = types.split( /\s+/ );
56 while ( (type = types[ i++ ]) ) {
57 // Namespaced event handlers
58 var namespaces = type.split(".");
59 type = namespaces.shift();
60 handler.type = namespaces.slice(0).sort().join(".");
62 // Get the current list of functions bound to this event
63 var handlers = events[ type ],
64 special = this.special[ type ] || {};
68 // Init the event handler queue
70 handlers = events[ type ] = {};
72 // Check for a special event handler
73 // Only use addEventListener/attachEvent if the special
74 // events handler returns false
75 if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) {
76 // Bind the global event handler to the element
77 if ( elem.addEventListener ) {
78 elem.addEventListener( type, handle, false );
79 } else if ( elem.attachEvent ) {
80 elem.attachEvent( "on" + type, handle );
86 var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers );
87 if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) {
88 modifiedHandler.guid = modifiedHandler.guid || handler.guid;
89 handler = modifiedHandler;
93 // Add the function to the element's handler list
94 handlers[ handler.guid ] = handler;
96 // Keep track of which events have been used, for global triggering
97 this.global[ type ] = true;
100 // Nullify elem to prevent memory leaks in IE
107 // Detach an event or set of events from an element
108 remove: function( elem, types, handler ) {
109 // don't do events on text and comment nodes
110 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
114 var events = jQuery.data( elem, "events" ), ret, type, fn;
117 // Unbind all events for the element
118 if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
119 for ( type in events ) {
120 this.remove( elem, type + (types || "") );
123 // types is actually an event object here
125 handler = types.handler;
129 // Handle multiple events seperated by a space
130 // jQuery(...).unbind("mouseover mouseout", fn);
131 types = types.split(/\s+/);
133 while ( (type = types[ i++ ]) ) {
134 // Namespaced event handlers
135 var namespaces = type.split(".");
136 type = namespaces.shift();
137 var all = !namespaces.length,
138 cleaned = jQuery.map( namespaces.slice(0).sort() , function(nm){ return nm.replace(/[^\w\s\.\|`]/g, function(ch){return "\\"+ch }) }),
139 namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
140 special = this.special[ type ] || {};
142 if ( events[ type ] ) {
143 // remove the given handler for the given type
145 fn = events[ type ][ handler.guid ];
146 delete events[ type ][ handler.guid ];
148 // remove all handlers for the given type
150 for ( var handle in events[ type ] ) {
151 // Handle the removal of namespaced events
152 if ( all || namespace.test( events[ type ][ handle ].type ) ) {
153 delete events[ type ][ handle ];
158 if ( special.remove ) {
159 special.remove.call( elem, namespaces, fn);
162 // remove generic event handler if no more handlers exist
163 for ( ret in events[ type ] ) {
167 if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
168 if ( elem.removeEventListener ) {
169 elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
170 } else if ( elem.detachEvent ) {
171 elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
175 delete events[ type ];
181 // Remove the expando if it's no longer used
182 for ( ret in events ) {
186 var handle = jQuery.data( elem, "handle" );
190 jQuery.removeData( elem, "events" );
191 jQuery.removeData( elem, "handle" );
196 // bubbling is internal
197 trigger: function( event, data, elem /*, bubbling */ ) {
198 // Event object or event type
199 var type = event.type || event,
200 bubbling = arguments[3];
203 event = typeof event === "object" ?
204 // jQuery.Event object
205 event[expando] ? event :
207 jQuery.extend( jQuery.Event(type), event ) :
208 // Just the event type (string)
211 if ( type.indexOf("!") >= 0 ) {
212 event.type = type = type.slice(0, -1);
213 event.exclusive = true;
216 // Handle a global trigger
218 // Don't bubble custom events when global (to avoid too much overhead)
219 event.stopPropagation();
220 // Only trigger if we've ever bound an event for it
221 if ( this.global[ type ] ) {
222 jQuery.each( jQuery.cache, function() {
223 if ( this.events && this.events[type] ) {
224 jQuery.event.trigger( event, data, this.handle.elem );
230 // Handle triggering a single element
232 // don't do events on text and comment nodes
233 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
237 // Clean up in case it is reused
238 event.result = undefined;
241 // Clone the incoming data, if any
242 data = jQuery.makeArray( data );
243 data.unshift( event );
246 event.currentTarget = elem;
248 // Trigger the event, it is assumed that "handle" is a function
249 var handle = jQuery.data( elem, "handle" );
251 handle.apply( elem, data );
254 var nativeFn, nativeHandler;
256 nativeFn = elem[ type ];
257 nativeHandler = elem[ "on" + type ];
258 // prevent IE from throwing an error for some elements with some event types, see #3533
260 // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
261 if ( (!nativeFn || (jQuery.nodeName(elem, 'a') && type === "click")) && nativeHandler && nativeHandler.apply( elem, data ) === false ) {
262 event.result = false;
265 // Trigger the native events (except for clicks on links)
266 if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type === "click") ) {
267 this.triggered = true;
270 // prevent IE from throwing an error for some hidden elements
274 this.triggered = false;
276 if ( !event.isPropagationStopped() ) {
277 var parent = elem.parentNode || elem.ownerDocument;
279 jQuery.event.trigger( event, data, parent, true );
284 handle: function( event ) {
285 // returned undefined or false
288 event = arguments[0] = jQuery.event.fix( event || window.event );
289 event.currentTarget = this;
291 // Namespaced event handlers
292 var namespaces = event.type.split(".");
293 event.type = namespaces.shift();
295 // Cache this now, all = true means, any handler
296 all = !namespaces.length && !event.exclusive;
298 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
300 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
302 for ( var j in handlers ) {
303 var handler = handlers[ j ];
305 // Filter the functions by class
306 if ( all || namespace.test(handler.type) ) {
307 // Pass in a reference to the handler function itself
308 // So that we can later remove it
309 event.handler = handler;
310 event.data = handler.data;
312 var ret = handler.apply( this, arguments );
314 if ( ret !== undefined ) {
316 if ( ret === false ) {
317 event.preventDefault();
318 event.stopPropagation();
322 if ( event.isImmediatePropagationStopped() ) {
330 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(" "),
332 fix: function( event ) {
333 if ( event[ expando ] ) {
337 // store a copy of the original event object
338 // and "clone" to set read-only properties
339 var originalEvent = event;
340 event = jQuery.Event( originalEvent );
342 for ( var i = this.props.length, prop; i; ) {
343 prop = this.props[ --i ];
344 event[ prop ] = originalEvent[ prop ];
347 // Fix target property, if necessary
348 if ( !event.target ) {
349 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
352 // check if target is a textnode (safari)
353 if ( event.target.nodeType === 3 ) {
354 event.target = event.target.parentNode;
357 // Add relatedTarget, if necessary
358 if ( !event.relatedTarget && event.fromElement ) {
359 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
362 // Calculate pageX/Y if missing and clientX/Y available
363 if ( event.pageX == null && event.clientX != null ) {
364 var doc = document.documentElement, body = document.body;
365 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
366 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
369 // Add which for key events
370 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
371 event.which = event.charCode || event.keyCode;
374 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
375 if ( !event.metaKey && event.ctrlKey ) {
376 event.metaKey = event.ctrlKey;
379 // Add which for click: 1 == left; 2 == middle; 3 == right
380 // Note: button is not normalized, so don't use it
381 if ( !event.which && event.button !== undefined ) {
382 event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
388 proxy: function( fn, proxy, thisObject ) {
389 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
393 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
394 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
395 // Set the guid of unique handler to the same of original handler, so it can be removed
396 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
397 // So proxy can be declared as an argument
403 // Make sure the ready event is setup
405 teardown: function() {}
409 add: function( proxy, data, namespaces, live ) {
410 jQuery.extend( proxy, data || {} );
412 proxy.guid += data.selector + data.live;
413 jQuery.event.add( this, data.live, liveHandler, data );
417 remove: function( namespaces ) {
418 if ( namespaces.length ) {
419 var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
421 jQuery.each( (jQuery.data(this, "events").live || {}), function() {
422 if ( name.test(this.type) ) {
428 jQuery.event.remove( this, namespaces[0], liveHandler );
437 jQuery.Event = function( src ){
438 // Allow instantiation without the 'new' keyword
439 if ( !this.preventDefault ) {
440 return new jQuery.Event( src );
444 if ( src && src.type ) {
445 this.originalEvent = src;
446 this.type = src.type;
452 // timeStamp is buggy for some events on Firefox(#3843)
453 // So we won't rely on the native value
454 this.timeStamp = now();
457 this[ expando ] = true;
460 function returnFalse() {
463 function returnTrue() {
467 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
468 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
469 jQuery.Event.prototype = {
470 preventDefault: function() {
471 this.isDefaultPrevented = returnTrue;
473 var e = this.originalEvent;
478 // if preventDefault exists run it on the original event
479 if ( e.preventDefault ) {
482 // otherwise set the returnValue property of the original event to false (IE)
483 e.returnValue = false;
485 stopPropagation: function() {
486 this.isPropagationStopped = returnTrue;
488 var e = this.originalEvent;
492 // if stopPropagation exists run it on the original event
493 if ( e.stopPropagation ) {
496 // otherwise set the cancelBubble property of the original event to true (IE)
497 e.cancelBubble = true;
499 stopImmediatePropagation: function(){
500 this.isImmediatePropagationStopped = returnTrue;
501 this.stopPropagation();
503 isDefaultPrevented: returnFalse,
504 isPropagationStopped: returnFalse,
505 isImmediatePropagationStopped: returnFalse
507 // Checks if an event happened on an element within another element
508 // Used in jQuery.event.special.mouseenter and mouseleave handlers
509 var withinElement = function( event ) {
510 // Check if mouse(over|out) are still within the same parent element
511 var parent = event.relatedTarget;
512 // Traverse up the tree
513 while ( parent && parent != this ) {
514 // Firefox sometimes assigns relatedTarget a XUL element
515 // which we cannot access the parentNode property of
516 try { parent = parent.parentNode; }
517 // assuming we've left the element since we most likely mousedover a xul element
521 if ( parent != this ) {
522 // set the correct event type
523 event.type = event.data;
524 // handle event if we actually just moused on to a non sub-element
525 jQuery.event.handle.apply( this, arguments );
530 // In case of event delegation, we only need to rename the event.type,
531 // liveHandler will take care of the rest.
532 delegate = function( event ) {
533 event.type = event.data;
534 jQuery.event.handle.apply( this, arguments );
537 // Create mouseenter and mouseleave events
539 mouseover: "mouseenter",
540 mouseout: "mouseleave"
541 }, function( orig, fix ) {
542 jQuery.event.special[ fix ] = {
543 setup: function(data){
544 jQuery.event.add( this, orig, data && data.selector ? delegate : withinElement, fix );
546 teardown: function(data){
547 jQuery.event.remove( this, orig, data && data.selector ? delegate : withinElement );
553 jQuery.event.special.submit = {
554 setup: function( data, namespaces, fn ) {
555 if ( !jQuery.support.submitBubbles && this.nodeName.toLowerCase() !== "form" ) {
556 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
557 var elem = e.target, type = elem.type;
559 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
560 return trigger( "submit", this, arguments );
564 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
565 var elem = e.target, type = elem.type;
567 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
568 return trigger( "submit", this, arguments );
576 remove: function( namespaces, fn ) {
577 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
578 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
582 // change delegation, happens here so we have bind.
583 jQuery.event.special.change = {
585 click: function( e ) {
588 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
589 return trigger( "change", this, arguments );
592 return changeFilters.keyup.call( this, e );
594 keyup: function( e ) {
595 var elem = e.target, data, index = elem.selectedIndex + "";
597 if ( elem.nodeName.toLowerCase() === "select" ) {
598 data = jQuery.data( elem, "_change_data" );
599 jQuery.data( elem, "_change_data", index );
601 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
602 return trigger( "change", this, arguments );
606 beforeactivate: function( e ) {
609 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
610 return trigger( "change", this, arguments );
613 blur: function( e ) {
614 var elem = e.target, nodeName = elem.nodeName.toLowerCase();
616 if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
617 && jQuery.data(elem, "_change_data") !== elem.value ) {
619 return trigger( "change", this, arguments );
622 focus: function( e ) {
623 var elem = e.target, nodeName = elem.nodeName.toLowerCase();
625 if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
626 jQuery.data( elem, "_change_data", elem.value );
630 setup: function( data, namespaces, fn ) {
631 // return false if we bubble
632 if ( !jQuery.support.changeBubbles ) {
633 for ( var type in changeFilters ) {
634 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
638 // always want to listen for change for trigger
641 remove: function( namespaces, fn ) {
642 if ( !jQuery.support.changeBubbles ) {
643 for ( var type in changeFilters ) {
644 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
650 var changeFilters = jQuery.event.special.change.filters;
652 function trigger( type, elem, args ) {
654 return jQuery.event.handle.apply( elem, args );
657 // Create "bubbling" focus and blur events
661 }, function( orig, fix ){
662 var event = jQuery.event,
663 handle = event.handle;
665 function ieHandler() {
666 arguments[0].type = orig;
667 return handle.apply(this, arguments);
670 event.special[orig] = {
672 if ( this.addEventListener ) {
673 this.addEventListener( orig, handle, true );
675 event.add( this, fix, ieHandler );
678 teardown:function() {
679 if ( this.removeEventListener ) {
680 this.removeEventListener( orig, handle, true );
682 event.remove( this, fix, ieHandler );
689 // TODO: make bind(), unbind() and one() DRY!
690 bind: function( type, data, fn, thisObject ) {
691 // Handle object literals
692 if ( typeof type === "object" ) {
693 for ( var key in type ) {
694 this.bind(key, data, type[key], fn);
699 if ( jQuery.isFunction( data ) ) {
704 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
705 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
706 jQuery.event.add( this, type, fn, data );
710 one: function( type, data, fn, thisObject ) {
711 // Handle object literals
712 if ( typeof type === "object" ) {
713 for ( var key in type ) {
714 this.one(key, data, type[key], fn);
719 if ( jQuery.isFunction( data ) ) {
724 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
725 var one = jQuery.event.proxy( fn, function( event ) {
726 jQuery( this ).unbind( event, one );
727 return fn.apply( this, arguments );
729 return this.each(function() {
730 jQuery.event.add( this, type, one, data );
734 unbind: function( type, fn ) {
735 // Handle object literals
736 if ( typeof type === "object" && !type.preventDefault ) {
737 for ( var key in type ) {
738 this.unbind(key, type[key]);
743 return this.each(function() {
744 jQuery.event.remove( this, type, fn );
748 trigger: function( type, data ) {
749 return this.each(function() {
750 jQuery.event.trigger( type, data, this );
754 triggerHandler: function( type, data ) {
756 var event = jQuery.Event( type );
757 event.preventDefault();
758 event.stopPropagation();
759 jQuery.event.trigger( event, data, this[0] );
764 toggle: function( fn ) {
765 // Save reference to arguments for access in closure
766 var args = arguments, i = 1;
768 // link all the functions, so any of them can unbind this click handler
769 while( i < args.length ) {
770 jQuery.event.proxy( fn, args[ i++ ] );
773 return this.click( jQuery.event.proxy( fn, function( event ) {
774 // Figure out which function to execute
775 var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
776 jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
778 // Make sure that clicks stop
779 event.preventDefault();
781 // and execute the function
782 return args[ lastToggle ].apply( this, arguments ) || false;
786 hover: function( fnOver, fnOut ) {
787 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
790 ready: function( fn ) {
791 // Attach the listeners
794 // If the DOM is already ready
795 if ( jQuery.isReady ) {
796 // Execute the function immediately
797 fn.call( document, jQuery );
799 // Otherwise, remember the function for later
801 // Add the function to the wait list
802 jQuery.readyList.push( fn );
808 live: function( type, data, fn, thisObject ) {
809 if ( jQuery.isFunction( data ) ) {
810 if ( fn !== undefined ) {
816 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
817 data: data, selector: this.selector, live: type
822 die: function( type, fn ) {
823 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
828 function liveHandler( event ) {
829 var stop = true, elems = [], selectors = [], args = arguments,
830 related, match, fn, elem, j, i, data,
831 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
835 if ( fn.live === event.type ||
836 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
839 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
840 !data.beforeFilter[event.type](event)) ) {
841 selectors.push( fn.selector );
848 match = jQuery( event.target ).closest( selectors, event.currentTarget );
850 for ( i = 0, l = match.length; i < l; i++ ) {
853 elem = match[i].elem;
856 if ( match[i].selector === fn.selector ) {
857 // Those two events require additional checking
858 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
859 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
862 if ( !related || related !== elem ) {
863 elems.push({ elem: elem, fn: fn });
869 for ( i = 0, l = elems.length; i < l; i++ ) {
871 event.currentTarget = match.elem;
872 event.data = match.fn.data;
873 if ( match.fn.apply( match.elem, args ) === false ) {
882 function liveConvert( type, selector ) {
883 return ["live", type, selector//.replace(/[^\w\s\.]/g, function(ch){ return "\\"+ch})
885 .replace(/ /g, "|")].join(".");
891 // Handle when the DOM is ready
893 // Make sure that the DOM is not already loaded
894 if ( !jQuery.isReady ) {
895 if ( !document.body ) {
896 return setTimeout( jQuery.ready, 13 );
899 // Remember that the DOM is ready
900 jQuery.isReady = true;
902 // If there are functions bound, to execute
903 if ( jQuery.readyList ) {
904 // Execute all of them
906 while ( (fn = jQuery.readyList[ i++ ]) ) {
907 fn.call( document, jQuery );
910 // Reset the list of functions
911 jQuery.readyList = null;
914 // Trigger any bound ready events
915 jQuery( document ).triggerHandler( "ready" );
920 var readyBound = false;
922 function bindReady() {
923 if ( readyBound ) { return; }
926 // Catch cases where $(document).ready() is called after the
927 // browser event has already occurred.
928 if ( document.readyState === "complete" ) {
929 return jQuery.ready();
932 // Mozilla, Opera and webkit nightlies currently support this event
933 if ( document.addEventListener ) {
934 // Use the handy event callback
935 document.addEventListener( "DOMContentLoaded", function() {
936 document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
940 // If IE event model is used
941 } else if ( document.attachEvent ) {
942 // ensure firing before onload,
943 // maybe late but safe also for iframes
944 document.attachEvent("onreadystatechange", function() {
945 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
946 if ( document.readyState === "complete" ) {
947 document.detachEvent( "onreadystatechange", arguments.callee );
952 // If IE and not a frame
953 // continually check to see if the document is ready
954 var toplevel = false;
957 toplevel = window.frameElement == null;
960 if ( document.documentElement.doScroll && toplevel ) {
962 if ( jQuery.isReady ) {
967 // If IE is used, use the trick by Diego Perini
968 // http://javascript.nwbox.com/IEContentLoaded/
969 document.documentElement.doScroll("left");
971 setTimeout( arguments.callee, 0 );
975 // and execute any waiting functions
981 // A fallback to window.onload, that will always work
982 jQuery.event.add( window, "load", jQuery.ready );
985 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
986 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
987 "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
989 // Handle event binding
990 jQuery.fn[ name ] = function( fn ) {
991 return fn ? this.bind( name, fn ) : this.trigger( name );
995 // Prevent memory leaks in IE
996 // Window isn't included so as not to unbind existing unload events
998 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1000 jQuery( window ).bind( 'unload', function() {
1001 for ( var id in jQuery.cache ) {
1003 if ( id != 1 && jQuery.cache[ id ].handle ) {
1004 jQuery.event.remove( jQuery.cache[ id ].handle.elem );