Merge branch 'master' of github.com:jquery/jquery into deferred
authorjaubourg <j@ubourg.net>
Tue, 28 Dec 2010 01:02:31 +0000 (02:02 +0100)
committerjaubourg <j@ubourg.net>
Tue, 28 Dec 2010 01:02:31 +0000 (02:02 +0100)
1  2 
src/ajax.js
src/core.js
test/unit/core.js

diff --combined src/ajax.js
@@@ -1,20 -1,12 +1,20 @@@
  (function( jQuery ) {
        
 -var rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
 -      rselectTextarea = /^(?:select|textarea)/i,
 -      rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
 +var r20 = /%20/g,
        rbracket = /\[\]$/,
 +      rhash = /#.*$/,
 +      rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
 +      rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
 +      rnoContent = /^(?:GET|HEAD)$/,
        rquery = /\?/,
 -      r20 = /%20/g,
 +      rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
 +      rselectTextarea = /^(?:select|textarea)/i,
 +      rts = /([?&])_=[^&]*/,
 +      rurl = /^(\w+:)?\/\/([^\/?#]+)/,
  
 +      // Slice function
 +      sliceFunc = Array.prototype.slice,
 +      
        // Keep a copy of the old load method
        _load = jQuery.fn.load;
  
@@@ -121,9 -113,8 +121,8 @@@ jQuery.each( "ajaxStart ajaxStop ajaxCo
        };
  });
  
- jQuery.extend({
-       get: function( url, data, callback, type ) {
+ jQuery.each( [ "get", "post" ], function( i, method ) {
+       jQuery[ method ] = function( url, data, callback, type ) {
                // shift arguments if data argument was omited
                if ( jQuery.isFunction( data ) ) {
                        type = type || callback;
                }
  
                return jQuery.ajax({
-                       type: "GET",
+                       type: method,
                        url: url,
                        data: data,
                        success: callback,
                        dataType: type
                });
-       },
+       };
+ });
+ jQuery.extend({
  
        getScript: function( url, callback ) {
                return jQuery.get(url, null, callback, "script");
                return jQuery.get(url, data, callback, "json");
        },
  
-       post: function( url, data, callback, type ) {
-               // shift arguments if data argument was omited
-               if ( jQuery.isFunction( data ) ) {
-                       type = type || callback;
-                       callback = data;
-                       data = {};
-               }
-               return jQuery.ajax({
-                       type: "POST",
-                       url: url,
-                       data: data,
-                       success: callback,
-                       dataType: type
-               });
-       },
        ajaxSetup: function( settings ) {
                jQuery.extend( jQuery.ajaxSettings, settings );
        },
                xhr: function() {
                        return new window.XMLHttpRequest();
                },
 -              xhrResponseFields: {
 -                      xml: "XML",
 -                      text: "Text",
 -                      json: "JSON"
 -              },
 -                      
 +
                accepts: {
                        xml: "application/xml, text/xml",
                        html: "text/html",
                        json: "application/json, text/javascript",
                        "*": "*/*"
                },
 -              
 +
                autoDataType: {
                        xml: /xml/,
                        html: /html/,
                        json: /json/
                },
 -              
 +
                // Prefilters
                // 1) They are useful to introduce custom dataTypes (see transport/jsonp for an example)
                // 2) These are called:
                //    * BEFORE asking for a transport
                //    * AFTER param serialization (s.data is a string if s.processData is true)
 -              // 3) They MUST be order agnostic
 -              prefilters: [],
 -              
 +              // 3) key is the dataType
 +              // 4) the catchall symbol "*" can be used
 +              // 5) execution will start with transport dataType and THEN continue down to "*" if needed
 +              prefilters: {},
 +
                // Transports bindings
                // 1) key is the dataType
                // 2) the catchall symbol "*" can be used
                // 3) selection will start with transport dataType and THEN go to "*" if needed
 -              transports: {
 -              },
 +              transports: {},
 +
 +              // List of data converters
 +              // 1) key format is "source_type destination_type" (a single space in-between)
 +              // 2) the catchall symbol "*" can be used for source_type
 +              dataConverters: {
 +
 +                      // Convert anything to text
 +                      "* text": window.String,
 +
 +                      // Text to html (true = no transformation)
 +                      "text html": true,
 +
 +                      // Evaluate text as a json expression
 +                      "text json": jQuery.parseJSON,
 +
 +                      // Parse text as xml
 +                      "text xml": jQuery.parseXML
 +              }
 +      },
 +
 +      // Main method
 +      // (s is used internally)
 +      ajax: function( url , options , s ) {
                
 -              // Checkers
 -              // 1) key is dataType
 -              // 2) they are called to control successful response
 -              // 3) error throws is used as error data
 -              dataCheckers: {
 -      
 -                      // Check if data is a string
 -                      "text": function(data) {
 -                              if ( typeof data != "string" ) {
 -                                      jQuery.error("typeerror");
 +              // Handle varargs
 +              if ( arguments.length === 1 ) {
 +                      options = url;
 +                      url = options ? options.url : undefined;
 +              }
 +              
 +              // Force options to be an object
 +              options = options || {};
 +              
 +              // Get the url if provided separately
 +              options.url = url || options.url;
 +              
 +              // Create the final options object
 +              s = jQuery.extend( true , {} , jQuery.ajaxSettings , options );
 +              
 +              // We force the original context
 +              // (plain objects used as context get extended)
 +              s.context = options.context;
 +              
 +              var // jQuery lists
 +                      jQuery_lastModified = jQuery.lastModified,
 +                      jQuery_etag = jQuery.etag,
 +                      // Callbacks contexts
 +                      callbackContext = s.context || s,
 +                      globalEventContext = s.context ? jQuery( s.context ) : jQuery.event,
 +                      // Deferreds
 +                      deferred = jQuery.Deferred(),
 +                      completeDeferred = jQuery._Deferred(),
 +                      // Headers (they are sent all at once)
 +                      requestHeaders = {},
 +                      // Response headers
 +                      responseHeadersString,
 +                      responseHeaders,
 +                      // transport
 +                      transport,
 +                      // timeout handle
 +                      timeoutTimer,
 +                      // The jXHR state
 +                      state = 0,
 +                      // Loop variable
 +                      i,
 +                      // Fake xhr
 +                      jXHR = {
 +                              
 +                              readyState: 0,
 +                              
 +                              // Caches the header
 +                              setRequestHeader: function(name,value) {
 +                                      if ( state === 0 ) {
 +                                              requestHeaders[ name.toLowerCase() ] = value;
 +                                      }
 +                                      return this;
 +                              },
 +                              
 +                              // Raw string
 +                              getAllResponseHeaders: function() {
 +                                      return state === 2 ? responseHeadersString : null;
 +                              },
 +                              
 +                              // Builds headers hashtable if needed
 +                              // (match is used internally)
 +                              getResponseHeader: function( key , match ) {
 +                                      
 +                                      if ( state !== 2 ) {
 +                                              return null;
 +                                      }
 +                                      
 +                                      if ( responseHeaders === undefined ) {
 +                                              
 +                                              responseHeaders = {};
 +                                              
 +                                              if ( typeof responseHeadersString === "string" ) {
 +                                                      
 +                                                      while( ( match = rheaders.exec( responseHeadersString ) ) ) {
 +                                                              responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
 +                                                      }
 +                                              }
 +                                      }
 +                                      return responseHeaders[ key.toLowerCase() ];
 +                              },
 +                              
 +                              // Cancel the request
 +                              abort: function( statusText ) {
 +                                      if ( transport && state !== 2 ) {
 +                                              transport.abort( statusText || "abort" );
 +                                              done( 0 , statusText );
 +                                      }
 +                                      return this;
                                }
 -                      },
 -      
 -                      // Check if xml has been properly parsed
 -                      "xml": function(data) {
 -                              var documentElement = data ? data.documentElement : data;
 -                              if ( ! documentElement || ! documentElement.nodeName ) {
 -                                      jQuery.error("typeerror");
 +                      };
 +
 +              // Callback for when everything is done
 +              // It is defined here because jslint complains if it is declared
 +              // at the end of the function (which would be more logical and readable)
 +              function done( status , statusText , response , headers) {
 +                      
 +                      // Called once
 +                      if ( state === 2 ) {
 +                              return;
 +                      }
 +                      
 +                      // State is "done" now
 +                      state = 2;
 +                      
 +                      // Set readyState
 +                      jXHR.readyState = status ? 4 : 0;
 +
 +                      // Cache response headers
 +                      responseHeadersString = headers || "";
 +
 +                      // Clear timeout if it exists
 +                      if ( timeoutTimer ) {
 +                              clearTimeout(timeoutTimer);
 +                      }
 +                      
 +                      var // Reference url
 +                              url = s.url,
 +                              // and ifModified status
 +                              ifModified = s.ifModified,
 +                              
 +                              // Is it a success?
 +                              isSuccess = 0,
 +                              // Stored success
 +                              success,
 +                              // Stored error
 +                              error = statusText;
 +                      
 +                      // If not timeout, force a jQuery-compliant status text
 +                      if ( statusText != "timeout" ) {
 +                              statusText = ( status >= 200 && status < 300 ) ? 
 +                                      "success" :
 +                                      ( status === 304 ? "notmodified" : "error" );
 +                      }
 +                      
 +                      // If successful, handle type chaining
 +                      if ( statusText === "success" || statusText === "notmodified" ) {
 +                              
 +                              // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
 +                              if ( s.ifModified ) {
 +                                      
 +                                      var lastModified = jXHR.getResponseHeader("Last-Modified"),
 +                                              etag = jXHR.getResponseHeader("Etag");
 +                                              
 +                                      if (lastModified) {
 +                                              jQuery_lastModified[ s.url ] = lastModified;
 +                                      }
 +                                      if (etag) {
 +                                              jQuery_etag[ s.url ] = etag;
 +                                      }
                                }
 -                              if ( documentElement.nodeName == "parsererror" ) {
 -                                      jQuery.error("parsererror");
 +                              
 +                              if ( s.ifModified && statusText === "notmodified" ) {
 +                                      
 +                                      success = null;
 +                                      isSuccess = 1;
 +                                      
 +                              } else {
 +                                      // Chain data conversions and determine the final value
 +                                      // (if an exception is thrown in the process, it'll be notified as an error)
 +                                      try {
 +                                              
 +                                              var i,
 +                                                      current,
 +                                                      prev,
 +                                                      checker,
 +                                                      conv1,
 +                                                      conv2,
 +                                                      oneConv,
 +                                                      convertion,
 +                                                      dataTypes = s.dataTypes,
 +                                                      dataConverters = s.dataConverters,
 +                                                      responses = {
 +                                                              "xml": "XML",
 +                                                              "text": "Text"
 +                                                      };
 +                                              
 +                                              for( i = 0 ; i < dataTypes.length ; i++ ) {
 +                                                      
 +                                                      current = dataTypes[ i ];
 +                                                      
 +                                                      if ( responses[ current ] ) {
 +                                                              jXHR[ "response" + responses[ current ] ] = response;
 +                                                              responses[ current ] = 0;
 +                                                      }
 +                                                      
 +                                                      if ( i ) {
 +                                                              
 +                                                              prev = dataTypes[ i - 1 ];
 +                                                              
 +                                                              if ( prev !== "*" && current !== "*" && prev !== current ) {
 +                                                              
 +                                                                      oneConv = conv1 = 
 +                                                                              dataConverters[ ( conversion = prev + " " + current ) ] ||
 +                                                                              dataConverters[ "* " + current ];
 +                                                                      
 +                                                                      if ( oneConv !== true ) {
 +                                                                              
 +                                                                              if ( ! oneConv && prev !== "text" && current !== "text" ) {
 +                                                                                      conv1 = dataConverters[ prev + " text" ] || dataConverters[ "* text" ];
 +                                                                                      conv2 = dataConverters[ "text " + current ];
 +                                                                              }
 +                                                                              
 +                                                                              if ( oneConv || conv1 && conv2 ) {
 +                                                                                      response = oneConv ? conv1( response ) : conv2( conv1( response ) );
 +                                                                              } else {
 +                                                                                      throw "no " + conversion;
 +                                                                              }
 +                                                                      }
 +                                                              }
 +                                                      } else if ( s.dataFilter ) {
 +                                                              
 +                                                              response = s.dataFilter( response );
 +                                                              dataTypes = s.dataTypes;
 +                                                      }
 +                                              }
 +              
 +                                              // We have a real success
 +                                              success = response;
 +                                              isSuccess = 1;
 +                                              
 +                                      } catch(e) {
 +                                              
 +                                              statusText = "parsererror";
 +                                              error = "" + e;
 +                                              
 +                                      }
                                }
 +                              
 +                      } else { // if not success, mark it as an error
 +                              
 +                                      error = error || statusText;
                        }
 -              },
 -              
 -              // List of data converters
 -              // 1) key format is "source_type => destination_type" (spaces required)
 -              // 2) the catchall symbol "*" can be used for source_type
 -              dataConverters: {
 -              
 -                      // Convert anything to text
 -                      "* => text": function(data) {
 -                              return "" + data;
 -                      },
 +                              
 +                      // Set data for the fake xhr object
 +                      jXHR.status = status;
 +                      jXHR.statusText = statusText;
                        
 -                      // Text to html (no transformation)
 -                      "text => html": function(data) {
 -                              return data;
 -                      },
 +                      // Success/Error
 +                      if ( isSuccess ) {
 +                              deferred.fire( callbackContext , [ success , statusText , jXHR ] );
 +                      } else {
 +                              deferred.fireReject( callbackContext , [ jXHR , statusText , error ] );
 +                      }
                        
 -                      // Evaluate text as a json expression
 -                      "text => json": jQuery.parseJSON,
 +                      if ( s.global ) {
 +                              globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ) ,
 +                                              [ jXHR , s , isSuccess ? success : error ] );
 +                      }
                        
 -                      // Parse text as xml
 -                      "text => xml": function(data) {
 -                              var xml, parser;
 -                              if ( window.DOMParser ) { // Standard
 -                                      parser = new DOMParser();
 -                                      xml = parser.parseFromString(data,"text/xml");
 -                              } else { // IE
 -                                      xml = new ActiveXObject("Microsoft.XMLDOM");
 -                                      xml.async="false";
 -                                      xml.loadXML(data);
 +                      // Complete
 +                      completeDeferred.fire( callbackContext, [ jXHR , statusText ] );
 +                      
 +                      if ( s.global ) {
 +                              globalEventContext.trigger( "ajaxComplete" , [ jXHR , s] );
 +                              // Handle the global AJAX counter
 +                              if ( ! --jQuery.active ) {
 +                                      jQuery.event.trigger( "ajaxStop" );
                                }
 -                              return xml;
                        }
                }
 -      },
 +              
 +              // Attach deferreds     
 +              jXHR.success = jXHR.then = deferred.then;
 +              jXHR.error = jXHR.fail = deferred.fail;
 +              jXHR.complete = completeDeferred.then;
  
 -      // Main method
 -      ajax: function( url , s ) {
 +              // Remove hash character (#7531: and string promotion)
 +              s.url = ( "" + s.url ).replace( rhash , "" );
                
 -              if ( arguments.length === 1 ) {
 -                      s = url;
 -                      url = s ? s.url : undefined;
 +              // Uppercase the type
 +              s.type = s.type.toUpperCase();
 +              
 +              // Determine if request has content
 +              s.hasContent = ! rnoContent.test( s.type );
 +              
 +              // Extract dataTypes list
 +              s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( /\s+/ );
 +              
 +              // Determine if a cross-domain request is in order
 +              var parts = rurl.exec( s.url.toLowerCase() ),
 +                      loc = location;
 +              s.crossDomain = !!( parts && ( parts[ 1 ] && parts[ 1 ] != loc.protocol || parts[ 2 ] != loc.host ) );
 +
 +              // Convert data if not already a string
 +              if ( s.data && s.processData && typeof s.data != "string" ) {
 +                      s.data = jQuery.param( s.data , s.traditional );
 +              }
 +
 +              // Watch for a new set of requests
 +              if ( s.global && jQuery.active++ === 0 ) {
 +                      jQuery.event.trigger( "ajaxStart" );
                }
                
 -              return jQuery.xhr().open( s ? s.type : undefined , url ).send( undefined , s );
 +              // Get transport
 +              transport = jQuery.ajax.prefilter( s ).transport( s );
                
 +              // If no transport, we auto-abort
 +              if ( ! transport ) {
 +                      
 +                      done( 0 , "transport not found" );
 +                      jXHR = false;
 +                      
 +              } else {
 +                      
 +                      // More options handling for requests with no content
 +                      if ( ! s.hasContent ) {
 +                              
 +                              // If data is available, append data to url
 +                              if ( s.data ) {
 +                                      s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
 +                              }
 +                                                              
 +                              // Add anti-cache in url if needed
 +                              if ( s.cache === false ) {
 +                                      
 +                                      var ts = jQuery.now(),
 +                                              // try replacing _= if it is there
 +                                              ret = s.url.replace( rts , "$1_=" + ts );
 +                                              
 +                                      // if nothing was replaced, add timestamp to the end
 +                                      s.url = ret + ( (ret == s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "");
 +                              }
 +                      }
 +                      
 +                      // Set the correct header, if data is being sent
 +                      if ( ( s.data && s.hasContent ) || options.contentType ) {
 +                              requestHeaders[ "content-type" ] = s.contentType;
 +                      }
 +              
 +                      // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
 +                      if ( s.ifModified ) {
 +                              if ( jQuery_lastModified[ s.url ] ) { 
 +                                      requestHeaders[ "if-modified-since" ] = jQuery_lastModified[ s.url ];
 +                              }
 +                              if ( jQuery_etag[ s.url ] ) {
 +                                      requestHeaders[ "if-none-match" ] = jQuery_etag[ s.url ];
 +                              }
 +                      }
 +              
 +                      // Set the Accepts header for the server, depending on the dataType
 +                      requestHeaders.accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
 +                              s.accepts[ s.dataTypes[ 0 ] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
 +                              s.accepts[ "*" ];
 +                              
 +                      // Check for headers option
 +                      for ( i in s.headers ) {
 +                              requestHeaders[ i.toLowerCase() ] = s.headers[ i ];
 +                      }
 +                      
 +                      // Allow custom headers/mimetypes and early abort
 +                      if ( s.beforeSend && ( s.beforeSend.call( callbackContext , jXHR , s ) === false || state === 2 ) ) {
 +                                      
 +                                      // Abort if not done already
 +                                      done( 0 , "abort" );
 +                                      jXHR = false;
 +                                      
 +                      } else {
 +                              
 +                              // Set state as sending
 +                              state = 1;
 +                              jXHR.readyState = 1;
 +                              
 +                              // Install callbacks on deferreds
 +                              for ( i in { success:1, error:1, complete:1 } ) {
 +                                      jXHR[ i ]( s[ i ] );
 +                              }
 +                              
 +                              // Send global event
 +                              if ( s.global ) {
 +                                      globalEventContext.trigger( "ajaxSend" , [ jXHR , s ] );
 +                              }
 +                              
 +                              // Timeout
 +                              if ( s.async && s.timeout > 0 ) {
 +                                      timeoutTimer = setTimeout(function(){
 +                                              jXHR.abort( "timeout" );
 +                                      }, s.timeout);
 +                              }
 +                              
 +                              // Try to send
 +                              try {
 +                                      transport.send(requestHeaders, done);
 +                              } catch (e) {
 +                                      // Propagate exception as error if not done
 +                                      if ( status === 1 ) {
 +
 +                                              done(0, "error", "" + e);
 +                                              jXHR = false;
 +                                              
 +                                      // Simply rethrow otherwise
 +                                      } else {
 +                                              jQuery.error(e);
 +                                      }
 +                              }
 +                      }
 +              }
 +              
 +              return jXHR;
        },
  
        // Serialize an array of form elements or a set of
@@@ -730,172 -359,6 +715,172 @@@ jQuery.extend(
  
  });
  
 +//Execute or select from functions in a given structure of options
 +function ajax_selectOrExecute( structure , s ) {
 +
 +      var dataTypes = s.dataTypes,
 +              transportDataType,
 +              list,
 +              selected,
 +              i,
 +              length,
 +              checked = {},
 +              flag,
 +              noSelect = structure !== "transports";
 +              
 +      function initSearch( dataType ) {
 +
 +              flag = transportDataType !== dataType && ! checked[ dataType ];
 +              
 +              if ( flag ) {
 +                      
 +                      checked[ dataType ] = 1;
 +                      transportDataType = dataType;
 +                      list = s[ structure ][ dataType ];
 +                      i = -1;
 +                      length = list ? list.length : 0 ;
 +              }
 +
 +              return flag;
 +      }
 +      
 +      initSearch( dataTypes[ 0 ] );
 +
 +      for ( i = 0 ; ( noSelect || ! selected ) && i <= length ; i++ ) {
 +              
 +              if ( i === length ) {
 +                      
 +                      initSearch( "*" );
 +                      
 +              } else {
 +
 +                      selected = list[ i ]( s , determineDataType );
 +
 +                      // If we got redirected to another dataType
 +                      // Search there (if not in progress or already tried)
 +                      if ( typeof( selected ) === "string" &&
 +                              initSearch( selected ) ) {
 +
 +                              dataTypes.unshift( selected );
 +                              selected = 0;
 +                      }
 +              }
 +      }
 +
 +      return noSelect ? jQuery.ajax : selected;
 +}
 +
 +// Add an element to one of the structures in ajaxSettings
 +function ajax_addElement( structure , args ) {
 +              
 +      var i,
 +              start = 0,
 +              length = args.length,
 +              dataTypes = [ "*" ],
 +              dLength = 1,
 +              dataType,
 +              functors = [],
 +              first,
 +              append,
 +              list;
 +              
 +      if ( length ) {
 +              
 +              first = jQuery.type( args[ 0 ] );
 +              
 +              if ( first === "object" ) {
 +                      return ajax_selectOrExecute( structure , args[ 0 ] );
 +              }
 +              
 +              structure = jQuery.ajaxSettings[ structure ];
 +              
 +              if ( first !== "function" ) {
 +                      
 +                      dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
 +                      dLength = dataTypes.length;
 +                      start = 1;
 +                      
 +              }
 +              
 +              if ( dLength && start < length ) {
 +                      
 +                      functors = sliceFunc.call( args , start );
 +                                      
 +                      for( i = 0 ; i < dLength ; i++ ) {
 +                              
 +                              dataType = dataTypes[ i ];
 +                              
 +                              first = /^\+/.test( dataType );
 +                              
 +                              if (first) {
 +                                      dataType = dataType.substr(1);
 +                              }
 +                              
 +                              if ( dataType !== "" ) {
 +                              
 +                                      append = Array.prototype[ first ? "unshift" : "push" ];
 +                                      list = structure[ dataType ] = structure[ dataType ] || [];
 +                                      append.apply( list , functors );
 +                              }
 +                      }
 +              }
 +      }
 +      
 +      return jQuery.ajax;
 +}
 +
 +// Install prefilter & transport methods
 +jQuery.each( [ "prefilter" , "transport" ] , function( _ , name ) {
 +      _ = name + "s";
 +      jQuery.ajax[ name ] = function() {
 +              return ajax_addElement( _ , arguments );
 +      };
 +} );
 +      
 +// Utility function that handles dataType when response is received
 +// (for those transports that can give text or xml responses)
 +function determineDataType( s , ct , text , xml ) {
 +      
 +      var autoDataType = s.autoDataType,
 +              type,
 +              regexp,
 +              dataTypes = s.dataTypes,
 +              transportDataType = dataTypes[0],
 +              response;
 +      
 +      // Auto (xml, json, script or text determined given headers)
 +      if ( transportDataType === "*" ) {
 +
 +              for ( type in autoDataType ) {
 +                      if ( ( regexp = autoDataType[ type ] ) && regexp.test( ct ) ) {
 +                              transportDataType = dataTypes[0] = type;
 +                              break;
 +                      }
 +              }                       
 +      } 
 +      
 +      // xml and parsed as such
 +      if ( transportDataType === "xml" &&
 +              xml &&
 +              xml.documentElement /* #4958 */ ) {
 +              
 +              response = xml;
 +      
 +      // Text response was provided
 +      } else {
 +              
 +              response = text;
 +              
 +              // If it's not really text, defer to dataConverters
 +              if ( transportDataType !== "text" ) {
 +                      dataTypes.unshift( "text" );
 +              }
 +              
 +      }
 +      
 +      return response;
 +}     
 +
  /*
   * Create the request object; Microsoft failed to properly
   * implement the XMLHttpRequest in IE7 (can't request local files),
diff --combined src/core.js
@@@ -60,8 -60,8 +60,8 @@@ var jQuery = function( selector, contex
        // Has the ready events already been bound?
        readyBound = false,
        
 -      // The functions to execute on DOM ready
 -      readyList = [],
 +      // The deferred used on DOM ready
 +      readyList,
  
        // The ready event handler
        DOMContentLoaded,
        indexOf = Array.prototype.indexOf,
        
        // [[Class]] -> type pairs
 -      class2type = {};
 +      class2type = {},
 +      
 +      // Marker for deferred
 +      deferredMarker = [];
  
  jQuery.fn = jQuery.prototype = {
        init: function( selector, context ) {
                return jQuery.each( this, callback, args );
        },
        
 -      ready: function( fn ) {
 +      ready: function() {
                // Attach the listeners
                jQuery.bindReady();
 -
 -              // If the DOM is already ready
 -              if ( jQuery.isReady ) {
 -                      // Execute the function immediately
 -                      fn.call( document, jQuery );
 -
 -              // Otherwise, remember the function for later
 -              } else if ( readyList ) {
 -                      // Add the function to the wait list
 -                      readyList.push( fn );
 -              }
 -
 -              return this;
 +              
 +              // Change ready & apply
 +              return ( jQuery.fn.ready = readyList.then ).apply( this , arguments );
        },
        
        eq: function( i ) {
@@@ -408,11 -415,23 +408,11 @@@ jQuery.extend(
                        }
  
                        // If there are functions bound, to execute
 -                      if ( readyList ) {
 -                              // Execute all of them
 -                              var fn,
 -                                      i = 0,
 -                                      ready = readyList;
 -
 -                              // Reset the list of functions
 -                              readyList = null;
 -
 -                              while ( (fn = ready[ i++ ]) ) {
 -                                      fn.call( document, jQuery );
 -                              }
 -
 -                              // Trigger any bound ready events
 -                              if ( jQuery.fn.trigger ) {
 -                                      jQuery( document ).trigger( "ready" ).unbind( "ready" );
 -                              }
 +                      readyList.fire( document , [ jQuery ] );
 +                      
 +                      // Trigger any bound ready events
 +                      if ( jQuery.fn.trigger ) {
 +                              jQuery( document ).trigger( "ready" ).unbind( "ready" );
                        }
                }
        },
                        jQuery.error( "Invalid JSON: " + data );
                }
        },
 +      
 +      // Cross-browser xml parsing
 +      // (xml & tmp used internally)
 +      parseXML: function( data , xml , tmp ) {
 +
 +              if ( window.DOMParser ) { // Standard
 +                      tmp = new DOMParser();
 +                      xml = tmp.parseFromString( data , "text/xml" );
 +              } else { // IE
 +                      xml = new ActiveXObject( "Microsoft.XMLDOM" );
 +                      xml.async = "false";
 +                      xml.loadXML( data );
 +              }
 +              
 +              tmp = xml.documentElement;
 +              
 +              if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
 +                      jQuery.error( "Invalid XML: " + data );
 +              }
 +              
 +              return xml;
 +      },
  
        noop: function() {},
  
        now: function() {
                return (new Date()).getTime();
        },
 +      
 +      // Create a simple deferred (one callbacks list)
 +      _Deferred: function() {
 +              
 +              var // callbacks list
 +                      callbacks = [],
 +                      // stored [ context , args ]
 +                      fired,
 +                      // to avoid firing when already doing so
 +                      firing,
 +                      // flag to know if the deferred has been cancelled
 +                      cancelled,
 +                      // the deferred itself
 +                      deferred  = {
 +                              
 +                              // then( f1, f2, ...)
 +                              then: function () {
 +                                      
 +                                      if ( ! cancelled ) {
 +                                              
 +                                              var args = arguments,
 +                                                      i,
 +                                                      length,
 +                                                      elem,
 +                                                      type,
 +                                                      _fired;
 +                                                      
 +                                              if ( fired ) {
 +                                                      _fired = fired;
 +                                                      fired = 0;
 +                                              }
 +                                              
 +                                              for ( i = 0, length = args.length ; i < length ; i++ ) {
 +                                                      elem = args[ i ];
 +                                                      type = jQuery.type( elem );
 +                                                      if ( type === "array" ) {
 +                                                              deferred.then.apply( deferred , elem );
 +                                                      } else if ( type === "function" ) {
 +                                                              callbacks.push( elem );
 +                                                      }
 +                                              }
 +                                              
 +                                              if ( _fired ) {
 +                                                      deferred.fire( _fired[ 0 ] , _fired[ 1 ] );
 +                                              }
 +                                      }
 +                                      
 +                                      return this;
 +                              },
 +                              
 +                              // resolve with given context and args
 +                              fire: function( context , args ) {
 +                                      if ( ! cancelled && ! fired && ! firing ) {
 +                                              
 +                                              firing = 1;
 +                                              
 +                                              try {
 +                                                      while( callbacks[ 0 ] ) {
 +                                                              callbacks.shift().apply( context , args );
 +                                                      }
 +                                              }
 +                                              finally {
 +                                                      fired = [ context , args ];
 +                                                      firing = 0;
 +                                              }
 +                                      }
 +                                      return this;
 +                              },
 +                              
 +                              // resolve with this as context and given arguments
 +                              resolve: function() {
 +                                      deferred.fire( this , arguments );
 +                                      return this;
 +                              },
 +                              
 +                              // Has this deferred been resolved?
 +                              isResolved: function() {
 +                                      return !!( firing || fired );
 +                              },
 +                              
 +                              // Cancel
 +                              cancel: function() {
 +                                      cancelled = 1;
 +                                      callbacks = [];
 +                                      return this;
 +                              },
 +                              
 +                              // Has this deferred been cancelled?
 +                              isCancelled: function() {
 +                                      return !!cancelled;
 +                              }
 +                      };
 +              
 +              // Add the deferred marker
 +              deferred.then._ = deferredMarker;
 +              
 +              return deferred;
 +      },
 +      
 +      // Full fledged deferred (two callbacks list)
 +      // Typical success/error system
 +      Deferred: function( func ) {
 +              
 +              var errorDeferred = jQuery._Deferred(),
 +                      deferred = jQuery._Deferred(),
 +                      successCancel = deferred.cancel;
 +                      
 +              // Add errorDeferred methods and redefine cancel                        
 +              jQuery.extend( deferred , {
 +
 +                              fail: errorDeferred.then,
 +                              fireReject: errorDeferred.fire,
 +                              reject: errorDeferred.resolve,
 +                              isRejected: errorDeferred.isResolved
 +
 +              } );
 +              
 +              // Remove cancel related
 +              delete deferred.cancel;
 +              delete deferred.isCancelled;
 +              
 +              // Make sure only one callback list will be used
 +              deferred.then( errorDeferred.cancel ).fail( successCancel );
 +              
 +              // Call given func if any
 +              if ( func ) {
 +                      func.call( deferred , deferred );
 +              }
 +              
 +              return deferred;
 +      },
 +
 +      // Check if an object is a deferred
 +      isDeferred: function( object ) {
 +              return !!( object && object.then && object.then._ === deferredMarker );
 +      },
 +      
 +      // Deferred helper
 +      when: function( object ) {
 +              object = jQuery.isDeferred( object ) ?
 +                      object :
 +                      jQuery.Deferred().resolve( object );
 +              return object;
 +      },
  
        // Use of jQuery.browser is frowned upon.
        // More details: http://docs.jquery.com/Utilities/jQuery.browser
        browser: {}
  });
  
 +// Create readyList deferred
 +// also force $.fn.ready to be recognized as a defer
 +readyList = jQuery._Deferred();
 +jQuery.fn.ready._ = deferredMarker;
 +
  // Populate the class2type map
  jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
        class2type[ "[object " + name + "]" ] = name.toLowerCase();
@@@ -1038,6 -886,11 +1038,11 @@@ function doScrollCheck() 
        jQuery.ready();
  }
  
+ // Expose jQuery as an Asynchronous Module
+ if ( typeof define !== "undefined" ) {
+       define( "jquery", [], function () { return jQuery; } );
+ }
  // Expose jQuery to the global object
  return (window.jQuery = window.$ = jQuery);
  
diff --combined test/unit/core.js
@@@ -12,7 -12,9 +12,9 @@@ test("Basic requirements", function() 
  });
  
  test("jQuery()", function() {
-       expect(23);
+       expect(24);
+       strictEqual( commonJSDefined, jQuery, "CommonJS registered (Bug #7102)" );
  
        // Basic constructor's behavior
  
@@@ -151,7 -153,7 +153,7 @@@ test("selector state", function() 
        test = jQuery("#main").eq(0);
        equals( test.selector, "#main.slice(0,1)", "#main eq Selector" );
        equals( test.context, document, "#main eq Context" );
-       
        var d = "<div />";
        equals(
                jQuery(d).appendTo(jQuery(d)).selector,
@@@ -253,38 -255,38 +255,38 @@@ test("isPlainObject", function() 
  
        // The use case that we want to match
        ok(jQuery.isPlainObject({}), "{}");
-       
        // Not objects shouldn't be matched
        ok(!jQuery.isPlainObject(""), "string");
        ok(!jQuery.isPlainObject(0) && !jQuery.isPlainObject(1), "number");
        ok(!jQuery.isPlainObject(true) && !jQuery.isPlainObject(false), "boolean");
        ok(!jQuery.isPlainObject(null), "null");
        ok(!jQuery.isPlainObject(undefined), "undefined");
-       
        // Arrays shouldn't be matched
        ok(!jQuery.isPlainObject([]), "array");
-  
        // Instantiated objects shouldn't be matched
        ok(!jQuery.isPlainObject(new Date), "new Date");
-  
        var fn = function(){};
-  
        // Functions shouldn't be matched
        ok(!jQuery.isPlainObject(fn), "fn");
-  
        // Again, instantiated objects shouldn't be matched
        ok(!jQuery.isPlainObject(new fn), "new fn (no methods)");
-  
        // Makes the function a little more realistic
        // (and harder to detect, incidentally)
        fn.prototype = {someMethod: function(){}};
-  
        // Again, instantiated objects shouldn't be matched
        ok(!jQuery.isPlainObject(new fn), "new fn");
  
        // DOM Element
        ok(!jQuery.isPlainObject(document.createElement("div")), "DOM Element");
-       
        // Window
        ok(!jQuery.isPlainObject(window), "window");
  
                        document.body.removeChild( iframe );
                        start();
                };
-  
                var doc = iframe.contentDocument || iframe.contentWindow.document;
                doc.open();
                doc.write("<body onload='window.parent.iframeDone(Object);'>");
@@@ -659,7 -661,7 +661,7 @@@ test("jQuery.merge()", function() 
  
        // Fixed at [5998], #3641
        same( parse([-2,-1], [0,1,2]), [-2,-1,0,1,2], "Second array including a zero (falsy)");
-       
        // After fixing #5527
        same( parse([], [null, undefined]), [null, undefined], "Second array including null and undefined values");
        same( parse({length:0}, [1,2]), {length:2, 0:1, 1:2}, "First array like");
@@@ -694,7 -696,7 +696,7 @@@ test("jQuery.extend(Object, Object)", f
        equals( deep1.foo2, document, "Make sure that a deep clone was not attempted on the document" );
  
        ok( jQuery.extend(true, {}, nestedarray).arr !== arr, "Deep extend of object must clone child array" );
-       
        // #5991
        ok( jQuery.isArray( jQuery.extend(true, { arr: {} }, nestedarray).arr ), "Cloned array heve to be an Array" );
        ok( jQuery.isPlainObject( jQuery.extend(true, { arr: arr }, { arr: {} }).arr ), "Cloned object heve to be an plain object" );
        empty = {};
        jQuery.extend(true, empty, optionsWithCustomObject);
        ok( empty.foo && empty.foo.date === customObject, "Custom objects copy correctly (no methods)" );
-       
        // Makes the class a little more realistic
        myKlass.prototype = { someMethod: function(){} };
        empty = {};
        jQuery.extend(true, empty, optionsWithCustomObject);
        ok( empty.foo && empty.foo.date === customObject, "Custom objects copy correctly" );
-       
        var ret = jQuery.extend(true, { foo: 4 }, { foo: new Number(5) } );
        ok( ret.foo == 5, "Wrapped numbers copy correctly" );
  
@@@ -849,10 -851,10 +851,10 @@@ test("jQuery.makeArray", function()
  
  test("jQuery.isEmptyObject", function(){
        expect(2);
-       
        equals(true, jQuery.isEmptyObject({}), "isEmptyObject on empty object literal" );
        equals(false, jQuery.isEmptyObject({a:1}), "isEmptyObject on non-empty object literal" );
-       
        // What about this ?
        // equals(true, jQuery.isEmptyObject(null), "isEmptyObject on null" );
  });
@@@ -878,23 -880,23 +880,23 @@@ test("jQuery.proxy", function()
  
  test("jQuery.parseJSON", function(){
        expect(8);
-       
        equals( jQuery.parseJSON(), null, "Nothing in, null out." );
        equals( jQuery.parseJSON( null ), null, "Nothing in, null out." );
        equals( jQuery.parseJSON( "" ), null, "Nothing in, null out." );
-       
        same( jQuery.parseJSON("{}"), {}, "Plain object parsing." );
        same( jQuery.parseJSON('{"test":1}'), {"test":1}, "Plain object parsing." );
  
        same( jQuery.parseJSON('\n{"test":1}'), {"test":1}, "Make sure leading whitespaces are handled." );
-       
        try {
                jQuery.parseJSON("{a:1}");
                ok( false, "Test malformed JSON string." );
        } catch( e ) {
                ok( true, "Test malformed JSON string." );
        }
-       
        try {
                jQuery.parseJSON("{'a':1}");
                ok( false, "Test malformed JSON string." );
                ok( true, "Test malformed JSON string." );
        }
  });
 +
 +test("jQuery._Deferred()", function() {
 +      
 +      expect( 10 );
 +      
 +      var deferred,
 +              object,
 +              test;
 +      
 +      deferred = jQuery._Deferred();
 +              
 +      test = false;
 +              
 +      deferred.then( function( value ) {
 +              equals( value , "value" , "Test pre-resolve callback" );
 +              test = true;
 +      } );
 +      
 +      deferred.resolve( "value" );
 +      
 +      ok( test , "Test pre-resolve callbacks called right away" );
 +
 +      test = false;
 +      
 +      deferred.then( function( value ) {
 +              equals( value , "value" , "Test post-resolve callback" );
 +              test = true;
 +      } );
 +      
 +      ok( test , "Test post-resolve callbacks called right away" );
 +      
 +      deferred.cancel();
 +      
 +      test = true;
 +      
 +      deferred.then( function() {
 +              ok( false , "Cancel was ignored" );
 +              test = false;
 +      } );
 +      
 +      ok( test , "Test cancel" );
 +      
 +      deferred = jQuery._Deferred().resolve();
 +      
 +      try {
 +              deferred.then( function() {
 +                      throw "Error";
 +              } , function() {
 +                      ok( true , "Test deferred do not cancel on exception" );
 +              } );
 +      } catch( e ) {
 +              strictEqual( e , "Error" , "Test deferred propagates exceptions");
 +              deferred.then();
 +      }
 +      
 +      test = "";
 +      deferred = jQuery._Deferred().then( function() {
 +              
 +              test += "A";
 +              
 +      }, function() {
 +              
 +              test += "B";
 +              
 +      } ).resolve();
 +      
 +      strictEqual( test , "AB" , "Test multiple then parameters" );
 +      
 +      test = "";
 +      
 +      deferred.then( function() {
 +              
 +              deferred.then( function() {
 +                      
 +                      test += "C";
 +                      
 +              } );
 +              
 +              test += "A";
 +              
 +      }, function() {
 +              
 +              test += "B";
 +      } );
 +      
 +      strictEqual( test , "ABC" , "Test then callbacks order" );
 +      
 +      deferred = jQuery._Deferred();
 +      
 +      deferred.fire( jQuery , [ document ] ).then( function( doc ) {
 +              ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" );
 +      });
 +});
 +
 +test("jQuery.Deferred()", function() {
 +      
 +      expect( 4 );
 +      
 +      jQuery.Deferred( function( defer ) {
 +              strictEqual( this , defer , "Defer passed as this & first argument" );
 +              this.resolve( "done" );
 +      }).then( function( value ) {
 +              strictEqual( value , "done" , "Passed function executed" );
 +      });
 +      
 +      jQuery.Deferred().resolve().then( function() {
 +              ok( true , "Success on resolve" );
 +      }).fail( function() {
 +              ok( false , "Error on resolve" );
 +      });
 +      
 +      jQuery.Deferred().reject().then( function() {
 +              ok( false , "Success on reject" );
 +      }).fail( function() {
 +              ok( true , "Error on reject" );
 +      });
 +});
 +      
 +test("jQuery.isDeferred()", function() {
 +      
 +      expect( 10 );
 +      
 +      var object1 = { then: function() { return this; } },
 +              object2 = { then: function() { return this; } };
 +              
 +      object2.then._ = [];
 +      
 +      // The use case that we want to match
 +      ok(jQuery.isDeferred(jQuery._Deferred()), "Simple deferred");
 +      ok(jQuery.isDeferred(jQuery.Deferred()), "Failable deferred");
 +      
 +      // Some other objects
 +      ok(!jQuery.isDeferred(object1), "Object with then & no marker");
 +      ok(!jQuery.isDeferred(object2), "Object with then & marker");
 +      
 +      // Not objects shouldn't be matched
 +      ok(!jQuery.isDeferred(""), "string");
 +      ok(!jQuery.isDeferred(0) && !jQuery.isDeferred(1), "number");
 +      ok(!jQuery.isDeferred(true) && !jQuery.isDeferred(false), "boolean");
 +      ok(!jQuery.isDeferred(null), "null");
 +      ok(!jQuery.isDeferred(undefined), "undefined");
 +      
 +      object1 = {custom: jQuery._Deferred().then};
 +      
 +      ok(!jQuery.isDeferred(object1) , "custom method name not found automagically");
 +});
 +
 +test("jQuery.when()", function() {
 +      
 +      expect( 2 );
 +      
 +      var cache, i;
 +      
 +      for( i = 1 ; i < 3 ; i++ ) {
 +              jQuery.when( cache || jQuery.Deferred( function() {
 +                      this.resolve( i );
 +              }) ).then( function( value ) {
 +                      strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) );
 +                      cache = value;
 +              }).fail( function() {
 +                      ok( false , "Fail called" );
 +              });
 +      }
 +});