String.prototype.trimNL = function()
{
	return this.replace(/^\s*/, "").replace(/\s*$/, "").replace(/\n/g, "");
};

String.prototype.endsWith = function(str, flags)
{
	var re = new RegExp(str + "$", flags);
	return re.test(this);
};

String.prototype.startsWith = function(str, flags)
{
	var re = new RegExp("^" + str, flags);
	return re.test(this);
};

String.prototype.htmlEncode = function()
{
	return this.replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/\"/g, "&quot;").replace(/&/g, "&amp;");
};

String.prototype.htmlDecode = function()
{
	return this.replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, "\"").replace(/&amp;/g, "&");
};

Array.prototype.indexOf = function(x)
{
	for (var i = 0; i < this.length; i++)
	{
		if (this[i] == x)
			return i;
	}
	return -1;
};

String.prototype.trim = function()
{
	return this.replace(/^\s+|\s+$/g, "");
};

String.prototype.ltrim = function()
{
	return this.replace(/^\s+/, "");
};

String.prototype.rtrim = function()
{
	return this.replace(/\s+$/, "");
};

String.prototype.toDate = function()
{
	var YearMonthDayDash = /^(\d{4})-(\d{1,2})-(\d{1,2})$/
	var YearMonthDaySlash = /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/
	var DayMonthYearDash = /^(\d{1,2})-(\d{1,2})-(\d{4})$/
	var DayMonthYearSlash = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/

	var dArr;
	var match;
	var Year;
	var Month;
	var Day;

	if (YearMonthDayDash.test(this))
	{
		match = YearMonthDayDash.exec(this);
		Year = match[1];
		Month = match[2];
		Day = match[3];
	}
	else if (YearMonthDaySlash.test(this))
	{
		match = YearMonthDaySlash.exec(this);
		Year = match[1];
		Month = match[2];
		Day = match[3];
	}
	else if (DayMonthYearDash.test(this))
	{
		match = DayMonthYearDash.exec(this);
		Day = match[1];
		Month = match[2];
		Year = match[3];
	}
	else if (DayMonthYearSlash.test(this))
	{
		match = DayMonthYearSlash.exec(this);
		Day = match[1];
		Month = match[2];
		Year = match[3];
	}
	else
		return null;

	var dt = new Date(Year, Month - 1, Day);
	if (dt.getDate() == Day && dt.getMonth + 1 == Month && dt.getFullYear() == Year)
		return dt;
		
	return null;
};

String.prototype.isDate = function()
{
	var YearMonthDayDash = /^(\d{4})-(\d{1,2})-(\d{1,2})$/
	var YearMonthDaySlash = /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/
	var DayMonthYearDash = /^(\d{1,2})-(\d{1,2})-(\d{4})$/
	var DayMonthYearSlash = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/

	var dArr;
	var match;
	var Year;
	var Month;
	var Day;

	if (YearMonthDayDash.test(this))
	{
		match = YearMonthDayDash.exec(this);
		Year = match[0];
		Month = match[1];
		Day = match[2];
	}
	else if (YearMonthDaySlash.test(this))
	{
		match = YearMonthDaySlash.exec(this);
		Year = match[0];
		Month = match[1];
		Day = match[2];
	}
	else if (DayMonthYearDash.test(this))
	{
		match = DayMonthYearDash.exec(this);
		Day = match[0];
		Month = match[1];
		Year = match[2];
	}
	else if (DayMonthYearSlash.test(this))
	{
		match = DayMonthYearSlash.exec(this);
		Day = match[0];
		Month = match[1];
		Year = match[2];
	}
	else
		return false;

	var dt = new Date(Year, Month - 1, Day);
	return (dt.getDate() == Day && dt.getMonth + 1 == Month && dt.getFullYear() == Year);
};

function MaxLength(e, obj, len)
{
	if(!e) e = window.event;
	if ((e.keyCode >= 48 && e.keyCode <= 90) ||		//Main border lowercase/numbers
		(e.keyCode >= 96 && e.keyCode <= 111) ||	//Number pad numbers/characters
		(e.keyCode >= 186 && e.keyCode <= 222)		//Special characters
		) 
		return (obj.value.length < len)
		
	return true;
}

function AnalizeARs(Expr, NodeAr)
{
	if(Expr == null) return true;

	Expr = ReplaceVars(Expr, NodeAr);

	return eval(Expr);
}

function ReplaceVars(str, NodeAr)
{
	for(var key in NodeAr)
		str = str.replace(new RegExp("ar::" + key, "g"), NodeAr[key]);
	return str;
}

/*****************************************************************************************************
	Box
		Description:
			Class for containing dimentions of a square, as well as defining classes to determine the
			size of a control, or whether a Point (defined below) is within the square.
		Input:
			Top		- Top location of the box.
			Right	- Right location of the box.
			Bottom	- Bottom location of the box.
			Left	- Left location of the box.
		Variables:
			Same as above.
		Usage:
			var bx = new Box(10, 20, 20, 10);
			var pt = new Point(11, 11);
			bx.IsPointInBox(pt);		//returns true
			
			pt = new Point(9, 9);
			bx.IsPointInBox(pt);		//returns false
			
			var tbl = document.getElementById(sometable);
			bx.FromControl(tbl, null);	// bx now contains the measurements of "sometable"
******************************************************************************************************/
var Box = Class.create(
{
	initalize: function(Top, Right, Bottom, Left)
	{
		this.Top = Top;
		this.Right = Right;
		this.Bottom = Bottom;
		this.Left = Left;
	},
	FromControl: function(obj, borderadjust)
	{
		if (obj == null) return;
		var curleft = 0;
		var curtop = 0;
		var curObj = obj;

		if (borderadjust == null) borderadjust = 0;

		if (curObj.offsetParent)
		{
			curleft = curObj.offsetLeft;
			curtop = curObj.offsetTop;

			while (curObj = curObj.offsetParent)
			{
				curleft += curObj.offsetLeft;
				curtop += curObj.offsetTop;
			}
		}

		this.Top = curtop + borderadjust;
		this.Left = curleft + borderadjust;
		this.Right = this.Left + obj.offsetWidth - borderadjust;
		this.Bottom = this.Top + obj.offsetHeight - borderadjust;
	},
	IsPointInBox: function(pt)
	{
		if ((pt.X >= this.Left && pt.X <= this.Right) && (pt.Y >= this.Top && pt.Y <= this.Bottom))
			return true;

		return false;
	}
});

/*****************************************************************************************************
	MouseInfo
		Description: 
			Returns information about the mouse, specificaly the x, y, and button clicked
		Input:
			e	- Event to analize for mouse information
		Variables:
			Point	- (see point definition below) X, Y
			Button	- String value, one of 3 possabilities
						left
						middle
						right
		Usage:
			var info = new MouseInfo(event);
			var mouseX = info.Point.X;
			var mouseY = info.Point.Y;
			var mouseButtonClicked = info.Button;
******************************************************************************************************/
var MouseInfo = Class.create(
{
	initialize: function(e)
	{
		if (!e)
			this.event = window.event;
		else
			this.event = e;

		this.Point = this.MousePos(this.event);
		this.Button = this.GetMouseButton(this.event);
	},

	MousePos: function(e)
	{
		var posx = Event.pointerX(e);
		var posy = Event.pointerY(e);
		//var YOffset = (document.all) ? document.body.scrollTop : window.pageYOffset;
		//var XOffset = (document.all) ? document.body.scrollLeft : window.pageXOffset;

		/*
		if (e.pageX || e.pageY)
		{
		alert('page');
		posx = e.pageX;
		posy = e.pageY;
		}
		else if (e.clientX || e.clientY)
		{
		alert('client');
		posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
		posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
		}
		*/
		var Pos = new Point(posx, posy);
		return Pos;
	},

	GetMouseButton: function(e)
	{
		if (e.which)
			return (e.which < 2) ? "left" : ((e.which == 2) ? "middle" : "right");
		else
			return (e.button < 2) ? "left" : ((e.button == 4) ? "middle" : "right");
	}
});

/*****************************************************************************************************
	Point
		Description:
			Basic class for holding an X and Y co-ordinate.
		Input:
			x		- Horizontal location of the point.
			y		- Vertical location of the point.
		Variables:
			X		- same as x above.
			Y		- same as y above.
		Usage:
			var pt = new Point(12, 29);
			var ptX = pt.X;
			var ptY = pt.Y;
******************************************************************************************************/
var Point = Class.create(
{
	initialize: function(x, y)
	{
		this.X = x;
		this.Y = y;
	}
});

function ObjHeight(obj)
{
	if (obj == null) return 0;
	var height = -1;

	if (typeof (obj.innerHeight) == 'number')
		height = obj.innerHeight;
	else if (document.documentElement && obj.clientHeight)
		height = obj.clientHeight;
	else if (document.body && obj.clientHeight)
		height = document.body.clientHeight;

	if (height <= 0) height = 0;
	return height;
}

function ObjWidth(obj)
{
	if (obj == null) return 0;
	var width = -1;
	if (typeof (obj.innerWidth) == 'number')
		width = obj.innerWidth;
	else if (document.documentElement && obj.clientWidth)
		width = obj.clientWidth;
	else if (document.body && obj.clientWidth)
		width = obj.clientWidth;

	if (width <= 0) width = 0;
	return width;
}

function ObjTop(obj)
{
	if (obj == null) return 0;
	var curtop = 0;
	var curObj = obj;

	if (curObj.offsetParent)
	{
		curtop = curObj.offsetTop;

		while (curObj = curObj.offsetParent)
			curtop += curObj.offsetTop;
	}

	return curtop;
}

function ObjLeft(obj)
{
	if (obj == null) return 0;
	var curleft = 0;
	var curObj = obj;

	if (curObj.offsetParent)
	{
		curleft = curObj.offsetLeft;

		while (curObj = curObj.offsetParent)
			curleft += curObj.offsetLeft;
	}
	return curleft;
}

function WindowHeight()
{
	return oWindowHeight(this);
}
function oWindowHeight(obj)
{
	if (typeof (obj.window.innerWidth) == 'number')
		return obj.window.innerHeight;
	else if (obj.document.documentElement && (obj.document.documentElement.clientWidth || obj.document.documentElement.clientHeight))
		return obj.document.documentElement.clientHeight;
	else if (obj.document.body && (obj.document.body.clientWidth || obj.document.body.clientHeight))
		return obj.document.body.clientHeight;

	return obj.document.body.parentNode.scrollHeight;
}

function WindowWidth()
{
	return oWindowWidth(this);
}
function oWindowWidth(obj)
{
	if (typeof (obj.window.innerWidth) == 'number')
		return obj.window.innerWidth;
	else if (obj.document.documentElement && (obj.document.documentElement.clientWidth || obj.document.documentElement.clientHeight))
		return obj.document.documentElement.clientWidth;
	else if (obj.document.body && (obj.document.body.clientWidth || obj.document.body.clientHeight))
		return obj.document.body.clientWidth;

	return obj.document.body.parentNode.scrollWidth;
}

function GetCenter(width, height)
{
	var obj = new Object();
	var winwidth = parseInt(WindowWidth());
	var winheight = parseInt(WindowHeight());
	var pxWidth = parseInt(width);
	var pxHeight = parseInt(height);
	var top = 0;
	var left = 0;
	
	// Convert % width to pixel width
	if (width.indexOf('%') >= 0)
		pxWidth = winwidth / (pxWidth / 100);
		
	if (height.indexOf('%') >= 0)
		pxHeight = winheight / (pxHeight / 100);

	// Calculate center position minus size of the object
	left = (winwidth - pxWidth) / 2;
	top = (winheight - pxHeight) / 2;
	
	// Factor in scroll bar position in order to center withing visible area
	left = (left + parseInt(ScrollLeft()));
	top = (top + parseInt(ScrollTop()));
	
	// Tack on unit
	left += 'px';
	top += 'px';
	
	obj.Left = left;
	obj.Top = top;
	obj.Width = pxWidth + 'px';
	obj.Height = pxHeight + 'px';
	return obj;
}

function ScrollTop()
{
	return oScrollTop(this);
}
function oScrollTop(obj)
{
	var pos;

	if (!obj.document.body.scrollTop)
	{
		if (obj.window.pageYOffset)
			pos = obj.window.pageYOffset;
		else
			pos = (obj.document.body.parentElement) ? obj.document.body.parentElement.scrollTop : 0;
	}
	else
		pos = obj.document.body.scrollTop;

	return pos;
}

function ScrollLeft()
{
	return oScrollLeft(this);
}
function oScrollLeft(obj)
{
	var pos;

	if (!obj.document.body.scrollLeft)
	{
		if (obj.window.pageXOffset)
			pos = obj.window.pageXOffset;
		else
			pos = (obj.document.body.parentElement) ? obj.document.body.parentElement.scrollLeft : 0;
	}
	else
		pos = obj.document.body.scrollLeft;

	return pos;
}

/*
	Replaces the class on obj with the passed in class cls storing the class that is replaced
	in obj.oldClass
*/
function oHover(obj, cls)
{
	var oldClass = obj.getAttribute("class");
	if (oldClass == null || oldClass == "")
	oldClass = obj.getAttribute("className");
	obj.oldClass = oldClass;

	obj.setAttribute("class", cls);
	obj.setAttribute("className", cls);
}

/*
	Should be used only with oHover, as oHover sets a variable on the object for restoring
	the class that was replaced by the oHover function.
*/
function oOut(obj)
{
	obj.setAttribute("class", obj.oldClass);
	obj.setAttribute("className", obj.oldClass);
}

/*
	Simple confirm box using the message provided, msg. Returns true if the user clicks
	OK or false if they click Cancel.
*/
function ConfirmIt(e, msg)
{
	var ret = confirm(msg);
	e.returnValue = ret;
	return ret;
}

// Element node wrapper. Only deals with element nodes ignoring text and empty space nodes.
function DomElement(target)
{
	var obj = target;
	if (!obj) throw new Error("Could not create DomElement from NULL object.");
	if (obj.nodeType != 1) throw new Error("Could not create DomElement from a non-element object.");
	// gets the first child element
	this.firstChild = function()
	{
		if (!obj) return null;
		for (var i = 0; i < obj.childNodes.length; i++)
		{
			if (obj.childNodes[i].nodeType == 1)
				return new DomElement(obj.childNodes[i]);
		}
		return null;
	};
	// gets the previous sibling element
	this.prev = function()
	{
		if (obj == null) return null;
		var e = obj;
		while (e.previousSibling != null)
		{
			e = e.previousSibling;
			if (e.nodeType == 1)
				return new DomElement(e);
		}
		return null;
	};
	// gets the next sibling element
	this.next = function()
	{
		if (obj == null) return null;
		var e = obj;
		while (e.nextSibling != null)
		{
			e = e.nextSibling;
			if (e.nodeType == 1)
				return new DomElement(e);
		}
		return null;
	};
	// gets the parent element
	// if tagname argument is given looks for parent element with specified tagName
	this.parent = function(tagname)
	{
		if (!obj) return null;
		var e = obj.parentNode;
		if (tagname)
		{
			tagname = tagname.toUpperCase();
			while (e)
			{
				if (e.nodeType == 1 && e.tagName && e.tagName.toUpperCase() == tagname)
					break;
				e = e.parentNode;
			}
		}
		else
		{
			while (e && e.nodeType != 1)
			{
				e = e.parentNode;
			}
		}
		if (e)
			return new DomElement(e);
		else
			return null;
	};
	// gets the actual element in the document
	this.element = function()
	{
		return obj;
	};
}

Url = {
	// private variables
	_qs: null,
	// get the host name of the current url
	getHost: function()
	{
		return window.location.hostname;
	},
	// gets the root of the current url
	getRoot: function()
	{
		var path = window.location.pathname;
		return path.slice(0, path.indexOf("/", 1));
	},
	getPath: function()
	{
		var path = window.location.pathname;
		return path.substr(path.indexOf("/", 1));
	},
	// redirects to the given page
	redirect: function(url)
	{
		if (url == null || url == "")
			return;
		if (url.charAt(0) == "/")
		{
			window.location = this.getRoot() + escape(url);
		}
		else
		{
			var path = window.location.pathname;
			path = path.slice(0, path.lastIndexOf("/") + 1);
			path += url;
			window.location = escape(path);
		}
	},
	// redirects to the given page (without escaping url)
	redirect2: function(url)
	{
		if (url == null || url == "")
			return;
		if (url.charAt(0) == "/")
		{
			window.location = this.getRoot() + url;
		}
		else
		{
			var path = window.location.pathname;
			path = path.slice(0, path.lastIndexOf("/") + 1);
			path += url;
			window.location = path;
		}
	},
	// returns a new url string
	// returned string cannot be used with the redirect method
	rewrite: function(url)
	{
		if (url == null || url == "")
			return "";
		if (url.charAt(0) == "/")
		{
			return this.getRoot() + url;
		}
		else
		{
			var path = window.location.pathname;
			path = path.slice(0, path.lastIndexOf("/") + 1);
			path += url;
			return path;
		}
	},
	// returns a new path string excluding the application root
	// returned string can be used with the redirect method
	rewritePath: function(url)
	{
		if (url == null || url == "")
			return "";
		var path = this.getPath();
		path = path.slice(0, path.lastIndexOf("/") + 1);
		path += url;
		return path;
	},
	// gets an array of query string params
	getParams: function()
	{
		if (this._qs != null)
			return this._qs;
		var qs = window.location.search.substr(1);
		var pairs = qs.split("&");
		var params = [];
		for (var i = 0; i < pairs.length; i++)
		{
			var pair = pairs[i].split("=");
			params[unescape(pair[0])] = unescape(pair[1]);
		}
		this._qs = params;
		return params;
	},
	// gets the value of a given query string parameter
	getParam: function(key)
	{
		var qs = this.getParams();
		return qs[key];
	}
};

function dumpProps(obj, parent)
{
	// Go through all the properties of the passed-in object 
	for (var i in obj)
	{
		// if a parent (2nd parameter) was passed in, then use that to 
		// build the message. Message includes i (the object's property name) 
		// then the object's property value on a new line 
		if (parent) { var msg = parent + "." + i + "\n" + obj[i]; } else { var msg = i + "\n" + obj[i]; }
		// Display the message. If the user clicks "OK", then continue. If they 
		// click "CANCEL" then quit this level of recursion 
		if (!confirm(msg)) { return; }
		// If this property (i) is an object, then recursively process the object 
		if (typeof obj[i] == "object")
		{
			if (parent) { dumpProps(obj[i], parent + "." + i); } else { dumpProps(obj[i], i); }
		}
	}
}