Update $.data to use a function instead of an object when attaching to JS objects...
authorColin Snover <github.com@zetafleet.com>
Mon, 7 Feb 2011 16:48:38 +0000 (10:48 -0600)
committerColin Snover <github.com@zetafleet.com>
Mon, 7 Feb 2011 16:48:38 +0000 (10:48 -0600)
src/data.js
src/event.js
test/unit/data.js
test/unit/event.js

index 21f0e3a..faa44f3 100644 (file)
@@ -63,12 +63,14 @@ jQuery.extend({
                }
 
                if ( !cache[ id ] ) {
                }
 
                if ( !cache[ id ] ) {
-                       cache[ id ] = {};
+                       // Use a Function as the cache object instead of an Object on JS objects
+                       // as a hack to prevent JSON.stringify from serializing it (#8108)
+                       cache[ id ] = isNode ? {} : function () {};
                }
 
                // An object can be passed to jQuery.data instead of a key/value pair; this gets
                // shallow copied over onto the existing cache
                }
 
                // An object can be passed to jQuery.data instead of a key/value pair; this gets
                // shallow copied over onto the existing cache
-               if ( typeof name === "object" ) {
+               if ( typeof name === "object" || typeof name === "function" ) {
                        if ( pvt ) {
                                cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
                        } else {
                        if ( pvt ) {
                                cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
                        } else {
index 2d53562..f3a5d9f 100644 (file)
@@ -7,8 +7,7 @@ var rnamespaces = /\.(.*)$/,
        rescape = /[^\w\s.|`]/g,
        fcleanup = function( nm ) {
                return nm.replace(rescape, "\\$&");
        rescape = /[^\w\s.|`]/g,
        fcleanup = function( nm ) {
                return nm.replace(rescape, "\\$&");
-       },
-       eventKey = "events";
+       };
 
 /*
  * A number of helper functions used for managing events.
 
 /*
  * A number of helper functions used for managing events.
@@ -58,23 +57,10 @@ jQuery.event = {
                        return;
                }
 
                        return;
                }
 
-               var events = elemData[ eventKey ],
+               var events = elemData.events,
                        eventHandle = elemData.handle;
 
                        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[ eventKey ] = elemData = function(){};
-                       }
-
+               if ( !events ) {
                        elemData.events = events = {};
                }
 
                        elemData.events = events = {};
                }
 
@@ -175,17 +161,12 @@ jQuery.event = {
 
                var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
                        elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
 
                var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
                        elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
-                       events = elemData && elemData[ eventKey ];
+                       events = elemData && elemData.events;
 
                if ( !elemData || !events ) {
                        return;
                }
 
 
                if ( !elemData || !events ) {
                        return;
                }
 
-               if ( typeof events === "function" ) {
-                       elemData = events;
-                       events = events.events;
-               }
-
                // types is actually an event object here
                if ( types && types.type ) {
                        handler = types.handler;
                // types is actually an event object here
                if ( types && types.type ) {
                        handler = types.handler;
@@ -285,10 +266,7 @@ jQuery.event = {
                        delete elemData.events;
                        delete elemData.handle;
 
                        delete elemData.events;
                        delete elemData.handle;
 
-                       if ( typeof elemData === "function" ) {
-                               jQuery.removeData( elem, eventKey, true );
-
-                       } else if ( jQuery.isEmptyObject( elemData ) ) {
+                       if ( jQuery.isEmptyObject( elemData ) ) {
                                jQuery.removeData( elem, undefined, true );
                        }
                }
                                jQuery.removeData( elem, undefined, true );
                        }
                }
@@ -329,7 +307,7 @@ jQuery.event = {
                                                // points to jQuery.expando
                                                var internalKey = jQuery.expando,
                                                        internalCache = this[ internalKey ];
                                                // points to jQuery.expando
                                                var internalKey = jQuery.expando,
                                                        internalCache = this[ internalKey ];
-                                               if ( internalCache && internalCache.events && internalCache.events[type] ) {
+                                               if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
                                                        jQuery.event.trigger( event, data, internalCache.handle.elem );
                                                }
                                        });
                                                        jQuery.event.trigger( event, data, internalCache.handle.elem );
                                                }
                                        });
@@ -355,9 +333,7 @@ jQuery.event = {
                event.currentTarget = elem;
 
                // Trigger the event, it is assumed that "handle" is a function
                event.currentTarget = elem;
 
                // Trigger the event, it is assumed that "handle" is a function
-               var handle = elem.nodeType ?
-                       jQuery._data( elem, "handle" ) :
-                       (jQuery._data( elem, eventKey ) || {}).handle;
+               var handle = jQuery._data( elem, "handle" );
 
                if ( handle ) {
                        handle.apply( elem, data );
 
                if ( handle ) {
                        handle.apply( elem, data );
@@ -435,11 +411,7 @@ jQuery.event = {
 
                event.namespace = event.namespace || namespace_sort.join(".");
 
 
                event.namespace = event.namespace || namespace_sort.join(".");
 
-               events = jQuery._data(this, eventKey);
-
-               if ( typeof events === "function" ) {
-                       events = events.events;
-               }
+               events = jQuery._data(this, "events");
 
                handlers = (events || {})[ event.type ];
 
 
                handlers = (events || {})[ event.type ];
 
@@ -606,7 +578,7 @@ jQuery.Event = function( src ) {
 
                // Events bubbling up the document may have been marked as prevented
                // by a handler lower down the tree; reflect the correct value.
 
                // 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 || 
+               this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
                        src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
 
        // Event type
                        src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
 
        // Event type
@@ -880,8 +852,8 @@ if ( document.addEventListener ) {
                jQuery.event.special[ fix ] = {
                        setup: function() {
                                this.addEventListener( orig, handler, true );
                jQuery.event.special[ fix ] = {
                        setup: function() {
                                this.addEventListener( orig, handler, true );
-                       }, 
-                       teardown: function() { 
+                       },
+                       teardown: function() {
                                this.removeEventListener( orig, handler, true );
                        }
                };
                                this.removeEventListener( orig, handler, true );
                        }
                };
@@ -1074,11 +1046,7 @@ function liveHandler( event ) {
        var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
                elems = [],
                selectors = [],
        var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
                elems = [],
                selectors = [],
-               events = jQuery._data( this, eventKey );
-
-       if ( typeof events === "function" ) {
-               events = events.events;
-       }
+               events = jQuery._data( this, "events" );
 
        // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
        if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
 
        // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
        if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
index 889fc2d..1ce512c 100644 (file)
@@ -22,7 +22,7 @@ function dataTests (elem) {
        strictEqual( jQuery.hasData(elem), false, "jQuery.hasData agrees no data exists initially" );
 
        var dataObj = jQuery.data(elem);
        strictEqual( jQuery.hasData(elem), false, "jQuery.hasData agrees no data exists initially" );
 
        var dataObj = jQuery.data(elem);
-       equals( typeof dataObj, "object", "Calling data with no args gives us a data object reference" );
+       equals( typeof dataObj, elem.nodeType ? "object" : "function", "Calling data with no args gives us a data object reference" );
        strictEqual( jQuery.data(elem), dataObj, "Calling jQuery.data returns the same data object when called multiple times" );
 
        strictEqual( jQuery.hasData(elem), false, "jQuery.hasData agrees no data exists even when an empty data obj exists" );
        strictEqual( jQuery.data(elem), dataObj, "Calling jQuery.data returns the same data object when called multiple times" );
 
        strictEqual( jQuery.hasData(elem), false, "jQuery.hasData agrees no data exists even when an empty data obj exists" );
@@ -187,8 +187,12 @@ test(".data()", function() {
        equals( nodiv.data(), null, "data() on empty set returns null" );
 
        var obj = { foo: "bar" };
        equals( nodiv.data(), null, "data() on empty set returns null" );
 
        var obj = { foo: "bar" };
-       deepEqual( jQuery(obj).data(), {}, "Retrieve data object from a wrapped JS object (#7524)" );
-})
+       jQuery(obj).data("foo", "baz");
+
+       var dataObj = jQuery.extend(true, {}, jQuery(obj).data());
+
+       deepEqual( dataObj, { foo: "baz" }, "Retrieve data object from a wrapped JS object (#7524)" );
+});
 
 test(".data(String) and .data(String, Object)", function() {
        expect(29);
 
 test(".data(String) and .data(String, Object)", function() {
        expect(29);
@@ -461,4 +465,15 @@ test(".removeData()", function() {
 
        div.removeData("test.foo");
        equals( div.data("test.foo"), undefined, "Make sure data is intact" );
 
        div.removeData("test.foo");
        equals( div.data("test.foo"), undefined, "Make sure data is intact" );
-});
\ No newline at end of file
+});
+
+if (window.JSON && window.JSON.stringify) {
+       test("JSON serialization (#8108)", function () {
+               expect(1);
+
+               var obj = { foo: "bar" };
+               jQuery.data(obj, "hidden", true);
+
+               equals( JSON.stringify(obj), '{"foo":"bar"}', "Expando is hidden from JSON.stringify" );
+       });
+}
\ No newline at end of file
index e4caee8..045ea73 100644 (file)
@@ -312,7 +312,7 @@ test("bind/delegate bubbling, isDefaultPrevented", function() {
                        // Use a native click so we don't get jQuery simulated bubbling
                        if ( document.createEvent ) {
                                var e = document.createEvent( 'MouseEvents' );
                        // Use a native click so we don't get jQuery simulated bubbling
                        if ( document.createEvent ) {
                                var e = document.createEvent( 'MouseEvents' );
-                               e.initEvent( "click", true, true ); 
+                               e.initEvent( "click", true, true );
                                $jq[0].dispatchEvent(e);
                        }
                        else if ( $jq[0].click ) {
                                $jq[0].dispatchEvent(e);
                        }
                        else if ( $jq[0].click ) {
@@ -548,7 +548,7 @@ test("bind(name, false), unbind(name, false)", function() {
 });
 
 test("bind()/trigger()/unbind() on plain object", function() {
 });
 
 test("bind()/trigger()/unbind() on plain object", function() {
-       expect( 8 );
+       expect( 7 );
 
        var obj = {};
 
 
        var obj = {};
 
@@ -570,7 +570,6 @@ test("bind()/trigger()/unbind() on plain object", function() {
        var events = jQuery._data(obj, "events");
        ok( events, "Object has events bound." );
        equals( obj.events, undefined, "Events object on plain objects is not events" );
        var events = jQuery._data(obj, "events");
        ok( events, "Object has events bound." );
        equals( obj.events, undefined, "Events object on plain objects is not events" );
-       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." );
 
        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." );