Using data() on JavaScript objects sets fields directly on the object. Note that...
authorDave Reed <dareed@microsoft.com>
Wed, 29 Sep 2010 13:46:25 +0000 (06:46 -0700)
committerJohn Resig <jeresig@gmail.com>
Wed, 29 Sep 2010 13:46:25 +0000 (06:46 -0700)
src/data.js
src/event.js
test/unit/data.js
test/unit/event.js

index 43ab595..66d24c4 100644 (file)
@@ -30,18 +30,17 @@ jQuery.extend({
                        windowData :
                        elem;
 
-               var id = elem[ jQuery.expando ], cache = jQuery.cache, thisCache,
-                       isNode = elem.nodeType,
-                       store;
+               var isNode = elem.nodeType,
+                       id = isNode ? elem[ jQuery.expando ] : null,
+                       cache = jQuery.cache, thisCache;
 
-               if ( !id && typeof name === "string" && data === undefined ) {
+               if ( isNode && !id && typeof name === "string" && data === undefined ) {
                        return;
                }
 
                // Get the data from the object directly
                if ( !isNode ) {
                        cache = elem;
-                       id = jQuery.expando;
 
                // Compute a unique ID for the element
                } else if ( !id ) {
@@ -55,26 +54,14 @@ jQuery.extend({
                                cache[ id ] = jQuery.extend(cache[ id ], name);
 
                        } else {
-                               store = jQuery.extend(cache[ id ], name);
-                               cache[ id ] = function() {
-                                       return store;
-                               };
+                               jQuery.extend( cache, name );
                        }
 
-               } else if ( !cache[ id ] ) {
-                       if ( isNode ) {
-                               cache[ id ] = {};
-
-                       } else {
-                               store = {};
-                               cache[ id ] = function() {
-                                       return store;
-                               };
-                       }
-                       
+               } else if ( isNode && !cache[ id ] ) {
+                       cache[ id ] = {};
                }
 
-               thisCache = isNode ? cache[ id ] : cache[ id ]();
+               thisCache = isNode ? cache[ id ] : cache;
 
                // Prevent overriding the named cache with undefined values
                if ( data !== undefined ) {
@@ -94,11 +81,9 @@ jQuery.extend({
                        elem;
 
                var isNode = elem.nodeType,
-                       id = elem[ jQuery.expando ], cache = jQuery.cache;
-               if ( id && !isNode ) {
-                       id = id();
-               }
-               var thisCache = cache[ id ];
+                       id = isNode ? elem[ jQuery.expando ] : elem,
+                       cache = jQuery.cache,
+                       thisCache = isNode ? cache[ id ] : id;
 
                // If we want to remove a specific section of the element's data
                if ( name ) {
@@ -107,23 +92,28 @@ jQuery.extend({
                                delete thisCache[ name ];
 
                                // If we've removed all the data, remove the element's cache
-                               if ( jQuery.isEmptyObject(thisCache) ) {
+                               if ( isNode && jQuery.isEmptyObject(thisCache) ) {
                                        jQuery.removeData( elem );
                                }
                        }
 
                // Otherwise, we want to remove all of the element's data
                } else {
-                       if ( jQuery.support.deleteExpando || !isNode ) {
+                       if ( isNode && jQuery.support.deleteExpando ) {
                                delete elem[ jQuery.expando ];
 
                        } else if ( elem.removeAttribute ) {
                                elem.removeAttribute( jQuery.expando );
-                       }
 
                        // Completely remove the data cache
-                       if ( isNode ) {
+                       } else if ( isNode ) {
                                delete cache[ id ];
+
+                       // Remove all fields from the object
+                       } else {
+                               for ( var n in elem ) {
+                                       delete elem[ n ];
+                               }
                        }
                }
        },
index dcb872a..5829948 100644 (file)
@@ -54,8 +54,25 @@ jQuery.event = {
                        return;
                }
 
-               var events = elemData.events = elemData.events || {},
+               var events = elemData.events,
                        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
+                       // the function does not need to be called, it just contains the data
+                       eventHandle = events.handle;
+                       events = events.events;
+
+               } else if ( !events ) {
+                       if ( !elem.nodeType ) {
+                               // On plain objects, create a fn that acts as the holder
+                               // of the values to avoid JSON serialization of event data
+                               elemData.events = elemData = function(){};
+                       }
+
+                       elemData.events = events = {};
+               }
 
                if ( !eventHandle ) {
                        elemData.handle = eventHandle = function() {
@@ -159,6 +176,11 @@ jQuery.event = {
                if ( !elemData || !events ) {
                        return;
                }
+               
+               if ( typeof events === "function" ) {
+                       elemData = events;
+                       events = events.events;
+               }
 
                // types is actually an event object here
                if ( types && types.type ) {
@@ -259,7 +281,10 @@ jQuery.event = {
                        delete elemData.events;
                        delete elemData.handle;
 
-                       if ( jQuery.isEmptyObject( elemData ) ) {
+                       if ( typeof elemData === "function" ) {
+                               delete elem.events;
+
+                       } else if ( jQuery.isEmptyObject( elemData ) ) {
                                jQuery.removeData( elem );
                        }
                }
@@ -319,7 +344,10 @@ jQuery.event = {
                event.currentTarget = elem;
 
                // Trigger the event, it is assumed that "handle" is a function
-               var handle = jQuery.data( elem, "handle" );
+               var handle = elem.nodeType ?
+                       jQuery.data( elem, "handle" ) :
+                       elem.events && elem.events.handle;
+
                if ( handle ) {
                        handle.apply( elem, data );
                }
@@ -393,6 +421,11 @@ jQuery.event = {
                event.namespace = event.namespace || namespace_sort.join(".");
 
                events = jQuery.data(this, "events");
+
+               if ( typeof events === "function" ) {
+                       events = events.events;
+               }
+
                handlers = (events || {})[ event.type ];
 
                if ( events && handlers ) {
@@ -1007,6 +1040,10 @@ function liveHandler( event ) {
                related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
                events = jQuery.data( this, "events" );
 
+       if ( typeof events === "function" ) {
+               events = events.events;
+       }
+
        // Make sure we avoid non-left-click bubbling in Firefox (#3861)
        if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
                return;
index 77ee099..2efa984 100644 (file)
@@ -1,27 +1,22 @@
 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, true, "jQuery.data adds an expando to the object" );
-       equals( typeof obj[jQuery.expando], "function", "jQuery.data adds an expando to the object as a function" );
+       equals( jQuery.data(obj), obj, "jQuery.data(obj) returns the object");
+       equals( jQuery.expando in obj, false, "jQuery.data(obj) did not add 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" );
+       equals( jQuery.expando in obj, false, "jQuery.data(obj,key) 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" );
-
-       var id = obj[jQuery.expando]();
-       equals( id in jQuery.cache, false, "jQuery.data did not add an entry to jQuery.cache" );
-
-       equals( id.foo, "bar", "jQuery.data worked correctly" );
+       equals( jQuery.expando in obj, false, "jQuery.data(obj,key,value) did not add an expando to the object" );
+       equals( obj.foo, "bar", "jQuery.data(obj,key,value) sets fields directly on the object." );
 });
 
 test("jQuery.acceptData", function() {
@@ -43,7 +38,7 @@ test("jQuery.acceptData", function() {
 });
 
 test("jQuery.data", function() {
-       expect(13);
+       expect(12);
        var div = document.createElement("div");
 
        ok( jQuery.data(div, "test") === undefined, "Check for no data exists" );
@@ -67,17 +62,15 @@ test("jQuery.data", function() {
 
        jQuery.data(div, "test3", "orig");
        jQuery.data(div, { "test": "in", "test2": "in2" });
-       equals( jQuery.data(div, "test"), "in", "Verify setting an object in data." );
-       equals( jQuery.data(div, "test2"), "in2", "Verify setting an object in data." );
-       equals( jQuery.data(div, "test3"), "orig", "Verify original not overwritten." );
+       equals( jQuery.data(div, "test"), "in", "Verify setting an object in data" );
+       equals( jQuery.data(div, "test2"), "in2", "Verify setting an object in data" );
+       equals( jQuery.data(div, "test3"), "orig", "Verify original not overwritten" );
 
        var obj = {};
        jQuery.data( obj, "prop", true );
 
-       ok( obj[ jQuery.expando ], "Data is being stored on the object." );
-       ok( obj[ jQuery.expando ]().prop, "Data is being stored on the object." );
-
-       equals( jQuery.data( obj, "prop" ), true, "Make sure the right value is retrieved." );
+       ok( obj.prop, "Data is being stored on the object" );
+       equals( jQuery.data( obj, "prop" ), true, "Make sure the right value is retrieved" );
 });
 
 test(".data()", function() {
@@ -95,7 +88,7 @@ test(".data()", function() {
 })
 
 test(".data(String) and .data(String, Object)", function() {
-       expect(27);
+       expect(29);
        var parent = jQuery("<div><div></div></div>"),
                div = parent.children();
 
@@ -173,14 +166,16 @@ test(".data(String) and .data(String, Object)", function() {
        equals( div.data("test.bar"), "testroot", "Check for unmatched namespace" );
 
        // #3748
-       var $elem = jQuery({});
+       var $elem = jQuery({exists:true});
        equals( $elem.data('nothing'), undefined, "Non-existent data returns undefined");
        equals( $elem.data('null',null).data('null'), null, "null's are preserved");
        equals( $elem.data('emptyString','').data('emptyString'), '', "Empty strings are preserved");
        equals( $elem.data('false',false).data('false'), false, "false's are preserved");
-
+       equals( $elem.data('exists'), true, "Existing data is returned" );
+       
        // Clean up
        $elem.removeData();
+       ok( jQuery.isEmptyObject( $elem[0] ), "removeData clears the object" );
 });
 
 test("data-* attributes", function() {
@@ -268,21 +263,34 @@ test("data-* attributes", function() {
 });
 
 test(".data(Object)", function() {
-       expect(2);
+       expect(4);
 
        var div = jQuery("<div/>");
 
        div.data({ "test": "in", "test2": "in2" });
-       equals( div.data("test"), "in", "Verify setting an object in data." );
-       equals( div.data("test2"), "in2", "Verify setting an object in data." );
+       equals( div.data("test"), "in", "Verify setting an object in data" );
+       equals( div.data("test2"), "in2", "Verify setting an object in data" );
+       
+       var obj = {test:"unset"},
+               jqobj = jQuery(obj);
+       jqobj.data({ "test": "in", "test2": "in2" });
+       equals( obj.test, "in", "Verify setting an object on an object extends the object" );
+       equals( obj.test2, "in2", "Verify setting an object on an object extends the object" ); 
 });
 
 test("jQuery.removeData", function() {
-       expect(1);
+       expect(4);
        var div = jQuery("#foo")[0];
        jQuery.data(div, "test", "testing");
        jQuery.removeData(div, "test");
        equals( jQuery.data(div, "test"), undefined, "Check removal of data" );
+       
+       var obj = {};
+       jQuery.data(obj, "test", "testing");
+       equals( obj.test, "testing", "verify data on plain object");
+       jQuery.removeData(obj, "test");
+       equals( jQuery.data(obj, "test"), undefined, "Check removal of data on plain object" );
+       equals( obj.test, undefined, "Check removal of data directly from plain object" );      
 });
 
 test(".removeData()", function() {
index 10751cc..b5e24c8 100644 (file)
@@ -443,7 +443,7 @@ test("bind(name, false), unbind(name, false)", function() {
 });
 
 test("bind()/trigger()/unbind() on plain object", function() {
-       expect( 2 );
+       expect( 5 );
 
        var obj = {};
 
@@ -457,7 +457,11 @@ test("bind()/trigger()/unbind() on plain object", function() {
                ok( true, "Custom event run." );
        });
 
-       ok( jQuery(obj).data("events"), "Object has events bound." );
+       var events = jQuery(obj).data("events");
+       ok( events, "Object has events bound." );
+       equals( typeof events, "function", "'events' expando is a function on plain objects." );
+       equals( obj.test, undefined, "Make sure that test event is not on the plain object." );
+       equals( obj.handle, undefined, "Make sure that the event handler is not on the plain object." );
 
        // Should trigger 1
        jQuery(obj).trigger("test");