/****
<function name='myError' type='ClassType'>
	<description>Construtor da Classe myError</description>
	<parameters>
		<param name='ID' description='Define o ID da Classe' Optional='false' Default='undefined' />
		<param name='oInnerException' description='Define a exceção dentro da exceção ocorrida dentro da classe' Optional='false' Default='undefined' />
	</parameters>
	<returns></returns>
</function>
****/
function myError(ID, oInnerException)
{
	return obj =
	{
		'id'							: ID,
		'name'						: oInnerException.name || oInnerException.description,
		'description'			: oInnerException.description || oInnerException.message,
		'innerException'	: oInnerException
	};
}

/****
<function name='throwException' type='Function'>
	<description>Método throwException</description>
	<parameters>
		<param name='Message' description='Define a mensagem de erro para mostrar' Optional='false' Default='undefined' />
	</parameters>
	<returns></returns>
</function>
****/
function throwException(Message)
{
	var oError 					= new Error('', '');
	oError.number 			= 0;
	oError.name 				= 'Class Ajax()';
	oError.description 	= Message;
	oError.message 			= Message;
	throw oError;
}

/****
<function name='AjaxParameter' type='ClassType'>
	<description>Contrustor da classe AjaxParameter</description>
	<parameters>
		<param name='Name' description='Define o nome do parametro' Optional='false' Default='undefined' />
		<param name='Value' description='Define o valor do parametro' Optional='false' Default='undefined' />
	</parameters>
	<returns></returns>
</function>
****/
function AjaxParameter(Name, Value)
{
	return oJson =
	{
		'name'	: Name,
		'value'	: Value
	};
}

/****
<function name='ListAjaxParameter' type='ClassType'>
	<description>Construtor da classe ListAjaxParameter</description>
	<parameters></parameters>
	<returns></returns>
</function>
****/
function ListAjaxParameter()
{
	this._arrList = new Array();
}
with (ListAjaxParameter)
{
	//Description: Adiciona um objeto tipo AjaxParameter dentro da lista de parametros
	prototype.add = function(Name, Value)
	{
		this._arrList.push(new AjaxParameter(Name, Value));
	}
	//Description: Remove um objeto referenciado pelo index da lista de parametros 
	prototype.remove = function(index)
	{
		this._arrList.splice(index, 1);
	}
	//Description: Retorna a quantidade de itens dentro da lista de parametros
	prototype.count = function()
	{
		return this._arrList.length;
	}
	//Description: Retorna o ultimo index da lista de parametros
	prototype.lastIndex = function()
	{
		return this._arrList.length - 1;
	}
	//Description: Remove todos os itens da lista de parametros
	prototype.clear = function()
	{
		this._arrList = new Array();
	}
	//Description: Retorna um objeto referenciado pelo index da lista de parametros
	prototype.item = function(index)
	{
		return this._arrList[index];
	}
	//Description: Retorna um array com todos os parametros na lista de parametros
	prototype.toArray = function()
	{
		return this._arrList;
	}
}

/****
<function name='ListReturnParameter' type='ClassType'>
	<description>Construtor da classe ListReturnParameter</description>
	<parameters></parameters>
	<returns></returns>
</function>
****/
function ListReturnParameter()
{
	this._arrList = new Array();
}
with (ListReturnParameter)
{
	//Description: Adiciona um valor na lista de parametros de retorno
	prototype.add = function(Value)
	{
		this._arrList.push(Value);
	}
	//Description: Remove um valor referenciado pelo index da lista de parametros de retorno
	prototype.remove = function(index)
	{
		this._arrList.splice(index, 1);
	}
	//Description: Retorna a quantidade de itens na lista de parametros de retorno
	prototype.count = function()
	{
		return this._arrList.length;
	}
	//Description: Retorna o ultimo index da lista de parametros de retorno
	prototype.lastIndex = function()
	{
		return this._arrList.length - 1;
	}
	//Description: Remove todos os itens da lista de parametros de retorno
	prototype.clear = function()
	{
		this._arrList = new Array();
	}
	//Description: Retorna o valor do item referenciado pelo index da lista de parametros de retorno
	prototype.item = function(index)
	{
		return this._arrList[index];
	}
	//Description: Retorna um array com todos os valores da lista de parametros de retorno
	prototype.toArray = function()
	{
		return this._arrList;
	}
}

/****
<function name='Ajax' type='ClassType'>
	<description>Construtor da Classe Ajax</description>
	<parameters>
		<param name='bitAsync' description='Define se a chamada vai ser feita Sync ou Async' Optional='true' Default='true' />
	</parameters>
	<returns></returns>
</function>
****/
function Ajax(bitAsync)
{
	/*Variaveis Privadas*/
	this._oAjax 						= null;
	this._tmpUrl 	 	 	 	 	 	= '';
	/*Variaveis Privadas*/
	
	/*Eventos Publicos*/
	//CallParameters: oAjaxObject, ArrayOf(callBackParameters)
	this.onCallBack 				= function() { };
	
	//CallParameters: oAjaxObject, url
	this.onSend 						= function() { };
	
	//CallParameters: oAjaxObject, oAjaxHTTPObject
	this.onStateChanged 		= function() { };
	
	//CallParameters: oAjaxObject, oAjaxHTTPObject, url, errorMessage
	this.onError 						= function() { };
	/*Eventos Publicos*/ 
	
	/*Propriedades Publicas*/
	this.method 						= 'POST';
	this.isRunning 					= 
	this.isAborting 				= 
	this.isAborted 					= 
	this.isDebugging 				= false;
	this.url 								= 
	this.responseText				= 
	this.responseXML				= '';
	this.responseJson				= null;
	this.isAsync 						= (bitAsync != 'undefined' && bitAsync != null) ? bitAsync : true;
	
	this.sendParameters			= new ListAjaxParameter();
	this.callBackParameters = new ListReturnParameter();
	/*Propriedades Publicas*/
	
	/*Checar compatibilidade com o browser*/
	this._checkBrowserAjaxSupport();
}

with (Ajax)
{
	/****
	<function name='_checkBrowserAjaxSupport' type='Function' Escope='Private'>
		<description>Verifica se o navegador suporta Ajax</description>
		<parameters></parameters>
		<returns></returns>
	</function>
	****/
	prototype._checkBrowserAjaxSupport = function()
	{
		try
		{
			this._InitializeAjaxObject();
		}
		catch(e)
		{
			throwException('Seu borwser não suporta o object XMLHTTP. Tente atualiza-lo ou usar o Firefox. =) >> ' + (myError(1, e)).description);
		}
		finally
		{
			this._oAjax = null;
		};
	}

	/****
	<function name='_InitializeAjaxObject' type='Function' Escope='Private'>
		<description>Instancia o XMLHTTP no objeto Ajax</description>
		<parameters></parameters>
		<returns></returns>
	</function>
	****/
	prototype._InitializeAjaxObject = function()
	{	
		//Valores padrões
		this.isRunning 			= 
		this.isAborted 		 	= 
		this.isAborting 		= false;
		this.responseText	 	= '';
		this.responseXML		= null;
		
		//Verifica as strings e as funções
		this._validateSendMethod();
		this._validateEvent(this.onError);
		this._validateEvent(this.onSend);
		this._validateEvent(this.onStateChanged);
		this._validateEvent(this.onCallBack);
		
		if (window.ActiveXObject) 
		{
			try 
			{
				this._oAjax	= new ActiveXObject('Msxml2.XMLHTTP.3.0');
			}
			catch(e) 
			{
				try 
				{
					this._oAjax	= new ActiveXObject('Microsoft.XMLHTTP');
				}
				catch(e) 
				{
					throwException('Não foi possível criar o objeto "XMLHTTP" >> ' + (myError(1, e)).description);
				};
			};
		}
		else if(window.XMLHttpRequest) 
		{
			try 
			{
				this._oAjax	= new XMLHttpRequest();
			}
			catch(e) 
			{
				throwException('Não foi possível criar o objeto "XMLHTTP" >> ' + (myError(1, e)).description);
			};
		};
		
		if (this.isAsync)
		{
			var oThis = this;
			this._oAjax.onreadystatechange = function() { oThis._onReadyStateChange(); };
			//ATUALIZAÇÕES PARA A NOVA VERSÃO
			/*if (typeof(XMLHttpProgressEvent) != 'undefined')
			{
				this._oAjax.onprogress = function(aEvent)
				{
					var totalSize = ((aEvent.totalSize == 4294967295) ? 'Unknown' : aEvent.totalSize);
					document.getElementById('selectTeste').options[document.getElementById('selectTeste').options.length] = (new Option(aEvent.position + '/' + totalSize));
					//document.getElementById('selectTeste').options[0] = (new Option(aEvent.position + '/' + totalSize));
				};
			};
			*/
		};
	}
	
	/****
	<function name='_isValidFunction' type='Function' Escope='Private'>
		<description>Verifica se a Function passada por parametro é valida.</description>
		<parameters>
			<param name='f' description='Uma Function' Optional='false' />
		</parameters>
		<returns>true, false</returns>
	</function>
	****/
	prototype._isValidFunction = function(f)
	{
		try 
		{
			return (f && typeof(f) == 'function');
		} 
		catch(e) 
		{
			return false;
		};
	}		
	
	/****
	<function name='_validateSendMethod' type='Function' Escope='Private'>
		<description>Verifica se o método usado pelo ajax é valido.</description>
		<parameters></parameters>
		<returns></returns>
	</function>
	****/
	prototype._validateSendMethod = function()
	{
		this.method = this.method.toUpperCase();
		if (this.method != 'POST' && this.method != 'GET')
			this.method = 'POST';
	}
	
	/****
	<function name='_validateEvent' type='Function' Escope='Private'>
		<description>Verifica se o método é valida para ser um evento. (Caso não seja atualiza com um evento em branco)</description>
		<parameters>
			<param name='f' description='Método' Optional='false' />
		</parameters>
		<returns></returns>
	</function>
	****/
	prototype._validateEvent = function(f)
	{
		try
		{
			f = (!this._isValidFunction(f)) ? function() { } : f;
		}
		catch(e)
		{
			f = function() { };
		};
	}
	
	/****
	<function name='_onReadyStateChange' type='Event' Escope='Private'>
		<description>Este método sera chamada toda vez que o objeto XMLHTTP mudar o seu estado (event proxy)</description>
		<parameters></parameters>
		<returns></returns>
	</function>
	****/
	prototype._onReadyStateChange = function()
	{
		this.isRunning = true;
		//Chama o método onStateChanged
		try
		{
			this.onStateChanged.apply(this, [this, this._oAjax]);
		}
		catch(e)
		{ 
			throwException('Um error ocorreu quando o evento onStateChanged foi chamado. >> ' + (myError(1, e)).description);
		};
		
		try
		{
			if (this._oAjax.readyState.toString().toUpperCase() == 'COMPLETE' || parseInt(this._oAjax.readyState, 10) == 4)
			{
				this.isRunning = false;
				if (this.isAborting || this._oAjax.status == 200)
				{
					if (this.isAborting)
					{
						this.isAborting 	= false;
						this.isAborted 		= true;
						this.responseText	= '';
						this.responseXML	= null;
					}
					else if (this._oAjax.status == 200)
					{
						this.responseText	= this._oAjax.responseText;
						this.responseXML	= this._oAjax.responseXML;
						try
						{
							//this.responseJson = XMLtoJson(this._oAjax.responseXML);
						}
						catch(temp_e)
						{
							this.responseJson = null;
						};
					};
					if (this.isAsync)
						try
						{
							//this.onCallBack.apply(this, [this].concat(this.callBackParameters.toArray()));
							this.onCallBack.apply(this, (new Array()).concat([this], this.callBackParameters.toArray()));
						}
						catch(e)
						{
							throw myError(1, e);
						};
				}
				else
				{
					try
					{
						//Se o Ajax estiver rodando em Debug Mode e houver um erro, uma nova janela do browser sera aberta como popup.
						if (this.isDebugging) window.open(this.getLastUrl());
						this.onError.apply(this, [this, this._oAjax, this.getLastUrl(), this._oAjax.statusText]);
					}
					catch(e)
					{
						throw myError(2, e);
					};
				};
			};
		}
		catch(e)
		{
			switch (parseInt(e.id))
			{
				case 1:
					throwException('Um erro ocorreu quando o evento onCallBack foi chamado. >> ' + e.description);
				break;
				case 2:
					throwException('Um erro ocorreu quando o evento onError foi chamado. >> ' + e.description);
				break;
				default:
					throwException('Um erro não identificado ocorreu no método privado _onReadyStateChange(). >> ' + e.description);
				break;
			};
		};
	}
	
	//MÉTODOS PUBLICOS

	/****
	<function name='abort' type='Function' Escope='Public'>
		<description>Este método cancela a chamada do Ajax (XMLHTTP).</description>
		<parameters></parameters>
		<returns></returns>
	</function>
	****/
	prototype.abort = function()
	{
		if (!this.isRunning) return;
		this.isAborting = true;
		this._oAjax.abort();
	}
	
	/****
	<function name='getLastUrl' type='Function' Escope='Public'>
		<description>Este método retorna a ultima URL que foi requisitada</description>
		<parameters></parameters>
		<returns>string</returns>
	</function>
	****/
	prototype.getLastUrl = function()
	{
		return this._tmpUrl;
	}
	
	/****
	<function name='send' type='Function' Escope='Public'>
		<description>Este método ira ativar o método Send do XMLHTTP, e ira aguardar aqui até que o método termine se estiver rodando 'Sync'.</description>
		<parameters></parameters>
		<returns></returns>
	</function>
	****/
	prototype.send = function()
	{
		//Declarar variaveis temporarias
		var urlTmp	= '';
		var indx		= 0;
		
		//Restaura padrões do objeto XMLHTTP
		this._InitializeAjaxObject();
		this.isRunning = true;
		
		//Constroe os parametros como string
		for (indx = 0; indx < this.sendParameters.count(); indx++)
		{
			urlTmp += (indx == 0) ? '' : '&';
			urlTmp += this.sendParameters.item(indx).name + '=' + escape(this.sendParameters.item(indx).value);
		};
		
		this._tmpUrl = this.url + '?' + urlTmp;
		if (this.method == 'POST')
		{
			//Abre a url usando o método Async
			this._oAjax.open(this.method, this.url, this.isAsync);
			this._oAjax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		}
		else
		{
			//Abre a url usando o método Sync
			this._oAjax.open(this.method, this._tmpUrl, this.isAsync);
			urlTmp = null;
		};
		
		//Chamar o evento onSend antes de executar
		try
		{
			this.onSend.apply(this, [this, this.getLastUrl()]);
		}
		catch(e)
		{
			this.isRunning = false;
			throwException('Um erro ocorreu quando o evento onSend foi chamado. >> ' + (myError(1, e)).description);
		};
				
		//Envia a Requisição
		this._oAjax.send(urlTmp);
		
		//Chama o método privado _onReadyStateChange se estiver rodando 'Sync'
		if (!this.isAsync)
			this._onReadyStateChange();
	}
	//FIM DOS MÉTODOS PUBLICOS
}

var whitespace = /^\s+$/;
function removeWhitespace(node) 
{
	for (i=0; i < node.childNodes.length; i++)
	{
		var current = node.childNodes[i];
		if (current.nodeType == 3 && whitespace.test(current.nodeValue)) 
		{
			// that is, if it's a whitespace text node
			node.removeChild(current);
			i--;
		} 
		else if (current.nodeType == 1) 
			removeWhitespace(current); //remove whitespace on child element's children
	}
	return node;
}

function XMLtoJson(XML)
{
	var XmlChildNodes = removeWhitespace(XML).childNodes;
	alert(XmlChildNodes);
	try
	{	
		var strJson = 'var __V_TryCompileJSON = {';
		strJson 		= strJson + parseChildNode(XmlChildNodes);
		strJson 		= strJson + '};';
		alert(strJson);
		eval(strJson);
		return __V_TryCompileJSON;
	}
	catch(e)
	{
		return null;
	}
}

function parseChildNode(item)
{
	var json = '';
	for (var i = 0; i < item.length; i++)
	{	
		if (item[i].tagName != undefined)
		{
			if (item[i].childNodes.length == 1 && item[i].childNodes[0].data != undefined)
			{
				json += '"' + item[i].tagName + '" : { "Value" : "' + item[i].childNodes[0].data + '"';
				json += parseAttributes(item[i]);
				json += '}';
			}
			else if (item[i].childNodes.length == 1)
			{
				json += '"' + item[i].tagName + '" : {' + parseChildNode(item[i].childNodes)
				json += parseAttributes(item[i]);
				json += '}';
			}
			else if (item[i].childNodes.length > 1)
			{
				var isCollection = true;
				var first = item[i].childNodes[0].tagName;
				for (var c = 0; c < item[i].childNodes.length; c++)
				{
					if (first != item[i].childNodes[c].tagName)
					{
						isCollection = false;
						break;
					};
				};
				
				if (!isCollection)
				{
					json += '"' + item[i].tagName + '" : {' + parseChildNode(item[i].childNodes)
					json += parseAttributes(item[i]);
					json += '}';
				}
				else
				{
					json += '"' + item[i].tagName + '" : [ ';
					for (var c = 0; c < item[i].childNodes.length; c++)
					{
						json += '{ ' + item[i].childNodes[c].tagName + ' : {' + parseChildNode(item[i].childNodes[c].childNodes) + "";
						json += parseAttributes(item[i].childNodes[c]);
						json += '}}';
						if (c < item[i].childNodes.length - 1 && item[i].childNodes[c+1].tagName != undefined)
							json += ', ';
					};
					json += ']';
				};
			};
			if (i < item.length - 1 && item[i+1].tagName != undefined) json += ', ';
		};
	};
	return json;
}

function parseAttributes(item)
{
	var json = '';
	if (item.attributes.length > 0)
	{
		json += ', '
		for (var a = 0; a < item.attributes.length; a++)
		{
			json += '"_' + item.attributes[a].name + '" : "' + item.attributes[a].value + '"';
			if (a < item.attributes.length - 1) json += ', ';
		};
	};
	
	return json;
}