/**
 * AJAX JavaScript library for fusionLib.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Copyright (c) 2007 - 2016 fusionLib. All rights reserved.
 */

(function() {

	/**
	 * Get the transport for AJAX requests.
	 *
	 * @return XMLHttpRequest The object to perform the request.
	 */
	function getTransport() {
		// If native XMLHttpRequest object
		if(typeof XMLHttpRequest !== 'undefined' && (window.location.protocol !== 'file:' || !window.ActiveXObject))
			return new XMLHttpRequest();
		// If ActiveX
		else {
			var progIDs = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP'];
			for(var i in progIDs) {
				try {
					return new ActiveXObject(progIDs[i]);
				}
				catch(e) { }
			}
		}

		return null;
	}

	// Defaults for ajax
	var defaultSettings = {};

	fusionLib.extend({
		/**
		 * Set default values for AJAX requests.
		 *
		 * @param settings map Default values for all future AJAX requests.
		 * @return map The previous settings.
		 */
		ajaxSetup: function(settings) {
			var old = defaultSettings;
			defaultSettings = settings;
			return old;
		},

		/**
		 * Perform an AJAX request.
		 *
		 * @param url string The URL to send the request to.
		 * @param options array  {
		 *	url: URL to send the request to
		 *	type: 'get' (default) | 'post'
		 *	async: true (default) | false
		 *	headers: { } (optional headers)
		 *	dataType: json | xml | html | script - The data type expected from the server, default is all
		 *	data: data to send with the request string or array (optional)
		 *	success: function called when the request completes successfully
		 *	error: function called when an error occures
		 *	timeout: ms before request times out (optional)
		 * }
		 * @return XMLHttpRequest The object performing the request.
		 */
		ajax: function(url, options) {
			if(arguments == 1) {
				options = url;
				url = options.url || defaultSettings.url;
			}
			var request = getTransport();
			if(typeof options === 'undefined') options = {};

			options = fusionLib.extend(fusionLib.extend({}, defaultSettings), options);
			options.headers = options.headers || {};
			options.method = options.method || 'get';
			options.type = options.type ? options.type.toLowerCase() : options.method;
			options.async = options.async === false ? false : true;
			options.data = options.data || '';
			if(typeof options.data !== 'string') {
				var results = [];
				for(var key in options.data)
					results.push(encodeURIComponent(key) + '=' + encodeURIComponent(options.data[key]));
				options.data = results.join('&');
			}

			var getData = options.type == 'get' ? (/\?/.test(url) ? '&' : '?') + options.data : '';
			var postData = options.type == 'post' ? options.data : '';

			request.onreadystatechange = function() {
				if(request.readyState == 4) {
					if(request.timerID) clearTimeout(request.timerID);

					if((request.status >= 200 && request.status < 300) || request.status == 304 || (request.status == 0 && request.responseText)) {
						if (options.success) {
							options.success(request.getResponseHeader('Content-Type').match(/^application\/json/i) ? JSON.parse(request.responseText) : request.responseText);
						}
					}
					else if(options.error) {
						options.error(request);
					}
				}
			};

			request.open(options.type, url + getData, options.async);

			// Set headers
			var defaultHeaders = {
				'Accept': 'text/javascript, application/json, text/html, application/xml, text/xml, */*',
				'Content-Type': 'application/x-www-form-urlencoded',
				'X-Requested-With': 'XMLHttpRequest'
			},
			name;
			if(options.dataType == 'json') defaultHeaders['Accept'] = 'application/json';
			else if(options.dataType == 'xml') defaultHeaders['Accept'] = 'application/xml, text/xml';
			else if(options.dataType == 'html') defaultHeaders['Accept'] = 'text/html';
			else if(options.dataType == 'script') defaultHeaders['Accept'] = 'text/javascript';
			for(name in defaultHeaders) {
				if(!options.headers[name])
					options.headers[name] = defaultHeaders[name];
			}
			for(name in options.headers)
				request.setRequestHeader(name, options.headers[name]);

			request.cancel = function() {
				request.onreadystatechange = function() {};
				request.abort();
			};

			// Do the request
			try {
				if(options.timeout) {
					request.timerID = setTimeout(function() {
						request.cancel();
						if(options.error) options.error(request);
					}, options.timeout);
				}
				request.send(postData);
			} catch (e) {
				if(options.error) options.error(request);
			}
			return request;
		},

		/**
		 * Perform an AJAX request using a GET HTTP request.
		 *
		 * @param url string The URL to send the request to.
		 * @param data string|array Optional data to pass with the request.
		 * @param success function The function to call on success.
		 * @return XMLHttpRequest The object performing the request.
		 */
		get: function(url, data, success) {
			if(typeof data === 'function') {
				success = data;
				data = undefined;
			}
			return this.ajax(url, {
				type: 'get',
				success: success,
				data: data
			});
		},

		/**
		 * Perform an AJAX request using a GET POST request.
		 *
		 * @param url string The URL to send the request to.
		 * @param data string|array Optional data to pass with the request.
		 * @param success function The function to call on success.
		 * @return XMLHttpRequest The object performing the request.
		 */
		post: function(url, data, success) {
			if(typeof data === 'function') {
				success = data;
				data = undefined;
			}
			return this.ajax(url, {
				type: 'post',
				success: success,
				data: data
			});
		},

		/**
		 * Load data via AJAX and insert the result into the elements inner html.
		 * HTTP GET is used if data is not given or HTTP POST if data given.
		 *
		 * @param url string The URL to send the request to.
		 * @param data map Optional data to pass to the server.
		 * @param success function Optional function to call if the data is loaded.
		 * @returns fusionLib This object.
		 */
		load: function(url, data, success) {
			if(typeof data === 'function') {
				success = data;
				data = undefined;
			}

			var elems = fusionLib()._result(this);
			this.ajax(url, {
				type: typeof data === 'undefined' ? 'get' : 'post',
				success:  function(d) {
					elems.each(function() { fusionLib(this).html(d); });
					if(success) success();
				},
				data: data,
				dataType: 'html'
			});

			return this;
		},

		/**
		 * Load JSON data and pass it decoded to the success callback.
		 * HTTP GET is used.
		 *
		 * @param url string The URL to send the request to.
		 * @param data map Optional array of data items or string.
		 * @param success function Optional function to call if the data is loaded.
		 * @return XMLHttpRequest The object performing the request.
		 */
		getJSON: function(url, data, success) {
			if(typeof data === 'function') {
				success = data;
				data = undefined;
			}

			return this.ajax(url, {
				type: 'get',
				success:  function(d) {
					if(success) success(typeof d === 'object' ? d : window.JSON.parse(d));
				},
				data: data,
				dataType: 'json'
			});
		}
	});

	fusionLib.ajaxSetup = fusionLib.fn.ajaxSetup;
	fusionLib.ajax = fusionLib.fn.ajax;
	fusionLib.get = fusionLib.fn.get;
	fusionLib.post = fusionLib.fn.post;
	fusionLib.getJSON = fusionLib.fn.getJSON;
})();
