AjaxObject Part I

The AjaxObject class is a layer built on top of the YUI Connection Utility to simplify dealing with the AJAX communication. It ensures the required connection parameters are set, simplifies client- and server-side error handling, provides automatic polling, prevents request duplication, allows for transaction rollbacks, and a more. Needless to say, it is very powerful, and one of the key infrastructure changes that will be going into the next version (12) of Mint.com.

The idea originally came to me, while looking at "The Callback Object and Scope" example in the Connection Utility documentation. The author creates a stub object to hold various useful functions related to the YAHOO.util.Connect.asyncRequest. Taking this to the next level, we create an instantiatable object that encapsulates the AJAX request system.

I have broken this article into 3 parts, two articles to cover the AjaxObject and one day to cover AjaxManager. The second article will come out early next week, followed by the third later in the week. You can see the complete source code for more detailed comments and a working version.

Example 1: Constants and Internal Functions

	// constants
var Y = YAHOO,
	YU = YAHOO.util,
	CONN = YU.Connect,
	LANG = Y.lang,
	YD = YU.Dom,
	YE = YU.Event,
	LOGOUT_URL = logout.event? + C.PARAM_NAME_NEXT_PAGE + =,

	// define known error codes
	ERROR_ABORTED = -1,
	ERROR_CODE_SESSION_TIMED_OUT = 1,
	ERROR_CODE_INVALID_PARAMETER = 2,
	ERROR_PAGE_NOT_FOUND = 404,

	// local variables
	_isUnloading = false,

	// Removes invalid chunks from a string, where an invalid chunk is more than one & in a row, ending with &, or ?&.
	_cleanInvalidChunks = function(s) {
        var str = ( + s).replace(/&+/g, &).replace(/\?&/, ?);
		return & === str.charAt(str.length - 1) ? str.slice(0, -1) : str;
	},

	// Stops the provided timer.
	_haltTimer = function(timer) {
		if (timer) {timer.cancel();}
	},

	// Handles error logging.
	_logError = function(name, error) {
		if (! _isUnloading) {
			Y.log(name + ": " + error);
		}
	},

	// Sets the status to unloading, so additional requests arent made while the page is unloading.
	_unloadingCallback = function() {_isUnloading = true;},

	// Instantiates an AjaxObject.
	_F = function(conf) {
		var cfg = {};
		LANG.augmentObject(cfg, _F.ATTR, true);
		LANG.augmentObject(cfg, conf || {}, true);
		if (! cfg.rId) {cfg.rId = yui-gen + YAHOO.env._id_counter++;}
		this._cfg = cfg;
		YU.AjaxManager.register(this);
	};

YU.AjaxObject = _F;
	
// this prevents AJAX requests from being made after the user leaves the current page
YE.on(window, unload, _unloadingCallback);
YE.on(window, beforeunload, _unloadingCallback);

The code in Example 1 creates a few constants that will be used later in the system, then several functions that will be leveraged later, followed by the AjaxObject constructor, and then registers two unload listeners. The error constants are defined by the developer (except -1 and 404, which are required), and can be sent by the server to indicate an error which AjaxObject should automatically handle. The functions are self-explainatory; except, that I would replace the Y.log of _logError() with your own logging sytem. The constructor takes an optional configuration object and applies the values to this._cfg after applying the default values. It also registers the AjaxObject with AjaxManager, which manages all AjaxObjects. Lastly, the unload and beforeunload events are listened on, to update the internal isUnloading value. This prevents AJAX requests from being sent while the page is unloading, which caused issues with the window.onerror logging system used on Mint.com.

Example 2: Augmenting YAHOO.util.AjaxObject

// the acceptable types of responses from the server
LANG.augmentObject(_F, {
	TYPE_JSON: text/json,
	TYPE_XML: text/xml,
	TYPE_XML_APP: application/xml
});

// a constant of default configuration parameters
_F.ATTR = {
	abortOnDuplicate: true,
	argument: null,
	cache: false,
	callback: null,
	data: null,
	failure: null,
	method: get,
	pollTimeout: 0,
	rId: null,
	rollback: null,
	scope: null,
	timeout: 10000,
	requestDelay: 0,
	type: null,
	url: null
};

In Example 2 several constants are applied to YAHOO.util.AjaxObject: the various acceptable response types, and the default configuration settings. The ATTR shows all possible configuration parameters that can be set and their default values. These values can be overridden permanently when the AjaxObject is instantiated, or temporarily whenever startRequest is called to actually make an AJAX request. Here is what these values mean:

Setting abortOnDuplicate to true tells AjaxObject to abort any existing request, whenever it makes a new request, preventing duplicate callback execution, but not necessarily duplicate requests to the server. The argument will be passed through to the callback function, like when using the Connection Utility directly. Setting cache to true will cause Connection Utility to cache the request. The callback is a function to execute when request is successfully. The data can be null for get requests or either a simple object, array, or string for post requests (the AjaxObject will automatically convert them to a search string). The failure is a function to execute if the AJAX request fails, in addition to the default failure logic. The method should be either get or post&rsquot;. Setting pollTimeout to a positive integer will cause AjaxObject to poll the configured URL until the callback function returns true. The rId is a unique identifer for this AjaxObject. The rollback is a function the developer can specify to execute a rollback when the request fails. The scope is the execution context to execute the callback callback function in. The timeout is the amount of time to wait before the AjaxObject automatically aborts (default is 10 seconds). Setting requestDelay to a positive integer causes AjaxObject to wait that many milliseconds before issuing its request; when used in conjunction with abortOnDuplicate it will prevent duplicate requests from being sent to the server, however, it will prevent startRequest from returning the YUI connection object. Set type to one of the constants defined in Example 2 when expecting the AJAX request to return a specific response type. And set the url whenever making any request (this must be provided or AjaxObject will throw an error when trying to execute startRequest).

The next article will cover the YAHOO.util.AjaxObject.prototype function and show an example of how to use AjaxObject.