Replaces "text in-between" technique with a full-fledged one-level transitive search for converters (unit tests added). Also cleans up auto dataType determination and adds converter checks in order to guess the best dataType possible.

This commit is contained in:
jaubourg 2011-01-21 03:58:28 +01:00
parent 2e2d5e9db5
commit dc2e7317a9
2 changed files with 107 additions and 39 deletions

View File

@ -372,8 +372,9 @@ jQuery.extend({
clearTimeout(timeoutTimer); clearTimeout(timeoutTimer);
} }
var // Reference dataTypes and responseFields var // Reference dataTypes, converters and responseFields
dataTypes = s.dataTypes, dataTypes = s.dataTypes,
converters = s.converters,
responseFields = s.responseFields, responseFields = s.responseFields,
responseField, responseField,
@ -400,44 +401,64 @@ jQuery.extend({
transportDataType = dataTypes[0], transportDataType = dataTypes[0],
ct, ct,
type, type,
finalDataType; finalDataType,
firstDataType;
// Auto (xml, json, script or text determined given headers) // Auto (xml, json, script or text determined given headers)
if ( transportDataType === "*" && ( ct = jXHR.getResponseHeader( "content-type" ) ) ) { if ( transportDataType === "*" ) {
// Remove all auto types
while( dataTypes[0] === "*" ) {
dataTypes.shift();
}
transportDataTypes = dataTypes[0];
// Get content type
ct = jXHR.getResponseHeader( "content-type" );
// Check if it's a known type
for ( type in contents ) { for ( type in contents ) {
if ( contents[ type ] && contents[ type ].test( ct ) ) { if ( contents[ type ] && contents[ type ].test( ct ) ) {
transportDataType = dataTypes[0] = type; dataTypes.unshift( ( transportDataType = type ) );
break; break;
} }
} }
type = undefined;
} }
// Get final dataType // Check to see if we have a response for the expected dataType
for( type in responses ) { if ( transportDataType in responses ) {
if ( ! finalDataType && type === transportDataType ) { finalDataType = transportDataType;
} else {
// Try convertible dataTypes
for ( type in responses ) {
if ( ! firstDataType ) {
firstDataType = type;
}
if ( ! transportDataType || converters[ type + " " + transportDataType ] ) {
finalDataType = type; finalDataType = type;
break;
} }
responseField = responseFields[ type ];
if ( responseField && ! ( responseField in jXHR ) ) {
jXHR[ responseField ] = responses[ type ];
} }
// Or just use first one
finalDataType = finalDataType || firstDataType;
} }
// If no response with the expected dataType was provided // If we found a dataType
// Take the last response as a default if it exists // We get the corresponding response
if ( ! finalDataType && type ) { // and add the dataType to the list if needed
finalDataType = type; if ( finalDataType ) {
if ( transportDataType === "*" ) { response = responses[ finalDataType ];
dataTypes.shift(); if ( finalDataType !== transportDataType ) {
}
dataTypes.unshift( finalDataType ); dataTypes.unshift( finalDataType );
} }
}
// Get final response // Fill responseXXX fields
response = responses[ finalDataType ]; for( type in responseFields ) {
if ( type in responses ) {
jXHR[ responseFields[ type ] ] = responses[ type ];
}
}
} }
// If successful, handle type chaining // If successful, handle type chaining
@ -473,6 +494,7 @@ jQuery.extend({
try { try {
var i, var i,
tmp,
// Current dataType // Current dataType
current, current,
// Previous dataType // Previous dataType
@ -483,9 +505,7 @@ jQuery.extend({
conv, conv,
// Conversion functions (when text is used in-between) // Conversion functions (when text is used in-between)
conv1, conv1,
conv2, conv2;
// Local references to converters
converters = s.converters;
// For each dataType in the chain // For each dataType in the chain
for( i = 0 ; i < dataTypes.length ; i++ ) { for( i = 0 ; i < dataTypes.length ; i++ ) {
@ -505,27 +525,32 @@ jQuery.extend({
// Get the dataType to convert from // Get the dataType to convert from
prev = dataTypes[ i - 1 ]; prev = dataTypes[ i - 1 ];
// If no catch-all and dataTypes are actually different // If no auto and dataTypes are actually different
if ( prev !== "*" && current !== "*" && prev !== current ) { if ( prev !== "*" && current !== "*" && prev !== current ) {
// Get the converter // Get the converter
conversion = prev + " " + current; conversion = prev + " " + current;
conv = converters[ conversion ] || converters[ "* " + current ]; conv = converters[ conversion ] || converters[ "* " + current ];
conv1 = conv2 = 0; // If there is no direct converter, search transitively
if ( ! conv ) {
conv1 = conv2 = undefined;
// If there is no direct converter and none of the dataTypes is text for( conv1 in converters ) {
if ( ! conv && prev !== "text" && current !== "text" ) { tmp = conv1.split( " " );
// Try with text in-between if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
conv1 = converters[ prev + " text" ] || converters[ "* text" ]; conv2 = converters[ tmp[ 1 ] + " " + current ];
conv2 = converters[ "text " + current ]; if ( conv2 ) {
// Revert back to a single converter conv1 = converters[ conv1 ];
// if one of the converter is an equivalence
if ( conv1 === true ) { if ( conv1 === true ) {
conv = conv2; conv = conv2;
} else if ( conv2 === true ) { } else if ( conv2 === true ) {
conv = conv1; conv = conv1;
} }
break;
}
}
}
} }
// If we found no converter, dispatch an error // If we found no converter, dispatch an error
if ( ! ( conv || conv1 && conv2 ) ) { if ( ! ( conv || conv1 && conv2 ) ) {

View File

@ -2006,6 +2006,49 @@ test( "jQuery.ajax - statusCode" , function() {
}); });
}); });
test("jQuery.ajax - transitive conversions", function() {
expect( 8 );
stop();
jQuery.when(
jQuery.ajax( url("data/json.php") , {
converters: {
"json myjson": function( data ) {
ok( true , "converter called" );
return data;
}
},
dataType: "myjson",
success: function() {
ok( true , "Transitive conversion worked" );
strictEqual( this.dataTypes[0] , "text" , "response was retrieved as text" );
strictEqual( this.dataTypes[1] , "myjson" , "request expected myjson dataType" );
}
}),
jQuery.ajax( url("data/json.php") , {
converters: {
"json myjson": function( data ) {
ok( true , "converter called (*)" );
return data;
}
},
contents: false, /* headers are wrong so we ignore them */
dataType: "* myjson",
success: function() {
ok( true , "Transitive conversion worked (*)" );
strictEqual( this.dataTypes[0] , "text" , "response was retrieved as text (*)" );
strictEqual( this.dataTypes[1] , "myjson" , "request expected myjson dataType (*)" );
}
})
).then( start , start );
});
test("jQuery.ajax - active counter", function() { test("jQuery.ajax - active counter", function() {
ok( jQuery.active == 0, "ajax active counter should be zero: " + jQuery.active ); ok( jQuery.active == 0, "ajax active counter should be zero: " + jQuery.active );
}); });