1 var fcleanup = function( nm ) {
2 return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
8 * A number of helper functions used for managing events.
9 * Many of the ideas behind this code originated from
10 * Dean Edwards' addEvent library.
14 // Bind an event to an element
15 // Original by Dean Edwards
16 add: function( elem, types, handler, data ) {
17 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
21 // For whatever reason, IE has trouble passing the window object
22 // around, causing it to be cloned in the process
23 if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
27 // Make sure that the function being executed has a unique ID
28 if ( !handler.guid ) {
29 handler.guid = jQuery.guid++;
32 // if data is passed, bind to handler
33 if ( data !== undefined ) {
34 // Create temporary function pointer to original handler
37 // Create unique handler function, wrapped around original handler
38 handler = jQuery.proxy( fn );
40 // Store data in unique handler
44 // Init the element's event structure
45 var elemData = jQuery.data( elem );
47 // If no elemData is found then we must be trying to bind to one of the
48 // banned noData elements
53 var events = elemData.events || (elemData.events = {}),
54 handle = elemData.handle, eventHandle;
57 eventHandle = function() {
58 // Handle the second event of a trigger and when
59 // an event is called after a page has unloaded
60 return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
61 jQuery.event.handle.apply( eventHandle.elem, arguments ) :
65 handle = elemData.handle = eventHandle;
68 // Add elem as a property of the handle function
69 // This is to prevent a memory leak with non-native
73 // Handle multiple events separated by a space
74 // jQuery(...).bind("mouseover mouseout", fn);
75 types = types.split(" ");
77 var type, i = 0, namespaces;
79 while ( (type = types[ i++ ]) ) {
81 handler = jQuery.proxy( handler );
83 if ( data !== undefined ) {
88 // Namespaced event handlers
89 if ( type.indexOf(".") > -1 ) {
90 namespaces = type.split(".");
91 type = namespaces.shift();
92 handler.type = namespaces.slice(0).sort().join(".");
99 // Get the current list of functions bound to this event
100 var handlers = events[ type ],
101 special = this.special[ type ] || {};
103 // Init the event handler queue
105 handlers = events[ type ] = {};
107 // Check for a special event handler
108 // Only use addEventListener/attachEvent if the special
109 // events handler returns false
110 if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) {
111 // Bind the global event handler to the element
112 if ( elem.addEventListener ) {
113 elem.addEventListener( type, handle, false );
115 } else if ( elem.attachEvent ) {
116 elem.attachEvent( "on" + type, handle );
122 var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers );
123 if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) {
124 modifiedHandler.guid = modifiedHandler.guid || handler.guid;
125 modifiedHandler.data = modifiedHandler.data || handler.data;
126 modifiedHandler.type = modifiedHandler.type || handler.type;
127 handler = modifiedHandler;
131 // Add the function to the element's handler list
132 handlers[ handler.guid ] = handler;
134 // Keep track of which events have been used, for global triggering
135 this.global[ type ] = true;
138 // Nullify elem to prevent memory leaks in IE
144 // Detach an event or set of events from an element
145 remove: function( elem, types, handler ) {
146 // don't do events on text and comment nodes
147 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
151 var elemData = jQuery.data( elem );
157 var events = elemData.events, ret, type, fn;
160 // Unbind all events for the element
161 if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
162 for ( type in events ) {
163 this.remove( elem, type + (types || "") );
167 // types is actually an event object here
169 handler = types.handler;
173 // Handle multiple events separated by a space
174 // jQuery(...).unbind("mouseover mouseout", fn);
175 types = types.split(" ");
177 var i = 0, all, namespaces, namespace;
179 while ( (type = types[ i++ ]) ) {
180 all = type.indexOf(".") < 0;
184 // Namespaced event handlers
185 namespaces = type.split(".");
186 type = namespaces.shift();
188 namespace = new RegExp("(^|\\.)" +
189 jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
192 var special = this.special[ type ] || {};
194 if ( events[ type ] ) {
195 // remove the given handler for the given type
197 fn = events[ type ][ handler.guid ];
198 delete events[ type ][ handler.guid ];
200 // remove all handlers for the given type
202 for ( var handle in events[ type ] ) {
203 // Handle the removal of namespaced events
204 if ( all || namespace.test( events[ type ][ handle ].type ) ) {
205 delete events[ type ][ handle ];
210 if ( special.remove ) {
211 special.remove.call( elem, namespaces || [], fn);
214 // remove generic event handler if no more handlers exist
215 for ( ret in events[ type ] ) {
220 if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
221 if ( elem.removeEventListener ) {
222 elem.removeEventListener( type, elemData.handle, false );
223 } else if ( elem.detachEvent ) {
224 elem.detachEvent( "on" + type, elemData.handle );
229 delete events[ type ];
235 // Remove the expando if it's no longer used
236 for ( ret in events ) {
241 var handle = elemData.handle;
246 delete elemData.events;
247 delete elemData.handle;
249 if ( jQuery.isEmptyObject( elemData ) ) {
250 jQuery.removeData( elem );
256 // bubbling is internal
257 trigger: function( event, data, elem /*, bubbling */ ) {
258 // Event object or event type
259 var type = event.type || event,
260 bubbling = arguments[3];
263 event = typeof event === "object" ?
264 // jQuery.Event object
265 event[expando] ? event :
267 jQuery.extend( jQuery.Event(type), event ) :
268 // Just the event type (string)
271 if ( type.indexOf("!") >= 0 ) {
272 event.type = type = type.slice(0, -1);
273 event.exclusive = true;
276 // Handle a global trigger
278 // Don't bubble custom events when global (to avoid too much overhead)
279 event.stopPropagation();
281 // Only trigger if we've ever bound an event for it
282 if ( this.global[ type ] ) {
283 jQuery.each( jQuery.cache, function() {
284 if ( this.events && this.events[type] ) {
285 jQuery.event.trigger( event, data, this.handle.elem );
291 // Handle triggering a single element
293 // don't do events on text and comment nodes
294 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
298 // Clean up in case it is reused
299 event.result = undefined;
302 // Clone the incoming data, if any
303 data = jQuery.makeArray( data );
304 data.unshift( event );
307 event.currentTarget = elem;
309 // Trigger the event, it is assumed that "handle" is a function
310 var handle = jQuery.data( elem, "handle" );
312 handle.apply( elem, data );
315 var parent = elem.parentNode || elem.ownerDocument;
317 // Trigger an inline bound script
319 if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
320 if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
321 event.result = false;
325 // prevent IE from throwing an error for some elements with some event types, see #3533
328 if ( !event.isPropagationStopped() && parent ) {
329 jQuery.event.trigger( event, data, parent, true );
331 } else if ( !event.isDefaultPrevented() ) {
332 var target = event.target, old,
333 isClick = jQuery.nodeName(target, "a") && type === "click";
335 if ( !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
337 if ( target[ type ] ) {
338 // Make sure that we don't accidentally re-trigger the onFOO events
339 old = target[ "on" + type ];
342 target[ "on" + type ] = null;
345 this.triggered = true;
349 // prevent IE from throwing an error for some elements with some event types, see #3533
353 target[ "on" + type ] = old;
356 this.triggered = false;
361 handle: function( event ) {
362 // returned undefined or false
365 event = arguments[0] = jQuery.event.fix( event || window.event );
366 event.currentTarget = this;
368 // Namespaced event handlers
369 var namespaces = event.type.split(".");
370 event.type = namespaces.shift();
372 // Cache this now, all = true means, any handler
373 all = !namespaces.length && !event.exclusive;
375 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
377 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
379 for ( var j in handlers ) {
380 var handler = handlers[ j ];
382 // Filter the functions by class
383 if ( all || namespace.test(handler.type) ) {
384 // Pass in a reference to the handler function itself
385 // So that we can later remove it
386 event.handler = handler;
387 event.data = handler.data;
389 var ret = handler.apply( this, arguments );
391 if ( ret !== undefined ) {
393 if ( ret === false ) {
394 event.preventDefault();
395 event.stopPropagation();
399 if ( event.isImmediatePropagationStopped() ) {
409 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(" "),
411 fix: function( event ) {
412 if ( event[ expando ] ) {
416 // store a copy of the original event object
417 // and "clone" to set read-only properties
418 var originalEvent = event;
419 event = jQuery.Event( originalEvent );
421 for ( var i = this.props.length, prop; i; ) {
422 prop = this.props[ --i ];
423 event[ prop ] = originalEvent[ prop ];
426 // Fix target property, if necessary
427 if ( !event.target ) {
428 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
431 // check if target is a textnode (safari)
432 if ( event.target.nodeType === 3 ) {
433 event.target = event.target.parentNode;
436 // Add relatedTarget, if necessary
437 if ( !event.relatedTarget && event.fromElement ) {
438 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
441 // Calculate pageX/Y if missing and clientX/Y available
442 if ( event.pageX == null && event.clientX != null ) {
443 var doc = document.documentElement, body = document.body;
444 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
445 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
448 // Add which for key events
449 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
450 event.which = event.charCode || event.keyCode;
453 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
454 if ( !event.metaKey && event.ctrlKey ) {
455 event.metaKey = event.ctrlKey;
458 // Add which for click: 1 === left; 2 === middle; 3 === right
459 // Note: button is not normalized, so don't use it
460 if ( !event.which && event.button !== undefined ) {
461 event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
467 // Deprecated, use jQuery.guid instead
470 // Deprecated, use jQuery.proxy instead
475 // Make sure the ready event is setup
476 setup: jQuery.bindReady,
477 teardown: jQuery.noop
481 add: function( proxy, data, namespaces, live ) {
482 jQuery.extend( proxy, data || {} );
484 proxy.guid += data.selector + data.live;
485 data.liveProxy = proxy;
487 jQuery.event.add( this, data.live, liveHandler, data );
491 remove: function( namespaces ) {
492 if ( namespaces.length ) {
493 var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
495 jQuery.each( (jQuery.data(this, "events").live || {}), function() {
496 if ( name.test(this.type) ) {
502 jQuery.event.remove( this, namespaces[0], liveHandler );
509 setup: function( data, namespaces, fn ) {
510 // We only want to do this special case on windows
511 if ( this.setInterval ) {
512 this.onbeforeunload = fn;
517 teardown: function( namespaces, fn ) {
518 if ( this.onbeforeunload === fn ) {
519 this.onbeforeunload = null;
526 jQuery.Event = function( src ) {
527 // Allow instantiation without the 'new' keyword
528 if ( !this.preventDefault ) {
529 return new jQuery.Event( src );
533 if ( src && src.type ) {
534 this.originalEvent = src;
535 this.type = src.type;
541 // timeStamp is buggy for some events on Firefox(#3843)
542 // So we won't rely on the native value
543 this.timeStamp = now();
546 this[ expando ] = true;
549 function returnFalse() {
552 function returnTrue() {
556 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
557 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
558 jQuery.Event.prototype = {
559 preventDefault: function() {
560 this.isDefaultPrevented = returnTrue;
562 var e = this.originalEvent;
567 // if preventDefault exists run it on the original event
568 if ( e.preventDefault ) {
571 // otherwise set the returnValue property of the original event to false (IE)
572 e.returnValue = false;
574 stopPropagation: function() {
575 this.isPropagationStopped = returnTrue;
577 var e = this.originalEvent;
581 // if stopPropagation exists run it on the original event
582 if ( e.stopPropagation ) {
585 // otherwise set the cancelBubble property of the original event to true (IE)
586 e.cancelBubble = true;
588 stopImmediatePropagation: function() {
589 this.isImmediatePropagationStopped = returnTrue;
590 this.stopPropagation();
592 isDefaultPrevented: returnFalse,
593 isPropagationStopped: returnFalse,
594 isImmediatePropagationStopped: returnFalse
597 // Checks if an event happened on an element within another element
598 // Used in jQuery.event.special.mouseenter and mouseleave handlers
599 var withinElement = function( event ) {
600 // Check if mouse(over|out) are still within the same parent element
601 var parent = event.relatedTarget;
603 // Traverse up the tree
604 while ( parent && parent !== this ) {
605 // Firefox sometimes assigns relatedTarget a XUL element
606 // which we cannot access the parentNode property of
608 parent = parent.parentNode;
610 // assuming we've left the element since we most likely mousedover a xul element
616 if ( parent !== this ) {
617 // set the correct event type
618 event.type = event.data;
620 // handle event if we actually just moused on to a non sub-element
621 jQuery.event.handle.apply( this, arguments );
626 // In case of event delegation, we only need to rename the event.type,
627 // liveHandler will take care of the rest.
628 delegate = function( event ) {
629 event.type = event.data;
630 jQuery.event.handle.apply( this, arguments );
633 // Create mouseenter and mouseleave events
635 mouseenter: "mouseover",
636 mouseleave: "mouseout"
637 }, function( orig, fix ) {
638 jQuery.event.special[ orig ] = {
639 setup: function( data ) {
640 jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
642 teardown: function( data ) {
643 jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
649 if ( !jQuery.support.submitBubbles ) {
651 jQuery.event.special.submit = {
652 setup: function( data, namespaces, fn ) {
653 if ( this.nodeName.toLowerCase() !== "form" ) {
654 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
655 var elem = e.target, type = elem.type;
657 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
658 return trigger( "submit", this, arguments );
662 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
663 var elem = e.target, type = elem.type;
665 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
666 return trigger( "submit", this, arguments );
675 remove: function( namespaces, fn ) {
676 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
677 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
683 // change delegation, happens here so we have bind.
684 if ( !jQuery.support.changeBubbles ) {
686 var formElems = /textarea|input|select/i;
688 function getVal( elem ) {
689 var type = elem.type, val = elem.value;
691 if ( type === "radio" || type === "checkbox" ) {
694 } else if ( type === "select-multiple" ) {
695 val = elem.selectedIndex > -1 ?
696 jQuery.map( elem.options, function( elem ) {
697 return elem.selected;
701 } else if ( elem.nodeName.toLowerCase() === "select" ) {
702 val = elem.selectedIndex;
708 function testChange( e ) {
709 var elem = e.target, data, val;
711 if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
715 data = jQuery.data( elem, "_change_data" );
718 // the current data will be also retrieved by beforeactivate
719 if ( e.type !== "focusout" || elem.type !== "radio" ) {
720 jQuery.data( elem, "_change_data", val );
723 if ( data === undefined || val === data ) {
727 if ( data != null || val ) {
729 return jQuery.event.trigger( e, arguments[1], elem );
733 jQuery.event.special.change = {
735 focusout: testChange,
737 click: function( e ) {
738 var elem = e.target, type = elem.type;
740 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
741 return testChange.call( this, e );
745 // Change has to be called before submit
746 // Keydown will be called before keypress, which is used in submit-event delegation
747 keydown: function( e ) {
748 var elem = e.target, type = elem.type;
750 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
751 (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
752 type === "select-multiple" ) {
753 return testChange.call( this, e );
757 // Beforeactivate happens also before the previous element is blurred
758 // with this event you can't trigger a change event, but you can store
759 // information/focus[in] is not needed anymore
760 beforeactivate: function( e ) {
762 jQuery.data( elem, "_change_data", getVal(elem) );
765 setup: function( data, namespaces, fn ) {
766 for ( var type in changeFilters ) {
767 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
770 return formElems.test( this.nodeName );
772 remove: function( namespaces, fn ) {
773 for ( var type in changeFilters ) {
774 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
777 return formElems.test( this.nodeName );
781 var changeFilters = jQuery.event.special.change.filters;
785 function trigger( type, elem, args ) {
787 return jQuery.event.handle.apply( elem, args );
790 // Create "bubbling" focus and blur events
791 if ( document.addEventListener ) {
792 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
793 jQuery.event.special[ fix ] = {
795 this.addEventListener( orig, handler, true );
797 teardown: function() {
798 this.removeEventListener( orig, handler, true );
802 function handler( e ) {
803 e = jQuery.event.fix( e );
805 return jQuery.event.handle.call( this, e );
810 jQuery.each(["bind", "one"], function( i, name ) {
811 jQuery.fn[ name ] = function( type, data, fn ) {
812 // Handle object literals
813 if ( typeof type === "object" ) {
814 for ( var key in type ) {
815 this[ name ](key, data, type[key], fn);
820 if ( jQuery.isFunction( data ) ) {
825 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
826 jQuery( this ).unbind( event, handler );
827 return fn.apply( this, arguments );
830 if ( type === "unload" && name !== "one" ) {
831 this.one( type, data, fn );
834 for ( var i = 0, l = this.length; i < l; i++ ) {
835 jQuery.event.add( this[i], type, handler, data );
844 unbind: function( type, fn ) {
845 // Handle object literals
846 if ( typeof type === "object" && !type.preventDefault ) {
847 for ( var key in type ) {
848 this.unbind(key, type[key]);
852 for ( var i = 0, l = this.length; i < l; i++ ) {
853 jQuery.event.remove( this[i], type, fn );
859 trigger: function( type, data ) {
860 return this.each(function() {
861 jQuery.event.trigger( type, data, this );
865 triggerHandler: function( type, data ) {
867 var event = jQuery.Event( type );
868 event.preventDefault();
869 event.stopPropagation();
870 jQuery.event.trigger( event, data, this[0] );
875 toggle: function( fn ) {
876 // Save reference to arguments for access in closure
877 var args = arguments, i = 1;
879 // link all the functions, so any of them can unbind this click handler
880 while ( i < args.length ) {
881 jQuery.proxy( fn, args[ i++ ] );
884 return this.click( jQuery.proxy( fn, function( event ) {
885 // Figure out which function to execute
886 var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
887 jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
889 // Make sure that clicks stop
890 event.preventDefault();
892 // and execute the function
893 return args[ lastToggle ].apply( this, arguments ) || false;
897 hover: function( fnOver, fnOut ) {
898 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
902 jQuery.each(["live", "die"], function( i, name ) {
903 jQuery.fn[ name ] = function( types, data, fn ) {
906 if ( jQuery.isFunction( data ) ) {
911 types = (types || "").split( /\s+/ );
913 while ( (type = types[ i++ ]) != null ) {
914 type = type === "focus" ? "focusin" : // focus --> focusin
915 type === "blur" ? "focusout" : // blur --> focusout
916 type === "hover" ? types.push("mouseleave") && "mouseenter" : // hover support
919 if ( name === "live" ) {
921 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
922 data: data, selector: this.selector, live: type
926 // unbind live handler
927 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
935 function liveHandler( event ) {
936 var stop, elems = [], selectors = [], args = arguments,
937 related, match, fn, elem, j, i, l, data,
938 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
940 // Make sure we avoid non-left-click bubbling in Firefox (#3861)
941 if ( event.button && event.type === "click" ) {
947 if ( fn.live === event.type ||
948 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
951 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
952 !data.beforeFilter[event.type](event)) ) {
953 selectors.push( fn.selector );
960 match = jQuery( event.target ).closest( selectors, event.currentTarget );
962 for ( i = 0, l = match.length; i < l; i++ ) {
965 elem = match[i].elem;
968 if ( match[i].selector === fn.selector ) {
969 // Those two events require additional checking
970 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
971 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
974 if ( !related || related !== elem ) {
975 elems.push({ elem: elem, fn: fn });
981 for ( i = 0, l = elems.length; i < l; i++ ) {
983 event.currentTarget = match.elem;
984 event.data = match.fn.data;
985 if ( match.fn.apply( match.elem, args ) === false ) {
994 function liveConvert( type, selector ) {
995 return "live." + (type ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
998 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
999 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1000 "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1002 // Handle event binding
1003 jQuery.fn[ name ] = function( fn ) {
1004 return fn ? this.bind( name, fn ) : this.trigger( name );
1007 if ( jQuery.attrFn ) {
1008 jQuery.attrFn[ name ] = true;
1012 // Prevent memory leaks in IE
1013 // Window isn't included so as not to unbind existing unload events
1015 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1016 if ( window.attachEvent && !window.addEventListener ) {
1017 window.attachEvent("onunload", function() {
1018 for ( var id in jQuery.cache ) {
1019 if ( jQuery.cache[ id ].handle ) {
1020 // Try/Catch is to handle iframes being unloaded, see #4280
1022 jQuery.event.remove( jQuery.cache[ id ].handle.elem );