/* Copyright (c) 2007 Infoteria Corp USA. All rights reserved. */

var RPCTransportGlobals = {
  version: '1.0.0',
	registry: {},
	inProcessFlags: {},
	processResult: function(id, obj)
	{
		if (id)
		{
			rpcObj = this.registry[id];
			if (rpcObj && !rpcObj.stopped)
			{
				rpcObj.handleResponse(obj);
				RPCTransportGlobals.inProcessFlags[this.id] = false;
			}
		}
	}
}

function RPCTransport(url, options)
{
		this.emptyFunction = function() {};
		this.id = (new Date()).getTime() + '-' + Math.round(Math.random() * 1000);
		this.stopped = false;
		RPCTransportGlobals.registry[this.id] = this;
		
		if (url)
		{
			this.request(url, options);
		}
}

RPCTransport.prototype = {
	setOptions: function(options) 
	{
		this.options = {
      timeout: 5000,
      asynchronous: true
    };

	  for (property in options) 
		{
 			this.options[property] = options[property];
	  }
	},

	request: function(url, options)
	{
		this.stopped = false;
		this.setOptions(options);
		
		var parameters = this.options.parameters || '';
		
		if (parameters.length > 0)
		{
			parameters += '&';
		}
		
		parameters += 'noCache=' + new Date().getTime() + '&callback=RPCTransportGlobals.processResult&__objid=' + this.id;

		try {
			this.url = url;

			if (parameters.length > 0)
				this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;

			if (window.opera) {
				navigator.userAgent.match(/Opera[\/\s]*([0-9\.]+)/);
				if (parseInt(RegExp.$1) >= 9) {
					
					//
					// Opera 9 only
					//   Workaround for the problem of synchronous <script> loading.
					//   1. Fetch response of the url by creating an <img> tag.
					//      It's asynchronous.
					//   2. On img.onerror, then start JSONRPC.
					//      Soon it will return, because the response is cached.
					//
					
					var img = document.createElement('img');
					img.onerror = function(e){
						try {
							this.tag=document.createElement('script');
							this.tag.id = 'rs-' + this.id;
							this.tag.setAttribute('type','text/javascript');
							this.tag.setAttribute('src', this.url);

							var hd=document.getElementsByTagName('head')[0];

							if (this.options.before)
								this.options.before.apply(this, [this]);

							RPCTransportGlobals.inProcessFlags[this.id] = true;
							hd.appendChild(this.tag);

							if (this.options.onFailure)
							{
								this.timeout = setTimeout(RPCTransport.prototype.handleTimeout.bind(this), this.options.timeout);
							}

							if (this.options.asynchronous == false)
							{
							  this.waitForCompletion();
							}
						} catch (e) {
							this.handleException(e)
						}
					}.bind(this);
					img.width = 0;
					img.height = 0;
					img.src = this.url;
					document.body.appendChild(img);
					return
				}
			}
			
			//	
			// Not Opera (and Opera <9)
			//
			
			this.tag=document.createElement('script');
			this.tag.id = 'rs-' + this.id;
			this.tag.setAttribute('type','text/javascript');
			this.tag.setAttribute('src', this.url);

			var hd=document.getElementsByTagName('head')[0];

			if (this.options.before)
				this.options.before.apply(this, [this]);

			RPCTransportGlobals.inProcessFlags[this.id] = true;
			hd.appendChild(this.tag);

			if (this.options.onFailure)
			{
				this.timeout = setTimeout(RPCTransport.prototype.handleTimeout.bind(this), this.options.timeout);
			}

			if (this.options.asynchronous == false)
			{
			  this.waitForCompletion();
			}
			
		} catch (e) {
			this.handleException(e)
		}
	},
	
	abort: function() {
		if (this.timeout)
		{
			clearTimeout(this.timeout);
		}
		
		if (this.tag && this.tag.parentNode)
		{
			this.tag.parentNode.removeChild(this.tag);
		}
		
		this.stopped = true;
	},
	
	handleResponse: function(response) {
		this.abort();
		this.stopped = true;
		this.response = response;
		this.responseObject = response;
		(this.options.onSuccess || this.emptyFunction).apply(this, [this]);
	},
	
	handleTimeout: function() {
		if (!this.stopped)
		{
			this.abort();
			this.notifyFailure();
			RPCTransportGlobals.inProcessFlags[this.id] = false;
		}
	},
	
	handleException: function(exc) {
		this.abort();
		this.exception = exc;
		this.notifyFailure();
		RPCTransportGlobals.inProcessFlags[this.id] = false;
	},
	
	notifyFailure: function() {
		(this.options.onFailure || this.emptyFunction).apply(this, [this]);
	},
	
	waitForCompletion: function() {
	  while (RPCTransportGlobals.inProcessFlags[this.id])
	  {
	    waitABit(waitForCompletion.bind(this), 100);
    }
	},
	
	waitABit: function(f, d) {
	  setTimeout(f, d);
	}
}