5 rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
6 rnoContent = /^(?:GET|HEAD)$/,
8 rurl = /^(\w+:)?\/\/([^\/?#]+)/,
10 slice = Array.prototype.slice,
12 isFunction = jQuery.isFunction,
16 // Creates a jQuery xhr object
17 jQuery.xhr = function( _native ) {
20 return jQuery.ajaxSettings.xhr();
23 function reset(force) {
25 // We only need to reset if we went through the init phase
26 // (with the exception of object creation)
27 if ( force || internal ) {
29 // Reset callbacks lists
31 success: createCBList(),
32 error: createCBList(),
33 complete: createCBList()
36 // Reset private variables
38 responseHeadersString = responseHeaders = internal = done = timeoutTimer = s = undefined;
44 // Remove responseX fields
45 for ( var name in xhr ) {
46 if ( /^response/.test(name) ) {
55 var // Options extraction
57 // Remove hash character (#7531: first for string promotion)
58 url = s.url = ( "" + s.url ).replace( rhash , "" ),
61 type = s.type = s.type.toUpperCase(),
63 // Determine if request has content
64 hasContent = s.hasContent = ! rnoContent.test( type ),
66 // Extract dataTypes list
67 dataType = s.dataType,
68 dataTypes = s.dataTypes = dataType ? jQuery.trim(dataType).toLowerCase().split(/\s+/) : ["*"],
70 // Determine if a cross-domain request is in order
71 parts = rurl.exec( url.toLowerCase() ),
73 crossDomain = s.crossDomain = !!( parts && ( parts[1] && parts[1] != loc.protocol || parts[2] != loc.host ) ),
75 // Get other options locally
77 originalContentType = s.contentType,
78 prefilters = s.prefilters,
86 // Convert data if not already a string
87 if ( data && s.processData && typeof data != "string" ) {
88 data = s.data = jQuery.param( data , s.traditional );
91 // Apply option prefilters
92 for (i in prefilters) {
97 internal = selectTransport( s );
99 // Re-actualize url & data
103 // If internal was found
106 // Get transportDataType
107 transportDataType = dataTypes[0];
109 // More options handling for requests with no content
110 if ( ! hasContent ) {
112 // If data is available, append data to url
114 url += (rquery.test(url) ? "&" : "?") + data;
117 // Add anti-cache in url if needed
118 if ( s.cache === false ) {
120 var ts = jQuery.now(),
121 // try replacing _= if it is there
122 ret = url.replace(rts, "$1_=" + ts );
124 // if nothing was replaced, add timestamp to the end
125 url = ret + ((ret == url) ? (rquery.test(url) ? "&" : "?") + "_=" + ts : "");
131 // Set the correct header, if data is being sent
132 if ( ( data && hasContent ) || originalContentType ) {
133 requestHeaders["content-type"] = s.contentType;
136 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
137 if ( s.ifModified ) {
138 if ( jQuery_lastModified[url] ) {
139 requestHeaders["if-modified-since"] = jQuery_lastModified[url];
141 if ( jQuery_etag[url] ) {
142 requestHeaders["if-none-match"] = jQuery_etag[url];
146 // Set the Accepts header for the server, depending on the dataType
147 requestHeaders.accept = transportDataType && accepts[ transportDataType ] ?
148 accepts[ transportDataType ] + ( transportDataType !== "*" ? ", */*; q=0.01" : "" ) :
151 // Check for headers option
152 for ( i in headers ) {
153 requestHeaders[ i.toLowerCase() ] = headers[ i ];
157 callbackContext = s.context || s;
158 globalEventContext = s.context ? jQuery(s.context) : jQuery.event;
160 for ( i in callbacksLists ) {
161 callbacksLists[i].bind(s[i]);
164 // Watch for a new set of requests
165 if ( s.global && jQuery.active++ === 0 ) {
166 jQuery.event.trigger( "ajaxStart" );
172 function whenDone(status, statusText, response, headers) {
180 // Cache response headers
181 responseHeadersString = headers || "";
183 // Clear timeout if it exists
184 if ( timeoutTimer ) {
185 clearTimeout(timeoutTimer);
190 // and ifModified status
191 ifModified = s.ifModified,
200 // If not timeout, force a jQuery-compliant status text
201 if ( statusText != "timeout" ) {
202 statusText = ( status >= 200 && status < 300 ) ?
204 ( status === 304 ? "notmodified" : "error" );
207 // If successful, handle type chaining
208 if ( statusText === "success" || statusText === "notmodified" ) {
210 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
212 var lastModified = xhr.getResponseHeader("Last-Modified"),
213 etag = xhr.getResponseHeader("Etag");
216 jQuery_lastModified[url] = lastModified;
219 jQuery_etag[url] = etag;
223 if ( ifModified && statusText === "notmodified" ) {
229 // Chain data conversions and determine the final value
230 // (if an exception is thrown in the process, it'll be notified as an error)
233 function checkData(data) {
234 if ( data !== undefined ) {
235 var testFunction = s.dataCheckers[srcDataType];
236 if ( isFunction( testFunction ) ) {
242 function convertData (data) {
243 var conversionFunction = dataConverters[srcDataType+" => "+destDataType] ||
244 dataConverters["* => "+destDataType],
245 noFunction = ! isFunction( conversionFunction );
247 if ( srcDataType != "text" && destDataType != "text" ) {
248 // We try to put text inbetween
249 var first = dataConverters[srcDataType+" => text"] ||
250 dataConverters["* => text"],
251 second = dataConverters["text => "+destDataType] ||
252 dataConverters["* => "+destDataType],
253 areFunctions = isFunction( first ) && isFunction( second );
254 if ( areFunctions ) {
255 conversionFunction = function (data) {
256 return second( first ( data ) );
259 noFunction = ! areFunctions;
262 jQuery.error( "no data converter between " + srcDataType + " and " + destDataType );
266 return conversionFunction(data);
269 var dataTypes = s.dataTypes,
273 dataConverters = s.dataConverters,
276 responseTypes = s.xhrResponseFields;
278 for ( i = 0, length = dataTypes.length ; i < length ; i++ ) {
280 destDataType = dataTypes[i];
282 if ( !srcDataType ) { // First time
285 srcDataType = destDataType;
289 if ( isFunction( s.dataFilter ) ) {
290 data = s.dataFilter(data, s.dataType);
295 } else { // Subsequent times
298 // JULIAN: for reasons unknown to me === doesn't work here
299 if (destDataType == "*") {
301 destDataType = srcDataType;
303 } else if ( srcDataType != destDataType ) {
306 data = convertData(data);
308 srcDataType = destDataType;
315 // Copy response into the xhr if it hasn't been already
316 var responseDataType,
317 responseType = responseTypes[srcDataType];
319 if ( responseType ) {
321 responseDataType = srcDataType;
325 responseType = responseTypes[ responseDataType = "text" ];
329 if ( responseType !== 1 ) {
330 xhr[ "response" + responseType ] = data;
331 responseTypes[ responseType ] = 1;
336 // We have a real success
342 statusText = "parsererror";
348 } else { // if not success, mark it as an error
350 error = error || statusText;
354 // Set data for the fake xhr object
356 xhr.statusText = statusText;
358 // Keep local copies of vars in case callbacks re-use the xhr
360 _callbacksLists = callbacksLists,
361 _callbackContext = callbackContext,
362 _globalEventContext = globalEventContext;
364 // Set state if the xhr hasn't been re-used
365 function _setState( value ) {
366 if ( xhr.readyState && s === _s ) {
372 if ( status && s.async ) {
381 _callbacksLists.success.fire( isSuccess , _callbackContext , success, statusText, xhr);
382 if ( isSuccess && _s.global ) {
383 _globalEventContext.trigger( "ajaxSuccess", [xhr, _s, success] );
386 _callbacksLists.error.fire( ! isSuccess , _callbackContext , xhr, statusText, error);
387 if ( !isSuccess && _s.global ) {
388 _globalEventContext.trigger( "ajaxError", [xhr, _s, error] );
391 _callbacksLists.complete.fire( 1 , _callbackContext, xhr, statusText);
393 _globalEventContext.trigger( "ajaxComplete", [xhr, _s] );
394 // Handle the global AJAX counter
395 if ( ! --jQuery.active ) {
396 jQuery.event.trigger( "ajaxStop" );
401 // Ready state control
402 function checkState( expected , test ) {
403 if ( expected !== true && ( expected === false || test === false || xhr.readyState !== expected ) ) {
404 jQuery.error("INVALID_STATE_ERR");
408 // Ready state change
409 function setState( value ) {
410 xhr.readyState = value;
411 if ( isFunction( xhr.onreadystatechange ) ) {
412 xhr.onreadystatechange();
417 jQuery_lastModified = jQuery.lastModified,
418 jQuery_etag = jQuery.etag,
425 // Headers (they are sent all at once)
428 responseHeadersString,
444 onreadystatechange: null,
447 open: function(type, url, async, username, password) {
466 send: function(data, moreOptions) {
468 checkState(1 , !sendFlag);
472 s = jQuery.extend( true,
476 moreOptions || ( moreOptions === false ? { global: false } : {} ) );
479 // We force the original context
480 // (plain objects used as context get extended)
481 s.context = moreOptions.context;
486 // If not internal, abort
488 done( 0 , "transport not found" );
492 // Allow custom headers/mimetypes and early abort
493 if ( s.beforeSend ) {
497 if ( s.beforeSend.call(callbackContext, xhr, s) === false || ! xhr.readyState || _s !== s ) {
500 if ( xhr.readyState && _s === s ) {
504 // Handle the global AJAX counter
505 if ( _s.global && ! --jQuery.active ) {
506 jQuery.event.trigger( "ajaxStop" );
517 globalEventContext.trigger("ajaxSend", [xhr, s]);
521 if ( s.async && s.timeout > 0 ) {
522 timeoutTimer = setTimeout(function(){
523 xhr.abort("timeout");
533 internal.send(requestHeaders, done);
540 done(0, "error", "" + e);
553 setRequestHeader: function(name,value) {
554 checkState(1, !sendFlag);
555 requestHeaders[ name.toLowerCase() ] = value;
560 getAllResponseHeaders: function() {
561 return xhr.readyState <= 1 ? "" : responseHeadersString;
564 // Builds headers hashtable if needed
565 getResponseHeader: function( key ) {
567 if ( xhr.readyState <= 1 ) {
573 if ( responseHeaders === undefined ) {
575 responseHeaders = {};
577 if ( typeof responseHeadersString === "string" ) {
581 while( ( match = rheaders.exec( responseHeadersString ) ) ) {
582 responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
586 return responseHeaders[ key.toLowerCase() ];
589 // Cancel the request
590 abort: function(statusText) {
592 internal.abort( statusText || "abort" );
598 // Init data (so that we can bind callbacks early
601 // Install callbacks related methods
602 jQuery.each(["bind","unbind"], function(_, name) {
603 xhr[name] = function(type) {
605 var functors = slice.call(arguments,1),
608 jQuery.each(type.split(/\s+/g), function() {
609 list = callbacksLists[this];
611 list[name].apply(list, functors );
619 jQuery.each(callbacksLists, function(name) {
621 xhr[name] = function() {
622 list = callbacksLists[name];
624 list.bind.apply(list, arguments );
630 // Return the xhr emulation
634 // Create a callback list
635 function createCBList() {
642 fire: function( flag , context ) {
644 // Save info for later bindings
645 fireArgs = arguments;
647 // Remove autoFire to keep bindings in order
650 var args = slice.call( fireArgs , 2 );
653 while ( flag && functors.length ) {
654 flag = functors.shift().apply( context , args ) !== false;
657 // Clean if asked to stop
668 var args = arguments,
670 length = args.length,
673 for ( ; i < length ; i++ ) {
677 if ( jQuery.isArray(func) ) {
679 list.bind.apply( list , func );
681 } else if ( isFunction(func) ) {
683 // Add if not already in
684 if ( ! pos( func ) ) {
685 functors.push( func );
691 list.fire.apply( list , fireArgs );
699 length = args.length,
705 for( ; i < length ; i++ ) {
707 if ( jQuery.isArray(func) ) {
708 list.unbind.apply(list,func);
709 } else if ( isFunction(func) ) {
710 position = pos(func);
712 functors.splice(position-1,1);
727 // Get the index of the functor in the list (1-based)
728 function pos( func ) {
729 for (var i = 0, length = functors.length; i < length && functors[i] !== func; i++) {
731 return i < length ? ( i + 1 ) : 0;
736 // Empty callbacks list
739 for (var i in list) {
740 list[i] = jQuery.noop;
747 jQuery.extend(jQuery.xhr, {
750 prefilter: function (functor) {
751 if ( isFunction(functor) ) {
752 jQuery.ajaxSettings.prefilters.push( functor );
757 // Bind a transport to one or more dataTypes
758 bindTransport: function () {
760 var args = arguments,
763 length = args.length,
770 transports = jQuery.ajaxSettings.transports;
774 if ( ! isFunction( args[ 0 ] ) ) {
776 dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
781 if ( dataTypes.length && start < length ) {
783 for ( i = start; i < length; i++ ) {
785 if ( isFunction(functor) ) {
786 functors.push( functor );
790 if ( functors.length ) {
792 jQuery.each ( dataTypes, function( _ , dataType ) {
794 first = /^\+/.test( dataType );
797 dataType = dataType.substr(1);
800 if ( dataType !== "" ) {
802 append = Array.prototype[ first ? "unshift" : "push" ];
804 list = transports[ dataType ];
806 jQuery.each ( functors, function( _ , functor ) {
810 list = transports[ dataType ] = [ functor ];
814 append.call( list , functor );
830 // Select a transport given options
831 function selectTransport( s ) {
833 var dataTypes = s.dataTypes,
842 function initSearch( dataType ) {
844 flag = transportDataType !== dataType && ! checked[ dataType ];
848 checked[ dataType ] = 1;
849 transportDataType = dataType;
850 transportsList = s.transports[ dataType ];
852 length = transportsList ? transportsList.length : 0 ;
858 initSearch( dataTypes[ 0 ] );
860 for ( i = 0 ; ! transport && i <= length ; i++ ) {
862 if ( i === length ) {
868 transport = transportsList[ i ]( s , determineDataType );
870 // If we got redirected to another dataType
871 // Search there (if not in progress or already tried)
872 if ( typeof( transport ) === "string" &&
873 initSearch( transport ) ) {
875 dataTypes.unshift( transport );
884 // Utility function that handles dataType when response is received
885 // (for those transports that can give text or xml responses)
886 function determineDataType( s , ct , text , xml ) {
888 var autoDataType = s.autoDataType,
891 dataTypes = s.dataTypes,
892 transportDataType = dataTypes[0],
895 // Auto (xml, json, script or text determined given headers)
896 if ( transportDataType === "*" ) {
898 for ( type in autoDataType ) {
899 if ( ( regexp = autoDataType[ type ] ) && regexp.test( ct ) ) {
900 transportDataType = dataTypes[0] = type;
906 // xml and parsed as such
907 if ( transportDataType === "xml" &&
909 xml.documentElement /* #4958 */ ) {
913 // Text response was provided
918 // If it's not really text, defer to dataConverters
919 if ( transportDataType !== "text" ) {
920 dataTypes.unshift( "text" );