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 jQuery.event.special.submit = {
573 setup: function( data, namespaces, fn ) {
574 if ( !jQuery.support.submitBubbles && this.nodeName.toLowerCase() !== "form" ) {
575 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
576 var elem = e.target, type = elem.type;
578 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
579 return trigger( "submit", this, arguments );
583 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
584 var elem = e.target, type = elem.type;
586 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
587 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 : "") );
601 // change delegation, happens here so we have bind.
602 jQuery.event.special.change = {
604 click: function( e ) {
607 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "checkbox" ) {
608 return trigger( "change", this, arguments );
611 return changeFilters.keyup.call( this, e );
613 keyup: function( e ) {
614 var elem = e.target, data, index = elem.selectedIndex + "";
616 if ( elem.nodeName.toLowerCase() === "select" ) {
617 data = jQuery.data( elem, "_change_data" );
618 jQuery.data( elem, "_change_data", index );
620 if ( (elem.type === "select-multiple" || data != null) && data !== index ) {
621 return trigger( "change", this, arguments );
625 beforeactivate: function( e ) {
628 if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" && !elem.checked ) {
629 return trigger( "change", this, arguments );
632 blur: function( e ) {
633 var elem = e.target, nodeName = elem.nodeName.toLowerCase();
635 if ( (nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password")))
636 && jQuery.data(elem, "_change_data") !== elem.value ) {
638 return trigger( "change", this, arguments );
641 focus: function( e ) {
642 var elem = e.target, nodeName = elem.nodeName.toLowerCase();
644 if ( nodeName === "textarea" || (nodeName === "input" && (elem.type === "text" || elem.type === "password" ) ) ) {
645 jQuery.data( elem, "_change_data", elem.value );
649 setup: function( data, namespaces, fn ) {
650 // return false if we bubble
651 if ( !jQuery.support.changeBubbles ) {
652 for ( var type in changeFilters ) {
653 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
657 // always want to listen for change for trigger
660 remove: function( namespaces, fn ) {
661 if ( !jQuery.support.changeBubbles ) {
662 for ( var type in changeFilters ) {
663 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
669 var changeFilters = jQuery.event.special.change.filters;
671 function trigger( type, elem, args ) {
673 return jQuery.event.handle.apply( elem, args );
676 // Create "bubbling" focus and blur events
680 }, function( orig, fix ){
681 var event = jQuery.event,
682 handle = event.handle;
684 function ieHandler() {
685 arguments[0].type = orig;
686 return handle.apply(this, arguments);
689 event.special[orig] = {
691 if ( this.addEventListener ) {
692 this.addEventListener( orig, handle, true );
694 event.add( this, fix, ieHandler );
697 teardown:function() {
698 if ( this.removeEventListener ) {
699 this.removeEventListener( orig, handle, true );
701 event.remove( this, fix, ieHandler );
708 // TODO: make bind(), unbind() and one() DRY!
709 bind: function( type, data, fn, thisObject ) {
710 // Handle object literals
711 if ( typeof type === "object" ) {
712 for ( var key in type ) {
713 this.bind(key, data, type[key], fn);
718 if ( jQuery.isFunction( data ) ) {
723 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
724 return type === "unload" ? this.one(type, data, fn, thisObject) : this.each(function() {
725 jQuery.event.add( this, type, fn, data );
729 one: function( type, data, fn, thisObject ) {
730 // Handle object literals
731 if ( typeof type === "object" ) {
732 for ( var key in type ) {
733 this.one(key, data, type[key], fn);
738 if ( jQuery.isFunction( data ) ) {
743 fn = thisObject === undefined ? fn : jQuery.event.proxy( fn, thisObject );
744 var one = jQuery.event.proxy( fn, function( event ) {
745 jQuery( this ).unbind( event, one );
746 return fn.apply( this, arguments );
748 return this.each(function() {
749 jQuery.event.add( this, type, one, data );
753 unbind: function( type, fn ) {
754 // Handle object literals
755 if ( typeof type === "object" && !type.preventDefault ) {
756 for ( var key in type ) {
757 this.unbind(key, type[key]);
762 return this.each(function() {
763 jQuery.event.remove( this, type, fn );
767 trigger: function( type, data ) {
768 return this.each(function() {
769 jQuery.event.trigger( type, data, this );
773 triggerHandler: function( type, data ) {
775 var event = jQuery.Event( type );
776 event.preventDefault();
777 event.stopPropagation();
778 jQuery.event.trigger( event, data, this[0] );
783 toggle: function( fn ) {
784 // Save reference to arguments for access in closure
785 var args = arguments, i = 1;
787 // link all the functions, so any of them can unbind this click handler
788 while( i < args.length ) {
789 jQuery.event.proxy( fn, args[ i++ ] );
792 return this.click( jQuery.event.proxy( fn, function( event ) {
793 // Figure out which function to execute
794 var lastToggle = ( jQuery.data( this, 'lastToggle' + fn.guid ) || 0 ) % i;
795 jQuery.data( this, 'lastToggle' + fn.guid, lastToggle + 1 );
797 // Make sure that clicks stop
798 event.preventDefault();
800 // and execute the function
801 return args[ lastToggle ].apply( this, arguments ) || false;
805 hover: function( fnOver, fnOut ) {
806 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
809 ready: function( fn ) {
810 // Attach the listeners
813 // If the DOM is already ready
814 if ( jQuery.isReady ) {
815 // Execute the function immediately
816 fn.call( document, jQuery );
818 // Otherwise, remember the function for later
820 // Add the function to the wait list
821 jQuery.readyList.push( fn );
827 live: function( type, data, fn, thisObject ) {
828 if ( jQuery.isFunction( data ) ) {
829 if ( fn !== undefined ) {
835 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
836 data: data, selector: this.selector, live: type
841 die: function( type, fn ) {
842 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
847 function liveHandler( event ) {
848 var stop = true, elems = [], selectors = [], args = arguments,
849 related, match, fn, elem, j, i, data,
850 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
854 if ( fn.live === event.type ||
855 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
858 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
859 !data.beforeFilter[event.type](event)) ) {
860 selectors.push( fn.selector );
867 match = jQuery( event.target ).closest( selectors, event.currentTarget );
869 for ( i = 0, l = match.length; i < l; i++ ) {
872 elem = match[i].elem;
875 if ( match[i].selector === fn.selector ) {
876 // Those two events require additional checking
877 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
878 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
881 if ( !related || related !== elem ) {
882 elems.push({ elem: elem, fn: fn });
888 for ( i = 0, l = elems.length; i < l; i++ ) {
890 event.currentTarget = match.elem;
891 event.data = match.fn.data;
892 if ( match.fn.apply( match.elem, args ) === false ) {
901 function liveConvert( type, selector ) {
902 return ["live", type, selector//.replace(/[^\w\s\.]/g, function(ch){ return "\\"+ch})
904 .replace(/ /g, "|")].join(".");
910 // Handle when the DOM is ready
912 // Make sure that the DOM is not already loaded
913 if ( !jQuery.isReady ) {
914 if ( !document.body ) {
915 return setTimeout( jQuery.ready, 13 );
918 // Remember that the DOM is ready
919 jQuery.isReady = true;
921 // If there are functions bound, to execute
922 if ( jQuery.readyList ) {
923 // Execute all of them
925 while ( (fn = jQuery.readyList[ i++ ]) ) {
926 fn.call( document, jQuery );
929 // Reset the list of functions
930 jQuery.readyList = null;
933 // Trigger any bound ready events
934 jQuery( document ).triggerHandler( "ready" );
939 var readyBound = false;
941 function bindReady() {
942 if ( readyBound ) { return; }
945 // Catch cases where $(document).ready() is called after the
946 // browser event has already occurred.
947 if ( document.readyState === "complete" ) {
948 return jQuery.ready();
951 // Mozilla, Opera and webkit nightlies currently support this event
952 if ( document.addEventListener ) {
953 // Use the handy event callback
954 document.addEventListener( "DOMContentLoaded", function() {
955 document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
959 // If IE event model is used
960 } else if ( document.attachEvent ) {
961 // ensure firing before onload,
962 // maybe late but safe also for iframes
963 document.attachEvent("onreadystatechange", function() {
964 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
965 if ( document.readyState === "complete" ) {
966 document.detachEvent( "onreadystatechange", arguments.callee );
971 // If IE and not a frame
972 // continually check to see if the document is ready
973 var toplevel = false;
976 toplevel = window.frameElement == null;
979 if ( document.documentElement.doScroll && toplevel ) {
981 if ( jQuery.isReady ) {
986 // If IE is used, use the trick by Diego Perini
987 // http://javascript.nwbox.com/IEContentLoaded/
988 document.documentElement.doScroll("left");
990 setTimeout( arguments.callee, 0 );
994 // and execute any waiting functions
1000 // A fallback to window.onload, that will always work
1001 jQuery.event.add( window, "load", jQuery.ready );
1004 jQuery.each( ("blur focus load resize scroll unload click dblclick " +
1005 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1006 "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1008 // Handle event binding
1009 jQuery.fn[ name ] = function( fn ) {
1010 return fn ? this.bind( name, fn ) : this.trigger( name );
1014 // Prevent memory leaks in IE
1015 // Window isn't included so as not to unbind existing unload events
1017 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1019 jQuery( window ).bind( 'unload', function() {
1020 for ( var id in jQuery.cache ) {
1022 if ( id != 1 && jQuery.cache[ id ].handle ) {
1023 jQuery.event.remove( jQuery.cache[ id ].handle.elem );