rhash = /#.*$/,
rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /(?:^file|^widget|\-extension):$/,
rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
rquery = /\?/,
rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
rselectTextarea = /^(?:select|textarea)/i,
rspacesAjax = /\s+/,
rts = /([?&])_=[^&]*/,
- rurl = /^(\w+:)?\/\/([^\/?#:]+)(?::(\d+))?/,
-
- // Slice function
- sliceFunc = Array.prototype.slice,
+ rurl = /^([\w\+\.\-]+:)\/\/([^\/?#:]*)(?::(\d+))?/,
// Keep a copy of the old load method
_load = jQuery.fn.load,
* 2) the catchall symbol "*" can be used
* 3) selection will start with transport dataType and THEN go to "*" if needed
*/
- transports = {};
+ transports = {},
+
+ // Document location
+ ajaxLocation,
+
+ // Document location segments
+ ajaxLocParts;
+
+// #8138, IE may throw an exception when accessing
+// a field from document.location if document.domain has been set
+try {
+ ajaxLocation = document.location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() );
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
function addToPrefiltersOrTransports( structure ) {
}
if ( jQuery.isFunction( func ) ) {
- var dataTypes = dataTypeExpression.split( rspacesAjax ),
+ var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
i = 0,
length = dataTypes.length,
dataType,
// any existing element
placeBefore = /^\+/.test( dataType );
if ( placeBefore ) {
- dataType = dataType.substr( 1 );
+ dataType = dataType.substr( 1 ) || "*";
}
list = structure[ dataType ] = structure[ dataType ] || [];
// then we add to the structure accordingly
}
//Base inspection function for prefilters and transports
-function inspectPrefiltersOrTransports( structure, options, originalOptions,
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
dataType /* internal */, inspected /* internal */ ) {
dataType = dataType || options.dataTypes[ 0 ];
selection;
for(; i < length && ( executeOnly || !selection ); i++ ) {
- selection = list[ i ]( options, originalOptions );
+ selection = list[ i ]( options, originalOptions, jqXHR );
// If we got redirected to another dataType
- // we try there if not done already
+ // we try there if executing only and not done already
if ( typeof selection === "string" ) {
- if ( inspected[ selection ] ) {
+ if ( !executeOnly || inspected[ selection ] ) {
selection = undefined;
} else {
options.dataTypes.unshift( selection );
selection = inspectPrefiltersOrTransports(
- structure, options, originalOptions, selection, inspected );
+ structure, options, originalOptions, jqXHR, selection, inspected );
}
}
}
// we try the catchall dataType if not done already
if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
selection = inspectPrefiltersOrTransports(
- structure, options, originalOptions, "*", inspected );
+ structure, options, originalOptions, jqXHR, "*", inspected );
}
// unnecessary when only executing (prefilters)
// but it'll be ignored by the caller in that case
dataType: "html",
data: params,
// Complete callback (responseText is used internally)
- complete: function( jXHR, status, responseText ) {
- // Store the response as specified by the jXHR object
- responseText = jXHR.responseText;
+ complete: function( jqXHR, status, responseText ) {
+ // Store the response as specified by the jqXHR object
+ responseText = jqXHR.responseText;
// If successful, inject the HTML into all the matched elements
- if ( jXHR.isResolved() ) {
+ if ( jqXHR.isResolved() ) {
// #4825: Get the actual response in case
// a dataFilter is present in ajaxSettings
- jXHR.done(function( r ) {
+ jqXHR.done(function( r ) {
responseText = r;
});
// See if a selector was specified
}
if ( callback ) {
- self.each( callback, [ responseText, status, jXHR ] );
+ self.each( callback, [ responseText, status, jqXHR ] );
}
}
});
},
ajaxSettings: {
- url: location.href,
+ url: ajaxLocation,
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
global: true,
type: "GET",
contentType: "application/x-www-form-urlencoded",
// Main method
ajax: function( url, options ) {
- // If options is not an object,
- // we simulate pre-1.5 signature
- if ( typeof options !== "object" ) {
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
options = url;
url = undefined;
}
var // Create the final options object
s = jQuery.extend( true, {}, jQuery.ajaxSettings, options ),
- // Callbacks contexts
+ // Callbacks context
// We force the original context if it exists
// or take it from jQuery.ajaxSettings otherwise
// (plain objects used as context get extended)
callbackContext =
( s.context = ( "context" in options ? options : jQuery.ajaxSettings ).context ) || s,
- globalEventContext = callbackContext === s ? jQuery.event : jQuery( callbackContext ),
+ // Context for global events
+ // It's the callbackContext if one was provided in the options
+ // and if it's a DOM node
+ globalEventContext = callbackContext !== s && callbackContext.nodeType ?
+ jQuery( callbackContext ) : jQuery.event,
// Deferreds
deferred = jQuery.Deferred(),
completeDeferred = jQuery._Deferred(),
// Status-dependent callbacks
statusCode = s.statusCode || {},
+ // ifModified key
+ ifModifiedKey,
// Headers (they are sent all at once)
requestHeaders = {},
// Response headers
// timeout handle
timeoutTimer,
// Cross-domain detection vars
- loc = document.location,
- protocol = loc.protocol || "http:",
parts,
- // The jXHR state
+ // The jqXHR state
state = 0,
+ // To know if global events are to be dispatched
+ fireGlobals,
// Loop variable
i,
// Fake xhr
- jXHR = {
+ jqXHR = {
readyState: 0,
// Cancel the request
abort: function( statusText ) {
+ statusText = statusText || "abort";
if ( transport ) {
- transport.abort( statusText || "abort" );
+ transport.abort( statusText );
}
done( 0, statusText );
return this;
}
// Dereference transport for early garbage collection
- // (no matter how long the jXHR object will be used)
+ // (no matter how long the jqXHR object will be used)
transport = undefined;
// Cache response headers
responseHeadersString = headers || "";
// Set readyState
- jXHR.readyState = status ? 4 : 0;
+ jqXHR.readyState = status ? 4 : 0;
var isSuccess,
success,
- error = ( statusText = statusText || "error" ),
- response = responses ? ajaxHandleResponses( s, jXHR, responses ) : undefined,
+ error,
+ response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
lastModified,
etag;
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
- if ( ( lastModified = jXHR.getResponseHeader( "Last-Modified" ) ) ) {
- jQuery.lastModified[ s.url ] = lastModified;
+ if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
+ jQuery.lastModified[ ifModifiedKey ] = lastModified;
}
- if ( ( etag = jXHR.getResponseHeader( "Etag" ) ) ) {
- jQuery.etag[ s.url ] = etag;
+ if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
+ jQuery.etag[ ifModifiedKey ] = etag;
}
}
} catch(e) {
// We have a parsererror
statusText = "parsererror";
- error = "" + e;
+ error = e;
+ }
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if( status ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
}
}
}
// Set data for the fake xhr object
- jXHR.status = status;
- jXHR.statusText = statusText;
+ jqXHR.status = status;
+ jqXHR.statusText = statusText;
// Success/Error
if ( isSuccess ) {
- deferred.resolveWith( callbackContext, [ success, statusText, jXHR ] );
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
} else {
- deferred.rejectWith( callbackContext, [ jXHR, statusText, error ] );
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
}
// Status-dependent callbacks
- jXHR.statusCode( statusCode );
+ jqXHR.statusCode( statusCode );
statusCode = undefined;
- if ( s.global ) {
+ if ( fireGlobals ) {
globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
- [ jXHR, s, isSuccess ? success : error ] );
+ [ jqXHR, s, isSuccess ? success : error ] );
}
// Complete
- completeDeferred.resolveWith( callbackContext, [ jXHR, statusText ] );
+ completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
- if ( s.global ) {
- globalEventContext.trigger( "ajaxComplete", [ jXHR, s] );
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
// Handle the global AJAX counter
if ( !( --jQuery.active ) ) {
jQuery.event.trigger( "ajaxStop" );
}
// Attach deferreds
- deferred.promise( jXHR );
- jXHR.success = jXHR.done;
- jXHR.error = jXHR.fail;
- jXHR.complete = completeDeferred.done;
+ deferred.promise( jqXHR );
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+ jqXHR.complete = completeDeferred.done;
// Status-dependent callbacks
- jXHR.statusCode = function( map ) {
+ jqXHR.statusCode = function( map ) {
if ( map ) {
var tmp;
if ( state < 2 ) {
statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
}
} else {
- tmp = map[ jXHR.status ];
- jXHR.then( tmp, tmp );
+ tmp = map[ jqXHR.status ];
+ jqXHR.then( tmp, tmp );
}
}
return this;
};
// Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
// We also use the url parameter if available
- s.url = ( "" + ( url || s.url ) ).replace( rhash, "" );
+ s.url = ( "" + ( url || s.url ) ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
// Extract dataTypes list
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
// Determine if a cross-domain request is in order
if ( !s.crossDomain ) {
parts = rurl.exec( s.url.toLowerCase() );
- s.crossDomain = !!(
- parts &&
- ( parts[ 1 ] && parts[ 1 ] != protocol ||
- parts[ 2 ] != loc.hostname ||
- ( parts[ 3 ] || ( ( parts[ 1 ] || protocol ) === "http:" ? 80 : 443 ) ) !=
- ( loc.port || ( protocol === "http:" ? 80 : 443 ) ) )
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
);
}
}
// Apply prefilters
- inspectPrefiltersOrTransports( prefilters, s, options );
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefiler, stop there
+ if ( state === 2 ) {
+ return false;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
// Uppercase the type
s.type = s.type.toUpperCase();
s.hasContent = !rnoContent.test( s.type );
// Watch for a new set of requests
- if ( s.global && jQuery.active++ === 0 ) {
+ if ( fireGlobals && jQuery.active++ === 0 ) {
jQuery.event.trigger( "ajaxStart" );
}
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
}
+ // Get ifModifiedKey before adding the anti-cache parameter
+ ifModifiedKey = s.url;
+
// Add anti-cache in url if needed
if ( s.cache === false ) {
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
- if ( jQuery.lastModified[ s.url ] ) {
- requestHeaders[ "if-modified-since" ] = jQuery.lastModified[ s.url ];
+ ifModifiedKey = ifModifiedKey || s.url;
+ if ( jQuery.lastModified[ ifModifiedKey ] ) {
+ requestHeaders[ "if-modified-since" ] = jQuery.lastModified[ ifModifiedKey ];
}
- if ( jQuery.etag[ s.url ] ) {
- requestHeaders[ "if-none-match" ] = jQuery.etag[ s.url ];
+ if ( jQuery.etag[ ifModifiedKey ] ) {
+ requestHeaders[ "if-none-match" ] = jQuery.etag[ ifModifiedKey ];
}
}
}
// Allow custom headers/mimetypes and early abort
- if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jXHR, s ) === false || state === 2 ) ) {
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
// Abort if not done already
- done( 0, "abort" );
- // Return false
- jXHR = false;
+ jqXHR.abort();
+ return false;
- } else {
+ }
- // Install callbacks on deferreds
- for ( i in { success: 1, error: 1, complete: 1 } ) {
- jXHR[ i ]( s[ i ] );
- }
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
- // Get transport
- transport = inspectPrefiltersOrTransports( transports, s, options );
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
- // If no transport, we auto-abort
- if ( !transport ) {
- done( 0, "notransport" );
- } else {
- // Set state as sending
- state = jXHR.readyState = 1;
- // Send global event
- if ( s.global ) {
- globalEventContext.trigger( "ajaxSend", [ jXHR, s ] );
- }
- // Timeout
- if ( s.async && s.timeout > 0 ) {
- timeoutTimer = setTimeout( function(){
- jXHR.abort( "timeout" );
- }, s.timeout );
- }
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout( function(){
+ jqXHR.abort( "timeout" );
+ }, s.timeout );
+ }
- try {
- transport.send( requestHeaders, done );
- } catch (e) {
- // Propagate exception as error if not done
- if ( status === 1 ) {
- done( 0, "error", "" + e );
- jXHR = false;
- // Simply rethrow otherwise
- } else {
- jQuery.error( e );
- }
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch (e) {
+ // Propagate exception as error if not done
+ if ( status < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ jQuery.error( e );
}
}
}
- return jXHR;
+
+ return jqXHR;
},
// Serialize an array of form elements or a set of
}
// If an array was passed in, assume that it is an array of form elements.
- if ( jQuery.isArray( a ) || a.jquery ) {
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
// Serialize the form elements
jQuery.each( a, function() {
add( this.name, this.value );
// Serialize object item.
} else {
- jQuery.each( obj, function( k, v ) {
- buildParams( prefix + "[" + k + "]", v, traditional, add );
- });
+ for ( var name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
}
} else {
* - finds the right dataType (mediates between content-type and expected dataType)
* - returns the corresponding response
*/
-function ajaxHandleResponses( s, jXHR, responses ) {
+function ajaxHandleResponses( s, jqXHR, responses ) {
var contents = s.contents,
dataTypes = s.dataTypes,
// Fill responseXXX fields
for( type in responseFields ) {
if ( type in responses ) {
- jXHR[ responseFields[type] ] = responses[ type ];
+ jqXHR[ responseFields[type] ] = responses[ type ];
}
}
while( dataTypes[ 0 ] === "*" ) {
dataTypes.shift();
if ( ct === undefined ) {
- ct = jXHR.getResponseHeader( "content-type" );
+ ct = jqXHR.getResponseHeader( "content-type" );
}
}
}
var dataTypes = s.dataTypes,
- converters = s.converters,
+ converters = {},
i,
+ key,
length = dataTypes.length,
tmp,
// Current and previous dataTypes
conversion,
// Conversion function
conv,
- // Conversion functions (when text is used in-between)
+ // Conversion functions (transitive conversion)
conv1,
conv2;
// For each dataType in the chain
for( i = 1; i < length; i++ ) {
+ // Create converters map
+ // with lowercased keys
+ if ( i === 1 ) {
+ for( key in s.converters ) {
+ if( typeof key === "string" ) {
+ converters[ key.toLowerCase() ] = s.converters[ key ];
+ }
+ }
+ }
+
// Get the dataTypes
prev = current;
current = dataTypes[ i ];