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 separated 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
261 var isClick = jQuery.nodeName(elem, "a") && type === "click";
263 // Trigger the native events (except for clicks on links)
264 if ( !bubbling && nativeFn && !event.isDefaultPrevented() && !isClick ) {
265 this.triggered = true;
268 // prevent IE from throwing an error for some hidden elements
271 // Handle triggering native .onfoo handlers
272 } else if ( nativeHandler && nativeHandler.apply( elem, data ) === false ) {
273 event.result = false;
276 this.triggered = false;
278 if ( !event.isPropagationStopped() ) {
279 var parent = elem.parentNode || elem.ownerDocument;
281 jQuery.event.trigger( event, data, parent, true );
286 handle: function( event ) {
287 // returned undefined or false
290 event = arguments[0] = jQuery.event.fix( event || window.event );
291 event.currentTarget = this;
293 // Namespaced event handlers
294 var namespaces = event.type.split(".");
295 event.type = namespaces.shift();
297 // Cache this now, all = true means, any handler
298 all = !namespaces.length && !event.exclusive;
300 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
302 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
304 for ( var j in handlers ) {
305 var handler = handlers[ j ];
307 // Filter the functions by class
308 if ( all || namespace.test(handler.type) ) {
309 // Pass in a reference to the handler function itself
310 // So that we can later remove it
311 event.handler = handler;
312 event.data = handler.data;
314 var ret = handler.apply( this, arguments );
316 if ( ret !== undefined ) {
318 if ( ret === false ) {
319 event.preventDefault();
320 event.stopPropagation();
324 if ( event.isImmediatePropagationStopped() ) {
334 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(" "),
336 fix: function( event ) {
337 if ( event[ expando ] ) {
341 // store a copy of the original event object
342 // and "clone" to set read-only properties
343 var originalEvent = event;
344 event = jQuery.Event( originalEvent );
346 for ( var i = this.props.length, prop; i; ) {
347 prop = this.props[ --i ];
348 event[ prop ] = originalEvent[ prop ];
351 // Fix target property, if necessary
352 if ( !event.target ) {
353 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
356 // check if target is a textnode (safari)
357 if ( event.target.nodeType === 3 ) {
358 event.target = event.target.parentNode;
361 // Add relatedTarget, if necessary
362 if ( !event.relatedTarget && event.fromElement ) {
363 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
366 // Calculate pageX/Y if missing and clientX/Y available
367 if ( event.pageX == null && event.clientX != null ) {
368 var doc = document.documentElement, body = document.body;
369 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
370 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
373 // Add which for key events
374 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
375 event.which = event.charCode || event.keyCode;
378 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
379 if ( !event.metaKey && event.ctrlKey ) {
380 event.metaKey = event.ctrlKey;
383 // Add which for click: 1 == left; 2 == middle; 3 == right
384 // Note: button is not normalized, so don't use it
385 if ( !event.which && event.button !== undefined ) {
386 event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
392 proxy: function( fn, proxy, thisObject ) {
393 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
397 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
398 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
399 // Set the guid of unique handler to the same of original handler, so it can be removed
400 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
401 // So proxy can be declared as an argument
407 // Make sure the ready event is setup
409 teardown: function() {}
413 add: function( proxy, data, namespaces, live ) {
414 jQuery.extend( proxy, data || {} );
416 proxy.guid += data.selector + data.live;
417 jQuery.event.add( this, data.live, liveHandler, data );
421 remove: function( namespaces ) {
422 if ( namespaces.length ) {
423 var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
425 jQuery.each( (jQuery.data(this, "events").live || {}), function() {
426 if ( name.test(this.type) ) {
432 jQuery.event.remove( this, namespaces[0], liveHandler );
439 setup: function( data, namespaces, fn ) {
440 // We only want to do this special case on windows
441 if ( this.setInterval ) {
442 this.onbeforeunload = fn;
447 teardown: function( namespaces, fn ) {
448 if ( this.onbeforeunload === fn ) {
449 this.onbeforeunload = null;
456 jQuery.Event = function( src ){
457 // Allow instantiation without the 'new' keyword
458 if ( !this.preventDefault ) {
459 return new jQuery.Event( src );
463 if ( src && src.type ) {
464 this.originalEvent = src;
465 this.type = src.type;
471 // timeStamp is buggy for some events on Firefox(#3843)
472 // So we won't rely on the native value
473 this.timeStamp = now();
476 this[ expando ] = true;
479 function returnFalse() {
482 function returnTrue() {
486 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
487 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
488 jQuery.Event.prototype = {
489 preventDefault: function() {
490 this.isDefaultPrevented = returnTrue;
492 var e = this.originalEvent;
497 // if preventDefault exists run it on the original event
498 if ( e.preventDefault ) {
501 // otherwise set the returnValue property of the original event to false (IE)
502 e.returnValue = false;
504 stopPropagation: function() {
505 this.isPropagationStopped = returnTrue;
507 var e = this.originalEvent;
511 // if stopPropagation exists run it on the original event
512 if ( e.stopPropagation ) {
515 // otherwise set the cancelBubble property of the original event to true (IE)
516 e.cancelBubble = true;
518 stopImmediatePropagation: function(){
519 this.isImmediatePropagationStopped = returnTrue;
520 this.stopPropagation();
522 isDefaultPrevented: returnFalse,
523 isPropagationStopped: returnFalse,
524 isImmediatePropagationStopped: returnFalse
526 // Checks if an event happened on an element within another element
527 // Used in jQuery.event.special.mouseenter and mouseleave handlers
528 var withinElement = function( event ) {
529 // Check if mouse(over|out) are still within the same parent element
530 var parent = event.relatedTarget;
531 // Traverse up the tree
532 while ( parent && parent != this ) {
533 // Firefox sometimes assigns relatedTarget a XUL element
534 // which we cannot access the parentNode property of
535 try { parent = parent.parentNode; }
536 // assuming we've left the element since we most likely mousedover a xul element
540 if ( parent != this ) {
541 // set the correct event type
542 event.type = event.data;
543 // handle event if we actually just moused on to a non sub-element
544 jQuery.event.handle.apply( this, arguments );
549 // In case of event delegation, we only need to rename the event.type,
550 // liveHandler will take care of the rest.
551 delegate = function( event ) {
552 event.type = event.data;
553 jQuery.event.handle.apply( this, arguments );
556 // Create mouseenter and mouseleave events
558 mouseenter: "mouseover",
559 mouseleave: "mouseout"
560 }, function( orig, fix ) {
561 jQuery.event.special[ orig ] = {
562 setup: function(data){
563 jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
565 teardown: function(data){
566 jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
572 if ( !jQuery.support.submitBubbles ) {
574 jQuery.event.special.submit = {
575 setup: function( data, namespaces, fn ) {
576 if ( this.nodeName.toLowerCase() !== "form" ) {
577 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
578 var elem = e.target, type = elem.type;
580 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
581 return trigger( "submit", this, arguments );
585 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
586 var elem = e.target, type = elem.type;
588 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
589 return trigger( "submit", this, arguments );
595 remove: function( namespaces, fn ) {
596 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
597 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
603 // change delegation, happens here so we have bind.
604 if ( !jQuery.support.changeBubbles ) {
606 jQuery.event.special.change = {
608 click: function( e ) {
611 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
612 return trigger( "change", this, arguments );
615 return changeFilters.keyup.call( this, e );
617 keyup: function( e ) {
618 var elem = e.target, data, index = elem.selectedIndex + "";
620 if ( elem.nodeName.toLowerCase() === "select" ) {
621 data = jQuery.data( elem, "_change_data" );
622 jQuery.data( elem, "_change_data", index );
624 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
625 return trigger( "change", this, arguments );
629 beforeactivate: function( e ) {
632 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
633 return trigger( "change", this, arguments );
636 blur: function( e ) {
637 var elem = e.target, nodeName = elem.nodeName.toLowerCase();
639 if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
640 && jQuery.data(elem, "_change_data") !== elem.value ) {
642 return trigger( "change", this, arguments );
645 focus: function( e ) {
646 var elem = e.target, nodeName = elem.nodeName.toLowerCase();
648 if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
649 jQuery.data( elem, "_change_data", elem.value );
653 setup: function( data, namespaces, fn ) {
654 for ( var type in changeFilters ) {
655 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
658 // always want to listen for change for trigger
661 remove: function( namespaces, fn ) {
662 for ( var type in changeFilters ) {
663 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
668 var changeFilters = jQuery.event.special.change.filters;
670 function trigger( type, elem, args ) {
672 return jQuery.event.handle.apply( elem, args );
675 // Create "bubbling" focus and blur events
676 if ( !jQuery.support.focusBubbles ) {
678 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ){
679 event.special[ orig ] = {
681 jQuery.event.add( this, fix, ieHandler );
683 teardown: function() {
684 jQuery.event.remove( this, fix, ieHandler );
688 function ieHandler() {
689 arguments[0].type = orig;
690 return jQuery.event.handle.apply(this, arguments);
697 // TODO: make bind(), unbind() and one() DRY!
698 bind: function( type, data, fn, thisObject ) {
699 // Handle object literals
700 if ( typeof type === "object" ) {
701 for ( var key in type ) {
702 this.bind(key, data, type[key], fn);
707 if ( jQuery.isFunction( data ) ) {
712 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
713 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
714 jQuery.event.add( this, type, fn, data );
718 one: function( type, data, fn, thisObject ) {
719 // Handle object literals
720 if ( typeof type === "object" ) {
721 for ( var key in type ) {
722 this.one(key, data, type[key], fn);
727 if ( jQuery.isFunction( data ) ) {
732 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
733 var one = jQuery.event.proxy( fn, function( event ) {
734 jQuery( this ).unbind( event, one );
735 return fn.apply( this, arguments );
737 return this.each(function() {
738 jQuery.event.add( this, type, one, data );
742 unbind: function( type, fn ) {
743 // Handle object literals
744 if ( typeof type === "object" && !type.preventDefault ) {
745 for ( var key in type ) {
746 this.unbind(key, type[key]);
751 return this.each(function() {
752 jQuery.event.remove( this, type, fn );
756 trigger: function( type, data ) {
757 return this.each(function() {
758 jQuery.event.trigger( type, data, this );
762 triggerHandler: function( type, data ) {
764 var event = jQuery.Event( type );
765 event.preventDefault();
766 event.stopPropagation();
767 jQuery.event.trigger( event, data, this[0] );
772 toggle: function( fn ) {
773 // Save reference to arguments for access in closure
774 var args = arguments, i = 1;
776 // link all the functions, so any of them can unbind this click handler
777 while( i < args.length ) {
778 jQuery.event.proxy( fn, args[ i++ ] );
781 return this.click( jQuery.event.proxy( fn, function( event ) {
782 // Figure out which function to execute
783 var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
784 jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
786 // Make sure that clicks stop
787 event.preventDefault();
789 // and execute the function
790 return args[ lastToggle ].apply( this, arguments ) || false;
794 hover: function( fnOver, fnOut ) {
795 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
798 ready: function( fn ) {
799 // Attach the listeners
802 // If the DOM is already ready
803 if ( jQuery.isReady ) {
804 // Execute the function immediately
805 fn.call( document, jQuery );
807 // Otherwise, remember the function for later
809 // Add the function to the wait list
810 jQuery.readyList.push( fn );
816 live: function( type, data, fn, thisObject ) {
817 if ( jQuery.isFunction( data ) ) {
818 if ( fn !== undefined ) {
824 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
825 data: data, selector: this.selector, live: type
830 die: function( type, fn ) {
831 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
836 function liveHandler( event ) {
837 var stop = true, elems = [], selectors = [], args = arguments,
838 related, match, fn, elem, j, i, data,
839 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
843 if ( fn.live === event.type ||
844 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
847 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
848 !data.beforeFilter[event.type](event)) ) {
849 selectors.push( fn.selector );
856 match = jQuery( event.target ).closest( selectors, event.currentTarget );
858 for ( i = 0, l = match.length; i < l; i++ ) {
861 elem = match[i].elem;
864 if ( match[i].selector === fn.selector ) {
865 // Those two events require additional checking
866 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
867 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
870 if ( !related || related !== elem ) {
871 elems.push({ elem: elem, fn: fn });
877 for ( i = 0, l = elems.length; i < l; i++ ) {
879 event.currentTarget = match.elem;
880 event.data = match.fn.data;
881 if ( match.fn.apply( match.elem, args ) === false ) {
890 function liveConvert( type, selector ) {
891 return ["live", type, selector//.replace(/[^\w\s\.]/g, function(ch){ return "\\"+ch})
893 .replace(/ /g, "|")].join(".");
899 // Handle when the DOM is ready
901 // Make sure that the DOM is not already loaded
902 if ( !jQuery.isReady ) {
903 if ( !document.body ) {
904 return setTimeout( jQuery.ready, 13 );
907 // Remember that the DOM is ready
908 jQuery.isReady = true;
910 // If there are functions bound, to execute
911 if ( jQuery.readyList ) {
912 // Execute all of them
914 while ( (fn = jQuery.readyList[ i++ ]) ) {
915 fn.call( document, jQuery );
918 // Reset the list of functions
919 jQuery.readyList = null;
922 // Trigger any bound ready events
923 jQuery( document ).triggerHandler( "ready" );
928 var readyBound = false;
930 function bindReady() {
931 if ( readyBound ) { return; }
934 // Catch cases where $(document).ready() is called after the
935 // browser event has already occurred.
936 if ( document.readyState === "complete" ) {
937 return jQuery.ready();
940 // Mozilla, Opera and webkit nightlies currently support this event
941 if ( document.addEventListener ) {
942 // Use the handy event callback
943 document.addEventListener( "DOMContentLoaded", function() {
944 document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
948 // If IE event model is used
949 } else if ( document.attachEvent ) {
950 // ensure firing before onload,
951 // maybe late but safe also for iframes
952 document.attachEvent("onreadystatechange", function() {
953 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
954 if ( document.readyState === "complete" ) {
955 document.detachEvent( "onreadystatechange", arguments.callee );
960 // If IE and not a frame
961 // continually check to see if the document is ready
962 var toplevel = false;
965 toplevel = window.frameElement == null;
968 if ( document.documentElement.doScroll && toplevel ) {
970 if ( jQuery.isReady ) {
975 // If IE is used, use the trick by Diego Perini
976 // http://javascript.nwbox.com/IEContentLoaded/
977 document.documentElement.doScroll("left");
979 setTimeout( arguments.callee, 0 );
983 // and execute any waiting functions
989 // A fallback to window.onload, that will always work
990 jQuery.event.add( window, "load", jQuery.ready );
993 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
994 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
995 "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
997 // Handle event binding
998 jQuery.fn[ name ] = function( fn ) {
999 return fn ? this.bind( name, fn ) : this.trigger( name );
1003 // Prevent memory leaks in IE
1004 // Window isn't included so as not to unbind existing unload events
1006 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1008 jQuery( window ).bind( 'unload', function() {
1009 for ( var id in jQuery.cache ) {
1011 if ( id != 1 && jQuery.cache[ id ].handle ) {
1012 jQuery.event.remove( jQuery.cache[ id ].handle.elem );