From 64e1cdbb95b8bbefbc9dec70ae30e0714a549619 Mon Sep 17 00:00:00 2001 From: jaubourg Date: Thu, 20 Jan 2011 04:12:15 +0100 Subject: [PATCH] Cleans up and simplifies code shared by ajaxPrefilter and ajaxTransport. Removes chainability of ajaxSetup, ajaxPrefilter and ajaxTransport. Also makes sure context is handled properly by ajaxSetup (unit test added). --- src/ajax.js | 201 ++++++++++++++++++++++++---------------------------- src/ajax/jsonp.js | 3 +- src/ajax/script.js | 6 +- test/unit/ajax.js | 41 +++++++++++ 4 files changed, 138 insertions(+), 113 deletions(-) diff --git a/src/ajax.js b/src/ajax.js index cbe5572..fc1ecfd 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -161,7 +161,9 @@ jQuery.extend({ ajaxSetup: function( settings ) { jQuery.extend( true, jQuery.ajaxSettings, settings ); - return this; + if ( settings.context ) { + jQuery.ajaxSettings.context = settings.context; + } }, ajaxSettings: { @@ -278,6 +280,14 @@ jQuery.extend({ }, + ajaxPrefilter: function( a , b ) { + ajaxPrefilterOrTransport( "prefilters" , a , b ); + }, + + ajaxTransport: function( a , b ) { + return ajaxPrefilterOrTransport( "transports" , a , b ); + }, + // Main method ajax: function( url , options ) { @@ -299,7 +309,11 @@ jQuery.extend({ jQuery_lastModified = jQuery.lastModified, jQuery_etag = jQuery.etag, // Callbacks contexts - callbackContext = options.context || s.context || s, + // We force the original context if it exists + // or take it from jQuery.ajaxSettings otherwise + // (plain objects used as context get extended) + callbackContext = + ( s.context = ( "context" in options ? options : jQuery.ajaxSettings ).context ) || s, globalEventContext = callbackContext === s ? jQuery.event : jQuery( callbackContext ), // Deferreds deferred = jQuery.Deferred(), @@ -373,10 +387,6 @@ jQuery.extend({ } }; - // We force the original context - // (plain objects used as context get extended) - s.context = options.context; - // Callback for when everything is done // It is defined here because jslint complains if it is declared // at the end of the function (which would be more logical and readable) @@ -850,126 +860,97 @@ jQuery.extend({ }); -//Execute or select from functions in a given structure of options -function ajax_selectOrExecute( structure , s , options ) { +// Base function for both ajaxPrefilter and ajaxTransport +function ajaxPrefilterOrTransport( arg0 , arg1 , arg2 ) { - var dataTypes = s.dataTypes, - transportDataType, - list, - selected, + var type = jQuery.type( arg1 ), + structure = jQuery.ajaxSettings[ arg0 ], i, - length, - checked = {}, - flag, - noSelect = structure !== "transports"; - - function initSearch( dataType ) { - - flag = transportDataType !== dataType && ! checked[ dataType ]; - - if ( flag ) { - - checked[ dataType ] = 1; - transportDataType = dataType; - list = s[ structure ][ dataType ]; - i = -1; - length = list ? list.length : 0 ; - } - - return flag; - } - - initSearch( dataTypes[ 0 ] ); - - for ( i = 0 ; ( noSelect || ! selected ) && i <= length ; i++ ) { - - if ( i === length ) { - - initSearch( "*" ); - - } else { - - selected = list[ i ]( s , options ); - - // If we got redirected to another dataType - // Search there (if not in progress or already tried) - if ( typeof( selected ) === "string" && - initSearch( selected ) ) { - - dataTypes.unshift( selected ); - selected = 0; - } - } - } - - return noSelect ? jQuery : selected; -} - -// Add an element to one of the structures in ajaxSettings -function ajax_addElement( structure , args ) { - - var i, - start = 0, - length = args.length, - dataTypes = [ "*" ], - dLength = 1, - dataType, - functors = [], - first, - append, - list; - - if ( length ) { - - first = jQuery.type( args[ 0 ] ); + length; + + // We have an options map so we have to inspect the structure + if ( type === "object" ) { + + var options = arg1, + originalOptions = arg2, + // When dealing with prefilters, we execute only + // (no selection so we never stop when a function + // returns a non-falsy, non-string value) + executeOnly = ( arg0 === "prefilters" ), + inspect = function( dataType, tested ) { + + if ( ! tested[ dataType ] ) { + + tested[ dataType ] = true; + + var list = structure[ dataType ], + selected; + + for( i = 0, length = list ? list.length : 0 ; ( executeOnly || ! selected ) && i < length ; i++ ) { + selected = list[ i ]( options , originalOptions ); + // If we got redirected to a different dataType, + // we add it and switch to the corresponding list + if ( typeof( selected ) === "string" && selected !== dataType ) { + options.dataTypes.unshift( selected ); + selected = inspect( selected , tested ); + // We always break in order not to continue + // to iterate in previous list + break; + } + } + // If we're only executing or nothing was selected + // we try the catchall dataType + if ( executeOnly || ! selected ) { + selected = inspect( "*" , tested ); + } + // This will be ignored by ajaxPrefilter + // so it's safe to return no matter what + return selected; + } - if ( first === "object" ) { - return ajax_selectOrExecute( structure , args[ 0 ] , args[ 1 ] ); - } + }; - structure = jQuery.ajaxSettings[ structure ]; + // Start inspection with current transport dataType + return inspect( options.dataTypes[ 0 ] , {} ); - if ( first !== "function" ) { + } else { - dataTypes = args[ 0 ].toLowerCase().split(/\s+/); - dLength = dataTypes.length; - start = 1; + // We're requested to add to the structure + // Signature is ( dataTypeExpression , function ) + // with dataTypeExpression being optional and + // defaulting to catchAll (*) + type = type === "function"; + if ( type ) { + arg2 = arg1; + arg1 = undefined; } + arg1 = arg1 || "*"; - if ( dLength && start < length ) { + // We control that the second argument is really a function + if ( type || jQuery.isFunction( arg2 ) ) { - functors = sliceFunc.call( args , start ); - - for( i = 0 ; i < dLength ; i++ ) { + var dataTypes = arg1.split( /\s+/ ), + functor = arg2, + dataType, + list, + placeBefore; + // For each dataType in the dataTypeExpression + for( i = 0 , length = dataTypes.length ; i < length ; i++ ) { dataType = dataTypes[ i ]; - - first = /^\+/.test( dataType ); - - if (first) { - dataType = dataType.substr(1); - } - - if ( dataType !== "" ) { - - append = Array.prototype[ first ? "unshift" : "push" ]; - list = structure[ dataType ] = structure[ dataType ] || []; - append.apply( list , functors ); + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ); } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( functor ); } } } - - return jQuery; } -// Install prefilter & transport methods -jQuery.each( [ "Prefilter" , "Transport" ] , function( _ , name ) { - _ = name.toLowerCase() + "s"; - jQuery[ "ajax" + name ] = function() { - return ajax_addElement( _ , arguments ); - }; -} ); - })( jQuery ); diff --git a/src/ajax/jsonp.js b/src/ajax/jsonp.js index 883876f..ff8d1f1 100644 --- a/src/ajax/jsonp.js +++ b/src/ajax/jsonp.js @@ -9,10 +9,11 @@ jQuery.ajaxSetup({ jsonpCallback: function() { return "jsonp" + jsc++; } +}); // Detect, normalize options and install callbacks for jsonp requests // (dataIsString is used internally) -}).ajaxPrefilter("json jsonp", function(s, originalSettings, dataIsString) { +jQuery.ajaxPrefilter("json jsonp", function(s, originalSettings, dataIsString) { dataIsString = ( typeof(s.data) === "string" ); diff --git a/src/ajax/script.js b/src/ajax/script.js index b0e576f..bdf69dd 100644 --- a/src/ajax/script.js +++ b/src/ajax/script.js @@ -14,9 +14,10 @@ jQuery.ajaxSetup({ converters: { "text script": jQuery.globalEval } +}); // Handle cache's special case and global -}).ajaxPrefilter("script", function(s) { +jQuery.ajaxPrefilter("script", function(s) { if ( s.cache === undefined ) { s.cache = false; @@ -26,9 +27,10 @@ jQuery.ajaxSetup({ s.type = "GET"; s.global = false; } +}); // Bind script tag hack transport -}).ajaxTransport("script", function(s) { +jQuery.ajaxTransport("script", function(s) { // This transport only deals with cross domain requests if ( s.crossDomain ) { diff --git a/test/unit/ajax.js b/test/unit/ajax.js index 52b0597..d262988 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -599,6 +599,47 @@ test("jQuery.ajax context modification", function() { equals( obj.test, "foo", "Make sure the original object is maintained." ); }); +test("jQuery.ajax context modification through ajaxSetup", function() { + expect(4); + + stop(); + + var obj = {}; + + jQuery.ajaxSetup({ + context: obj + }); + + strictEqual( jQuery.ajaxSettings.context, obj, "Make sure the context is properly set in ajaxSettings." ); + + jQuery.ajax({ + url: url("data/name.html"), + complete: function() { + strictEqual( this, obj, "Make sure the original object is maintained." ); + jQuery.ajax({ + url: url("data/name.html"), + context: {}, + complete: function() { + ok( this !== obj, "Make sure overidding context is possible." ); + jQuery.ajaxSetup({ + context: false + }); + jQuery.ajax({ + url: url("data/name.html"), + beforeSend: function(){ + this.test = "foo2"; + }, + complete: function() { + ok( this !== obj, "Make sure unsetting context is possible." ); + start(); + } + }); + } + }); + } + }); +}); + test("jQuery.ajax() - disabled globals", function() { expect( 3 ); stop(); -- 1.7.10.4