Make sure that jQuery.data( elem ) always returns a data object, fixes #5971. Improve...
authorjeresig <jeresig@gmail.com>
Thu, 28 Jan 2010 19:12:44 +0000 (14:12 -0500)
committerjeresig <jeresig@gmail.com>
Thu, 28 Jan 2010 19:12:44 +0000 (14:12 -0500)
src/data.js
src/event.js
test/unit/data.js

index e3dc190..0049a9f 100644 (file)
@@ -1,5 +1,4 @@
 var expando = "jQuery" + now(), uuid = 0, windowData = {};
-var emptyObject = {};
 
 jQuery.extend({
        cache: {},
@@ -25,8 +24,7 @@ jQuery.extend({
 
                var id = elem[ expando ], cache = jQuery.cache, thisCache;
 
-               // Handle the case where there's no name immediately
-               if ( !name && !id ) {
+               if ( !id && typeof name === "string" && data === undefined ) {
                        return null;
                }
 
@@ -40,17 +38,16 @@ jQuery.extend({
                if ( typeof name === "object" ) {
                        elem[ expando ] = id;
                        thisCache = cache[ id ] = jQuery.extend(true, {}, name);
-               } else if ( cache[ id ] ) {
-                       thisCache = cache[ id ];
-               } else if ( typeof data === "undefined" ) {
-                       thisCache = emptyObject;
-               } else {
-                       thisCache = cache[ id ] = {};
+
+               } else if ( !cache[ id ] ) {
+                       elem[ expando ] = id;
+                       cache[ id ] = {};
                }
 
+               thisCache = cache[ id ];
+
                // Prevent overriding the named cache with undefined values
                if ( data !== undefined ) {
-                       elem[ expando ] = id;
                        thisCache[ name ] = data;
                }
 
index efefe57..2978bc6 100644 (file)
@@ -42,8 +42,16 @@ jQuery.event = {
                }
 
                // Init the element's event structure
-               var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ),
-                       handle = jQuery.data( elem, "handle" ), eventHandle;
+               var elemData = jQuery.data( elem );
+
+               // If no elemData is found then we must be trying to bind to one of the
+               // banned noData elements
+               if ( !elemData ) {
+                       return;
+               }
+
+               var events = elemData.events || (elemData.events = {}),
+                       handle = elemData.handle, eventHandle;
 
                if ( !handle ) {
                        eventHandle = function() {
@@ -54,13 +62,7 @@ jQuery.event = {
                                        undefined;
                        };
 
-                       handle = jQuery.data( elem, "handle", eventHandle );
-               }
-
-               // If no handle is found then we must be trying to bind to one of the
-               // banned noData elements
-               if ( !handle ) {
-                       return;
+                       handle = elemData.handle = eventHandle;
                }
 
                // Add elem as a property of the handle function
@@ -70,15 +72,11 @@ jQuery.event = {
 
                // Handle multiple events separated by a space
                // jQuery(...).bind("mouseover mouseout", fn);
-               types = types.split( /\s+/ );
+               types = types.split(" ");
 
-               var type, i = 0;
+               var type, i = 0, namespaces;
 
                while ( (type = types[ i++ ]) ) {
-                       // Namespaced event handlers
-                       var namespaces = type.split(".");
-                       type = namespaces.shift();
-
                        if ( i > 1 ) {
                                handler = jQuery.proxy( handler );
 
@@ -87,7 +85,16 @@ jQuery.event = {
                                }
                        }
 
-                       handler.type = namespaces.slice(0).sort().join(".");
+                       // Namespaced event handlers
+                       if ( type.indexOf(".") > -1 ) {
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+                               handler.type = namespaces.slice(0).sort().join(".");
+
+                       } else {
+                               namespaces = [];
+                               handler.type = "";
+                       }
 
                        // Get the current list of functions bound to this event
                        var handlers = events[ type ],
@@ -104,6 +111,7 @@ jQuery.event = {
                                        // Bind the global event handler to the element
                                        if ( elem.addEventListener ) {
                                                elem.addEventListener( type, handle, false );
+
                                        } else if ( elem.attachEvent ) {
                                                elem.attachEvent( "on" + type, handle );
                                        }
@@ -140,7 +148,13 @@ jQuery.event = {
                        return;
                }
 
-               var events = jQuery.data( elem, "events" ), ret, type, fn;
+               var elemData = jQuery.data( elem );
+
+               if ( !elemData ) {
+                       return;
+               }
+
+               var events = elemData.events, ret, type, fn;
 
                if ( events ) {
                        // Unbind all events for the element
@@ -148,6 +162,7 @@ jQuery.event = {
                                for ( type in events ) {
                                        this.remove( elem, type + (types || "") );
                                }
+
                        } else {
                                // types is actually an event object here
                                if ( types.type ) {
@@ -157,16 +172,24 @@ jQuery.event = {
 
                                // Handle multiple events separated by a space
                                // jQuery(...).unbind("mouseover mouseout", fn);
-                               types = types.split(/\s+/);
-                               var i = 0;
+                               types = types.split(" ");
+
+                               var i = 0, all, namespaces, namespace;
+
                                while ( (type = types[ i++ ]) ) {
-                                       // Namespaced event handlers
-                                       var namespaces = type.split(".");
-                                       type = namespaces.shift();
-                                       var all = !namespaces.length,
-                                               cleaned = jQuery.map( namespaces.slice(0).sort(), fcleanup ),
-                                               namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
-                                               special = this.special[ type ] || {};
+                                       all = type.indexOf(".") < 0;
+                                       namespaces = null;
+
+                                       if ( !all ) {
+                                               // Namespaced event handlers
+                                               namespaces = type.split(".");
+                                               type = namespaces.shift();
+
+                                               namespace = new RegExp("(^|\\.)" + 
+                                                       jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
+                                       }
+
+                                       var special = this.special[ type ] || {};
 
                                        if ( events[ type ] ) {
                                                // remove the given handler for the given type
@@ -185,21 +208,23 @@ jQuery.event = {
                                                }
 
                                                if ( special.remove ) {
-                                                       special.remove.call( elem, namespaces, fn);
+                                                       special.remove.call( elem, namespaces || [], fn);
                                                }
 
                                                // remove generic event handler if no more handlers exist
                                                for ( ret in events[ type ] ) {
+
                                                        break;
                                                }
                                                if ( !ret ) {
                                                        if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
                                                                if ( elem.removeEventListener ) {
-                                                                       elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
+                                                                       elem.removeEventListener( type, elemData.handle, false );
                                                                } else if ( elem.detachEvent ) {
-                                                                       elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
+                                                                       elem.detachEvent( "on" + type, elemData.handle );
                                                                }
                                                        }
+
                                                        ret = null;
                                                        delete events[ type ];
                                                }
@@ -211,13 +236,19 @@ jQuery.event = {
                        for ( ret in events ) {
                                break;
                        }
+
                        if ( !ret ) {
-                               var handle = jQuery.data( elem, "handle" );
+                               var handle = elemData.handle;
                                if ( handle ) {
                                        handle.elem = null;
                                }
-                               jQuery.removeData( elem, "events" );
-                               jQuery.removeData( elem, "handle" );
+
+                               delete elemData.events;
+                               delete elemData.handle;
+
+                               if ( jQuery.isEmptyObject( elemData ) ) {
+                                       jQuery.removeData( elem );
+                               }
                        }
                }
        },
@@ -796,11 +827,16 @@ jQuery.each(["bind", "one"], function( i, name ) {
                        return fn.apply( this, arguments );
                }) : fn;
 
-               return type === "unload" && name !== "one" ?
-                       this.one( type, data, fn ) :
-                       this.each(function() {
-                               jQuery.event.add( this, type, handler, data );
-                       });
+               if ( type === "unload" && name !== "one" ) {
+                       this.one( type, data, fn );
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.add( this[i], type, handler, data );
+                       }
+               }
+
+               return this;
        };
 });
 
@@ -811,12 +847,14 @@ jQuery.fn.extend({
                        for ( var key in type ) {
                                this.unbind(key, type[key]);
                        }
-                       return this;
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.remove( this[i], type, fn );
+                       }
                }
 
-               return this.each(function() {
-                       jQuery.event.remove( this, type, fn );
-               });
+               return this;
        },
        trigger: function( type, data ) {
                return this.each(function() {
index fd03c54..79b23c6 100644 (file)
@@ -1,20 +1,19 @@
 module("data");
 
 test("expando", function(){
-       expect(7);
+       expect(6);
        
        equals("expando" in jQuery, true, "jQuery is exposing the expando");
        
        var obj = {};
        jQuery.data(obj);
-       equals( jQuery.expando in obj, false, "jQuery.data did not add an expando to the object" );
-       
-       jQuery.data(obj, true);
-       equals( jQuery.expando in obj, false, "jQuery.data did not add an expando to the object" );
-       
+       equals( jQuery.expando in obj, true, "jQuery.data adds an expando to the object" );
+
+       obj = {};       
        jQuery.data(obj, 'test');
        equals( jQuery.expando in obj, false, "jQuery.data did not add an expando to the object" );
-       
+
+       obj = {};
        jQuery.data(obj, "foo", "bar");
        equals( jQuery.expando in obj, true, "jQuery.data added an expando to the object" );