Added support for:

- Cross Domain getScript
  $.getScript("http://foo.com/script.js");
- JSONP
  $.ajax({ url: "script.js", type: "jsonp" });
  $.getJSON("script.js?callback=?");
- Cross Domain JSONP/getJSON
  $.getJSON("http://foo.com/script.js?callback=?");
- No-cache Ajax Requests
  $.ajax({ url: "test.html", cache: false });
This commit is contained in:
John Resig 2007-09-03 23:45:14 +00:00
parent 456f0fe598
commit a5dbcaf675
4 changed files with 424 additions and 165 deletions

10
build/test/data/jsonp.php Normal file
View file

@ -0,0 +1,10 @@
<?php
error_reporting(0);
$callback = $_REQUEST['callback'];
$json = $_REQUEST['json'];
if($json) {
echo $callback . '([ {"name": "John", "age": 21}, {"name": "Peter", "age": 25 } ])';
} else {
echo $callback . '({ "data": {"lang": "en", "length": 25} })';
}
?>

View file

@ -41,11 +41,11 @@
<form id="form" action="formaction"> <form id="form" action="formaction">
<input type="text" name="action" value="Test" id="text1"/> <input type="text" name="action" value="Test" id="text1"/>
<input type="text" name="text2" value="Test" id="text2" disabled="disabled"/> <input type="text" name="text2" value="Test" id="text2" disabled="disabled"/>
<input type="radio" name="radio1" id="radio1"/> <input type="radio" name="radio1" id="radio1" value="on"/>
<input type="radio" name="radio2" id="radio2" checked="checked"/> <input type="radio" name="radio2" id="radio2" checked="checked"/>
<input type="checkbox" name="check" id="check1" checked="checked"/> <input type="checkbox" name="check" id="check1" checked="checked"/>
<input type="checkbox" id="check2"/> <input type="checkbox" id="check2" value="on"/>
<input type="hidden" name="hidden" id="hidden1"/> <input type="hidden" name="hidden" id="hidden1"/>
<input type="text" style="display:none;" name="foo[bar]" id="hidden2"/> <input type="text" style="display:none;" name="foo[bar]" id="hidden2"/>

View file

@ -244,6 +244,8 @@ jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".sp
}; };
}); });
var jsc = (new Date).getTime();
jQuery.extend({ jQuery.extend({
/** /**
@ -599,30 +601,95 @@ jQuery.extend({
* @see ajaxSetup(Map) * @see ajaxSetup(Map)
*/ */
ajax: function( s ) { ajax: function( s ) {
var jsonp, jsre = /=(\?|%3F)/g, status, data;
// Extend the settings, but re-extend 's' so that it can be // Extend the settings, but re-extend 's' so that it can be
// checked again later (in the test suite, specifically) // checked again later (in the test suite, specifically)
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
// if data available
if ( s.data ) {
// convert data if not already a string // convert data if not already a string
if ( s.processData && typeof s.data != "string" ) if ( s.data && s.processData && typeof s.data != "string" )
s.data = jQuery.param(s.data); s.data = jQuery.param(s.data);
// append data to url for get requests // Break the data into one single string
if ( s.type.toLowerCase() == "get" ) { var q = s.url.indexOf("?");
// "?" + data or "&" + data (in case there are already params) if ( q > -1 ) {
s.url += (s.url.indexOf("?") > -1 ? "&" : "?") + s.data; s.data = (s.data ? s.data + "&" : "") + s.url.slice(q + 1);
s.url = s.url.slice(0, q);
}
// Handle JSONP Parameter Callbacks
if ( s.dataType == "jsonp" ) {
if ( !s.data || !s.data.match(jsre) )
s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
s.dataType = "json";
}
// Build temporary JSONP function
if ( s.dataType == "json" && s.data && s.data.match(jsre) ) {
jsonp = "jsonp" + jsc++;
s.data = s.data.replace(jsre, "=" + jsonp);
// We need to make sure
// that a JSONP style response is executed properly
s.dataType = "script";
// Handle JSONP-style loading
window[ jsonp ] = function(tmp){
data = tmp;
success();
// Garbage collect
window[ jsonp ] = undefined;
try{ delete window[ jsonp ]; } catch(e){}
};
}
if ( s.dataType == "script" && s.cache == null )
s.cache = false;
if ( s.cache === false && s.type.toLowerCase() == "get" )
s.data = (s.data ? s.data + "&" : "") + "_=" + (new Date()).getTime();
// If data is available, append data to url for get requests
if ( s.data && s.type.toLowerCase() == "get" ) {
s.url += "?" + s.data;
// IE likes to send both get and post data, prevent this // IE likes to send both get and post data, prevent this
s.data = null; s.data = null;
} }
}
// Watch for a new set of requests // Watch for a new set of requests
if ( s.global && ! jQuery.active++ ) if ( s.global && ! jQuery.active++ )
jQuery.event.trigger( "ajaxStart" ); jQuery.event.trigger( "ajaxStart" );
// If we're requesting a remote document
// and trying to load JSON or Script
if ( !s.url.indexOf("http") && s.dataType == "script" ) {
var script = document.createElement("script");
script.src = s.url;
// Handle Script loading
if ( !jsonp && (s.success || s.complete) ) {
var done = false;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function(){
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
done = true;
success();
complete();
document.body.removeChild( script );
}
};
}
document.body.appendChild(script);
// We handle everything using the script element injection
return;
}
var requestDone = false; var requestDone = false;
// Create the request object; Microsoft failed to properly // Create the request object; Microsoft failed to properly
@ -645,7 +712,7 @@ jQuery.extend({
xml.setRequestHeader("X-Requested-With", "XMLHttpRequest"); xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// Allow custom headers/mimetypes // Allow custom headers/mimetypes
if( s.beforeSend ) if ( s.beforeSend )
s.beforeSend(xml); s.beforeSend(xml);
if ( s.global ) if ( s.global )
@ -663,7 +730,7 @@ jQuery.extend({
ival = null; ival = null;
} }
var status = isTimeout == "timeout" && "timeout" || status = isTimeout == "timeout" && "timeout" ||
!jQuery.httpSuccess( xml ) && "error" || !jQuery.httpSuccess( xml ) && "error" ||
s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" || s.ifModified && jQuery.httpNotModified( xml, s.url ) && "notmodified" ||
"success"; "success";
@ -672,7 +739,7 @@ jQuery.extend({
// Watch for, and catch, XML document parse errors // Watch for, and catch, XML document parse errors
try { try {
// process the data (runs the xml through httpData regardless of callback) // process the data (runs the xml through httpData regardless of callback)
var data = jQuery.httpData( xml, s.dataType ); data = jQuery.httpData( xml, s.dataType );
} catch(e) { } catch(e) {
status = "parsererror"; status = "parsererror";
} }
@ -689,30 +756,17 @@ jQuery.extend({
if ( s.ifModified && modRes ) if ( s.ifModified && modRes )
jQuery.lastModified[s.url] = modRes; jQuery.lastModified[s.url] = modRes;
// If a local callback was specified, fire it and pass it the data // JSONP handles its own success callback
if ( s.success ) if ( !jsonp )
s.success( data, status ); success();
// Fire the global callback
if ( s.global )
jQuery.event.trigger( "ajaxSuccess", [xml, s] );
} else } else
jQuery.handleError(s, xml, status); jQuery.handleError(s, xml, status);
// The request was completed // Fire the complete handlers
if( s.global ) complete();
jQuery.event.trigger( "ajaxComplete", [xml, s] );
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active )
jQuery.event.trigger( "ajaxStop" );
// Process result
if ( s.complete )
s.complete(xml, status);
// Stop memory leaks // Stop memory leaks
if(s.async) if ( s.async )
xml = null; xml = null;
} }
}; };
@ -748,6 +802,30 @@ jQuery.extend({
// return XMLHttpRequest to allow aborting the request etc. // return XMLHttpRequest to allow aborting the request etc.
return xml; return xml;
function success(){
// If a local callback was specified, fire it and pass it the data
if ( s.success )
s.success( data, status );
// Fire the global callback
if ( s.global )
jQuery.event.trigger( "ajaxSuccess", [xml, s] );
}
function complete(){
// Process result
if ( s.complete )
s.complete(xml, status);
// The request was completed
if ( s.global )
jQuery.event.trigger( "ajaxComplete", [xml, s] );
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active )
jQuery.event.trigger( "ajaxStop" );
}
}, },
handleError: function( s, xml, status, e ) { handleError: function( s, xml, status, e ) {
@ -793,7 +871,7 @@ jQuery.extend({
httpData: function( r, type ) { httpData: function( r, type ) {
var ct = r.getResponseHeader("content-type"); var ct = r.getResponseHeader("content-type");
var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0; var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0;
data = xml ? r.responseXML : r.responseText; var data = xml ? r.responseXML : r.responseText;
if ( xml && data.documentElement.tagName == "parsererror" ) if ( xml && data.documentElement.tagName == "parsererror" )
throw "parsererror"; throw "parsererror";

View file

@ -3,7 +3,164 @@ module("ajax");
// Safari 3 randomly crashes when running these tests, // Safari 3 randomly crashes when running these tests,
// but only in the full suite - you can run just the Ajax // but only in the full suite - you can run just the Ajax
// tests and they'll pass // tests and they'll pass
if ( !jQuery.browser.safari ) { //if ( !jQuery.browser.safari ) {
test("$.ajax() - success callbacks", function() {
expect( 8 );
$.ajaxSetup({ timeout: 0 });
stop();
setTimeout(function(){
$('#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" );
});
$.ajax({
url: 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);
});
if ( !isLocal ) {
test("$.ajax() - error callbacks", function() {
expect( 8 );
stop();
$('#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( true, "ajaxError" );
}).ajaxSuccess(function(){
ok( false, "ajaxSuccess" );
});
$.ajaxSetup({ timeout: 500 });
$.ajax({
url: url("data/name.php?wait=5"),
beforeSend: function(){ ok(true, "beforeSend"); },
success: function(){ ok(false, "success"); },
error: function(){ ok(true, "error"); },
complete: function(){ ok(true, "complete"); }
});
});
}
test("$.ajax() - disabled globals", function() {
expect( 3 );
stop();
$('#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" );
});
$.ajax({
global: false,
url: url("data/name.html"),
beforeSend: function(){ ok(true, "beforeSend"); },
success: function(){ ok(true, "success"); },
error: function(){ ok(false, "error"); },
complete: function(){
ok(true, "complete");
setTimeout(function(){ start(); }, 13);
}
});
});
test("$.ajax - xml: non-namespace elements inside namespaced elements", function() {
expect(3);
stop();
$.ajax({
url: url("data/with_fries.xml"),
dataType: "xml",
success: function(resp) {
equals( $("properties", resp).length, 1, 'properties in responseXML' );
equals( $("jsconf", resp).length, 1, 'jsconf in responseXML' );
equals( $("thing", resp).length, 2, 'things in responseXML' );
start();
}
});
});
test("$.ajax - beforeSend", function() {
expect(1);
stop();
var check = false;
$.ajaxSetup({ timeout: 0 });
$.ajax({
url: url("data/name.html"),
beforeSend: function(xml) {
check = true;
},
success: function(data) {
ok( check, "check beforeSend was executed" );
start();
}
});
});
var foobar;
test("$.ajax - dataType html", function() {
expect(5);
stop();
foobar = null;
testFoo = undefined;
var verifyEvaluation = function() {
ok( testFoo == "foo", 'Check if script was evaluated for datatype html' );
ok( foobar == "bar", 'Check if script src was evaluated for datatype html' );
start();
};
$.ajax({
dataType: "html",
url: url("data/test.html"),
success: function(data) {
$("#ap").html(data);
ok( data.match(/^html text/), 'Check content for datatype html' );
setTimeout(verifyEvaluation, 600);
}
});
});
test("serialize()", function() { test("serialize()", function() {
expect(1); expect(1);
@ -179,139 +336,153 @@ test("$.getScript(String, Function) - no callback", function() {
$.getScript(url("data/test.js"), start); $.getScript(url("data/test.js"), start);
}); });
test("$.ajax - xml: non-namespace elements inside namespaced elements", function() {
expect(3);
stop();
$.ajax({
url: url("data/with_fries.xml"),
dataType: "xml",
success: function(resp) {
equals( $("properties", resp).length, 1, 'properties in responseXML' );
equals( $("jsconf", resp).length, 1, 'jsconf in responseXML' );
equals( $("thing", resp).length, 2, 'things in responseXML' );
start();
}
});
});
test("test global handlers - success", function() {
expect( isLocal ? 4 : 8 );
stop();
var counter = { complete: 0, success: 0, error: 0, send: 0 },
success = function() { counter.success++ },
error = function() { counter.error++ },
complete = function() { counter.complete++ },
send = function() { counter.send++ };
$('#foo').ajaxStart(complete).ajaxStop(complete).ajaxSend(send).ajaxComplete(complete).ajaxError(error).ajaxSuccess(success);
// start with successful test
$.ajax({url: url("data/name.html"), beforeSend: send, success: success, error: error, complete: function() {
equals( counter.error, 0, 'Check succesful request, error callback' );
equals( counter.success, 2, 'Check succesful request, success callback' );
equals( counter.complete, 3, 'Check succesful request, complete callback' );
equals( counter.send, 2, 'Check succesful request, send callback' );
if ( !isLocal ) {
counter.error = counter.success = counter.complete = counter.send = 0;
$.ajaxTimeout(500);
$.ajax({url: url("data/name.php?wait=5"), beforeSend: send, success: success, error: error, complete: function() {
equals( counter.error, 2, 'Check failed request, error callback' );
equals( counter.success, 0, 'Check failed request, success callback' );
equals( counter.complete, 3, 'Check failed request, failed callback' );
equals( counter.send, 2, 'Check failed request, send callback' );
start();
}});
} else
start();
}});
});
test("test global handlers - failure", function() {
expect( isLocal ? 4 : 8 );
stop();
var counter = { complete: 0, success: 0, error: 0, send: 0 },
success = function() { counter.success++ },
error = function() { counter.error++ },
complete = function() { counter.complete++ },
send = function() { counter.send++ };
$.ajaxTimeout(0);
$('#foo').ajaxStart(complete).ajaxStop(complete).ajaxSend(send).ajaxComplete(complete).ajaxError(error).ajaxSuccess(success);
$.ajax({url: url("data/name.php"), global: false, beforeSend: send, success: success, error: error, complete: function() {
ok( counter.error == 0, 'Check sucesful request without globals' );
ok( counter.success == 1, 'Check sucesful request without globals' );
ok( counter.complete == 0, 'Check sucesful request without globals' );
ok( counter.send == 1, 'Check sucesful request without globals' );
if ( !isLocal ) {
counter.error = counter.success = counter.complete = counter.send = 0;
$.ajaxTimeout(500);
$.ajax({url: url("data/name.php?wait=5"), global: false, beforeSend: send, success: success, error: error, complete: function() {
var x = counter;
ok( counter.error == 1, 'Check failed request without globals' );
ok( counter.success == 0, 'Check failed request without globals' );
ok( counter.complete == 0, 'Check failed request without globals' );
ok( counter.send == 1, 'Check failed request without globals' );
start();
}});
} else
start();
}});
});
test("$.ajax - beforeSend", function() {
expect(1);
stop();
var check = false;
$.ajaxSetup({ timeout: 0 });
$.ajax({
url: url("data/name.html"),
beforeSend: function(xml) {
check = true;
},
success: function(data) {
ok( check, "check beforeSend was executed" );
start();
}
});
});
test("$.ajax - dataType html", function() {
expect(5);
stop();
foobar = null;
testFoo = undefined;
var verifyEvaluation = function() {
ok( testFoo == "foo", 'Check if script was evaluated for datatype html' );
ok( foobar == "bar", 'Check if script src was evaluated for datatype html' );
start();
};
$.ajax({
dataType: "html",
url: url("data/test.html"),
success: function(data) {
$("#ap").html(data);
ok( data.match(/^html text/), 'Check content for datatype html' );
setTimeout(verifyEvaluation, 600);
}
});
});
if ( !isLocal ) { if ( !isLocal ) {
test("$.ajax() - JSONP, Local", function() {
expect(7);
var count = 0;
function plus(){ if ( ++count == 7 ) start(); }
stop();
$.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, no callback)" );
plus();
}
});
$.ajax({
url: "data/jsonp.php?callback=?",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, url callback)" );
plus();
}
});
$.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
data: "callback=?",
success: function(data){
ok( data.data, "JSON results returned (GET, data callback)" );
plus();
}
});
$.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
data: { callback: "?" },
success: function(data){
ok( data.data, "JSON results returned (GET, data obj callback)" );
plus();
}
});
$.ajax({
type: "POST",
url: "data/jsonp.php",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (POST, no callback)" );
plus();
}
});
$.ajax({
type: "POST",
url: "data/jsonp.php",
data: "callback=?",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (POST, data callback)" );
plus();
}
});
$.ajax({
type: "POST",
url: "data/jsonp.php",
data: { callback: "?" },
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (POST, data obj callback)" );
plus();
}
});
});
test("$.ajax() - JSONP, Remote", function() {
expect(4);
var count = 0;
function plus(){ if ( ++count == 4 ) start(); }
var base = window.location.href.replace(/\?.*$/, "");
stop();
$.ajax({
url: base + "data/jsonp.php",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, no callback)" );
plus();
}
});
$.ajax({
url: base + "data/jsonp.php?callback=?",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, url callback)" );
plus();
}
});
$.ajax({
url: base + "data/jsonp.php",
dataType: "jsonp",
data: "callback=?",
success: function(data){
ok( data.data, "JSON results returned (GET, data callback)" );
plus();
}
});
$.ajax({
url: base + "data/jsonp.php",
dataType: "jsonp",
data: { callback: "?" },
success: function(data){
ok( data.data, "JSON results returned (GET, data obj callback)" );
plus();
}
});
});
test("$.ajax() - script, Remote", function() {
expect(2);
var base = window.location.href.replace(/\?.*$/, "");
stop();
$.ajax({
url: base + "data/test.js",
dataType: "script",
success: function(data){
ok( foobar, "Script results returned (GET, no callback)" );
start();
}
});
});
test("$.getJSON(String, Hash, Function) - JSON array", function() { test("$.getJSON(String, Hash, Function) - JSON array", function() {
expect(4); expect(4);
stop(); stop();
@ -454,4 +625,4 @@ test("custom timeout does not set error message when timeout occurs, see #970",
} }
} //}