Commit cb811c04 authored by Dave Reed's avatar Dave Reed Committed by John Resig

Using data() on JavaScript objects sets fields directly on the object. Note...

Using data() on JavaScript objects sets fields directly on the object. Note that events are now a property of a function (to avoid JSON serialization - and only in the case of JavaScript objects, not DOM nodes). Fixes #6807.
parent ec7ea3fb
......@@ -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 ];
}
}
}
},
......
......@@ -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;
......
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() {
......
......@@ -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");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment