Skip to main content

Bug Tracker

Side navigation

#159 closed enhancement (fixed)

Opened August 30, 2006 09:32AM UTC

Closed September 26, 2006 02:39AM UTC

Last modified March 15, 2012 01:35AM UTC

Ajax refactoring

Reported by: will@willjessup Owned by:
Priority: major Milestone:
Component: ajax Version:
Keywords: ajax Cc:
Blocked by: Blocking:
Description

Description

refactored the code in the ajax module so that things are homogenous in usage internally with the benefit of less lines of code, and more flexibility in creating and evaluating ajax requests

Changes & notes (non exhaustive)

ln 1514: $.fn.load needs to be looked at , i removed its funky one-off callback in favor of the global methods - but doing so should probably break its ability to execute on every matched item(untested)

$.ajax is now called w/ a single object both internally and externally jQuery.ajax({type: type, url: url, data: params, complete: callback, ifModified: ifModified});

$.httpData executes in only one place for each type of function

$.param executes in only one place, ever.

$.param returns if data = null

Added ability to pass ifModified anywhere in ajax object.

Tested

Firefox1.5 vs.

get (w/ and w/out data)

getifmodified (w/ and w/out data)

load (w/ and w/out data)

loadifmodified yada yada

and $.ajax

Example usage


$.ajax({
      url: "/ajaxmessages.php",
      type: "GET",
      ifModified: 1,
      error: function() { alert("error") },
      success: function(json, status) {},
      complete: function(json, status) {
         eval("var args = " + json);
         $("#ajaxUpdate").append('<div>' + args.messages[0] + '</div>');
      }

  });

CODE

/**
 * Load HTML from a remote file and inject it into the DOM
 */
jQuery.fn.loadIfModified = function( url, params, callback ) {
	this.load( url, params, callback, 1 );
};

jQuery.fn.load = function( url, params, callback, ifModified ) {
	if ( url.constructor == Function )
		return this.bind("load", url);

	// Default to a GET request
	var type = "GET";

	// If the second parameter was provided
	if ( params ) {
		// If it's a function
		if ( params.constructor == Function ) {
			// We assume that it's the callback
			callback = params;
			params = null;
		} else
			type = "POST";

	}

	jQuery.ajax({type: type, url: url, data: params, complete: callback, ifModified: ifModified});

};

// If IE is used, create a wrapper for the XMLHttpRequest object
if ( jQuery.browser.msie && XMLHttpRequest == undefined )
	XMLHttpRequest = function(){
		return new ActiveXObject(
			navigator.userAgent.indexOf("MSIE 5") >= 0 ?
			"Microsoft.XMLHTTP" : "Msxml2.XMLHTTP"
		);
	};

// Attach a bunch of functions for handling common AJAX events
new function(){
	var e = "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess".split(',');
	
	for ( var i = 0; i < e.length; i++ ) new function(){
		var o = e[i];
		jQuery.fn[o] = function(f){
			return this.bind(o, f);
		};
	};
};

jQuery.extend({

	/**
	 * Load a remote page using a GET request
	 */
	get: function( url, data, callback, type, ifModified ) {
		if ( data.constructor == Function ) {
			type = callback;
			callback = data;
			data = null;
		}

		// Build and start the HTTP Request
		jQuery.ajax({type: "GET", url: url, data: data, complete: callback, ifModified: ifModified});
	},

	getIfModified: function( url, data, callback, type ) {
		jQuery.get(url, data, callback, type, 1);
	},

	getScript: function( url, data, callback ) {
		jQuery.get(url, data, callback, "script");
	},
	
	/**
	 * Load a remote page using a POST request.
	 */
	post: function( url, data, callback, type ) {
		// Build and start the HTTP Request
		jQuery.ajax({type: "POST", url: url, data: data, complete: callback});
	},

	// timeout (ms)
	timeout: 0,

	ajaxTimeout: function(timeout) {
		jQuery.timeout = timeout;
	},

	// Last-Modified header cache for next request
	lastModified: {},

	/**
	 * A common wrapper for making XMLHttpRequests
	 */
	ajax: function(o) {

                //Grab vars from object passed to $.ajax
		ret = o.complete;
		var ifModified = o.ifModified;
		var success = o.success;
		var error = o.error;
		data = jQuery.param(o.data);
		url = o.url;
		type = o.type;

		// Watch for a new set of requests
		if ( ! jQuery.active++ )
			jQuery.event.trigger( "ajaxStart" );

		var requestDone = false;
	
		// Create the request object
		var xml = new XMLHttpRequest();

		// Open the socket
		xml.open(type || "GET", url, true);
		
		// Set the correct header, if data is being sent
		if ( data )
			xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		
		// Set the If-Modified-Since header, if ifModified mode.
		if ( ifModified )
			xml.setRequestHeader("If-Modified-Since",
				jQuery.lastModified[url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
		
		// Set header so calling script knows that it's an XMLHttpRequest
		xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");

		// Make sure the browser sends the right content length
		if ( xml.overrideMimeType )
			xml.setRequestHeader("Connection", "close");
		
		// Wait for a response to come back
		var onreadystatechange = function(istimeout){
			// The transfer is complete and the data is available, or the request timed out
			if ( xml && (xml.readyState == 4 || istimeout == "timeout") ) {
				requestDone = true;

				var status = jQuery.httpSuccess( xml ) && istimeout != "timeout" ?
					ifModified && jQuery.httpNotModified( xml, url ) ? "notmodified" : "success" : "error";
				
				// Make sure that the request was successful or notmodified
				if ( status != "error" ) {
					// Cache Last-Modified header, if ifModified mode.
					var modRes = xml.getResponseHeader("Last-Modified");
					if ( ifModified && modRes ) jQuery.lastModified[url] = modRes;

					// If a local callback was specified, fire it
					if ( success ) success( jQuery.httpData(xml), status );
					
					// Fire the global callback
					jQuery.event.trigger( "ajaxSuccess" );
				
				// Otherwise, the request was not successful
				} else {
					// If a local callback was specified, fire it
					if ( error ) error( jQuery.httpData(xml), status );
					
					// Fire the global callback
					jQuery.event.trigger( "ajaxError" );
				}
				
				// The request was completed
				jQuery.event.trigger( "ajaxComplete" );

				// Handle the global AJAX counter
				if ( ! --jQuery.active )
					jQuery.event.trigger( "ajaxStop" );
	
				// Process result
				if ( ret ) ret( jQuery.httpData(xml), status);
				
				// Stop memory leaks
				xml.onreadystatechange = function(){};
				xml = null;
				
			}
		};
		xml.onreadystatechange = onreadystatechange;
		
		// Timeout checker
		if(jQuery.timeout > 0)
			setTimeout(function(){
				// Check to see if the request is still happening
				if (xml) {
					// Cancel the request
					xml.abort();

					if ( !requestDone ) onreadystatechange( "timeout" );

					// Clear from memory
					xml = null;
				}
			}, jQuery.timeout);
		
		// Send the data
		xml.send(data);
	},
	
	// Counter for holding the number of active queries
	active: 0,
	
	// Determines if an XMLHttpRequest was successful or not
	httpSuccess: function(r) {
		try {
			return !r.status && location.protocol == "file:" ||
				( r.status >= 200 && r.status < 300 ) || r.status == 304 ||
				jQuery.browser.safari && r.status == undefined;
		} catch(e){}

		return false;
	},

	// Determines if an XMLHttpRequest returns NotModified
	httpNotModified: function(xml, url) {
		try {
			var xmlRes = xml.getResponseHeader("Last-Modified");

			// Firefox always returns 200. check Last-Modified date
			return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
				jQuery.browser.safari && xml.status == undefined;
		} catch(e){}

		return false;
	},
	
	// Get the data out of an XMLHttpRequest.
	// Return parsed XML if content-type header is "xml" and type is "xml" or omitted,
	// otherwise return plain text.
	httpData: function(r,type) {
		var ct = r.getResponseHeader("content-type");
		var data = !type && ct && ct.indexOf("xml") >= 0;
		data = type == "xml" || data ? r.responseXML : r.responseText;

		// If the type is "script", eval it
		if ( type == "script" ) eval.call( window, data );

		return data;
	},
	
	// Serialize an array of form elements or a set of
	// key/values into a query string
	param: function(a) {
		var s = [];
                if(!a) return;
		// If an array was passed in, assume that it is an array
		// of form elements

		if ( a.constructor == Array ) {
			// Serialize the form elements
			for ( var i = 0; i < a.length; i++ )
				s.push( a[i].name + "=" + encodeURIComponent( a[i].value ) );

		// Otherwise, assume that it's an object of key/value pairs
		} else {
			// Serialize the key/values
			for ( var j in a )
				s.push( j + "=" + encodeURIComponent( a[j] ) );
		}
		
		// Return the resulting serialization
		return s.join("&");
	}

});
Attachments (0)
Change History (4)

Changed September 06, 2006 05:50AM UTC by anonymous comment:1

priority: majortrivial
resolution: → wontfix
status: newclosed

hu hu your text is great!

But so great that it doesn't stay in his box ;) (safari 2.0.4 - 419.3)

Changed September 14, 2006 01:55AM UTC by will comment:2

keywords: ajax, pwnajax
summary: Ajax p-p-p-powerupAjax refactoring

not sure why this was closed?

Changed September 23, 2006 08:39AM UTC by joern comment:3

milestone: 1.0
priority: trivialmajor
resolution: wontfix
status: closedreopened
version: 1.0b1

Changed September 26, 2006 02:39AM UTC by john comment:4

resolution: → fixed
status: reopenedclosed

Most of this has already been added in - thanks for the code, Will.