Rewrite of the Ajax module by Julian Aubourg. Some (dated) details can be found here: http://oksoclap.com/6Y26bm1ZsB more details are forthcoming. Fixes #7195.
This commit is contained in:
parent
29616e60c8
commit
ab3ba4a812
12 changed files with 1997 additions and 469 deletions
4
Makefile
4
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
|
||||
|
|
547
src/ajax.js
547
src/ajax.js
|
@ -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;
|
||||
|
@ -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,342 +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: "*/*"
|
||||
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");
|
||||
}
|
||||
},
|
||||
|
||||
ajax: function( origSettings ) {
|
||||
// IE8 leaks a lot when we've set abort, and IE6-8 a little
|
||||
// when we have set onreadystatechange. Bug #6242
|
||||
// XXX IE7 still leaks when abort is called, no matter what
|
||||
// we do
|
||||
function cleanup() {
|
||||
// IE6 will throw an error setting xhr.abort
|
||||
try {
|
||||
xhr.abort = xhr.onreadystatechange = jQuery.noop;
|
||||
} catch(e) {}
|
||||
// Check if xml has been properly parsed
|
||||
"xml": function(data) {
|
||||
var documentElement = data ? data.documentElement : data;
|
||||
if ( ! documentElement || ! documentElement.nodeName ) {
|
||||
jQuery.error("typeerror");
|
||||
}
|
||||
|
||||
var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings),
|
||||
jsonp, status, data, type = s.type.toUpperCase(), noContent = rnoContent.test(type);
|
||||
|
||||
// toString fixes people passing a window.location or
|
||||
// document.location to $.ajax, which worked in 1.4.2 and
|
||||
// earlier (bug #7531). It should be removed in 1.5.
|
||||
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 );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if ( s.dataType === "script" && s.cache === undefined ) {
|
||||
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 );
|
||||
if ( documentElement.nodeName == "parsererror" ) {
|
||||
jQuery.error("parsererror");
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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]);
|
||||
}
|
||||
|
||||
if ( jQuery.etag[s.url] ) {
|
||||
xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
|
||||
return xml;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 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");
|
||||
// Main method
|
||||
ajax: function( url , s ) {
|
||||
|
||||
if ( arguments.length === 1 ) {
|
||||
s = url;
|
||||
url = s ? s.url : undefined;
|
||||
}
|
||||
|
||||
// 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 ) {}
|
||||
return jQuery.xhr().open( s ? s.type : undefined , url ).send( undefined , s );
|
||||
|
||||
// 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 ) {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
};
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// return XMLHttpRequest to allow aborting the request etc.
|
||||
return xhr;
|
||||
},
|
||||
|
||||
// Serialize an array of form elements or a set of
|
||||
|
@ -595,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: {}
|
||||
|
||||
});
|
||||
|
||||
|
@ -714,16 +384,21 @@ if ( window.ActiveXObject ) {
|
|||
if ( window.location.protocol !== "file:" ) {
|
||||
try {
|
||||
return new window.XMLHttpRequest();
|
||||
} catch(xhrError) {}
|
||||
} catch( xhrError ) {}
|
||||
}
|
||||
|
||||
try {
|
||||
return new window.ActiveXObject("Microsoft.XMLHTTP");
|
||||
} catch(activeError) {}
|
||||
} catch( activeError ) {}
|
||||
};
|
||||
}
|
||||
|
||||
// Does this browser support XHR requests?
|
||||
jQuery.support.ajax = !!jQuery.ajaxSettings.xhr();
|
||||
var testXHR = jQuery.ajaxSettings.xhr();
|
||||
|
||||
})( jQuery );
|
||||
// Does this browser support XHR requests?
|
||||
jQuery.support.ajax = !!testXHR;
|
||||
|
||||
// Does this browser support crossDomain XHR requests
|
||||
jQuery.support.cors = testXHR && "withCredentials" in testXHR;
|
||||
|
||||
})(jQuery);
|
89
src/transports/jsonp.js
Normal file
89
src/transports/jsonp.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
(function( jQuery ) {
|
||||
|
||||
var jsc = jQuery.now(),
|
||||
jsre = /\=\?(&|$)/,
|
||||
rquery = /\?/;
|
||||
|
||||
// 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.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);
|
83
src/transports/script.js
Normal file
83
src/transports/script.js
Normal file
|
@ -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);
|
191
src/transports/xhr.js
Normal file
191
src/transports/xhr.js
Normal file
|
@ -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);
|
941
src/xhr.js
Normal file
941
src/xhr.js
Normal file
|
@ -0,0 +1,941 @@
|
|||
(function( jQuery , undefined ) {
|
||||
|
||||
var rquery = /\?/,
|
||||
rhash = /#.*$/,
|
||||
rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
|
||||
rnoContent = /^(?:GET|HEAD)$/,
|
||||
rts = /([?&])_=[^&]*/,
|
||||
rurl = /^(\w+:)?\/\/([^\/?#]+)/,
|
||||
|
||||
slice = 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.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.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
|
||||
if ( headers ) {
|
||||
xhr.setRequestHeaders( headers );
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
// Ditto with an s
|
||||
setRequestHeaders: function(map) {
|
||||
checkState(1, !sendFlag);
|
||||
for ( var name in map ) {
|
||||
requestHeaders[ name.toLowerCase() ] = map[name];
|
||||
}
|
||||
return xhr;
|
||||
},
|
||||
|
||||
// Utility method to get headers set
|
||||
getRequestHeader: function(name) {
|
||||
checkState(1, !sendFlag);
|
||||
return requestHeaders[ name.toLowerCase() ];
|
||||
},
|
||||
|
||||
// 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(["bind","unbind"], function(_, name) {
|
||||
xhr[name] = function(type) {
|
||||
|
||||
var functors = slice.call(arguments,1),
|
||||
list;
|
||||
|
||||
jQuery.each(type.split(/\s+/g), function() {
|
||||
list = callbacksLists[this];
|
||||
if ( list ) {
|
||||
list[name].apply(list, functors );
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
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 = slice.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);
|
4
test/data/atom+xml.php
Normal file
4
test/data/atom+xml.php
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?php header("Content-type: atom+xml") ?>
|
||||
<root>
|
||||
<element />
|
||||
</root>
|
15
test/data/css.php
Normal file
15
test/data/css.php
Normal file
|
@ -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
|
||||
}
|
||||
?>
|
5
test/data/headers.php
Normal file
5
test/data/headers.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
header( "Multiple-Line: Hello\n World" );
|
||||
header( "Multiple-Multiple-Line: Hello\n Beautiful\n\t World" );
|
||||
header( "Single-Line: Hello World" );
|
7
test/data/with_fries_over_jsonp.php
Normal file
7
test/data/with_fries_over_jsonp.php
Normal file
|
@ -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)";
|
||||
?>
|
|
@ -20,6 +20,10 @@
|
|||
<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>
|
||||
|
|
|
@ -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,6 +278,32 @@ test("jQuery.ajax() - error callbacks", function() {
|
|||
});
|
||||
});
|
||||
|
||||
test(".ajax() - headers" , function() {
|
||||
|
||||
// No multiple line headers in IE
|
||||
expect( jQuery.browser.msie ? 2 : 4 );
|
||||
|
||||
stop();
|
||||
|
||||
jQuery.ajax({
|
||||
url: url("data/headers.php"),
|
||||
success: function( _1 , _2 , xhr ){
|
||||
ok(true, "success");
|
||||
equals( xhr.getResponseHeader( "Single-Line" ) , "Hello World" , "Single line header" );
|
||||
// No multiple line headers in IE
|
||||
if ( ! jQuery.browser.msie ) {
|
||||
// Each browser has its own unique way to deal with spaces after line breaks
|
||||
// in multiple line headers, so we use regular expressions
|
||||
ok( /^Hello\s+World$/.test( xhr.getResponseHeader( "Multiple-Line" ) ) , "Multiple line" );
|
||||
ok( /^Hello\s+Beautiful\s+World$/.test( xhr.getResponseHeader( "Multiple-Multiple-Line" ) ) , "Multiple multiple line" );
|
||||
}
|
||||
start();
|
||||
},
|
||||
error: function(){ ok(false, "error"); }
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test(".ajax() - hash", function() {
|
||||
expect(3);
|
||||
|
||||
|
@ -163,6 +399,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 +643,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 +686,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);
|
||||
|
||||
|
@ -359,6 +768,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;
|
||||
|
||||
|
@ -841,12 +1271,13 @@ test("jQuery.ajax() - JSONP, Local", function() {
|
|||
});
|
||||
});
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
|
@ -1033,6 +1464,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();
|
||||
|
@ -1302,6 +1757,7 @@ test("jQuery.ajax - If-Modified-Since support", function() {
|
|||
});
|
||||
},
|
||||
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
|
||||
|
@ -1357,6 +1813,60 @@ test("jQuery.ajax - Etag support", function() {
|
|||
});
|
||||
});
|
||||
|
||||
test("jQuery ajax - headers", function() {
|
||||
|
||||
stop();
|
||||
|
||||
jQuery.ajax(url("data/css.php?wait=1&id=headers"), {
|
||||
headers: {
|
||||
testKey: "testValue"
|
||||
},
|
||||
beforeSend: function( xhr ) {
|
||||
equals( xhr.getRequestHeader("testKey") , "testValue" , "Headers properly set" );
|
||||
setTimeout( start , 13 );
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
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 );
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue