// Utilities hjælpe klasse
// 
// Copyright 2006-2009 René Lønstrup @ RLdesign.dk
// 
// May not be used commercially without permission from the author.
//
// Version: 1.1.2.15
//
//
// CHANGELOG:
//

//
// 29/09/2009
//     1.1.2.14 -> 1.1.2.15
//           Fixed bug in FixDecimalNumber resulting in it not working in Internet Explorer 7 and down
// 05/08/2009
//     1.1.2.13 -> 1.1.2.14
//           Added clear and fill functionality to Serialize function
//           Added handling of whether the function should use an objects value or a string
// 04/05/2009
//     1.1.2.12 -> 1.1.2.13
//           Added functions FilterNumberKeys and FixDecimalNumber
// 18/03/2009
//     1.1.2.11 -> 1.1.2.12
//           Fixed a small potential bug i function ReorderList
// 04/03/2009
//     1.1.2.10 -> 1.1.2.11
//           Fixed several bugs in OpenPopup function
// 09/02/2009
//     1.1.2.9 -> 1.1.2.10
//           Added find functionality to Serialize function
// 19/01/2009
//     1.1.2.8 -> 1.1.2.9
//           Added functions GetCookie, SetCookie and RemoveCookie to enable manipulation of cookies
//           Fixed bug in GetMargins and GetPaddings eliminating af NaN error in Internet Explorer
// 11/01/2009
//     1.1.2.7 -> 1.1.2.8
//           Removed unnessesary argument from function ReorderList
// 07/11/2008
//     1.1.2.6 -> 1.1.2.7
//           Extended GetMargins to work in a multiple-frame environment
//           Extended GetPaddings to work in a multiple-frame environment
// 02/10/2008
//     1.1.2.5 -> 1.1.2.6
//           Added new function GetTypedKey
// 30/09/2008
//     1.1.2.4 -> 1.1.2.5
//           Fixed a bug in CenterObject which meant that it didn't center objects according to the 
//           scrolling offset of the page
// 10/07/2008
//     1.1.2.3 -> 1.1.2.4
//           Added new function ToggleVisibility
// 10/07/2008
//     1.1.2.2 -> 1.1.2.3
//           Added possibility to pass variables or objects through ReorderList
// 30/06/2008
//     1.1.2.1 -> 1.1.2.2
//           Added new function OpenPopup
// 12/06/2008
//     1.1.2.0 -> 1.1.2.1
//           Fixed a very small potential bug in ReorderList
// 30/05/2008
//     1.1.1.7 -> 1.1.2.0
//           Reordered all functions alphabetically
//           Added better memory cleanup to function ReorderSelectOption
//           Added new function ReorderList
// 29/05/2008
//     1.1.1.6 -> 1.1.1.7
//           Added new function ClearWhiteSpace
// 25/04/2008
//     1.1.1.5 -> 1.1.1.6
//           Extended defineObject to work in a multiple-frame environment
//           Extended makeElement to work in a multiple-frame environment
// 20/04/2008
//     1.1.1.4 -> 1.1.1.5
//           Fixed bug in new function SetOffsetPosition
// 15/04/2008
//     1.1.1.3 -> 1.1.1.4
//           Added new function ToggleDisplay
// 13/04/2008
//     1.1.1.2 -> 1.1.1.3
//           Change name of function SetOffsetPosition to SetPositionToOffset
//           Added new function SetOffsetPosition
//           Tweaked function CenterObject a little bit
// 07/04/2008
//     1.1.1.1 -> 1.1.1.2
//           Added function GetCursorPosition
// 07/04/2008
//     1.1.1.0 -> 1.1.1.1
//           Added function InsertAfter
//           Added function PrependChild
// 23/03/2008
//     1.1.0.9 -> 1.1.1.0
//           Added function GetHighestZIndex
//           Added function CenterObject
//           Added function GetCssValue
//           Added function SetOffsetPosition
// 22/03/2008
//     1.1.0.8 -> 1.1.0.9
//           Fixed another bug in GetOpacity
// 21/03/2008
//     1.1.0.7 -> 1.1.0.8
//           Fixed bug in GetOpacity
//           Fixed bug in GetMargins and GetPaddings which meant that they didn't parse the values correctly, thus
//           wrongly returning NaN-values
//           Added functions GetWindowSize and GetPageSize which should be used in place of the seperate functions for
//           height and width.
//           Added function SetDisplay which set the display property of an object
//           Added function GetOpacity to join SetOpacity
// 14/03/2008
//     1.1.0.6 -> 1.1.0.7
//           Fixed a bug in GetOffsetPosition making it useless i IE6 when run on an advanced page-layout
// 11/03/2008
//     1.1.0.5 -> 1.1.0.6
//           Fixed a couple of bugs in Serialize, and added an extra arguments-switch to force it out of
//           running the supplied object through DefineObject - so the function also works with strings with commas
// 10/03/2008
//     1.1.0.4 -> 1.1.0.5
//           Added error-handling to DefineObject to avoid error when passing an ID of a non-existing object
// 09/03/2008
//     1.1.0.3 -> 1.1.0.4
//           Added function GetOffsetPosition to get top left position of element
//           Changed functions GetPaddings and GetMargins to correctly return an Object instead of an Array
// 06/03/2008
//     1.1.0.2 -> 1.1.0.3
//           Fixed bug caused by a misspelled variable i DefineEventRelatedTarget
// 02/03/2008
//     1.1.0.1 -> 1.1.0.2
//           Removed dependancy of RLdesign.js file.
// 24/02/2008
//     1.1.0.0 -> 1.1.0.1
//           Added 'get count' switch to Serialize-function, which returns the number of serialized items
// 23/02/2008
//     1.0.0.0 -> 1.1.0.0
//           Added function Serialize which serializes data to and from an input field


if (!RLdesign) {
	var RLdesign = function() { };
}
RLdesign.Utils = function() {
	return {

		// centers an object relative to the browser-window
		CenterObject: function(obj, horizontal, vertical) {
			var h = (horizontal === null) ? false : horizontal;
			var v = (vertical === null) ? false : vertical;
			var o = RLdesign.Utils.DefineObject(obj);
			if (!o) return;

			var scroll = RLdesign.Utils.GetScrollingOffset();

			var width = parseInt(o.offsetWidth);
			var height = parseInt(o.offsetHeight);
			var windowSize = RLdesign.Utils.GetWindowSize();

			var hcenter = Math.floor((windowSize["width"] - width) / 2) + scroll["horizontal"];
			var vcenter = Math.floor((windowSize["height"] - height) / 2) + scroll["vertical"];

			if (h) o.style.left = hcenter + "px";
			if (v) o.style.top = vcenter + "px";
		},

		// removes any child nodes of the given object
		ClearChildElements: function(obj) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null) return;
			if (elem.hasChildNodes()) {
				while (elem.childNodes.length >= 1) {
					elem.removeChild(elem.firstChild);
				}
			}
		},

		// clears the specified global interval variable (specified as a string), if exists
		ClearInterval: function(sInterval) {
			var bIntervalExist = !!window[sInterval];
			if (bIntervalExist) window.clearInterval(window[sInterval]);
		},

		// clears the specified global timeout variable (specified as a string), if exists
		ClearTimeout: function(sTimeout) {
			var bTimeoutExist = !!window[sTimeout];
			if (bTimeoutExist) window.clearTimeout(window[sTimeout]);
		},

		// removes whitespace children from the given object
		ClearWhiteSpace: function(obj) {
			var o = RLdesign.Utils.DefineObject(obj);
			var aTextnodes = new Array();
			for (var i = 0; i < o.childNodes.length; i++) {
				if (o.childNodes[i].tagName == undefined) {
					aTextnodes[aTextnodes.length] = o.childNodes[i];
				}
			}
			for (var t = 0; t < aTextnodes.length; t++) {
				o.removeChild(aTextnodes[t]);
			}
		},

		// returns a crossbrowser event object
		DefineEvent: function(e) {
			return e || window.event;
		},

		// returns the keycode for the key that was pressed during the event		
		DefineEventKeyCode: function(e) {
			var ev = RLdesign.Utils.DefineEvent(e);
			var keycode = 0;
			if (ev.which) keycode = ev.which; // Netscape4
			else if (ev.keyCode) keycode = ev.keyCode; // IE
			else if (ev.charCode) keycode = ev.charCode; // Gecko
			return keycode;
		},

		// returns the related target from the event
		// used in conjunction with mouseover / -out events, and returns the element from which the mouse came / goes
		// ie. in the case of a mouseover, returns the element from which the mouse came, and vice versa for mouseout
		DefineEventRelatedTarget: function(e) {
			var t = e.relatedTarget;
			if (!t && (e.type == "mouseover" || e.type == "mouseout")) {
				t = (e.type == "mouseout") ? e.toElement : e.fromElement;
			}
			return t;
		},

		// returns a boolean representation of whether the shift key was pressed during the event		
		DefineEventShiftKey: function(e) {
			var ev = RLdesign.Utils.DefineEvent(e);
			var bShift = false;
			if (e.shiftKey) { bShift = e.shiftKey } // IE etc
			else if (e.modifiers) { // Netscape 4
				if (e.modifiers & 4) { // bitwise AND to see if shift is pressed
					bShift = true;
				}
			}
			return bShift;
		},

		// returns the target for the event in question, typically a click-event
		// ie. in the case of a click-event, the target is the element being clicked on
		DefineEventTarget: function(e) {
			var ev = RLdesign.Utils.DefineEvent(e);
			var element = ev.srcElement || ev.target;
			if (element.nodeType == 3) element = element.parentNode; // defeat Safari bug
			return element;
		},

		// tries to return an object from an arbitrary argument that can be a string or an object
		// if successfully finding an object, either from a search for the object-id or the object itself, 
		// returns the object. If unsuccessful, returns null
		DefineObject: function(el, d) {
			var doc = (d) ? d : document;
			var elem = null;
			// if el is an object
			if (typeof (el) == "object") {
				try {
					elem = el;
				}
				catch (ex) {
					elem = null;
				}
			}
			// if el is the id of an html-object
			else if (doc.getElementById(el)) {
				try {
					elem = doc.getElementById(el);
				}
				catch (ex) {
					elem = null;
				}
			}
			// if el is a string representation of an document.getElementById - or similar statement
			else if (typeof (el) == "string" && (el.indexOf("(") != -1 || el.indexOf("[") != -1)) {
				try {
					eval("elem = " + el);
				}
				catch (ex) {
					elem = null;
				}
			}
			// last resort
			else if (typeof (el) == "string") {
				try {
					eval("elem = " + el);
				}
				catch (ex) {
					elem = null;
				}
			}
			return elem;
		},

		// returns a crossbrowser return code designed to prevent further events i mozilla browser, if need be
		DefineReturnCode: function(e, r) {
			var ev = RLdesign.Utils.DefineEvent(e);
			if (r == false) {
				if (ev.stopPropagation) ev.stopPropagation();
				else if (ev.cancelBubble) ev.cancelBubble = true;
				if (ev.preventDefault) ev.preventDefault();
				else if (ev.returnValue) ev.returnValue = false;
			}
			else {
				if (typeof (ev.returnValue) == "string") ev.returnValue = r;
			}
			return r;
		},

		// Filters keys pressed, only allowing numeric numbers (and certain special keys)
		// Usage:
		// <input type="text" onkeydown="return RLdesign.Utils.FilterNumberKeys(this, event, false);" />
		FilterNumberKeys: function(oInput, e, allowdecimals) {
			var ev = RLdesign.Utils.DefineEvent(e);
			var charcode = RLdesign.Utils.DefineEventKeyCode(e);
			var whitelist = [8, 9, 16, 17, 18, 19, 20, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 144];
			if (allowdecimals) {
				whitelist[whitelist.length] = 110;
				whitelist[whitelist.length] = 188;
				whitelist[whitelist.length] = 190;
			}
			if (ev.ctrlKey && charcode == 65) return true; // ctrl+a
			else if (ev.ctrlKey && charcode == 67) return true; // ctrl+c
			else if (ev.ctrlKey && charcode == 86) return true; // ctrl+v
			else if (ev.ctrlKey && charcode == 88) return true; // ctrl+x
			else if (ev.ctrlKey && charcode == 90) return true; // ctrl+z
			else if (RLdesign.Arrays.InArray(whitelist, charcode)) return true;
			else return false;
		},

		FixDecimalNumber: function(no) {
			if (typeof no == "string") {
				var fixed = no;
				var commas = 0;
				var dots = 0;
				var firstcomma = -1;
				var firstdot = -1;
				for (var i = 0; i < no.length; i++) {
					var c = String.fromCharCode(no.charCodeAt(i));
					if (c == ".") {
						dots++;
						if (firstdot == -1) firstdot = i;
						continue;
					}
					if (c == ",") {
						commas++;
						if (firstcomma == -1) firstcomma = i;
						continue;
					}
				}
				if (dots == 1 && commas == 0) { // uses dots as thousands delimiter
					fixed = fixed;
				}
				else if (dots == 0 && commas == 1) { // uses commas as thousands delimiter
					fixed = fixed.replace(",", ".");
				}
				else if (dots > commas || firstdot < firstcomma) { // uses dots as thousands delimiter
					fixed = fixed.replace(".", "").replace(",", ".");
				}
				else if (commas > dots || firstcomma < firstdot) { // uses commas as thousands delimiter
					fixed = fixed.replace(",", "");
				}

				return fixed;
			}
			else {
				return no;
			}
		},

		GetCssValue: function(obj, sCss) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null) return;
			var cStyle = null;
			if (elem.currentStyle) {
				cStyle = elem.currentStyle;
			}
			else if (document.defaultView && document.defaultView.getComputedStyle) {
				cStyle = document.defaultView.getComputedStyle(elem, "");
			}
			var sValue;
			if (cStyle) {
				sValue = cStyle[sCss];
			}
			else {
				sValue = elem.style[sCss];
			}
			return sValue;
		},

		GetCursorPosition: function(e) {
			var ev = RLdesign.Utils.DefineEvent(e);
			var pos = new Object();
			pos["top"] = 0;
			pos["left"] = 0;
			if (ev.pageX || ev.pageY) {
				pos["left"] = ev.pageX;
				pos["top"] = ev.pageY;
			}
			else if (ev.clientX || ev.clientY) {
				pos["left"] = ev.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
				pos["top"] = ev.clientY + document.body.scrollTop + document.documentElement.scrollTop;
			}
			return pos;
		},

		GetHighestZIndex: function(oRoot, sTag) {
			if (!document.getElementsByTagName) return 0;
			var root = (!oRoot) ? document : oRoot;
			var tag = (!sTag) ? "*" : sTag;
			var allElems = root.getElementsByTagName(tag);
			var maxZIndex = 0;
			for (var i = 0; i < allElems.length; i++) {
				var elem = allElems[i];
				var cStyle = null;
				if (elem.currentStyle) { cStyle = elem.currentStyle; }
				else if (document.defaultView && document.defaultView.getComputedStyle) {
					cStyle = document.defaultView.getComputedStyle(elem, "");
				}
				var sNum;
				if (cStyle) {
					sNum = Number(cStyle.zIndex);
				}
				else {
					sNum = Number(elem.style.zIndex);
				}
				if (!isNaN(sNum)) {
					maxZIndex = Math.max(maxZIndex, sNum);
				}
			}
			return maxZIndex;
		},

		// returns an object of the top, bottom, left and right margins of the given object
		GetMargins: function(obj, d) {
			var doc = (d) ? d : document;
			var elem = RLdesign.Utils.DefineObject(obj);
			var aReturn = new Object();
			aReturn["top"] = 0;
			aReturn["bottom"] = 0;
			aReturn["left"] = 0;
			aReturn["right"] = 0;
			if (elem == null) return aReturn; // returns default values if no correct object was specified
			var cStyle = null;
			if (elem.currentStyle) {
				cStyle = elem.currentStyle;
			}
			else if (doc.defaultView && doc.defaultView.getComputedStyle) {
				cStyle = doc.defaultView.getComputedStyle(elem, "");
			}
			if (cStyle) {
				aReturn["top"] = parseInt(cStyle.marginTop);
				aReturn["bottom"] = parseInt(cStyle.marginBottom);
				aReturn["left"] = parseInt(cStyle.marginLeft);
				aReturn["right"] = parseInt(cStyle.marginRight);
			}
			else {
				aReturn["top"] = parseInt(elem.style.marginTop);
				aReturn["bottom"] = parseInt(elem.style.marginBottom);
				aReturn["left"] = parseInt(elem.style.marginLeft);
				aReturn["right"] = parseInt(elem.style.marginRight);
			}
			if (isNaN(aReturn["top"])) aReturn["top"] = 0;
			if (isNaN(aReturn["bottom"])) aReturn["bottom"] = 0;
			if (isNaN(aReturn["left"])) aReturn["left"] = 0;
			if (isNaN(aReturn["right"])) aReturn["right"] = 0;
			return aReturn;
		},

		// gets top left position of object
		GetOffsetPosition: function(obj) {
			var oNode = RLdesign.Utils.DefineObject(obj);
			var iLeft = 0;
			var aReturn = new Object();
			aReturn["top"] = 0;
			aReturn["left"] = 0;
			if (oNode.offsetParent) {
				do {
					aReturn["left"] += parseInt(oNode.offsetLeft);
					aReturn["top"] += parseInt(oNode.offsetTop);
				} while (oNode = oNode.offsetParent);
			}
			else if (oNode.getBoundingClientRect) { // IE only method of finding elements position / dimension
				aReturn["top"] = oNode.getBoundingClientRect().top - 2; // minus two to counteract IE upper-left corner anomaly
				aReturn["left"] = oNode.getBoundingClientRect().left - 2; // minus two to counteract IE upper-left corner anomaly
			}
			else {
				while (oNode.tagName.toLowerCase() != "body") {
					aReturn["left"] += parseInt(oNode.offsetLeft);
					aReturn["top"] += parseInt(oNode.offsetTop);
					oNode = oNode.offsetParent;
				}
			}
			return aReturn;
		},

		// positions an object relative to the browser-window
		//
		// Usage:
		//     Centering the object horizontally and vertically:
		//     [Utils].SetOffsetPosition(myObject, "center", "center");
		//     Centering the object horizontally and fixing its position to 20 pixel from the top:
		//     [Utils].SetOffsetPosition(myObject, "center", 20);
		SetOffsetPosition: function(obj, horizontal, vertical) {
			var h = (horizontal === null || (isNaN(horizontal) && horizontal != "center")) ? 0 : horizontal;
			var v = (vertical === null || (isNaN(vertical) && vertical != "center")) ? 0 : vertical;
			var o = RLdesign.Utils.DefineObject(obj);
			if (!o) return;

			var position = new Object();
			position["left"] = parseInt(RLdesign.Utils.GetCssValue(o, "left"));
			position["top"] = parseInt(RLdesign.Utils.GetCssValue(o, "top"));
			var scroll = RLdesign.Utils.GetScrollingOffset();

			if (h === "center") RLdesign.Utils.CenterObject(o, true, false);
			else o.style.left = scroll["horizontal"] + h + "px";
			if (v === "center") RLdesign.Utils.CenterObject(o, false, true);
			else o.style.top = scroll["vertical"] + v + "px";
		},

		GetOpacity: function(obj) {
			var opacity = (parseInt(obj.style.width) == obj.offsetWidth) ? 100 : 0;
			if (obj.style.opacity) { opacity = Math.round(obj.style.opacity * 100); }
			else if (obj.style.MozOpacity) { opacity = Math.round(obj.style.MozOpacity * 100); }
			else if (obj.style.KHTMLOpacity) { opacity = Math.round(obj.style.KHTMLOpacity * 100); }
			else if (obj.style.filter) {
				var filter = obj.style.filter;
				//				var pattern = /opacity\b[^=]*=(.*?)\)/;
				var pattern = /\(opacity:(.*?)\)/;
				oRegex = filter.match(pattern);
				if (typeof (oRegex) == "object") {
					var match = oRegex[1];
					opacity = (match != null) ? parseInt(match) : opacity;
				}
			}
			return opacity;
		},

		// returns an object of the top, bottom, left and right paddings of the given object
		GetPaddings: function(obj, d) {
			var doc = (d) ? d : document;
			var elem = RLdesign.Utils.DefineObject(obj);
			var aReturn = new Object();
			aReturn["top"] = 0;
			aReturn["bottom"] = 0;
			aReturn["left"] = 0;
			aReturn["right"] = 0;
			if (elem == null) return aReturn;
			var cStyle = null;
			if (elem.currentStyle) {
				cStyle = elem.currentStyle;
			}
			else if (doc.defaultView && doc.defaultView.getComputedStyle) {
				cStyle = doc.defaultView.getComputedStyle(elem, "");
			}
			if (cStyle) {
				aReturn["top"] = parseInt(cStyle.paddingTop);
				aReturn["bottom"] = parseInt(cStyle.paddingBottom);
				aReturn["left"] = parseInt(cStyle.paddingLeft);
				aReturn["right"] = parseInt(cStyle.paddingRight);
			}
			else {
				aReturn["top"] = parseInt(elem.style.paddingTop);
				aReturn["bottom"] = parseInt(elem.style.paddingBottom);
				aReturn["left"] = parseInt(elem.style.paddingLeft);
				aReturn["right"] = parseInt(elem.style.paddingRight);
			}
			if (isNaN(aReturn["top"])) aReturn["top"] = 0;
			if (isNaN(aReturn["bottom"])) aReturn["bottom"] = 0;
			if (isNaN(aReturn["left"])) aReturn["left"] = 0;
			if (isNaN(aReturn["right"])) aReturn["right"] = 0;
			return aReturn;
		},

		// returns the width and height of the current page (or more precisely, the width/height of the body element of the page)
		GetPageSize: function() {
			var size = new Object();
			size["width"] = null;
			size["height"] = null;
			var scrollWidth = parseInt(document.body.scrollWidth);
			var offsetWidth = parseInt(document.body.offsetWidth);
			var scrollHeight = parseInt(document.body.scrollHeight);
			var offsetHeight = parseInt(document.body.offsetHeight);
			size["width"] = (scrollWidth > offsetWidth) ? scrollWidth : offsetWidth;
			size["height"] = (scrollHeight > offsetHeight) ? scrollHeight : offsetHeight;
			return size;
		},

		// returns the height of the current page (or more precisely, the height of the body element of the page)
		GetPageHeight: function() {
			var y = null;
			var scrollHeight = parseInt(document.body.scrollHeight);
			var offsetHeight = parseInt(document.body.offsetHeight);
			if (scrollHeight > offsetHeight) { // all but Explorer Mac
				y = scrollHeight;
			}
			else { // Explorer Mac - would also work in Explorer 6 Strict, Mozilla and Safari
				y = offsetHeight;
			}
			return y;
		},

		// returns the width of the current page (or more precisely, the width of the body element of the page)
		GetPageWidth: function() {
			var x = null;
			var scrollWidth = parseInt(document.body.scrollWidth);
			var offsetWidth = parseInt(document.body.offsetWidth);
			if (scrollWidth > offsetWidth) { // all but Explorer Mac
				x = scrollWidth;
			}
			else { // Explorer Mac - would also work in Explorer 6 Strict, Mozilla and Safari
				x = offsetWidth;
			}
			return x;
		},

		// returns the scrolling offset on both the horizontal and the vertical axis
		GetScrollingOffset: function() {
			var offset = new Object();
			offset["vertical"] = parseInt(RLdesign.Utils.GetVerticalScrollingOffset());
			offset["horizontal"] = parseInt(RLdesign.Utils.GetHorizontalScrollingOffset());
			return offset;
		},

		// returns the scrolling offset on the horizontal axis, measured from the left edge of the page
		GetHorizontalScrollingOffset: function(d) {
			var doc = (d) ? d : document;
			var x = null;
			if (self.pageXOffset) { // all except Explorer
				x = self.pageXOffset;
			}
			else if (doc.documentElement && doc.documentElement.scrollLeft) { // Explorer 6 Strict
				x = doc.documentElement.scrollLeft;
			}
			else if (doc.body) { // all other Explorers
				x = doc.body.scrollLeft;
			}
			return parseInt(x);
		},

		// returns the scrolling offset on the vertical axis, measured from the top of the page
		GetVerticalScrollingOffset: function(d) {
			var doc = (d) ? d : document;
			var y = null;
			if (self.pageYOffset) { // all except Explorer
				y = self.pageYOffset;
			}
			else if (doc.documentElement && doc.documentElement.scrollTop) { // Explorer 6 Strict
				y = doc.documentElement.scrollTop;
			}
			else if (doc.body) { // all other Explorers
				y = doc.body.scrollTop;
			}
			return parseInt(y);
		},

		// returns the key that was pressed as a string
		GetTypedKey: function(e) {
			var charcode = RLdesign.Utils.DefineEventKeyCode(e);
			return String.fromCharCode(charcode);
		},

		// returns the height and width of the current browser window
		GetWindowSize: function() {
			var size = new Object();
			size["width"] = null;
			size["height"] = null;
			var y, x = null;
			if (self.innerHeight && self.innerWidth) { // all except Explorer
				y = self.innerHeight;
				x = self.innerWidth;
			}
			else if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth) { // Explorer 6 Strict Mode
				y = document.documentElement.clientHeight;
				x = document.documentElement.clientWidth;
			}
			else if (document.body) { // other Explorers
				y = document.body.clientHeight;
				x = document.body.clientWidth;
			}
			size["height"] = parseInt(y);
			size["width"] = parseInt(x);
			return size;
		},

		// returns the height of the current browser window
		GetWindowHeight: function() {
			var y = null;
			if (self.innerHeight) { // all except Explorer
				y = self.innerHeight;
			}
			else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
				y = document.documentElement.clientHeight;
			}
			else if (document.body) { // other Explorers
				y = document.body.clientHeight;
			}
			return parseInt(y);
		},

		// returns the width of the current browser window
		GetWindowWidth: function() {
			var x = null;
			if (self.innerWidth) { // all except Explorer
				x = self.innerWidth;
			}
			else if (document.documentElement && document.documentElement.clientWidth) { // Explorer 6 Strict Mode
				x = document.documentElement.clientWidth;
			}
			else if (document.body) { // other Explorers
				x = document.body.clientWidth;
			}
			return parseInt(x);
		},

		// returns the z-index value of the given object
		GetZIndex: function(obj) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null) return;
			var cStyle = null;
			if (elem.currentStyle) {
				cStyle = elem.currentStyle;
			}
			else if (document.defaultView && document.defaultView.getComputedStyle) {
				cStyle = document.defaultView.getComputedStyle(elem, "");
			}
			var sNum;
			if (cStyle) {
				sNum = Number(cStyle.zIndex);
			}
			else {
				sNum = Number(elem.style.zIndex);
			}
			return sNum;
		},

		InsertAfter: function(parent, node, referenceNode) {
			parent.insertBefore(node, referenceNode.nextSibling);
		},

		// inserts a given textstring at the cursors position in a textfield
		InsertAtCursor: function(obj, sValue, bSelectRange) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null || (elem.tagName.toLowerCase() != "textarea" && (elem.tagName.toLowerCase() != "input") || elem.type.toLowerCase() != "text")) return;
			if (document.selection) {
				elem.focus();
				sel = document.selection.createRange();
				sel.text = sValue;
				if (bSelectRange) {
					sel = document.selection.createRange();
					sel.moveStart("character", sValue.length * -1);
					sel.moveEnd("character", 0);
					sel.select();
				}
			}
			else if (elem.selectionStart || elem.selectionStart == "0") {
				var startPos = elem.selectionStart;
				var endPos = elem.selectionEnd;
				elem.value = elem.value.substring(0, startPos) + sValue + elem.value.substring(endPos, elem.value.length);
				if (bSelectRange) {
					elem.setSelectionRange(startPos, startPos + 1);
				}
				elem.focus();
			}
			else {
				elem.value += sValue;
				elem.focus();
			}
		},

		// evaluates whether the given argument object is an array, returning a boolean
		IsArray: function(a) {
			return (a && a.length && typeof (a) != "string" && !a.tagName && !a.alert && typeof (a[0]) != "undefined");
		},

		// evaluates whether the given argument object is an object, returning a boolean
		IsObject: function(o) {
			return (o && o.length == undefined && typeof (o) == "object" && !o.alert);
		},

		// returns a boolean representation of whether the given argument is numeric
		IsNumeric: function(sInput) {
			var sValidChars = "0123456789,.-";
			sInput = sInput.toString();
			if (sInput.length == 0) return false;
			var bResult = true;
			for (var i = 0; i < sInput.length; i++) {
				cChar = sInput.charAt(i);
				if (sValidChars.indexOf(cChar) == -1) {
					bResult = false;
					break;
				}
			}
			return bResult;
		},

		// creates a given html element in memory and returns the element
		MakeElement: function(tag, attrs, text, d) {
			var doc = (d) ? d : document;
			var e = doc.createElement(tag);
			if (attrs) {
				for (var key in attrs) {
					if (typeof (attrs[key]) == "function") continue; // defeat bug caused by augmenting arrays with custom functions
					if (key == "class") { e.className = attrs[key]; }
					else if (key == "id") { e.id = attrs[key]; }
					else { e.setAttribute(key, attrs[key]); }
				}
			}
			if (text) e.appendChild(doc.createTextNode(text));
			return e;
		},

		OpenPopup: function(url, name, parameters, size, position) {
			var popup = null;

			var p = null;
			if (parameters != null) {
				p = "";
				if (RLdesign.Utils.IsObject(parameters)) {
					for (var key in parameters) {
						p += key + "=" + parameters[key] + ",";
					}
					if (p.lastIndexOf(",") == p.length - 1) p = p.substring(0, p.length - 1);
				}
				else {
					p = parameters;
				}
			}

			var pw = 0;
			var ph = 0;

			if (size != null && RLdesign.Utils.IsObject(size)) {
				if (p == null) { p = ""; }
				else if (p.indexOf("height") != -1 || p.indexOf("width") != -1) {
					alert("Cannot set the size of the popup window twice.");
					return null;
				}
				if (size["width"].indexOf("%") != -1) {
					size["width"] = size["width"].replace("%", "");
					var scrw = screen.width;
					size["width"] = parseInt((scrw / 100) * size["width"]);
				}
				if (size["height"].indexOf("%") != -1) {
					size["height"] = size["height"].replace("%", "");
					var scrh = screen.height;
					size["height"] = parseInt((scrh / 100) * size["height"]);
				}
				if (p != "") p += ",";
				p += "width=" + size["width"];
				p += ",height=" + size["height"];
				pw = parseInt(size["width"]);
				ph = parseInt(size["height"]);
			}

			if (arguments.length == 0) {
				popup = window.open();
			}
			else if (url != null && name != null && p != "") {
				popup = window.open(url, name, p);
			}
			else if (url != null && name != null) {
				popup = window.open(url, name);
			}
			else if (url != null) {
				popup = window.open(url);
			}

			if (position != null && RLdesign.Utils.IsObject(position)) {
				if (position["left"].indexOf("%") != -1) {
					position["left"] = position["left"].replace("%", "");
					var scrw = screen.width;
					position["left"] = parseInt(((scrw - pw) / 100) * position["left"]);
				}
				if (position["top"].indexOf("%") != -1) {
					position["top"] = position["top"].replace("%", "");
					var scrh = screen.height;
					position["top"] = parseInt(((scrh - ph) / 100) * position["top"]);
				}
				popup.focus();
				popup.moveTo(parseInt(position["left"]), parseInt(position["top"]));
			}

			return popup;
		},


		PrependChild: function(parent, node) {
			parent.insertBefore(node, parent.firstChild);
		},

		// Enables moving of list items in elements such as <ul>, <ol> and <table>
		ReorderList: function(obj, delta, ListType, postFunction, postPassAlong) {
			var o = RLdesign.Utils.DefineObject(obj);
			if (!ListType) ListType = "ul";
			else ListType = ListType.toLowerCase();
			var tagName = (ListType.toLowerCase() == "table") ? "tr" : "li";
			var iDelta = parseInt(delta);

			var row = o;
			//			do {
			//				row = row.parentNode;
			//			} while (row.tagName.toLowerCase() != tagName);
			while (row.tagName.toLowerCase() != tagName) {
				row = row.parentNode;
			}

			var list = row.parentNode;

			RLdesign.Utils.ClearWhiteSpace(list);

			var bFirst = false;
			var bLast = false;
			var bSecLast = false;

			for (var i = 0; i < list.childNodes.length; i++) {
				if (list.childNodes[i] === row && i == 0) {
					bFirst = true;
					break;
				}
				if (list.childNodes[i] === row && i == (list.childNodes.length - 1)) {
					bLast = true;
					break;
				}
				if (list.childNodes[i] === row && i == (list.childNodes.length - 2)) {
					bSecLast = true;
					break;
				}
			}

			// move down
			if (iDelta < 0) {
				if (bLast) { // insert at top
					list.removeChild(row);
					list.insertBefore(row, list.childNodes[0]);
				}
				else if (bSecLast) { // insert at end
					list.removeChild(row);
					list.appendChild(row);
				}
				else { // move one down
					try {
						var next = row.nextSibling.nextSibling;
						list.removeChild(row);
						list.insertBefore(row, next);
						var next = null;
					}
					catch (ex) {
						// if failed, probably means that the list-item is a single child, and thus shouldn't be moved
					}
				}
			}
			// move up
			else if (iDelta > 0) {
				if (bFirst) { // insert at bottom
					list.removeChild(row);
					list.appendChild(row);
				}
				else { // move one up
					var prev = row.previousSibling;
					list.removeChild(row);
					list.insertBefore(row, prev);
					prev = null;
				}
			}

			if (postFunction != null && typeof (postFunction) == "function") {
				postFunction(postPassAlong);
			}
			list = row = o = null;
		},

		// Enables moving of options in a select
		ReorderSelectOption: function(obj, delta) {
			var oSelect = RLdesign.Utils.DefineObject(obj);
			if (oSelect == null || oSelect.tagName.toLowerCase() != "select" || oSelect.options.length <= 0) return false; // end if not <select>
			var bMoveAbsolute = (arguments.length == 4) ? !!(arguments[3]) : false;
			var iOptionIndex = (arguments.length >= 3) ? parseInt(arguments[2]) : oSelect.selectedIndex;
			var iDelta = parseInt(delta);

			var iNewIndex = null;

			// determine which index position the selected option should be moved to:
			// if absolute position and not out of range
			if (bMoveAbsolute && iDelta >= 0 && iDelta < oSelect.options.length) {
				iNewIndex = iDelta;
			}
			// if relative position - outer edge values should loop around to other end of list
			// ie. move down from start, go to end and vice versa
			else if (iDelta == 1 || iDelta == -1) {
				iNewIndex = (iOptionIndex + iDelta);
				if (iNewIndex == -1) { iNewIndex = oSelect.options.length - 1; }
				else if (iNewIndex == oSelect.options.length) { iNewIndex = 0; }
			}
			// if all else fails, set new index the same as old index
			else {
				iNewIndex = iOptionIndex;
			}

			if (iNewIndex == iOptionIndex) return false; // end if old index == new index, thus no reason to go further

			var tmpOption = oSelect.options[iOptionIndex]; // build temp option in memory to store information
			oSelect.options[iOptionIndex] = new Option(oSelect.options[iNewIndex].text, oSelect.options[iNewIndex].value);
			oSelect.options[iNewIndex] = new Option(tmpOption.text, tmpOption.value);
			oSelect.selectedIndex = iNewIndex;

			tmpOption = oSelect = null;
			return true;
		},

		// replaces a given set of characters starting at the cursors position in a textfield
		ReplaceAfterCursor: function(obj, sValue, iLength, bSelectRange) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null || (elem.tagName.toLowerCase() != "textarea" && (elem.tagName.toLowerCase() != "input") || elem.type.toLowerCase() != "text")) return;
			if (iLength == null) iLength = sValue.length;
			if (document.selection) {
				elem.focus();
				var sel = document.selection.createRange();
				if (sel.text.length != iLength) {
					sel.moveStart("character", 0);
					sel.moveEnd("character", iLength);
				}
				sel.select();
				sel.text = sValue;
				if (bSelectRange) {
					sel = document.selection.createRange();
					sel.moveStart("character", 0);
					sel.moveEnd("character", sValue.length);
				}
				sel.select();
			}
			else if (elem.selectionStart || elem.selectionStart == "0") {
				var startPos = elem.selectionStart;
				var endPos = elem.selectionEnd;
				elem.value = elem.value.substring(0, startPos) + sValue + elem.value.substring(endPos, elem.value.length);
				if (bSelectRange) {
					elem.setSelectionRange(startPos, startPos + iLength);
				}
				elem.focus();
			}
		},

		// replaces a given set of characters ending at the cursors position in a textfield
		ReplaceBeforeCursor: function(obj, sValue, iLength, bSelectRange) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null || (elem.tagName.toLowerCase() != "textarea" && (elem.tagName.toLowerCase() != "input") || elem.type.toLowerCase() != "text")) return;
			if (iLength == null) iLength = sValue.length;
			if (document.selection) {
				elem.focus();
				var sel = document.selection.createRange();
				sel.moveStart("character", iLength * -1);
				sel.moveEnd("character", 0);
				sel.select();
				sel.text = sValue;
				if (bSelectRange) {
					sel = document.selection.createRange();
					sel.moveStart("character", sValue.length * -1);
					sel.moveEnd("character", 0);
				}
				sel.select();
			}
			else if (elem.selectionStart || elem.selectionStart == "0") {
				var startPos = obj.selectionStart - iLength;
				if (startPos < 0) startPos = 0;
				var endPos = elem.selectionEnd;
				elem.value = elem.value.substring(0, startPos) + sValue + elem.value.substring(endPos, elem.value.length);
				elem.focus();
			}
		},

		// rounds a number off to a default of two decimal places.
		// The amount decimals can be customized by providing a second (optional) argument in the form of an int
		RoundNumber: function(fNumber) {
			var rlength = (arguments.length == 2) ? parseInt(arguments[1]) : 2; // The number of decimal places to round to
			var returnNumber = parseFloat(fNumber);
			if (returnNumber.toFixed) { // if browser supports js 1.5 method of rounding
				returnNumber = returnNumber.toFixed(rlength);
			}
			else { // if legacy browser
				if (returnNumber > 8191 && returnNumber < 10485) { // workaround for javascript bug
					returnNumber = returnNumber - 5000;
					returnNumber = Math.round(returnNumber * Math.pow(10, rlength)) / Math.pow(10, rlength);
					returnNumber = returnNumber + 5000;
				}
				else {
					returnNumber = Math.round(returnNumber * Math.pow(10, rlength)) / Math.pow(10, rlength);
				}
			}
			return returnNumber;
		},

		// sets the cursors position relative to its current position in a textfield
		SetRelativeCursorPosition: function(obj, iLength) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null || (elem.tagName.toLowerCase() != "textarea" && (elem.tagName.toLowerCase() != "input") || elem.type.toLowerCase() != "text")) return;
			if (document.selection) {
				elem.focus();
				var sel = document.selection.createRange();
				sel.moveStart("character", iLength);
				sel.moveEnd("character", iLength);
				sel.select();
			}
			else if (elem.selectionStart || elem.selectionStart == "0") {
				var startPos = elem.selectionStart + iLength;
				elem.setSelectionRange(startPos, startPos);
				elem.focus();
			}
		},

		// selects a range of characters in a textfield
		SelectRange: function(obj, iStart, iLength) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null || (elem.tagName.toLowerCase() != "textarea" && (elem.tagName.toLowerCase() != "input") || elem.type.toLowerCase() != "text")) return;
			if (elem.createTextRange) {
				var oRange = elem.createTextRange();
				oRange.moveStart("character", iStart);
				oRange.moveEnd("character", iLength - elem.value.length);
				oRange.select();
			}
			else if (elem.selectionStart || elem.selectionStart == "0") {
				elem.setSelectionRange(iStart, iLength);
			}
			elem.focus();
		},


		SetCookie: function(cookieName, cookieValue, nDays) {
			var today = new Date();
			var expire = new Date();
			if (nDays == null || nDays == 0) nDays = 1;
			expire.setTime(today.getTime() + (nDays * 24 * 60 * 60 * 1000));
			document.cookie = cookieName + "=" + escape(cookieValue) + ";expires=" + expire.toGMTString();
		},

		GetCookie: function(cookieName) {
			var nameEQ = cookieName + "=";
			var ca = document.cookie.split(';');
			for (var i = 0; i < ca.length; i++) {
				var c = ca[i];
				while (c.charAt(0) == ' ') c = c.substring(1, c.length);
				if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
			}
			return null;
		},

		RemoveCookie: function(cookieName) {
			RLdesign.Utils.SetCookie(cookieName, "", -1);
		},

		SetDisplay: function(obj, sParam) {
			obj = RLdesign.Utils.DefineObject(obj);
			obj.style.display = (sParam) ? sParam : "";
		},

		// sets opacity in a cross-browser sort-of way (doesn't work in pre-9 operas)
		SetOpacity: function(obj, iOpacity, bRelative, iDirection) {
			iDirection = (iDirection && iDirection < 0) ? -1 : 1;
			oElem = RLdesign.Utils.DefineObject(obj);
			if (oElem == null) return;
			if (bRelative) {
				iOpacity = iOpacity * iDirection;
				/*  NOT WORKING! - Figure out opacity when first time (should possibly be 100% but seen as "" (empty string)  */
				//						if (oElem.filter) oElem.filter.item("DXImageTransform.Microsoft.Alpha").opacity += iOpacity;
				//						oElem.style.KHTMLOpacity += (iOpacity / 100); // Safari<1.2, Konqueror
				//						oElem.style.MozOpacity += (iOpacity / 100); // Older Mozilla and Firefox
				//						oElem.style.opacity += (iOpacity / 100); // Safari 1.2, newer Firefox and Mozilla, CSS3
				oElem.style.opacity = oElem.style.opacity * 1 + (iOpacity / 100); // Safari 1.2, newer Firefox and Mozilla, CSS3
			}
			else {
				iOpacity = (iOpacity == 100) ? 99.999 : iOpacity;
				oElem.style.filter = "alpha(opacity:" + iOpacity + ")"; // IE/Win
				oElem.style.KHTMLOpacity = iOpacity / 100; // Safari<1.2, Konqueror
				oElem.style.MozOpacity = iOpacity / 100; // Older Mozilla and Firefox
				oElem.style.opacity = iOpacity / 100; // Safari 1.2, newer Firefox and Mozilla, CSS3
			}
		},

		Serialize: function(o, action, seperator, value) {
			var addtoobject = !(arguments.length > 4 && arguments[4]);
			var obj = (addtoobject) ? RLdesign.Utils.DefineObject(o) : o;
			var sval = (addtoobject) ? obj.value : obj;
			switch (action.toLowerCase()) {
				case "add":
					sval = (sval == "") ? value : sval + seperator + value;
					if (addtoobject)
						obj.value = sval;
					else
						return sval;
					break;
				case "remove":
					var aValues = sval.split(seperator);
					var sValues = "";
					for (var i = 0; i < aValues.length; i++) {
						if (aValues[i] != value) {
							sValues = (sValues == "") ? aValues[i] : sValues + seperator + aValues[i];
						}
					}
					if (addtoobject)
						obj.value = sValues;
					else
						return sValues;
					break;
				case "get":
				case "fetch":
					return sval.split(seperator);
					break;
				case "length":
				case "count":
				case "getlength":
				case "getcount":
					return sval.split(seperator).length;
					break;
				case "find":
				case "search":
					var aValues = sval.split(seperator);
					var sValues = "";
					for (var i = 0; i < aValues.length; i++) {
						if (aValues[i] === value) {
							return true;
						}
					}
					return false;
					break;
				case "empty":
				case "clear":
					sval = "";
					if (addtoobject)
						obj.value = sval;
					else
						return sval;
					break;
				case "fill":
					if (RLdesign.Utils.IsArray(value)) {
						var sval = "";
						for (var i = 0; i < value.length; i++) {
							if (value[i] != "") {
								sval = (i == 0) ? value[i] : sval + seperator + value[i];
							}
						}
					}
					if (addtoobject)
						obj.value = sval;
					else
						return sval;
					break;
			}
		},

		SetPositionToOffset: function(obj, horizontal, vertical) {
			var h = (horizontal === null) ? true : horizontal;
			var v = (vertical === null) ? true : vertical;
			var o = RLdesign.Utils.DefineObject(obj);
			if (!o) return;

			var position = new Object();
			position["left"] = parseInt(RLdesign.Utils.GetCssValue(o, "left"));
			position["top"] = parseInt(RLdesign.Utils.GetCssValue(o, "top"));
			var scroll = RLdesign.Utils.GetScrollingOffset();
			if (h) o.style.left = scroll["horizontal"] + position["left"] + "px";
			if (v) o.style.top = scroll["vertical"] + position["top"] + "px";
		},

		ToggleDisplay: function(obj, sProp) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null) return;

			if (sProp != undefined && sProp != null) {
				elem.style.display = sProp;
			}
			else {
				var sProp = RLdesign.Utils.GetCssValue(obj, "display");
				if (sProp == "none") elem.style.display = "block";
				else elem.style.display = "none";
			}
		},

		ToggleVisibility: function(obj, sProp) {
			var elem = RLdesign.Utils.DefineObject(obj);
			if (elem == null) return;

			if (sProp != undefined && sProp != null) {
				elem.style.visibility = sProp;
			}
			else {
				var sProp = RLdesign.Utils.GetCssValue(obj, "visibility");
				if (sProp == "hidden") elem.style.display = "visible";
				else elem.style.display = "hidden";
			}
		},

		// converts first character of textstring to uppercase while lowercasing the rest of the string
		UcFirst: function(sText) {
			var sLower = sText.toLowerCase();
			sFirst = sLower.substring(0, 1);
			sRest = sLower.substring(1);
			return sFirst.toUpperCase() + sRest;
		}
	}
} ();
