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() ) {
332 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(" "),
334 fix: function( event ) {
335 if ( event[ expando ] ) {
339 // store a copy of the original event object
340 // and "clone" to set read-only properties
341 var originalEvent = event;
342 event = jQuery.Event( originalEvent );
344 for ( var i = this.props.length, prop; i; ) {
345 prop = this.props[ --i ];
346 event[ prop ] = originalEvent[ prop ];
349 // Fix target property, if necessary
350 if ( !event.target ) {
351 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
354 // check if target is a textnode (safari)
355 if ( event.target.nodeType === 3 ) {
356 event.target = event.target.parentNode;
359 // Add relatedTarget, if necessary
360 if ( !event.relatedTarget && event.fromElement ) {
361 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
364 // Calculate pageX/Y if missing and clientX/Y available
365 if ( event.pageX == null && event.clientX != null ) {
366 var doc = document.documentElement, body = document.body;
367 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
368 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
371 // Add which for key events
372 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
373 event.which = event.charCode || event.keyCode;
376 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
377 if ( !event.metaKey && event.ctrlKey ) {
378 event.metaKey = event.ctrlKey;
381 // Add which for click: 1 == left; 2 == middle; 3 == right
382 // Note: button is not normalized, so don't use it
383 if ( !event.which && event.button !== undefined ) {
384 event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
390 proxy: function( fn, proxy, thisObject ) {
391 if ( proxy !== undefined && !jQuery.isFunction( proxy ) ) {
395 // FIXME: Should proxy be redefined to be applied with thisObject if defined?
396 proxy = proxy || function() { return fn.apply( thisObject !== undefined ? thisObject : this, arguments ); };
397 // Set the guid of unique handler to the same of original handler, so it can be removed
398 proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
399 // So proxy can be declared as an argument
405 // Make sure the ready event is setup
407 teardown: function() {}
411 add: function( proxy, data, namespaces, live ) {
412 jQuery.extend( proxy, data || {} );
414 proxy.guid += data.selector + data.live;
415 jQuery.event.add( this, data.live, liveHandler, data );
419 remove: function( namespaces ) {
420 if ( namespaces.length ) {
421 var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
423 jQuery.each( (jQuery.data(this, "events").live || {}), function() {
424 if ( name.test(this.type) ) {
430 jQuery.event.remove( this, namespaces[0], liveHandler );
439 jQuery.Event = function( src ){
440 // Allow instantiation without the 'new' keyword
441 if ( !this.preventDefault ) {
442 return new jQuery.Event( src );
446 if ( src && src.type ) {
447 this.originalEvent = src;
448 this.type = src.type;
454 // timeStamp is buggy for some events on Firefox(#3843)
455 // So we won't rely on the native value
456 this.timeStamp = now();
459 this[ expando ] = true;
462 function returnFalse() {
465 function returnTrue() {
469 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
470 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
471 jQuery.Event.prototype = {
472 preventDefault: function() {
473 this.isDefaultPrevented = returnTrue;
475 var e = this.originalEvent;
480 // if preventDefault exists run it on the original event
481 if ( e.preventDefault ) {
484 // otherwise set the returnValue property of the original event to false (IE)
485 e.returnValue = false;
487 stopPropagation: function() {
488 this.isPropagationStopped = returnTrue;
490 var e = this.originalEvent;
494 // if stopPropagation exists run it on the original event
495 if ( e.stopPropagation ) {
498 // otherwise set the cancelBubble property of the original event to true (IE)
499 e.cancelBubble = true;
501 stopImmediatePropagation: function(){
502 this.isImmediatePropagationStopped = returnTrue;
503 this.stopPropagation();
505 isDefaultPrevented: returnFalse,
506 isPropagationStopped: returnFalse,
507 isImmediatePropagationStopped: returnFalse
509 // Checks if an event happened on an element within another element
510 // Used in jQuery.event.special.mouseenter and mouseleave handlers
511 var withinElement = function( event ) {
512 // Check if mouse(over|out) are still within the same parent element
513 var parent = event.relatedTarget;
514 // Traverse up the tree
515 while ( parent && parent != this ) {
516 // Firefox sometimes assigns relatedTarget a XUL element
517 // which we cannot access the parentNode property of
518 try { parent = parent.parentNode; }
519 // assuming we've left the element since we most likely mousedover a xul element
523 if ( parent != this ) {
524 // set the correct event type
525 event.type = event.data;
526 // handle event if we actually just moused on to a non sub-element
527 jQuery.event.handle.apply( this, arguments );
532 // In case of event delegation, we only need to rename the event.type,
533 // liveHandler will take care of the rest.
534 delegate = function( event ) {
535 event.type = event.data;
536 jQuery.event.handle.apply( this, arguments );
539 // Create mouseenter and mouseleave events
541 mouseenter: "mouseover",
542 mouseleave: "mouseout"
543 }, function( orig, fix ) {
544 jQuery.event.special[ orig ] = {
545 setup: function(data){
546 jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
548 teardown: function(data){
549 jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
555 jQuery.event.special.submit = {
556 setup: function( data, namespaces, fn ) {
557 if ( !jQuery.support.submitBubbles && this.nodeName.toLowerCase() !== "form" ) {
558 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
559 var elem = e.target, type = elem.type;
561 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
562 return trigger( "submit", this, arguments );
566 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
567 var elem = e.target, type = elem.type;
569 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
570 return trigger( "submit", this, arguments );
578 remove: function( namespaces, fn ) {
579 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
580 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
584 // change delegation, happens here so we have bind.
585 jQuery.event.special.change = {
587 click: function( e ) {
590 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
591 return trigger( "change", this, arguments );
594 return changeFilters.keyup.call( this, e );
596 keyup: function( e ) {
597 var elem = e.target, data, index = elem.selectedIndex + "";
599 if ( elem.nodeName.toLowerCase() === "select" ) {
600 data = jQuery.data( elem, "_change_data" );
601 jQuery.data( elem, "_change_data", index );
603 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
604 return trigger( "change", this, arguments );
608 beforeactivate: function( e ) {
611 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
612 return trigger( "change", this, arguments );
615 blur: function( e ) {
616 var elem = e.target, nodeName = elem.nodeName.toLowerCase();
618 if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
619 && jQuery.data(elem, "_change_data") !== elem.value ) {
621 return trigger( "change", this, arguments );
624 focus: function( e ) {
625 var elem = e.target, nodeName = elem.nodeName.toLowerCase();
627 if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
628 jQuery.data( elem, "_change_data", elem.value );
632 setup: function( data, namespaces, fn ) {
633 // return false if we bubble
634 if ( !jQuery.support.changeBubbles ) {
635 for ( var type in changeFilters ) {
636 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
640 // always want to listen for change for trigger
643 remove: function( namespaces, fn ) {
644 if ( !jQuery.support.changeBubbles ) {
645 for ( var type in changeFilters ) {
646 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
652 var changeFilters = jQuery.event.special.change.filters;
654 function trigger( type, elem, args ) {
656 return jQuery.event.handle.apply( elem, args );
659 // Create "bubbling" focus and blur events
663 }, function( orig, fix ){
664 var event = jQuery.event,
665 handle = event.handle;
667 function ieHandler() {
668 arguments[0].type = orig;
669 return handle.apply(this, arguments);
672 event.special[orig] = {
674 if ( this.addEventListener ) {
675 this.addEventListener( orig, handle, true );
677 event.add( this, fix, ieHandler );
680 teardown:function() {
681 if ( this.removeEventListener ) {
682 this.removeEventListener( orig, handle, true );
684 event.remove( this, fix, ieHandler );
691 // TODO: make bind(), unbind() and one() DRY!
692 bind: function( type, data, fn, thisObject ) {
693 // Handle object literals
694 if ( typeof type === "object" ) {
695 for ( var key in type ) {
696 this.bind(key, data, type[key], fn);
701 if ( jQuery.isFunction( data ) ) {
706 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
707 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
708 jQuery.event.add( this, type, fn, data );
712 one: function( type, data, fn, thisObject ) {
713 // Handle object literals
714 if ( typeof type === "object" ) {
715 for ( var key in type ) {
716 this.one(key, data, type[key], fn);
721 if ( jQuery.isFunction( data ) ) {
726 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
727 var one = jQuery.event.proxy( fn, function( event ) {
728 jQuery( this ).unbind( event, one );
729 return fn.apply( this, arguments );
731 return this.each(function() {
732 jQuery.event.add( this, type, one, data );
736 unbind: function( type, fn ) {
737 // Handle object literals
738 if ( typeof type === "object" && !type.preventDefault ) {
739 for ( var key in type ) {
740 this.unbind(key, type[key]);
745 return this.each(function() {
746 jQuery.event.remove( this, type, fn );
750 trigger: function( type, data ) {
751 return this.each(function() {
752 jQuery.event.trigger( type, data, this );
756 triggerHandler: function( type, data ) {
758 var event = jQuery.Event( type );
759 event.preventDefault();
760 event.stopPropagation();
761 jQuery.event.trigger( event, data, this[0] );
766 toggle: function( fn ) {
767 // Save reference to arguments for access in closure
768 var args = arguments, i = 1;
770 // link all the functions, so any of them can unbind this click handler
771 while( i < args.length ) {
772 jQuery.event.proxy( fn, args[ i++ ] );
775 return this.click( jQuery.event.proxy( fn, function( event ) {
776 // Figure out which function to execute
777 var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
778 jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
780 // Make sure that clicks stop
781 event.preventDefault();
783 // and execute the function
784 return args[ lastToggle ].apply( this, arguments ) || false;
788 hover: function( fnOver, fnOut ) {
789 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
792 ready: function( fn ) {
793 // Attach the listeners
796 // If the DOM is already ready
797 if ( jQuery.isReady ) {
798 // Execute the function immediately
799 fn.call( document, jQuery );
801 // Otherwise, remember the function for later
803 // Add the function to the wait list
804 jQuery.readyList.push( fn );
810 live: function( type, data, fn, thisObject ) {
811 if ( jQuery.isFunction( data ) ) {
812 if ( fn !== undefined ) {
818 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
819 data: data, selector: this.selector, live: type
824 die: function( type, fn ) {
825 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
830 function liveHandler( event ) {
831 var stop = true, elems = [], selectors = [], args = arguments,
832 related, match, fn, elem, j, i, data,
833 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
837 if ( fn.live === event.type ||
838 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
841 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
842 !data.beforeFilter[event.type](event)) ) {
843 selectors.push( fn.selector );
850 match = jQuery( event.target ).closest( selectors, event.currentTarget );
852 for ( i = 0, l = match.length; i < l; i++ ) {
855 elem = match[i].elem;
858 if ( match[i].selector === fn.selector ) {
859 // Those two events require additional checking
860 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
861 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
864 if ( !related || related !== elem ) {
865 elems.push({ elem: elem, fn: fn });
871 for ( i = 0, l = elems.length; i < l; i++ ) {
873 event.currentTarget = match.elem;
874 event.data = match.fn.data;
875 if ( match.fn.apply( match.elem, args ) === false ) {
884 function liveConvert( type, selector ) {
885 return ["live", type, selector//.replace(/[^\w\s\.]/g, function(ch){ return "\\"+ch})
887 .replace(/ /g, "|")].join(".");
893 // Handle when the DOM is ready
895 // Make sure that the DOM is not already loaded
896 if ( !jQuery.isReady ) {
897 if ( !document.body ) {
898 return setTimeout( jQuery.ready, 13 );
901 // Remember that the DOM is ready
902 jQuery.isReady = true;
904 // If there are functions bound, to execute
905 if ( jQuery.readyList ) {
906 // Execute all of them
908 while ( (fn = jQuery.readyList[ i++ ]) ) {
909 fn.call( document, jQuery );
912 // Reset the list of functions
913 jQuery.readyList = null;
916 // Trigger any bound ready events
917 jQuery( document ).triggerHandler( "ready" );
922 var readyBound = false;
924 function bindReady() {
925 if ( readyBound ) { return; }
928 // Catch cases where $(document).ready() is called after the
929 // browser event has already occurred.
930 if ( document.readyState === "complete" ) {
931 return jQuery.ready();
934 // Mozilla, Opera and webkit nightlies currently support this event
935 if ( document.addEventListener ) {
936 // Use the handy event callback
937 document.addEventListener( "DOMContentLoaded", function() {
938 document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
942 // If IE event model is used
943 } else if ( document.attachEvent ) {
944 // ensure firing before onload,
945 // maybe late but safe also for iframes
946 document.attachEvent("onreadystatechange", function() {
947 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
948 if ( document.readyState === "complete" ) {
949 document.detachEvent( "onreadystatechange", arguments.callee );
954 // If IE and not a frame
955 // continually check to see if the document is ready
956 var toplevel = false;
959 toplevel = window.frameElement == null;
962 if ( document.documentElement.doScroll && toplevel ) {
964 if ( jQuery.isReady ) {
969 // If IE is used, use the trick by Diego Perini
970 // http://javascript.nwbox.com/IEContentLoaded/
971 document.documentElement.doScroll("left");
973 setTimeout( arguments.callee, 0 );
977 // and execute any waiting functions
983 // A fallback to window.onload, that will always work
984 jQuery.event.add( window, "load", jQuery.ready );
987 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
988 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
989 "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
991 // Handle event binding
992 jQuery.fn[ name ] = function( fn ) {
993 return fn ? this.bind( name, fn ) : this.trigger( name );
997 // Prevent memory leaks in IE
998 // Window isn't included so as not to unbind existing unload events
1000 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1002 jQuery( window ).bind( 'unload', function() {
1003 for ( var id in jQuery.cache ) {
1005 if ( id != 1 && jQuery.cache[ id ].handle ) {
1006 jQuery.event.remove( jQuery.cache[ id ].handle.elem );