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 // Creates a jQuery xhr object
13 jQuery.xhr = function( _native ) {
16 return jQuery.ajaxSettings.xhr();
19 function reset( force ) {
21 // We only need to reset if we went through the init phase
22 // (with the exception of object creation)
23 if ( force || internal ) {
25 // Reset callbacks lists
26 deferred = jQuery.Deferred();
27 completeDeferred = jQuery._Deferred();
29 xhr.success = xhr.then = deferred.then;
30 xhr.error = xhr.fail = deferred.fail;
31 xhr.complete = completeDeferred.then;
33 // Reset private variables
35 responseHeadersString = responseHeaders = internal = done = timeoutTimer = s = undefined;
41 // Remove responseX fields
42 for ( var name in xhr ) {
43 if ( /^response/.test(name) ) {
52 var // Options extraction
54 // Remove hash character (#7531: first for string promotion)
55 url = s.url = ( "" + s.url ).replace( rhash , "" ),
58 type = s.type = s.type.toUpperCase(),
60 // Determine if request has content
61 hasContent = s.hasContent = ! rnoContent.test( type ),
63 // Extract dataTypes list
64 dataType = s.dataType,
65 dataTypes = s.dataTypes = dataType ? jQuery.trim(dataType).toLowerCase().split(/\s+/) : ["*"],
67 // Determine if a cross-domain request is in order
68 parts = rurl.exec( url.toLowerCase() ),
70 crossDomain = s.crossDomain = !!( parts && ( parts[1] && parts[1] != loc.protocol || parts[2] != loc.host ) ),
72 // Get other options locally
74 originalContentType = s.contentType,
75 prefilters = s.prefilters,
83 // Convert data if not already a string
84 if ( data && s.processData && typeof data != "string" ) {
85 data = s.data = jQuery.param( data , s.traditional );
89 internal = jQuery.xhr.prefilter( s ).transport( s );
91 // Re-actualize url & data
95 // If internal was found
98 // Get transportDataType
99 transportDataType = dataTypes[0];
101 // More options handling for requests with no content
102 if ( ! hasContent ) {
104 // If data is available, append data to url
106 url += (rquery_xhr.test(url) ? "&" : "?") + data;
109 // Add anti-cache in url if needed
110 if ( s.cache === false ) {
112 var ts = jQuery.now(),
113 // try replacing _= if it is there
114 ret = url.replace(rts, "$1_=" + ts );
116 // if nothing was replaced, add timestamp to the end
117 url = ret + ((ret == url) ? (rquery_xhr.test(url) ? "&" : "?") + "_=" + ts : "");
123 // Set the correct header, if data is being sent
124 if ( ( data && hasContent ) || originalContentType ) {
125 requestHeaders["content-type"] = s.contentType;
128 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
129 if ( s.ifModified ) {
130 if ( jQuery_lastModified[url] ) {
131 requestHeaders["if-modified-since"] = jQuery_lastModified[url];
133 if ( jQuery_etag[url] ) {
134 requestHeaders["if-none-match"] = jQuery_etag[url];
138 // Set the Accepts header for the server, depending on the dataType
139 requestHeaders.accept = transportDataType && accepts[ transportDataType ] ?
140 accepts[ transportDataType ] + ( transportDataType !== "*" ? ", */*; q=0.01" : "" ) :
143 // Check for headers option
144 for ( i in headers ) {
145 requestHeaders[ i.toLowerCase() ] = headers[ i ];
149 callbackContext = s.context || s;
150 globalEventContext = s.context ? jQuery(s.context) : jQuery.event;
152 for ( i in { success:1, error:1, complete:1 } ) {
156 // Watch for a new set of requests
157 if ( s.global && jQuery.active++ === 0 ) {
158 jQuery.event.trigger( "ajaxStart" );
164 function whenDone(status, statusText, response, headers) {
172 // Cache response headers
173 responseHeadersString = headers || "";
175 // Clear timeout if it exists
176 if ( timeoutTimer ) {
177 clearTimeout(timeoutTimer);
182 // and ifModified status
183 ifModified = s.ifModified,
192 // If not timeout, force a jQuery-compliant status text
193 if ( statusText != "timeout" ) {
194 statusText = ( status >= 200 && status < 300 ) ?
196 ( status === 304 ? "notmodified" : "error" );
199 // If successful, handle type chaining
200 if ( statusText === "success" || statusText === "notmodified" ) {
202 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
204 var lastModified = xhr.getResponseHeader("Last-Modified"),
205 etag = xhr.getResponseHeader("Etag");
208 jQuery_lastModified[url] = lastModified;
211 jQuery_etag[url] = etag;
215 if ( ifModified && statusText === "notmodified" ) {
221 // Chain data conversions and determine the final value
222 // (if an exception is thrown in the process, it'll be notified as an error)
233 dataTypes = s.dataTypes,
234 dataCheckers = s.dataCheckers,
235 dataConverters = s.dataConverters,
236 dataFilter = s.dataFilter,
242 for( i = 0 ; i < dataTypes.length ; i++ ) {
244 current = dataTypes[ i ];
248 prev = dataTypes[ i - 1 ];
250 if ( prev === "*" ) {
254 } else if ( current !== "*" && prev !== current ) {
257 dataConverters[ ( conversion = prev + " " + current ) ] ||
258 dataConverters[ "* " + current ];
260 console.log( conversion );
262 if ( ! oneConv && prev !== "text" && current !== "text" ) {
263 conv1 = dataConverters[ prev + " text" ] || dataConverters[ "* text" ];
264 conv2 = dataConverters[ "text " + current ];
266 if ( oneConv || conv1 && conv2 ) {
267 response = oneConv ? conv1( response ) : conv2( conv1( response ) );
269 throw "no " + conversion;
274 checker = dataCheckers[ current ];
276 if ( response != null && checker ) {
280 if ( responses[ current ] ) {
281 xhr[ "response" + responses[ current ] ] = response;
282 responses[ current ] = 0;
285 if ( ! i && dataFilter ) {
287 response = dataFilter( response );
289 dataTypes = s.dataTypes;
295 // We have a real success
301 statusText = "parsererror";
307 } else { // if not success, mark it as an error
309 error = error || statusText;
313 // Set data for the fake xhr object
315 xhr.statusText = statusText;
317 // Keep local copies of vars in case callbacks re-use the xhr
319 _deferred = deferred,
320 _completeDeferred = completeDeferred,
321 _callbackContext = callbackContext,
322 _globalEventContext = globalEventContext;
325 // Set state if the xhr hasn't been re-used
326 function _setState( value ) {
327 if ( xhr.readyState && s === _s ) {
333 if ( status && s.async ) {
343 _deferred.fire( _callbackContext , [ success , statusText , xhr ] );
345 _deferred.fireReject( _callbackContext , [ xhr , statusText , error ] );
349 _globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ) , [ xhr , _s , isSuccess ? success : error ] );
353 _completeDeferred.fire( _callbackContext, [ xhr , statusText ] );
356 _globalEventContext.trigger( "ajaxComplete", [xhr, _s] );
357 // Handle the global AJAX counter
358 if ( ! --jQuery.active ) {
359 jQuery.event.trigger( "ajaxStop" );
364 // Ready state control
365 function checkState( expected , test ) {
366 if ( expected !== true && ( expected === false || test === false || xhr.readyState !== expected ) ) {
367 jQuery.error("INVALID_STATE_ERR");
371 // Ready state change
372 function setState( value ) {
373 xhr.readyState = value;
374 if ( jQuery.isFunction( xhr.onreadystatechange ) ) {
375 xhr.onreadystatechange();
380 jQuery_lastModified = jQuery.lastModified,
381 jQuery_etag = jQuery.etag,
389 // Headers (they are sent all at once)
392 responseHeadersString,
408 onreadystatechange: null,
411 open: function(type, url, async, username, password) {
430 send: function(data, moreOptions) {
432 checkState(1 , !sendFlag);
436 s = jQuery.extend( true,
440 moreOptions || ( moreOptions === false ? { global: false } : {} ) );
443 // We force the original context
444 // (plain objects used as context get extended)
445 s.context = moreOptions.context;
450 // If not internal, abort
452 done( 0 , "transport not found" );
456 // Allow custom headers/mimetypes and early abort
457 if ( s.beforeSend ) {
461 if ( s.beforeSend.call(callbackContext, xhr, s) === false || ! xhr.readyState || _s !== s ) {
464 if ( xhr.readyState && _s === s ) {
468 // Handle the global AJAX counter
469 if ( _s.global && ! --jQuery.active ) {
470 jQuery.event.trigger( "ajaxStop" );
481 globalEventContext.trigger("ajaxSend", [xhr, s]);
485 if ( s.async && s.timeout > 0 ) {
486 timeoutTimer = setTimeout(function(){
487 xhr.abort("timeout");
497 internal.send(requestHeaders, done);
504 done(0, "error", "" + e);
517 setRequestHeader: function(name,value) {
518 checkState(1, !sendFlag);
519 requestHeaders[ name.toLowerCase() ] = value;
524 getAllResponseHeaders: function() {
525 return xhr.readyState <= 1 ? "" : responseHeadersString;
528 // Builds headers hashtable if needed
529 getResponseHeader: function( key ) {
531 if ( xhr.readyState <= 1 ) {
537 if ( responseHeaders === undefined ) {
539 responseHeaders = {};
541 if ( typeof responseHeadersString === "string" ) {
545 while( ( match = rheaders.exec( responseHeadersString ) ) ) {
546 responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
550 return responseHeaders[ key.toLowerCase() ];
553 // Cancel the request
554 abort: function(statusText) {
556 internal.abort( statusText || "abort" );
562 // Init data (so that we can bind callbacks early
565 // Return the xhr emulation
569 // Execute or select from functions in a given structure of options
570 function xhr_selectOrExecute( structure , s ) {
572 var dataTypes = s.dataTypes,
580 noSelect = structure !== "transports";
582 function initSearch( dataType ) {
584 flag = transportDataType !== dataType && ! checked[ dataType ];
588 checked[ dataType ] = 1;
589 transportDataType = dataType;
590 list = s[ structure ][ dataType ];
592 length = list ? list.length : 0 ;
598 initSearch( dataTypes[ 0 ] );
600 for ( i = 0 ; ( noSelect || ! selected ) && i <= length ; i++ ) {
602 if ( i === length ) {
608 selected = list[ i ]( s , determineDataType );
610 // If we got redirected to another dataType
611 // Search there (if not in progress or already tried)
612 if ( typeof( selected ) === "string" &&
613 initSearch( selected ) ) {
615 dataTypes.unshift( selected );
621 return noSelect ? jQuery.xhr : selected;
624 // Add an element to one of the xhr structures in ajaxSettings
625 function xhr_addElement( structure , args ) {
630 length = args.length,
641 first = jQuery.type( args[ 0 ] );
643 if ( first === "object" ) {
644 return xhr_selectOrExecute( structure , args[ 0 ] );
647 structure = jQuery.ajaxSettings[ structure ];
649 if ( first !== "function" ) {
651 dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
652 dLength = dataTypes.length;
657 if ( dLength && start < length ) {
659 functors = sliceFunc.call( args , start );
663 for( i = 0 ; i < dLength ; i++ ) {
665 dataType = dataTypes[ i ];
667 first = /^\+/.test( dataType );
670 dataType = dataType.substr(1);
673 if ( dataType !== "" ) {
675 append = Array.prototype[ first ? "unshift" : "push" ];
677 list = structure[ dataType ] = structure[ dataType ] || [];
679 for ( j = 0; j < length; j++ ) {
680 append.call( list , functors[ j ] );
690 // Install prefilter & transport methods
691 jQuery.each( [ "prefilter" , "transport" ] , function( _ , name ) {
693 jQuery.xhr[ name ] = function() {
694 return xhr_addElement( _ , arguments );
698 // Utility function that handles dataType when response is received
699 // (for those transports that can give text or xml responses)
700 function determineDataType( s , ct , text , xml ) {
702 var autoDataType = s.autoDataType,
705 dataTypes = s.dataTypes,
706 transportDataType = dataTypes[0],
709 // Auto (xml, json, script or text determined given headers)
710 if ( transportDataType === "*" ) {
712 for ( type in autoDataType ) {
713 if ( ( regexp = autoDataType[ type ] ) && regexp.test( ct ) ) {
714 transportDataType = dataTypes[0] = type;
720 // xml and parsed as such
721 if ( transportDataType === "xml" &&
723 xml.documentElement /* #4958 */ ) {
727 // Text response was provided
732 // If it's not really text, defer to dataConverters
733 if ( transportDataType !== "text" ) {
734 dataTypes.unshift( "text" );