From 1db2f02cc78b10bd638d70457645e1daa5c01344 Mon Sep 17 00:00:00 2001
From: jaubourg <aubourg.julian@gmail.com>
Date: Tue, 21 Dec 2010 16:58:52 +0100
Subject: [PATCH] Rewrote the data conversion logic in ajax. Should be cleaner
 and faster.

---
 src/ajax.js |   29 +++++-------
 src/xhr.js  |  151 ++++++++++++++++++++++-------------------------------------
 2 files changed, 69 insertions(+), 111 deletions(-)

diff --git a/src/ajax.js b/src/ajax.js
index 320c13d..a50a039 100644
--- a/src/ajax.js
+++ b/src/ajax.js
@@ -181,12 +181,7 @@ jQuery.extend({
 		xhr: function() {
 			return new window.XMLHttpRequest();
 		},
-		xhrResponseFields: {
-			xml: "XML",
-			text: "Text",
-			json: "JSON"
-		},
-			
+
 		accepts: {
 			xml: "application/xml, text/xml",
 			html: "text/html",
@@ -194,13 +189,13 @@ jQuery.extend({
 			json: "application/json, text/javascript",
 			"*": "*/*"
 		},
-		
+
 		autoDataType: {
 			xml: /xml/,
 			html: /html/,
 			json: /json/
 		},
-		
+
 		// Prefilters
 		// 1) They are useful to introduce custom dataTypes (see transport/jsonp for an example)
 		// 2) These are called:
@@ -210,26 +205,26 @@ jQuery.extend({
 		// 4) the catchall symbol "*" can be used
 		// 5) execution will start with transport dataType and THEN continue down to "*" if needed
 		prefilters: {},
-		
+
 		// Transports bindings
 		// 1) key is the dataType
 		// 2) the catchall symbol "*" can be used
 		// 3) selection will start with transport dataType and THEN go to "*" if needed
 		transports: {},
-		
+
 		// Checkers
 		// 1) key is dataType
 		// 2) they are called to control successful response
 		// 3) error throws is used as error data
 		dataCheckers: {
-	
+
 			// Check if data is a string
 			"text": function(data) {
 				if ( typeof data != "string" ) {
 					jQuery.error("typeerror");
 				}
 			},
-	
+
 			// Check if xml has been properly parsed
 			"xml": function(data) {
 				var documentElement = data ? data.documentElement : data;
@@ -241,25 +236,25 @@ jQuery.extend({
 				}
 			}
 		},
-		
+
 		// List of data converters
 		// 1) key format is "source_type => destination_type" (spaces required)
 		// 2) the catchall symbol "*" can be used for source_type
 		dataConverters: {
-		
+
 			// Convert anything to text
 			"* => text": function(data) {
 				return "" + data;
 			},
-			
+
 			// Text to html (no transformation)
 			"text => html": function(data) {
 				return data;
 			},
-			
+
 			// Evaluate text as a json expression
 			"text => json": jQuery.parseJSON,
-			
+
 			// Parse text as xml
 			"text => xml": function(data) {
 				var xml, parser;
diff --git a/src/xhr.js b/src/xhr.js
index e792e1d..1993e0b 100644
--- a/src/xhr.js
+++ b/src/xhr.js
@@ -7,9 +7,7 @@ var rquery_xhr = /\?/,
 	rts = /([?&])_=[^&]*/,
 	rurl = /^(\w+:)?\/\/([^\/?#]+)/,
 	
-	sliceFunc = Array.prototype.slice,
-	
-	isFunction = jQuery.isFunction;
+	sliceFunc = Array.prototype.slice;
 	
 // Creates a jQuery xhr object
 jQuery.xhr = function( _native ) {
@@ -86,7 +84,7 @@ jQuery.xhr = function( _native ) {
 		if ( data && s.processData && typeof data != "string" ) {
 			data = s.data = jQuery.param( data , s.traditional );
 		}
-		
+
 		// Get internal
 		internal = jQuery.xhr.prefilter( s ).transport( s );
 		
@@ -147,7 +145,7 @@ jQuery.xhr = function( _native ) {
 				requestHeaders[ i.toLowerCase() ] = headers[ i ];
 			}			
 		}
-			
+		
 		callbackContext = s.context || s;
 		globalEventContext = s.context ? jQuery(s.context) : jQuery.event;
 		
@@ -224,111 +222,76 @@ jQuery.xhr = function( _native ) {
 				// (if an exception is thrown in the process, it'll be notified as an error)
 				try {
 					
-					function checkData(data) {
-						if ( data !== undefined ) {
-							var testFunction = s.dataCheckers[srcDataType];
-							if ( isFunction( testFunction ) ) {
-								testFunction(data);
-							}
-						}
-					}
-					
-					function convertData (data) {
-						var conversionFunction = dataConverters[srcDataType+" => "+destDataType] ||
-								dataConverters["* => "+destDataType],
-							noFunction = ! isFunction( conversionFunction );
-						if ( noFunction ) {
-							if ( srcDataType != "text" && destDataType != "text" ) {
-								// We try to put text inbetween
-								var first = dataConverters[srcDataType+" => text"] ||
-										dataConverters["* => text"],
-									second = dataConverters["text => "+destDataType] ||
-										dataConverters["* => "+destDataType],
-									areFunctions = isFunction( first ) && isFunction( second );
-								if ( areFunctions ) {
-									conversionFunction = function (data) {
-										return second( first ( data ) );
-									};
-								}
-								noFunction = ! areFunctions;
-							}
-							if ( noFunction ) {
-								jQuery.error( "no data converter between " + srcDataType + " and " + destDataType );
-							}
-							
-						}
-						return conversionFunction(data);
-					}
-					
-					var dataTypes = s.dataTypes,
-						i,
-						length,
-						data = response,
+					var i,
+						current,
+						prev,
+						checker,
+						conv1,
+						conv2,
+						oneConv,
+						convertion,
+						dataTypes = s.dataTypes,
+						dataCheckers = s.dataCheckers,
 						dataConverters = s.dataConverters,
-						srcDataType,
-						destDataType,
-						responseTypes = s.xhrResponseFields;
+						dataFilter = s.dataFilter,
+						responses = {
+							"xml": "XML",
+							"text": "Text"
+						};
+					
+					for( i = 0 ; i < dataTypes.length ; i++ ) {
 						
-					for ( i = 0, length = dataTypes.length ; i < length ; i++ ) {
-	
-						destDataType = dataTypes[i];
+						current = dataTypes[ i ];
 						
-						if ( !srcDataType ) { // First time
+						if ( i ) {
 							
-							// Copy type
-							srcDataType = destDataType;
-							// Check
-							checkData(data);
-							// Apply dataFilter
-							if ( isFunction( s.dataFilter ) ) {
-								data = s.dataFilter(data, s.dataType);
-								// Recheck data
-								checkData(data);
-							}
-							
-						} else { // Subsequent times
+							prev = dataTypes[ i - 1 ];
 							
-							// handle auto
-							// JULIAN: for reasons unknown to me === doesn't work here
-							if (destDataType == "*") {
-	
-								destDataType = srcDataType;
+							if ( prev === "*" ) {
 								
-							} else if ( srcDataType != destDataType ) {
+								prev = current;
 								
-								// Convert
-								data = convertData(data);
-								// Copy type & check
-								srcDataType = destDataType;
-								checkData(data);
+							} else if ( current !== "*" && prev !== current ) {
+							
+								oneConv = conv1 = 
+									dataConverters[ ( conversion = prev + " => " + current ) ] ||
+									dataConverters[ "* => " + current ];
 								
+								if ( ! oneConv && prev !== "text" && current !== "text" ) {
+									conv1 = dataConverters[ prev + " => text" ] || dataConverters[ "* => text" ];
+									conv2 = dataConverters[ "text => " + current ];
+								}
+								if ( oneConv || conv1 && conv2 ) {
+									response = oneConv ? conv1( response ) : conv2( conv1( response ) );
+								} else {
+									throw "no " + conversion;
+								}
 							}
-							
 						}
-	
-						// Copy response into the xhr if it hasn't been already
-						var responseDataType,
-							responseType = responseTypes[srcDataType];
 						
-						if ( responseType ) {
-							
-							responseDataType = srcDataType;
-							
-						} else {
-							
-							responseType = responseTypes[ responseDataType = "text" ];
-							
+						checker = dataCheckers[ current ];
+						
+						if ( response != null && checker ) {
+							checker( response );
 						}
-							
-						if ( responseType !== 1 ) {
-							xhr[ "response" + responseType ] = data;
-							responseTypes[ responseType ] = 1;
+						
+						if ( responses[ current ] ) {
+							xhr[ "response" + responses[ current ] ] = response;
+							responses[ current ] = 0;
 						}
 						
+						if ( ! i && dataFilter ) {
+							
+							response = dataFilter( response );
+							
+							dataTypes = s.dataTypes;
+							dataFilter = 0;
+							i--;
+						}
 					}
 	
 					// We have a real success
-					success = data;
+					success = response;
 					isSuccess = 1;
 					
 				} catch(e) {
@@ -406,7 +369,7 @@ jQuery.xhr = function( _native ) {
 	// Ready state change
 	function setState( value ) {
 		xhr.readyState = value;
-		if ( isFunction( xhr.onreadystatechange ) ) {
+		if ( jQuery.isFunction( xhr.onreadystatechange ) ) {
 			xhr.onreadystatechange();
 		}
 	}
-- 
1.7.10.4