Remove forever-alone regexps in event.js
[jquery.git] / src / ajax.js
blobcfd43d991fdab060b10d2d18daafa8c5c54c3af7
1 (function( jQuery ) {
3 var r20 = /%20/g,
4         rbracket = /\[\]$/,
5         rCRLF = /\r?\n/g,
6         rhash = /#.*$/,
7         rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
8         rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
9         // #7653, #8125, #8152: local protocol detection
10         rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
11         rnoContent = /^(?:GET|HEAD)$/,
12         rprotocol = /^\/\//,
13         rquery = /\?/,
14         rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
15         rselectTextarea = /^(?:select|textarea)/i,
16         rspacesAjax = /\s+/,
17         rts = /([?&])_=[^&]*/,
18         rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
20         // Keep a copy of the old load method
21         _load = jQuery.fn.load,
23         /* Prefilters
24          * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
25          * 2) These are called:
26          *    - BEFORE asking for a transport
27          *    - AFTER param serialization (s.data is a string if s.processData is true)
28          * 3) key is the dataType
29          * 4) the catchall symbol "*" can be used
30          * 5) execution will start with transport dataType and THEN continue down to "*" if needed
31          */
32         prefilters = {},
34         /* Transports bindings
35          * 1) key is the dataType
36          * 2) the catchall symbol "*" can be used
37          * 3) selection will start with transport dataType and THEN go to "*" if needed
38          */
39         transports = {},
41         // Document location
42         ajaxLocation,
44         // Document location segments
45         ajaxLocParts,
47         // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
48         allTypes = ["*/"] + ["*"];
50 // #8138, IE may throw an exception when accessing
51 // a field from window.location if document.domain has been set
52 try {
53         ajaxLocation = location.href;
54 } catch( e ) {
55         // Use the href attribute of an A element
56         // since IE will modify it given document.location
57         ajaxLocation = document.createElement( "a" );
58         ajaxLocation.href = "";
59         ajaxLocation = ajaxLocation.href;
62 // Segment location into parts
63 ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
65 // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
66 function addToPrefiltersOrTransports( structure ) {
68         // dataTypeExpression is optional and defaults to "*"
69         return function( dataTypeExpression, func ) {
71                 if ( typeof dataTypeExpression !== "string" ) {
72                         func = dataTypeExpression;
73                         dataTypeExpression = "*";
74                 }
76                 if ( jQuery.isFunction( func ) ) {
77                         var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
78                                 i = 0,
79                                 length = dataTypes.length,
80                                 dataType,
81                                 list,
82                                 placeBefore;
84                         // For each dataType in the dataTypeExpression
85                         for ( ; i < length; i++ ) {
86                                 dataType = dataTypes[ i ];
87                                 // We control if we're asked to add before
88                                 // any existing element
89                                 placeBefore = /^\+/.test( dataType );
90                                 if ( placeBefore ) {
91                                         dataType = dataType.substr( 1 ) || "*";
92                                 }
93                                 list = structure[ dataType ] = structure[ dataType ] || [];
94                                 // then we add to the structure accordingly
95                                 list[ placeBefore ? "unshift" : "push" ]( func );
96                         }
97                 }
98         };
101 // Base inspection function for prefilters and transports
102 function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
103                 dataType /* internal */, inspected /* internal */ ) {
105         dataType = dataType || options.dataTypes[ 0 ];
106         inspected = inspected || {};
108         inspected[ dataType ] = true;
110         var list = structure[ dataType ],
111                 i = 0,
112                 length = list ? list.length : 0,
113                 executeOnly = ( structure === prefilters ),
114                 selection;
116         for ( ; i < length && ( executeOnly || !selection ); i++ ) {
117                 selection = list[ i ]( options, originalOptions, jqXHR );
118                 // If we got redirected to another dataType
119                 // we try there if executing only and not done already
120                 if ( typeof selection === "string" ) {
121                         if ( !executeOnly || inspected[ selection ] ) {
122                                 selection = undefined;
123                         } else {
124                                 options.dataTypes.unshift( selection );
125                                 selection = inspectPrefiltersOrTransports(
126                                                 structure, options, originalOptions, jqXHR, selection, inspected );
127                         }
128                 }
129         }
130         // If we're only executing or nothing was selected
131         // we try the catchall dataType if not done already
132         if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
133                 selection = inspectPrefiltersOrTransports(
134                                 structure, options, originalOptions, jqXHR, "*", inspected );
135         }
136         // unnecessary when only executing (prefilters)
137         // but it'll be ignored by the caller in that case
138         return selection;
141 // A special extend for ajax options
142 // that takes "flat" options (not to be deep extended)
143 // Fixes #9887
144 function ajaxExtend( target, src ) {
145         var key, deep,
146                 flatOptions = jQuery.ajaxSettings.flatOptions || {};
147         for ( key in src ) {
148                 if ( src[ key ] !== undefined ) {
149                         ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
150                 }
151         }
152         if ( deep ) {
153                 jQuery.extend( true, target, deep );
154         }
157 jQuery.fn.extend({
158         load: function( url, params, callback ) {
159                 if ( typeof url !== "string" && _load ) {
160                         return _load.apply( this, arguments );
162                 // Don't do a request if no elements are being requested
163                 } else if ( !this.length ) {
164                         return this;
165                 }
167                 var off = url.indexOf( " " );
168                 if ( off >= 0 ) {
169                         var selector = url.slice( off, url.length );
170                         url = url.slice( 0, off );
171                 }
173                 // Default to a GET request
174                 var type = "GET";
176                 // If the second parameter was provided
177                 if ( params ) {
178                         // If it's a function
179                         if ( jQuery.isFunction( params ) ) {
180                                 // We assume that it's the callback
181                                 callback = params;
182                                 params = undefined;
184                         // Otherwise, build a param string
185                         } else if ( typeof params === "object" ) {
186                                 params = jQuery.param( params, jQuery.ajaxSettings.traditional );
187                                 type = "POST";
188                         }
189                 }
191                 var self = this;
193                 // Request the remote document
194                 jQuery.ajax({
195                         url: url,
196                         type: type,
197                         dataType: "html",
198                         data: params,
199                         // Complete callback (responseText is used internally)
200                         complete: function( jqXHR, status, responseText ) {
201                                 // Store the response as specified by the jqXHR object
202                                 responseText = jqXHR.responseText;
203                                 // If successful, inject the HTML into all the matched elements
204                                 if ( jqXHR.isResolved() ) {
205                                         // #4825: Get the actual response in case
206                                         // a dataFilter is present in ajaxSettings
207                                         jqXHR.done(function( r ) {
208                                                 responseText = r;
209                                         });
210                                         // See if a selector was specified
211                                         self.html( selector ?
212                                                 // Create a dummy div to hold the results
213                                                 jQuery("<div>")
214                                                         // inject the contents of the document in, removing the scripts
215                                                         // to avoid any 'Permission Denied' errors in IE
216                                                         .append(responseText.replace(rscript, ""))
218                                                         // Locate the specified elements
219                                                         .find(selector) :
221                                                 // If not, just inject the full result
222                                                 responseText );
223                                 }
225                                 if ( callback ) {
226                                         self.each( callback, [ responseText, status, jqXHR ] );
227                                 }
228                         }
229                 });
231                 return this;
232         },
234         serialize: function() {
235                 return jQuery.param( this.serializeArray() );
236         },
238         serializeArray: function() {
239                 return this.map(function(){
240                         return this.elements ? jQuery.makeArray( this.elements ) : this;
241                 })
242                 .filter(function(){
243                         return this.name && !this.disabled &&
244                                 ( this.checked || rselectTextarea.test( this.nodeName ) ||
245                                         rinput.test( this.type ) );
246                 })
247                 .map(function( i, elem ){
248                         var val = jQuery( this ).val();
250                         return val == null ?
251                                 null :
252                                 jQuery.isArray( val ) ?
253                                         jQuery.map( val, function( val, i ){
254                                                 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
255                                         }) :
256                                         { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
257                 }).get();
258         }
261 // Attach a bunch of functions for handling common AJAX events
262 jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
263         jQuery.fn[ o ] = function( f ){
264                 return this.bind( o, f );
265         };
268 jQuery.each( [ "get", "post" ], function( i, method ) {
269         jQuery[ method ] = function( url, data, callback, type ) {
270                 // shift arguments if data argument was omitted
271                 if ( jQuery.isFunction( data ) ) {
272                         type = type || callback;
273                         callback = data;
274                         data = undefined;
275                 }
277                 return jQuery.ajax({
278                         type: method,
279                         url: url,
280                         data: data,
281                         success: callback,
282                         dataType: type
283                 });
284         };
287 jQuery.extend({
289         getScript: function( url, callback ) {
290                 return jQuery.get( url, undefined, callback, "script" );
291         },
293         getJSON: function( url, data, callback ) {
294                 return jQuery.get( url, data, callback, "json" );
295         },
297         // Creates a full fledged settings object into target
298         // with both ajaxSettings and settings fields.
299         // If target is omitted, writes into ajaxSettings.
300         ajaxSetup: function( target, settings ) {
301                 if ( settings ) {
302                         // Building a settings object
303                         ajaxExtend( target, jQuery.ajaxSettings );
304                 } else {
305                         // Extending ajaxSettings
306                         settings = target;
307                         target = jQuery.ajaxSettings;
308                 }
309                 ajaxExtend( target, settings );
310                 return target;
311         },
313         ajaxSettings: {
314                 url: ajaxLocation,
315                 isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
316                 global: true,
317                 type: "GET",
318                 contentType: "application/x-www-form-urlencoded",
319                 processData: true,
320                 async: true,
321                 /*
322                 timeout: 0,
323                 data: null,
324                 dataType: null,
325                 username: null,
326                 password: null,
327                 cache: null,
328                 traditional: false,
329                 headers: {},
330                 */
332                 accepts: {
333                         xml: "application/xml, text/xml",
334                         html: "text/html",
335                         text: "text/plain",
336                         json: "application/json, text/javascript",
337                         "*": allTypes
338                 },
340                 contents: {
341                         xml: /xml/,
342                         html: /html/,
343                         json: /json/
344                 },
346                 responseFields: {
347                         xml: "responseXML",
348                         text: "responseText"
349                 },
351                 // List of data converters
352                 // 1) key format is "source_type destination_type" (a single space in-between)
353                 // 2) the catchall symbol "*" can be used for source_type
354                 converters: {
356                         // Convert anything to text
357                         "* text": window.String,
359                         // Text to html (true = no transformation)
360                         "text html": true,
362                         // Evaluate text as a json expression
363                         "text json": jQuery.parseJSON,
365                         // Parse text as xml
366                         "text xml": jQuery.parseXML
367                 },
369                 // For options that shouldn't be deep extended:
370                 // you can add your own custom options here if
371                 // and when you create one that shouldn't be
372                 // deep extended (see ajaxExtend)
373                 flatOptions: {
374                         context: true,
375                         url: true
376                 }
377         },
379         ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
380         ajaxTransport: addToPrefiltersOrTransports( transports ),
382         // Main method
383         ajax: function( url, options ) {
385                 // If url is an object, simulate pre-1.5 signature
386                 if ( typeof url === "object" ) {
387                         options = url;
388                         url = undefined;
389                 }
391                 // Force options to be an object
392                 options = options || {};
394                 var // Create the final options object
395                         s = jQuery.ajaxSetup( {}, options ),
396                         // Callbacks context
397                         callbackContext = s.context || s,
398                         // Context for global events
399                         // It's the callbackContext if one was provided in the options
400                         // and if it's a DOM node or a jQuery collection
401                         globalEventContext = callbackContext !== s &&
402                                 ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
403                                                 jQuery( callbackContext ) : jQuery.event,
404                         // Deferreds
405                         deferred = jQuery.Deferred(),
406                         completeDeferred = jQuery.Callbacks( "once memory" ),
407                         // Status-dependent callbacks
408                         statusCode = s.statusCode || {},
409                         // ifModified key
410                         ifModifiedKey,
411                         // Headers (they are sent all at once)
412                         requestHeaders = {},
413                         requestHeadersNames = {},
414                         // Response headers
415                         responseHeadersString,
416                         responseHeaders,
417                         // transport
418                         transport,
419                         // timeout handle
420                         timeoutTimer,
421                         // Cross-domain detection vars
422                         parts,
423                         // The jqXHR state
424                         state = 0,
425                         // To know if global events are to be dispatched
426                         fireGlobals,
427                         // Loop variable
428                         i,
429                         // Fake xhr
430                         jqXHR = {
432                                 readyState: 0,
434                                 // Caches the header
435                                 setRequestHeader: function( name, value ) {
436                                         if ( !state ) {
437                                                 var lname = name.toLowerCase();
438                                                 name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
439                                                 requestHeaders[ name ] = value;
440                                         }
441                                         return this;
442                                 },
444                                 // Raw string
445                                 getAllResponseHeaders: function() {
446                                         return state === 2 ? responseHeadersString : null;
447                                 },
449                                 // Builds headers hashtable if needed
450                                 getResponseHeader: function( key ) {
451                                         var match;
452                                         if ( state === 2 ) {
453                                                 if ( !responseHeaders ) {
454                                                         responseHeaders = {};
455                                                         while( ( match = rheaders.exec( responseHeadersString ) ) ) {
456                                                                 responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
457                                                         }
458                                                 }
459                                                 match = responseHeaders[ key.toLowerCase() ];
460                                         }
461                                         return match === undefined ? null : match;
462                                 },
464                                 // Overrides response content-type header
465                                 overrideMimeType: function( type ) {
466                                         if ( !state ) {
467                                                 s.mimeType = type;
468                                         }
469                                         return this;
470                                 },
472                                 // Cancel the request
473                                 abort: function( statusText ) {
474                                         statusText = statusText || "abort";
475                                         if ( transport ) {
476                                                 transport.abort( statusText );
477                                         }
478                                         done( 0, statusText );
479                                         return this;
480                                 }
481                         };
483                 // Callback for when everything is done
484                 // It is defined here because jslint complains if it is declared
485                 // at the end of the function (which would be more logical and readable)
486                 function done( status, nativeStatusText, responses, headers ) {
488                         // Called once
489                         if ( state === 2 ) {
490                                 return;
491                         }
493                         // State is "done" now
494                         state = 2;
496                         // Clear timeout if it exists
497                         if ( timeoutTimer ) {
498                                 clearTimeout( timeoutTimer );
499                         }
501                         // Dereference transport for early garbage collection
502                         // (no matter how long the jqXHR object will be used)
503                         transport = undefined;
505                         // Cache response headers
506                         responseHeadersString = headers || "";
508                         // Set readyState
509                         jqXHR.readyState = status > 0 ? 4 : 0;
511                         var isSuccess,
512                                 success,
513                                 error,
514                                 statusText = nativeStatusText,
515                                 response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
516                                 lastModified,
517                                 etag;
519                         // If successful, handle type chaining
520                         if ( status >= 200 && status < 300 || status === 304 ) {
522                                 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
523                                 if ( s.ifModified ) {
525                                         if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
526                                                 jQuery.lastModified[ ifModifiedKey ] = lastModified;
527                                         }
528                                         if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
529                                                 jQuery.etag[ ifModifiedKey ] = etag;
530                                         }
531                                 }
533                                 // If not modified
534                                 if ( status === 304 ) {
536                                         statusText = "notmodified";
537                                         isSuccess = true;
539                                 // If we have data
540                                 } else {
542                                         try {
543                                                 success = ajaxConvert( s, response );
544                                                 statusText = "success";
545                                                 isSuccess = true;
546                                         } catch(e) {
547                                                 // We have a parsererror
548                                                 statusText = "parsererror";
549                                                 error = e;
550                                         }
551                                 }
552                         } else {
553                                 // We extract error from statusText
554                                 // then normalize statusText and status for non-aborts
555                                 error = statusText;
556                                 if ( !statusText || status ) {
557                                         statusText = "error";
558                                         if ( status < 0 ) {
559                                                 status = 0;
560                                         }
561                                 }
562                         }
564                         // Set data for the fake xhr object
565                         jqXHR.status = status;
566                         jqXHR.statusText = "" + ( nativeStatusText || statusText );
568                         // Success/Error
569                         if ( isSuccess ) {
570                                 deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
571                         } else {
572                                 deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
573                         }
575                         // Status-dependent callbacks
576                         jqXHR.statusCode( statusCode );
577                         statusCode = undefined;
579                         if ( fireGlobals ) {
580                                 globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
581                                                 [ jqXHR, s, isSuccess ? success : error ] );
582                         }
584                         // Complete
585                         completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
587                         if ( fireGlobals ) {
588                                 globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
589                                 // Handle the global AJAX counter
590                                 if ( !( --jQuery.active ) ) {
591                                         jQuery.event.trigger( "ajaxStop" );
592                                 }
593                         }
594                 }
596                 // Attach deferreds
597                 deferred.promise( jqXHR );
598                 jqXHR.success = jqXHR.done;
599                 jqXHR.error = jqXHR.fail;
600                 jqXHR.complete = completeDeferred.add;
602                 // Status-dependent callbacks
603                 jqXHR.statusCode = function( map ) {
604                         if ( map ) {
605                                 var tmp;
606                                 if ( state < 2 ) {
607                                         for ( tmp in map ) {
608                                                 statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
609                                         }
610                                 } else {
611                                         tmp = map[ jqXHR.status ];
612                                         jqXHR.then( tmp, tmp );
613                                 }
614                         }
615                         return this;
616                 };
618                 // Remove hash character (#7531: and string promotion)
619                 // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
620                 // We also use the url parameter if available
621                 s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
623                 // Extract dataTypes list
624                 s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
626                 // Determine if a cross-domain request is in order
627                 if ( s.crossDomain == null ) {
628                         parts = rurl.exec( s.url.toLowerCase() );
629                         s.crossDomain = !!( parts &&
630                                 ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
631                                         ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
632                                                 ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
633                         );
634                 }
636                 // Convert data if not already a string
637                 if ( s.data && s.processData && typeof s.data !== "string" ) {
638                         s.data = jQuery.param( s.data, s.traditional );
639                 }
641                 // Apply prefilters
642                 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
644                 // If request was aborted inside a prefiler, stop there
645                 if ( state === 2 ) {
646                         return false;
647                 }
649                 // We can fire global events as of now if asked to
650                 fireGlobals = s.global;
652                 // Uppercase the type
653                 s.type = s.type.toUpperCase();
655                 // Determine if request has content
656                 s.hasContent = !rnoContent.test( s.type );
658                 // Watch for a new set of requests
659                 if ( fireGlobals && jQuery.active++ === 0 ) {
660                         jQuery.event.trigger( "ajaxStart" );
661                 }
663                 // More options handling for requests with no content
664                 if ( !s.hasContent ) {
666                         // If data is available, append data to url
667                         if ( s.data ) {
668                                 s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
669                                 // #9682: remove data so that it's not used in an eventual retry
670                                 delete s.data;
671                         }
673                         // Get ifModifiedKey before adding the anti-cache parameter
674                         ifModifiedKey = s.url;
676                         // Add anti-cache in url if needed
677                         if ( s.cache === false ) {
679                                 var ts = jQuery.now(),
680                                         // try replacing _= if it is there
681                                         ret = s.url.replace( rts, "$1_=" + ts );
683                                 // if nothing was replaced, add timestamp to the end
684                                 s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
685                         }
686                 }
688                 // Set the correct header, if data is being sent
689                 if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
690                         jqXHR.setRequestHeader( "Content-Type", s.contentType );
691                 }
693                 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
694                 if ( s.ifModified ) {
695                         ifModifiedKey = ifModifiedKey || s.url;
696                         if ( jQuery.lastModified[ ifModifiedKey ] ) {
697                                 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
698                         }
699                         if ( jQuery.etag[ ifModifiedKey ] ) {
700                                 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
701                         }
702                 }
704                 // Set the Accepts header for the server, depending on the dataType
705                 jqXHR.setRequestHeader(
706                         "Accept",
707                         s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
708                                 s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
709                                 s.accepts[ "*" ]
710                 );
712                 // Check for headers option
713                 for ( i in s.headers ) {
714                         jqXHR.setRequestHeader( i, s.headers[ i ] );
715                 }
717                 // Allow custom headers/mimetypes and early abort
718                 if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
719                                 // Abort if not done already
720                                 jqXHR.abort();
721                                 return false;
723                 }
725                 // Install callbacks on deferreds
726                 for ( i in { success: 1, error: 1, complete: 1 } ) {
727                         jqXHR[ i ]( s[ i ] );
728                 }
730                 // Get transport
731                 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
733                 // If no transport, we auto-abort
734                 if ( !transport ) {
735                         done( -1, "No Transport" );
736                 } else {
737                         jqXHR.readyState = 1;
738                         // Send global event
739                         if ( fireGlobals ) {
740                                 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
741                         }
742                         // Timeout
743                         if ( s.async && s.timeout > 0 ) {
744                                 timeoutTimer = setTimeout( function(){
745                                         jqXHR.abort( "timeout" );
746                                 }, s.timeout );
747                         }
749                         try {
750                                 state = 1;
751                                 transport.send( requestHeaders, done );
752                         } catch (e) {
753                                 // Propagate exception as error if not done
754                                 if ( state < 2 ) {
755                                         done( -1, e );
756                                 // Simply rethrow otherwise
757                                 } else {
758                                         jQuery.error( e );
759                                 }
760                         }
761                 }
763                 return jqXHR;
764         },
766         // Serialize an array of form elements or a set of
767         // key/values into a query string
768         param: function( a, traditional ) {
769                 var s = [],
770                         add = function( key, value ) {
771                                 // If value is a function, invoke it and return its value
772                                 value = jQuery.isFunction( value ) ? value() : value;
773                                 s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
774                         };
776                 // Set traditional to true for jQuery <= 1.3.2 behavior.
777                 if ( traditional === undefined ) {
778                         traditional = jQuery.ajaxSettings.traditional;
779                 }
781                 // If an array was passed in, assume that it is an array of form elements.
782                 if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
783                         // Serialize the form elements
784                         jQuery.each( a, function() {
785                                 add( this.name, this.value );
786                         });
788                 } else {
789                         // If traditional, encode the "old" way (the way 1.3.2 or older
790                         // did it), otherwise encode params recursively.
791                         for ( var prefix in a ) {
792                                 buildParams( prefix, a[ prefix ], traditional, add );
793                         }
794                 }
796                 // Return the resulting serialization
797                 return s.join( "&" ).replace( r20, "+" );
798         }
801 function buildParams( prefix, obj, traditional, add ) {
802         if ( jQuery.isArray( obj ) ) {
803                 // Serialize array item.
804                 jQuery.each( obj, function( i, v ) {
805                         if ( traditional || rbracket.test( prefix ) ) {
806                                 // Treat each array item as a scalar.
807                                 add( prefix, v );
809                         } else {
810                                 // If array item is non-scalar (array or object), encode its
811                                 // numeric index to resolve deserialization ambiguity issues.
812                                 // Note that rack (as of 1.0.0) can't currently deserialize
813                                 // nested arrays properly, and attempting to do so may cause
814                                 // a server error. Possible fixes are to modify rack's
815                                 // deserialization algorithm or to provide an option or flag
816                                 // to force array serialization to be shallow.
817                                 buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
818                         }
819                 });
821         } else if ( !traditional && obj != null && typeof obj === "object" ) {
822                 // Serialize object item.
823                 for ( var name in obj ) {
824                         buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
825                 }
827         } else {
828                 // Serialize scalar item.
829                 add( prefix, obj );
830         }
833 // This is still on the jQuery object... for now
834 // Want to move this to jQuery.ajax some day
835 jQuery.extend({
837         // Counter for holding the number of active queries
838         active: 0,
840         // Last-Modified header cache for next request
841         lastModified: {},
842         etag: {}
846 /* Handles responses to an ajax request:
847  * - sets all responseXXX fields accordingly
848  * - finds the right dataType (mediates between content-type and expected dataType)
849  * - returns the corresponding response
850  */
851 function ajaxHandleResponses( s, jqXHR, responses ) {
853         var contents = s.contents,
854                 dataTypes = s.dataTypes,
855                 responseFields = s.responseFields,
856                 ct,
857                 type,
858                 finalDataType,
859                 firstDataType;
861         // Fill responseXXX fields
862         for ( type in responseFields ) {
863                 if ( type in responses ) {
864                         jqXHR[ responseFields[type] ] = responses[ type ];
865                 }
866         }
868         // Remove auto dataType and get content-type in the process
869         while( dataTypes[ 0 ] === "*" ) {
870                 dataTypes.shift();
871                 if ( ct === undefined ) {
872                         ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
873                 }
874         }
876         // Check if we're dealing with a known content-type
877         if ( ct ) {
878                 for ( type in contents ) {
879                         if ( contents[ type ] && contents[ type ].test( ct ) ) {
880                                 dataTypes.unshift( type );
881                                 break;
882                         }
883                 }
884         }
886         // Check to see if we have a response for the expected dataType
887         if ( dataTypes[ 0 ] in responses ) {
888                 finalDataType = dataTypes[ 0 ];
889         } else {
890                 // Try convertible dataTypes
891                 for ( type in responses ) {
892                         if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
893                                 finalDataType = type;
894                                 break;
895                         }
896                         if ( !firstDataType ) {
897                                 firstDataType = type;
898                         }
899                 }
900                 // Or just use first one
901                 finalDataType = finalDataType || firstDataType;
902         }
904         // If we found a dataType
905         // We add the dataType to the list if needed
906         // and return the corresponding response
907         if ( finalDataType ) {
908                 if ( finalDataType !== dataTypes[ 0 ] ) {
909                         dataTypes.unshift( finalDataType );
910                 }
911                 return responses[ finalDataType ];
912         }
915 // Chain conversions given the request and the original response
916 function ajaxConvert( s, response ) {
918         // Apply the dataFilter if provided
919         if ( s.dataFilter ) {
920                 response = s.dataFilter( response, s.dataType );
921         }
923         var dataTypes = s.dataTypes,
924                 converters = {},
925                 i,
926                 key,
927                 length = dataTypes.length,
928                 tmp,
929                 // Current and previous dataTypes
930                 current = dataTypes[ 0 ],
931                 prev,
932                 // Conversion expression
933                 conversion,
934                 // Conversion function
935                 conv,
936                 // Conversion functions (transitive conversion)
937                 conv1,
938                 conv2;
940         // For each dataType in the chain
941         for ( i = 1; i < length; i++ ) {
943                 // Create converters map
944                 // with lowercased keys
945                 if ( i === 1 ) {
946                         for ( key in s.converters ) {
947                                 if ( typeof key === "string" ) {
948                                         converters[ key.toLowerCase() ] = s.converters[ key ];
949                                 }
950                         }
951                 }
953                 // Get the dataTypes
954                 prev = current;
955                 current = dataTypes[ i ];
957                 // If current is auto dataType, update it to prev
958                 if ( current === "*" ) {
959                         current = prev;
960                 // If no auto and dataTypes are actually different
961                 } else if ( prev !== "*" && prev !== current ) {
963                         // Get the converter
964                         conversion = prev + " " + current;
965                         conv = converters[ conversion ] || converters[ "* " + current ];
967                         // If there is no direct converter, search transitively
968                         if ( !conv ) {
969                                 conv2 = undefined;
970                                 for ( conv1 in converters ) {
971                                         tmp = conv1.split( " " );
972                                         if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
973                                                 conv2 = converters[ tmp[1] + " " + current ];
974                                                 if ( conv2 ) {
975                                                         conv1 = converters[ conv1 ];
976                                                         if ( conv1 === true ) {
977                                                                 conv = conv2;
978                                                         } else if ( conv2 === true ) {
979                                                                 conv = conv1;
980                                                         }
981                                                         break;
982                                                 }
983                                         }
984                                 }
985                         }
986                         // If we found no converter, dispatch an error
987                         if ( !( conv || conv2 ) ) {
988                                 jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
989                         }
990                         // If found converter is not an equivalence
991                         if ( conv !== true ) {
992                                 // Convert with 1 or 2 converters accordingly
993                                 response = conv ? conv( response ) : conv2( conv1(response) );
994                         }
995                 }
996         }
997         return response;
1000 })( jQuery );