rts = /([?&])_=[^&]*/,
rurl = /^(\w+:)?\/\/([^\/?#]+)/,
- sliceFunc = Array.prototype.slice,
-
- isFunction = jQuery.isFunction;
+ sliceFunc = Array.prototype.slice;
// Creates a jQuery xhr object
jQuery.xhr = function( _native ) {
return jQuery.ajaxSettings.xhr();
}
- function reset(force) {
+ function reset( force ) {
// We only need to reset if we went through the init phase
// (with the exception of object creation)
if ( force || internal ) {
-
+
// Reset callbacks lists
- callbacksLists = {
- success: createCBList(),
- error: createCBList(),
- complete: createCBList()
- };
+ deferred = jQuery.Deferred();
+ completeDeferred = jQuery._Deferred();
+
+ xhr.success = xhr.then = deferred.then;
+ xhr.error = xhr.fail = deferred.fail;
+ xhr.complete = completeDeferred.then;
// Reset private variables
requestHeaders = {};
if ( data && s.processData && typeof data != "string" ) {
data = s.data = jQuery.param( data , s.traditional );
}
-
- // Apply option prefilters
- for (i in prefilters) {
- prefilters[i](s);
- }
-
+
// Get internal
- internal = selectTransport( s );
+ internal = jQuery.xhr.prefilter( s ).transport( s );
// Re-actualize url & data
url = s.url;
requestHeaders[ i.toLowerCase() ] = headers[ i ];
}
}
-
+
callbackContext = s.context || s;
globalEventContext = s.context ? jQuery(s.context) : jQuery.event;
- for ( i in callbacksLists ) {
- callbacksLists[i].bind(s[i]);
+ for ( i in { success:1, error:1, complete:1 } ) {
+ xhr[ i ]( s[ i ] );
}
// Watch for a new set of requests
// (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
-
- // Copy type
- srcDataType = destDataType;
- // Check
- checkData(data);
- // Apply dataFilter
- if ( isFunction( s.dataFilter ) ) {
- data = s.dataFilter(data, s.dataType);
- // Recheck data
- checkData(data);
- }
+ if ( i ) {
- } 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 ];
+ console.log( conversion );
+
+ 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) {
// Keep local copies of vars in case callbacks re-use the xhr
var _s = s,
- _callbacksLists = callbacksLists,
+ _deferred = deferred,
+ _completeDeferred = completeDeferred,
_callbackContext = callbackContext,
_globalEventContext = globalEventContext;
+
// Set state if the xhr hasn't been re-used
function _setState( value ) {
if ( xhr.readyState && s === _s ) {
// We're done
_setState( 4 );
- // Success
- _callbacksLists.success.fire( isSuccess , _callbackContext , success, statusText, xhr);
- if ( isSuccess && _s.global ) {
- _globalEventContext.trigger( "ajaxSuccess", [xhr, _s, success] );
+ // Success/Error
+ if ( isSuccess ) {
+ _deferred.fire( _callbackContext , [ success , statusText , xhr ] );
+ } else {
+ _deferred.fireReject( _callbackContext , [ xhr , statusText , error ] );
}
- // Error
- _callbacksLists.error.fire( ! isSuccess , _callbackContext , xhr, statusText, error);
- if ( !isSuccess && _s.global ) {
- _globalEventContext.trigger( "ajaxError", [xhr, _s, error] );
+
+ if ( _s.global ) {
+ _globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ) , [ xhr , _s , isSuccess ? success : error ] );
}
+
// Complete
- _callbacksLists.complete.fire( 1 , _callbackContext, xhr, statusText);
+ _completeDeferred.fire( _callbackContext, [ xhr , statusText ] );
+
if ( _s.global ) {
_globalEventContext.trigger( "ajaxComplete", [xhr, _s] );
// Handle the global AJAX counter
// Ready state change
function setState( value ) {
xhr.readyState = value;
- if ( isFunction( xhr.onreadystatechange ) ) {
+ if ( jQuery.isFunction( xhr.onreadystatechange ) ) {
xhr.onreadystatechange();
}
}
// Callback stuff
callbackContext,
globalEventContext,
- callbacksLists,
+ deferred,
+ completeDeferred,
// Headers (they are sent all at once)
requestHeaders,
// Response headers
// Init data (so that we can bind callbacks early
reset(1);
- // Install callbacks related methods
- jQuery.each(["bind","unbind"], function(_, name) {
- xhr[name] = function(type) {
-
- var functors = sliceFunc.call(arguments,1),
- list;
-
- jQuery.each(type.split(/\s+/g), function() {
- list = callbacksLists[this];
- if ( list ) {
- list[name].apply(list, functors );
- }
- });
-
- return this;
- };
- });
-
- jQuery.each(callbacksLists, function(name) {
- var list;
- xhr[name] = function() {
- list = callbacksLists[name];
- if ( list ) {
- list.bind.apply(list, arguments );
- }
- return this;
- };
- });
-
// Return the xhr emulation
return xhr;
};
-// Create a callback list
-function createCBList() {
-
- var functors = [],
- autoFire = 0,
- fireArgs,
- list = {
-
- fire: function( flag , context ) {
-
- // Save info for later bindings
- fireArgs = arguments;
-
- // Remove autoFire to keep bindings in order
- autoFire = 0;
-
- var args = sliceFunc.call( fireArgs , 2 );
-
- // Execute callbacks
- while ( flag && functors.length ) {
- flag = functors.shift().apply( context , args ) !== false;
- }
-
- // Clean if asked to stop
- if ( ! flag ) {
- clean();
- }
-
- // Set autoFire
- autoFire = 1;
- },
-
- bind: function() {
-
- var args = arguments,
- i = 0,
- length = args.length,
- func;
-
- for ( ; i < length ; i++ ) {
-
- func = args[ i ];
-
- if ( jQuery.isArray(func) ) {
-
- list.bind.apply( list , func );
-
- } else if ( isFunction(func) ) {
-
- // Add if not already in
- if ( ! pos( func ) ) {
- functors.push( func );
- }
- }
- }
-
- if ( autoFire ) {
- list.fire.apply( list , fireArgs );
- }
- },
-
- unbind: function() {
-
- var i = 0,
- args = arguments,
- length = args.length,
- func,
- position;
-
- if ( length ) {
-
- for( ; i < length ; i++ ) {
- func = args[i];
- if ( jQuery.isArray(func) ) {
- list.unbind.apply(list,func);
- } else if ( isFunction(func) ) {
- position = pos(func);
- if ( position ) {
- functors.splice(position-1,1);
- }
- }
- }
-
- } else {
-
- functors = [];
-
- }
-
- }
-
- };
-
- // Get the index of the functor in the list (1-based)
- function pos( func ) {
- for (var i = 0, length = functors.length; i < length && functors[i] !== func; i++) {
- }
- return i < length ? ( i + 1 ) : 0;
- }
-
- // Clean the object
- function clean() {
- // Empty callbacks list
- functors = [];
- // Inhibit methods
- for (var i in list) {
- list[i] = jQuery.noop;
- }
- }
-
- return list;
-}
-
-jQuery.extend(jQuery.xhr, {
-
- // Add new prefilter
- prefilter: function (functor) {
- if ( isFunction(functor) ) {
- jQuery.ajaxSettings.prefilters.push( functor );
- }
- return this;
- },
-
- // Bind a transport to one or more dataTypes
- bindTransport: function () {
-
- var args = arguments,
- i,
- start = 0,
- length = args.length,
- dataTypes = [ "*" ],
- functors = [],
- functor,
- first,
- append,
- list,
- transports = jQuery.ajaxSettings.transports;
-
- if ( length ) {
-
- if ( ! isFunction( args[ 0 ] ) ) {
-
- dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
- start = 1;
-
- }
-
- if ( dataTypes.length && start < length ) {
-
- for ( i = start; i < length; i++ ) {
- functor = args[i];
- if ( isFunction(functor) ) {
- functors.push( functor );
- }
- }
-
- if ( functors.length ) {
-
- jQuery.each ( dataTypes, function( _ , dataType ) {
-
- first = /^\+/.test( dataType );
-
- if (first) {
- dataType = dataType.substr(1);
- }
-
- if ( dataType !== "" ) {
-
- append = Array.prototype[ first ? "unshift" : "push" ];
-
- list = transports[ dataType ];
-
- jQuery.each ( functors, function( _ , functor ) {
-
- if ( ! list ) {
-
- list = transports[ dataType ] = [ functor ];
-
- } else {
-
- append.call( list , functor );
- }
- } );
- }
-
- } );
- }
- }
- }
-
- return this;
- }
-
-
-});
-
-// Select a transport given options
-function selectTransport( s ) {
+// Execute or select from functions in a given structure of options
+function xhr_selectOrExecute( structure , s ) {
var dataTypes = s.dataTypes,
transportDataType,
- transportsList,
- transport,
+ list,
+ selected,
i,
length,
checked = {},
- flag;
+ flag,
+ noSelect = structure !== "transports";
function initSearch( dataType ) {
checked[ dataType ] = 1;
transportDataType = dataType;
- transportsList = s.transports[ dataType ];
+ list = s[ structure ][ dataType ];
i = -1;
- length = transportsList ? transportsList.length : 0 ;
+ length = list ? list.length : 0 ;
}
return flag;
initSearch( dataTypes[ 0 ] );
- for ( i = 0 ; ! transport && i <= length ; i++ ) {
+ for ( i = 0 ; ( noSelect || ! selected ) && i <= length ; i++ ) {
if ( i === length ) {
} else {
- transport = transportsList[ i ]( s , determineDataType );
+ selected = list[ i ]( s , determineDataType );
// If we got redirected to another dataType
// Search there (if not in progress or already tried)
- if ( typeof( transport ) === "string" &&
- initSearch( transport ) ) {
+ if ( typeof( selected ) === "string" &&
+ initSearch( selected ) ) {
- dataTypes.unshift( transport );
- transport = 0;
+ dataTypes.unshift( selected );
+ selected = 0;
}
}
}
- return transport;
+ return noSelect ? jQuery.xhr : selected;
}
+
+// Add an element to one of the xhr structures in ajaxSettings
+function xhr_addElement( structure , args ) {
+
+ var i,
+ j,
+ start = 0,
+ length = args.length,
+ dataTypes = [ "*" ],
+ dLength = 1,
+ dataType,
+ functors = [],
+ first,
+ append,
+ list;
+
+ if ( length ) {
+
+ first = jQuery.type( args[ 0 ] );
+
+ if ( first === "object" ) {
+ return xhr_selectOrExecute( structure , args[ 0 ] );
+ }
+
+ structure = jQuery.ajaxSettings[ structure ];
+
+ if ( first !== "function" ) {
+
+ dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
+ dLength = dataTypes.length;
+ start = 1;
+
+ }
+
+ if ( dLength && start < length ) {
+
+ functors = sliceFunc.call( args , start );
+
+ length -= start;
+
+ for( i = 0 ; i < dLength ; i++ ) {
+
+ dataType = dataTypes[ i ];
+
+ first = /^\+/.test( dataType );
+
+ if (first) {
+ dataType = dataType.substr(1);
+ }
+
+ if ( dataType !== "" ) {
+
+ append = Array.prototype[ first ? "unshift" : "push" ];
+
+ list = structure[ dataType ] = structure[ dataType ] || [];
+
+ for ( j = 0; j < length; j++ ) {
+ append.call( list , functors[ j ] );
+ }
+ }
+ }
+ }
+ }
+
+ return jQuery.xhr;
+}
+
+// Install prefilter & transport methods
+jQuery.each( [ "prefilter" , "transport" ] , function( _ , name ) {
+ _ = name + "s";
+ jQuery.xhr[ name ] = function() {
+ return xhr_addElement( _ , arguments );
+ };
+} );
// Utility function that handles dataType when response is received
// (for those transports that can give text or xml responses)