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("\\.(?:.*\\.)?") + "(\\.|$)")
195 var special = this.special[ type ] || {};
197 if ( events[ type ] ) {
198 // remove the given handler for the given type
200 fn = events[ type ][ handler.guid ];
201 delete events[ type ][ handler.guid ];
203 // remove all handlers for the given type
205 for ( var handle in events[ type ] ) {
206 // Handle the removal of namespaced events
207 if ( all || namespace.test( events[ type ][ handle ].type ) ) {
208 delete events[ type ][ handle ];
213 if ( special.remove ) {
214 special.remove.call( elem, namespaces, fn);
217 // remove generic event handler if no more handlers exist
218 for ( ret in events[ type ] ) {
223 if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
224 if ( elem.removeEventListener ) {
225 elem.removeEventListener( type, elemData.handle, false );
226 } else if ( elem.detachEvent ) {
227 elem.detachEvent( "on" + type, elemData.handle );
232 delete events[ type ];
238 // Remove the expando if it's no longer used
239 for ( ret in events ) {
244 var handle = elemData.handle;
249 delete elemData.events;
250 delete elemData.handle;
252 if ( jQuery.isEmptyObject( elemData ) ) {
253 jQuery.removeData( elem );
259 // bubbling is internal
260 trigger: function( event, data, elem /*, bubbling */ ) {
261 // Event object or event type
262 var type = event.type || event,
263 bubbling = arguments[3];
266 event = typeof event === "object" ?
267 // jQuery.Event object
268 event[expando] ? event :
270 jQuery.extend( jQuery.Event(type), event ) :
271 // Just the event type (string)
274 if ( type.indexOf("!") >= 0 ) {
275 event.type = type = type.slice(0, -1);
276 event.exclusive = true;
279 // Handle a global trigger
281 // Don't bubble custom events when global (to avoid too much overhead)
282 event.stopPropagation();
284 // Only trigger if we've ever bound an event for it
285 if ( this.global[ type ] ) {
286 jQuery.each( jQuery.cache, function() {
287 if ( this.events && this.events[type] ) {
288 jQuery.event.trigger( event, data, this.handle.elem );
294 // Handle triggering a single element
296 // don't do events on text and comment nodes
297 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
301 // Clean up in case it is reused
302 event.result = undefined;
305 // Clone the incoming data, if any
306 data = jQuery.makeArray( data );
307 data.unshift( event );
310 event.currentTarget = elem;
312 // Trigger the event, it is assumed that "handle" is a function
313 var handle = jQuery.data( elem, "handle" );
315 handle.apply( elem, data );
318 var parent = elem.parentNode || elem.ownerDocument;
320 // Trigger an inline bound script
322 if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
323 if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
324 event.result = false;
328 // prevent IE from throwing an error for some elements with some event types, see #3533
331 if ( !event.isPropagationStopped() && parent ) {
332 jQuery.event.trigger( event, data, parent, true );
334 } else if ( !event.isDefaultPrevented() ) {
335 var target = event.target, old,
336 isClick = jQuery.nodeName(target, "a") && type === "click";
338 if ( !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
340 if ( target[ type ] ) {
341 // Make sure that we don't accidentally re-trigger the onFOO events
342 old = target[ "on" + type ];
345 target[ "on" + type ] = null;
348 this.triggered = true;
352 // prevent IE from throwing an error for some elements with some event types, see #3533
356 target[ "on" + type ] = old;
359 this.triggered = false;
364 handle: function( event ) {
365 // returned undefined or false
368 event = arguments[0] = jQuery.event.fix( event || window.event );
369 event.currentTarget = this;
371 // Namespaced event handlers
372 var namespaces = event.type.split(".");
373 event.type = namespaces.shift();
375 // Cache this now, all = true means, any handler
376 all = !namespaces.length && !event.exclusive;
378 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
380 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
382 for ( var j in handlers ) {
383 var handler = handlers[ j ];
385 // Filter the functions by class
386 if ( all || namespace.test(handler.type) ) {
387 // Pass in a reference to the handler function itself
388 // So that we can later remove it
389 event.handler = handler;
390 event.data = handler.data;
392 var ret = handler.apply( this, arguments );
394 if ( ret !== undefined ) {
396 if ( ret === false ) {
397 event.preventDefault();
398 event.stopPropagation();
402 if ( event.isImmediatePropagationStopped() ) {
412 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(" "),
414 fix: function( event ) {
415 if ( event[ expando ] ) {
419 // store a copy of the original event object
420 // and "clone" to set read-only properties
421 var originalEvent = event;
422 event = jQuery.Event( originalEvent );
424 for ( var i = this.props.length, prop; i; ) {
425 prop = this.props[ --i ];
426 event[ prop ] = originalEvent[ prop ];
429 // Fix target property, if necessary
430 if ( !event.target ) {
431 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
434 // check if target is a textnode (safari)
435 if ( event.target.nodeType === 3 ) {
436 event.target = event.target.parentNode;
439 // Add relatedTarget, if necessary
440 if ( !event.relatedTarget && event.fromElement ) {
441 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
444 // Calculate pageX/Y if missing and clientX/Y available
445 if ( event.pageX == null && event.clientX != null ) {
446 var doc = document.documentElement, body = document.body;
447 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
448 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
451 // Add which for key events
452 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
453 event.which = event.charCode || event.keyCode;
456 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
457 if ( !event.metaKey && event.ctrlKey ) {
458 event.metaKey = event.ctrlKey;
461 // Add which for click: 1 === left; 2 === middle; 3 === right
462 // Note: button is not normalized, so don't use it
463 if ( !event.which && event.button !== undefined ) {
464 event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
470 // Deprecated, use jQuery.guid instead
473 // Deprecated, use jQuery.proxy instead
478 // Make sure the ready event is setup
479 setup: jQuery.bindReady,
480 teardown: jQuery.noop
484 add: function( proxy, data, namespaces, live ) {
485 jQuery.extend( proxy, data || {} );
487 proxy.guid += data.selector + data.live;
488 data.liveProxy = proxy;
490 jQuery.event.add( this, data.live, liveHandler, data );
494 remove: function( namespaces ) {
495 if ( namespaces.length ) {
496 var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
498 jQuery.each( (jQuery.data(this, "events").live || {}), function() {
499 if ( name.test(this.type) ) {
505 jQuery.event.remove( this, namespaces[0], liveHandler );
512 setup: function( data, namespaces, fn ) {
513 // We only want to do this special case on windows
514 if ( this.setInterval ) {
515 this.onbeforeunload = fn;
520 teardown: function( namespaces, fn ) {
521 if ( this.onbeforeunload === fn ) {
522 this.onbeforeunload = null;
529 jQuery.Event = function( src ) {
530 // Allow instantiation without the 'new' keyword
531 if ( !this.preventDefault ) {
532 return new jQuery.Event( src );
536 if ( src && src.type ) {
537 this.originalEvent = src;
538 this.type = src.type;
544 // timeStamp is buggy for some events on Firefox(#3843)
545 // So we won't rely on the native value
546 this.timeStamp = now();
549 this[ expando ] = true;
552 function returnFalse() {
555 function returnTrue() {
559 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
560 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
561 jQuery.Event.prototype = {
562 preventDefault: function() {
563 this.isDefaultPrevented = returnTrue;
565 var e = this.originalEvent;
570 // if preventDefault exists run it on the original event
571 if ( e.preventDefault ) {
574 // otherwise set the returnValue property of the original event to false (IE)
575 e.returnValue = false;
577 stopPropagation: function() {
578 this.isPropagationStopped = returnTrue;
580 var e = this.originalEvent;
584 // if stopPropagation exists run it on the original event
585 if ( e.stopPropagation ) {
588 // otherwise set the cancelBubble property of the original event to true (IE)
589 e.cancelBubble = true;
591 stopImmediatePropagation: function() {
592 this.isImmediatePropagationStopped = returnTrue;
593 this.stopPropagation();
595 isDefaultPrevented: returnFalse,
596 isPropagationStopped: returnFalse,
597 isImmediatePropagationStopped: returnFalse
600 // Checks if an event happened on an element within another element
601 // Used in jQuery.event.special.mouseenter and mouseleave handlers
602 var withinElement = function( event ) {
603 // Check if mouse(over|out) are still within the same parent element
604 var parent = event.relatedTarget;
606 // Traverse up the tree
607 while ( parent && parent !== this ) {
608 // Firefox sometimes assigns relatedTarget a XUL element
609 // which we cannot access the parentNode property of
611 parent = parent.parentNode;
613 // assuming we've left the element since we most likely mousedover a xul element
619 if ( parent !== this ) {
620 // set the correct event type
621 event.type = event.data;
623 // handle event if we actually just moused on to a non sub-element
624 jQuery.event.handle.apply( this, arguments );
629 // In case of event delegation, we only need to rename the event.type,
630 // liveHandler will take care of the rest.
631 delegate = function( event ) {
632 event.type = event.data;
633 jQuery.event.handle.apply( this, arguments );
636 // Create mouseenter and mouseleave events
638 mouseenter: "mouseover",
639 mouseleave: "mouseout"
640 }, function( orig, fix ) {
641 jQuery.event.special[ orig ] = {
642 setup: function( data ) {
643 jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
645 teardown: function( data ) {
646 jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
652 if ( !jQuery.support.submitBubbles ) {
654 jQuery.event.special.submit = {
655 setup: function( data, namespaces, fn ) {
656 if ( this.nodeName.toLowerCase() !== "form" ) {
657 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
658 var elem = e.target, type = elem.type;
660 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
661 return trigger( "submit", this, arguments );
665 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
666 var elem = e.target, type = elem.type;
668 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
669 return trigger( "submit", this, arguments );
678 remove: function( namespaces, fn ) {
679 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
680 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
686 // change delegation, happens here so we have bind.
687 if ( !jQuery.support.changeBubbles ) {
689 var formElems = /textarea|input|select/i;
691 function getVal( elem ) {
692 var type = elem.type, val = elem.value;
694 if ( type === "radio" || type === "checkbox" ) {
697 } else if ( type === "select-multiple" ) {
698 val = elem.selectedIndex > -1 ?
699 jQuery.map( elem.options, function( elem ) {
700 return elem.selected;
704 } else if ( elem.nodeName.toLowerCase() === "select" ) {
705 val = elem.selectedIndex;
711 function testChange( e ) {
712 var elem = e.target, data, val;
714 if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
718 data = jQuery.data( elem, "_change_data" );
721 // the current data will be also retrieved by beforeactivate
722 if ( e.type !== "focusout" || elem.type !== "radio" ) {
723 jQuery.data( elem, "_change_data", val );
726 if ( data === undefined || val === data ) {
730 if ( data != null || val ) {
732 return jQuery.event.trigger( e, arguments[1], elem );
736 jQuery.event.special.change = {
738 focusout: testChange,
740 click: function( e ) {
741 var elem = e.target, type = elem.type;
743 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
744 return testChange.call( this, e );
748 // Change has to be called before submit
749 // Keydown will be called before keypress, which is used in submit-event delegation
750 keydown: function( e ) {
751 var elem = e.target, type = elem.type;
753 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
754 (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
755 type === "select-multiple" ) {
756 return testChange.call( this, e );
760 // Beforeactivate happens also before the previous element is blurred
761 // with this event you can't trigger a change event, but you can store
762 // information/focus[in] is not needed anymore
763 beforeactivate: function( e ) {
765 jQuery.data( elem, "_change_data", getVal(elem) );
768 setup: function( data, namespaces, fn ) {
769 for ( var type in changeFilters ) {
770 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
773 return formElems.test( this.nodeName );
775 remove: function( namespaces, fn ) {
776 for ( var type in changeFilters ) {
777 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
780 return formElems.test( this.nodeName );
784 var changeFilters = jQuery.event.special.change.filters;
788 function trigger( type, elem, args ) {
790 return jQuery.event.handle.apply( elem, args );
793 // Create "bubbling" focus and blur events
794 if ( document.addEventListener ) {
795 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
796 jQuery.event.special[ fix ] = {
798 this.addEventListener( orig, handler, true );
800 teardown: function() {
801 this.removeEventListener( orig, handler, true );
805 function handler( e ) {
806 e = jQuery.event.fix( e );
808 return jQuery.event.handle.call( this, e );
813 jQuery.each(["bind", "one"], function( i, name ) {
814 jQuery.fn[ name ] = function( type, data, fn ) {
815 // Handle object literals
816 if ( typeof type === "object" ) {
817 for ( var key in type ) {
818 this[ name ](key, data, type[key], fn);
823 if ( jQuery.isFunction( data ) ) {
828 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
829 jQuery( this ).unbind( event, handler );
830 return fn.apply( this, arguments );
833 if ( type === "unload" && name !== "one" ) {
834 this.one( type, data, fn );
837 for ( var i = 0, l = this.length; i < l; i++ ) {
838 jQuery.event.add( this[i], type, handler, data );
847 unbind: function( type, fn ) {
848 // Handle object literals
849 if ( typeof type === "object" && !type.preventDefault ) {
850 for ( var key in type ) {
851 this.unbind(key, type[key]);
855 for ( var i = 0, l = this.length; i < l; i++ ) {
856 jQuery.event.remove( this[i], type, fn );
862 trigger: function( type, data ) {
863 return this.each(function() {
864 jQuery.event.trigger( type, data, this );
868 triggerHandler: function( type, data ) {
870 var event = jQuery.Event( type );
871 event.preventDefault();
872 event.stopPropagation();
873 jQuery.event.trigger( event, data, this[0] );
878 toggle: function( fn ) {
879 // Save reference to arguments for access in closure
880 var args = arguments, i = 1;
882 // link all the functions, so any of them can unbind this click handler
883 while ( i < args.length ) {
884 jQuery.proxy( fn, args[ i++ ] );
887 return this.click( jQuery.proxy( fn, function( event ) {
888 // Figure out which function to execute
889 var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
890 jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
892 // Make sure that clicks stop
893 event.preventDefault();
895 // and execute the function
896 return args[ lastToggle ].apply( this, arguments ) || false;
900 hover: function( fnOver, fnOut ) {
901 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
905 jQuery.each(["live", "die"], function( i, name ) {
906 jQuery.fn[ name ] = function( types, data, fn ) {
909 if ( jQuery.isFunction( data ) ) {
914 types = (types || "").split( /\s+/ );
916 while ( (type = types[ i++ ]) != null ) {
917 type = type === "focus" ? "focusin" : // focus --> focusin
918 type === "blur" ? "focusout" : // blur --> focusout
919 type === "hover" ? types.push("mouseleave") && "mouseenter" : // hover support
922 if ( name === "live" ) {
924 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
925 data: data, selector: this.selector, live: type
929 // unbind live handler
930 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
938 function liveHandler( event ) {
939 var stop, elems = [], selectors = [], args = arguments,
940 related, match, fn, elem, j, i, l, data,
941 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
943 // Make sure we avoid non-left-click bubbling in Firefox (#3861)
944 if ( event.button && event.type === "click" ) {
950 if ( fn.live === event.type ||
951 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
954 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
955 !data.beforeFilter[event.type](event)) ) {
956 selectors.push( fn.selector );
963 match = jQuery( event.target ).closest( selectors, event.currentTarget );
965 for ( i = 0, l = match.length; i < l; i++ ) {
968 elem = match[i].elem;
971 if ( match[i].selector === fn.selector ) {
972 // Those two events require additional checking
973 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
974 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
977 if ( !related || related !== elem ) {
978 elems.push({ elem: elem, fn: fn });
984 for ( i = 0, l = elems.length; i < l; i++ ) {
986 event.currentTarget = match.elem;
987 event.data = match.fn.data;
988 if ( match.fn.apply( match.elem, args ) === false ) {
997 function liveConvert( type, selector ) {
998 return "live." + (type ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
1001 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
1002 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
1003 "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
1005 // Handle event binding
1006 jQuery.fn[ name ] = function( fn ) {
1007 return fn ? this.bind( name, fn ) : this.trigger( name );
1010 if ( jQuery.attrFn ) {
1011 jQuery.attrFn[ name ] = true;
1015 // Prevent memory leaks in IE
1016 // Window isn't included so as not to unbind existing unload events
1018 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1019 if ( window.attachEvent && !window.addEventListener ) {
1020 window.attachEvent("onunload", function() {
1021 for ( var id in jQuery.cache ) {
1022 if ( jQuery.cache[ id ].handle ) {
1023 // Try/Catch is to handle iframes being unloaded, see #4280
1025 jQuery.event.remove( jQuery.cache[ id ].handle.elem );