
	try { now_loading('RPC support.'); } catch (e) {}

	var __vispid;
	var __username;
	var __token;

	function initialise_RPC(vispid, username, token)
	{
		__vispid   = vispid;
		__username = username;
		__token    = token;
	}

	function switch_visp(vispid)
	{
		__vispid   = vispid;
	}

	function get_selected_visp()
	{
		return __vispid;
	}

	// +------------------------------+
	// | Class implementing a calling |
	// | context for remote procedure |
	// | call instances.              |
	// +------------------------------+
	function biData()
	{
		this.inp   = new Object();
		this.out   = 0;
		this.error    = '';

		return this;
	}

	biData.prototype.reset = function ()
	{
		this.inp   = '';
		this.out   = 0;
		this.error = '';
	};

	{
		var __xml;
		function return_XMLHTTPRequest()
		{
			//return (__xml != undefined)
			//	? __xml
			//	: (__xml = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));

			return window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
		}
	}

	// +------------------------------+
	// | Class: Remote procedure call |
	// +------------------------------+
	function RPC(server, path, type, user, pass)
	{
		if (server == undefined)
			server = document.location.toString().replace(/^\s*http:\/\//, '').replace(/\/.*$/, '');

		// validate & set server
		this.service = (new String(server)).replace(/^\s*http:\/\//i, '');
		this.service = 'http://'+this.service;

		// validate & set path
		var path = (new String(path)).replace(/^\/+/, '');
		this.service += '/'+path;

		this.user = user ? user : null;
		this.pass = pass;

		// determine service type
		this.type = (!!type) ? (new String(type)).toLowerCase() : 'byarg';
		this.type = (this.type == 'byarg') ? 'byarg' : 'byname';

		// instantiate XMLHttpRequest object, barf on failure
		this.xml = return_XMLHTTPRequest();
		if (!this.xml) { alert('bad browser!'); return null; }
	
		return this;
	}

	function dim()
	{
		//document.getElementById("stop_the_clicking").style.display = "block";
		//document.getElementById("pleasewait_wrapper").style.display = "block";
	}

	function light()
	{
		//document.getElementById("stop_the_clicking").style.display = "none";
		//document.getElementById("pleasewait_wrapper").style.display = "none";
	}

	RPC.prototype.fire = function (func, data) {

		setTimeout(dim, 1);

		// clean function name
		func = (new String(func)).replace(/\s*/g, '');
		this.func = func;

		// build function url
		var url = (this.type == 'byarg')
			? this.service+func+'&_server_rpc=yes&username='+__username+'&token='+__token+'&vispid='+__vispid
			: this.service.replace(/\?/, func)+'?_server_rpc=yes&username='+__username+'&token='+__token+'&vispid='+__vispid;

		// open the request
		if (this.user == null) { 
			this.xml.open('POST', url, false);
		} else {
			this.xml.open('POST', url, false, this.user, this.pass);
		}
	
		// send the input data & close request
		this.xml.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

		var tries = 0;
		while (true)
		{
			this.xml.send('data='+escape(serialise(data.inp)));

			// check for errors
			if (this.xml.status != 200) { 
				data.error = 'function '+func+'(), response: '+this.xml.statusText; 
				setTimeout(light, 1);
				return false; 
			}
			try {
				if (this.xml.responseText != '') break;
			} catch(e) {
			}

			tries++;
			if (tries > 1) {
				data.error = 'RPC failed to return any data after multiple attempts.';
				setTimeout(light, 1);
				return false;
			}
		}
		
		// set output values
		data.error = '';
		try {
			data.out = eval(unserialise(this.xml.responseText));
			setTimeout(light, 1);
			if (data.out['__auth'] == 'ok') return true;
			throw badauth(data.out['__auth']);
		} catch (e) {
			data.error = "RPC_ERROR ("+func+"): ["+e+"]";
			alert(data.error);
			return false;
		}
		
		setTimeout(light, 1);
		data.error = 'Unknown error';
		return true;
	};
	
	function make_afire_cb(_func, _x, _nld, _data)
	{
		var x = _x;
		var func = _func;
		var nld = _nld;
		var data = _data;

		return function() { 
			try {
				if (x.xml.readyState != 4) return;
				
				// set output values
				data.error = '';

				// check for errors
				try {
					if (x.xml.status != 200) { 
						data.error = 'function '+func+'(), response: '+x.xml.statusText; 
						x.callback(data);
						if (!nld) setTimeout(light, 1);
						return; 
					}
					if (x.xml.responseText == '') throw 'RPC not ready';
				} catch(e) {
					if (x.retries > 10) {
						alert('the RPC failed after 10 retries.');
						return;
					}
					setTimeout(function() {
						x.afire(func, data, x.callback, nld, x);
					}, 200);
					return;
				}

				data.out = eval(unserialise(x.xml.responseText));
				if (data.out['__auth'] == 'ok') {
					x.callback(data);
					if (!nld) setTimeout(light, 1);
					return;
				}
				throw badauth(data.out['__auth']);

			} catch (e) {
				data.error = "RPC_ERROR ("+func+"): ["+e+"]";
				alert(data.error);
			}
		}	
	}
	
	RPC.prototype.afire = function (func, data, callback, _nld, _x) {
		var x = _x ? _x : this;
		var nld = _nld;
		if (!nld) dim();
		
		// set callback
		x.callback = callback;
		x.retries = (x.retries ? 1 : x.retries+1);
		
		// clean function name
		func = (new String(func)).replace(/\s*/g, '');
		x.func = func;

		// build function url
		var url = (x.type == 'byarg')
			? x.service+func+'&_server_rpc=yes&username='+__username+'&token='+__token+'&vispid='+__vispid
			: x.service.replace(/\?/, func)+'?_server_rpc=yes&username='+__username+'&token='+__token+'&vispid='+__vispid;

		// open the request
		if (x.user == null) { 
			x.xml.open('POST', url, true);
		} else {
			x.xml.open('POST', url, true, x.user, x.pass);
		}
	
		// setup the async xml callback
		x.xml.onreadystatechange = make_afire_cb(func, x, nld, data);
		
		// send the input data & close request
		x.xml.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
		x.xml.send('data='+escape(serialise(data.inp)));
	
		return true;
	};

	function badauth(code)
	{
		if (code == 'baduser') 
			return 'You failed to authenticate to MANTIS.';
		if (code == 'badvisp') 
			return 'MANTIS has disallowed the requested action, you are not authorised to access the specified VISP.';
		if (code == 'badentity') 
			return 'MANTIS has disallowed the requested action, the requested specified a customer entity from a VISP which is not currently selected.';
		if (code == 'autherror')
			return 'An error has occured during authentication/authorisation. Please seek support from your MANTIS vendor.';
		return 'MANTIS has disallowed the requested action, for an unspecified reason.';
	}

