5 rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
6 rnoContent = /^(?:GET|HEAD)$/,
8 rurl = /^(\w+:)?\/\/([^\/?#]+)/,
10 sliceFunc = Array.prototype.slice,
12 isFunction = jQuery.isFunction;
14 // Creates a jQuery xhr object
15 jQuery.xhr = function( _native ) {
18 return jQuery.ajaxSettings.xhr();
21 function reset( force ) {
23 // We only need to reset if we went through the init phase
24 // (with the exception of object creation)
25 if ( force || internal ) {
27 // Reset callbacks lists
28 deferred = jQuery.deferred();
29 completeDeferred = jQuery._deferred();
31 xhr.success = xhr.then = deferred.then;
32 xhr.error = xhr.fail = deferred.fail;
33 xhr.complete = completeDeferred.then;
35 // Reset private variables
37 responseHeadersString = responseHeaders = internal = done = timeoutTimer = s = undefined;
43 // Remove responseX fields
44 for ( var name in xhr ) {
45 if ( /^response/.test(name) ) {
54 var // Options extraction
56 // Remove hash character (#7531: first for string promotion)
57 url = s.url = ( "" + s.url ).replace( rhash , "" ),
60 type = s.type = s.type.toUpperCase(),
62 // Determine if request has content
63 hasContent = s.hasContent = ! rnoContent.test( type ),
65 // Extract dataTypes list
66 dataType = s.dataType,
67 dataTypes = s.dataTypes = dataType ? jQuery.trim(dataType).toLowerCase().split(/\s+/) : ["*"],
69 // Determine if a cross-domain request is in order
70 parts = rurl.exec( url.toLowerCase() ),
72 crossDomain = s.crossDomain = !!( parts && ( parts[1] && parts[1] != loc.protocol || parts[2] != loc.host ) ),
74 // Get other options locally
76 originalContentType = s.contentType,
77 prefilters = s.prefilters,
85 // Convert data if not already a string
86 if ( data && s.processData && typeof data != "string" ) {
87 data = s.data = jQuery.param( data , s.traditional );
91 internal = jQuery.xhr.prefilter( s ).transport( s );
93 // Re-actualize url & data
97 // If internal was found
100 // Get transportDataType
101 transportDataType = dataTypes[0];
103 // More options handling for requests with no content
104 if ( ! hasContent ) {
106 // If data is available, append data to url
108 url += (rquery_xhr.test(url) ? "&" : "?") + data;
111 // Add anti-cache in url if needed
112 if ( s.cache === false ) {
114 var ts = jQuery.now(),
115 // try replacing _= if it is there
116 ret = url.replace(rts, "$1_=" + ts );
118 // if nothing was replaced, add timestamp to the end
119 url = ret + ((ret == url) ? (rquery_xhr.test(url) ? "&" : "?") + "_=" + ts : "");
125 // Set the correct header, if data is being sent
126 if ( ( data && hasContent ) || originalContentType ) {
127 requestHeaders["content-type"] = s.contentType;
130 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
131 if ( s.ifModified ) {
132 if ( jQuery_lastModified[url] ) {
133 requestHeaders["if-modified-since"] = jQuery_lastModified[url];
135 if ( jQuery_etag[url] ) {
136 requestHeaders["if-none-match"] = jQuery_etag[url];
140 // Set the Accepts header for the server, depending on the dataType
141 requestHeaders.accept = transportDataType && accepts[ transportDataType ] ?
142 accepts[ transportDataType ] + ( transportDataType !== "*" ? ", */*; q=0.01" : "" ) :
145 // Check for headers option
146 for ( i in headers ) {
147 requestHeaders[ i.toLowerCase() ] = headers[ i ];
151 callbackContext = s.context || s;
152 globalEventContext = s.context ? jQuery(s.context) : jQuery.event;
154 for ( i in { success:1, error:1, complete:1 } ) {
158 // Watch for a new set of requests
159 if ( s.global && jQuery.active++ === 0 ) {
160 jQuery.event.trigger( "ajaxStart" );
166 function whenDone(status, statusText, response, headers) {
174 // Cache response headers
175 responseHeadersString = headers || "";
177 // Clear timeout if it exists
178 if ( timeoutTimer ) {
179 clearTimeout(timeoutTimer);
184 // and ifModified status
185 ifModified = s.ifModified,
194 // If not timeout, force a jQuery-compliant status text
195 if ( statusText != "timeout" ) {
196 statusText = ( status >= 200 && status < 300 ) ?
198 ( status === 304 ? "notmodified" : "error" );
201 // If successful, handle type chaining
202 if ( statusText === "success" || statusText === "notmodified" ) {
204 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
206 var lastModified = xhr.getResponseHeader("Last-Modified"),
207 etag = xhr.getResponseHeader("Etag");
210 jQuery_lastModified[url] = lastModified;
213 jQuery_etag[url] = etag;
217 if ( ifModified && statusText === "notmodified" ) {
223 // Chain data conversions and determine the final value
224 // (if an exception is thrown in the process, it'll be notified as an error)
227 function checkData(data) {
228 if ( data !== undefined ) {
229 var testFunction = s.dataCheckers[srcDataType];
230 if ( isFunction( testFunction ) ) {
236 function convertData (data) {
237 var conversionFunction = dataConverters[srcDataType+" => "+destDataType] ||
238 dataConverters["* => "+destDataType],
239 noFunction = ! isFunction( conversionFunction );
241 if ( srcDataType != "text" && destDataType != "text" ) {
242 // We try to put text inbetween
243 var first = dataConverters[srcDataType+" => text"] ||
244 dataConverters["* => text"],
245 second = dataConverters["text => "+destDataType] ||
246 dataConverters["* => "+destDataType],
247 areFunctions = isFunction( first ) && isFunction( second );
248 if ( areFunctions ) {
249 conversionFunction = function (data) {
250 return second( first ( data ) );
253 noFunction = ! areFunctions;
256 jQuery.error( "no data converter between " + srcDataType + " and " + destDataType );
260 return conversionFunction(data);
263 var dataTypes = s.dataTypes,
267 dataConverters = s.dataConverters,
270 responseTypes = s.xhrResponseFields;
272 for ( i = 0, length = dataTypes.length ; i < length ; i++ ) {
274 destDataType = dataTypes[i];
276 if ( !srcDataType ) { // First time
279 srcDataType = destDataType;
283 if ( isFunction( s.dataFilter ) ) {
284 data = s.dataFilter(data, s.dataType);
289 } else { // Subsequent times
292 // JULIAN: for reasons unknown to me === doesn't work here
293 if (destDataType == "*") {
295 destDataType = srcDataType;
297 } else if ( srcDataType != destDataType ) {
300 data = convertData(data);
302 srcDataType = destDataType;
309 // Copy response into the xhr if it hasn't been already
310 var responseDataType,
311 responseType = responseTypes[srcDataType];
313 if ( responseType ) {
315 responseDataType = srcDataType;
319 responseType = responseTypes[ responseDataType = "text" ];
323 if ( responseType !== 1 ) {
324 xhr[ "response" + responseType ] = data;
325 responseTypes[ responseType ] = 1;
330 // We have a real success
336 statusText = "parsererror";
342 } else { // if not success, mark it as an error
344 error = error || statusText;
348 // Set data for the fake xhr object
350 xhr.statusText = statusText;
352 // Keep local copies of vars in case callbacks re-use the xhr
354 _deferred = deferred,
355 _completeDeferred = completeDeferred,
356 _callbackContext = callbackContext,
357 _globalEventContext = globalEventContext;
360 // Set state if the xhr hasn't been re-used
361 function _setState( value ) {
362 if ( xhr.readyState && s === _s ) {
368 if ( status && s.async ) {
378 _deferred.fire( _callbackContext , [ success , statusText , xhr ] );
380 _deferred.fireReject( _callbackContext , [ xhr , statusText , error ] );
384 _globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ) , [ xhr , _s , isSuccess ? success : error ] );
388 _completeDeferred.fire( _callbackContext, [ xhr , statusText ] );
391 _globalEventContext.trigger( "ajaxComplete", [xhr, _s] );
392 // Handle the global AJAX counter
393 if ( ! --jQuery.active ) {
394 jQuery.event.trigger( "ajaxStop" );
399 // Ready state control
400 function checkState( expected , test ) {
401 if ( expected !== true && ( expected === false || test === false || xhr.readyState !== expected ) ) {
402 jQuery.error("INVALID_STATE_ERR");
406 // Ready state change
407 function setState( value ) {
408 xhr.readyState = value;
409 if ( isFunction( xhr.onreadystatechange ) ) {
410 xhr.onreadystatechange();
415 jQuery_lastModified = jQuery.lastModified,
416 jQuery_etag = jQuery.etag,
424 // Headers (they are sent all at once)
427 responseHeadersString,
443 onreadystatechange: null,
446 open: function(type, url, async, username, password) {
465 send: function(data, moreOptions) {
467 checkState(1 , !sendFlag);
471 s = jQuery.extend( true,
475 moreOptions || ( moreOptions === false ? { global: false } : {} ) );
478 // We force the original context
479 // (plain objects used as context get extended)
480 s.context = moreOptions.context;
485 // If not internal, abort
487 done( 0 , "transport not found" );
491 // Allow custom headers/mimetypes and early abort
492 if ( s.beforeSend ) {
496 if ( s.beforeSend.call(callbackContext, xhr, s) === false || ! xhr.readyState || _s !== s ) {
499 if ( xhr.readyState && _s === s ) {
503 // Handle the global AJAX counter
504 if ( _s.global && ! --jQuery.active ) {
505 jQuery.event.trigger( "ajaxStop" );
516 globalEventContext.trigger("ajaxSend", [xhr, s]);
520 if ( s.async && s.timeout > 0 ) {
521 timeoutTimer = setTimeout(function(){
522 xhr.abort("timeout");
532 internal.send(requestHeaders, done);
539 done(0, "error", "" + e);
552 setRequestHeader: function(name,value) {
553 checkState(1, !sendFlag);
554 requestHeaders[ name.toLowerCase() ] = value;
559 getAllResponseHeaders: function() {
560 return xhr.readyState <= 1 ? "" : responseHeadersString;
563 // Builds headers hashtable if needed
564 getResponseHeader: function( key ) {
566 if ( xhr.readyState <= 1 ) {
572 if ( responseHeaders === undefined ) {
574 responseHeaders = {};
576 if ( typeof responseHeadersString === "string" ) {
580 while( ( match = rheaders.exec( responseHeadersString ) ) ) {
581 responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
585 return responseHeaders[ key.toLowerCase() ];
588 // Cancel the request
589 abort: function(statusText) {
591 internal.abort( statusText || "abort" );
597 // Init data (so that we can bind callbacks early
600 // Return the xhr emulation
604 // Execute or select from functions in a given structure of options
605 function xhr_selectOrExecute( structure , s ) {
607 var dataTypes = s.dataTypes,
615 noSelect = structure !== "transports";
617 function initSearch( dataType ) {
619 flag = transportDataType !== dataType && ! checked[ dataType ];
623 checked[ dataType ] = 1;
624 transportDataType = dataType;
625 list = s[ structure ][ dataType ];
627 length = list ? list.length : 0 ;
633 initSearch( dataTypes[ 0 ] );
635 for ( i = 0 ; ( noSelect || ! selected ) && i <= length ; i++ ) {
637 if ( i === length ) {
643 selected = list[ i ]( s , determineDataType );
645 // If we got redirected to another dataType
646 // Search there (if not in progress or already tried)
647 if ( typeof( selected ) === "string" &&
648 initSearch( selected ) ) {
650 dataTypes.unshift( selected );
656 return noSelect ? jQuery.xhr : selected;
659 // Add an element to one of the xhr structures in ajaxSettings
660 function xhr_addElement( structure , args ) {
665 length = args.length,
676 first = jQuery.type( args[ 0 ] );
678 if ( first === "object" ) {
679 return xhr_selectOrExecute( structure , args[ 0 ] );
682 structure = jQuery.ajaxSettings[ structure ];
684 if ( first !== "function" ) {
686 dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
687 dLength = dataTypes.length;
692 if ( dLength && start < length ) {
694 functors = sliceFunc.call( args , start );
698 for( i = 0 ; i < dLength ; i++ ) {
700 dataType = dataTypes[ i ];
702 first = /^\+/.test( dataType );
705 dataType = dataType.substr(1);
708 if ( dataType !== "" ) {
710 append = Array.prototype[ first ? "unshift" : "push" ];
712 list = structure[ dataType ] = structure[ dataType ] || [];
714 for ( j = 0; j < length; j++ ) {
715 append.call( list , functors[ j ] );
725 // Install prefilter & transport methods
726 jQuery.each( [ "prefilter" , "transport" ] , function( _ , name ) {
728 jQuery.xhr[ name ] = function() {
729 return xhr_addElement( _ , arguments );
733 // Utility function that handles dataType when response is received
734 // (for those transports that can give text or xml responses)
735 function determineDataType( s , ct , text , xml ) {
737 var autoDataType = s.autoDataType,
740 dataTypes = s.dataTypes,
741 transportDataType = dataTypes[0],
744 // Auto (xml, json, script or text determined given headers)
745 if ( transportDataType === "*" ) {
747 for ( type in autoDataType ) {
748 if ( ( regexp = autoDataType[ type ] ) && regexp.test( ct ) ) {
749 transportDataType = dataTypes[0] = type;
755 // xml and parsed as such
756 if ( transportDataType === "xml" &&
758 xml.documentElement /* #4958 */ ) {
762 // Text response was provided
767 // If it's not really text, defer to dataConverters
768 if ( transportDataType !== "text" ) {
769 dataTypes.unshift( "text" );