62785f355c9320ab652f3464f2c4532b7fbb0bfe
[jquery.git] / src / ajax / ajax.js
1 jQuery.fn.extend({
2         // DEPRECATED
3         loadIfModified: function( url, params, callback ) {
4                 this.load( url, params, callback, 1 );
5         },
6
7         load: function( url, params, callback, ifModified ) {
8                 if ( jQuery.isFunction( url ) )
9                         return this.bind("load", url);
10
11                 var off = url.indexOf(" ");
12                 if ( off >= 0 ) {
13                         var selector = url.slice(off, url.length);
14                         url = url.slice(0, off);
15                 }
16
17                 callback = callback || function(){};
18
19                 // Default to a GET request
20                 var type = "GET";
21
22                 // If the second parameter was provided
23                 if ( params )
24                         // If it's a function
25                         if ( jQuery.isFunction( params ) ) {
26                                 // We assume that it's the callback
27                                 callback = params;
28                                 params = null;
29
30                         // Otherwise, build a param string
31                         } else {
32                                 params = jQuery.param( params );
33                                 type = "POST";
34                         }
35
36                 var self = this;
37
38                 // Request the remote document
39                 jQuery.ajax({
40                         url: url,
41                         type: type,
42                         data: params,
43                         ifModified: ifModified,
44                         complete: function(res, status){
45                                 // If successful, inject the HTML into all the matched elements
46                                 if ( status == "success" || !ifModified && status == "notmodified" )
47                                         // See if a selector was specified
48                                         self.html( selector ?
49                                                 // Create a dummy div to hold the results
50                                                 jQuery("<div/>")
51                                                         // inject the contents of the document in, removing the scripts
52                                                         // to avoid any 'Permission Denied' errors in IE
53                                                         .append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
54
55                                                         // Locate the specified elements
56                                                         .find(selector) :
57
58                                                 // If not, just inject the full result
59                                                 res.responseText );
60
61                                 // Add delay to account for Safari's delay in globalEval
62                                 setTimeout(function(){
63                                         self.each( callback, [res.responseText, status, res] );
64                                 }, 13);
65                         }
66                 });
67                 return this;
68         },
69
70         serialize: function() {
71                 return jQuery.param( this );
72         },
73
74         // DEPRECATED
75         // This method no longer does anything - all script evaluation is
76         // taken care of within the HTML injection methods.
77         evalScripts: function(){}
78
79 });
80
81 // Attach a bunch of functions for handling common AJAX events
82 jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
83         jQuery.fn[o] = function(f){
84                 return this.bind(o, f);
85         };
86 });
87
88 var jsc = (new Date).getTime();
89
90 jQuery.extend({
91         get: function( url, data, callback, type, ifModified ) {
92                 // shift arguments if data argument was ommited
93                 if ( jQuery.isFunction( data ) ) {
94                         callback = data;
95                         data = null;
96                 }
97                 
98                 return jQuery.ajax({
99                         type: "GET",
100                         url: url,
101                         data: data,
102                         success: callback,
103                         dataType: type,
104                         ifModified: ifModified
105                 });
106         },
107
108         // DEPRECATED
109         getIfModified: function( url, data, callback, type ) {
110                 return jQuery.get(url, data, callback, type, 1);
111         },
112
113         getScript: function( url, callback ) {
114                 return jQuery.get(url, null, callback, "script");
115         },
116
117         getJSON: function( url, data, callback ) {
118                 return jQuery.get(url, data, callback, "json");
119         },
120
121         post: function( url, data, callback, type ) {
122                 if ( jQuery.isFunction( data ) ) {
123                         callback = data;
124                         data = {};
125                 }
126
127                 return jQuery.ajax({
128                         type: "POST",
129                         url: url,
130                         data: data,
131                         success: callback,
132                         dataType: type
133                 });
134         },
135
136         // DEPRECATED
137         ajaxTimeout: function( timeout ) {
138                 jQuery.ajaxSettings.timeout = timeout;
139         },
140         
141         ajaxSetup: function( settings ) {
142                 jQuery.extend( jQuery.ajaxSettings, settings );
143         },
144
145         ajaxSettings: {
146                 global: true,
147                 type: "GET",
148                 timeout: 0,
149                 contentType: "application/x-www-form-urlencoded",
150                 processData: true,
151                 async: true,
152                 data: null
153         },
154         
155         // Last-Modified header cache for next request
156         lastModified: {},
157
158         ajax: function( s ) {
159                 var jsonp, jsre = /=(\?|%3F)/g, status, data;
160
161                 // Extend the settings, but re-extend 's' so that it can be
162                 // checked again later (in the test suite, specifically)
163                 s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
164
165                 // convert data if not already a string
166                 if ( s.data && s.processData && typeof s.data != "string" )
167                         s.data = jQuery.param(s.data);
168
169                 // Break the data into one single string
170                 var q = s.url.indexOf("?");
171                 if ( q > -1 ) {
172                         s.data = (s.data ? s.data + "&" : "") + s.url.slice(q + 1);
173                         s.url = s.url.slice(0, q);
174                 }
175
176                 // Handle JSONP Parameter Callbacks
177                 if ( s.dataType == "jsonp" ) {
178                         if ( !s.data || !s.data.match(jsre) )
179                                 s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
180                         s.dataType = "json";
181                 }
182
183                 // Build temporary JSONP function
184                 if ( s.dataType == "json" && s.data && s.data.match(jsre) ) {
185                         jsonp = "jsonp" + jsc++;
186                         s.data = s.data.replace(jsre, "=" + jsonp);
187
188                         // We need to make sure
189                         // that a JSONP style response is executed properly
190                         s.dataType = "script";
191
192                         // Handle JSONP-style loading
193                         window[ jsonp ] = function(tmp){
194                                 data = tmp;
195                                 success();
196                                 // Garbage collect
197                                 window[ jsonp ] = undefined;
198                                 try{ delete window[ jsonp ]; } catch(e){}
199                         };
200                 }
201
202                 if ( s.dataType == "script" && s.cache == null )
203                         s.cache = false;
204
205                 if ( s.cache === false && s.type.toLowerCase() == "get" )
206                         s.data = (s.data ? s.data + "&" : "") + "_=" + (new Date()).getTime();
207
208                 // If data is available, append data to url for get requests
209                 if ( s.data && s.type.toLowerCase() == "get" ) {
210                         s.url += "?" + s.data;
211
212                         // IE likes to send both get and post data, prevent this
213                         s.data = null;
214                 }
215
216                 // Watch for a new set of requests
217                 if ( s.global && ! jQuery.active++ )
218                         jQuery.event.trigger( "ajaxStart" );
219
220                 // If we're requesting a remote document
221                 // and trying to load JSON or Script
222                 if ( !s.url.indexOf("http") && s.dataType == "script" ) {
223                         var script = document.createElement("script");
224                         script.src = s.url;
225
226                         // Handle Script loading
227                         if ( !jsonp && (s.success || s.complete) ) {
228                                 var done = false;
229
230                                 // Attach handlers for all browsers
231                                 script.onload = script.onreadystatechange = function(){
232                                         if ( !done && (!this.readyState || 
233                                                         this.readyState == "loaded" || this.readyState == "complete") ) {
234                                                 done = true;
235                                                 success();
236                                                 complete();
237                                                 document.body.removeChild( script );
238                                         }
239                                 };
240                         }
241
242                         document.body.appendChild(script);
243
244                         // We handle everything using the script element injection
245                         return;
246                 }
247
248                 var requestDone = false;
249
250                 // Create the request object; Microsoft failed to properly
251                 // implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
252                 var xml = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
253
254                 // Open the socket
255                 xml.open(s.type, s.url, s.async);
256
257                 // Set the correct header, if data is being sent
258                 if ( s.data )
259                         xml.setRequestHeader("Content-Type", s.contentType);
260
261                 // Set the If-Modified-Since header, if ifModified mode.
262                 if ( s.ifModified )
263                         xml.setRequestHeader("If-Modified-Since",
264                                 jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
265
266                 // Set header so the called script knows that it's an XMLHttpRequest
267                 xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
268
269                 // Allow custom headers/mimetypes
270                 if ( s.beforeSend )
271                         s.beforeSend(xml);
272                         
273                 if ( s.global )
274                     jQuery.event.trigger("ajaxSend", [xml, s]);
275
276                 // Wait for a response to come back
277                 var onreadystatechange = function(isTimeout){
278                         // The transfer is complete and the data is available, or the request timed out
279                         if ( !requestDone && xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
280                                 requestDone = true;
281                                 
282                                 // clear poll interval
283                                 if (ival) {
284                                         clearInterval(ival);
285                                         ival = null;
286                                 }
287                                 
288                                 status = isTimeout == "timeout" && "timeout" ||
289                                         !jQuery.httpSuccess( xml ) && "error" ||
290                                         s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" ||
291                                         "success";
292
293                                 if ( status == "success" ) {
294                                         // Watch for, and catch, XML document parse errors
295                                         try {
296                                                 // process the data (runs the xml through httpData regardless of callback)
297                                                 data = jQuery.httpData( xml, s.dataType );
298                                         } catch(e) {
299                                                 status = "parsererror";
300                                         }
301                                 }
302
303                                 // Make sure that the request was successful or notmodified
304                                 if ( status == "success" ) {
305                                         // Cache Last-Modified header, if ifModified mode.
306                                         var modRes;
307                                         try {
308                                                 modRes = xml.getResponseHeader("Last-Modified");
309                                         } catch(e) {} // swallow exception thrown by FF if header is not available
310         
311                                         if ( s.ifModified && modRes )
312                                                 jQuery.lastModified[s.url] = modRes;
313
314                                         // JSONP handles its own success callback
315                                         if ( !jsonp )
316                                                 success();      
317                                 } else
318                                         jQuery.handleError(s, xml, status);
319
320                                 // Fire the complete handlers
321                                 complete();
322
323                                 // Stop memory leaks
324                                 if ( s.async )
325                                         xml = null;
326                         }
327                 };
328                 
329                 if ( s.async ) {
330                         // don't attach the handler to the request, just poll it instead
331                         var ival = setInterval(onreadystatechange, 13); 
332
333                         // Timeout checker
334                         if ( s.timeout > 0 )
335                                 setTimeout(function(){
336                                         // Check to see if the request is still happening
337                                         if ( xml ) {
338                                                 // Cancel the request
339                                                 xml.abort();
340         
341                                                 if( !requestDone )
342                                                         onreadystatechange( "timeout" );
343                                         }
344                                 }, s.timeout);
345                 }
346                         
347                 // Send the data
348                 try {
349                         xml.send(s.data);
350                 } catch(e) {
351                         jQuery.handleError(s, xml, null, e);
352                 }
353                 
354                 // firefox 1.5 doesn't fire statechange for sync requests
355                 if ( !s.async )
356                         onreadystatechange();
357                 
358                 // return XMLHttpRequest to allow aborting the request etc.
359                 return xml;
360
361                 function success(){
362                         // If a local callback was specified, fire it and pass it the data
363                         if ( s.success )
364                                 s.success( data, status );
365
366                         // Fire the global callback
367                         if ( s.global )
368                                 jQuery.event.trigger( "ajaxSuccess", [xml, s] );
369                 }
370
371                 function complete(){
372                         // Process result
373                         if ( s.complete )
374                                 s.complete(xml, status);
375
376                         // The request was completed
377                         if ( s.global )
378                                 jQuery.event.trigger( "ajaxComplete", [xml, s] );
379
380                         // Handle the global AJAX counter
381                         if ( s.global && ! --jQuery.active )
382                                 jQuery.event.trigger( "ajaxStop" );
383                 }
384         },
385
386         handleError: function( s, xml, status, e ) {
387                 // If a local callback was specified, fire it
388                 if ( s.error ) s.error( xml, status, e );
389
390                 // Fire the global callback
391                 if ( s.global )
392                         jQuery.event.trigger( "ajaxError", [xml, s, e] );
393         },
394
395         // Counter for holding the number of active queries
396         active: 0,
397
398         // Determines if an XMLHttpRequest was successful or not
399         httpSuccess: function( r ) {
400                 try {
401                         return !r.status && location.protocol == "file:" ||
402                                 ( r.status >= 200 && r.status < 300 ) || r.status == 304 ||
403                                 jQuery.browser.safari && r.status == undefined;
404                 } catch(e){}
405                 return false;
406         },
407
408         // Determines if an XMLHttpRequest returns NotModified
409         httpNotModified: function( xml, url ) {
410                 try {
411                         var xmlRes = xml.getResponseHeader("Last-Modified");
412
413                         // Firefox always returns 200. check Last-Modified date
414                         return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
415                                 jQuery.browser.safari && xml.status == undefined;
416                 } catch(e){}
417                 return false;
418         },
419
420         httpData: function( r, type ) {
421                 var ct = r.getResponseHeader("content-type");
422                 var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0;
423                 var data = xml ? r.responseXML : r.responseText;
424
425                 if ( xml && data.documentElement.tagName == "parsererror" )
426                         throw "parsererror";
427
428                 // If the type is "script", eval it in global context
429                 if ( type == "script" )
430                         jQuery.globalEval( data );
431
432                 // Get the JavaScript object, if JSON is used.
433                 if ( type == "json" )
434                         data = eval("(" + data + ")");
435
436                 return data;
437         },
438
439         // Serialize an array of form elements or a set of
440         // key/values into a query string
441         param: function( a ) {
442                 var s = [];
443
444                 // If an array was passed in, assume that it is an array
445                 // of form elements
446                 if ( a.constructor == Array || a.jquery )
447                         // Serialize the form elements
448                         jQuery.each( a, function(){
449                                 s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
450                         });
451
452                 // Otherwise, assume that it's an object of key/value pairs
453                 else
454                         // Serialize the key/values
455                         for ( var j in a )
456                                 // If the value is an array then the key names need to be repeated
457                                 if ( a[j] && a[j].constructor == Array )
458                                         jQuery.each( a[j], function(){
459                                                 s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
460                                         });
461                                 else
462                                         s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );
463
464                 // Return the resulting serialization
465                 return s.join("&");
466         }
467
468 });