Skip to content

Commit

Permalink
Update $.data to use a function instead of an object when attaching t…
Browse files Browse the repository at this point in the history
…o JS objects in order to hide attached metadata from JSON.stringify. Remove event.js code that was doing this before specifically for events, which is now redundant. Fixes #8108. 1.5-stable
  • Loading branch information
csnover committed Feb 7, 2011
1 parent a2dbdc1 commit 7acb141
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 53 deletions.
6 changes: 4 additions & 2 deletions src/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ jQuery.extend({
}

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
if ( typeof name === "object" ) {
if ( typeof name === "object" || typeof name === "function" ) {
if ( pvt ) {
cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
} else {
Expand Down
56 changes: 12 additions & 44 deletions src/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ var rnamespaces = /\.(.*)$/,
rescape = /[^\w\s.|`]/g,
fcleanup = function( nm ) {
return nm.replace(rescape, "\\$&");
},
eventKey = "events";
};

/*
* A number of helper functions used for managing events.
Expand Down Expand Up @@ -58,23 +57,10 @@ jQuery.event = {
return;
}

var events = elemData[ eventKey ],
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[ eventKey ] = elemData = function(){};
}

if ( !events ) {
elemData.events = events = {};
}

Expand Down Expand Up @@ -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 ),
events = elemData && elemData[ eventKey ];
events = elemData && elemData.events;

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;
Expand Down Expand Up @@ -285,10 +266,7 @@ jQuery.event = {
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 );
}
}
Expand Down Expand Up @@ -329,7 +307,7 @@ jQuery.event = {
// 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 );
}
});
Expand All @@ -355,9 +333,7 @@ jQuery.event = {
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 );
Expand Down Expand Up @@ -435,11 +411,7 @@ jQuery.event = {

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 ];

Expand Down Expand Up @@ -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.
this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;

// Event type
Expand Down Expand Up @@ -880,8 +852,8 @@ if ( document.addEventListener ) {
jQuery.event.special[ fix ] = {
setup: function() {
this.addEventListener( orig, handler, true );
},
teardown: function() {
},
teardown: function() {
this.removeEventListener( orig, handler, true );
}
};
Expand Down Expand Up @@ -1074,11 +1046,7 @@ function liveHandler( event ) {
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" ) {
Expand Down
23 changes: 19 additions & 4 deletions test/unit/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function dataTests (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" );
Expand Down Expand Up @@ -187,8 +187,12 @@ test(".data()", function() {
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);
Expand Down Expand Up @@ -461,4 +465,15 @@ test(".removeData()", function() {

div.removeData("test.foo");
equals( div.data("test.foo"), undefined, "Make sure data is intact" );
});
});

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" );
});
}
5 changes: 2 additions & 3 deletions test/unit/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
e.initEvent( "click", true, true );
e.initEvent( "click", true, true );
$jq[0].dispatchEvent(e);
}
else if ( $jq[0].click ) {
Expand Down Expand Up @@ -548,7 +548,7 @@ test("bind(name, false), unbind(name, false)", function() {
});

test("bind()/trigger()/unbind() on plain object", function() {
expect( 8 );
expect( 7 );

var obj = {};

Expand All @@ -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" );
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." );

Expand Down

0 comments on commit 7acb141

Please sign in to comment.