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
29 success: createCBList(),
30 error: createCBList(),
31 complete: createCBList()
34 // Reset private variables
36 responseHeadersString = responseHeaders = internal = done = timeoutTimer = s = undefined;
42 // Remove responseX fields
43 for ( var name in xhr ) {
44 if ( /^response/.test(name) ) {
53 var // Options extraction
55 // Remove hash character (#7531: first for string promotion)
56 url = s.url = ( "" + s.url ).replace( rhash , "" ),
59 type = s.type = s.type.toUpperCase(),
61 // Determine if request has content
62 hasContent = s.hasContent = ! rnoContent.test( type ),
64 // Extract dataTypes list
65 dataType = s.dataType,
66 dataTypes = s.dataTypes = dataType ? jQuery.trim(dataType).toLowerCase().split(/\s+/) : ["*"],
68 // Determine if a cross-domain request is in order
69 parts = rurl.exec( url.toLowerCase() ),
71 crossDomain = s.crossDomain = !!( parts && ( parts[1] && parts[1] != loc.protocol || parts[2] != loc.host ) ),
73 // Get other options locally
75 originalContentType = s.contentType,
76 prefilters = s.prefilters,
84 // Convert data if not already a string
85 if ( data && s.processData && typeof data != "string" ) {
86 data = s.data = jQuery.param( data , s.traditional );
89 // Apply option prefilters
90 for ( i = 0; i < prefilters.length; i++ ) {
95 internal = selectTransport( s );
97 // Re-actualize url & data
101 // If internal was found
104 // Get transportDataType
105 transportDataType = dataTypes[0];
107 // More options handling for requests with no content
108 if ( ! hasContent ) {
110 // If data is available, append data to url
112 url += (rquery_xhr.test(url) ? "&" : "?") + data;
115 // Add anti-cache in url if needed
116 if ( s.cache === false ) {
118 var ts = jQuery.now(),
119 // try replacing _= if it is there
120 ret = url.replace(rts, "$1_=" + ts );
122 // if nothing was replaced, add timestamp to the end
123 url = ret + ((ret == url) ? (rquery_xhr.test(url) ? "&" : "?") + "_=" + ts : "");
129 // Set the correct header, if data is being sent
130 if ( ( data && hasContent ) || originalContentType ) {
131 requestHeaders["content-type"] = s.contentType;
134 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
135 if ( s.ifModified ) {
136 if ( jQuery_lastModified[url] ) {
137 requestHeaders["if-modified-since"] = jQuery_lastModified[url];
139 if ( jQuery_etag[url] ) {
140 requestHeaders["if-none-match"] = jQuery_etag[url];
144 // Set the Accepts header for the server, depending on the dataType
145 requestHeaders.accept = transportDataType && accepts[ transportDataType ] ?
146 accepts[ transportDataType ] + ( transportDataType !== "*" ? ", */*; q=0.01" : "" ) :
149 // Check for headers option
150 for ( i in headers ) {
151 requestHeaders[ i.toLowerCase() ] = headers[ i ];
155 callbackContext = s.context || s;
156 globalEventContext = s.context ? jQuery(s.context) : jQuery.event;
158 for ( i in callbacksLists ) {
159 callbacksLists[i].bind(s[i]);
162 // Watch for a new set of requests
163 if ( s.global && jQuery.active++ === 0 ) {
164 jQuery.event.trigger( "ajaxStart" );
170 function whenDone(status, statusText, response, headers) {
178 // Cache response headers
179 responseHeadersString = headers || "";
181 // Clear timeout if it exists
182 if ( timeoutTimer ) {
183 clearTimeout(timeoutTimer);
188 // and ifModified status
189 ifModified = s.ifModified,
198 // If not timeout, force a jQuery-compliant status text
199 if ( statusText != "timeout" ) {
200 statusText = ( status >= 200 && status < 300 ) ?
202 ( status === 304 ? "notmodified" : "error" );
205 // If successful, handle type chaining
206 if ( statusText === "success" || statusText === "notmodified" ) {
208 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
210 var lastModified = xhr.getResponseHeader("Last-Modified"),
211 etag = xhr.getResponseHeader("Etag");
214 jQuery_lastModified[url] = lastModified;
217 jQuery_etag[url] = etag;
221 if ( ifModified && statusText === "notmodified" ) {
227 // Chain data conversions and determine the final value
228 // (if an exception is thrown in the process, it'll be notified as an error)
231 function checkData(data) {
232 if ( data !== undefined ) {
233 var testFunction = s.dataCheckers[srcDataType];
234 if ( isFunction( testFunction ) ) {
240 function convertData (data) {
241 var conversionFunction = dataConverters[srcDataType+" => "+destDataType] ||
242 dataConverters["* => "+destDataType],
243 noFunction = ! isFunction( conversionFunction );
245 if ( srcDataType != "text" && destDataType != "text" ) {
246 // We try to put text inbetween
247 var first = dataConverters[srcDataType+" => text"] ||
248 dataConverters["* => text"],
249 second = dataConverters["text => "+destDataType] ||
250 dataConverters["* => "+destDataType],
251 areFunctions = isFunction( first ) && isFunction( second );
252 if ( areFunctions ) {
253 conversionFunction = function (data) {
254 return second( first ( data ) );
257 noFunction = ! areFunctions;
260 jQuery.error( "no data converter between " + srcDataType + " and " + destDataType );
264 return conversionFunction(data);
267 var dataTypes = s.dataTypes,
271 dataConverters = s.dataConverters,
274 responseTypes = s.xhrResponseFields;
276 for ( i = 0, length = dataTypes.length ; i < length ; i++ ) {
278 destDataType = dataTypes[i];
280 if ( !srcDataType ) { // First time
283 srcDataType = destDataType;
287 if ( isFunction( s.dataFilter ) ) {
288 data = s.dataFilter(data, s.dataType);
293 } else { // Subsequent times
296 // JULIAN: for reasons unknown to me === doesn't work here
297 if (destDataType == "*") {
299 destDataType = srcDataType;
301 } else if ( srcDataType != destDataType ) {
304 data = convertData(data);
306 srcDataType = destDataType;
313 // Copy response into the xhr if it hasn't been already
314 var responseDataType,
315 responseType = responseTypes[srcDataType];
317 if ( responseType ) {
319 responseDataType = srcDataType;
323 responseType = responseTypes[ responseDataType = "text" ];
327 if ( responseType !== 1 ) {
328 xhr[ "response" + responseType ] = data;
329 responseTypes[ responseType ] = 1;
334 // We have a real success
340 statusText = "parsererror";
346 } else { // if not success, mark it as an error
348 error = error || statusText;
352 // Set data for the fake xhr object
354 xhr.statusText = statusText;
356 // Keep local copies of vars in case callbacks re-use the xhr
358 _callbacksLists = callbacksLists,
359 _callbackContext = callbackContext,
360 _globalEventContext = globalEventContext;
362 // Set state if the xhr hasn't been re-used
363 function _setState( value ) {
364 if ( xhr.readyState && s === _s ) {
370 if ( status && s.async ) {
379 _callbacksLists.success.fire( isSuccess , _callbackContext , success, statusText, xhr);
380 if ( isSuccess && _s.global ) {
381 _globalEventContext.trigger( "ajaxSuccess", [xhr, _s, success] );
384 _callbacksLists.error.fire( ! isSuccess , _callbackContext , xhr, statusText, error);
385 if ( !isSuccess && _s.global ) {
386 _globalEventContext.trigger( "ajaxError", [xhr, _s, error] );
389 _callbacksLists.complete.fire( 1 , _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,
423 // Headers (they are sent all at once)
426 responseHeadersString,
442 onreadystatechange: null,
445 open: function(type, url, async, username, password) {
464 send: function(data, moreOptions) {
466 checkState(1 , !sendFlag);
470 s = jQuery.extend( true,
474 moreOptions || ( moreOptions === false ? { global: false } : {} ) );
477 // We force the original context
478 // (plain objects used as context get extended)
479 s.context = moreOptions.context;
484 // If not internal, abort
486 done( 0 , "transport not found" );
490 // Allow custom headers/mimetypes and early abort
491 if ( s.beforeSend ) {
495 if ( s.beforeSend.call(callbackContext, xhr, s) === false || ! xhr.readyState || _s !== s ) {
498 if ( xhr.readyState && _s === s ) {
502 // Handle the global AJAX counter
503 if ( _s.global && ! --jQuery.active ) {
504 jQuery.event.trigger( "ajaxStop" );
515 globalEventContext.trigger("ajaxSend", [xhr, s]);
519 if ( s.async && s.timeout > 0 ) {
520 timeoutTimer = setTimeout(function(){
521 xhr.abort("timeout");
531 internal.send(requestHeaders, done);
538 done(0, "error", "" + e);
551 setRequestHeader: function(name,value) {
552 checkState(1, !sendFlag);
553 requestHeaders[ name.toLowerCase() ] = value;
558 getAllResponseHeaders: function() {
559 return xhr.readyState <= 1 ? "" : responseHeadersString;
562 // Builds headers hashtable if needed
563 getResponseHeader: function( key ) {
565 if ( xhr.readyState <= 1 ) {
571 if ( responseHeaders === undefined ) {
573 responseHeaders = {};
575 if ( typeof responseHeadersString === "string" ) {
579 while( ( match = rheaders.exec( responseHeadersString ) ) ) {
580 responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
584 return responseHeaders[ key.toLowerCase() ];
587 // Cancel the request
588 abort: function(statusText) {
590 internal.abort( statusText || "abort" );
596 // Init data (so that we can bind callbacks early
599 // Install callbacks related methods
600 jQuery.each(callbacksLists, function(name) {
602 xhr[name] = function() {
603 list = callbacksLists[name];
605 list.bind.apply(list, arguments );
611 // Return the xhr emulation
615 // Create a callback list
616 function createCBList() {
623 fire: function( flag , context ) {
625 // Save info for later bindings
626 fireArgs = arguments;
628 // Remove autoFire to keep bindings in order
631 var args = sliceFunc.call( fireArgs , 2 );
634 while ( flag && functors.length ) {
635 flag = functors.shift().apply( context , args ) !== false;
638 // Clean if asked to stop
649 var args = arguments,
651 length = args.length,
654 for ( ; i < length ; i++ ) {
658 if ( jQuery.isArray(func) ) {
660 list.bind.apply( list , func );
662 } else if ( isFunction(func) ) {
664 // Add if not already in
665 if ( ! pos( func ) ) {
666 functors.push( func );
672 list.fire.apply( list , fireArgs );
680 length = args.length,
686 for( ; i < length ; i++ ) {
688 if ( jQuery.isArray(func) ) {
689 list.unbind.apply(list,func);
690 } else if ( isFunction(func) ) {
691 position = pos(func);
693 functors.splice(position-1,1);
708 // Get the index of the functor in the list (1-based)
709 function pos( func ) {
710 for (var i = 0, length = functors.length; i < length && functors[i] !== func; i++) {
712 return i < length ? ( i + 1 ) : 0;
717 // Empty callbacks list
720 for (var i in list) {
721 list[i] = jQuery.noop;
728 jQuery.extend(jQuery.xhr, {
731 prefilter: function (functor) {
732 if ( isFunction(functor) ) {
733 jQuery.ajaxSettings.prefilters.push( functor );
738 // Bind a transport to one or more dataTypes
739 bindTransport: function () {
741 var args = arguments,
744 length = args.length,
751 transports = jQuery.ajaxSettings.transports;
755 if ( ! isFunction( args[ 0 ] ) ) {
757 dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
762 if ( dataTypes.length && start < length ) {
764 for ( i = start; i < length; i++ ) {
766 if ( isFunction(functor) ) {
767 functors.push( functor );
771 if ( functors.length ) {
773 jQuery.each ( dataTypes, function( _ , dataType ) {
775 first = /^\+/.test( dataType );
778 dataType = dataType.substr(1);
781 if ( dataType !== "" ) {
783 append = Array.prototype[ first ? "unshift" : "push" ];
785 list = transports[ dataType ];
787 jQuery.each ( functors, function( _ , functor ) {
791 list = transports[ dataType ] = [ functor ];
795 append.call( list , functor );
811 // Select a transport given options
812 function selectTransport( s ) {
814 var dataTypes = s.dataTypes,
823 function initSearch( dataType ) {
825 flag = transportDataType !== dataType && ! checked[ dataType ];
829 checked[ dataType ] = 1;
830 transportDataType = dataType;
831 transportsList = s.transports[ dataType ];
833 length = transportsList ? transportsList.length : 0 ;
839 initSearch( dataTypes[ 0 ] );
841 for ( i = 0 ; ! transport && i <= length ; i++ ) {
843 if ( i === length ) {
849 transport = transportsList[ i ]( s , determineDataType );
851 // If we got redirected to another dataType
852 // Search there (if not in progress or already tried)
853 if ( typeof( transport ) === "string" &&
854 initSearch( transport ) ) {
856 dataTypes.unshift( transport );
865 // Utility function that handles dataType when response is received
866 // (for those transports that can give text or xml responses)
867 function determineDataType( s , ct , text , xml ) {
869 var autoDataType = s.autoDataType,
872 dataTypes = s.dataTypes,
873 transportDataType = dataTypes[0],
876 // Auto (xml, json, script or text determined given headers)
877 if ( transportDataType === "*" ) {
879 for ( type in autoDataType ) {
880 if ( ( regexp = autoDataType[ type ] ) && regexp.test( ct ) ) {
881 transportDataType = dataTypes[0] = type;
887 // xml and parsed as such
888 if ( transportDataType === "xml" &&
890 xml.documentElement /* #4958 */ ) {
894 // Text response was provided
899 // If it's not really text, defer to dataConverters
900 if ( transportDataType !== "text" ) {
901 dataTypes.unshift( "text" );