/* Remoting.js
 *
 * Author: Jonas Jonsson (fatbrain@sphere.se)
 * Copyright (C) 2003-2004 Jonas Jonsson
 * All Rights Reserved 2003-2004
 */

function emptyFunction() {}

function IsArray(o) { return o != null && o.constructor == Array; }

function JavaScript()
{
	this.IsUndefined = function(o) { return typeof(o) == "undefined"; }
	this.HasProperty = function(propertyList, name)
	{
		return typeof(propertyList[name]) != "undefined";
	}
	this.GetProperty = function(propertyList, name, defaultValue)
	{
		if(!propertyList || typeof(propertyList[name]) == "undefined") return defaultValue;
		return propertyList[name];
	}

	/* Unicode Functions */
	function urlEncode(n)
	{
		return "%" + (n <= 15?"0"+n.toString(16):n.toString(16));
	}

	function createCache()
	{
		var utf8Cache = new Object();
		
		for(var c = 0; c < 255; c++)
		{
			if(c >= 0 && c <= 127)
				utf8Cache[c] = urlEncode(c); //(c < 48 || (c > 57 && c < 65) || (c > 90 && c < 97) || c > 122)?urlEncode(c):String.fromCharCode(c);
			else if(c >= 128 && c <= 191)
				utf8Cache[c] = "%c2" + urlEncode(c);
			else if(c >= 192 && c <= 255)
				utf8Cache[c] = "%c3" + urlEncode(c - 0x40);
		}
		
		return utf8Cache;
	}

	var utf8Cache = createCache();

	function char2Utf8(unicodeCharCode)
	{
		var utf8Char = "";
		var c = unicodeCharCode;
		var q = c <= 0x7f?0:c <= 0x7ff?1:c <= 0xffff?2:c <= 0x1fffff?3:c <= 0x3ffffff?4:5;
		var m = [ 0x0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc ][q];
		while(q-- > 0)
		{
			var n = 0x80 | (c & 0x3f);
			utf8Char = urlEncode(n) + utf8Char;
			c >>= 6;
		}
		utf8Char = urlEncode(m | c) + utf8Char;
		
		if(!utf8Cache[unicodeCharCode]) utf8Cache[unicodeCharCode] = utf8Char;
		return utf8Char;
	}

	function Unicode2Utf8(unicodeString)
	{
		if(!unicodeString) return "";
		
		var utf8String = "";
		
		for(var i = 0, c = unicodeString.charCodeAt(0); i < unicodeString.length; c = unicodeString.charCodeAt(++i))
		{
			utf8String += utf8Cache[c] || char2Utf8(c);
		}

		return utf8String;
	}

	this.Unicode2Utf8 = function(unicodeString)
	{
		return Unicode2Utf8(unicodeString);
	}
	
	this.createQueryString = function(paramList)
	{
		if(!paramList || typeof(paramList) != "object")
			return "";
			
		var parameterString = "";
		var oEmpty = {};
		for(var p in paramList)
		{
			if(!oEmpty[p])
			{
				var sName = js.Unicode2Utf8(p);
				var oValue = paramList[p];
				var sValue = "";
				
				if(IsArray(oValue))
				{
					for(var i = 0; i < oValue.length; i++)
						sValue += (sValue.length?"&":"") + sName + "=" + js.Unicode2Utf8(oValue[i].toString());
				}
				else if(oValue != null)
					sValue = sName + "=" + js.Unicode2Utf8(oValue.toString());
				
				parameterString += (parameterString?"&":"") + sValue;
			}
		}

		return parameterString;
	}
	
	this.deserializeJson = function(jsonString)
	{
		eval("var oJson = " + jsonString);
		return oJson;
	}
}

function getParam(paramList, name, defaultValue)
{
	if(!paramList || typeof(paramList) != "object" || typeof(paramList[name]) == "undefined") return defaultValue;
	return paramList[name];
}

// Extending intrinsic object Array //
Array.prototype.AddRange = function(range)
{
	for(var i = 0; i < range.length; i++)
		this.Add(range[i]);
}
Array.prototype.Add = function(obj)
{
	this.Insert(this.length, obj);
}
Array.prototype.Insert = function(n, obj)
{
	for(var i = this.length; i > n; i--)
		this[i] = this[i - 1];
	this[n] = obj;
}
Array.prototype.InsertRange = function(n, range)
{
	for(var i = 0; i < range.length; i++)
		this.Insert(n+i, range[i]);
}
Array.prototype.Remove = function(obj)
{
	for(var i = 0; i < this.length; i++)
		if(this[i] == obj)
			this.RemoveAt(i);
}
Array.prototype.RemoveAt = function(n)
{
	for(var i = n + 1; i < this.length; i++)
		this[i - 1] = this[i];
	this.length--;
}
Array.prototype.RemoveRange = function(n, count)
{
	while(count-- > 0)
		this.RemoveAt(n);
}
Array.prototype.IndexOf = function(obj, n)
{
	n = n || 0;
	for(var i = n; i < this.length; i++)
		if(this[i] == obj)
			return i;
	
	return -1;
}
Array.prototype.Contains = function(obj)
{
	return this.IndexOf(obj) > -1;
}
Array.prototype.Enqueue = function(obj)
{
	this.Add(obj);
}
Array.prototype.Dequeue = function()
{
	if(this.length > 0)
	{
		var obj = this[0];
		this.RemoveAt(0);
		return obj;
	}
	
	return null;
}

// Extending intrinsic object String //
String.prototype.IndexOf = function(text, n)
{
	return this.indexOf(text, n?n:0);
}
String.prototype.EndsWith = function(text)
{
	return this.IndexOf(text, this.length - text.length) > -1;
}
String.prototype.StartsWith = function(text)
{
	return this.IndexOf(text) == 0;
}
String.prototype.Substring = function(nStart, nLength)
{
	nLength = nLength || (this.length - nStart);
	return this.substr(nStart, nLength);
}
String.prototype.Insert = function(n, text)
{
	return this.Substring(0, n) + text + this.Substring(n);
}
String.prototype.Remove = function(n, count)
{
	return this.Substring(0, n) + this.Substring(n + count);
}
String.prototype.Split = function(c)
{
	return this.split(c);
}
String.prototype.ToLower = function()
{
	return this.toLowerCase();
}
String.prototype.ToUpper = function()
{
	return this.toUpperCase();
}
String.prototype.TrimStart = function()
{
	return this.replace(/$\s+/, "");
}
String.prototype.TrimEnd = function()
{
	return this.replace(/\s+^/, "");
}
String.prototype.Trim = function()
{
	return this.TrimStart().TrimEnd();
}
String.prototype.Append = function(text)
{
	return this + text;
}

// Extending intrinsic object Number //
Number.prototype.Unit = function(unit)
{
	return this.toString() + unit;
}

/*
Function.Call = function(fn, args)
{
	switch(args.length)
	{
		case 0: fn(); break;
		case 1: fn(args[0]); break;
		case 2: fn(args[0], args[1]); break;
		case 3: fn(args[0], args[1], args[2]); break;
		case 4: fn(args[0], args[1], args[2], args[3]); break;
		case 5: fn(args[0], args[1], args[2], args[3], args[4]); break;
		case 6: fn(args[0], args[1], args[2], args[3], args[4], args[5]); break;
		case 7: fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); break;
		case 8: fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break;
		case 9: fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); break;
	}
}

Object.prototype.Constructor = function()
{
	var cArray = arguments;
	var args = arguments.callee.caller.arguments;
	for(var i = 0; i < cArray.length; i++)
		if(args.length == cArray[i][document.all?"length":"arity"])
		{
			Function.Call(cArray[i], args);
		}
}

function DateTime()
{
	this.Constructor(function(a) { alert(this.a); });
}
DateTime.prototype = new Date();
DateTime.prototype.constructor = Date.prototype.constructor;

var a = new DateTime(121212);
*/
/* Extending Mozilla */
if(!document.all)
{
	function __attachEvent(domObject, sEvent, fpNotify)
	{
		var type = sEvent.StartsWith("on")?sEvent.Substring(2):sEvent;
		var listener = fpNotify;
		var useCapture = false;
		
		domObject.addEventListener(type, listener, useCapture);
	}
	
	function __detachEvent(domObject, sEvent, fpNotify)
	{
		var type = sEvent.StartsWith("on")?sEvent.Substring(2):sEvent;
		var listener = fpNotify;
		var useCapture = false;
		
		domObject.removeEventListener(type, listener, useCapture);
	}
	
	Window.prototype.attachEvent = function(sEvent, fpNotify)
	{
		__attachEvent(this, sEvent, fpNotify);
	}
	Node.prototype.attachEvent = function(sEvent, fpNotify)
	{
		__attachEvent(this, sEvent, fpNotify);
	}
	Window.prototype.detachEvent = function(sEvent, fpNotify)
	{
		__detachEvent(this, sEvent, fpNotify);
	}
	Node.prototype.detachEvent = function(sEvent, fpNotify)
	{
		__detachEvent(this, sEvent, fpNotify);
	}
	
	XMLDocument.prototype.__defineGetter__("xml", function() { return (new XMLSerializer()).serializeToString(this); });
	XMLDocument.prototype.loadXML = function(xml)
	{
		var oldxml = this.xml;
		var doc = (new DOMParser()).parseFromString(xml, "text/xml");
		
		while(this.hasChildNodes())
			this.removeChild(this.firstChild);
		
		for(var i = 0, nodes = doc.childNodes; i < nodes.length; i++)
			this.appendChild(this.importNode(nodes[i], true));
			
		return oldxml;
	}
	XMLDocument.prototype.load = function(Url)
	{
		var oxmlhttp = getXmlHttpRequest();
		oxmlhttp.open("GET", Url, false);
		oxmlhttp.send(null);

		var doc = oxmlhttp.responseXML;
		for(var i = 0, nodes = doc.childNodes; i < nodes.length; i++)
			this.appendChild(this.importNode(nodes[i], true));
	}
}

// Extending intrinsic object Object //
Object.prototype.EventHandler = function(whatEvent)
{
	function eventHandler(object, whatEvent)
	{
		var events = [];
	
		object[whatEvent] = function()
		{
			if(arguments.length == 1 && typeof(arguments[0]) == "function")
			{
				events.Add(arguments[0]);
			}
			else
			{
				object[whatEvent].Invoke(arguments[0]);
			}
		}
		object[whatEvent].Invoke = function(eventArgs)
		{
			for(var i = 0; i < events.length; i++)
				events[i](eventArgs);
		}
		object[whatEvent].Clear = function() { events = []; }
	}
	new eventHandler(this, whatEvent);
}

Object.toSource = function(graph)
{
	var emptyObject = new Object();
	var processed = new Object();
	
	function function2String(v)
	{
		return "function() {}";
	}
	
	function string2String(v)
	{
		return "\"" + v.replace(/\\/g, "\\\\").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\f/g, "\\f").replace(/\t/g, "\\t").replace(/"/g, "\\\"").replace(/'/g, "\\'") + "\"";
	}
	
	function object2String(graph)
	{
		if(graph in processed) return;
		processed[graph] = true;
		
		var s = "{";
		
		for(var p in graph)
		{
			if(p in emptyObject)
				continue;
				
			var v = graph[p];

			s += (s.length > 1?",":"") + p + ":";
			
			if(v == null)
				s += "null";
			else if(typeof(v) == "string")
				s += string2String(v);
			else if(typeof(v) == "number")
				s += v.toString();
			else if(typeof(v) == "function")
				s += function2String(v);
			else if(typeof(v) == "boolean")
				s += v.toString();
			else if(typeof(v) == "object")
			{
				if(v.constructor == Array)
					s += "[]";
				else if(v.constructor == Date)
					s += v.valueOf().toString();
				else if(v.constructor == RegExp)
					s += v.toString();
				else
					s += object2String(v);
			}
		}
		
		s += "}";
		
		return s;
	}
	
	if(typeof(graph) == "object")
		return object2String(graph);
	else if(typeof(graph) == "undefined")
		return "undefined";
}


// Dom object //
function Dom()
{
	var properties = [];
	var events = [];

	/* Properties */
	function getProperty(domObject, sProperty)
	{
		for(var i = 0; i < properties.length; i++)
			if(properties[i].domObject == domObject && properties[i].sProperty == sProperty)
				return properties[i];
				
		return null;
	}
	
	function deleteProperty(domObject, sProperty)
	{
		for(var i = 0; i < properties.length; i++)
			if((!domObject || properties[i].domObject == domObject) && (!sProperty || properties[i].sProperty == sProperty))
			{
				try
				{
					properties[i].domObject[properties[i].sProperty] = properties[i].initialValue;
					properties.RemoveAt(i);
					i--;
				}
				catch(e)
				{
					alert(properties[i].sProperty);
				}
			}
	}
	
	this.getProperty = function(domObject, sProperty)
	{
		return domObject[sProperty];
	}
	
	this.setProperties = function(domObject, propertyValueList)
	{
		for(var p in propertyValueList)
		{
			if(typeof(propertyValueList[p]) == "object")
				this.setProperties(domObject[p], propertyValueList[p]);
			else if(typeof(propertyValueList[p]) == "string")
				this.setProperty(domObject, p, propertyValueList[p]);
		}
	}

	this.setProperty = function(domObject, sProperty, value, initialValue)
	{
		if(!document.all)
		{
			domObject[sProperty] = value;
			return;
		}
		
		if(domObject && sProperty)
		{
			var prop = getProperty(domObject, sProperty);
			if(prop == null)
				properties.Add({ domObject: domObject, sProperty: sProperty, initialValue: js.IsUndefined(initialValue)?domObject[sProperty]:initialValue });
			
			domObject[sProperty] = value;
		}
	}
	
	this.deleteProperty = function(domObject, sProperty)
	{
		if(!document.all) return;
		deleteProperty(domObject, sProperty);
	}
	
	this.removeChild = function(parentObject, childObject)
	{
		deleteProperty(childObject);
		return parentObject.removeChild(childObject);
	}
	
	/* Events */
	this.attachEvent = function(domObject, sEvent, fpNotify)
	{
		events.Add({ domObject: domObject, sEvent: sEvent, fpNotify: fpNotify });
		try
		{
			domObject.attachEvent(sEvent, fpNotify);
		}
		catch(e)
		{
			alert(sEvent);
		}
	}
	
	this.detachEvent = function(domObject, sEvent, fpNotify)
	{
		for(var i = 0; i < events.length; i++)
		{
			if((!domObject || events[i].domObject == domObject) && (!sEvent || events[i].sEvent == sEvent) && (!fpNotify || events[i].fpNotify == fpNotify))
			{
				events[i].domObject.detachEvent(events[i].sEvent, events[i].fpNotify);
				events[i].domObject = null;
				events[i].sEvent = null;
				events[i].fpNotify = null;
				events.RemoveAt(i);
				i--;
			}
		}
	}
	
	function wrapEvent(evt)
	{
		var isIE = document.all?true:false;
		this.target = isIE?evt.srcElement:evt.target;
		this.keyCode = evt.keyCode;
		this.ctrlKey = evt.ctrlKey;
		this.shiftKey = evt.shiftKey;
		this.altKey = evt.altKey;
		this.keyString = (this.ctrlKey?"c":"") + (this.shiftKey?"s":"") + (this.altKey?"a":"") + this.keyCode;
		this.clientX = evt.clientX;
		this.clientY = evt.clientY;
		this.relatedTarget = document.all?evt.fromElement:evt.relatedTarget;
		this.screenX = evt.screenX;
		this.screenY = evt.screenY;
		this.layerX = document.all?evt.offsetX:evt.layerX;
		this.layerY = document.all?evt.offsetY:evt.layerY;
		this.type = evt.type;
		this.preventDefault = function()
		{
			if(document.all)
				evt.returnValue = false;
			else
				evt.preventDefault();
		}
		this.cancel = function()
		{
			if(document.all)
				evt.cancelBubble = true;
			else
				evt.stopPropagation();
		}
	}
	
	this.getEvent = function()
	{
		var evt = document.all?window.event:arguments.callee.caller.arguments[0];
		return new wrapEvent(evt);
	}

	this.attachEvent(window, "onunload", function()
	{
		if(document.all)
		{
			dom.deleteProperty();
			dom.detachEvent();
		}
	});
	
	// Various Dom functions //
	this.HasChild = function(parentObject, childObject)
	{
		if(parentObject == childObject)
			return true;
		
		if(parentObject.childNodes.length > 0)
		{
			for(var i = 0; i < parentObject.childNodes.length; i++)
				if(this.HasChild(parentObject.childNodes[i], childObject))
					return true;
		}

		return false;
	}

	this.getX = function(e) { return e.style.position == "absolute"?parseInt(e.style.left):dom.getOffset(e, "Left"); }
	this.getY = function(e) { return e.style.position == "absolute"?parseInt(e.style.top):dom.getOffset(e, "Top"); }
	this.getLoc = function(e) { return new Point(dom.getX(e), dom.getY(e)); }
	this.getOffset = function(e, whatOffset)
	{
		for(var i = 0; e != null; i += e["offset" + whatOffset], e = e.offsetParent);
		return i;
	}
	this.getWidth = function(e) { return e.offsetWidth + (document.all?(new Number(e.style.marginLeft) + new Number(e.style.marginRight)):0); }
	this.getHeight = function(e) { return e.offsetHeight + (document.all?(new Number(e.style.marginTop) + new Number(e.style.marginBottom)):0); }
}

function Point(x, y)
{
	this.x = x;
	this.y = y;
	this.MoveBy = function(point) { this.x += point.x; this.y += point.y; return this; }
	this.toString = function() { return this.x + "," + this.y; }
}

var dom = new Dom();
var js = new JavaScript();