Skip to content

Commit 52c1db9

Browse files
author
InfinitiesLoop
committed
Ticket #6807. data() sets fields directly on plain objects and avoids the 'events' field from being serializable.
1 parent 2f1aea7 commit 52c1db9

File tree

4 files changed

+114
-53
lines changed

4 files changed

+114
-53
lines changed

src/data.js

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,33 @@ jQuery.extend({
2626
windowData :
2727
elem;
2828

29-
var id = elem[ jQuery.expando ], cache = jQuery.cache, thisCache,
30-
isNode = elem.nodeType;
29+
var isNode = elem.nodeType,
30+
id = isNode ? elem[ jQuery.expando ] : null,
31+
cache = jQuery.cache, thisCache;
3132

32-
if ( !id && typeof name === "string" && data === undefined ) {
33+
if ( isNode && !id && typeof name === "string" && data === undefined ) {
3334
return;
3435
}
3536

36-
// Get the data from the object directly
37-
if ( !isNode ) {
38-
cache = elem;
39-
id = jQuery.expando;
40-
41-
// Compute a unique ID for the element
42-
} else if ( !id ) {
37+
if ( isNode && !id ) {
38+
// Compute a unique ID for the element
4339
elem[ jQuery.expando ] = id = ++jQuery.uuid;
4440
}
4541

4642
// Avoid generating a new cache unless none exists and we
4743
// want to manipulate it.
4844
if ( typeof name === "object" ) {
49-
cache[ id ] = jQuery.extend(true, {}, name);
45+
if ( isNode ) {
46+
cache[ id ] = jQuery.extend(true, {}, name);
47+
} else {
48+
jQuery.extend(true, elem, name);
49+
}
5050

51-
} else if ( !cache[ id ] ) {
51+
} else if ( isNode && !cache[ id ] ) {
5252
cache[ id ] = {};
5353
}
5454

55-
thisCache = cache[ id ];
55+
thisCache = isNode ? cache[ id ] : elem;
5656

5757
// Prevent overriding the named cache with undefined values
5858
if ( data !== undefined ) {
@@ -71,24 +71,21 @@ jQuery.extend({
7171
windowData :
7272
elem;
7373

74-
var id = elem[ jQuery.expando ], cache = jQuery.cache,
75-
isNode = elem.nodeType, thisCache = isNode ? cache[ id ] : id;
74+
var isNode = elem.nodeType,
75+
id = isNode ? elem[ jQuery.expando ] : elem,
76+
cache = jQuery.cache,
77+
thisCache = isNode ? cache[ id ] : id;
7678

7779
// If we want to remove a specific section of the element's data
7880
if ( name ) {
7981
if ( thisCache ) {
8082
// Remove the section of cache data
8183
delete thisCache[ name ];
82-
83-
// If we've removed all the data, remove the element's cache
84-
if ( jQuery.isEmptyObject(thisCache) ) {
85-
jQuery.removeData( elem );
86-
}
8784
}
8885

8986
// Otherwise, we want to remove all of the element's data
9087
} else {
91-
if ( jQuery.support.deleteExpando || !isNode ) {
88+
if ( isNode && jQuery.support.deleteExpando ) {
9289
delete elem[ jQuery.expando ];
9390

9491
} else if ( elem.removeAttribute ) {
@@ -98,6 +95,11 @@ jQuery.extend({
9895
// Completely remove the data cache
9996
if ( isNode ) {
10097
delete cache[ id ];
98+
} else {
99+
// remove all fields from the object
100+
for ( var n in elem ) {
101+
delete elem[ n ];
102+
}
101103
}
102104
}
103105
}

src/event.js

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,33 @@ jQuery.event = {
4949
if ( !elemData ) {
5050
return;
5151
}
52-
53-
var events = elemData.events = elemData.events || {},
52+
53+
var container,
54+
events = elemData.events,
5455
eventHandle = elemData.handle;
56+
if ( typeof events === "function" ) {
57+
// on plain objects events is a function that returns
58+
// a container object, which holds the events and handle,
59+
// which prevents this data from being JSON serialized
60+
container = events();
61+
eventHandle = container.handle;
62+
events = container.events;
63+
} else if ( !events ) {
64+
if ( elem.nodeType ) {
65+
elemData.events = events = {};
66+
} else {
67+
// on plain objects, create a function that returns
68+
// a container of the events and handle
69+
events = {};
70+
container = { events: events };
71+
elemData.events = function() {
72+
return container;
73+
}
74+
}
75+
}
5576

5677
if ( !eventHandle ) {
57-
elemData.handle = eventHandle = function() {
78+
( container || elemData ).handle = eventHandle = function() {
5879
// Handle the second event of a trigger and when
5980
// an event is called after a page has unloaded
6081
return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
@@ -150,11 +171,17 @@ jQuery.event = {
150171

151172
var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
152173
elemData = jQuery.data( elem ),
153-
events = elemData && elemData.events;
174+
events = elemData && elemData.events,
175+
container;
154176

155177
if ( !elemData || !events ) {
156178
return;
157179
}
180+
181+
if ( typeof events === "function" ) {
182+
container = events();
183+
events = container.events;
184+
}
158185

159186
// types is actually an event object here
160187
if ( types && types.type ) {
@@ -247,15 +274,17 @@ jQuery.event = {
247274

248275
// Remove the expando if it's no longer used
249276
if ( jQuery.isEmptyObject( events ) ) {
250-
var handle = elemData.handle;
277+
var handle = ( container || elemData ).handle;
251278
if ( handle ) {
252279
handle.elem = null;
253280
}
254281

255282
delete elemData.events;
256-
delete elemData.handle;
257-
258-
if ( jQuery.isEmptyObject( elemData ) ) {
283+
if ( !container ) {
284+
delete elemData.handle;
285+
}
286+
287+
if ( !container && jQuery.isEmptyObject( elemData ) ) {
259288
jQuery.removeData( elem );
260289
}
261290
}
@@ -315,7 +344,13 @@ jQuery.event = {
315344
event.currentTarget = elem;
316345

317346
// Trigger the event, it is assumed that "handle" is a function
318-
var handle = jQuery.data( elem, "handle" );
347+
var handle;
348+
if ( elem.nodeType ) {
349+
handle = jQuery.data( elem, "handle" );
350+
} else {
351+
var container = jQuery.data( elem, "events" );
352+
handle = typeof container === "function" && container().handle;
353+
}
319354
if ( handle ) {
320355
handle.apply( elem, data );
321356
}
@@ -388,6 +423,9 @@ jQuery.event = {
388423
event.namespace = event.namespace || namespace_sort.join(".");
389424

390425
events = jQuery.data(this, "events");
426+
if ( typeof events === "function" ) {
427+
events = events().events;
428+
}
391429
handlers = (events || {})[ event.type ];
392430

393431
if ( events && handlers ) {
@@ -993,6 +1031,9 @@ function liveHandler( event ) {
9931031
var stop, maxLevel, elems = [], selectors = [],
9941032
related, match, handleObj, elem, j, i, l, data, close, namespace,
9951033
events = jQuery.data( this, "events" );
1034+
if ( typeof events === "function" ) {
1035+
events = events().events;
1036+
}
9961037

9971038
// Make sure we avoid non-left-click bubbling in Firefox (#3861)
9981039
if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {

test/unit/data.js

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,19 @@ test("expando", function(){
77

88
var obj = {};
99
jQuery.data(obj);
10-
equals( jQuery.expando in obj, true, "jQuery.data adds an expando to the object" );
10+
equals( jQuery.expando in obj, false, "jQuery.data did not add an expando to the object" );
1111

1212
obj = {};
1313
jQuery.data(obj, 'test');
14-
equals( jQuery.expando in obj, false, "jQuery.data did not add an expando to the object" );
14+
equals( jQuery.expando in obj, false, "jQuery.data(obj,key) did not add an expando to the object" );
1515

1616
obj = {};
1717
jQuery.data(obj, "foo", "bar");
18-
equals( jQuery.expando in obj, true, "jQuery.data added an expando to the object" );
18+
equals( jQuery.expando in obj, false, "jQuery.data(obj,key,value) did not add an expando to the object" );
19+
equals( obj.foo, "bar", "jQuery.data(obj,key,value) sets fields directly on the object." );
1920

2021
var id = obj[jQuery.expando];
2122
equals( id in jQuery.cache, false, "jQuery.data did not add an entry to jQuery.cache" );
22-
23-
equals( id.foo, "bar", "jQuery.data worked correctly" );
2423
});
2524

2625
test("jQuery.data", function() {
@@ -47,16 +46,16 @@ test("jQuery.data", function() {
4746
ok( jQuery.data(div, "test") === null, "Check for null data");
4847

4948
jQuery.data(div, { "test": "in", "test2": "in2" });
50-
equals( jQuery.data(div, "test"), "in", "Verify setting an object in data." );
51-
equals( jQuery.data(div, "test2"), "in2", "Verify setting an object in data." );
49+
equals( jQuery.data(div, "test"), "in", "Verify setting an object in data" );
50+
equals( jQuery.data(div, "test2"), "in2", "Verify setting an object in data" );
5251

5352
var obj = {};
5453
jQuery.data( obj, "prop", true );
5554

56-
ok( obj[ jQuery.expando ], "Data is being stored on the object." );
57-
ok( obj[ jQuery.expando ].prop, "Data is being stored on the object." );
58-
59-
equals( jQuery.data( obj, "prop" ), true, "Make sure the right value is retrieved." );
55+
ok( !obj[ jQuery.expando ], "Cache is not being stored on the object" );
56+
ok( obj.prop, "Data is being stored directly on the object" );
57+
debugger;
58+
equals( jQuery.data( obj, "prop" ), true, "Make sure the right value is retrieved" );
6059
});
6160

6261
test(".data()", function() {
@@ -68,7 +67,7 @@ test(".data()", function() {
6867
})
6968

7069
test(".data(String) and .data(String, Object)", function() {
71-
expect(23);
70+
expect(25);
7271
var div = jQuery("<div/>");
7372

7473
ok( div.data("test") === undefined, "Check for no data exists" );
@@ -125,32 +124,48 @@ test(".data(String) and .data(String, Object)", function() {
125124
equals( div.data("test.bar"), "testroot", "Check for unmatched namespace" );
126125

127126
// #3748
128-
var $elem = jQuery({});
129-
equals( $elem.data('nothing'), undefined, "Non-existent data returns undefined");
130-
equals( $elem.data('null',null).data('null'), null, "null's are preserved");
131-
equals( $elem.data('emptyString','').data('emptyString'), '', "Empty strings are preserved");
132-
equals( $elem.data('false',false).data('false'), false, "false's are preserved");
127+
var $elem = jQuery({exists:true});
128+
equals( $elem.data('nothing'), undefined, "Non-existent data returns undefined" );
129+
equals( $elem.data('null',null).data('null'), null, "null's are preserved" );
130+
equals( $elem.data('emptyString','').data('emptyString'), '', "Empty strings are preserved" );
131+
equals( $elem.data('false',false).data('false'), false, "false's are preserved" );
133132

133+
equals( $elem.data('exists'), true, "Existing data is returned" );
134+
134135
// Clean up
135136
$elem.removeData();
137+
ok( jQuery.isEmptyObject( $elem[0] ), "removeData clears the object" );
136138
});
137139

138140
test(".data(Object)", function() {
139-
expect(2);
141+
expect(4);
140142

141143
var div = jQuery("<div/>");
142144

143145
div.data({ "test": "in", "test2": "in2" });
144-
equals( div.data("test"), "in", "Verify setting an object in data." );
145-
equals( div.data("test2"), "in2", "Verify setting an object in data." );
146+
equals( div.data("test"), "in", "Verify setting an object in data" );
147+
equals( div.data("test2"), "in2", "Verify setting an object in data" );
148+
149+
var obj = {test:"unset"},
150+
jqobj = jQuery(obj);
151+
jqobj.data({ "test": "in", "test2": "in2" });
152+
equals( obj.test, "in", "Verify setting an object on an object extends the object" );
153+
equals( obj.test2, "in2", "Verify setting an object on an object extends the object" );
146154
});
147155

148156
test("jQuery.removeData", function() {
149-
expect(1);
157+
expect(4);
150158
var div = jQuery("#foo")[0];
151159
jQuery.data(div, "test", "testing");
152160
jQuery.removeData(div, "test");
153161
equals( jQuery.data(div, "test"), undefined, "Check removal of data" );
162+
163+
var obj = {};
164+
jQuery.data(obj, "test", "testing");
165+
equals( obj.test, "testing", "verify data on plain object");
166+
jQuery.removeData(obj, "test");
167+
equals( jQuery.data(obj, "test"), undefined, "Check removal of data on plain object" );
168+
equals( obj.test, undefined, "Check removal of data directly from plain object" );
154169
});
155170

156171
test(".removeData()", function() {

test/unit/event.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ test("bind(name, false), unbind(name, false)", function() {
404404
});
405405

406406
test("bind()/trigger()/unbind() on plain object", function() {
407-
expect( 2 );
407+
expect( 4 );
408408

409409
var obj = {};
410410

@@ -418,7 +418,10 @@ test("bind()/trigger()/unbind() on plain object", function() {
418418
ok( true, "Custom event run." );
419419
});
420420

421-
ok( jQuery(obj).data("events"), "Object has events bound." );
421+
var events = jQuery(obj).data("events");
422+
ok( events, "Object has events bound." );
423+
equals( typeof events, "function", "'events' expando is a function on plain objects." );
424+
ok( events(), "'events' expando is a function on plain objects which returns a container." );
422425

423426
// Should trigger 1
424427
jQuery(obj).trigger("test");

0 commit comments

Comments
 (0)