/**
 * (c) Copyright Ashantiplc Limited
 * All Rights Reserved
 * Duplication prohibited. Redistriubtion, Transmission, displayed by any means prohibited.
 * You may not alter or remove any trademark, copyright or other notices.
 * Author: Andrey Anisimov | no email
 *
 * AJAX classes.
 *
 * This file contains definitions of the following classes:
 * - AJAXException
 * - AJAXDomToObjectConverter
 * - AJAXResponse
 * - AJAXRequest
 * - AJAX
 */

function AJAXException(message) {
	
	this.message = message;
	
	this.toString = function() {
		return 'AJAXException: ' + this.message;
	}
}

/**
 * Class to convert DOM Document object to simple JavaScript object.
 */
function AJAXDomToObjectConverter() {
	
	this.getBool = function(node) {
		return (node.firstChild.nodeValue == '1') ? true : false;
	}
	
	this.getInt = function(node) {
		return parseInt(node.firstChild.nodeValue);
	}
	
	this.getFloat = function(node) {
		return parseFloat(node.firstChild.nodeValue);
	}
	
	this.getArray = function(node) {
		var arr = new Array();
		for (var i = 0; i < node.childNodes.length; i++) {
			if (node.childNodes.item(i).nodeType == 1) {
				arr.push(this.getValue(node.childNodes.item(i)));
			}
		}
		return arr;
	}
	
	this.getObject = function(node) {
		var obj = new Object();
		this.convert(node, obj);
		return obj;
	}
	
	this.getString = function(node) {
		try {
			return node.firstChild.nodeValue;
		} catch (ex) {
			return '';
		}
	}
	
	this.getValue = function(node) {
		var attr = node.attributes.getNamedItem('type');
		var type = (attr) ? attr.nodeValue : 'string';
		switch (type) {
			case 'null':	return null;
			case 'bool':	return this.getBool(node);
			case 'int':		return this.getInt(node);
			case 'float':	return this.getFloat(node);
			case 'array':	return this.getArray(node);
			case 'object':	return this.getObject(node);
			default:		return this.getString(node);
		}
	}
	
	this.convert = function(node, obj) {
		for (var i = 0; i < node.childNodes.length; i++) {
			if (node.childNodes.item(i).nodeType == 1) {
				var child = node.childNodes.item(i);
				var name = child.nodeName;
				obj[name] = this.getValue(child);
			}
		}
	}
	
	this.toString = function() {
		return 'AJAXDomToObjectConverter';
	}
}

/**
 * Object of this class passed to callback method as result of request processing.
 */
function AJAXResponse(dom) {
	
	var dom2obj = new AJAXDomToObjectConverter();
	dom2obj.convert(dom, this);
	
	this.toString = function() {
		return 'AJAXResponse';
	}
}

/**
 * Object of this class used to send requests to server.
 * NOTE: use separate objects for separate requests.
 */
function AJAXRequest(url, callback) {
	
	this.url = url;
		
	this.createHttp = function(callback) {
		// Create XMLHTTP Object depending of browser.
		var http = (navigator.appName.indexOf('Microsoft') >= 0)
			? new ActiveXObject('Microsoft.XMLHTTP')
			: new XMLHttpRequest();
			
		// HTTP notifier substitution
		http.onreadystatechange = function() {
			if (http.readyState == 4) {
				// Connection is ready
				if (http.status == 200) {
					// Status code is OK
					try {
						var rootNode;
						if (http.responseXML && (rootNode = http.responseXML.documentElement)) {
							rootNode.normalize();
							// XML data retrieved
							if ('response' == rootNode.nodeName) {
								// Invoke callback function with AJAXResponse object
								callback(new AJAXResponse(rootNode));
							} else if ('error' == rootNode.nodeName) {
								// Server says that there were some errors...
								var error = new AJAXResponse(rootNode);
								throw new AJAXException(
									"There were errors while processing request by server side:\n\n"
									+ error.message + "\n"
									+ error.description
								);
							} else {
								throw new AJAXException("Unsupported response format:\n" + http.responseText);
							}
						} else {
							// Invoke callback with plain text
							callback(http.responseText);
						}
					} catch (ex) {
						throw new AJAXException(ex.message);
					}
		        } else {
		        	throw new AJAXException("There was a problem retrieving the XML data:\n" + http.statusText);
		        }
			}
		}
		
		return http;
	}
	
	this.http = this.createHttp(callback);
		
	this.send = function(content) {
		if (content && content.length > 0) {
			this.http.open('POST', this.url);
			this.http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			this.http.send(content);
		} else {
			this.http.open('GET', this.url, true);
			this.http.send(null);
		}
	}
	
	this.toString = function() {
		return 'AJAXRequest';
	}
}

/**
 * End-user class represents the whole AJAX package.
 */
function AJAX() {
	
	this.urlEncode = function(str) {
	    if (str == null) return '';
		str = encodeURI(str);
        str = str.replace(/\+/g, "%2B");
		return str.replace(/&/g, "%26");
	}
	
	this.sendRequest = function(command, callback) {
		var req = new AJAXRequest(command, callback);
		var content = '';
		for (var i = 2; i < arguments.length; i++) {
			for (var name in arguments[i]) {
				var value = arguments[i][name];
				content += this.urlEncode(name) + '=' + this.urlEncode(value) + '&';
			}
		}
		req.send(content);
	}
	
	this.toString = function() {
		return 'AJAX';
	}
}

/**
 * Perform access to AJAX functions via this object.
 */
var ajax = new AJAX();
