Skip to content

Commit 5c1b81f

Browse files
committed
Data: Patch $._data & $._removeData as well, add more tests
Also, extract the util to patch a prototype as we'll need it in the event module as well.
1 parent ced329a commit 5c1b81f

File tree

3 files changed

+196
-109
lines changed

3 files changed

+196
-109
lines changed

src/jquery/data.js

+47-75
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import { migratePatchFunc, migrateWarn } from "../main.js";
2-
import { camelCase } from "../utils.js";
2+
import { camelCase, patchProto } from "../utils.js";
33

44
var rmultiDash = /[A-Z]/g,
55
rnothtmlwhite = /[^\x20\t\r\n\f]+/g,
6-
origJQueryData = jQuery.data;
6+
origJQueryData = jQuery.data,
7+
origJQueryPrivateData = jQuery._data;
78

89
function unCamelCase( str ) {
910
return str.replace( rmultiDash, "-$&" ).toLowerCase();
1011
}
1112

1213
function patchDataCamelCase( origData, options ) {
1314
var apiName = options.apiName,
14-
isInstanceMethod = options.isInstanceMethod;
15+
isPrivateData = options.isPrivateData,
16+
isInstanceMethod = options.isInstanceMethod,
17+
origJQueryStaticData = isPrivateData ? origJQueryPrivateData : origJQueryData;
1518

1619
function objectSetter( elem, obj ) {
1720
var curData, key;
@@ -23,7 +26,7 @@ function patchDataCamelCase( origData, options ) {
2326

2427
// Don't use the instance method here to avoid `data-*` attributes
2528
// detection this early.
26-
curData = origJQueryData( elem );
29+
curData = origJQueryStaticData( elem );
2730

2831
for ( key in obj ) {
2932
if ( key !== camelCase( key ) ) {
@@ -56,7 +59,7 @@ function patchDataCamelCase( origData, options ) {
5659

5760
// Don't use the instance method here to avoid `data-*` attributes
5861
// detection this early.
59-
curData = origJQueryData( elem );
62+
curData = origJQueryStaticData( elem );
6063

6164
if ( curData && name in curData ) {
6265
migrateWarn( "data-camelCase",
@@ -66,7 +69,7 @@ function patchDataCamelCase( origData, options ) {
6669
curData[ name ] = value;
6770
}
6871

69-
origJQueryData( elem, name, value );
72+
origJQueryStaticData( elem, name, value );
7073

7174
// Since the "set" path can have two possible entry points
7275
// return the expected data based on which path was taken.
@@ -124,7 +127,7 @@ function patchDataCamelCase( origData, options ) {
124127

125128
// Don't use the instance method here to avoid `data-*` attributes
126129
// detection this early.
127-
curData = origJQueryData( elem );
130+
curData = origJQueryStaticData( elem );
128131

129132
if ( curData && name in curData ) {
130133
migrateWarn( "data-camelCase",
@@ -139,11 +142,13 @@ function patchDataCamelCase( origData, options ) {
139142
}
140143

141144
function patchRemoveDataCamelCase( origRemoveData, options ) {
142-
var isInstanceMethod = options.isInstanceMethod;
145+
var isPrivateData = options.isPrivateData,
146+
isInstanceMethod = options.isInstanceMethod,
147+
origJQueryStaticData = isPrivateData ? origJQueryPrivateData : origJQueryData;
143148

144149
function remove( elem, keys ) {
145150
var i, singleKey, unCamelCasedKeys,
146-
curData = jQuery.data( elem );
151+
curData = origJQueryStaticData( elem );
147152

148153
if ( keys === undefined ) {
149154
origRemoveData( elem );
@@ -223,104 +228,64 @@ function patchRemoveDataCamelCase( origRemoveData, options ) {
223228
migratePatchFunc( jQuery, "data",
224229
patchDataCamelCase( jQuery.data, {
225230
apiName: "jQuery.data()",
231+
isPrivateData: false,
232+
isInstanceMethod: false
233+
} ),
234+
"data-camelCase" );
235+
migratePatchFunc( jQuery, "_data",
236+
patchDataCamelCase( jQuery._data, {
237+
apiName: "jQuery._data()",
238+
isPrivateData: true,
226239
isInstanceMethod: false
227240
} ),
228241
"data-camelCase" );
229242
migratePatchFunc( jQuery.fn, "data",
230243
patchDataCamelCase( jQuery.fn.data, {
231244
apiName: "jQuery.fn.data()",
245+
isPrivateData: false,
232246
isInstanceMethod: true
233247
} ),
234248
"data-camelCase" );
235249

236250
migratePatchFunc( jQuery, "removeData",
237251
patchRemoveDataCamelCase( jQuery.removeData, {
252+
isPrivateData: false,
253+
isInstanceMethod: false
254+
} ),
255+
"data-camelCase" );
256+
migratePatchFunc( jQuery, "_removeData",
257+
patchRemoveDataCamelCase( jQuery._removeData, {
258+
isPrivateData: true,
238259
isInstanceMethod: false
239260
} ),
240261
"data-camelCase" );
241-
242262
migratePatchFunc( jQuery.fn, "removeData",
243263

244264
// No, it's not a typo - we're intentionally passing
245265
// the static method here as we need something working on
246266
// a single element.
247267
patchRemoveDataCamelCase( jQuery.removeData, {
268+
isPrivateData: false,
248269
isInstanceMethod: true
249270
} ),
250271
"data-camelCase" );
251272

252-
253273
function patchDataProto( original, options ) {
254-
255-
// Support: IE 9 - 10 only, iOS 7 - 8 only
256-
// Older IE doesn't have a way to change an existing prototype.
257-
// Just return the original method there.
258-
// Older WebKit supports `__proto__` but not `Object.setPrototypeOf`.
259-
// To avoid complicating code, don't patch the API there either.
260-
if ( !Object.setPrototypeOf ) {
261-
return original;
262-
}
263-
264-
var i,
274+
var warningId = options.warningId,
265275
apiName = options.apiName,
266-
isInstanceMethod = options.isInstanceMethod,
267-
268-
// `Object.prototype` keys are not enumerable so list the
269-
// official ones here. An alternative would be wrapping
270-
// data objects with a Proxy but that creates additional issues
271-
// like breaking object identity on subsequent calls.
272-
objProtoKeys = [
273-
"__proto__",
274-
"__defineGetter__",
275-
"__defineSetter__",
276-
"__lookupGetter__",
277-
"__lookupSetter__",
278-
"hasOwnProperty",
279-
"isPrototypeOf",
280-
"propertyIsEnumerable",
281-
"toLocaleString",
282-
"toString",
283-
"valueOf"
284-
],
285-
286-
// Use a null prototype at the beginning so that we can define our
287-
// `__proto__` getter & setter. We'll reset the prototype afterwards.
288-
intermediateDataObj = Object.create( null );
289-
290-
for ( i = 0; i < objProtoKeys.length; i++ ) {
291-
( function( key ) {
292-
Object.defineProperty( intermediateDataObj, key, {
293-
get: function() {
294-
migrateWarn( "data-null-proto",
295-
"Accessing properties from " + apiName +
296-
" inherited from Object.prototype is deprecated" );
297-
return ( key + "__cache" ) in intermediateDataObj ?
298-
intermediateDataObj[ key + "__cache" ] :
299-
Object.prototype[ key ];
300-
},
301-
set: function( value ) {
302-
migrateWarn( "data-null-proto",
303-
"Setting properties from " + apiName +
304-
" inherited from Object.prototype is deprecated" );
305-
intermediateDataObj[ key + "__cache" ] = value;
306-
}
307-
} );
308-
} )( objProtoKeys[ i ] );
309-
}
310-
311-
Object.setPrototypeOf( intermediateDataObj, Object.prototype );
276+
isInstanceMethod = options.isInstanceMethod;
312277

313-
return function jQueryDataProtoPatched() {
278+
return function apiWithProtoPatched() {
314279
var result = original.apply( this, arguments );
315280

316281
if ( arguments.length !== ( isInstanceMethod ? 0 : 1 ) || result === undefined ) {
317282
return result;
318283
}
319284

320-
// Insert an additional object in the prototype chain between `result`
321-
// and `Object.prototype`; that intermediate object proxies properties
322-
// to `Object.prototype`, warning about their usage first.
323-
Object.setPrototypeOf( result, intermediateDataObj );
285+
patchProto( result, {
286+
warningId: warningId,
287+
apiName: apiName
288+
} );
324289

325290
return result;
326291
};
@@ -330,15 +295,22 @@ function patchDataProto( original, options ) {
330295
// so that each of the two patches can be independently disabled.
331296
migratePatchFunc( jQuery, "data",
332297
patchDataProto( jQuery.data, {
298+
warningId: "data-null-proto",
333299
apiName: "jQuery.data()",
334-
isPrivateData: false,
300+
isInstanceMethod: false
301+
} ),
302+
"data-null-proto" );
303+
migratePatchFunc( jQuery, "_data",
304+
patchDataProto( jQuery._data, {
305+
warningId: "data-null-proto",
306+
apiName: "jQuery._data()",
335307
isInstanceMethod: false
336308
} ),
337309
"data-null-proto" );
338310
migratePatchFunc( jQuery.fn, "data",
339311
patchDataProto( jQuery.fn.data, {
312+
warningId: "data-null-proto",
340313
apiName: "jQuery.fn.data()",
341-
isPrivateData: true,
342314
isInstanceMethod: true
343315
} ),
344316
"data-null-proto" );

src/utils.js

+69
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,74 @@
1+
import { migrateWarn } from "./main.js";
2+
13
export function camelCase( string ) {
24
return string.replace( /-([a-z])/g, function( _, letter ) {
35
return letter.toUpperCase();
46
} );
57
}
8+
9+
// Insert an additional object in the prototype chain between `objrvy`
10+
// and `Object.prototype`; that intermediate object proxies properties
11+
// to `Object.prototype`, warning about their usage first.
12+
export function patchProto( object, options ) {
13+
14+
// Support: IE 9 - 10 only, iOS 7 - 8 only
15+
// Older IE doesn't have a way to change an existing prototype.
16+
// Just return the original method there.
17+
// Older WebKit supports `__proto__` but not `Object.setPrototypeOf`.
18+
// To avoid complicating code, don't patch the API there either.
19+
if ( !Object.setPrototypeOf ) {
20+
return object;
21+
}
22+
23+
var i,
24+
warningId = options.warningId,
25+
apiName = options.apiName,
26+
27+
// `Object.prototype` keys are not enumerable so list the
28+
// official ones here. An alternative would be wrapping
29+
// objects with a Proxy but that creates additional issues
30+
// like breaking object identity on subsequent calls.
31+
objProtoKeys = [
32+
"__proto__",
33+
"__defineGetter__",
34+
"__defineSetter__",
35+
"__lookupGetter__",
36+
"__lookupSetter__",
37+
"hasOwnProperty",
38+
"isPrototypeOf",
39+
"propertyIsEnumerable",
40+
"toLocaleString",
41+
"toString",
42+
"valueOf"
43+
],
44+
45+
// Use a null prototype at the beginning so that we can define our
46+
// `__proto__` getter & setter. We'll reset the prototype afterward.
47+
intermediateObj = Object.create( null );
48+
49+
for ( i = 0; i < objProtoKeys.length; i++ ) {
50+
( function( key ) {
51+
Object.defineProperty( intermediateObj, key, {
52+
get: function() {
53+
migrateWarn( warningId,
54+
"Accessing properties from " + apiName +
55+
" inherited from Object.prototype is deprecated" );
56+
return ( key + "__cache" ) in intermediateObj ?
57+
intermediateObj[ key + "__cache" ] :
58+
Object.prototype[ key ];
59+
},
60+
set: function( value ) {
61+
migrateWarn( warningId,
62+
"Setting properties from " + apiName +
63+
" inherited from Object.prototype is deprecated" );
64+
intermediateObj[ key + "__cache" ] = value;
65+
}
66+
} );
67+
} )( objProtoKeys[ i ] );
68+
}
69+
70+
Object.setPrototypeOf( intermediateObj, Object.prototype );
71+
Object.setPrototypeOf( object, intermediateObj );
72+
73+
return object;
74+
}

0 commit comments

Comments
 (0)