Merge branch 'master' of github.com:jquery/jquery into jquery-master
[jquery.git] / src / event.js
index b61b11e..2ddf288 100644 (file)
@@ -8,7 +8,8 @@ var rnamespaces = /\.(.*)$/,
        fcleanup = function( nm ) {
                return nm.replace(rescape, "\\$&");
        },
-       focusCounts = { focusin: 0, focusout: 0 };
+       focusCounts = { focusin: 0, focusout: 0 },
+       eventKey = "events";
 
 /*
  * A number of helper functions used for managing events.
@@ -50,7 +51,7 @@ jQuery.event = {
                }
 
                // Init the element's event structure
-               var elemData = jQuery.data( elem );
+               var elemData = jQuery._data( elem );
 
                // If no elemData is found then we must be trying to bind to one of the
                // banned noData elements
@@ -58,12 +59,9 @@ jQuery.event = {
                        return;
                }
 
-               // Use a key less likely to result in collisions for plain JS objects.
-               // Fixes bug #7150.
-               var eventKey = elem.nodeType ? "events" : "__events__",
-                       events = elemData[ eventKey ],
+               var events = elemData[ eventKey ],
                        eventHandle = elemData.handle;
-                       
+
                if ( typeof events === "function" ) {
                        // On plain objects events is a fn that holds the the data
                        // which prevents this data from being JSON serialized
@@ -143,9 +141,9 @@ jQuery.event = {
                                        }
                                }
                        }
-                       
-                       if ( special.add ) { 
-                               special.add.call( elem, handleObj ); 
+
+                       if ( special.add ) {
+                               special.add.call( elem, handleObj );
 
                                if ( !handleObj.handler.guid ) {
                                        handleObj.handler.guid = handler.guid;
@@ -177,14 +175,13 @@ jQuery.event = {
                }
 
                var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
-                       eventKey = elem.nodeType ? "events" : "__events__",
-                       elemData = jQuery.data( elem ),
+                       elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
                        events = elemData && elemData[ eventKey ];
 
                if ( !elemData || !events ) {
                        return;
                }
-               
+
                if ( typeof events === "function" ) {
                        elemData = events;
                        events = events.events;
@@ -222,7 +219,7 @@ jQuery.event = {
                                namespaces = type.split(".");
                                type = namespaces.shift();
 
-                               namespace = new RegExp("(^|\\.)" + 
+                               namespace = new RegExp("(^|\\.)" +
                                        jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
                        }
 
@@ -290,10 +287,10 @@ jQuery.event = {
                        delete elemData.handle;
 
                        if ( typeof elemData === "function" ) {
-                               jQuery.removeData( elem, eventKey );
+                               jQuery.removeData( elem, eventKey, true );
 
                        } else if ( jQuery.isEmptyObject( elemData ) ) {
-                               jQuery.removeData( elem );
+                               jQuery.removeData( elem, undefined, true );
                        }
                }
        },
@@ -325,9 +322,16 @@ jQuery.event = {
 
                                // Only trigger if we've ever bound an event for it
                                if ( jQuery.event.global[ type ] ) {
+                                       // XXX This code smells terrible. event.js should not be directly
+                                       // inspecting the data cache
                                        jQuery.each( jQuery.cache, function() {
-                                               if ( this.events && this.events[type] ) {
-                                                       jQuery.event.trigger( event, data, this.handle.elem );
+                                               // internalKey variable is just used to make it easier to find
+                                               // and potentially change this stuff later; currently it just
+                                               // points to jQuery.expando
+                                               var internalKey = jQuery.expando,
+                                                       internalCache = this[ internalKey ];
+                                               if ( internalCache && internalCache.events && internalCache.events[type] ) {
+                                                       jQuery.event.trigger( event, data, internalCache.handle.elem );
                                                }
                                        });
                                }
@@ -353,8 +357,8 @@ jQuery.event = {
 
                // Trigger the event, it is assumed that "handle" is a function
                var handle = elem.nodeType ?
-                       jQuery.data( elem, "handle" ) :
-                       (jQuery.data( elem, "__events__" ) || {}).handle;
+                       jQuery._data( elem, "handle" ) :
+                       (jQuery._data( elem, eventKey ) || {}).handle;
 
                if ( handle ) {
                        handle.apply( elem, data );
@@ -384,7 +388,7 @@ jQuery.event = {
                                isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
                                special = jQuery.event.special[ targetType ] || {};
 
-                       if ( (!special._default || special._default.call( elem, event ) === false) && 
+                       if ( (!special._default || special._default.call( elem, event ) === false) &&
                                !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
 
                                try {
@@ -432,7 +436,7 @@ jQuery.event = {
 
                event.namespace = event.namespace || namespace_sort.join(".");
 
-               events = jQuery.data(this, this.nodeType ? "events" : "__events__");
+               events = jQuery._data(this, eventKey);
 
                if ( typeof events === "function" ) {
                        events = events.events;
@@ -454,7 +458,7 @@ jQuery.event = {
                                        event.handler = handleObj.handler;
                                        event.data = handleObj.data;
                                        event.handleObj = handleObj;
-       
+
                                        var ret = handleObj.handler.apply( this, args );
 
                                        if ( ret !== undefined ) {
@@ -553,7 +557,7 @@ jQuery.event = {
                        add: function( handleObj ) {
                                jQuery.event.add( this,
                                        liveConvert( handleObj.origType, handleObj.selector ),
-                                       jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); 
+                                       jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
                        },
 
                        remove: function( handleObj ) {
@@ -583,7 +587,7 @@ jQuery.removeEvent = document.removeEventListener ?
                if ( elem.removeEventListener ) {
                        elem.removeEventListener( type, handle, false );
                }
-       } : 
+       } :
        function( elem, type, handle ) {
                if ( elem.detachEvent ) {
                        elem.detachEvent( "on" + type, handle );
@@ -600,10 +604,12 @@ jQuery.Event = function( src ) {
        if ( src && src.type ) {
                this.originalEvent = src;
                this.type = src.type;
+
                // Events bubbling up the document may have been marked as prevented
                // by a handler lower down the tree; reflect the correct value.
                this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || 
                        src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
+
        // Event type
        } else {
                this.type = src;
@@ -634,7 +640,7 @@ jQuery.Event.prototype = {
                if ( !e ) {
                        return;
                }
-               
+
                // if preventDefault exists run it on the original event
                if ( e.preventDefault ) {
                        e.preventDefault();
@@ -730,7 +736,7 @@ if ( !jQuery.support.submitBubbles ) {
                                                return trigger( "submit", this, arguments );
                                        }
                                });
-        
+
                                jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
                                        var elem = e.target,
                                                type = elem.type;
@@ -785,14 +791,14 @@ if ( !jQuery.support.changeBubbles ) {
                        return;
                }
 
-               data = jQuery.data( elem, "_change_data" );
+               data = jQuery._data( elem, "_change_data" );
                val = getVal(elem);
 
                // the current data will be also retrieved by beforeactivate
                if ( e.type !== "focusout" || elem.type !== "radio" ) {
-                       jQuery.data( elem, "_change_data", val );
+                       jQuery._data( elem, "_change_data", val );
                }
-               
+
                if ( data === undefined || val === data ) {
                        return;
                }
@@ -806,7 +812,7 @@ if ( !jQuery.support.changeBubbles ) {
 
        jQuery.event.special.change = {
                filters: {
-                       focusout: testChange, 
+                       focusout: testChange,
 
                        beforedeactivate: testChange,
 
@@ -835,7 +841,7 @@ if ( !jQuery.support.changeBubbles ) {
                        // information
                        beforeactivate: function( e ) {
                                var elem = e.target;
-                               jQuery.data( elem, "_change_data", getVal(elem) );
+                               jQuery._data( elem, "_change_data", getVal(elem) );
                        }
                },
 
@@ -877,15 +883,15 @@ if ( document.addEventListener ) {
                                if ( focusCounts[fix]++ === 0 ) {
                                        document.addEventListener( orig, handler, true );
                                }
-                       }, 
-                       teardown: function() { 
+                       },
+                       teardown: function() {
                                if ( --focusCounts[fix] === 0 ) {
                                        document.removeEventListener( orig, handler, true );
                                }
                        }
                };
 
-               function handler( e ) { 
+               function handler( e ) {
                        e = jQuery.event.fix( e );
                        e.type = fix;
                        return jQuery.event.trigger( e, null, e.target );
@@ -902,7 +908,7 @@ jQuery.each(["bind", "one"], function( i, name ) {
                        }
                        return this;
                }
-               
+
                if ( jQuery.isFunction( data ) || data === false ) {
                        fn = data;
                        data = undefined;
@@ -942,20 +948,20 @@ jQuery.fn.extend({
 
                return this;
        },
-       
+
        delegate: function( selector, types, data, fn ) {
                return this.live( types, data, fn, selector );
        },
-       
+
        undelegate: function( selector, types, fn ) {
                if ( arguments.length === 0 ) {
                                return this.unbind( "live" );
-               
+
                } else {
                        return this.die( types, null, fn, selector );
                }
        },
-       
+
        trigger: function( type, data ) {
                return this.each(function() {
                        jQuery.event.trigger( type, data, this );
@@ -984,8 +990,8 @@ jQuery.fn.extend({
 
                return this.click( jQuery.proxy( fn, function( event ) {
                        // Figure out which function to execute
-                       var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
-                       jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+                       var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+                       jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
 
                        // Make sure that clicks stop
                        event.preventDefault();
@@ -1012,12 +1018,12 @@ jQuery.each(["live", "die"], function( i, name ) {
                var type, i = 0, match, namespaces, preType,
                        selector = origSelector || this.selector,
                        context = origSelector ? this : jQuery( this.context );
-               
+
                if ( typeof types === "object" && !types.preventDefault ) {
                        for ( var key in types ) {
                                context[ name ]( key, data, types[key], selector );
                        }
-                       
+
                        return this;
                }
 
@@ -1064,7 +1070,7 @@ jQuery.each(["live", "die"], function( i, name ) {
                                context.unbind( "live." + liveConvert( type, selector ), fn );
                        }
                }
-               
+
                return this;
        };
 });
@@ -1073,7 +1079,7 @@ function liveHandler( event ) {
        var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
                elems = [],
                selectors = [],
-               events = jQuery.data( this, this.nodeType ? "events" : "__events__" );
+               events = jQuery._data( this, eventKey );
 
        if ( typeof events === "function" ) {
                events = events.events;
@@ -1083,7 +1089,7 @@ function liveHandler( event ) {
        if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
                return;
        }
-       
+
        if ( event.namespace ) {
                namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
        }
@@ -1181,21 +1187,4 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl
        }
 });
 
-// Prevent memory leaks in IE
-// Window isn't included so as not to unbind existing unload events
-// More info:
-//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
-if ( window.attachEvent && !window.addEventListener ) {
-       jQuery(window).bind("unload", function() {
-               for ( var id in jQuery.cache ) {
-                       if ( jQuery.cache[ id ].handle ) {
-                               // Try/Catch is to handle iframes being unloaded, see #4280
-                               try {
-                                       jQuery.event.remove( jQuery.cache[ id ].handle.elem );
-                               } catch(e) {}
-                       }
-               }
-       });
-}
-
 })( jQuery );