Added support for:
[jquery.git] / src / ajax / ajax.js
index fa04718..0b965e4 100644 (file)
@@ -15,6 +15,7 @@ jQuery.fn.extend({
         * @param Function callback (optional) A function to be executed whenever the data is loaded (parameters: responseText, status and response itself).
         * @cat Ajax
         */
+       // DEPRECATED
        loadIfModified: function( url, params, callback ) {
                this.load( url, params, callback, 1 );
        },
@@ -47,6 +48,12 @@ jQuery.fn.extend({
                if ( jQuery.isFunction( url ) )
                        return this.bind("load", url);
 
+               var off = url.indexOf(" ");
+               if ( off >= 0 ) {
+                       var selector = url.slice(off, url.length);
+                       url = url.slice(0, off);
+               }
+
                callback = callback || function(){};
 
                // Default to a GET request
@@ -77,9 +84,24 @@ jQuery.fn.extend({
                        complete: function(res, status){
                                // If successful, inject the HTML into all the matched elements
                                if ( status == "success" || !ifModified && status == "notmodified" )
-                                       self.html(res.responseText)
-
-                               self.each( callback, [res.responseText, status, res] );
+                                       // See if a selector was specified
+                                       self.html( selector ?
+                                               // Create a dummy div to hold the results
+                                               jQuery("<div/>")
+                                                       // inject the contents of the document in, removing the scripts
+                                                       // to avoid any 'Permission Denied' errors in IE
+                                                       .append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
+
+                                                       // Locate the specified elements
+                                                       .find(selector) :
+
+                                               // If not, just inject the full result
+                                               res.responseText );
+
+                               // Add delay to account for Safari's delay in globalEval
+                               setTimeout(function(){
+                                       self.each( callback, [res.responseText, status, res] );
+                               }, 13);
                        }
                });
                return this;
@@ -106,7 +128,12 @@ jQuery.fn.extend({
         */
        serialize: function() {
                return jQuery.param( this );
-       }
+       },
+
+       // DEPRECATED
+       // This method no longer does anything - all script evaluation is
+       // taken care of within the HTML injection methods.
+       evalScripts: function(){}
 
 });
 
@@ -217,6 +244,8 @@ jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".sp
        };
 });
 
+var jsc = (new Date).getTime();
+
 jQuery.extend({
 
        /**
@@ -294,6 +323,7 @@ jQuery.extend({
         * @param Function callback (optional) A function to be executed whenever the data is loaded successfully.
         * @cat Ajax
         */
+       // DEPRECATED
        getIfModified: function( url, data, callback, type ) {
                return jQuery.get(url, data, callback, type, 1);
        },
@@ -406,6 +436,7 @@ jQuery.extend({
         * @param Number time How long before an AJAX request times out, in milliseconds.
         * @cat Ajax
         */
+       // DEPRECATED
        ajaxTimeout: function( timeout ) {
                jQuery.ajaxSettings.timeout = timeout;
        },
@@ -570,27 +601,95 @@ jQuery.extend({
         * @see ajaxSetup(Map)
         */
        ajax: function( s ) {
-               // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
-               s = jQuery.extend({}, jQuery.ajaxSettings, s);
-
-               // if data available
-               if ( s.data ) {
-                       // convert data if not already a string
-                       if (s.processData && typeof s.data != "string")
-                       s.data = jQuery.param(s.data);
-                       // append data to url for get requests
-                       if( s.type.toLowerCase() == "get" ) {
-                               // "?" + data or "&" + data (in case there are already params)
-                               s.url += ((s.url.indexOf("?") > -1) ? "&" : "?") + s.data;
-                               // IE likes to send both get and post data, prevent this
-                               s.data = null;
-                       }
+               var jsonp, jsre = /=(\?|%3F)/g, status, data;
+
+               // Extend the settings, but re-extend 's' so that it can be
+               // checked again later (in the test suite, specifically)
+               s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
+
+               // convert data if not already a string
+               if ( s.data && s.processData && typeof s.data != "string" )
+                       s.data = jQuery.param(s.data);
+
+               // Break the data into one single string
+               var q = s.url.indexOf("?");
+               if ( q > -1 ) {
+                       s.data = (s.data ? s.data + "&" : "") + s.url.slice(q + 1);
+                       s.url = s.url.slice(0, q);
+               }
+
+               // Handle JSONP Parameter Callbacks
+               if ( s.dataType == "jsonp" ) {
+                       if ( !s.data || !s.data.match(jsre) )
+                               s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+                       s.dataType = "json";
+               }
+
+               // Build temporary JSONP function
+               if ( s.dataType == "json" && s.data && s.data.match(jsre) ) {
+                       jsonp = "jsonp" + jsc++;
+                       s.data = s.data.replace(jsre, "=" + jsonp);
+
+                       // We need to make sure
+                       // that a JSONP style response is executed properly
+                       s.dataType = "script";
+
+                       // Handle JSONP-style loading
+                       window[ jsonp ] = function(tmp){
+                               data = tmp;
+                               success();
+                               // Garbage collect
+                               window[ jsonp ] = undefined;
+                               try{ delete window[ jsonp ]; } catch(e){}
+                       };
+               }
+
+               if ( s.dataType == "script" && s.cache == null )
+                       s.cache = false;
+
+               if ( s.cache === false && s.type.toLowerCase() == "get" )
+                       s.data = (s.data ? s.data + "&" : "") + "_=" + (new Date()).getTime();
+
+               // If data is available, append data to url for get requests
+               if ( s.data && s.type.toLowerCase() == "get" ) {
+                       s.url += "?" + s.data;
+
+                       // IE likes to send both get and post data, prevent this
+                       s.data = null;
                }
 
                // Watch for a new set of requests
                if ( s.global && ! jQuery.active++ )
                        jQuery.event.trigger( "ajaxStart" );
 
+               // If we're requesting a remote document
+               // and trying to load JSON or Script
+               if ( !s.url.indexOf("http") && s.dataType == "script" ) {
+                       var script = document.createElement("script");
+                       script.src = s.url;
+
+                       // Handle Script loading
+                       if ( !jsonp && (s.success || s.complete) ) {
+                               var done = false;
+
+                               // Attach handlers for all browsers
+                               script.onload = script.onreadystatechange = function(){
+                                       if ( !done && (!this.readyState || 
+                                                       this.readyState == "loaded" || this.readyState == "complete") ) {
+                                               done = true;
+                                               success();
+                                               complete();
+                                               document.body.removeChild( script );
+                                       }
+                               };
+                       }
+
+                       document.body.appendChild(script);
+
+                       // We handle everything using the script element injection
+                       return;
+               }
+
                var requestDone = false;
 
                // Create the request object; Microsoft failed to properly
@@ -613,7 +712,7 @@ jQuery.extend({
                xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
 
                // Allow custom headers/mimetypes
-               if( s.beforeSend )
+               if ( s.beforeSend )
                        s.beforeSend(xml);
                        
                if ( s.global )
@@ -622,7 +721,7 @@ jQuery.extend({
                // Wait for a response to come back
                var onreadystatechange = function(isTimeout){
                        // The transfer is complete and the data is available, or the request timed out
-                       if ( xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
+                       if ( !requestDone && xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
                                requestDone = true;
                                
                                // clear poll interval
@@ -631,74 +730,64 @@ jQuery.extend({
                                        ival = null;
                                }
                                
-                               var status;
-                               try {
-                                       status = isTimeout == "timeout" && "timeout" ||
-                                               !jQuery.httpSuccess( xml ) && "error" ||
-                                               s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" ||
-                                               "success";
-
-                                       // Make sure that the request was successful or notmodified
-                                       if ( status != "error" && status != "timeout" ) {
-                                               // Cache Last-Modified header, if ifModified mode.
-                                               var modRes;
-                                               try {
-                                                       modRes = xml.getResponseHeader("Last-Modified");
-                                               } catch(e) {} // swallow exception thrown by FF if header is not available
-       
-                                               if ( s.ifModified && modRes )
-                                                       jQuery.lastModified[s.url] = modRes;
-       
+                               status = isTimeout == "timeout" && "timeout" ||
+                                       !jQuery.httpSuccess( xml ) && "error" ||
+                                       s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" ||
+                                       "success";
+
+                               if ( status == "success" ) {
+                                       // Watch for, and catch, XML document parse errors
+                                       try {
                                                // process the data (runs the xml through httpData regardless of callback)
-                                               var data = jQuery.httpData( xml, s.dataType );
-       
-                                               // If a local callback was specified, fire it and pass it the data
-                                               if ( s.success )
-                                                       s.success( data, status );
-       
-                                               // Fire the global callback
-                                               if( s.global )
-                                                       jQuery.event.trigger( "ajaxSuccess", [xml, s] );
-                                       } else
-                                               jQuery.handleError(s, xml, status);
-                               } catch(e) {
-                                       status = "error";
-                                       jQuery.handleError(s, xml, status, e);
+                                               data = jQuery.httpData( xml, s.dataType );
+                                       } catch(e) {
+                                               status = "parsererror";
+                                       }
                                }
 
-                               // The request was completed
-                               if( s.global )
-                                       jQuery.event.trigger( "ajaxComplete", [xml, s] );
+                               // Make sure that the request was successful or notmodified
+                               if ( status == "success" ) {
+                                       // Cache Last-Modified header, if ifModified mode.
+                                       var modRes;
+                                       try {
+                                               modRes = xml.getResponseHeader("Last-Modified");
+                                       } catch(e) {} // swallow exception thrown by FF if header is not available
+       
+                                       if ( s.ifModified && modRes )
+                                               jQuery.lastModified[s.url] = modRes;
 
-                               // Handle the global AJAX counter
-                               if ( s.global && ! --jQuery.active )
-                                       jQuery.event.trigger( "ajaxStop" );
+                                       // JSONP handles its own success callback
+                                       if ( !jsonp )
+                                               success();      
+                               } else
+                                       jQuery.handleError(s, xml, status);
 
-                               // Process result
-                               if ( s.complete )
-                                       s.complete(xml, status);
+                               // Fire the complete handlers
+                               complete();
 
                                // Stop memory leaks
-                               if(s.async)
+                               if ( s.async )
                                        xml = null;
                        }
                };
                
-               // don't attach the handler to the request, just poll it instead
-               var ival = setInterval(onreadystatechange, 13); 
-
-               // Timeout checker
-               if ( s.timeout > 0 )
-                       setTimeout(function(){
-                               // Check to see if the request is still happening
-                               if ( xml ) {
-                                       // Cancel the request
-                                       xml.abort();
-
-                                       if( !requestDone )
-                                               onreadystatechange( "timeout" );
-                               }
-                       }, s.timeout);
+               if ( s.async ) {
+                       // don't attach the handler to the request, just poll it instead
+                       var ival = setInterval(onreadystatechange, 13); 
+
+                       // Timeout checker
+                       if ( s.timeout > 0 )
+                               setTimeout(function(){
+                                       // Check to see if the request is still happening
+                                       if ( xml ) {
+                                               // Cancel the request
+                                               xml.abort();
+       
+                                               if( !requestDone )
+                                                       onreadystatechange( "timeout" );
+                                       }
+                               }, s.timeout);
+               }
                        
                // Send the data
                try {
@@ -713,6 +802,30 @@ jQuery.extend({
                
                // return XMLHttpRequest to allow aborting the request etc.
                return xml;
+
+               function success(){
+                       // If a local callback was specified, fire it and pass it the data
+                       if ( s.success )
+                               s.success( data, status );
+
+                       // Fire the global callback
+                       if ( s.global )
+                               jQuery.event.trigger( "ajaxSuccess", [xml, s] );
+               }
+
+               function complete(){
+                       // Process result
+                       if ( s.complete )
+                               s.complete(xml, status);
+
+                       // The request was completed
+                       if ( s.global )
+                               jQuery.event.trigger( "ajaxComplete", [xml, s] );
+
+                       // Handle the global AJAX counter
+                       if ( s.global && ! --jQuery.active )
+                               jQuery.event.trigger( "ajaxStop" );
+               }
        },
 
        handleError: function( s, xml, status, e ) {
@@ -757,12 +870,15 @@ jQuery.extend({
         */
        httpData: function( r, type ) {
                var ct = r.getResponseHeader("content-type");
-               var data = !type && ct && ct.indexOf("xml") >= 0;
-               data = type == "xml" || data ? r.responseXML : r.responseText;
+               var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0;
+               var data = xml ? r.responseXML : r.responseText;
+
+               if ( xml && data.documentElement.tagName == "parsererror" )
+                       throw "parsererror";
 
                // If the type is "script", eval it in global context
                if ( type == "script" )
-                       (new Function( data ))();
+                       jQuery.globalEval( data );
 
                // Get the JavaScript object, if JSON is used.
                if ( type == "json" )