]> source.dussan.org Git - jquery.git/commitdiff
Data: always camelCase keys in .data()
authorTimmy Willison <timmywillisn@gmail.com>
Mon, 4 May 2015 13:36:58 +0000 (09:36 -0400)
committerTimmy Willison <timmywillisn@gmail.com>
Mon, 4 May 2015 14:41:44 +0000 (10:41 -0400)
- This effectively implements our "Embrace HTML5" option
- Related: http://goo.gl/GcQAtn

Fixes gh-2257

src/data.js
test/unit/data.js

index da2b01b5db7d676ae05ebd03476c604506501aee..30acce094f66584cca081c421cc1e25b1e5c3b9f 100644 (file)
@@ -41,14 +41,14 @@ function dataAttr( elem, key, data ) {
 
 // checks a cache object for emptiness
 function isEmptyDataObject( obj ) {
-       var name;
-       for ( name in obj ) {
+       var key;
+       for ( key in obj ) {
 
                // if the public data object is empty, the private is still empty
-               if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+               if ( key === "data" && jQuery.isEmptyObject( obj[ key ] ) ) {
                        continue;
                }
-               if ( name !== "toJSON" ) {
+               if ( key !== "toJSON" ) {
                        return false;
                }
        }
@@ -56,12 +56,12 @@ function isEmptyDataObject( obj ) {
        return true;
 }
 
-function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
+function internalData( elem, key, data, pvt /* Internal Use Only */ ) {
        if ( !jQuery.acceptData( elem ) ) {
                return;
        }
 
-       var ret, thisCache,
+       var thisCache, prop,
                internalKey = jQuery.expando,
 
                // We have to handle DOM nodes and JS objects differently because IE6-7
@@ -79,7 +79,7 @@ function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
        // Avoid doing any more work than we need to when trying to get data on an
        // object that has no data at all
        if ( (!id || !cache[id] || (!pvt && !cache[id].data)) &&
-               data === undefined && typeof name === "string" ) {
+               data === undefined && typeof key === "string" ) {
                return;
        }
 
@@ -99,16 +99,6 @@ function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
                cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
        }
 
-       // 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" || typeof name === "function" ) {
-               if ( pvt ) {
-                       cache[ id ] = jQuery.extend( cache[ id ], name );
-               } else {
-                       cache[ id ].data = jQuery.extend( cache[ id ].data, name );
-               }
-       }
-
        thisCache = cache[ id ];
 
        // jQuery data() is stored in a separate object inside the object's internal data
@@ -122,31 +112,28 @@ function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
                thisCache = thisCache.data;
        }
 
-       if ( data !== undefined ) {
-               thisCache[ jQuery.camelCase( name ) ] = data;
+       // 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 key === "object" || typeof key === "function" ) {
+               for ( prop in key ) {
+                       thisCache[ jQuery.camelCase( prop ) ] = key[ prop ];
+               }
+               // Stop here, ignore other arguments
+               return thisCache;
        }
 
-       // Check for both converted-to-camel and non-converted data property names
-       // If a data property was specified
-       if ( typeof name === "string" ) {
-
-               // First Try to find as-is property data
-               ret = thisCache[ name ];
-
-               // Test for null|undefined property data
-               if ( ret == null ) {
-
-                       // Try to find the camelCased property
-                       ret = thisCache[ jQuery.camelCase( name ) ];
-               }
-       } else {
-               ret = thisCache;
+       if ( data !== undefined ) {
+               return thisCache[ jQuery.camelCase( key ) ] = data;
        }
 
-       return ret;
+       // We always set camelCased properties (gh-2257)
+       return typeof key === "string" ?
+               thisCache[ jQuery.camelCase( key ) ] :
+               // Return the whole cache if no key was specified
+               thisCache;
 }
 
-function internalRemoveData( elem, name, pvt ) {
+function internalRemoveData( elem, key, pvt ) {
        if ( !jQuery.acceptData( elem ) ) {
                return;
        }
@@ -164,41 +151,29 @@ function internalRemoveData( elem, name, pvt ) {
                return;
        }
 
-       if ( name ) {
+       if ( key ) {
 
                thisCache = pvt ? cache[ id ] : cache[ id ].data;
 
                if ( thisCache ) {
 
-                       // Support array or space separated string names for data keys
-                       if ( !jQuery.isArray( name ) ) {
-
-                               // try the string as a key before any manipulation
-                               if ( name in thisCache ) {
-                                       name = [ name ];
-                               } else {
+                       // Support array or space separated string keys for data keys
+                       if ( jQuery.isArray( key ) ) {
 
-                                       // split the camel cased version by spaces unless a key with the spaces exists
-                                       name = jQuery.camelCase( name );
-                                       if ( name in thisCache ) {
-                                               name = [ name ];
-                                       } else {
-                                               name = name.split(" ");
-                                       }
-                               }
+                               // If "key" is an array of keys...
+                               // We always use camelCased keys (gh-2257)
+                               key = jQuery.map( key, jQuery.camelCase );
                        } else {
-                               // If "name" is an array of keys...
-                               // When data is initially created, via ("key", "val") signature,
-                               // keys will be converted to camelCase.
-                               // Since there is no way to tell _how_ a key was added, remove
-                               // both plain key and camelCase key. #12786
-                               // This will only penalize the array argument path.
-                               name = name.concat( jQuery.map( name, jQuery.camelCase ) );
+
+                               // split the camel cased version by spaces
+                               // unless a key with the spaces exists
+                               key = jQuery.camelCase( key );
+                               key = key in thisCache ? [ key ] : key.split( " " );
                        }
 
-                       i = name.length;
+                       i = key.length;
                        while ( i-- ) {
-                               delete thisCache[ name[i] ];
+                               delete thisCache[ key[ i ] ];
                        }
 
                        // If there is no data left in the cache, we want to continue
index 98d714eb63d216e14d33ca882895520676d07b05..b691bd1ba5d5db14548dae15d7862c13b0bc7eb7 100644 (file)
@@ -534,8 +534,65 @@ test("jQuery.data should not miss data with preset hyphenated property names", f
        });
 });
 
-test("jQuery.data supports interoperable hyphenated/camelCase get/set of properties with arbitrary non-null|NaN|undefined values", function() {
+test(".data should not miss attr() set data-* with hyphenated property names", function() {
+       expect(2);
+
+       var a, b;
+
+       a = jQuery("<div/>").appendTo("#qunit-fixture");
+
+       a.attr( "data-long-param", "test" );
+       a.data( "long-param", { a: 2 });
+
+       deepEqual( a.data("long-param"), { a: 2 }, "data with property long-param was found, 1" );
+
+       b = jQuery("<div/>").appendTo("#qunit-fixture");
+
+       b.attr( "data-long-param", "test" );
+       b.data( "long-param" );
+       b.data( "long-param", { a: 2 });
+
+       deepEqual( b.data("long-param"), { a: 2 }, "data with property long-param was found, 2" );
+});
+
+test(".data always sets data with the camelCased key (gh-2257)", function() {
+       expect( 36 );
+
+       var div = jQuery("<div>").appendTo("#qunit-fixture"),
+               datas = {
+                       "non-empty": "a string",
+                       "empty-string": "",
+                       "one-value": 1,
+                       "zero-value": 0,
+                       "an-array": [],
+                       "an-object": {},
+                       "bool-true": true,
+                       "bool-false": false,
+
+                       // JSHint enforces double quotes,
+                       // but JSON strings need double quotes to parse
+                       // so we need escaped double quotes here
+                       "some-json": "{ \"foo\": \"bar\" }"
+               };
+
+       jQuery.each( datas, function( key, val ) {
+               div.data( key, val );
+               var allData = div.data();
+               equal( allData[ key ], undefined, ".data(key, val) does not store with hyphenated keys" );
+               equal( allData[ jQuery.camelCase( key ) ], val, ".data(key, val) stores the camelCased key" );
+       });
 
+       div.removeData();
+
+       div.data( datas );
+       jQuery.each( datas, function( key, val ) {
+               var allData = div.data();
+               equal( allData[ key ], undefined, ".data(object) does not store with hyphenated keys" );
+               equal( allData[ jQuery.camelCase( key ) ], val, ".data(object) stores the camelCased key" );
+       });
+});
+
+test(".data supports interoperable hyphenated/camelCase get/set of properties with arbitrary non-null|NaN|undefined values", function() {
        var div = jQuery("<div/>", { id: "hyphened" }).appendTo("#qunit-fixture"),
                datas = {
                        "non-empty": "a string",
@@ -597,7 +654,36 @@ test("jQuery.data supports interoperable removal of hyphenated/camelCase propert
        });
 });
 
-test( ".removeData supports removal of hyphenated properties via array (#12786)", function() {
+test(".data supports interoperable removal of properties SET TWICE #13850", function() {
+       var div = jQuery("<div>").appendTo("#qunit-fixture"),
+               datas = {
+                       "non-empty": "a string",
+                       "empty-string": "",
+                       "one-value": 1,
+                       "zero-value": 0,
+                       "an-array": [],
+                       "an-object": {},
+                       "bool-true": true,
+                       "bool-false": false,
+                       // JSHint enforces double quotes,
+                       // but JSON strings need double quotes to parse
+                       // so we need escaped double quotes here
+                       "some-json": "{ \"foo\": \"bar\" }"
+               };
+
+       expect( 9 );
+
+       jQuery.each( datas, function( key, val ) {
+               div.data( key, val );
+               div.data( key, val );
+
+               div.removeData( key );
+
+               equal( div.data( key ), undefined, "removal: " + key );
+       });
+});
+
+test( ".removeData supports removal of hyphenated properties via array (#12786, gh-2257)", function() {
        expect( 4 );
 
        var div, plain, compare;
@@ -605,11 +691,10 @@ test( ".removeData supports removal of hyphenated properties via array (#12786)"
        div = jQuery("<div>").appendTo("#qunit-fixture");
        plain = jQuery({});
 
-       // When data is batch assigned (via plain object), the properties
-       // are not camel cased as they are with (property, value) calls
+       // Properties should always be camelCased
        compare = {
                // From batch assignment .data({ "a-a": 1 })
-               "a-a": 1,
+               "aA": 1,
                // From property, value assignment .data( "b-b", 1 )
                "bB": 1
        };
@@ -624,7 +709,6 @@ test( ".removeData supports removal of hyphenated properties via array (#12786)"
        div.removeData([ "a-a", "b-b" ]);
        plain.removeData([ "a-a", "b-b" ]);
 
-       // NOTE: Timo's proposal for "propEqual" (or similar) would be nice here
        deepEqual( div.data(), {}, "Data is empty. (div)" );
        deepEqual( plain.data(), {}, "Data is empty. (plain)" );
 });