Bug Tracker

Opened 14 years ago

Closed 14 years ago

Last modified 8 years ago

#159 closed enhancement (fixed)

Ajax refactoring

Reported by: will@… 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("&");
	}

});

Change History (4)

comment:1 Changed 14 years ago by anonymous

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)

comment:2 Changed 14 years ago by will

Keywords: pwn removed
Summary: Ajax p-p-p-powerupAjax refactoring

not sure why this was closed?

comment:3 Changed 14 years ago by joern

Milestone: 1.0
Priority: trivialmajor
Resolution: wontfix
Status: closedreopened
Version: 1.0b1

comment:4 Changed 14 years ago by john

Resolution: fixed
Status: reopenedclosed

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

Note: See TracTickets for help on using tickets.