	onLoad.add(xps_init);
	
	var xps				= null;
	var xps_poolSize	= 10;
	var xps_poolsUsed	= 0;
	var xps_maxUsed	 	= 0;
	var xps_pool		= new Array(xps_poolSize);
	var XPS_TIMEOUT		= 60000;
	var xps_base		= "Control";
	var gw_ajax			= false;
	var gw_ajaxTry		= false;
	function xps_init() {
		var i;
		for (i=0; i<xps_poolSize; i++) {
			xps_pool[i] = new Object();
			xps_pool[i]["id"]			= i;
			xps_pool[i]["req"]			= null;
			xps_pool[i]["isAvail"]		= 0;
			xps_pool[i]["iframe"]		= addIframe("xps_pool_"+i);
			xps_pool[i]["url"]			= null;
			xps_pool[i]["callback"]		= null;
			xps_pool[i]["time"]			= 0;
			xps_pool[i]["timeout"]		= null;
			xps_pool[i]["retryCount"]	= 0;
		}
		xps	= XPS.getService(gw_mainXpsFunctions);
		if (typeof(xpsFunctions)!="undefined") xps.addFunctions(xpsFunctions);
	}
	
	function xps_findXpsQueue() {
		var i;
		for (i=0; i<xps_poolSize; i++) {
			if (xps_pool[i].isAvail == 0) {
				xps_pool[i].isAvail++;
				if (xps_pool[i].isAvail == 1) {
					xps_poolsUsed++;
					if (xps_maxUsed < xps_poolsUsed) xps_maxUsed = xps_poolsUsed;
					xps_pool[i].timeout = setInterval("xps_timeout("+i+")", XPS_TIMEOUT);
					return xps_pool[i];
				}
				alert("XPS ERROR CONDITION");	//in theory this can never happen (assuming we do not have a bug)
				xps_pool[i].isAvail--;
			}
		}
		
		error("Out of queues required to communicate with the server");
		return null;
	}
	
	function xps_timeout(index) {
		var q			= xps_pool[index];
		if (q.url==null) {
			clearInterval(q.timeout);
			return;
		}
		log("xps timeout "+index+": "+q.url);
		var req	= q.req;
		if (req != null)
		{
			req.abort();
			q.req	= null;
		}
		
		if (gw_ajax==true)
		{
			doAjaxRequest(q);
		}
		else
		{
			q.iframe.src	= q.url;
		}
	}
	
	function doAjaxRequest(q)
    {
    	var url			= q.url;
    	try
    	{
			if (XMLHttpRequest)
			{
				// Non-IE browsers
				q.req = new XMLHttpRequest();
			}
			else if (ActiveXObject)
			{
				// IE
				q.req = new ActiveXObject("Microsoft.XMLHTTP");
			}
			if (isdefined(q.req) && q.req!=null)
			{
				q.req.open("GET", url+"&_ajax=true", true);
				q.req.onreadystatechange=onAjaxResponse;
				q.req.send("");
			}
		}
		catch(err)
		{
			//gw_ajax	= false;
		}
  	}

	function onAjaxResponse()
   	{
		for (i=0; i<xps_poolSize; i++)
		 {
		 	var req	= xps_pool[i].req;
			if (req != null)
			{
				if (req.readyState == 4)
				{
					// Complete
					if (req.status == 200)
					{
						// OK response
				   		try
				   		{
				   			req.onreadystatechange=function(){};
				   			
					   		gw_ajax		= true;
					   		var xml 	= req.responseXML.documentElement;
					   		var	index	= parseInt(xml.getElementsByTagName('jsId')[0].firstChild.data);
					   		var r		= new Array();
					
							var elements	= xml.getElementsByTagName("object");   			
					        for (var i=0; i<elements.length; i++)
					        {
								var t = elements[i];
								if (t != null)
								{
									var obj	= new Object();
									for (j=0;j<t.childNodes.length;j++)
									{
										var node	= t.childNodes[j];
										if (node.nodeType==1)
										{
											//alert(node.nodeType +":"+node.nodeName+":"+node.firstChild.nodeValue);
											obj[node.nodeName]=node.firstChild.nodeValue;
										}
									}
									r.push(obj);
								}
							}
							xps_callback(index,r);
						}
						catch(err)
						{
							//log("ERROR onAjaxResponse: "+err.description);
						}
			      	}
			      	req.abort();
			    }
		    }
		}
	}
	function xps_callback(index, response) {
		//yeah, server executed the request and here is the response
		var poolObject			= xps_pool[index];
		var callback			= poolObject.callback;
		clearInterval(poolObject.timeout);
		poolObject.timeout		= null;
		poolObject.callback		= null;
		poolObject.url			= null;
		poolObject.req			= null;
		poolObject.isAvail--;		// return it as available
		xps_poolsUsed--;

		try {
			if (typeof(response.error)!="undefined") {
				error(response.error, true);
				return;
			}
			
			if (typeof(response.complain)!="undefined") {
				complain(response.complain, true);
			}
			
			if (callback!=null) {
				if (response.complain!=null && response.complain!="undefined") {
					complain(response.complain);
				}
				callback(response);
			}
		} catch(err) {
			error(err.name+" "+err.message+" ...when calling\r\n"+callback.toString());
		}
	}

	XPS = {
		getService : function(functions) {
			var ARGS = "_Args";
			this.onerror = null;
			this.addFunctions = function(info){
				var i=0;
				for(i=0;i<info.length;i++) {
					this.addFunction(info[i]);
				}
			}
			this.addFunction = function(info){
				var name, alias;
				if (info==null || typeof(info)!="object" || info.length<1) { //array
					error("addFunction expects an array with <functionName, paramName1, paramName2, etc.>; check for trailing comma in xpsFunctions definition");
					return;
				}
				alias			= info[0];
				var pos			= alias.indexOf('.');
				var className	= "";
				if (pos!=-1) {
					//includes the package/class name
					className	= alias.substring(0, pos);
					var method	= alias.substring(pos+1);
					if (typeof(this[className])=="undefined") this[className] = new Object();
					this[className][method] = new Function('var args = new Array(), i;for(i=0;i<arguments.length;i++){args.push(arguments[i]);};	xps.call("' + alias + '", args, false); return null;');
					this[className][method+"_debug"] = new Function('var args = new Array(), i;for(i=0;i<arguments.length;i++){args.push(arguments[i]);};	xps.call("' + alias + '", args, true); return null;');
					this[className][method+ARGS] = info;
				} else {
					this[alias] = new Function('var args = new Array(), i;for(i=0;i<arguments.length;i++){args.push(arguments[i]);};	xps.call("' + alias + '", args, false); return null;');
					this[alias+"_debug"] = new Function('var args = new Array(), i;for(i=0;i<arguments.length;i++){args.push(arguments[i]);};	xps.call("' + alias + '", args, true); return null;');
					this[alias+ARGS] = info;
				}
				//alert("Create "+alias+ARGS);
			}
			this.setBase = function(newBase,useAjax){
				xps_base		= newBase;
				gw_ajaxTry		= useAjax;
			}
			this.call = function(alias, args, debugalert){
				var i=0;
				var params = "";
				callback = args.pop();
				var pos			= alias.indexOf('.');
				var className	= "";
				var method		= alias;
				var paramNames	= null;
				if (pos!=-1) {
					//includes the package/class name
					className	= alias.substring(0, pos);
					method		= alias.substring(pos+1);
					paramNames	= this[className][method+ARGS];
				} else {
					paramNames	= this[alias+ARGS];
				}
				
				var i=0, j=1; //skip XPSname/alias
				var paramName;
				do {
					if (j!=paramNames.length) {
						paramName = paramNames[j++];
						var value = args[i++];

						if (value==null) value = "";
						if (typeof(value)=="object") { //array
							if (typeof(value.length)=="undefined") {
								//real object
								var len = 0;
								var aparams = "";
								for (var tt in value) {
									len++;
									aparams += tt + ":" + encodeURIComponent(value[tt]) + PARAM_STRING_SEPARATOR;
								}
								params += "&" + paramName + "=" + encodeURIComponent(PARAM_ARRAY_INDICATOR + "object:"+len + PARAM_ARRAY_INDICATOR + aparams);
							} else {
								//array and not real object
								var aparams = PARAM_ARRAY_INDICATOR + value.length + PARAM_ARRAY_INDICATOR;
								var tt;
								for (tt=0; tt<value.length; tt++) {
									aparams += encodeURIComponent(value[tt]) + PARAM_STRING_SEPARATOR;
								}
								params += "&" + paramName + "=" + encodeURIComponent(aparams);
							}
						} else {
							params += "&" + paramName + "=" + encodeURIComponent(value);
						}
					}

					if (i == args.length && j==paramNames.length) break;

					if (j==paramNames.length && i != args.length) {
						error("Too many parameters ("+paramNames.length+") in function call compared to definition: "+alias+"("+args+") : "+params);
						return;
					}
				} while (true);

				if (callback!=null && typeof(callback)!="Object" && callback.toString().startsWith("file:")) {	//special condition - create new iframe for file download
					var url	= getRootPath()+xps_base+"?command="+alias+"&rkey="+getTime()+params;
					//dialog(url, name, w, h, x, y, resizeable, scrollbars, menubar, location, toolbar, personalbar, status)
					var obj = new Object();
					obj.url		= url;
					obj.name	= callback.substring(5);
					modalDialog(getRootPath()+"include/download.jsp", obj, 300, 125);
				} else {
					// find available xps_pool object
					var q = xps_findXpsQueue();
					var app = "_GWOV_";
					try {
						if (isdefined(applicationId)) app = applicationId;
					} catch(err) {
						// do nothing; keep default value
					}
					q.url 			= getRootPath()+xps_base+getHttpSessionId()+"?command="+alias+"&_js_reponse="+q.id+"&rkey="+getTime()+"&_gw_app="+app+params;	//_js_reponse is special
					q.callback		= callback;
					q.time			= getTime();
					q.retryCount	= 0;
					
					//log("xps->"+alias+":"+params);
					//first request is sent 2 ways to see which works; prefers ajax
					if (gw_ajax==true || gw_ajaxTry)
					{
						gw_ajaxTry	= false;
						doAjaxRequest(q);
					}
					else
					{
						q.iframe.src = q.url;
					}
					
				}
			}
			
			// actual code called when initializing xps
			if (functions) this.addFunctions(functions);
			return this;
		}
	};
