Added a couple new AJAX methods and finished documenting everything.
[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 /**
87  * A function for serializing a set of input elements into
88  * a string of data.
89  *
90  * @example $("input[@type=text]").serialize();
91  * @before <input type='text' name='name' value='John'/>
92  * <input type='text' name='location' value='Boston'/>
93  * @after name=John&location=Boston
94  * @desc Serialize a selection of input elements to a string
95  *
96  * @name serialize
97  * @type String
98  * @cat AJAX
99  */
100 jQuery.fn.serialize = function(){
101         return $.param( this );
102 };
103
104 // If IE is used, create a wrapper for the XMLHttpRequest object
105 if ( jQuery.browser.msie && typeof XMLHttpRequest == "undefined" )
106         XMLHttpRequest = function(){
107                 return new ActiveXObject(
108                         navigator.userAgent.indexOf("MSIE 5") >= 0 ?
109                         "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP"
110                 );
111         };
112
113 // Attach a bunch of functions for handling common AJAX events
114
115 /**
116  * Attach a function to be executed whenever an AJAX request begins.
117  *
118  * @example $("#loading").ajaxStart(function(){
119  *   $(this).show();
120  * });
121  * @desc Show a loading message whenever an AJAX request starts.
122  *
123  * @name ajaxStart
124  * @type jQuery
125  * @param Function callback The function to execute.
126  * @cat AJAX
127  */
128  
129 /**
130  * Attach a function to be executed whenever all AJAX requests have ended.
131  *
132  * @example $("#loading").ajaxStop(function(){
133  *   $(this).hide();
134  * });
135  * @desc Hide a loading message after all the AJAX requests have stopped.
136  *
137  * @name ajaxStop
138  * @type jQuery
139  * @param Function callback The function to execute.
140  * @cat AJAX
141  */
142  
143 /**
144  * Attach a function to be executed whenever an AJAX request completes.
145  *
146  * @example $("#msg").ajaxComplete(function(){
147  *   $(this).append("<li>Request Complete.</li>");
148  * });
149  * @desc Show a message when an AJAX request completes.
150  *
151  * @name ajaxComplete
152  * @type jQuery
153  * @param Function callback The function to execute.
154  * @cat AJAX
155  */
156  
157 /**
158  * Attach a function to be executed whenever an AJAX request completes
159  * successfully.
160  *
161  * @example $("#msg").ajaxSuccess(function(){
162  *   $(this).append("<li>Successful Request!</li>");
163  * });
164  * @desc Show a message when an AJAX request completes successfully.
165  *
166  * @name ajaxSuccess
167  * @type jQuery
168  * @param Function callback The function to execute.
169  * @cat AJAX
170  */
171  
172 /**
173  * Attach a function to be executed whenever an AJAX request fails.
174  *
175  * @example $("#msg").ajaxError(function(){
176  *   $(this).append("<li>Error requesting page.</li>");
177  * });
178  * @desc Show a message when an AJAX request fails.
179  *
180  * @name ajaxError
181  * @type jQuery
182  * @param Function callback The function to execute.
183  * @cat AJAX
184  */
185
186 new function(){
187         var e = "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess".split(",");
188         
189         for ( var i = 0; i < e.length; i++ ) new function(){
190                 var o = e[i];
191                 jQuery.fn[o] = function(f){
192                         return this.bind(o, f);
193                 };
194         };
195 };
196
197 jQuery.extend({
198
199         /**
200          * Load a remote page using an HTTP GET request. All of the arguments to
201          * the method (except URL) are optional.
202          *
203          * @example $.get("test.cgi")
204          *
205          * @example $.get("test.cgi", { name: "John", time: "2pm" } )
206          *
207          * @example $.get("test.cgi", function(data){
208          *   alert("Data Loaded: " + data);
209          * })
210          *
211          * @example $.get("test.cgi",
212          *   { name: "John", time: "2pm" },
213          *   function(data){
214          *     alert("Data Loaded: " + data);
215          *   }
216          * )
217          *
218          * @name $.get
219          * @type jQuery
220          * @param String url The URL of the page to load.
221          * @param Hash params A set of key/value pairs that will be sent to the server.
222          * @param Function callback A function to be executed whenever the data is loaded.
223          * @cat AJAX
224          */
225         get: function( url, data, callback, type, ifModified ) {
226                 if ( data.constructor == Function ) {
227                         type = callback;
228                         callback = data;
229                         data = null;
230                 }
231                 
232                 if ( data ) url += "?" + jQuery.param(data);
233                 
234                 // Build and start the HTTP Request
235                 jQuery.ajax( "GET", url, null, function(r, status) {
236                         if ( callback ) callback( jQuery.httpData(r,type), status );
237                 }, ifModified);
238         },
239         
240         /**
241          * Load a remote page using an HTTP GET request, only if it hasn't
242          * been modified since it was last retrieved. All of the arguments to
243          * the method (except URL) are optional.
244          *
245          * @example $.getIfModified("test.html")
246          *
247          * @example $.getIfModified("test.html", { name: "John", time: "2pm" } )
248          *
249          * @example $.getIfModified("test.cgi", function(data){
250          *   alert("Data Loaded: " + data);
251          * })
252          *
253          * @example $.getifModified("test.cgi",
254          *   { name: "John", time: "2pm" },
255          *   function(data){
256          *     alert("Data Loaded: " + data);
257          *   }
258          * )
259          *
260          * @name $.getIfModified
261          * @type jQuery
262          * @param String url The URL of the page to load.
263          * @param Hash params A set of key/value pairs that will be sent to the server.
264          * @param Function callback A function to be executed whenever the data is loaded.
265          * @cat AJAX
266          */
267         getIfModified: function( url, data, callback, type ) {
268                 jQuery.get(url, data, callback, type, 1);
269         },
270
271         /**
272          * Loads, and executes, a remote JavaScript file using an HTTP GET request.
273          * All of the arguments to the method (except URL) are optional.
274          *
275          * @example $.getScript("test.js")
276          *
277          * @example $.getScript("test.js", function(){
278          *   alert("Script loaded and executed.");
279          * })
280          *
281          *
282          * @name $.getScript
283          * @type jQuery
284          * @param String url The URL of the page to load.
285          * @param Function callback A function to be executed whenever the data is loaded.
286          * @cat AJAX
287          */
288         getScript: function( url, data, callback ) {
289                 jQuery.get(url, data, callback, "script");
290         },
291         
292         /**
293          * Load a remote JSON object using an HTTP GET request.
294          * All of the arguments to the method (except URL) are optional.
295          *
296          * @example $.getJSON("test.js", function(json){
297          *   alert("JSON Data: " + json.users[3].name);
298          * })
299          *
300          * @example $.getJSON("test.js",
301          *   { name: "John", time: "2pm" },
302          *   function(json){
303          *     alert("JSON Data: " + json.users[3].name);
304          *   }
305          * )
306          *
307          * @name $.getJSON
308          * @type jQuery
309          * @param String url The URL of the page to load.
310          * @param Hash params A set of key/value pairs that will be sent to the server.
311          * @param Function callback A function to be executed whenever the data is loaded.
312          * @cat AJAX
313          */
314         getJSON: function( url, data, callback ) {
315                 jQuery.get(url, data, callback, "json");
316         },
317         
318         /**
319          * Load a remote page using an HTTP POST request. All of the arguments to
320          * the method (except URL) are optional.
321          *
322          * @example $.post("test.cgi")
323          *
324          * @example $.post("test.cgi", { name: "John", time: "2pm" } )
325          *
326          * @example $.post("test.cgi", function(data){
327          *   alert("Data Loaded: " + data);
328          * })
329          *
330          * @example $.post("test.cgi",
331          *   { name: "John", time: "2pm" },
332          *   function(data){
333          *     alert("Data Loaded: " + data);
334          *   }
335          * )
336          *
337          * @name $.post
338          * @type jQuery
339          * @param String url The URL of the page to load.
340          * @param Hash params A set of key/value pairs that will be sent to the server.
341          * @param Function callback A function to be executed whenever the data is loaded.
342          * @cat AJAX
343          */
344         post: function( url, data, callback, type ) {
345                 // Build and start the HTTP Request
346                 jQuery.ajax( "POST", url, jQuery.param(data), function(r, status) {
347                         if ( callback ) callback( jQuery.httpData(r,type), status );
348                 });
349         },
350         
351         // timeout (ms)
352         timeout: 0,
353
354         /**
355          * Set the timeout of all AJAX requests to a specific amount of time.
356          * This will make all future AJAX requests timeout after a specified amount
357          * of time (the default is no timeout).
358          *
359          * @example $.ajaxTimeout( 5000 );
360          * @desc Make all AJAX requests timeout after 5 seconds.
361          *
362          * @name $.ajaxTimeout
363          * @type jQuery
364          * @param Number time How long before an AJAX request times out.
365          * @cat AJAX
366          */
367         ajaxTimeout: function(timeout) {
368                 jQuery.timeout = timeout;
369         },
370
371         // Last-Modified header cache for next request
372         lastModified: {},
373         
374         /**
375          * Load a remote page using an HTTP request. This function is the primary
376          * means of making AJAX requests using jQuery. $.ajax() takes one property,
377          * an object of key/value pairs, that're are used to initalize the request.
378          *
379          * These are all the key/values that can be passed in to 'prop':
380          *
381          * (String) type - The type of request to make (e.g. "POST" or "GET").
382          *
383          * (String) url - The URL of the page to request.
384          * 
385          * (String) data - A string of data to be sent to the server (POST only).
386          *
387          * (String) dataType - The type of data that you're expecting back from
388          * the server (e.g. "xml", "html", "script", or "json").
389          *
390          * (Function) error - A function to be called if the request fails. The
391          * function gets passed two arguments: The XMLHttpRequest object and a
392          * string describing the type of error that occurred.
393          *
394          * (Function) success - A function to be called if the request succeeds. The
395          * function gets passed one argument: The data returned from the server,
396          * formatted according to the 'dataType' parameter.
397          *
398          * (Function) complete - A function to be called when the request finishes. The
399          * function gets passed two arguments: The XMLHttpRequest object and a
400          * string describing the type the success of the request.
401          *
402          * @example $.ajax({
403          *   type: "GET",
404          *   url: "test.js",
405          *   dataType: "script"
406          * })
407          * @desc Load and execute a JavaScript file.
408          *
409          * @example $.ajax({
410          *   type: "POST",
411          *   url: "some.php",
412          *   data: "name=John&location=Boston",
413          *   success: function(msg){
414          *     alert( "Data Saved: " + msg );
415          *   }
416          * });
417          * @desc Save some data to the server and notify the user once its complete.
418          *
419          * @name $.ajax
420          * @type jQuery
421          * @param Hash prop A set of properties to initialize the request with.
422          * @cat AJAX
423          */
424         ajax: function( type, url, data, ret, ifModified ) {
425                 // If only a single argument was passed in,
426                 // assume that it is a object of key/value pairs
427                 if ( !url ) {
428                         ret = type.complete;
429                         var success = type.success;
430                         var error = type.error;
431                         var dataType = type.dataType;
432                         data = type.data;
433                         url = type.url;
434                         type = type.type;
435                 }
436                 
437                 // Watch for a new set of requests
438                 if ( ! jQuery.active++ )
439                         jQuery.event.trigger( "ajaxStart" );
440
441                 var requestDone = false;
442         
443                 // Create the request object
444                 var xml = new XMLHttpRequest();
445         
446                 // Open the socket
447                 xml.open(type || "GET", url, true);
448                 
449                 // Set the correct header, if data is being sent
450                 if ( data )
451                         xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
452                 
453                 // Set the If-Modified-Since header, if ifModified mode.
454                 if ( ifModified )
455                         xml.setRequestHeader("If-Modified-Since",
456                                 jQuery.lastModified[url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
457                 
458                 // Set header so calling script knows that it's an XMLHttpRequest
459                 xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
460         
461                 // Make sure the browser sends the right content length
462                 if ( xml.overrideMimeType )
463                         xml.setRequestHeader("Connection", "close");
464                 
465                 // Wait for a response to come back
466                 var onreadystatechange = function(istimeout){
467                         // The transfer is complete and the data is available, or the request timed out
468                         if ( xml && (xml.readyState == 4 || istimeout == "timeout") ) {
469                                 requestDone = true;
470
471                                 var status = jQuery.httpSuccess( xml ) && istimeout != "timeout" ?
472                                         ifModified && jQuery.httpNotModified( xml, url ) ? "notmodified" : "success" : "error";
473                                 
474                                 // Make sure that the request was successful or notmodified
475                                 if ( status != "error" ) {
476                                         // Cache Last-Modified header, if ifModified mode.
477                                         var modRes = xml.getResponseHeader("Last-Modified");
478                                         if ( ifModified && modRes ) jQuery.lastModified[url] = modRes;
479                                         
480                                         // If a local callback was specified, fire it
481                                         if ( success )
482                                                 success( jQuery.httpData( xml, dataType ), status );
483                                         
484                                         // Fire the global callback
485                                         jQuery.event.trigger( "ajaxSuccess" );
486                                 
487                                 // Otherwise, the request was not successful
488                                 } else {
489                                         // If a local callback was specified, fire it
490                                         if ( error ) error( xml, status );
491                                         
492                                         // Fire the global callback
493                                         jQuery.event.trigger( "ajaxError" );
494                                 }
495                                 
496                                 // The request was completed
497                                 jQuery.event.trigger( "ajaxComplete" );
498                                 
499                                 // Handle the global AJAX counter
500                                 if ( ! --jQuery.active )
501                                         jQuery.event.trigger( "ajaxStop" );
502         
503                                 // Process result
504                                 if ( ret ) ret(xml, status);
505                                 
506                                 // Stop memory leaks
507                                 xml.onreadystatechange = function(){};
508                                 xml = null;
509                                 
510                         }
511                 };
512                 xml.onreadystatechange = onreadystatechange;
513                 
514                 // Timeout checker
515                 if(jQuery.timeout > 0)
516                         setTimeout(function(){
517                                 // Check to see if the request is still happening
518                                 if (xml) {
519                                         // Cancel the request
520                                         xml.abort();
521
522                                         if ( !requestDone ) onreadystatechange( "timeout" );
523
524                                         // Clear from memory
525                                         xml = null;
526                                 }
527                         }, jQuery.timeout);
528                 
529                 // Send the data
530                 xml.send(data);
531         },
532         
533         // Counter for holding the number of active queries
534         active: 0,
535         
536         // Determines if an XMLHttpRequest was successful or not
537         httpSuccess: function(r) {
538                 try {
539                         return !r.status && location.protocol == "file:" ||
540                                 ( r.status >= 200 && r.status < 300 ) || r.status == 304 ||
541                                 jQuery.browser.safari && r.status == undefined;
542                 } catch(e){}
543
544                 return false;
545         },
546
547         // Determines if an XMLHttpRequest returns NotModified
548         httpNotModified: function(xml, url) {
549                 try {
550                         var xmlRes = xml.getResponseHeader("Last-Modified");
551
552                         // Firefox always returns 200. check Last-Modified date
553                         return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
554                                 jQuery.browser.safari && xml.status == undefined;
555                 } catch(e){}
556
557                 return false;
558         },
559         
560         /* Get the data out of an XMLHttpRequest.
561          * Return parsed XML if content-type header is "xml" and type is "xml" or omitted,
562          * otherwise return plain text.
563          * (String) data - The type of data that you're expecting back,
564          * (e.g. "xml", "html", "script")
565          */
566         httpData: function(r,type) {
567                 var ct = r.getResponseHeader("content-type");
568                 var data = !type && ct && ct.indexOf("xml") >= 0;
569                 data = type == "xml" || data ? r.responseXML : r.responseText;
570
571                 // If the type is "script", eval it
572                 if ( type == "script" ) eval.call( window, data );
573
574                 // Get the JavaScript object, if JSON is used.
575                 if ( type == "json" ) eval( "data = " + data );
576
577                 return data;
578         },
579         
580         // Serialize an array of form elements or a set of
581         // key/values into a query string
582         param: function(a) {
583                 var s = [];
584                 
585                 // If an array was passed in, assume that it is an array
586                 // of form elements
587                 if ( a.constructor == Array || a.jquery ) {
588                         // Serialize the form elements
589                         for ( var i = 0; i < a.length; i++ )
590                                 s.push( a[i].name + "=" + encodeURIComponent( a[i].value ) );
591                         
592                 // Otherwise, assume that it's an object of key/value pairs
593                 } else {
594                         // Serialize the key/values
595                         for ( var j in a )
596                                 s.push( j + "=" + encodeURIComponent( a[j] ) );
597                 }
598                 
599                 // Return the resulting serialization
600                 return s.join("&");
601         }
602
603 });