	function SOAPEngine () {}

	SOAPEngine.XMLHTTP = ["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
		
	SOAPEngine.preHookHandler = function() {};
	SOAPEngine.postHookHandler = function() {};
	SOAPEngine.errorHookHandler = function(message) {
		if (typeof message == "object" && message.name == "Error" && message.description) {
			alert("Error: " + message.description);
		} else {	
			alert(message);
		}
		if (SOAPEngine.postHookHandler)
			SOAPEngine.postHookHandler();
	};

	SOAPEngine.soapInfo = { url:'', uri:''};

	SOAPEngine.SOAP = 1;
	SOAPEngine.XMLONLY = 2;

	SOAPEngine.type = SOAPEngine.SOAP;
		
	SOAPEngine.soapHeader = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>';
	SOAPEngine.soapTrailer = '</soap:Body></soap:Envelope>';

	SOAPEngine.req = null;
	SOAPEngine.data = null

	SOAPEngine.loadingDivID = 'soap_loading';
	SOAPEngine.loadingDiv = "<div id='soap_loading' style='{color:#FFFFFF; background-color:#CC0000; text-decoration:blink; float:left; position:absolute; left:0; top:0; padding:3px; padding-left:10px; padding-right:10px; margin:2px; font-family: Arial; font-size:10pt;}'>Loading...</div>";

	SOAPEngine.callstack = null;

	SOAPEngine.setPreHook = function(handler) {
		SOAPEngine.preHookHandler = handler;
	}

	SOAPEngine.setPostHook = function(handler) {
		SOAPEngine.postHookHandler = handler;
	}

	SOAPEngine.setErrorHook = function(handler) {
		SOAPEngine.errorHookHandler = handler;
	}

	SOAPEngine.setInfo = function (info) {
		SOAPEngine.soapInfo = info;
	}

	SOAPEngine.setLoadingDiv = function (inLoadingDiv) {
		SOAPEngine.loadingDiv = inLoadingDiv;
	}

	// These next 4 methods were added to control multiple calls to the engine.
	// They manage a very rudimentary 'callStack', lining up calls to be made and processing
	// them as necessary.

	SOAPEngine.pushCallStack = function(call) {
	
		if (SOAPEngine.callStack == null)
		{
			SOAPEngine.callStack = new Array();
			// debug("instanciando callstack");
		}
		SOAPEngine.callStack.push(call);
		// debug("colocando " + call.action + " no stack");
	}

	SOAPEngine.popRunningCallStack = function() {
		// debug("tirando running call do stack");
		var arTmp = new Array();
		for (x=0; x<SOAPEngine.callStack.length; x++){
			if (SOAPEngine.callStack[x].running != true) {
				// debug("novo stack terá processo " + x);
				arTmp.push(SOAPEngine.callStack[x]);
			}
		}
		SOAPEngine.callStack = arTmp;
	}

	SOAPEngine.makeCallRun = function(call) {
	
		var runningCall = null;
		var bout = false;
		// debug("pedido para rodar " + call.action + " vou checar se pode");
		for(x=0; x<SOAPEngine.callStack.length; x++) {
			// debug("checando call " + x);
			if (SOAPEngine.callStack[x].running == true)
			{
				// debug("call " + x + " é a que está rodando");
				runningCall = SOAPEngine.callStack[x];
				break;
			}
		}
		if (runningCall == null)
		{
			// debug("como nada está rodando, vou rodar a " + call.action);
			call.running = true;
			bout = true;
		}
		// debug("pedido para rodar retornando " + bout);
		return bout;
	}

	SOAPEngine.processExtraEntries = function() {

		// debug("chamada pós resposta - procesando entries extras");
		if (SOAPEngine.callStack != null)
		{
			for(x=0; x<SOAPEngine.callStack.length; x++) {
				// debug("checando processo " + x);
				var call = SOAPEngine.callStack[x];
				if (call.running == false)
				{
					// debug("achei um que pode ser rodado call " + call.action);
					if(SOAPEngine.makeCallRun(call)) {
						SOAPEngine.realExecute(call.action, call.params, call.callback);
					}
					break;
				}
			}
		}

	}

	// End of call stack methods

	SOAPEngine.execute = function execute(action, params, callback) {
		
		var call = {action:action, params:params, callback:callback, running:false};
		SOAPEngine.pushCallStack(call);

		if(SOAPEngine.makeCallRun(call)) {
			SOAPEngine.realExecute(call.action, call.params, call.callback);
		}
	}

	SOAPEngine.realExecute = function(action, params, callback) {
		if (SOAPEngine.preHookHandler)
			SOAPEngine.preHookHandler();

		var loadDiv = document.getElementById(SOAPEngine.loadingDivID);
		if (loadDiv == null)
		{
			document.body.innerHTML += SOAPEngine.loadingDiv;
			loadDiv = document.getElementById(SOAPEngine.loadingDivID);
		} else {
			loadDiv.style.display = '';
		}

		if (window.XMLHttpRequest) {
			SOAPEngine.req = new XMLHttpRequest();
		} else if (window.ActiveXObject && !(navigator.userAgent.indexOf('Mac') >= 0 && navigator.userAgent.indexOf("MSIE") >= 0)) {
			SOAPEngine.req = SOAPEngine.newActiveXObject(SOAPEngine.XMLHTTP);
		}

		SOAPEngine.req.onreadystatechange = function() { SOAPEngine.responseCallBack(action, callback); };

		if (SOAPEngine.type == SOAPEngine.SOAP)
		{
			SOAPEngine.req.open("POST", SOAPEngine.soapInfo.url, true);
			SOAPEngine.req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			SOAPEngine.req.setRequestHeader("SOAPAction", SOAPEngine.soapInfo.uri + action);
		
			reqData = SOAPEngine.Serialize(params);
			//alert (reqData);
			SOAPEngine.req.send(SOAPEngine.soapHeader 
							+ '<' + action + ' xmlns="' + SOAPEngine.soapInfo.uri + '">'
							+ reqData + '</' + action + '>' + SOAPEngine.soapTrailer);
		} else {
			SOAPEngine.req.open("GET", SOAPEngine.soapInfo.url + action, true);
			SOAPEngine.req.send("");
		}
	}

	SOAPEngine.responseCallBack = function(action, callback) {
		
		if (SOAPEngine.req.readyState == 4) {
			try {
				var reply = SOAPEngine.req.responseText;
				if (reply != null && reply != "") {
					if (SOAPEngine.req.status && SOAPEngine.req.status == 200) {

						SOAPEngine.popRunningCallStack();

						var loadDiv = document.getElementById(SOAPEngine.loadingDivID);
						loadDiv.style.display = 'none';

						var data = JSONParser.Parse(SOAPEngine.req.responseXML.documentElement);

						if (SOAPEngine.type == SOAPEngine.SOAP)
						{
							try
							{
								SOAPEngine.data = data['soap:Envelope']['soap:Body'][action + 'Response'][action + 'Result'];
							}
							catch (ex)
							{
								SOAPEngine.data = data['soapenv:Envelope']['soapenv:Body'][action + 'Response'][action + 'Return'];
							}
						} else {
							SOAPEngine.data = data;
						}

						if (callback)
							callback(SOAPEngine.data);

						if (SOAPEngine.postHookHandler)
							SOAPEngine.postHookHandler();

						SOAPEngine.processExtraEntries();

					} else {
						SOAPEngine.dealError(reply);
					}
				} else {
//					SOAPEngine.dealError('No data received from server');
				}
			} catch (ex) {
				SOAPEngine.dealError(ex);
			}
		}
	}

	SOAPEngine.dealError = function(msg) {
		SOAPEngine.popRunningCallStack();
		document.getElementById(SOAPEngine.loadingDivID).style.display = 'none';
		if (SOAPEngine.errorHookHandler)
			SOAPEngine.errorHookHandler(msg);
		SOAPEngine.processExtraEntries();		
	}

	SOAPEngine.newActiveXObject = function(axarray) {
		var returnValue;
		for (var i = 0; i < axarray.length; i++) {
			try {
				returnValue = new ActiveXObject(axarray[i]);
				break;
			} catch (ex) {}
		}
		return returnValue;
	}

	SOAPEngine.Serialize = function(obj, xml, parentname) {
		if (!xml)
			xml = '';
		for (o in obj) {
			if (typeof(obj[o]) == 'object') {
				var name = o;
				if (!Number(name) && name!=0) {
					xml += '<' + name + '>';
					xml = SOAPEngine.Serialize(obj[o], xml, name);
					xml += '</' + name + '>\n';
				} else {
					if(name!=0) xml += '<' + parentname + '>';
					xml = SOAPEngine.Serialize(obj[o], xml, parentname);
					if(name!=obj.length-1) xml += '</' + parentname + '>\n';
				}
			} else {
				xml += '<' + o + '>';
				xml += obj[o];
				xml += '</' + o + '>\n';
			}
		}
		return xml;
	}
