/*
Copyright (c) 2006, All rights reserved.
Code licensed under the BSD License:
http://www.ajaxobject.com
Version: 0.1.10.6 Beta (x.x.month.year Milestone)
*/

/*
 * Javascript class to build assynchronous HTTP requests.
 * 
 * @author Eduardo Lundgren <braeker@gmail.com>
 * @author Jose Berardo <berardo@especializa.com.br>
 * @greetings Thyago Mendes, Thiago Andrade
 * 
 * @version 0.1.10.6 beta
 */ 

/**
 * The AjaxObject global namespace
 * @author Eduardo Lundgren (braeker)
 * @constructor
 */

var AjaxObject = {};

/**
 * 
 * Returns the namespace specified and creates it if it doesn't exist
 * <p>Usage: AjaxObject.namespace("nameOfNamespace");</p>
 * <p>Usage: AjaxObject.namespace("level.names.of.your.namespace");</p>
 * 
 * @author Eduardo Lundgren (braeker)
 * @param  {String} sNameSpace String representation of the desired 
 * @return {null}
 */

AjaxObject.namespace = function(sNameSpace) {
	var levels = sNameSpace.split(".");
	var curLevel = AjaxObject;
	for (var i = (levels[0] == "AjaxObject") ? 1 : 0; i < levels.length; i++) {
 		curLevel[levels[i]] = curLevel[levels[i]] || {};
		curLevel = curLevel[levels[i]];
	}
	return curLevel;
}

AjaxObject.getUserPath = function() {
	var scripts = document.getElementsByTagName("script");
	for (i in scripts) {
		var script = scripts[i];
		var pattern = new RegExp("/AjaxObject.js", "ig");
		
		if (pattern.test(script.src)) {
			return script.src.replace("AjaxObject.js", "");
		}
	}
	return null;
}

AjaxObject.require = function(sPath) {
	
	var libraries = {};
	
	libraries["dom.*"] = libraries["dom"] = ["dom/dom.js"];
	libraries["dom.dom"] = ["dom/dom.js"]
	
	libraries["util.*"] = libraries["util"] = ["util/util.js", "util/suggest.js"];
	libraries["util.util"] = ["util/util.js"];
	libraries["util.suggest"] = ["util/suggest.js"];
	
	libraries["event.*"] = libraries["event"] = ["event/event.js"];
	libraries["event.event"] = ["event/event.js"]
	
	libraries["*"] = [libraries["dom.*"], libraries["util.*"], libraries["event.*"]];

	var references = libraries[sPath];
	for (i in references) {
		var reference = references[i];
		if (typeof reference != "string" && reference.length > 1) {
			for (z in reference)
				AjaxObject.loadLibrary(reference[z]);
		}
		else AjaxObject.loadLibrary(reference);
	}
}

AjaxObject.loadLibrary = function(sLibraryPath) {
	var OUTPUT_SCRIPT_TAG_FORMAT = '<script src="{path}"></script>';
	var output = OUTPUT_SCRIPT_TAG_FORMAT.replace("{path}" , AjaxObject.getUserPath() + sLibraryPath)
	document.write(output);
}


/**
 *
 * Base class for Ajax Connections.
 * @class Base class for Ajax Connections.
 * <p>Usage: var ajax = new AjaxObject.Connector(attributes);</p>
 * 
 * @author Eduardo Lundgren (braeker)
 * @constructor
 * @param {Object} attributes The attribute(s) to be loaded in ajax transaction.  
 * All attribute names use camelCase.
 */
AjaxObject.Connector = function(attributes) {
	this.init(attributes);
}

AjaxObject.Connector.prototype = {
	/**
	 * The collection of attributes to be used.
	 * Each attribute must have at least "url" defined to load.
	 * 
	 * @author Eduardo Lundgren (braeker)
	 * @type Object
	 */
	attributes: null,

	/**
	 * repository which will be stored all request obejcts of each transaction
	 * 
	 * <p>How to use: requestObjects[0] = { name: objElement.name , value: objElement.value };</p>
	 */
	requestObjects: [],
	
	/**
	 * repository which will be stored all request values of each transaction
	 * 
	 * <p>How to use: requestValues["key"] = "value";</p>
	 */
	requestValues: {},
	
	/**
	 * Initialize the attributes of class
	 * 
	 * @author Eduardo Lundgren (braeker)
	 * @param {Object} attributes The name of the attribute.
	 */
	init: function(attributes) {
		
		attributes.method = attributes.method ? attributes.method : "POST";
		attributes.format = attributes.format ? attributes.format : "text";
		attributes.charset = attributes.charset ? attributes.charset : "utf-8";
		attributes.saveCache = typeof attributes.saveCache != "undefined" ? attributes.saveCache : false;
		
		if (attributes.defaultLoadingFunction == true)
			attributes.loading = attributes.loading ? attributes.loading : AjaxObject.util.Html.defaultLoadingFunction;
			
		this.attributes = attributes;
		
		/**
		 * reseting values of attributes...
		 */
		this.requestValues = {};
		this.requestObjects = [];
	},

   /**
    * Put the ajax objects in a queue to be executed one by one
    * 
    * @author Eduardo Lundgren (braeker)
    */
	load: function() {
		AjaxObject.Thread.run(this);
	},


	/**
	 * Add a request value inside a target url
	 * <p>Usage: 
	 *    ajax.addRequestValue("key", "value");
	 * 	  or ajax.addValue("key", "value");
	 * </p>
	 * 
	 * @author Eduardo Lundgren (braeker)
	 * @param {Object} sKey
	 * @param {Object} sValue
	 */
	addRequestValue: function(sKey, sValue) {
		this.requestValues[sKey] = sValue;
	},
	
	addValue: function(sKey, sValue) {
		this.requestValues[sKey] = sValue;
	},
	
	/**
	 * Add a request object inside a target url
	 * <p>Usage:
	 * 	  ajax.addRequestObject(objReference);
	 * 	  or ajax.addObject(objReference);
	 * </p>
	 * 
	 * @author Eduardo Lundgren (braeker)
	 * @param {Object} objElement
	 */
	addRequestObject: function(objElement) {
		try{ this.requestObjects[this.requestObjects.length] = {name: objElement.name, value: objElement.value}; }
		catch(e){ }
	},
	
	addObject: function(objElement) {
		try{ this.requestObjects[this.requestObjects.length] = {name: objElement.name, value: objElement.value}; }
		catch(e){ }
	},
	
	/**
	 * Add all fields of a <form> passed as parameter
	 * 
	 * @author Eduardo Lundgren (braeker)
	 * @param {Object} objForm HTML <form> object
	 */ 
	submitForm: function(objForm) {
		var reArray = new RegExp(/.*\[\]/);
		for (x = 0; x < objForm.elements.length; x++) {
			var element = objForm.elements[x];
			elementName = reArray.test(element.name) ? element.name : element.name + '[]';
			
			if (!element.disabled) {
				var isInBlockedList = { "radio": true, "checkbox": true, "select-multiple": true }; 
				var canAddThisObject = isInBlockedList[element.type] == true ? false : true;
				
				/**
				 * Adding simple objects
				 */
				if (canAddThisObject || element.checked)
					this.addRequestObject(element);
					
				/**
				 * Adding values for multiples selects
				 */			
				if (element.type == 'select-multiple') {	
					for (i = 0; i < element.options.length; i++) {
						if (element.options[i].selected == true)
							this.addRequestObject( {name: elementName, value: element.options[i].value} );
					}
				}
			}
		}
	},
	
	/**
	 * Build the request string of stored values found into requestObjects and requestValues
	 * 
	 * @author Eduardo Lundgren (braeker)
	 * @return {String} string Request string
	 */
	buildRequestString: function() {
		string = "";
		for (key in this.requestValues) {
			value = this.requestValues[key];
			string += key + '=' + encodeURIComponent(value) + "&";
		}
		for (x in this.requestObjects) {
			object = this.requestObjects[x];
			if (object.name) string += object.name + '=' + encodeURIComponent(object.value) + "&";
		}
		return string;
	}

}

/**
 * Instanciate a new "thread" with a reference to an AjaxObject.Connector
 * this connector object store informations about some ajax requested by the user
 * 
 * @author Eduardo Lundgren (braeker)
 */
AjaxObject.Thread = {
	queue: [],
	locked: false,
	Connector: {},
	lastConnectorRead: 0,
	
	/**
	 * Queue Connector objects and call one by one
	 * 
	 * @author Eduardo Lundgren (braeker)
	 * @param {Object} objConnector
	 */
	run: function(objConnector) {
		this.queue[this.queue.length] = objConnector;
		if (!this.locked) this.dispatch();
	},
	
   /**
    * Start ajax transaction
    * 
    * @author Eduardo Lundgren (braeker)
    */
	dispatch: function() {
		/**
		 * Locking temporarily the XmlHttpRequest transaction
		 */
		this.locked = true;
		
		/**
		 * at the end of the queue the lock is disabled and the dispatch method is stopped.
		 */
		if (this.lastConnectorRead == this.queue.length) {
			this.locked = false;
			return false;
		}
		
		/**
		 * getting the current queued object
		 */
		var objConnector = this.queue[this.lastConnectorRead];
		
		/**
		 * Associating this current thread with the current queued object
		 */
		objConnector.thread = this;	
		objConnector.xmlHttp = this.getXmlHttpRequestObject();

		var requestString = objConnector.buildRequestString();
		
		if (typeof objConnector.attributes.url == "undefined")
		{
			/**
			 * if the attribute url wasnīt found, this error message is called
			 */
			AjaxObject.Error.showAttributeMessage("url");
			this.callNextQueuedObject();
			return false;
		}
		
		/**
		 * opening the first call to loading function
		 */
		loadingArguments = objConnector.attributes.loadingArgs;
		if (typeof objConnector.attributes.loading != "undefined")
			objConnector.attributes.loading('loading', loadingArguments);
		
		/**
		 * opening the xmlHttpRequest object...
		 */
		if (objConnector.attributes.method.toUpperCase() == "POST")
		{
			objConnector.xmlHttp.open("POST", objConnector.attributes.url, true);
			objConnector.xmlHttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=' + objConnector.attributes.charset);
			objConnector.xmlHttp.setRequestHeader('Content-Length', requestString.length);
			
			/**
			 * if the attribute "saveCache" is defined to false this headers will be set.
			 */
			if (!objConnector.attributes.saveCache)
			{
		 		objConnector.xmlHttp.setRequestHeader("Cache-Control", "no-store, no-cache, must-revalidate");
				objConnector.xmlHttp.setRequestHeader("Cache-Control", "post-check=0, pre-check=0");
				objConnector.xmlHttp.setRequestHeader("Pragma", "no-cache");
			}
			
			objConnector.xmlHttp.send(requestString);
			
		}
		else if (objConnector.attributes.method.toUpperCase() == "GET") {
			objConnector.xmlHttp.open("GET", objConnector.attributes.url + "?" + requestString, true);
			objConnector.xmlHttp.send(null);
		}
		
		objConnector.xmlHttp.onreadystatechange = function() {
			objConnector.thread.onReadyStateChange();
		}
	},
	
	/**
	 * Check for the response code for the HTTP request.
	 * 
	 * @author Eduardo Lundgren (braeker)
	 * @see xmlHttp.onreadystatechange
	 */
	onReadyStateChange: function() {
		var objConnector = this.queue[this.lastConnectorRead];
		states = ['uninitialized', 'loading', 'loaded', 'interactive', 'complete'];
		
		/**
		 * Here is calling the loading function.
		 * The functionality of this method is a little bit complex, because itīs interage with the readyState.
		 * 
		 * When you load your ajax pre-settings, the readystate follow this levels: 
		 * 	readyState Status Codes:
		 * 		0 = uninitialized
		 * 		1 = loading
		 * 		2 = loaded
		 * 		3 = interactive
		 * 		4 = complete
		 *
		 * When you will implement your loading function you may use like this.
		 * 
		 * <code>
		 * loading: function(readyState, args) { if (readyState == "loading") { //show div loading }; if (readyState == "complete") { //hide div loading } },
		 * loadingArgs: { div1: "users", div2: "products" }
		 * </code>
		 * 
		 */
		arguments = objConnector.attributes.loadingArgs;
		if (typeof objConnector.attributes.loading != "undefined")
			objConnector.attributes.loading(states[objConnector.xmlHttp.readyState], arguments);
		
		if (objConnector.xmlHttp.readyState == 4) {
			if (objConnector.xmlHttp.status == 200) {
				
				/**
				 * When "callBack" is called, two parameters is passed into it. The first one is the responseText generated by the url opened
				 * and the second is the "callBackArgs"
				 * 
				 * <p>
				 * Usage: 
				 * 		callBack: function(responseText, args) { alert(args.email) },
				 * 		callBackArgs: { nome: "Eduardo", email: "braeker@gmail.com" }
				 * </p>
				 */
				arguments = objConnector.attributes.callBackArgs;
				if (typeof objConnector.attributes.callBack != "undefined") {
					
					var responseParameter = objConnector.xmlHttp.responseText;
					
					if (objConnector.attributes.autoParseJSONResult)
						responseParameter = AjaxObject.util.JSON.parse(responseParameter);
					/**
					 * calling the callback function
					 */
					objConnector.attributes.callBack(responseParameter, arguments);
				}
				
					
				
				/**
				 * calling the next queue position...
				 */	
				this.callNextQueuedObject();
			
			} else {
				/**
				 * The "exception" function is called when something wrong occurs.
				 * 
				 * @param {int} xmlHttp.status A number thatīs identify the status when the error occurs.
				 * @param {String} xmlHttp.statusText A description of the error.
				 */
				if (typeof objConnector.attributes.exception != "undefined")
					objConnector.attributes.exception(objConnector.xmlHttp.status, objConnector.xmlHttp.statusText);
				else
					AjaxObject.Error.dataReceiveProblem(objConnector.xmlHttp.statusText);
				
				/**
				 * calling the next queue position...
				 */
				this.callNextQueuedObject();
			}
		}
	},
	
	/**
	 * Instanciate a new XmlHttpRequest thread
	 */
	getXmlHttpRequestObject: function() {
		try { return new XMLHttpRequest(); }
		catch(e) { try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { return false; } }
	},
	
	/**
	 * Call the next Connector object of a queue
	 */
	callNextQueuedObject: function() {
		this.lastConnectorRead++;
		this.locked = false;
		this.dispatch();
	}
	
}

/**
 * Manage error messages that could be occurred
 * <p>Usage: AjaxObject.Error.showAttributeMessage("url");</p>
 * 
 * @author Eduardo Lundgren (braeker)
 */
AjaxObject.Error = {
	HEADER_ERROR: "AjaxObject ERROR:\n",
	ATTRIBUTE_ERROR: "The [{attribute}] have to be passed in the attributes.",
	DATA_RECEIVE_ERROR: "Data receive problem: {responseText}",
	OBJECT_NOT_FOUND_ERROR: "The object was not found. Create it first.\n\nError details: {sPathError}",
	DEFAULT_MESSAGE_ERROR: "Error details: {sMessage}",
	
	/**
	 * Called when user wasnīt set the url attribute
	 * @param {Object} sAttrName
	 */
	showAttributeMessage: function(sAttrName) {
		alert(this.HEADER_ERROR + this.ATTRIBUTE_ERROR.replace("{attribute}", sAttrName));
	},
	/**
	 * Called when user wasnīt set the exception function
	 * @param {Object} sResponseText
	 */
	dataReceiveProblem: function(sResponseText) {
		alert(this.HEADER_ERROR + this.DATA_RECEIVE_ERROR.replace("{responseText}", sResponseText));
	},
	
	objectNotFound: function(sPathError) {
		alert(this.HEADER_ERROR + this.OBJECT_NOT_FOUND_ERROR.replace("{sPathError}", sPathError));
	},
	
	showMessage: function(sMessage) {
		alert(this.HEADER_ERROR + this.DEFAULT_MESSAGE_ERROR.replace("{sMessage}", sMessage));
	}
	
}



