iCM = function() {
	/* === Private Area === */
	var transactionId = 0;
	var pollInterval = 50;
	var active = {};
	
	// Fetch the appropriate XML Http Object for the current browser
	function GetAjaxObject(isrpc, targetid) {
		var obj, xmlhttp;
		if (window.XMLHttpRequest) {
			xmlhttp = new XMLHttpRequest();
			obj = { connection: xmlhttp, transaction: ++transactionId, rpc: isrpc, target: targetid };
		} else if (window.ActiveXObject) {
			try 
			{
				xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
				obj = { connection: xmlhttp, transaction: ++transactionId, rpc: isrpc, target: targetid };
			}
			catch(err) {
			}
		}
		return obj;
	}
	
	// Try and retrieve where the page was launched from
	function GetURL(arguments, isget) { 
		var regex = /(.*)\/.*?$/;
		var loc = location.href.match(regex)[1] + '/';
		if (arguments.url)
		{
			loc += arguments.url;
			if (isget) {
				loc += '?' + arguments.method;
				for (key in arguments.params) {
					loc += '&' + key + '=' + encodeURI(arguments.params[key]);
				}
			}
		}
		return loc;
	} 
	
	function HandleResponse(obj, state) {
		var httpStatus = -1;
		if (state) {
			// Get status
			try {
				httpStatus = obj.connection.status && obj.connection.status != 0 ? obj.connection.status : 20000;
			} catch(err) {
			}
			
			if (httpStatus >= 200 && httpStatus < 300) {
				// Successful
				response = {}; 
				responseOk = false;
				if (obj.connection.responseText && obj.connection.responseText.length > 0) {
					response = Ext.util.JSON.decode(obj.connection.responseText);
					responseOk = true;
				}
				if (obj.rpc) {
					// RPC call returned
					if (state.ok) {
						state.ok(obj.target, response);
					}
				} else {
					// Ajax call returned
					if (responseOk) {
						for (respidx=0;respidx<response.length;respidx++) {
							area = response[respidx];
							for(key in area) {
								areaId = key;
								areaElem = Ext.get(areaId);
								if (areaElem) {
									areaElem.dom.innerHTML = area[key];
									RewriteFrameLinks(areaElem.dom);
								}
								if (state.ok) {
									state.ok(key, area[key]);
								}
							}
						}
					}
				}
			} else {
				// Failed
				if (state.fail) {
					state.fail(httpStatus, obj);
				}
			}
		}
	}

	// Make an AJAX Call
	function MakeCall(method, target, url, content, okcallback, failcallback, rpccall) {
		var stateMgmt = { ok: okcallback, fail: failcallback, timeout: null };
		var obj = GetAjaxObject(rpccall, target);
		if (obj) {
			// Open connection
			obj.connection.open(method, url, true);
			
			// Set up headers
			if (rpccall) {
				obj.connection.setRequestHeader("Content-Type", "application/json");
				obj.connection.setRequestHeader("Accept", "application/json");
				if (method == "POST") {
					content = Ext.util.JSON.encode(content);
					obj.connection.setRequestHeader("Content-Length", content.length);
				}
			}
			else 
			{
				if (method == "POST") {
					obj.connection.setRequestHeader("Content-Length", content.length);
					obj.connection.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
					obj.connection.setRequestHeader("Connection", "close");
				}
			}
			
			// Send content
			PollActiveRequest(obj, stateMgmt);
			obj.connection.send(content);

			// Return object			
			return obj;
		} else return null;
	}
	
	// Poll an Active Request for status changes
	function PollActiveRequest(obj, statemgmt) {
		active[obj.transaction] = window.setInterval(
			function () {
				if (obj.connection && obj.connection.readyState == 4) {
					window.clearInterval(active[obj.transaction]);
					delete active[obj.transaction];
					
					HandleResponse(obj, statemgmt);
				}
			}
			, pollInterval
		);
	}

	function RewriteFrameLinks(element) {
		var links = element.getElementsByTagName('a');
		for (ilink=0;ilink<links.length;ilink++) {
			var link = links[ilink];
			if (link.getAttribute("href") && (link.getAttribute("rel") == "external") || link.getAttribute("rel") == "parent") {
				link.target = "_top";
			}
		}
	}

	/* === Public Area === */
	return {
		// Ascertain if cookie exists
		CookieExists: function(name) {
			var nameEQ = name + "=";
			var cookies = document.cookie.split(';');
			for (var cookieidx=0; cookieidx<cookies.length;cookieidx++) {
				var cookie = cookies[cookieidx];
				if (cookie.indexOf(nameEQ) >= 0) return true;
			}
			return false;
		},
		
		// Make an AJAX Get Request
		Get: function(request, okcallback, failcallback) {
			url = request.url + '?__ajax=';
			for (i=0;i<request.target.length;i++) {
				if (i>0) url += ',';
				url += request.target[i];
			}
			for (var key in request.arguments) {
				if (request.arguments[key]) {
					url += '&' + key + '=' + escape(request.arguments[key]);
				}
			}
			this.MakeGetCall(request.target, url, okcallback, failcallback);
		},
		
		// Make an AJAX post request
		FormPostRequest: function(targetId, url, form, okcallback, failcallback) {
			params = "";
			inputs = Ext.get(targetId).query('input');
			for (i=0; i<inputs.length; i++) {
				if (params != '')
					params += '&';
				params += inputs[i].name + '=' + escape(encodeURI(inputs[i].value));
			}
			this.MakePostCall(targetId, url, params, okcallback, failcallback);
		},

		// Set a cookie's value
		SetCookie: function(name, value, days, hours, minutes) {
			var expire = new Date();
			var today = new Date();
			var ticks = (minutes * 60 * 1000) +
									(hours * 60 * 60 * 1000) +
									(days * 24 * 60 * 60 * 1000);
			expire.setTime(today.getTime() + ticks);
			document.cookie = name + '=' + escape(value) + ';expires=' + expire.toGMTString();
		},

		// Make an AJAX call via the 'get' mechanism
		MakeGetCall: function(targets, url, okcallback, failcallback) {
			MakeCall("GET", targets, url, null, okcallback, failcallback, false);
		},
		
		MakePostCall: function(targets, url, params, okcallback, failcallback) {
			MakeCall("POST", targets, url, params, okcallback, failcallback, false);
		},

		// Make an RPC Call via the 'get' mechanism
		MakeGetRPCCall: function(arguments, okcallback, failcallback, targets) {
			url = GetURL(arguments, true);
			MakeCall("GET", targets, url, null, okcallback, failcallback, true);
		},

		// Make an RPC Call via the 'post' mechanism
		MakePostRPCCall: function(arguments, okcallback, failcallback, targets) {
			url = GetURL(arguments, false);
			MakeCall("POST", targets, url, arguments, okcallback, failcallback, true);
		},
		
		// Prepare a new request. Request will contain target areas to update and arguments for call
		NewRequest: function(target) {
			req = {};
			req.target = [];
			req.target.push(target);
			req.arguments = {};
			
			return this.SetHREF(req, location.href);
		},

		// Populate request with contents of given href		
		SetHREF: function(request, href) {
			idx = href.indexOf('?');
			if (idx > 0) {
				// Take everything after the '?' as arguments to the call
				href = href.substring(idx + 1);
				args = href.split('&');
				for (iarg=0;iarg<args.length;iarg++) {
					var arg = args[iarg];
					if (arg.indexOf('=') > 0) {
						argparts = arg.split('=');
						argkey = argparts[0];
						argval = argparts[1];
						request.arguments[argkey] = argval;
					}
				}
				
				// Everything before the '?' is the destination Url
				request.url = location.href.substring(0, idx);
			}
			return request;
		}
	}
}();

iCMRPC = function()
{
	return {
	}
}();

iCM.RPC = iCMRPC;
