About half done documenting the AJAX module.
[jquery.git] / src / ajax / ajax.js
1 // AJAX Plugin
2 // Docs Here:
3 // http://jquery.com/docs/ajax/
4
5 /**
6  * Load HTML from a remote file and inject it into the DOM, only if it's
7  * been modified by the server.
8  *
9  * @example $("#feeds").loadIfModified("feeds.html")
10  * @before <div id="feeds"></div>
11  * @result <div id="feeds"><b>45</b> feeds found.</div>
12  *
13  * @name loadIfModified
14  * @type jQuery
15  * @param String url The URL of the HTML file to load.
16  * @param Hash params A set of key/value pairs that will be sent to the server.
17  * @param Function callback A function to be executed whenever the data is loaded.
18  * @cat AJAX
19  */
20 jQuery.fn.loadIfModified = function( url, params, callback ) {
21         this.load( url, params, callback, 1 );
22 };
23
24 /**
25  * Load HTML from a remote file and inject it into the DOM.
26  *
27  * @example $("#feeds").load("feeds.html")
28  * @before <div id="feeds"></div>
29  * @result <div id="feeds"><b>45</b> feeds found.</div>
30  *
31  * @name load
32  * @type jQuery
33  * @param String url The URL of the HTML file to load.
34  * @param Hash params A set of key/value pairs that will be sent to the server.
35  * @param Function callback A function to be executed whenever the data is loaded.
36  * @cat AJAX
37  */
38 jQuery.fn.load = function( url, params, callback, ifModified ) {
39         if ( url.constructor == Function )
40                 return this.bind("load", url);
41
42         callback = callback || function(){};
43
44         // Default to a GET request
45         var type = "GET";
46
47         // If the second parameter was provided
48         if ( params ) {
49                 // If it's a function
50                 if ( params.constructor == Function ) {
51                         // We assume that it's the callback
52                         callback = params;
53                         params = null;
54                         
55                 // Otherwise, build a param string
56                 } else {
57                         params = jQuery.param( params );
58                         type = "POST";
59                 }
60         }
61         
62         var self = this;
63         
64         // Request the remote document
65         jQuery.ajax( type, url, params,function(res, status){
66                 
67                 if ( status == "success" || !ifModified && status == "notmodified" ) {
68                         // Inject the HTML into all the matched elements
69                         self.html(res.responseText).each( callback, [res.responseText, status] );
70                         
71                         // Execute all the scripts inside of the newly-injected HTML
72                         $("script", self).each(function(){
73                                 if ( this.src )
74                                         $.getScript( this.src );
75                                 else
76                                         eval.call( window, this.text || this.textContent || this.innerHTML || "" );
77                         });
78                 } else
79                         callback.apply( self, [res.responseText, status] );
80
81         }, ifModified);
82         
83         return this;
84 };
85
86 // If IE is used, create a wrapper for the XMLHttpRequest object
87 if ( jQuery.browser.msie && typeof XMLHttpRequest == "undefined" )
88         XMLHttpRequest = function(){
89                 return new ActiveXObject(
90                         navigator.userAgent.indexOf("MSIE 5") >= 0 ?
91                         "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP"
92                 );
93         };
94
95 // Attach a bunch of functions for handling common AJAX events
96 new function(){
97         var e = "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess".split(',');
98         
99         for ( var i = 0; i < e.length; i++ ) new function(){
100                 var o = e[i];
101                 jQuery.fn[o] = function(f){
102                         return this.bind(o, f);
103                 };
104         };
105 };
106
107 jQuery.extend({
108
109         /**
110          * Load a remote page using an HTTP GET request.
111          *
112          * @example $.get("test.cgi")
113          *
114          * @name $.get
115          * @type jQuery
116          * @param String url The URL of the HTML file to load.
117          * @cat AJAX
118          */
119          
120         /**
121          * Load a remote page using an HTTP GET request.
122          *
123          * @example $.get("test.cgi", { name: "John", time: "2pm" } )
124          *
125          * @name $.get
126          * @type jQuery
127          * @param String url The URL of the HTML file to load.
128          * @param Hash params A set of key/value pairs that will be sent to the server.
129          * @cat AJAX
130          */
131          
132         /**
133          * Load a remote page using an HTTP GET request.
134          *
135          * @example $.get("test.cgi", function(){
136          *   alert("Data Loaded.");
137          * })
138          *
139          * @name $.get
140          * @type jQuery
141          * @param String url The URL of the HTML file to load.
142          * @param Function callback A function to be executed whenever the data is loaded.
143          * @cat AJAX
144          */
145
146         /**
147          * Load a remote page using an HTTP GET request.
148          *
149          * @example $.get("test.cgi",
150          *   { name: "John", time: "2pm" },
151          *   function(){ alert("Data Loaded."); }
152          * )
153          *
154          * @name $.get
155          * @type jQuery
156          * @param String url The URL of the HTML file to load.
157          * @param Hash params A set of key/value pairs that will be sent to the server.
158          * @param Function callback A function to be executed whenever the data is loaded.
159          * @cat AJAX
160          */
161         get: function( url, data, callback, type, ifModified ) {
162                 if ( data.constructor == Function ) {
163                         type = callback;
164                         callback = data;
165                         data = null;
166                 }
167                 
168                 if ( data ) url += "?" + jQuery.param(data);
169                 
170                 // Build and start the HTTP Request
171                 jQuery.ajax( "GET", url, null, function(r, status) {
172                         if ( callback ) callback( jQuery.httpData(r,type), status );
173                 }, ifModified);
174         },
175         
176         /**
177          * Load a remote page using an HTTP GET request, and only if it hasn't
178          * been modified since it was last retieved.
179          *
180          * @example $.getIfModified("test.cgi")
181          *
182          * @name $.getIfModified
183          * @type jQuery
184          * @param String url The URL of the HTML file to load.
185          * @cat AJAX
186          */
187          
188         /**
189          * Load a remote page using an HTTP GET request, and only if it hasn't
190          * been modified since it was last retieved.
191          *
192          * @example $.getIfModified("test.cgi", { name: "John", time: "2pm" })
193          *
194          * @name $.getIfModified
195          * @type jQuery
196          * @param String url The URL of the HTML file to load.
197          * @param Hash params A set of key/value pairs that will be sent to the server.
198          * @cat AJAX
199          */
200          
201         /**
202          * Load a remote page using an HTTP GET request, and only if it hasn't
203          * been modified since it was last retieved.
204          *
205          * @example $.getIfModified("test.cgi", function(){
206          *   alert("Data Loaded.");
207          * })
208          *
209          * @name $.getIfModified
210          * @type jQuery
211          * @param String url The URL of the HTML file to load.
212          * @param Function callback A function to be executed whenever the data is loaded.
213          * @cat AJAX
214          */
215
216         /**
217          * Load a remote page using an HTTP GET request, and only if it hasn't
218          * been modified since it was last retieved.
219          *
220          * @example $.getIfModified("test.cgi",
221          *   { name: "John", time: "2pm" },
222          *   function(){ alert("Data Loaded."); }
223          * )
224          *
225          * @name $.getIfModified
226          * @type jQuery
227          * @param String url The URL of the HTML file to load.
228          * @param Hash params A set of key/value pairs that will be sent to the server.
229          * @param Function callback A function to be executed whenever the data is loaded.
230          * @cat AJAX
231          */
232         getIfModified: function( url, data, callback, type ) {
233                 jQuery.get(url, data, callback, type, 1);
234         },
235
236         getScript: function( url, data, callback ) {
237                 jQuery.get(url, data, callback, "script");
238         },
239         
240         /**
241          * Load a remote page using a POST request.
242          */
243         post: function( url, data, callback, type ) {
244                 // Build and start the HTTP Request
245                 jQuery.ajax( "POST", url, jQuery.param(data), function(r, status) {
246                         if ( callback ) callback( jQuery.httpData(r,type), status );
247                 });
248         },
249         
250         // timeout (ms)
251         timeout: 0,
252
253         ajaxTimeout: function(timeout) {
254                 jQuery.timeout = timeout;
255         },
256
257         // Last-Modified header cache for next request
258         lastModified: {},
259         
260         /**
261          * A common wrapper for making XMLHttpRequests
262          */
263         ajax: function( type, url, data, ret, ifModified ) {
264                 // If only a single argument was passed in,
265                 // assume that it is a object of key/value pairs
266                 if ( !url ) {
267                         ret = type.complete;
268                         var success = type.success;
269                         var error = type.error;
270                         data = type.data;
271                         url = type.url;
272                         type = type.type;
273                 }
274                 
275                 // Watch for a new set of requests
276                 if ( ! jQuery.active++ )
277                         jQuery.event.trigger( "ajaxStart" );
278
279                 var requestDone = false;
280         
281                 // Create the request object
282                 var xml = new XMLHttpRequest();
283         
284                 // Open the socket
285                 xml.open(type || "GET", url, true);
286                 
287                 // Set the correct header, if data is being sent
288                 if ( data )
289                         xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
290                 
291                 // Set the If-Modified-Since header, if ifModified mode.
292                 if ( ifModified )
293                         xml.setRequestHeader("If-Modified-Since",
294                                 jQuery.lastModified[url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
295                 
296                 // Set header so calling script knows that it's an XMLHttpRequest
297                 xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
298         
299                 // Make sure the browser sends the right content length
300                 if ( xml.overrideMimeType )
301                         xml.setRequestHeader("Connection", "close");
302                 
303                 // Wait for a response to come back
304                 var onreadystatechange = function(istimeout){
305                         // The transfer is complete and the data is available, or the request timed out
306                         if ( xml && (xml.readyState == 4 || istimeout == "timeout") ) {
307                                 requestDone = true;
308
309                                 var status = jQuery.httpSuccess( xml ) && istimeout != "timeout" ?
310                                         ifModified && jQuery.httpNotModified( xml, url ) ? "notmodified" : "success" : "error";
311                                 
312                                 // Make sure that the request was successful or notmodified
313                                 if ( status != "error" ) {
314                                         // Cache Last-Modified header, if ifModified mode.
315                                         var modRes = xml.getResponseHeader("Last-Modified");
316                                         if ( ifModified && modRes ) jQuery.lastModified[url] = modRes;
317                                         
318                                         // If a local callback was specified, fire it
319                                         if ( success ) success( xml, status );
320                                         
321                                         // Fire the global callback
322                                         jQuery.event.trigger( "ajaxSuccess" );
323                                 
324                                 // Otherwise, the request was not successful
325                                 } else {
326                                         // If a local callback was specified, fire it
327                                         if ( error ) error( xml, status );
328                                         
329                                         // Fire the global callback
330                                         jQuery.event.trigger( "ajaxError" );
331                                 }
332                                 
333                                 // The request was completed
334                                 jQuery.event.trigger( "ajaxComplete" );
335                                 
336                                 // Handle the global AJAX counter
337                                 if ( ! --jQuery.active )
338                                         jQuery.event.trigger( "ajaxStop" );
339         
340                                 // Process result
341                                 if ( ret ) ret(xml, status);
342                                 
343                                 // Stop memory leaks
344                                 xml.onreadystatechange = function(){};
345                                 xml = null;
346                                 
347                         }
348                 };
349                 xml.onreadystatechange = onreadystatechange;
350                 
351                 // Timeout checker
352                 if(jQuery.timeout > 0)
353                         setTimeout(function(){
354                                 // Check to see if the request is still happening
355                                 if (xml) {
356                                         // Cancel the request
357                                         xml.abort();
358
359                                         if ( !requestDone ) onreadystatechange( "timeout" );
360
361                                         // Clear from memory
362                                         xml = null;
363                                 }
364                         }, jQuery.timeout);
365                 
366                 // Send the data
367                 xml.send(data);
368         },
369         
370         // Counter for holding the number of active queries
371         active: 0,
372         
373         // Determines if an XMLHttpRequest was successful or not
374         httpSuccess: function(r) {
375                 try {
376                         return !r.status && location.protocol == "file:" ||
377                                 ( r.status >= 200 && r.status < 300 ) || r.status == 304 ||
378                                 jQuery.browser.safari && r.status == undefined;
379                 } catch(e){}
380
381                 return false;
382         },
383
384         // Determines if an XMLHttpRequest returns NotModified
385         httpNotModified: function(xml, url) {
386                 try {
387                         var xmlRes = xml.getResponseHeader("Last-Modified");
388
389                         // Firefox always returns 200. check Last-Modified date
390                         return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
391                                 jQuery.browser.safari && xml.status == undefined;
392                 } catch(e){}
393
394                 return false;
395         },
396         
397         // Get the data out of an XMLHttpRequest.
398         // Return parsed XML if content-type header is "xml" and type is "xml" or omitted,
399         // otherwise return plain text.
400         httpData: function(r,type) {
401                 var ct = r.getResponseHeader("content-type");
402                 var data = !type && ct && ct.indexOf("xml") >= 0;
403                 data = type == "xml" || data ? r.responseXML : r.responseText;
404
405                 // If the type is "script", eval it
406                 if ( type == "script" ) eval.call( window, data );
407
408                 // Get the JavaScript object, if JSON is used.
409                 if ( type == "json" ) eval( "data = " + data );
410
411                 return data;
412         },
413         
414         // Serialize an array of form elements or a set of
415         // key/values into a query string
416         param: function(a) {
417                 var s = [];
418                 
419                 // If an array was passed in, assume that it is an array
420                 // of form elements
421                 if ( a.constructor == Array ) {
422                         // Serialize the form elements
423                         for ( var i = 0; i < a.length; i++ )
424                                 s.push( a[i].name + "=" + encodeURIComponent( a[i].value ) );
425                         
426                 // Otherwise, assume that it's an object of key/value pairs
427                 } else {
428                         // Serialize the key/values
429                         for ( var j in a )
430                                 s.push( j + "=" + encodeURIComponent( a[j] ) );
431                 }
432                 
433                 // Return the resulting serialization
434                 return s.join("&");
435         }
436
437 });