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",
337 special = jQuery.event.special[ type ] || {};
339 if ( (!special._default || special._default.call( elem, event ) === false) &&
340 !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
343 if ( target[ type ] ) {
344 // Make sure that we don't accidentally re-trigger the onFOO events
345 old = target[ "on" + type ];
348 target[ "on" + type ] = null;
351 this.triggered = true;
355 // prevent IE from throwing an error for some elements with some event types, see #3533
359 target[ "on" + type ] = old;
362 this.triggered = false;
367 handle: function( event ) {
368 // returned undefined or false
371 event = arguments[0] = jQuery.event.fix( event || window.event );
372 event.currentTarget = this;
374 // Namespaced event handlers
375 var namespaces = event.type.split(".");
376 event.type = namespaces.shift();
378 // Cache this now, all = true means, any handler
379 all = !namespaces.length && !event.exclusive;
381 var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
383 handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
385 for ( var j in handlers ) {
386 var handler = handlers[ j ];
388 // Filter the functions by class
389 if ( all || namespace.test(handler.type) ) {
390 // Pass in a reference to the handler function itself
391 // So that we can later remove it
392 event.handler = handler;
393 event.data = handler.data;
395 var ret = handler.apply( this, arguments );
397 if ( ret !== undefined ) {
399 if ( ret === false ) {
400 event.preventDefault();
401 event.stopPropagation();
405 if ( event.isImmediatePropagationStopped() ) {
415 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(" "),
417 fix: function( event ) {
418 if ( event[ expando ] ) {
422 // store a copy of the original event object
423 // and "clone" to set read-only properties
424 var originalEvent = event;
425 event = jQuery.Event( originalEvent );
427 for ( var i = this.props.length, prop; i; ) {
428 prop = this.props[ --i ];
429 event[ prop ] = originalEvent[ prop ];
432 // Fix target property, if necessary
433 if ( !event.target ) {
434 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
437 // check if target is a textnode (safari)
438 if ( event.target.nodeType === 3 ) {
439 event.target = event.target.parentNode;
442 // Add relatedTarget, if necessary
443 if ( !event.relatedTarget && event.fromElement ) {
444 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
447 // Calculate pageX/Y if missing and clientX/Y available
448 if ( event.pageX == null && event.clientX != null ) {
449 var doc = document.documentElement, body = document.body;
450 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
451 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
454 // Add which for key events
455 if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
456 event.which = event.charCode || event.keyCode;
459 // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
460 if ( !event.metaKey && event.ctrlKey ) {
461 event.metaKey = event.ctrlKey;
464 // Add which for click: 1 === left; 2 === middle; 3 === right
465 // Note: button is not normalized, so don't use it
466 if ( !event.which && event.button !== undefined ) {
467 event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
473 // Deprecated, use jQuery.guid instead
476 // Deprecated, use jQuery.proxy instead
481 // Make sure the ready event is setup
482 setup: jQuery.bindReady,
483 teardown: jQuery.noop
487 add: function( proxy, data, namespaces, live ) {
488 jQuery.extend( proxy, data || {} );
490 proxy.guid += data.selector + data.live;
491 data.liveProxy = proxy;
493 jQuery.event.add( this, data.live, liveHandler, data );
497 remove: function( namespaces ) {
498 if ( namespaces.length ) {
499 var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
501 jQuery.each( (jQuery.data(this, "events").live || {}), function() {
502 if ( name.test(this.type) ) {
508 jQuery.event.remove( this, namespaces[0], liveHandler );
515 setup: function( data, namespaces, fn ) {
516 // We only want to do this special case on windows
517 if ( this.setInterval ) {
518 this.onbeforeunload = fn;
523 teardown: function( namespaces, fn ) {
524 if ( this.onbeforeunload === fn ) {
525 this.onbeforeunload = null;
532 jQuery.Event = function( src ) {
533 // Allow instantiation without the 'new' keyword
534 if ( !this.preventDefault ) {
535 return new jQuery.Event( src );
539 if ( src && src.type ) {
540 this.originalEvent = src;
541 this.type = src.type;
547 // timeStamp is buggy for some events on Firefox(#3843)
548 // So we won't rely on the native value
549 this.timeStamp = now();
552 this[ expando ] = true;
555 function returnFalse() {
558 function returnTrue() {
562 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
563 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
564 jQuery.Event.prototype = {
565 preventDefault: function() {
566 this.isDefaultPrevented = returnTrue;
568 var e = this.originalEvent;
573 // if preventDefault exists run it on the original event
574 if ( e.preventDefault ) {
577 // otherwise set the returnValue property of the original event to false (IE)
578 e.returnValue = false;
580 stopPropagation: function() {
581 this.isPropagationStopped = returnTrue;
583 var e = this.originalEvent;
587 // if stopPropagation exists run it on the original event
588 if ( e.stopPropagation ) {
591 // otherwise set the cancelBubble property of the original event to true (IE)
592 e.cancelBubble = true;
594 stopImmediatePropagation: function() {
595 this.isImmediatePropagationStopped = returnTrue;
596 this.stopPropagation();
598 isDefaultPrevented: returnFalse,
599 isPropagationStopped: returnFalse,
600 isImmediatePropagationStopped: returnFalse
603 // Checks if an event happened on an element within another element
604 // Used in jQuery.event.special.mouseenter and mouseleave handlers
605 var withinElement = function( event ) {
606 // Check if mouse(over|out) are still within the same parent element
607 var parent = event.relatedTarget;
609 // Traverse up the tree
610 while ( parent && parent !== this ) {
611 // Firefox sometimes assigns relatedTarget a XUL element
612 // which we cannot access the parentNode property of
614 parent = parent.parentNode;
616 // assuming we've left the element since we most likely mousedover a xul element
622 if ( parent !== this ) {
623 // set the correct event type
624 event.type = event.data;
626 // handle event if we actually just moused on to a non sub-element
627 jQuery.event.handle.apply( this, arguments );
632 // In case of event delegation, we only need to rename the event.type,
633 // liveHandler will take care of the rest.
634 delegate = function( event ) {
635 event.type = event.data;
636 jQuery.event.handle.apply( this, arguments );
639 // Create mouseenter and mouseleave events
641 mouseenter: "mouseover",
642 mouseleave: "mouseout"
643 }, function( orig, fix ) {
644 jQuery.event.special[ orig ] = {
645 setup: function( data ) {
646 jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
648 teardown: function( data ) {
649 jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
655 if ( !jQuery.support.submitBubbles ) {
657 jQuery.event.special.submit = {
658 setup: function( data, namespaces, fn ) {
659 if ( this.nodeName.toLowerCase() !== "form" ) {
660 jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
661 var elem = e.target, type = elem.type;
663 if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
664 return trigger( "submit", this, arguments );
668 jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
669 var elem = e.target, type = elem.type;
671 if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
672 return trigger( "submit", this, arguments );
681 remove: function( namespaces, fn ) {
682 jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
683 jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
689 // change delegation, happens here so we have bind.
690 if ( !jQuery.support.changeBubbles ) {
692 var formElems = /textarea|input|select/i;
694 function getVal( elem ) {
695 var type = elem.type, val = elem.value;
697 if ( type === "radio" || type === "checkbox" ) {
700 } else if ( type === "select-multiple" ) {
701 val = elem.selectedIndex > -1 ?
702 jQuery.map( elem.options, function( elem ) {
703 return elem.selected;
707 } else if ( elem.nodeName.toLowerCase() === "select" ) {
708 val = elem.selectedIndex;
714 function testChange( e ) {
715 var elem = e.target, data, val;
717 if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
721 data = jQuery.data( elem, "_change_data" );
724 // the current data will be also retrieved by beforeactivate
725 if ( e.type !== "focusout" || elem.type !== "radio" ) {
726 jQuery.data( elem, "_change_data", val );
729 if ( data === undefined || val === data ) {
733 if ( data != null || val ) {
735 return jQuery.event.trigger( e, arguments[1], elem );
739 jQuery.event.special.change = {
741 focusout: testChange,
743 click: function( e ) {
744 var elem = e.target, type = elem.type;
746 if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
747 return testChange.call( this, e );
751 // Change has to be called before submit
752 // Keydown will be called before keypress, which is used in submit-event delegation
753 keydown: function( e ) {
754 var elem = e.target, type = elem.type;
756 if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
757 (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
758 type === "select-multiple" ) {
759 return testChange.call( this, e );
763 // Beforeactivate happens also before the previous element is blurred
764 // with this event you can't trigger a change event, but you can store
765 // information/focus[in] is not needed anymore
766 beforeactivate: function( e ) {
768 jQuery.data( elem, "_change_data", getVal(elem) );
771 setup: function( data, namespaces, fn ) {
772 for ( var type in changeFilters ) {
773 jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
776 return formElems.test( this.nodeName );
778 remove: function( namespaces, fn ) {
779 for ( var type in changeFilters ) {
780 jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
783 return formElems.test( this.nodeName );
787 var changeFilters = jQuery.event.special.change.filters;
791 function trigger( type, elem, args ) {
793 return jQuery.event.handle.apply( elem, args );
796 // Create "bubbling" focus and blur events
797 if ( document.addEventListener ) {
798 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
799 jQuery.event.special[ fix ] = {
801 this.addEventListener( orig, handler, true );
803 teardown: function() {
804 this.removeEventListener( orig, handler, true );
808 function handler( e ) {
809 e = jQuery.event.fix( e );
811 return jQuery.event.handle.call( this, e );
816 jQuery.each(["bind", "one"], function( i, name ) {
817 jQuery.fn[ name ] = function( type, data, fn ) {
818 // Handle object literals
819 if ( typeof type === "object" ) {
820 for ( var key in type ) {
821 this[ name ](key, data, type[key], fn);
826 if ( jQuery.isFunction( data ) ) {
831 var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
832 jQuery( this ).unbind( event, handler );
833 return fn.apply( this, arguments );
836 if ( type === "unload" && name !== "one" ) {
837 this.one( type, data, fn );
840 for ( var i = 0, l = this.length; i < l; i++ ) {
841 jQuery.event.add( this[i], type, handler, data );
850 unbind: function( type, fn ) {
851 // Handle object literals
852 if ( typeof type === "object" && !type.preventDefault ) {
853 for ( var key in type ) {
854 this.unbind(key, type[key]);
858 for ( var i = 0, l = this.length; i < l; i++ ) {
859 jQuery.event.remove( this[i], type, fn );
865 trigger: function( type, data ) {
866 return this.each(function() {
867 jQuery.event.trigger( type, data, this );
871 triggerHandler: function( type, data ) {
873 var event = jQuery.Event( type );
874 event.preventDefault();
875 event.stopPropagation();
876 jQuery.event.trigger( event, data, this[0] );
881 toggle: function( fn ) {
882 // Save reference to arguments for access in closure
883 var args = arguments, i = 1;
885 // link all the functions, so any of them can unbind this click handler
886 while ( i < args.length ) {
887 jQuery.proxy( fn, args[ i++ ] );
890 return this.click( jQuery.proxy( fn, function( event ) {
891 // Figure out which function to execute
892 var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
893 jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
895 // Make sure that clicks stop
896 event.preventDefault();
898 // and execute the function
899 return args[ lastToggle ].apply( this, arguments ) || false;
903 hover: function( fnOver, fnOut ) {
904 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
908 jQuery.each(["live", "die"], function( i, name ) {
909 jQuery.fn[ name ] = function( types, data, fn ) {
912 if ( jQuery.isFunction( data ) ) {
917 types = (types || "").split( /\s+/ );
919 while ( (type = types[ i++ ]) != null ) {
920 type = type === "focus" ? "focusin" : // focus --> focusin
921 type === "blur" ? "focusout" : // blur --> focusout
922 type === "hover" ? types.push("mouseleave") && "mouseenter" : // hover support
925 if ( name === "live" ) {
927 jQuery( this.context ).bind( liveConvert( type, this.selector ), {
928 data: data, selector: this.selector, live: type
932 // unbind live handler
933 jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
941 function liveHandler( event ) {
942 var stop, elems = [], selectors = [], args = arguments,
943 related, match, fn, elem, j, i, l, data,
944 live = jQuery.extend({}, jQuery.data( this, "events" ).live);
946 // Make sure we avoid non-left-click bubbling in Firefox (#3861)
947 if ( event.button && event.type === "click" ) {
953 if ( fn.live === event.type ||
954 fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
957 if ( !(data.beforeFilter && data.beforeFilter[event.type] &&
958 !data.beforeFilter[event.type](event)) ) {
959 selectors.push( fn.selector );
966 match = jQuery( event.target ).closest( selectors, event.currentTarget );
968 for ( i = 0, l = match.length; i < l; i++ ) {
971 elem = match[i].elem;
974 if ( match[i].selector === fn.selector ) {
975 // Those two events require additional checking
976 if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
977 related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
980 if ( !related || related !== elem ) {
981 elems.push({ elem: elem, fn: fn });
987 for ( i = 0, l = elems.length; i < l; i++ ) {
989 event.currentTarget = match.elem;
990 event.data = match.fn.data;
991 if ( match.fn.apply( match.elem, args ) === false ) {
1000 function liveConvert( type, selector ) {
1001 return "live." + (type ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
1004 jQuery.each( ("blur focus focusin focusout 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 );
1013 if ( jQuery.attrFn ) {
1014 jQuery.attrFn[ name ] = true;
1018 // Prevent memory leaks in IE
1019 // Window isn't included so as not to unbind existing unload events
1021 // - http://isaacschlueter.com/2006/10/msie-memory-leaks/
1022 if ( window.attachEvent && !window.addEventListener ) {
1023 window.attachEvent("onunload", function() {
1024 for ( var id in jQuery.cache ) {
1025 if ( jQuery.cache[ id ].handle ) {
1026 // Try/Catch is to handle iframes being unloaded, see #4280
1028 jQuery.event.remove( jQuery.cache[ id ].handle.elem );