API Docs for:
Show:

File: latest/ajax.js

/**
 * Daniel15 JavaScript Framework - By Daniel15, 2011
 * http://dl.vc/jsframework
 * Feel free to use any of this, but please link back to my site (dan.cx)
 */
 
/**
 * Create a new AJAX requester. 
 * @class Ajax
 * @constructor
 * @module JSFramework
 * @submodule AJAX
 * @param  {String} url URL to load
 * @param  {Object} options Options, which may consist of the following:
 *
 *  - method: `POST` or `GET`
 *  - onSuccess, onFailure, onComplete: Callbacks for when the specified event happens
 *  - format: `json` or `text`, format to interpret result as
 *  - data: GET or POST data to send when doing request
 *  - context: What "this" should be when callback is called
 * @example
	// Basic GET request:
	var request = new Ajax('test.php',
	{
		onSuccess: function(data)
		{
		    console.log(data);
		}
	});
	request.send();
 * @example
 	// POST request
 	// Uses Util.buildQueryString to encode POST data. Arrays and hashes are sent using PHP syntax
	(new Ajax('test.php',
	{
		data:
		{
		    'hello': 'world',
		    // Supports arrays
		    'arrayTest': [1, 2, 3, 4, 5],
		    // Supports hashes
		    'hashText': 
		    {
		        'awesome': true,
		        'something': 'else'
		    }
		},
		onSuccess: function(data)
		{
		    console.log(data);
		}
	})).send();
	
	// Alternatively, the data can be passed to the send() method directly:
	(new Ajax('test.php',
	{
		onSuccess: function(data)
		{
		    console.log(data);
		}
	})).send({hello: 'world'});
 */
var Ajax = function(url, options)
{
	/**
	 * URL the request will be to
	 * @property url
	 * @type String
	 */
	this.url = url;
	/**
	 * Current request, if any
	 * @property currentRequest
	 * @type XMLHttpRequest
	 * @private
	 */
	this.currentRequest = null;
	
	/**
	 * Options for the AJAX request
	 * @property options
	 * @type Object
	 */
	this.options = Util.extend(
	{
		// Default options:
		method: 'post',
		// Default the callbacks to empty functions
		onSuccess: Util.emptyFn,
		onFailure: Util.emptyFn,
		onComplete: Util.emptyFn,
		format: 'json',
		data: null,
		context: null
	}, options);
}

// Shared variables
/**
 * Count of how many requests have been done
 * @property requestCount
 * @type Integer
 * @static
 * @private
 */
Ajax.requestCount = 0;
/**
 * All the currently executing requests
 * @property requests
 * @type Object
 * @static
 * @private
 */
Ajax.requests = {};

Ajax.prototype = 
{
	/**
	 * Send this AJAX request
	 * @method send
	 * @param {Object} data	Query string data to send, optional.
	 */
	send: function(data)
	{
		// Abort any current request if required
		if (this.currentRequest)
		{
			this.currentRequest.onreadystatechange = Util.emptyFn;
			this.currentRequest.abort();
		}
		
		var xhr = this.currentRequest = this._getXHR();
		xhr.open(this.options.method, this.url, true);
		xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
		if (this.options.method == 'post')
			xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			
		var self = this;
		// Save the request so we can abort it if the user switches page
		var requestId = ++Ajax.requestCount;
		Ajax.requests[requestId] = xhr;
		
		xhr.onreadystatechange = function()
		{
			// readyState 4 == complete
			if (xhr.readyState == 4)
			{
				self._onComplete(xhr, requestId);
			}
		}
		xhr.send(Util.buildQueryString(data || this.options.data));
	},
	
	/**
	 * Internal function, called when request completes
	 * @method _onComplete
	 * @param	{XMLHttpRequest} xhr        XHR object used to send request
	 * @param	{Integer}        requestId  Request ID
	 * @private
	 */
	_onComplete: function(xhr, requestId)
	{
		var callback = xhr.status == 200 ? this.options.onSuccess : this.options.onFailure;
		
		if (this.options.format == 'json')
		{
			try
			{
				var data = JSON.parse(xhr.responseText);
			}
			catch (e)
			{
				this.options.onFailure.call(this.options.context, xhr.responseText, xhr, e);
			}
			callback.call(this.options.context, data, xhr);
		}
		else
		{
			callback.call(this.options.context, xhr.responseText, xhr);
		}
		
		this.options.onComplete(xhr);
		this.currentRequest = null;
		// Remove this request from the currently executing requests
		Ajax.requests[requestId] = null;
		delete Ajax.requests[requestId];
	},
	
	/**
	 * Internal function, get an XMLHttpRequest object
	 * @method _getXHR
	 * @return XMLHttpRequest object
	 * @private
	 */
	_getXHR: (function()
	{
		// W3C
		if (window.XMLHttpRequest)
		{
			return function()
			{
				return new XMLHttpRequest();
			}
		}
		// Internet Explorer
		// Reference: http://blogs.msdn.com/b/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx
		else if (window.ActiveXObject)
		{
			return function()
			{
				try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch (e) { }
				try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch (e) { }
				try { return new ActiveXObject("Microsoft.XMLHTTP") } catch (e) { }
				
				alert('Could not create AJAX requester!');
			}
		}
	})()
};

// Abort all the requests when navigating away
// Needs to be done this way as this is a non-standard event :(
window.onunload = function()
{
	for (var i in Ajax.requests)
	{
		Ajax.requests[i].onreadystatechange = Util.emptyFn;
		Ajax.requests[i].abort();
	}
}

// No native JSON support (eg. Internet Explorer < 8)
// Very simple JSON.parse polyfill
if (!window.JSON)
	window.JSON = {};
if (!window.JSON.parse)
{
	window.JSON.parse = function(data)
	{
		//\n's in JSON string, when evaluated will create errors in IE
		var data = data.replace(/[\n\r]/g, '');
		return eval('(' + data + ')');
	}
}