Merge branch 'bug5566' into csnover-bug5566. Fixes #4386, #5566, #6997.
authorColin Snover <github.com@zetafleet.com>
Sun, 12 Dec 2010 08:39:06 +0000 (02:39 -0600)
committerColin Snover <github.com@zetafleet.com>
Sun, 12 Dec 2010 08:39:06 +0000 (02:39 -0600)
Conflicts:
src/manipulation.js
test/unit/manipulation.js

25 files changed:
Makefile
Rakefile
build.xml
src/ajax.js
src/attributes.js
src/core.js
src/data.js
src/effects.js
src/event.js
src/manipulation.js
src/transports/jsonp.js [new file with mode: 0644]
src/transports/script.js [new file with mode: 0644]
src/transports/xhr.js [new file with mode: 0644]
src/xhr.js [new file with mode: 0644]
test/data/atom+xml.php [new file with mode: 0644]
test/data/css.php [new file with mode: 0644]
test/data/headers.php [new file with mode: 0644]
test/data/with_fries_over_jsonp.php [new file with mode: 0644]
test/index.html
test/unit/ajax.js
test/unit/attributes.js
test/unit/core.js
test/unit/data.js
test/unit/event.js
test/unit/manipulation.js

index 0dae732..935f69c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -24,6 +24,10 @@ BASE_FILES = ${SRC_DIR}/core.js\
        ${SRC_DIR}/manipulation.js\
        ${SRC_DIR}/css.js\
        ${SRC_DIR}/ajax.js\
+       ${SRC_DIR}/xhr.js\
+       ${SRC_DIR}/transports/jsonp.js\
+       ${SRC_DIR}/transports/script.js\
+       ${SRC_DIR}/transports/xhr.js\
        ${SRC_DIR}/effects.js\
        ${SRC_DIR}/offset.js\
        ${SRC_DIR}/dimensions.js
index d0d8f66..5ea143b 100644 (file)
--- a/Rakefile
+++ b/Rakefile
@@ -9,7 +9,7 @@ test_dir  = File.join( prefix, 'test' )
 # setting DIST_DIR before calling rake
 dist_dir  = ENV['DIST_DIR'] || File.join( prefix, 'dist' )
 
-base_files = %w{intro core support data queue attributes event selector traversing manipulation css ajax effects offset dimensions outro}.map { |js| File.join( src_dir, "#{js}.js" ) }
+base_files = %w{intro core support data queue attributes event selector traversing manipulation css ajax xhr transports/jsonp transports/script transports/xhr effects offset dimensions outro}.map { |js| File.join( src_dir, "#{js}.js" ) }
 
 # Sizzle, QUnit and jQuery files/dirs
 sizzle_dir = File.join( src_dir, "sizzle" )
index c6be9f9..f6650f4 100644 (file)
--- a/build.xml
+++ b/build.xml
                        <fileset file="src/manipulation.js" />
                        <fileset file="src/css.js" />
                        <fileset file="src/ajax.js" />
+                       <fileset file="src/xhr.js" />
+                       <fileset file="src/transports/jsonp.js" />
+                       <fileset file="src/transports/script.js" />
+                       <fileset file="src/transports/xhr.js" />
                        <fileset file="src/effects.js" />
                        <fileset file="src/offset.js" />
                        <fileset file="src/dimensions.js" />
@@ -72,6 +76,7 @@
                <exec executable="git" outputproperty="date">
                        <arg line="log -1 --pretty=format:%ad" />
                </exec>
+               <replaceregexp match="(\(\s*function\s*\(\s*jQuery\s*\)\s*\{)|(\}\s*\)\s*\(\s*jQuery\s*\)\s*;)" flags="g" replace="" file="${JQ}" />
                <replaceregexp match="Date: " replace="Date: ${date}" file="${JQ}" />
                <echo message="${JQ} built." />
        </target>
index d10b931..da130fa 100644 (file)
@@ -1,17 +1,11 @@
 (function( jQuery ) {
-
-var jsc = jQuery.now(),
-       rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+       
+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,
-       rnoContent = /^(?:GET|HEAD)$/,
        rbracket = /\[\]$/,
-       jsre = /\=\?(&|$)/,
        rquery = /\?/,
-       rts = /([?&])_=[^&]*/,
-       rurl = /^(\w+:)?\/\/([^\/?#]+)/,
        r20 = /%20/g,
-       rhash = /#.*$/,
 
        // Keep a copy of the old load method
        _load = jQuery.fn.load;
@@ -49,9 +43,9 @@ jQuery.fn.extend({
                                type = "POST";
                        }
                }
-
+               
                var self = this;
-
+               
                // Request the remote document
                jQuery.ajax({
                        url: url,
@@ -90,36 +84,37 @@ jQuery.fn.extend({
        },
 
        serializeArray: function() {
-               return this.map(function() {
+               return this.map(function(){
                        return this.elements ? jQuery.makeArray(this.elements) : this;
                })
-               .filter(function() {
+               .filter(function(){
                        return this.name && !this.disabled &&
                                (this.checked || rselectTextarea.test(this.nodeName) ||
                                        rinput.test(this.type));
                })
-               .map(function( i, elem ) {
+               .map(function(i, elem){
                        var val = jQuery(this).val();
 
                        return val == null ?
                                null :
                                jQuery.isArray(val) ?
-                                       jQuery.map( val, function( val, i ) {
-                                               return { name: elem.name, value: val };
+                                       jQuery.map( val, function(val, i){
+                                               return {name: elem.name, value: val};
                                        }) :
-                                       { name: elem.name, value: val };
+                                       {name: elem.name, value: val};
                }).get();
        }
 });
 
 // Attach a bunch of functions for handling common AJAX events
-jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
-       jQuery.fn[o] = function( f ) {
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function(i,o){
+       jQuery.fn[o] = function(f){
                return this.bind(o, f);
        };
 });
 
 jQuery.extend({
+
        get: function( url, data, callback, type ) {
                // shift arguments if data argument was omited
                if ( jQuery.isFunction( data ) ) {
@@ -176,332 +171,120 @@ jQuery.extend({
                /*
                timeout: 0,
                data: null,
+               dataType: null,
+               dataTypes: null,
                username: null,
                password: null,
+               cache: null,
                traditional: false,
                */
-               // This function can be overriden by calling jQuery.ajaxSetup
                xhr: function() {
                        return new window.XMLHttpRequest();
                },
+               xhrResponseFields: {
+                       xml: "XML",
+                       text: "Text",
+                       json: "JSON"
+               },
+                       
                accepts: {
                        xml: "application/xml, text/xml",
                        html: "text/html",
-                       script: "text/javascript, application/javascript",
-                       json: "application/json, text/javascript",
                        text: "text/plain",
-                       _default: "*/*"
-               }
-       },
-
-       ajax: function( origSettings ) {
-               var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings),
-                       jsonp, status, data, type = s.type.toUpperCase(), noContent = rnoContent.test(type);
-
-               s.url = s.url.replace( rhash, "" );
-
-               // Use original (not extended) context object if it was provided
-               s.context = origSettings && origSettings.context != null ? origSettings.context : s;
-
-               // convert data if not already a string
-               if ( s.data && s.processData && typeof s.data !== "string" ) {
-                       s.data = jQuery.param( s.data, s.traditional );
-               }
-
-               // Handle JSONP Parameter Callbacks
-               if ( s.dataType === "jsonp" ) {
-                       if ( type === "GET" ) {
-                               if ( !jsre.test( s.url ) ) {
-                                       s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
-                               }
-                       } else if ( !s.data || !jsre.test(s.data) ) {
-                               s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
-                       }
-                       s.dataType = "json";
-               }
-
-               // Build temporary JSONP function
-               if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
-                       jsonp = s.jsonpCallback || ("jsonp" + jsc++);
-
-                       // Replace the =? sequence both in the query string and the data
-                       if ( s.data ) {
-                               s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
-                       }
-
-                       s.url = s.url.replace(jsre, "=" + jsonp + "$1");
-
-                       // We need to make sure
-                       // that a JSONP style response is executed properly
-                       s.dataType = "script";
-
-                       // Handle JSONP-style loading
-                       var customJsonp = window[ jsonp ];
-
-                       window[ jsonp ] = function( tmp ) {
-                               if ( jQuery.isFunction( customJsonp ) ) {
-                                       customJsonp( tmp );
-
-                               } else {
-                                       // Garbage collect
-                                       window[ jsonp ] = undefined;
-
-                                       try {
-                                               delete window[ jsonp ];
-                                       } catch( jsonpError ) {}
-                               }
-
-                               data = tmp;
-                               jQuery.handleSuccess( s, xhr, status, data );
-                               jQuery.handleComplete( s, xhr, status, data );
-                               
-                               if ( head ) {
-                                       head.removeChild( script );
+                       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: [],
+               
+               // 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: {
+               },
+               
+               // 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");
                                }
-                       };
-               }
-
-               if ( s.dataType === "script" && s.cache === null ) {
-                       s.cache = false;
-               }
-
-               if ( s.cache === false && noContent ) {
-                       var ts = jQuery.now();
-
-                       // try replacing _= if it is there
-                       var 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 : "");
-               }
-
-               // If data is available, append data to url for GET/HEAD requests
-               if ( s.data && noContent ) {
-                       s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
-               }
-
-               // Watch for a new set of requests
-               if ( s.global && jQuery.active++ === 0 ) {
-                       jQuery.event.trigger( "ajaxStart" );
-               }
-
-               // Matches an absolute URL, and saves the domain
-               var parts = rurl.exec( s.url ),
-                       remote = parts && (parts[1] && parts[1].toLowerCase() !== location.protocol || parts[2].toLowerCase() !== location.host);
-
-               // If we're requesting a remote document
-               // and trying to load JSON or Script with a GET
-               if ( s.dataType === "script" && type === "GET" && remote ) {
-                       var head = document.getElementsByTagName("head")[0] || document.documentElement;
-                       var script = document.createElement("script");
-                       if ( s.scriptCharset ) {
-                               script.charset = s.scriptCharset;
-                       }
-                       script.src = s.url;
-
-                       // Handle Script loading
-                       if ( !jsonp ) {
-                               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;
-                                               jQuery.handleSuccess( s, xhr, status, data );
-                                               jQuery.handleComplete( s, xhr, status, data );
-
-                                               // Handle memory leak in IE
-                                               script.onload = script.onreadystatechange = null;
-                                               if ( head && script.parentNode ) {
-                                                       head.removeChild( script );
-                                               }
-                                       }
-                               };
-                       }
-
-                       // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
-                       // This arises when a base node is used (#2709 and #4378).
-                       head.insertBefore( script, head.firstChild );
-
-                       // We handle everything using the script element injection
-                       return undefined;
-               }
-
-               var requestDone = false;
-
-               // Create the request object
-               var xhr = s.xhr();
-
-               if ( !xhr ) {
-                       return;
-               }
-
-               // Open the socket
-               // Passing null username, generates a login popup on Opera (#2865)
-               if ( s.username ) {
-                       xhr.open(type, s.url, s.async, s.username, s.password);
-               } else {
-                       xhr.open(type, s.url, s.async);
-               }
-
-               // Need an extra try/catch for cross domain requests in Firefox 3
-               try {
-                       // Set content-type if data specified and content-body is valid for this type
-                       if ( (s.data != null && !noContent) || (origSettings && origSettings.contentType) ) {
-                               xhr.setRequestHeader("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] ) {
-                                       xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
+                       },
+       
+                       // Check if xml has been properly parsed
+                       "xml": function(data) {
+                               var documentElement = data ? data.documentElement : data;
+                               if ( ! documentElement || ! documentElement.nodeName ) {
+                                       jQuery.error("typeerror");
                                }
-
-                               if ( jQuery.etag[s.url] ) {
-                                       xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
+                               if ( documentElement.nodeName == "parsererror" ) {
+                                       jQuery.error("parsererror");
                                }
                        }
-
-                       // Set header so the called script knows that it's an XMLHttpRequest
-                       // Only send the header if it's not a remote XHR
-                       if ( !remote ) {
-                               xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
-                       }
-
-                       // Set the Accepts header for the server, depending on the dataType
-                       xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
-                               s.accepts[ s.dataType ] + ", */*; q=0.01" :
-                               s.accepts._default );
-               } catch( headerError ) {}
-
-               // Allow custom headers/mimetypes and early abort
-               if ( s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false ) {
-                       // Handle the global AJAX counter
-                       if ( s.global && jQuery.active-- === 1 ) {
-                               jQuery.event.trigger( "ajaxStop" );
-                       }
-
-                       // close opended socket
-                       xhr.abort();
-                       return false;
-               }
-
-               if ( s.global ) {
-                       jQuery.triggerGlobal( s, "ajaxSend", [xhr, s] );
-               }
-
-               // Wait for a response to come back
-               var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
-                       // The request was aborted
-                       if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
-                               // Opera doesn't call onreadystatechange before this point
-                               // so we simulate the call
-                               if ( !requestDone ) {
-                                       jQuery.handleComplete( s, xhr, status, data );
-                               }
-
-                               requestDone = true;
-                               if ( xhr ) {
-                                       xhr.onreadystatechange = jQuery.noop;
-                               }
-
-                       // The transfer is complete and the data is available, or the request timed out
-                       } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
-                               requestDone = true;
-                               xhr.onreadystatechange = jQuery.noop;
-
-                               status = isTimeout === "timeout" ?
-                                       "timeout" :
-                                       !jQuery.httpSuccess( xhr ) ?
-                                               "error" :
-                                               s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
-                                                       "notmodified" :
-                                                       "success";
-
-                               var errMsg;
-
-                               if ( status === "success" ) {
-                                       // Watch for, and catch, XML document parse errors
-                                       try {
-                                               // process the data (runs the xml through httpData regardless of callback)
-                                               data = jQuery.httpData( xhr, s.dataType, s );
-                                       } catch( parserError ) {
-                                               status = "parsererror";
-                                               errMsg = parserError;
-                                       }
-                               }
-
-                               // Make sure that the request was successful or notmodified
-                               if ( status === "success" || status === "notmodified" ) {
-                                       // JSONP handles its own success callback
-                                       if ( !jsonp ) {
-                                               jQuery.handleSuccess( s, xhr, status, data );
-                                       }
-                               } else {
-                                       jQuery.handleError( s, xhr, status, errMsg );
-                               }
-
-                               // Fire the complete handlers
-                               if ( !jsonp ) {
-                                       jQuery.handleComplete( s, xhr, status, data );
-                               }
-
-                               if ( isTimeout === "timeout" ) {
-                                       xhr.abort();
-                               }
-
-                               // Stop memory leaks
-                               if ( s.async ) {
-                                       xhr = null;
+               },
+               
+               // 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;
+                       },
+                       
+                       // Text to html (no transformation)
+                       "text => html": function(data) {
+                               return data;
+                       },
+                       
+                       // Evaluate text as a json expression
+                       "text => json": jQuery.parseJSON,
+                       
+                       // 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);
                                }
+                               return xml;
                        }
-               };
-
-               // Override the abort handler, if we can (IE 6 doesn't allow it, but that's OK)
-               // Opera doesn't fire onreadystatechange at all on abort
-               try {
-                       var oldAbort = xhr.abort;
-                       xhr.abort = function() {
-                               if ( xhr ) {
-                                       // oldAbort has no call property in IE7 so
-                                       // just do it this way, which works in all
-                                       // browsers
-                                       Function.prototype.call.call( oldAbort, xhr );
-                               }
-
-                               onreadystatechange( "abort" );
-                       };
-               } catch( abortError ) {}
-
-               // Timeout checker
-               if ( s.async && s.timeout > 0 ) {
-                       setTimeout(function() {
-                               // Check to see if the request is still happening
-                               if ( xhr && !requestDone ) {
-                                       onreadystatechange( "timeout" );
-                               }
-                       }, s.timeout);
-               }
-
-               // Send the data
-               try {
-                       xhr.send( noContent || s.data == null ? null : s.data );
-
-               } catch( sendError ) {
-                       jQuery.handleError( s, xhr, null, sendError );
-
-                       // Fire the complete handlers
-                       jQuery.handleComplete( s, xhr, status, data );
                }
+       },
 
-               // firefox 1.5 doesn't fire statechange for sync requests
-               if ( !s.async ) {
-                       onreadystatechange();
+       // Main method
+       ajax: function( url , s ) {
+               
+               if ( arguments.length === 1 ) {
+                       s = url;
+                       url = s ? s.url : undefined;
                }
-
-               // return XMLHttpRequest to allow aborting the request etc.
-               return xhr;
+               
+               return jQuery.xhr().open( s ? s.type : undefined , url ).send( undefined , s );
+               
        },
 
        // Serialize an array of form elements or a set of
@@ -585,110 +368,7 @@ jQuery.extend({
 
        // Last-Modified header cache for next request
        lastModified: {},
-       etag: {},
-
-       handleError: function( s, xhr, status, e ) {
-               // If a local callback was specified, fire it
-               if ( s.error ) {
-                       s.error.call( s.context, xhr, status, e );
-               }
-
-               // Fire the global callback
-               if ( s.global ) {
-                       jQuery.triggerGlobal( s, "ajaxError", [xhr, s, e] );
-               }
-       },
-
-       handleSuccess: function( s, xhr, status, data ) {
-               // If a local callback was specified, fire it and pass it the data
-               if ( s.success ) {
-                       s.success.call( s.context, data, status, xhr );
-               }
-
-               // Fire the global callback
-               if ( s.global ) {
-                       jQuery.triggerGlobal( s, "ajaxSuccess", [xhr, s] );
-               }
-       },
-
-       handleComplete: function( s, xhr, status ) {
-               // Process result
-               if ( s.complete ) {
-                       s.complete.call( s.context, xhr, status );
-               }
-
-               // The request was completed
-               if ( s.global ) {
-                       jQuery.triggerGlobal( s, "ajaxComplete", [xhr, s] );
-               }
-
-               // Handle the global AJAX counter
-               if ( s.global && jQuery.active-- === 1 ) {
-                       jQuery.event.trigger( "ajaxStop" );
-               }
-       },
-               
-       triggerGlobal: function( s, type, args ) {
-               (s.context && s.context.url == null ? jQuery(s.context) : jQuery.event).trigger(type, args);
-       },
-
-       // Determines if an XMLHttpRequest was successful or not
-       httpSuccess: function( xhr ) {
-               try {
-                       // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
-                       return !xhr.status && location.protocol === "file:" ||
-                               xhr.status >= 200 && xhr.status < 300 ||
-                               xhr.status === 304 || xhr.status === 1223;
-               } catch(e) {}
-
-               return false;
-       },
-
-       // Determines if an XMLHttpRequest returns NotModified
-       httpNotModified: function( xhr, url ) {
-               var lastModified = xhr.getResponseHeader("Last-Modified"),
-                       etag = xhr.getResponseHeader("Etag");
-
-               if ( lastModified ) {
-                       jQuery.lastModified[url] = lastModified;
-               }
-
-               if ( etag ) {
-                       jQuery.etag[url] = etag;
-               }
-
-               return xhr.status === 304;
-       },
-
-       httpData: function( xhr, type, s ) {
-               var ct = xhr.getResponseHeader("content-type") || "",
-                       xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
-                       data = xml ? xhr.responseXML : xhr.responseText;
-
-               if ( xml && data.documentElement.nodeName === "parsererror" ) {
-                       jQuery.error( "parsererror" );
-               }
-
-               // Allow a pre-filtering function to sanitize the response
-               // s is checked to keep backwards compatibility
-               if ( s && s.dataFilter ) {
-                       data = s.dataFilter( data, type );
-               }
-
-               // The filter can actually parse the response
-               if ( typeof data === "string" ) {
-                       // Get the JavaScript object, if JSON is used.
-                       if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
-                               data = jQuery.parseJSON( data );
-
-                       // If the type is "script", eval it in global context
-                       } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
-                               jQuery.globalEval( data );
-                       }
-               }
-
-               return data;
-       }
+       etag: {}
 
 });
 
@@ -701,19 +381,24 @@ jQuery.extend({
  */
 if ( window.ActiveXObject ) {
        jQuery.ajaxSettings.xhr = function() {
-               if ( window.location.protocol !== "file:" ) {
-                       try {
-                               return new window.XMLHttpRequest();
-                       } catch(xhrError) {}
-               }
-
+       if ( window.location.protocol !== "file:" ) {
                try {
-                       return new window.ActiveXObject("Microsoft.XMLHTTP");
-               } catch(activeError) {}
+                       return new window.XMLHttpRequest();
+               } catch( xhrError ) {}
+       }
+       
+       try {
+               return new window.ActiveXObject("Microsoft.XMLHTTP");
+       } catch( activeError ) {}
        };
 }
 
+var testXHR = jQuery.ajaxSettings.xhr();
+
 // Does this browser support XHR requests?
-jQuery.support.ajax = !!jQuery.ajaxSettings.xhr();
+jQuery.support.ajax = !!testXHR;
+
+// Does this browser support crossDomain XHR requests
+jQuery.support.cors = testXHR && "withCredentials" in testXHR;
 
 })( jQuery );
index 4824c29..78b1bfd 100644 (file)
@@ -1,6 +1,6 @@
 (function( jQuery ) {
 
-var rclass = /[\n\t]/g,
+var rclass = /[\n\t\r]/g,
        rspaces = /\s+/,
        rreturn = /\r/g,
        rspecialurl = /^(?:href|src|style)$/,
@@ -205,7 +205,6 @@ jQuery.fn.extend({
                                if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
                                        return elem.getAttribute("value") === null ? "on" : elem.value;
                                }
-                               
 
                                // Everything else, we just grab the value
                                return (elem.value || "").replace(rreturn, "");
@@ -271,10 +270,10 @@ jQuery.extend({
                height: true,
                offset: true
        },
-               
+
        attr: function( elem, name, value, pass ) {
-               // don't set attributes on text and comment nodes
-               if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+               // don't get/set attributes on text, comment and attribute nodes
+               if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) {
                        return undefined;
                }
 
@@ -289,88 +288,96 @@ jQuery.extend({
                // Try to normalize/fix the name
                name = notxml && jQuery.props[ name ] || name;
 
-               // These attributes require special treatment
-               var special = rspecialurl.test( name );
+               // Only do all the following if this is a node (faster for style)
+               if ( elem.nodeType === 1 ) {
+                       // These attributes require special treatment
+                       var special = rspecialurl.test( name );
+
+                       // Safari mis-reports the default selected property of an option
+                       // Accessing the parent's selectedIndex property fixes it
+                       if ( name === "selected" && !jQuery.support.optSelected ) {
+                               var parent = elem.parentNode;
+                               if ( parent ) {
+                                       parent.selectedIndex;
+
+                                       // Make sure that it also works with optgroups, see #5701
+                                       if ( parent.parentNode ) {
+                                               parent.parentNode.selectedIndex;
+                                       }
+                               }
+                       }
 
-               // Safari mis-reports the default selected property of an option
-               // Accessing the parent's selectedIndex property fixes it
-               if ( name === "selected" && !jQuery.support.optSelected ) {
-                       var parent = elem.parentNode;
-                       if ( parent ) {
-                               parent.selectedIndex;
+                       // If applicable, access the attribute via the DOM 0 way
+                       // 'in' checks fail in Blackberry 4.7 #6931
+                       if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {
+                               if ( set ) {
+                                       // We can't allow the type property to be changed (since it causes problems in IE)
+                                       if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
+                                               jQuery.error( "type property can't be changed" );
+                                       }
+
+                                       if ( value === null ) {
+                                               if ( elem.nodeType === 1 ) {
+                                                       elem.removeAttribute( name );
+                                               }
 
-                               // Make sure that it also works with optgroups, see #5701
-                               if ( parent.parentNode ) {
-                                       parent.parentNode.selectedIndex;
+                                       } else {
+                                               elem[ name ] = value;
+                                       }
                                }
-                       }
-               }
 
-               // If applicable, access the attribute via the DOM 0 way
-               // 'in' checks fail in Blackberry 4.7 #6931
-               if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {
-                       if ( set ) {
-                               // We can't allow the type property to be changed (since it causes problems in IE)
-                               if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
-                                       jQuery.error( "type property can't be changed" );
+                               // browsers index elements by id/name on forms, give priority to attributes.
+                               if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
+                                       return elem.getAttributeNode( name ).nodeValue;
                                }
 
-                               if ( value === null ) {
-                                       if ( elem.nodeType === 1 ) {
-                                               elem.removeAttribute( name );
-                                       }
+                               // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+                               // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+                               if ( name === "tabIndex" ) {
+                                       var attributeNode = elem.getAttributeNode( "tabIndex" );
 
-                               } else {
-                                       elem[ name ] = value;
+                                       return attributeNode && attributeNode.specified ?
+                                               attributeNode.value :
+                                               rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+                                                       0 :
+                                                       undefined;
                                }
-                       }
 
-                       // browsers index elements by id/name on forms, give priority to attributes.
-                       if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
-                               return elem.getAttributeNode( name ).nodeValue;
+                               return elem[ name ];
                        }
 
-                       // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
-                       // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
-                       if ( name === "tabIndex" ) {
-                               var attributeNode = elem.getAttributeNode( "tabIndex" );
+                       if ( !jQuery.support.style && notxml && name === "style" ) {
+                               if ( set ) {
+                                       elem.style.cssText = "" + value;
+                               }
 
-                               return attributeNode && attributeNode.specified ?
-                                       attributeNode.value :
-                                       rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
-                                               0 :
-                                               undefined;
+                               return elem.style.cssText;
                        }
 
-                       return elem[ name ];
-               }
-
-               if ( !jQuery.support.style && notxml && name === "style" ) {
                        if ( set ) {
-                               elem.style.cssText = "" + value;
+                               // convert the value to a string (all browsers do this but IE) see #1070
+                               elem.setAttribute( name, "" + value );
                        }
 
-                       return elem.style.cssText;
-               }
+                       // Ensure that missing attributes return undefined
+                       // Blackberry 4.7 returns "" from getAttribute #6938
+                       if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {
+                               return undefined;
+                       }
 
-               if ( set ) {
-                       // convert the value to a string (all browsers do this but IE) see #1070
-                       elem.setAttribute( name, "" + value );
-               }
+                       var attr = !jQuery.support.hrefNormalized && notxml && special ?
+                                       // Some attributes require a special call on IE
+                                       elem.getAttribute( name, 2 ) :
+                                       elem.getAttribute( name );
 
-               // Ensure that missing attributes return undefined
-               // Blackberry 4.7 returns "" from getAttribute #6938
-               if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {
-                       return undefined;
+                       // Non-existent attributes return null, we normalize to undefined
+                       return attr === null ? undefined : attr;
                }
-
-               var attr = !jQuery.support.hrefNormalized && notxml && special ?
-                               // Some attributes require a special call on IE
-                               elem.getAttribute( name, 2 ) :
-                               elem.getAttribute( name );
-
-               // Non-existent attributes return null, we normalize to undefined
-               return attr === null ? undefined : attr;
+               // Handle everything which isn't a DOM element node
+               if ( set ) {
+                       elem[ name ] = value;
+               }
+               return elem[ name ];
        }
 });
 
index 9e1bfc6..346e52d 100644 (file)
@@ -215,7 +215,7 @@ jQuery.fn = jQuery.prototype = {
                        this.toArray() :
 
                        // Return just the object
-                       ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
+                       ( num < 0 ? this[ this.length + num ] : this[ num ] );
        },
 
        // Take an array of elements and push it onto the stack
@@ -733,6 +733,7 @@ jQuery.extend({
                        }
                }
 
+               // Flatten any nested arrays
                return ret.concat.apply( [], ret );
        },
 
index f7f65ef..f1e031f 100644 (file)
@@ -138,15 +138,17 @@ jQuery.fn.extend({
 
                if ( typeof key === "undefined" ) {
                        if ( this.length ) {
-                               var attr = this[0].attributes, name;
                                data = jQuery.data( this[0] );
 
-                               for ( var i = 0, l = attr.length; i < l; i++ ) {
-                                       name = attr[i].name;
-
-                                       if ( name.indexOf( "data-" ) === 0 ) {
-                                               name = name.substr( 5 );
-                                               dataAttr( this[0], name, data[ name ] );
+                               if ( this[0].nodeType === 1 ) {
+                                       var attr = this[0].attributes, name;
+                                       for ( var i = 0, l = attr.length; i < l; i++ ) {
+                                               name = attr[i].name;
+       
+                                               if ( name.indexOf( "data-" ) === 0 ) {
+                                                       name = name.substr( 5 );
+                                                       dataAttr( this[0], name, data[ name ] );
+                                               }
                                        }
                                }
                        }
index 51ce0c5..067383b 100644 (file)
@@ -2,7 +2,7 @@
 
 var elemdisplay = {},
        rfxtypes = /^(?:toggle|show|hide)$/,
-       rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/,
+       rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
        timerId,
        fxAttrs = [
                // height animations
index 3cfc817..fd470e7 100644 (file)
@@ -716,7 +716,7 @@ if ( !jQuery.support.submitBubbles ) {
 
        jQuery.event.special.submit = {
                setup: function( data, namespaces ) {
-                       if ( this.nodeName.toLowerCase() !== "form" ) {
+                       if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) {
                                jQuery.event.add(this, "click.specialSubmit", function( e ) {
                                        var elem = e.target,
                                                type = elem.type;
@@ -1075,8 +1075,8 @@ function liveHandler( event ) {
                events = events.events;
        }
 
-       // Make sure we avoid non-left-click bubbling in Firefox (#3861)
-       if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+       // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
+       if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
                return;
        }
        
index 9b7cfef..3895a7a 100644 (file)
@@ -355,7 +355,7 @@ jQuery.fn.extend({
                                                        root(this[i], first) :
                                                        this[i],
                                                i > 0 || results.cacheable || this.length > 1  ?
-                                                       jQuery(fragment).clone(true)[0] :
+                                                       fragment.cloneNode(true) :
                                                        fragment
                                        );
                                }
diff --git a/src/transports/jsonp.js b/src/transports/jsonp.js
new file mode 100644 (file)
index 0000000..d9e77f2
--- /dev/null
@@ -0,0 +1,89 @@
+(function( jQuery ) {
+
+var jsc = jQuery.now(),
+       jsre = /\=\?(&|$)/,
+       rquery_jsonp = /\?/;
+
+// Default jsonp callback name
+jQuery.ajaxSettings.jsonpCallback = function() {
+       return "jsonp" + jsc++;
+};
+
+// Normalize jsonp queries
+// 1) put callback parameter in url or data
+// 2) ensure transportDataType is json
+// 3) ensure options jsonp is always provided so that jsonp requests are always
+//    json request with the jsonp option set
+jQuery.xhr.prefilter( function(s) {
+       
+       var transportDataType = s.dataTypes[0];
+       
+       if ( s.jsonp ||
+               transportDataType === "jsonp" ||
+               transportDataType === "json" && ( jsre.test(s.url) || typeof(s.data) === "string" && jsre.test(s.data) ) ) {
+
+               var jsonp = s.jsonp = s.jsonp || "callback",
+                       jsonpCallback = s.jsonpCallback =
+                               jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
+                       url = s.url.replace(jsre, "=" + jsonpCallback + "$1"),
+                       data = s.url == url && typeof(s.data) === "string" ? s.data.replace(jsre, "=" + jsonpCallback + "$1") : s.data;
+                       
+               if ( url == s.url && data == s.data ) {
+                       url = url += (rquery_jsonp.test( url ) ? "&" : "?") + jsonp + "=" + jsonpCallback;
+               }
+               
+               s.url = url;
+               s.data = data;
+               
+               s.dataTypes[0] = "json";
+       }
+       
+});
+
+// Bind transport to json dataType
+jQuery.xhr.bindTransport("json", function(s) {
+
+       if ( s.jsonp ) {
+               
+               // Put callback in place
+               var responseContainer,
+                       jsonpCallback = s.jsonpCallback,
+                       previous = window[ jsonpCallback ];
+                       
+               window [ jsonpCallback ] = function( response ) {
+                       responseContainer = [response];
+               };
+               
+               s.complete = [function() {
+
+                       // Set callback back to previous value
+                       window[ jsonpCallback ] = previous;
+                       
+                       // Call if it was a function and we have a response
+                       if ( previous) {
+                               if ( responseContainer && jQuery.isFunction ( previous ) ) {
+                                       window[ jsonpCallback ] ( responseContainer[0] );
+                               }
+                       } else {
+                               // else, more memory leak avoidance
+                               try{ delete window[ jsonpCallback ]; } catch(e){}
+                       }
+                       
+               }, s.complete ];
+                               
+               // Use data converter to retrieve json after script execution
+               s.dataConverters["script => json"] = function() {
+                       if ( ! responseContainer ) {
+                               jQuery.error("Callback '" + jsonpCallback + "' was not called");
+                       }
+                       return responseContainer[ 0 ];
+               };
+               
+               // Delegate to script transport
+               return "script";
+               
+       }
+
+});
+
+})( jQuery );
diff --git a/src/transports/script.js b/src/transports/script.js
new file mode 100644 (file)
index 0000000..fe38735
--- /dev/null
@@ -0,0 +1,83 @@
+(function( jQuery ) {
+
+// Install text to script executor
+jQuery.extend( true, jQuery.ajaxSettings , {
+
+       accepts: {
+               script: "text/javascript, application/javascript"
+       },
+       
+       autoDataType: {
+               script: /javascript/
+       },
+               
+       dataConverters: {
+               "text => script": jQuery.globalEval
+       }
+} );
+
+// Bind script tag hack transport
+jQuery.xhr.bindTransport("script", function(s) {
+       
+       // Handle cache special case
+       if ( s.cache === undefined ) {
+               s.cache = false;
+       }
+       
+       // This transport only deals with cross domain get requests
+       if ( s.crossDomain && s.async && ( s.type === "GET" || ! s.data ) ) {
+               
+               s.global = false;
+               
+               var script,
+                       head = document.getElementsByTagName("head")[0] || document.documentElement;
+               
+               return {
+                       
+                       send: function(_, callback) {
+
+                               script = document.createElement("script");
+
+                               script.async = "async";
+
+                               if ( s.scriptCharset ) {
+                                       script.charset = s.scriptCharset;
+                               }
+                               
+                               script.src = s.url;
+                               
+                               // Attach handlers for all browsers
+                               script.onload = script.onreadystatechange = function(statusText) {
+                                       
+                                       if ( (!script.readyState ||
+                                                       script.readyState === "loaded" || script.readyState === "complete") ) {
+                                                               
+                                               // Handle memory leak in IE
+                                               script.onload = script.onreadystatechange = null;
+                                               
+                                               // Remove the script
+                                               if ( head && script.parentNode ) {
+                                                       head.removeChild( script );
+                                               }
+                                               
+                                               script = undefined;
+                                               
+                                               // Callback & dereference
+                                               callback(statusText ? 0 : 200, statusText || "success");
+                                       }
+                               };
+                               // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
+                               // This arises when a base node is used (#2709 and #4378).
+                               head.insertBefore( script, head.firstChild );
+                       },
+                       
+                       abort: function(statusText) {
+                               if ( script ) {
+                                       script.onload(statusText);
+                               }
+                       }
+               };
+       }
+});
+
+})( jQuery );
diff --git a/src/transports/xhr.js b/src/transports/xhr.js
new file mode 100644 (file)
index 0000000..783ee46
--- /dev/null
@@ -0,0 +1,191 @@
+(function( jQuery ) {
+
+var // Next fake timer id
+       xhrPollingId = jQuery.now(),
+       
+       // Callbacks hashtable
+       xhrs = {},
+
+       // #5280: see end of file
+       xhrUnloadAbortMarker = [];
+
+       
+jQuery.xhr.bindTransport( function( s , determineDataType ) {
+       
+       // Cross domain only allowed if supported through XMLHttpRequest
+       if ( ! s.crossDomain || jQuery.support.cors ) {
+               
+               var callback;
+               
+               return {
+                       
+                       send: function(headers, complete) {
+                               
+                               var xhr = s.xhr(),
+                                       handle;
+                               
+                               // Open the socket
+                               // Passing null username, generates a login popup on Opera (#2865)
+                               if ( s.username ) {
+                                       xhr.open(s.type, s.url, s.async, s.username, s.password);
+                               } else {
+                                       xhr.open(s.type, s.url, s.async);
+                               }
+                               
+                               // Requested-With header
+                               // Not set for crossDomain requests with no content
+                               // (see why at http://trac.dojotoolkit.org/ticket/9486)
+                               // Won't change header if already provided in beforeSend
+                               if ( ! ( s.crossDomain && ! s.hasContent ) && ! headers["x-requested-with"] ) {
+                                       headers["x-requested-with"] = "XMLHttpRequest";
+                               }
+                               
+                               // Need an extra try/catch for cross domain requests in Firefox 3
+                               try {
+                                       
+                                       jQuery.each(headers, function(key,value) {
+                                               xhr.setRequestHeader(key,value);
+                                       });
+                                       
+                               } catch(_) {}
+                               
+                               // Do send the request
+                               try {
+                                       xhr.send( ( s.hasContent && s.data ) || null );
+                               } catch(e) {
+                                       complete(0, "error", "" + e);
+                                       return;
+                               }
+                               
+                               // Listener
+                               callback = function ( abortStatusText ) {
+                                       
+                                       // Was never called and is aborted or complete
+                                       if ( callback && ( abortStatusText || xhr.readyState === 4 ) ) {
+                                       
+                                               // Do not listen anymore
+                                               if (handle) {
+                                                       xhr.onreadystatechange = jQuery.noop;
+                                                       delete xhrs[ handle ];
+                                                       handle = undefined;
+                                               }
+                                               
+                                               callback = 0;
+                                               
+                                               // Get info
+                                               var status, statusText, response, responseHeaders;
+                                                       
+                                               if ( abortStatusText ) {
+                                                       
+                                                       if ( xhr.readyState !== 4 ) {
+                                                               xhr.abort();
+                                                       }
+                                                       
+                                                       // Stop here if unloadAbort
+                                                       if ( abortStatusText === xhrUnloadAbortMarker ) {
+                                                               return;
+                                                       }
+                                                       
+                                                       status = 0;
+                                                       statusText = abortStatusText;
+                                                       
+                                               } else {
+                                                       
+                                                       status = xhr.status;
+                                                       
+                                                       try { // Firefox throws an exception when accessing statusText for faulty cross-domain requests
+                                                               
+                                                               statusText = xhr.statusText;
+                                                               
+                                                       } catch( e ) {
+                                                               
+                                                               statusText = ""; // We normalize with Webkit giving an empty statusText
+                                                               
+                                                       }
+                                                       
+                                                       responseHeaders = xhr.getAllResponseHeaders();
+                                                       
+                                                       // Filter status for non standard behaviours
+                                                       // (so many they seem to be the actual "standard")
+                                                       status =
+                                                               // Opera returns 0 when it should be 304
+                                                               // Webkit returns 0 for failing cross-domain no matter the real status
+                                                               status === 0 ?
+                                                                       (
+                                                                               ! s.crossDomain || statusText ? // Webkit, Firefox: filter out faulty cross-domain requests
+                                                                               (
+                                                                                       responseHeaders ? // Opera: filter out real aborts #6060
+                                                                                       304
+                                                                                       :
+                                                                                       0
+                                                                               )
+                                                                               :
+                                                                               302 // We assume 302 but could be anything cross-domain related
+                                                                       )
+                                                                       :
+                                                                       (
+                                                                               status == 1223 ?        // IE sometimes returns 1223 when it should be 204 (see #1450)
+                                                                                       204
+                                                                                       :
+                                                                                       status
+                                                                       );
+                                                                       
+                                                       // Guess response if needed & update datatype accordingly
+                                                       if ( status >= 200 && status < 300 ) {
+                                                               response = 
+                                                                       determineDataType(
+                                                                               s,
+                                                                               xhr.getResponseHeader("content-type"),
+                                                                               xhr.responseText,
+                                                                               xhr.responseXML );
+                                                       }
+                                               }
+                                               
+                                               // Call complete
+                                               complete(status,statusText,response,responseHeaders);
+                                       }
+                               };
+                               
+                               // if we're in sync mode
+                               // or it's in cache and has been retrieved directly (IE6 & IE7)
+                               // we need to manually fire the callback
+                               if ( ! s.async || xhr.readyState === 4 ) {
+                                       
+                                       callback();
+                                       
+                               } else {
+                                       
+                                       // Listener is externalized to handle abort on unload
+                                       handle = xhrPollingId++;
+                                       xhrs[ handle ] = xhr;
+                                       xhr.onreadystatechange = function() {
+                                               callback();
+                                       };
+                               }                                       
+                       },
+                       
+                       abort: function(statusText) {
+                               if ( callback ) {
+                                       callback(statusText);
+                               }
+                       }
+               };
+       }
+});
+
+// #5280: we need to abort on unload or IE will keep connections alive
+jQuery(window).bind( "unload" , function() {
+       
+       // Abort all pending requests
+       jQuery.each(xhrs, function(_, xhr) {
+               if ( xhr.onreadystatechange ) {
+                       xhr.onreadystatechange( xhrUnloadAbortMarker );
+               }
+       });
+       
+       // Resest polling structure to be safe
+       xhrs = {};
+       
+});
+
+})( jQuery );
diff --git a/src/xhr.js b/src/xhr.js
new file mode 100644 (file)
index 0000000..57903e0
--- /dev/null
@@ -0,0 +1,909 @@
+(function( jQuery ) {
+
+var rquery_xhr = /\?/,
+       rhash = /#.*$/,
+       rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
+       rnoContent = /^(?:GET|HEAD)$/,
+       rts = /([?&])_=[^&]*/,
+       rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+       
+       sliceFunc = Array.prototype.slice,
+       
+       isFunction = jQuery.isFunction;
+       
+// Creates a jQuery xhr object
+jQuery.xhr = function( _native ) {
+       
+       if ( _native ) {
+               return jQuery.ajaxSettings.xhr();
+       }
+       
+       function reset(force) {
+               
+               // We only need to reset if we went through the init phase
+               // (with the exception of object creation)
+               if ( force || internal ) {
+               
+                       // Reset callbacks lists
+                       callbacksLists = {
+                               success: createCBList(),
+                               error: createCBList(),
+                               complete: createCBList()
+                       };
+                       
+                       // Reset private variables
+                       requestHeaders = {};
+                       responseHeadersString = responseHeaders = internal = done = timeoutTimer = s = undefined;
+                       
+                       // Reset state
+                       xhr.readyState = 0;
+                       sendFlag = 0;
+                       
+                       // Remove responseX fields
+                       for ( var name in xhr ) {
+                               if ( /^response/.test(name) ) {
+                                       delete xhr[name];
+                               }
+                       }
+               }
+       }
+       
+       function init() {
+               
+               var // Options extraction
+               
+                       // Remove hash character (#7531: first for string promotion)
+                       url = s.url = ( "" + s.url ).replace( rhash , "" ),
+                       
+                       // Uppercase the type
+                       type = s.type = s.type.toUpperCase(),
+                       
+                       // Determine if request has content
+                       hasContent = s.hasContent = ! rnoContent.test( type ),
+                       
+                       // Extract dataTypes list
+                       dataType = s.dataType,
+                       dataTypes = s.dataTypes = dataType ? jQuery.trim(dataType).toLowerCase().split(/\s+/) : ["*"],
+                       
+                       // Determine if a cross-domain request is in order
+                       parts = rurl.exec( url.toLowerCase() ),
+                       loc = location,
+                       crossDomain = s.crossDomain = !!( parts && ( parts[1] && parts[1] != loc.protocol || parts[2] != loc.host ) ),
+                       
+                       // Get other options locally
+                       data = s.data,
+                       originalContentType = s.contentType,
+                       prefilters = s.prefilters,
+                       accepts = s.accepts,
+                       headers = s.headers,
+                       
+                       // Other Variables
+                       transportDataType,
+                       i;
+                       
+               // Convert data if not already a string
+               if ( data && s.processData && typeof data != "string" ) {
+                       data = s.data = jQuery.param( data , s.traditional );
+               }
+               
+               // Apply option prefilters
+               for (i in prefilters) {
+                       prefilters[i](s);
+               }
+               
+               // Get internal
+               internal = selectTransport( s );
+               
+               // Re-actualize url & data
+               url = s.url;
+               data = s.data;
+               
+               // If internal was found
+               if ( internal ) {
+                       
+                       // Get transportDataType
+                       transportDataType = dataTypes[0];
+                       
+                       // More options handling for requests with no content
+                       if ( ! hasContent ) {
+                               
+                               // If data is available, append data to url
+                               if ( data ) {
+                                       url += (rquery_xhr.test(url) ? "&" : "?") + data;
+                               }
+                                                               
+                               // Add anti-cache in url if needed
+                               if ( s.cache === false ) {
+                                       
+                                       var ts = jQuery.now(),
+                                               // try replacing _= if it is there
+                                               ret = url.replace(rts, "$1_=" + ts );
+                                               
+                                       // if nothing was replaced, add timestamp to the end
+                                       url = ret + ((ret == url) ? (rquery_xhr.test(url) ? "&" : "?") + "_=" + ts : "");
+                               }
+                               
+                               s.url = url;
+                       }
+                       
+                       // Set the correct header, if data is being sent
+                       if ( ( data && hasContent ) || originalContentType ) {
+                               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[url] ) { 
+                                       requestHeaders["if-modified-since"] = jQuery_lastModified[url];
+                               }
+                               if ( jQuery_etag[url] ) {
+                                       requestHeaders["if-none-match"] = jQuery_etag[url];
+                               }
+                       }
+               
+                       // Set the Accepts header for the server, depending on the dataType
+                       requestHeaders.accept = transportDataType && accepts[ transportDataType ] ?
+                               accepts[ transportDataType ] + ( transportDataType !== "*" ? ", */*; q=0.01" : "" ) :
+                               accepts[ "*" ];
+                               
+                       // Check for headers option
+                       for ( i in headers ) {
+                               requestHeaders[ i.toLowerCase() ] = headers[ i ];
+                       }                       
+               }
+                       
+               callbackContext = s.context || s;
+               globalEventContext = s.context ? jQuery(s.context) : jQuery.event;
+               
+               for ( i in callbacksLists ) {
+                       callbacksLists[i].bind(s[i]);
+               }
+               
+               // Watch for a new set of requests
+               if ( s.global && jQuery.active++ === 0 ) {
+                       jQuery.event.trigger( "ajaxStart" );
+               }
+               
+               done = whenDone;
+       }
+       
+       function whenDone(status, statusText, response, headers) {
+               
+               // Called once
+               done = undefined;
+               
+               // Reset sendFlag
+               sendFlag = 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 ( ifModified ) {
+                               var lastModified = xhr.getResponseHeader("Last-Modified"),
+                                       etag = xhr.getResponseHeader("Etag");
+                                       
+                               if (lastModified) {
+                                       jQuery_lastModified[url] = lastModified;
+                               }
+                               if (etag) {
+                                       jQuery_etag[url] = etag;
+                               }
+                       }
+                       
+                       if ( 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 {
+                                       
+                                       function checkData(data) {
+                                               if ( data !== undefined ) {
+                                                       var testFunction = s.dataCheckers[srcDataType];
+                                                       if ( isFunction( testFunction ) ) {
+                                                               testFunction(data);
+                                                       }
+                                               }
+                                       }
+                                       
+                                       function convertData (data) {
+                                               var conversionFunction = dataConverters[srcDataType+" => "+destDataType] ||
+                                                               dataConverters["* => "+destDataType],
+                                                       noFunction = ! isFunction( conversionFunction );
+                                               if ( noFunction ) {
+                                                       if ( srcDataType != "text" && destDataType != "text" ) {
+                                                               // We try to put text inbetween
+                                                               var first = dataConverters[srcDataType+" => text"] ||
+                                                                               dataConverters["* => text"],
+                                                                       second = dataConverters["text => "+destDataType] ||
+                                                                               dataConverters["* => "+destDataType],
+                                                                       areFunctions = isFunction( first ) && isFunction( second );
+                                                               if ( areFunctions ) {
+                                                                       conversionFunction = function (data) {
+                                                                               return second( first ( data ) );
+                                                                       };
+                                                               }
+                                                               noFunction = ! areFunctions;
+                                                       }
+                                                       if ( noFunction ) {
+                                                               jQuery.error( "no data converter between " + srcDataType + " and " + destDataType );
+                                                       }
+                                                       
+                                               }
+                                               return conversionFunction(data);
+                                       }
+                                       
+                                       var dataTypes = s.dataTypes,
+                                               i,
+                                               length,
+                                               data = response,
+                                               dataConverters = s.dataConverters,
+                                               srcDataType,
+                                               destDataType,
+                                               responseTypes = s.xhrResponseFields;
+                                               
+                                       for ( i = 0, length = dataTypes.length ; i < length ; i++ ) {
+       
+                                               destDataType = dataTypes[i];
+                                               
+                                               if ( !srcDataType ) { // First time
+                                                       
+                                                       // Copy type
+                                                       srcDataType = destDataType;
+                                                       // Check
+                                                       checkData(data);
+                                                       // Apply dataFilter
+                                                       if ( isFunction( s.dataFilter ) ) {
+                                                               data = s.dataFilter(data, s.dataType);
+                                                               // Recheck data
+                                                               checkData(data);
+                                                       }
+                                                       
+                                               } else { // Subsequent times
+                                                       
+                                                       // handle auto
+                                                       // JULIAN: for reasons unknown to me === doesn't work here
+                                                       if (destDataType == "*") {
+       
+                                                               destDataType = srcDataType;
+                                                               
+                                                       } else if ( srcDataType != destDataType ) {
+                                                               
+                                                               // Convert
+                                                               data = convertData(data);
+                                                               // Copy type & check
+                                                               srcDataType = destDataType;
+                                                               checkData(data);
+                                                               
+                                                       }
+                                                       
+                                               }
+       
+                                               // Copy response into the xhr if it hasn't been already
+                                               var responseDataType,
+                                                       responseType = responseTypes[srcDataType];
+                                               
+                                               if ( responseType ) {
+                                                       
+                                                       responseDataType = srcDataType;
+                                                       
+                                               } else {
+                                                       
+                                                       responseType = responseTypes[ responseDataType = "text" ];
+                                                       
+                                               }
+                                                       
+                                               if ( responseType !== 1 ) {
+                                                       xhr[ "response" + responseType ] = data;
+                                                       responseTypes[ responseType ] = 1;
+                                               }
+                                               
+                                       }
+       
+                                       // We have a real success
+                                       success = data;
+                                       isSuccess = 1;
+                                       
+                               } catch(e) {
+                                       
+                                       statusText = "parsererror";
+                                       error = "" + e;
+                                       
+                               }
+                       }
+                       
+               } else { // if not success, mark it as an error
+                       
+                               error = error || statusText;
+                               
+               }
+                       
+               // Set data for the fake xhr object
+               xhr.status = status;
+               xhr.statusText = statusText;
+               
+               // Keep local copies of vars in case callbacks re-use the xhr
+               var _s = s,
+                       _callbacksLists = callbacksLists,
+                       _callbackContext = callbackContext,
+                       _globalEventContext = globalEventContext;
+                       
+               // Set state if the xhr hasn't been re-used
+               function _setState( value ) {
+                       if ( xhr.readyState && s === _s ) {
+                               setState( value );
+                       }
+               }
+                               
+               // Really completed?
+               if ( status && s.async ) {
+                       setState( 2 );
+                       _setState( 3 );
+               }
+               
+               // We're done
+               _setState( 4 );
+               
+               // Success
+               _callbacksLists.success.fire( isSuccess , _callbackContext , success, statusText, xhr);
+               if ( isSuccess && _s.global ) {
+                       _globalEventContext.trigger( "ajaxSuccess", [xhr, _s, success] );
+               }
+               // Error
+               _callbacksLists.error.fire( ! isSuccess , _callbackContext , xhr, statusText, error);
+               if ( !isSuccess && _s.global ) {
+                       _globalEventContext.trigger( "ajaxError", [xhr, _s, error] );   
+               }
+               // Complete
+               _callbacksLists.complete.fire( 1 , _callbackContext, xhr, statusText);
+               if ( _s.global ) {
+                       _globalEventContext.trigger( "ajaxComplete", [xhr, _s] );
+                       // Handle the global AJAX counter
+                       if ( ! --jQuery.active ) {
+                               jQuery.event.trigger( "ajaxStop" );
+                       }
+               }
+       }
+       
+       // Ready state control
+       function checkState( expected , test ) {
+               if ( expected !== true && ( expected === false || test === false || xhr.readyState !== expected ) ) {
+                       jQuery.error("INVALID_STATE_ERR");
+               }
+       }
+       
+       // Ready state change
+       function setState( value ) {
+               xhr.readyState = value;
+               if ( isFunction( xhr.onreadystatechange ) ) {
+                       xhr.onreadystatechange();
+               }
+       }
+       
+       var // jQuery lists
+               jQuery_lastModified = jQuery.lastModified,
+               jQuery_etag = jQuery.etag,
+               // Options object
+               s,
+               // Callback stuff
+               callbackContext,
+               globalEventContext,
+               callbacksLists,
+               // Headers (they are sent all at once)
+               requestHeaders,
+               // Response headers
+               responseHeadersString,
+               responseHeaders,
+               // Done callback
+               done,
+               // transport
+               internal,
+               // timeout handle
+               timeoutTimer,
+               // The send flag
+               sendFlag,
+               // Fake xhr
+               xhr = {
+                       // state
+                       readyState: 0,
+                       
+                       // Callback
+                       onreadystatechange: null,
+                       
+                       // Open
+                       open: function(type, url, async, username, password) {
+                               
+                               xhr.abort();
+                               reset();
+                               
+                               s = {
+                                       type: type,
+                                       url: url,
+                                       async: async,
+                                       username: username,
+                                       password: password
+                               };
+                               
+                               setState(1);
+                               
+                               return xhr;
+                       },
+                       
+                       // Send
+                       send: function(data, moreOptions) {
+                               
+                               checkState(1 , !sendFlag);
+                               
+                               s.data = data;
+                               
+                               s = jQuery.extend( true,
+                                       {},
+                                       jQuery.ajaxSettings,
+                                       s,
+                                       moreOptions || ( moreOptions === false ? { global: false } : {} ) );
+                                       
+                               if ( moreOptions ) {
+                                       // We force the original context
+                                       // (plain objects used as context get extended)
+                                       s.context = moreOptions.context;
+                               }
+                               
+                               init();
+                               
+                               // If not internal, abort
+                               if ( ! internal ) {
+                                       done( 0 , "transport not found" );
+                                       return false;
+                               }
+                               
+                               // Allow custom headers/mimetypes and early abort
+                               if ( s.beforeSend ) {
+                                       
+                                       var _s = s;
+                                       
+                                       if ( s.beforeSend.call(callbackContext, xhr, s) === false || ! xhr.readyState || _s !== s ) {
+                                               
+                                               // Abort if not done
+                                               if ( xhr.readyState && _s === s ) {
+                                                       xhr.abort();
+                                               }
+       
+                                               // Handle the global AJAX counter
+                                               if ( _s.global && ! --jQuery.active ) {
+                                                       jQuery.event.trigger( "ajaxStop" );
+                                               }
+                                               
+                                               return false;
+                                       }
+                               }
+                               
+                               sendFlag = 1;
+                               
+                               // Send global event
+                               if ( s.global ) {
+                                       globalEventContext.trigger("ajaxSend", [xhr, s]);
+                               }
+                               
+                               // Timeout
+                               if ( s.async && s.timeout > 0 ) {
+                                       timeoutTimer = setTimeout(function(){
+                                               xhr.abort("timeout");
+                                       }, s.timeout);
+                               }
+                               
+                               if ( s.async ) {
+                                       setState(1);
+                               }
+                               
+                               try {
+                                       
+                                       internal.send(requestHeaders, done);
+                                       return xhr;
+                                                                                       
+                               } catch (e) {
+                                       
+                                       if ( done ) {
+                                               
+                                               done(0, "error", "" + e);
+                                               
+                                       } else {
+                                               
+                                               jQuery.error(e);
+                                               
+                                       }
+                               }
+                               
+                               return false;
+                       },
+                       
+                       // Caches the header
+                       setRequestHeader: function(name,value) {
+                               checkState(1, !sendFlag);
+                               requestHeaders[ name.toLowerCase() ] = value;
+                               return xhr;
+                       },
+                       
+                       // Raw string
+                       getAllResponseHeaders: function() {
+                               return xhr.readyState <= 1 ? "" : responseHeadersString;
+                       },
+                       
+                       // Builds headers hashtable if needed
+                       getResponseHeader: function( key ) {
+                               
+                               if ( xhr.readyState <= 1 ) {
+                                       
+                                       return null;
+                                       
+                               }
+                               
+                               if ( responseHeaders === undefined ) {
+                                       
+                                       responseHeaders = {};
+                                       
+                                       if ( typeof responseHeadersString === "string" ) {
+                                               
+                                               var match;
+                                               
+                                               while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+                                                       responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
+                                               }
+                                       }
+                               }
+                               return responseHeaders[ key.toLowerCase() ];
+                       },
+                       
+                       // Cancel the request
+                       abort: function(statusText) {
+                               if (internal) {
+                                       internal.abort( statusText || "abort" );
+                               }
+                               xhr.readyState = 0;
+                       }
+               };
+               
+       // Init data (so that we can bind callbacks early
+       reset(1);
+
+       // Install callbacks related methods
+       jQuery.each(callbacksLists, function(name) {
+               var list;
+               xhr[name] = function() {
+                       list = callbacksLists[name];
+                       if ( list ) {
+                               list.bind.apply(list, arguments );
+                       }
+                       return this;
+               };
+       });
+       
+       // Return the xhr emulation
+       return xhr;
+};
+
+// Create a callback list
+function createCBList() {
+       
+       var functors = [],
+               autoFire = 0,
+               fireArgs,
+               list = {
+               
+                       fire: function( flag , context ) {
+                               
+                               // Save info for later bindings
+                               fireArgs = arguments;
+                               
+                               // Remove autoFire to keep bindings in order
+                               autoFire = 0;
+                                       
+                               var args = sliceFunc.call( fireArgs , 2 );
+                                       
+                               // Execute callbacks
+                               while ( flag && functors.length ) {
+                                       flag = functors.shift().apply( context , args ) !== false;
+                               }
+                                       
+                               // Clean if asked to stop
+                               if ( ! flag ) {
+                                       clean();
+                               }
+                                               
+                               // Set autoFire
+                               autoFire = 1;                                   
+                       },
+                       
+                       bind: function() {
+                               
+                               var args = arguments,
+                                       i = 0,
+                                       length = args.length,
+                                       func;
+                               
+                               for ( ; i < length ; i++ ) {
+                                       
+                                       func = args[ i ];
+                                       
+                                       if ( jQuery.isArray(func) ) {
+                                               
+                                               list.bind.apply( list , func );
+                                               
+                                       } else if ( isFunction(func) ) {
+                                               
+                                               // Add if not already in
+                                               if ( ! pos( func ) ) {
+                                                       functors.push( func );
+                                               }
+                                       }
+                               }
+                               
+                               if ( autoFire ) {
+                                       list.fire.apply( list , fireArgs );
+                               }
+                       },
+                       
+                       unbind: function() {
+                               
+                               var i = 0,
+                                       args = arguments,
+                                       length = args.length,
+                                       func,                                   
+                                       position;
+                                       
+                               if ( length ) {
+                                               
+                                       for( ; i < length ; i++ ) {
+                                               func = args[i];
+                                               if ( jQuery.isArray(func) ) {
+                                                       list.unbind.apply(list,func);
+                                               } else if ( isFunction(func) ) {
+                                                       position = pos(func);
+                                                       if ( position ) {
+                                                               functors.splice(position-1,1);
+                                                       }
+                                               }
+                                       }
+                               
+                               } else {
+                                       
+                                       functors = [];
+                               
+                               }
+
+                       }
+                       
+               };
+
+       // Get the index of the functor in the list (1-based)
+       function pos( func ) {
+               for (var i = 0, length = functors.length; i < length && functors[i] !== func; i++) {
+               }
+               return i < length ? ( i + 1 ) : 0;
+       }
+               
+       // Clean the object
+       function clean() {
+               // Empty callbacks list
+               functors = [];
+               // Inhibit methods
+               for (var i in list) {
+                       list[i] = jQuery.noop;
+               }
+       }
+                               
+       return list;
+}
+
+jQuery.extend(jQuery.xhr, {
+       
+       // Add new prefilter
+       prefilter: function (functor) {
+               if ( isFunction(functor) ) {
+                       jQuery.ajaxSettings.prefilters.push( functor );
+               }
+               return this;
+       },
+       
+       // Bind a transport to one or more dataTypes
+       bindTransport: function () {
+               
+               var args = arguments,
+                       i,
+                       start = 0,
+                       length = args.length,
+                       dataTypes = [ "*" ],
+                       functors = [],
+                       functor,
+                       first,
+                       append,
+                       list,
+                       transports = jQuery.ajaxSettings.transports;
+                       
+               if ( length ) {
+                               
+                       if ( ! isFunction( args[ 0 ] ) ) {
+                               
+                               dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
+                               start = 1;
+                               
+                       }
+                       
+                       if ( dataTypes.length && start < length ) {
+                               
+                               for ( i = start; i < length; i++ ) {
+                                       functor = args[i];
+                                       if ( isFunction(functor) ) {
+                                               functors.push( functor );
+                                       }
+                               }
+                                               
+                               if ( functors.length ) {
+                                                       
+                                       jQuery.each ( dataTypes, function( _ , dataType ) {
+                                               
+                                               first = /^\+/.test( dataType );
+                                               
+                                               if (first) {
+                                                       dataType = dataType.substr(1);
+                                               }
+                                               
+                                               if ( dataType !== "" ) {
+                                               
+                                                       append = Array.prototype[ first ? "unshift" : "push" ];
+                                                       
+                                                       list = transports[ dataType ];
+                                       
+                                                       jQuery.each ( functors, function( _ , functor ) {
+                                                                       
+                                                               if ( ! list ) {
+                                                                       
+                                                                       list = transports[ dataType ] = [ functor ];
+                                                                       
+                                                               } else {
+                                                                       
+                                                                       append.call( list , functor );
+                                                               }
+                                                       } );
+                                               }
+                                                                       
+                                       } );
+                               }
+                       }
+               }
+               
+               return this;
+       }
+
+       
+});
+
+// Select a transport given options
+function selectTransport( s ) {
+
+       var dataTypes = s.dataTypes,
+               transportDataType,
+               transportsList,
+               transport,
+               i,
+               length,
+               checked = {},
+               flag;
+               
+       function initSearch( dataType ) {
+
+               flag = transportDataType !== dataType && ! checked[ dataType ];
+               
+               if ( flag ) {
+                       
+                       checked[ dataType ] = 1;
+                       transportDataType = dataType;
+                       transportsList = s.transports[ dataType ];
+                       i = -1;
+                       length = transportsList ? transportsList.length : 0 ;
+               }
+
+               return flag;
+       }
+       
+       initSearch( dataTypes[ 0 ] );
+
+       for ( i = 0 ; ! transport && i <= length ; i++ ) {
+               
+               if ( i === length ) {
+                       
+                       initSearch( "*" );
+                       
+               } else {
+
+                       transport = transportsList[ i ]( s , determineDataType );
+
+                       // If we got redirected to another dataType
+                       // Search there (if not in progress or already tried)
+                       if ( typeof( transport ) === "string" &&
+                               initSearch( transport ) ) {
+
+                               dataTypes.unshift( transport );
+                               transport = 0;
+                       }
+               }
+       }
+
+       return transport;
+}
+       
+// 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;
+}      
+
+})( jQuery );
diff --git a/test/data/atom+xml.php b/test/data/atom+xml.php
new file mode 100644 (file)
index 0000000..944591a
--- /dev/null
@@ -0,0 +1,4 @@
+<?php header("Content-type: atom+xml") ?>
+<root>
+       <element />
+</root>
\ No newline at end of file
diff --git a/test/data/css.php b/test/data/css.php
new file mode 100644 (file)
index 0000000..9d079e7
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+error_reporting(0);
+$id = isset ( $_REQUEST['id'] ) ? $_REQUEST['id'] : null;
+$wait = isset( $_REQUEST['wait'] ) ? $_REQUEST['wait'] : null;
+
+if ( $wait ) sleep( $wait );
+
+header("Content-type: text/css");
+
+if ( $id ) {
+       ?>
+       div#<?= $id ?> { margin-left: 27px }
+       <?php
+}
+?>
\ No newline at end of file
diff --git a/test/data/headers.php b/test/data/headers.php
new file mode 100644 (file)
index 0000000..f2c21c0
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+header( "Sample-Header: Hello World" );
+
+$headers = array();
+
+foreach( $_SERVER as $key => $value ) { 
+       
+       if ( substr( $key , 0 , 5 ) == "HTTP_" ) { 
+               
+               $key = str_replace( "_" , "-" , substr( $key , 5) );
+               $headers[ $key ] = $value;
+
+       }
+       
+} 
+
+foreach( explode( "_" , $_GET[ "keys" ] ) as $key ) {
+       echo "$key: " . $headers[ strtoupper( $key ) ] . "\n";
+}
diff --git a/test/data/with_fries_over_jsonp.php b/test/data/with_fries_over_jsonp.php
new file mode 100644 (file)
index 0000000..456aeb3
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+error_reporting(0);
+$callback = $_REQUEST['callback'];
+$json = $_REQUEST['json'];
+$text = json_encode(file_get_contents(dirname(__FILE__)."/with_fries.xml"));
+echo "$callback($text)";
+?>
index e668727..bd11ebb 100644 (file)
        <script src="../src/manipulation.js"></script>
        <script src="../src/css.js"></script>
        <script src="../src/ajax.js"></script>
+       <script src="../src/xhr.js"></script>
+       <script src="../src/transports/jsonp.js"></script>
+       <script src="../src/transports/script.js"></script>
+       <script src="../src/transports/xhr.js"></script>
        <script src="../src/effects.js"></script>
        <script src="../src/offset.js"></script>
        <script src="../src/dimensions.js"></script>
index 4ce14c2..35c0302 100644 (file)
@@ -38,6 +38,216 @@ test("jQuery.ajax() - success callbacks", function() {
        });
 });
 
+test("jQuery.ajax() - success callbacks - (url, options) syntax", function() {
+       expect( 8 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+
+       setTimeout(function(){
+               jQuery('#foo').ajaxStart(function(){
+                       ok( true, "ajaxStart" );
+               }).ajaxStop(function(){
+                       ok( true, "ajaxStop" );
+                       start();
+               }).ajaxSend(function(){
+                       ok( true, "ajaxSend" );
+               }).ajaxComplete(function(){
+                       ok( true, "ajaxComplete" );
+               }).ajaxError(function(){
+                       ok( false, "ajaxError" );
+               }).ajaxSuccess(function(){
+                       ok( true, "ajaxSuccess" );
+               });
+
+               jQuery.ajax( url("data/name.html") , {
+                       beforeSend: function(){ ok(true, "beforeSend"); },
+                       success: function(){ ok(true, "success"); },
+                       error: function(){ ok(false, "error"); },
+                       complete: function(){ ok(true, "complete"); }
+               });
+       }, 13);
+});
+
+test("jQuery.ajax() - success/error callbacks (remote)", function() {
+       
+       var supports = jQuery.support.cors;
+       
+       expect( supports ? 9 : 6 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+
+       setTimeout(function(){
+               jQuery('#foo').ajaxStart(function(){
+                       ok( true, "ajaxStart" );
+               }).ajaxStop(function(){
+                       ok( true, "ajaxStop" );
+                       start();
+               }).ajaxSend(function(){
+                       ok( supports , "ajaxSend" );
+               }).ajaxComplete(function(){
+                       ok( true, "ajaxComplete" );
+               }).ajaxError(function(){
+                       ok( ! supports, "ajaxError" );
+               }).ajaxSuccess(function(){
+                       ok( supports, "ajaxSuccess" );
+               });
+
+               jQuery.ajax({
+                       // JULIAN TODO: Get an url especially for jQuery
+                       url: "http://rockstarapps.com/test.php",
+                       dataType: "text",
+                       beforeSend: function(){ ok(supports, "beforeSend"); },
+                       success: function( val ){ ok(supports, "success"); ok(supports && val.length, "data received"); },
+                       error: function(_ , a , b ){ ok(!supports, "error"); },
+                       complete: function(){ ok(true, "complete"); }
+               });
+       }, 13);
+});
+
+test("jQuery.ajax() - success callbacks (late binding)", function() {
+       expect( 8 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+
+       setTimeout(function(){
+               jQuery('#foo').ajaxStart(function(){
+                       ok( true, "ajaxStart" );
+               }).ajaxStop(function(){
+                       ok( true, "ajaxStop" );
+                       start();
+               }).ajaxSend(function(){
+                       ok( true, "ajaxSend" );
+               }).ajaxComplete(function(){
+                       ok( true, "ajaxComplete" );
+               }).ajaxError(function(){
+                       ok( false, "ajaxError" );
+               }).ajaxSuccess(function(){
+                       ok( true, "ajaxSuccess" );
+               });
+
+               jQuery.ajax({
+                       url: url("data/name.html"),
+                       beforeSend: function(){ ok(true, "beforeSend"); }
+               })
+                       .complete(function(){ ok(true, "complete"); })
+                       .success(function(){ ok(true, "success"); })
+                       .error(function(){ ok(false, "error"); });
+       }, 13);
+});
+
+test("jQuery.ajax() - success callbacks (oncomplete binding)", function() {
+       expect( 8 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+
+       setTimeout(function(){
+               jQuery('#foo').ajaxStart(function(){
+                       ok( true, "ajaxStart" );
+               }).ajaxStop(function(){
+                       ok( true, "ajaxStop" );
+               }).ajaxSend(function(){
+                       ok( true, "ajaxSend" );
+               }).ajaxComplete(function(){
+                       ok( true, "ajaxComplete" );
+               }).ajaxError(function(){
+                       ok( false, "ajaxError" );
+               }).ajaxSuccess(function(){
+                       ok( true, "ajaxSuccess" );
+               });
+
+               jQuery.ajax({
+                       url: url("data/name.html"),
+                       beforeSend: function(){ ok(true, "beforeSend"); },
+                       complete: function(xhr) {
+                               xhr
+                               .complete(function(){ ok(true, "complete"); })
+                               .success(function(){ ok(true, "success"); })
+                               .error(function(){ ok(false, "error"); })
+                               .complete(function(){ start(); });
+                       }
+               })
+       }, 13);
+});
+
+test("jQuery.ajax() - success callbacks (very late binding)", function() {
+       expect( 8 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+
+       setTimeout(function(){
+               jQuery('#foo').ajaxStart(function(){
+                       ok( true, "ajaxStart" );
+               }).ajaxStop(function(){
+                       ok( true, "ajaxStop" );
+               }).ajaxSend(function(){
+                       ok( true, "ajaxSend" );
+               }).ajaxComplete(function(){
+                       ok( true, "ajaxComplete" );
+               }).ajaxError(function(){
+                       ok( false, "ajaxError" );
+               }).ajaxSuccess(function(){
+                       ok( true, "ajaxSuccess" );
+               });
+
+               jQuery.ajax({
+                       url: url("data/name.html"),
+                       beforeSend: function(){ ok(true, "beforeSend"); },
+                       complete: function(xhr) {
+                               setTimeout (function() {
+                                       xhr
+                                       .complete(function(){ ok(true, "complete"); })
+                                       .success(function(){ ok(true, "success"); })
+                                       .error(function(){ ok(false, "error"); })
+                                       .complete(function(){ start(); });
+                               },100);
+                       }
+               })
+       }, 13);
+});
+
+test("jQuery.ajax() - success callbacks (order)", function() {
+       expect( 1 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+       
+       var testString = "";
+
+       setTimeout(function(){
+               jQuery.ajax({
+                       url: url("data/name.html"),
+                       success: function( _1 , _2 , xhr ) {
+                               xhr.success(function() {
+                                       xhr.success(function() {
+                                               testString += "E";
+                                       });
+                                       testString += "D";
+                               });
+                               testString += "A";
+                       },
+                       complete: function() {
+                               strictEqual(testString, "ABCDE", "Proper order");
+                               start();
+                       }
+               }).success(function() {
+                       testString += "B";
+               }).success(function() {
+                       testString += "C";
+               });
+       }, 13);
+});
+
 test("jQuery.ajax() - error callbacks", function() {
        expect( 8 );
        stop();
@@ -68,9 +278,45 @@ test("jQuery.ajax() - error callbacks", function() {
        });
 });
 
+test(".ajax() - headers" , function() {
+
+       expect( 2 );
+       
+       stop();
+       
+       var requestHeaders = {
+               siMPle: "value",
+               "SometHing-elsE": "other value",
+               OthEr: "something else"
+               },
+               list = [],
+               i;
+               
+       for( i in requestHeaders ) {
+               list.push( i );
+       }
+       
+       jQuery.ajax(url("data/headers.php?keys="+list.join( "_" ) ), {
+               headers: requestHeaders,
+               success: function( data , _ , xhr ) {
+                       var tmp = [];
+                       for ( i in requestHeaders ) {
+                               tmp.push( i , ": " , requestHeaders[ i ] , "\n" );
+                       }
+                       tmp = tmp.join( "" );
+                       
+                       equals( data , tmp , "Headers were sent" );
+                       equals( xhr.getResponseHeader( "Sample-Header" ) , "Hello World" , "Sample header received" );
+                       start();
+               },
+               error: function(){ ok(false, "error"); }
+       });
+               
+});
+
 test(".ajax() - hash", function() {
        expect(3);
-
+       
        jQuery.ajax({
                url: "data/name.html#foo",
                beforeSend: function( xhr, settings ) {
@@ -78,15 +324,15 @@ test(".ajax() - hash", function() {
                        return false;
                }
        });
-
+       
        jQuery.ajax({
                url: "data/name.html?abc#foo",
                beforeSend: function( xhr, settings ) {
-                       equals(settings.url, "data/name.html?abc", "Make sure that the URL is trimmed.");
+               equals(settings.url, "data/name.html?abc", "Make sure that the URL is trimmed.");
                        return false;
                }
        });
-
+       
        jQuery.ajax({
                url: "data/name.html?abc#foo",
                data: { "test": 123 },
@@ -100,7 +346,7 @@ test(".ajax() - hash", function() {
 test(".ajax() - 304", function() {
        expect( 1 );
        stop();
-
+       
        jQuery.ajax({
                url: url("data/notmodified.php"),
                success: function(){ ok(true, "304 ok"); },
@@ -163,6 +409,136 @@ test("jQuery.ajax() - abort", function() {
        equals( xhr.readyState, 0, "XHR readyState indicates successful abortion" );
 });
 
+test("jQuery.ajax() - readyState (success)", function() {
+       expect( 1 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+       
+       var control = "";
+
+       setTimeout(function(){
+               jQuery.ajax({
+                       url: url("data/name.html"),
+                       beforeSend: function( xhr ) {
+                               xhr.onreadystatechange = function() {
+                                       control += xhr.readyState;
+                               }
+                       },
+                       complete: function(){ 
+                               setTimeout( function() {
+                                       equals( control , "1234" , "onreadystatechange was properly called" );
+                               }, 13 );
+                               start();
+                       }
+               });
+       }, 13);
+});
+
+test("jQuery.ajax() - readyState (abort)", function() {
+       expect( 2 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+       
+       var control = "";
+
+       setTimeout(function(){
+
+               jQuery.ajaxSetup({ timeout: 500 });
+
+               jQuery.ajax({
+                       url: url("data/name.php?wait=5"),
+                       beforeSend: function( xhr ) {
+                               xhr.onreadystatechange = function() {
+                                       control += xhr.readyState;
+                               }
+                       },
+                       complete: function( xhr ){ 
+                               setTimeout( function() {
+                                       equals( control , "14" , "onreadystatechange was properly called" );
+                                       equals( xhr.readyState, 0 , "readyState is 0" );
+                               }, 13 );
+                               start();
+                       }
+               });
+       }, 13);
+});
+
+test("jQuery.xhr() - reuse", function() {
+       expect( 15 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+       
+       var number = 0;
+       
+       setTimeout(function(){
+               jQuery('#foo').ajaxStart(function(){
+                       ok( true, "ajaxStart" );
+               }).ajaxStop(function(){
+                       ok( true, "ajaxStop" );
+                       start();
+               }).ajaxSend(function(){
+                       number++;
+                       ok( true, "ajaxSend (" + number +")" );
+               }).ajaxComplete(function(){
+                       ok( true, "ajaxComplete (" + number +")" );
+               }).ajaxError(function(){
+                       ok( false, "ajaxError (" + number +")" );
+               }).ajaxSuccess(function(){
+                       ok( true, "ajaxSuccess (" + number +")" );
+               });
+
+               jQuery.ajax({
+                       url: url("data/name.html"),
+                       beforeSend: function(){ ok(true, "beforeSend (1)"); },
+                       success: function( _1 , _2 , xhr ){
+                               ok(true, "success (1)");
+                               xhr.complete(function() {
+                                       ok(true, "complete (1bis)"); 
+                               });
+                               xhr.open( "GET", url("data/name.html") );
+                               xhr.success( function(){ ok(true, "beforeSend (2)"); } )
+                               xhr.send( null, {
+                                       success: function(){ ok(true, "success (2)"); },
+                                       error: function(){ ok(false, "error (2)"); },
+                                       complete: function(){ ok(true, "complete (2)"); }
+                               } );
+                       },
+                       error: function(){ ok(false, "error (1)"); },
+                       complete: function(){ ok(true, "complete (1)"); }
+               });
+       }, 13);
+});
+
+test("jQuery.xhr() - early binding", function() {
+       expect( 2 );
+
+       jQuery.ajaxSetup({ timeout: 0 });
+
+       stop();
+       
+       jQuery.xhr()
+               .success( function(){ ok(true, "success"); } )
+               .error( function(){ ok(false, "error"); } )
+               .complete( function(){ ok(true, "complete"); start(); } )
+               .open( "GET", url("data/name.html") )
+               .send();
+});
+
+test("jQuery.xhr() - get native implementation", function() {
+       
+       var xhr = jQuery.xhr(true);
+       
+       ok( xhr.readyState !== undefined , "implements XMLHttpRequest" );
+       ok( ! jQuery.isFunction( xhr.success ) , "is not jQuery's abstraction" );
+       
+});
+
 test("Ajax events with context", function() {
        expect(14);
        
@@ -277,6 +653,34 @@ test("jQuery.ajax() - disabled globals", function() {
        });
 });
 
+test("jQuery.xhr() - disabled globals through xhr.send(data , false)", function() {
+       expect( 2 );
+       stop();
+
+       jQuery('#foo').ajaxStart(function(){
+               ok( false, "ajaxStart" );
+       }).ajaxStop(function(){
+               ok( false, "ajaxStop" );
+       }).ajaxSend(function(){
+               ok( false, "ajaxSend" );
+       }).ajaxComplete(function(){
+               ok( false, "ajaxComplete" );
+       }).ajaxError(function(){
+               ok( false, "ajaxError" );
+       }).ajaxSuccess(function(){
+               ok( false, "ajaxSuccess" );
+       });
+
+       jQuery.xhr()
+               .success(function(){ ok(true, "success"); })
+               .error(function(){ ok(false, "error"); })
+               .complete(function(){
+                 ok(true, "complete");
+                 setTimeout(function(){ start(); }, 13);
+               })
+               .open("GET", url("data/name.html")).send(undefined, false);
+});
+
 test("jQuery.ajax - xml: non-namespace elements inside namespaced elements", function() {
        expect(3);
        stop();
@@ -292,6 +696,21 @@ test("jQuery.ajax - xml: non-namespace elements inside namespaced elements", fun
        });
 });
 
+test("jQuery.ajax - xml: non-namespace elements inside namespaced elements (over JSONP)", function() {
+       expect(3);
+       stop();
+       jQuery.ajax({
+         url: url("data/with_fries_over_jsonp.php"),
+         dataType: "jsonp xml",
+         success: function(resp) {
+               equals( jQuery("properties", resp).length, 1, 'properties in responseXML' );
+               equals( jQuery("jsconf", resp).length, 1, 'jsconf in responseXML' );
+               equals( jQuery("thing", resp).length, 2, 'things in responseXML' );
+               start();
+         }
+       });
+});
+
 test("jQuery.ajax - HEAD requests", function() {
        expect(2);
 
@@ -315,7 +734,7 @@ test("jQuery.ajax - HEAD requests", function() {
                        });
                }
        });
-       
+
 });
 
 test("jQuery.ajax - beforeSend", function() {
@@ -359,6 +778,27 @@ test("jQuery.ajax - beforeSend, cancel request (#2688)", function() {
        ok( request === false, "canceled request must return false instead of XMLHttpRequest instance" );
 });
 
+test("jQuery.ajax - beforeSend, cancel request manually", function() {
+       expect(2);
+       var request = jQuery.ajax({
+               url: url("data/name.html"),
+               beforeSend: function(xhr) {
+                       ok( true, "beforeSend got called, canceling" );
+                       xhr.abort();
+               },
+               success: function() {
+                       ok( false, "request didn't get canceled" );
+               },
+               complete: function() {
+                       ok( false, "request didn't get canceled" );
+               },
+               error: function() {
+                       ok( false, "request didn't get canceled" );
+               }
+       });
+       ok( request === false, "canceled request must return false instead of XMLHttpRequest instance" );
+});
+
 window.foobar = null;
 window.testFoo = undefined;
 
@@ -456,7 +896,7 @@ test("jQuery.param()", function() {
        equals( jQuery.param({"foo": {"bar": []} }), "foo%5Bbar%5D=", "Empty array param" );
        equals( jQuery.param({"foo": {"bar": [], foo: 1} }), "foo%5Bbar%5D=&foo%5Bfoo%5D=1", "Empty array param" );
        equals( jQuery.param({"foo": {"bar": {}} }), "foo%5Bbar%5D=", "Empty object param" );
-       
+
        jQuery.ajaxSetup({ traditional: true });
        
        var params = {foo:"bar", baz:42, quux:"All your base are belong to us"};
@@ -710,10 +1150,10 @@ test("jQuery.getScript(String, Function) - no callback", function() {
 });
 
 test("jQuery.ajax() - JSONP, Local", function() {
-       expect(8);
+       expect(9);
 
        var count = 0;
-       function plus(){ if ( ++count == 8 ) start(); }
+       function plus(){ if ( ++count == 9 ) start(); }
 
        stop();
 
@@ -828,14 +1268,26 @@ test("jQuery.ajax() - JSONP, Local", function() {
                        plus();
                }
        });
+
+       //#7578
+       jQuery.ajax({
+               url: "data/jsonp.php",
+               dataType: "jsonp",
+               beforeSend: function(){
+                       strictEqual( this.cache, false, "cache must be false on JSON request" );
+                       plus();
+                       return false;
+               }
+       });
 });
 
-test("JSONP - Custom JSONP Callback", function() {
+test("jQuery.ajax() - JSONP - Custom JSONP Callback", function() {
        expect(1);
        stop();
 
        window.jsonpResults = function(data) {
                ok( data.data, "JSON results returned (GET, custom callback function)" );
+               window.jsonpResults = undefined;
                start();
        };
 
@@ -932,7 +1384,7 @@ test("jQuery.ajax() - script, Remote with POST", function() {
        expect(3);
 
        var base = window.location.href.replace(/[^\/]*$/, "");
-
+       
        stop();
 
        jQuery.ajax({
@@ -1022,6 +1474,30 @@ test("jQuery.ajax() - json by content-type", function() {
        });
 });
 
+test("jQuery.ajax() - json by content-type disabled with options", function() {
+       expect(6);
+
+       stop();
+
+       jQuery.ajax({
+               url: url("data/json.php"),
+               data: { header: "json", json: "array" },
+               autoDataType: {
+                       json: false
+               },
+               success: function( text ) {
+                       equals( typeof text , "string" , "json wasn't auto-determined" );
+                       var json = this.dataConverters["text => json"]( text );
+                       ok( json.length >= 2, "Check length");
+                       equals( json[0].name, 'John', 'Check JSON: first, name' );
+                       equals( json[0].age, 21, 'Check JSON: first, age' );
+                       equals( json[1].name, 'Peter', 'Check JSON: second, name' );
+                       equals( json[1].age, 25, 'Check JSON: second, age' );
+                       start();
+               }
+       });
+});
+
 test("jQuery.getJSON(String, Hash, Function) - JSON array", function() {
        expect(5);
        stop();
@@ -1279,7 +1755,7 @@ test("jQuery.ajax - If-Modified-Since support", function() {
                                                ok(data == null, "response body should be empty")
                                        }
                                        start();
-                               },
+                       },
                                error: function() {
                                        // Do this because opera simply refuses to implement 304 handling :(
                                        // A feature-driven way of detecting this would be appreciated
@@ -1287,10 +1763,11 @@ test("jQuery.ajax - If-Modified-Since support", function() {
                                        ok(jQuery.browser.opera, "error");
                                        ok(jQuery.browser.opera, "error");
                                        start();
-                               }
+                       }
                        });
                },
                error: function() {
+                       equals(false, "error");
                        // Do this because opera simply refuses to implement 304 handling :(
                        // A feature-driven way of detecting this would be appreciated
                        // See: http://gist.github.com/599419
@@ -1325,8 +1802,8 @@ test("jQuery.ajax - Etag support", function() {
                                                ok(data == null, "response body should be empty")
                                        }
                                        start();
-                               },
-                               error: function() {
+                       },
+                       error: function() {
                                        // Do this because opera simply refuses to implement 304 handling :(
                                        // A feature-driven way of detecting this would be appreciated
                                        // See: http://gist.github.com/599419
@@ -1346,11 +1823,58 @@ test("jQuery.ajax - Etag support", function() {
        });
 });
 
+test("jQuery ajax - failing cross-domain", function() {
+
+       expect( 2 );
+       
+       stop();
+       
+       var i = 2;
+       
+       jQuery.ajax({
+               url: 'http://somewebsitethatdoesnotexist.com',
+               success: function(){ ok( false , "success" ); },
+               error: function(xhr,_,e){ ok( true , "file not found: " + xhr.status + " => " + e ); },
+               complete: function() { if ( ! --i ) start(); }
+       });
+       
+       jQuery.ajax({
+               url: 'http://www.google.com',
+               success: function(){ ok( false , "success" ); },
+               error: function(xhr,_,e){ ok( true , "access denied: " + xhr.status + " => " + e ); },
+               complete: function() { if ( ! --i ) start(); }
+       });
+       
+});
+
+test("jQuery ajax - atom+xml", function() {
+
+       stop();
+       
+       jQuery.ajax({
+               url: url( 'data/atom+xml.php' ),
+               success: function(){ ok( true , "success" ); },
+               error: function(){ ok( false , "error" ); },
+               complete: function() { start(); }
+       });
+       
+});
+
 test("jQuery.ajax - active counter", function() {
     ok( jQuery.active == 0, "ajax active counter should be zero: " + jQuery.active );
 });
 
+test( "jQuery.ajax - Location object as url (#7531)", 1, function () {
+       var success = false;
+       try {
+               var xhr = jQuery.ajax({ url: window.location });
+               success = true;
+               xhr.abort();
+       } catch (e) {}
+
+       ok( success, "document.location did not generate exception" );
+});
 
 }
 
-//}
+//}
\ No newline at end of file
index 2d0a0d6..f9506b3 100644 (file)
@@ -3,8 +3,33 @@ module("attributes");
 var bareObj = function(value) { return value; };
 var functionReturningObj = function(value) { return (function() { return value; }); };
 
+test("jQuery.props: itegrity test", function() {
+  
+  expect(1);
+  
+  //  This must be maintained and equal jQuery.props
+  //  Ensure that accidental or erroneous property 
+  //  overwrites don't occur
+  //  This is simply for better code coverage and future proofing. 
+  var propsShouldBe = {
+    "for": "htmlFor",
+    "class": "className",
+    readonly: "readOnly",
+    maxlength: "maxLength",
+    cellspacing: "cellSpacing",
+    rowspan: "rowSpan",
+    colspan: "colSpan",
+    tabindex: "tabIndex",
+    usemap: "useMap",
+    frameborder: "frameBorder"
+  };
+  
+  same(propsShouldBe, jQuery.props, "jQuery.props passes integrity check");
+
+});
+
 test("attr(String)", function() {
-       expect(31);
+       expect(37);
 
        // This one sometimes fails randomly ?!
        equals( jQuery('#text1').attr('value'), "Test", 'Check for value attribute' );
@@ -67,6 +92,14 @@ test("attr(String)", function() {
        ok( jQuery().attr("doesntexist") === undefined, "Make sure undefined is returned when no element is there." );
 
        equals( jQuery(document).attr("nodeName"), "#document", "attr works correctly on document nodes (bug #7451)." );
+
+       var attributeNode = document.createAttribute("irrelevant"),
+               commentNode = document.createComment("some comment"),
+               textNode = document.createTextNode("some text"),
+               obj = {};
+       jQuery.each( [document, attributeNode, commentNode, textNode, obj, "#firstp"], function( i, ele ) {
+               strictEqual( jQuery(ele).attr("nonexisting"), undefined, "attr works correctly for non existing attributes (bug #7500)." );
+       });
 });
 
 if ( !isLocal ) {
@@ -100,7 +133,7 @@ test("attr(Hash)", function() {
 });
 
 test("attr(String, Object)", function() {
-       expect(24);
+       expect(30);
 
        var div = jQuery("div").attr("foo", "bar"),
                fail = false;
@@ -134,6 +167,25 @@ test("attr(String, Object)", function() {
        jQuery("#name").attr('maxLength', '10');
        equals( document.getElementById('name').maxLength, '10', 'Set maxlength attribute' );
 
+       var attributeNode = document.createAttribute("irrelevant"),
+               commentNode = document.createComment("some comment"),
+               textNode = document.createTextNode("some text"),
+               obj = {};
+       jQuery.each( [document, obj, "#firstp"], function( i, ele ) {
+               var $ele = jQuery( ele );
+               $ele.attr( "nonexisting", "foo" );
+               equal( $ele.attr("nonexisting"), "foo", "attr(name, value) works correctly for non existing attributes (bug #7500)." );
+       });
+       jQuery.each( [commentNode, textNode, attributeNode], function( i, ele ) {
+               var $ele = jQuery( ele );
+               $ele.attr( "nonexisting", "foo" );
+               strictEqual( $ele.attr("nonexisting"), undefined, "attr(name, value) works correctly on comment and text nodes (bug #7500)." );
+       });
+       //cleanup
+       jQuery.each( [document, "#firstp"], function( i, ele ) {
+               jQuery( ele ).removeAttr("nonexisting");
+       });
+
        var table = jQuery('#table').append("<tr><td>cell</td></tr><tr><td>cell</td><td>cell</td></tr><tr><td>cell</td><td>cell</td></tr>"),
                td = table.find('td:first');
        td.attr("rowspan", "2");
@@ -304,8 +356,26 @@ test("attr('tabindex', value)", function() {
 });
 
 test("removeAttr(String)", function() {
-       expect(1);
+       expect(7);
        equals( jQuery('#mark').removeAttr( "class" )[0].className, "", "remove class" );
+
+       var attributeNode = document.createAttribute("irrelevant"),
+               commentNode = document.createComment("some comment"),
+               textNode = document.createTextNode("some text"),
+               obj = {};
+       //removeAttr only really removes on DOM element nodes handle all other seperatyl
+       strictEqual( jQuery( "#firstp" ).attr( "nonexisting", "foo" ).removeAttr( "nonexisting" )[0].nonexisting, undefined, "removeAttr works correctly on DOM element nodes" );
+
+       jQuery.each( [document, obj], function( i, ele ) {
+               var $ele = jQuery( ele );
+               $ele.attr( "nonexisting", "foo" ).removeAttr( "nonexisting" );
+               strictEqual( ele.nonexisting, "", "removeAttr works correctly on non DOM element nodes (bug #7500)." );
+       });
+       jQuery.each( [commentNode, textNode, attributeNode], function( i, ele ) {
+               $ele = jQuery( ele );
+               $ele.attr( "nonexisting", "foo" ).removeAttr( "nonexisting" );
+               strictEqual( ele.nonexisting, undefined, "removeAttr works correctly on non DOM element nodes (bug #7500)." );
+       });
 });
 
 test("val()", function() {
@@ -719,7 +789,7 @@ test("toggleClass(Fucntion[, boolean]) with incoming value", function() {
 });
 
 test("addClass, removeClass, hasClass", function() {
-       expect(14);
+       expect(17);
  
        var jq = jQuery("<p>Hi</p>"), x = jq[0];
  
@@ -739,12 +809,14 @@ test("addClass, removeClass, hasClass", function() {
        ok( jq.hasClass("hi"), "Check has1" );
        ok( jq.hasClass("bar"), "Check has2" );
  
-       var jq = jQuery("<p class='class1\nclass2\tcla.ss3\n'></p>");
-       ok( jq.hasClass("class1"), "Check hasClass with carriage return" );
-       ok( jq.is(".class1"), "Check is with carriage return" );
+       var jq = jQuery("<p class='class1\nclass2\tcla.ss3\n\rclass4'></p>");
+       ok( jq.hasClass("class1"), "Check hasClass with line feed" );
+       ok( jq.is(".class1"), "Check is with line feed" );
        ok( jq.hasClass("class2"), "Check hasClass with tab" );
        ok( jq.is(".class2"), "Check is with tab" );
        ok( jq.hasClass("cla.ss3"), "Check hasClass with dot" );
+       ok( jq.hasClass("class4"), "Check hasClass with carriage return" );
+       ok( jq.is(".class4"), "Check is with carriage return" );
  
        jq.removeClass("class2");
        ok( jq.hasClass("class2")==false, "Check the class has been properly removed" );
@@ -752,4 +824,6 @@ test("addClass, removeClass, hasClass", function() {
        ok( jq.hasClass("cla.ss3"), "Check the dotted class has not been removed" );
        jq.removeClass("cla.ss3");
        ok( jq.hasClass("cla.ss3")==false, "Check the dotted class has been removed" );
+       jq.removeClass("class4");
+       ok( jq.hasClass("class4")==false, "Check the class has been properly removed" );
 });
index 7ef2ad7..7057783 100644 (file)
@@ -547,15 +547,15 @@ test("toArray()", function() {
 })
 
 test("get(Number)", function() {
-       expect(1);
+       expect(2);
        equals( jQuery("p").get(0), document.getElementById("firstp"), "Get A Single Element" );
+       strictEqual( jQuery("#firstp").get(1), undefined, "Try get with index larger elements count" );
 });
 
 test("get(-Number)",function() {
-       expect(1);
-       equals( jQuery("p").get(-1),
-               document.getElementById("first"),
-               "Get a single element with negative index" )
+       expect(2);
+       equals( jQuery("p").get(-1), document.getElementById("first"), "Get a single element with negative index" );
+       strictEqual( jQuery("#firstp").get(-2), undefined, "Try get with index negative index larger then elements count" );
 })
 
 test("each(Function)", function() {
index 315043c..1a0f84c 100644 (file)
@@ -79,7 +79,7 @@ test("jQuery.data", function() {
 });
 
 test(".data()", function() {
-       expect(4);
+       expect(5);
 
        var div = jQuery("#foo");
        strictEqual( div.data("foo"), undefined, "Make sure that missing result is undefined" );
@@ -90,6 +90,9 @@ test(".data()", function() {
 
        var nodiv = jQuery("#unfound");
        equals( nodiv.data(), null, "data() on empty set returns null" );
+
+       var obj = { foo: "bar" };
+       equals( jQuery(obj).data(), obj, "Retrieve data object from a wrapped JS object (#7524)" );
 })
 
 test(".data(String) and .data(String, Object)", function() {
index 54431dd..a647e5f 100644 (file)
@@ -493,7 +493,7 @@ test("bind(name, false), unbind(name, false)", function() {
 });
 
 test("bind()/trigger()/unbind() on plain object", function() {
-       expect( 7 );
+       expect( 8 );
 
        var obj = {};
 
@@ -503,8 +503,13 @@ test("bind()/trigger()/unbind() on plain object", function() {
        // Make sure it doesn't complain when no events are found
        jQuery(obj).unbind("test");
 
-       jQuery(obj).bind("test", function(){
-               ok( true, "Custom event run." );
+       jQuery(obj).bind({
+               test: function() {
+                       ok( true, "Custom event run." );
+               },
+               submit: function() {
+                       ok( true, "Custom submit event run." );
+               }
        });
 
        var events = jQuery(obj).data("__events__");
@@ -516,8 +521,10 @@ test("bind()/trigger()/unbind() on plain object", function() {
 
        // Should trigger 1
        jQuery(obj).trigger("test");
+       jQuery(obj).trigger("submit");
 
        jQuery(obj).unbind("test");
+       jQuery(obj).unbind("submit");
 
        // Should trigger 0
        jQuery(obj).trigger("test");
index 71a501a..29c79a2 100644 (file)
@@ -848,7 +848,7 @@ test("replaceAll(String|Element|Array&lt;Element&gt;|jQuery)", function() {
 });
 
 test("clone()", function() {
-       expect(35);
+       expect(36);
        equals( 'This is a normal link: Yahoo', jQuery('#en').text(), 'Assert text for #en' );
        var clone = jQuery('#yahoo').clone();
        equals( 'Try them out:Yahoo', jQuery('#first').append(clone).text(), 'Check for clone' );
@@ -918,6 +918,8 @@ test("clone()", function() {
        form.appendChild( div );
 
        equals( jQuery(form).clone().children().length, 1, "Make sure we just get the form back." );
+
+       equal( jQuery("body").clone().children()[0].id, "qunit-header", "Make sure cloning body works" );
 });
 
 if (!isLocal) {