// Foldout menu class
// 
// Copyright 2008-2009 René Lønstrup @ RLdesign.dk
// 
// May not be used commercially without permission from the author.
//
//
// Version 1.0.0.3
//
//
// CHANGELOG:
//
// 09/06/2009
//     1.0.0.3
//           Removed dependancy on other library files
// 13/04/2009
//     1.0.0.2
//           Tweaked the hide timeout a bit to respond faster
// 29/01/2009
//     1.0.0.1
//           Added possibility to alter the waittime before the submenus disappear via a global variable
// 15/09/2008
//     1.0.0.0
//           Initial release


if (!RLdesign) {
	var RLdesign = function() { };
}
if (!RLdesign.Web) {
	RLdesign.Web = function() { };
}
if (!RLdesign.Web.UI) {
	RLdesign.Web.UI = function() { };
}
if (!RLdesign.Web.UI.Forms) {
	RLdesign.Web.UI.Forms = function() { };
}
RLdesign.Web.UI.Forms.FoldoutMenu = function() {
	return {
		Init: function() {
			var menus = RLdesign.Web.Dom.Extensions.GetElementsByClassName("foldoutmenu");
			for (var i = 0; i < menus.length; i++) {
				RLdesign.Web.UI.Forms.FoldoutMenu.InitMenu(menus[i]);
			}
		},

		InitMenu: function(objUl) {
			var childUls = objUl.getElementsByTagName("ul");
			for (var i = 0; i < childUls.length; i++) {
				RLdesign.Utils.ToggleDisplay(childUls[i], "none");
				RLdesign.Web.UI.Forms.FoldoutMenu.SetEventHandler(childUls[i]);
			}
		},

		SetEventHandler: function(obj) {
			var parent = obj.parentNode;
			RLdesign.Events.SetEventHandler(parent, "mouseover", function() { RLdesign.Web.UI.Forms.FoldoutMenu.Show(obj); });
			RLdesign.Events.SetEventHandler(parent, "mouseout", function() { RLdesign.Web.UI.Forms.FoldoutMenu.Hide(obj); });
			parent = null;
		},

		Show: function(obj) {
			window.clearTimeout(obj.outtimer);
			RLdesign.Utils.ToggleDisplay(obj, "block");
		},
		Hide: function(obj) {
			var timeoutvalue = null;
			if (window["RLdesignFoldoutMenuTimeout"] != null) timeoutvalue = parseInt(RLdesignFoldoutMenuTimeout);
			if (timeoutvalue == null) timeoutvalue = 50;
			obj.outtimer = window.setTimeout(function() { RLdesign.Utils.ToggleDisplay(obj, "none") }, timeoutvalue);
		}
	}
} ();



if (!RLdesign.Utils) {
	RLdesign.Utils = function() {
		return {
			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";
				}
			},
			
			// 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;
			},
			
			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;
			},
			
			// returns a crossbrowser event object
			DefineEvent: function(e) {
				return e || window.event;
			},

			// 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) {
				var elem = null;
				if (typeof(el) == "object") elem = el;
				else if (document.getElementById(el)) elem = document.getElementById(el);
				else if (typeof(el) == "string" && (el.indexOf("(") || el.indexOf("["))) eval("elem = " + el);
				else if (typeof(el) == "string") eval("elem = " + el);
				return elem;
			},
			
			// 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);
			}
		}
	} ();
}



if (!RLdesign.Web) {
	RLdesign.Web = function() { };
}
if (!RLdesign.Web.Dom) {
	RLdesign.Web.Dom = function() { };
}
if (!RLdesign.Web.Dom.Extensions) {
	RLdesign.Web.Dom.Extensions = function() {
		return {
			GetElementsByClassName: function(c) {
				var oBase = (arguments.length >= 2) ? arguments[1] : document;
				var oGetChildren = (arguments.length >= 3) ? !!arguments[2] : true;
				var cl = c; //.toLowerCase(); // removed case-insensitivity
				var aE = new Array();
				this.findElements = function() {
					var oCurrent = (arguments.length == 1) ? arguments[0] : oBase;
					var i = 0;
					// klasse sammenligning
					if (oCurrent) {
						if (oCurrent.className) {
							var sCl = oCurrent.className; //.toLowerCase(); // removed case-insensitivity
							if (sCl.indexOf(cl) >= 0) {
								var arrCl = sCl.split(" ");
								for (i = 0; i < arrCl.length; i++) {
									if (arrCl[i] == cl) {
										aE[aE.length] = oCurrent;
										break;
									}
								}
							}
						}
						if (oGetChildren === true) {
							// fortsætter på childNodes
							var children = oCurrent.childNodes;
							for (i = 0; i < children.length; i++) {
								if (children[i].tagName != undefined) this.findElements(children[i]);
							}
						}
					}
				}
				this.findElements();
				return aE;
			}
		}
	} ();
}




if (!RLdesign.Arrays) {
	RLdesign.Arrays = function() {
		return {
			// Removes the index from the array
			// Usage:
			// RLdesign.Arrays.RemoveElement(myArray, 3);
			RemoveElement: function(haystack, index) {
				var size = haystack.length;
				if (parseInt(index) != "NaN" && index >= 0 && index < size) {
					for (i = index; i < size; i++) {
						haystack[i] = haystack[i + 1];
					}
					haystack.length = size - 1;
				}
				return haystack;
			},


			// Searches for needle in array (haystack), returns true if found, false if not
			// Usage:
			// var found = RLdesign.Arrays.InArray(myArray, "foo");
			InArray: function(haystack, needle) {
				for (var i = 0; i < haystack.length; i++) {
					if (haystack[i] === needle) return true;
				}
				return false;
			},


			// Equivalent to InArray, but searches associative arrays instead
			InAssociativeArray: function(haystack, needle) {
				if (haystack[needle] != undefined) return true;
				return false;
			},


			// Returns the index of needle in array (haystack) if found, otherwise returns null
			// Usage:
			// var index = RLdesign.Arrays.GetRowIndex(myArray, "foo");
			GetRowIndex: function(haystack, needle) {
				var size = haystack.length;
				var index = null;
				for (var i = 0; i < size; i++) {
					if (haystack[i] == needle) {
						index = i;
						break;
					}
				}
				return index;
			},


			// Returns true if array (haystack) is empty, or false if not
			// Usage:
			// var empty = RLdesign.Arrays.IsEmpty(myArray);
			// if (!empty) {  }
			IsEmpty: function(haystack) {
				var len = haystack.length;
				if (len == 0) {
					for (var key in this) {
						len++;
					}
				}
				return (len > 0) ? false : true;
			},


			// Searches for an array (needle) inside another array (haystack) returning true if found, false if not
			// Usage:
			// var exists = RLdesign.Arrays.FindArray(myArray, myInnerArray);
			// if (exists) {  }
			FindArray: function(haystack, needle) {
				var iNeedleSize = needle.length;
				var iSize = haystack.length;
				var bFound = false;
				var iFoundIndex = null;
				for (var i = 0; i < iSize; i++) {
					if (!bFound && RLdesign.Arrays.IsArray(haystack[i]) && haystack[i].length == iNeedleSize) {
						for (var y = 0; y < iNeedleSize; y++) {
							if (needle[y] == haystack[i][y]) {
								bFound = true;
							}
							else {
								bFound = false;
								break;
							}
						}
					}
					if (bFound) {
						iFoundIndex = i;
						break;
					}
				}
				return bFound;
			},


			// 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");
			}
		}
	} ();
}
if (!RLdesign.Events) {
	RLdesign.Events = function() {
		var bPageLoaded = false;
		var aHandlers = new Array();
		var aStartASAPCollection = new Array();
		var aEventsToBeConnected = new Array();
		var aEventsConnected = new Array();
		var iTIMEOUT = 50;
		return {
			// attempts to run an userdefined function (fn) when the specified object (obj) has been loaded in memory
			StartWhenLoaded: function(obj, fn) {
				// fn should be referenced as a string representation of the functions name
				// if it is not, we will attempt to extract the function name from the function
				if (typeof (fn) != "string") {
					var sFn = fn.toString();
					var iStart = sFn.indexOf("function") + 9;
					var iEnd = sFn.indexOf("(");
					fn = sFn.substring(iStart, iEnd);
					if (fn == "") return false;
				}
				// if obj was referenced as a string, it will be checked for the correct syntax for this use
				if (typeof (obj) == "string" && obj.indexOf("'")) {
					obj = obj.replace(/'/g, "\"");
				}
				// saves info about what we are about to do
				var aStartASAP = new Array(obj, fn);
				if (!aStartASAPCollection.findArray(aStartASAP)) {
					aStartASAPCollection.push(aStartASAP);
				}
				// tries to extract an object from obj
				var elem = RLdesign.Utils.DefineObject(obj);
				if (elem) {
					// if object referenced was an array it will be iterated through 
					// to see if any of the objects is ready loaded and if so,
					// the function will be executed
					// and information on the objects and the function will be saved
					if (RLdesign.Utils.IsArray(elem)) {
						var elemA = null;
						for (var i = 0; i < elem.length; i++) {
							aEventsToBeConnected = new Array(elem[i], fn);
							elemA = RLdesign.Utils.DefineObject(obj);
							if (elemA) {
								if (!aEventsConnected.findArray(aEventsToBeConnected)) {
									aEventsConnected.push(aEventsToBeConnected);
									eval(fn + "(elem[i])");
								}
							}
						}
					}
					// if obj is a single object, the function will be executed directly
					// and information on the object and the function will be saved
					else if (RLdesign.Utils.IsObject(elem)) {
						aEventsToBeConnected = new Array(elem, fn);
						if (!aEventsConnected.findArray(aEventsToBeConnected)) {
							aEventsConnected.push(aEventsToBeConnected);
							eval(fn + "(elem)");
						}
					}
				}
				// recursive runs until page has been loaded
				if (!bPageLoaded) {
					setTimeout("RLdesign.Events.StartWhenLoaded('" + obj + "','" + fn + "')", iTIMEOUT);
				}
			},

			/// <summary>
			/// Appends an user-defined event handler of the specified type to the object
			/// </summary>
			/// <param name="obj">The object to append the eventhandler to - can be either an object or a stringID</param>
			/// <param name="sEventType">A string-representation of the eventtype - e.g. "load" for the onload-event, "click" for onclick and so on</param>
			/// <param name="funktion">The function to run - can be either a function or a string-representation of the function (beta)</param>
			/// <param name="bOptionalAvoidWrapping">Tells the script not to wrap the function - basically avoids returning anything</param>
			/// <returns>void</returns>
			SetEventHandler: function(obj, sEventType, funktion, bOptionalAvoidWrapping) {
				if (!bOptionalAvoidWrapping || bOptionalAvoidWrapping == undefined) var bOptionalAvoidWrapping = false;
				else bOptionalAvoidWrapping = true;

				if (typeof (funktion) == "string") bOptionalAvoidWrapping = true;

				// if the object is an array, we sets the eventhandler to every contained element, one at a time
				if (RLdesign.Utils.IsArray(obj)) {
					var ok = true;
					for (var i = 0; i < el.length; i++) {
						ok = (this.SetEventHandler(obj[i], sEventType, funktion) && ok);
					}
					return ok;
				}
				// if obj was referenced as a string, it will be checked for the correct syntax for this use
				// replaces ' (single pling) with " (quotation mark)
				if (typeof (obj) == "string" && obj.indexOf("'")) {
					obj = obj.replace(/'/g, "\"");
				}
				// tries to extract an object from obj
				var elem = RLdesign.Utils.DefineObject(obj);
				// if object doesn't exists yet, saves event-handler information to array
				if (elem == null) {
					var aHandler = new Array(obj, sEventType, funktion);
					aHandlers.push(aHandler);
					return false;
				}
				// wraps function in order to provide easy cross-browser event-trapping
				var fnWrapped = null;
				if (!bOptionalAvoidWrapping) {
					fnWrapped = function(e) {
						return funktion(RLdesign.Utils.DefineEvent(e));
					};
				}
				else {
					//				alert(typeof(funktion));
					fnWrapped = (typeof (funktion) == "string") ? eval(funktion) : funktion;
				}
				// adding event-handler to element
				if (elem.addEventListener) { elem.addEventListener(sEventType, fnWrapped, false); }
				else if (elem.attachEvent) { elem.attachEvent("on" + sEventType, fnWrapped); }
				else {
					var ontype = "on" + sEventType;
					var old = elem[ontype];
					if (old) {
						elem[ontype] = function(event) {
							var res = old(event);
							return fnWrapped(event) && res;
						}
					}
					else { elem[ontype] = fnWrapped; }
				}
				return true;
			},

			RemoveEventHandler: function(obj, sEventType, funktion, bOptionalAvoidWrapping) {
				if (!bOptionalAvoidWrapping || bOptionalAvoidWrapping == undefined) var bOptionalAvoidWrapping = false;
				else bOptionalAvoidWrapping = true;
				// if the object is an array, we remove the eventhandler from every contained element, one at a time
				if (RLdesign.Utils.IsArray(obj)) {
					var ok = true;
					for (var i = 0; i < el.length; i++) {
						ok = (this.RemoveEventHandler(obj[i], sEventType, funktion) && ok);
					}
					return ok;
				}
				// if obj was referenced as a string, it will be checked for the correct syntax for this use
				// replaces ' (single pling) with " (quotation mark)
				if (typeof (obj) == "string" && obj.indexOf("'")) {
					obj = obj.replace(/'/g, "\"");
				}
				// tries to extract an object from obj
				var elem = RLdesign.Utils.DefineObject(obj);
				// if object doesn't exists yet, returns because then it isn't possible to remove an eventhandler that doesn't exist
				if (elem == null) return false;
				// wraps function in order to provide easy cross-browser event-trapping
				var fnWrapped = null;
				if (!bOptionalAvoidWrapping) {
					var fnWrapped = function(e) {
						return funktion(RLdesign.Utils.DefineEvent(e));
					};
				}
				else {
					fnWrapped = (typeof (funktion) == "string") ? eval(funktion) : funktion;
				}
				// removing event-handler to element
				if (elem.removeEventListener) { elem.removeEventListener(sEventType, fnWrapped, false); }
				else if (elem.detachEvent) { elem.detachEvent("on" + sEventType, fnWrapped); }
				else {
					var ontype = "on" + sEventType;
					elem[ontype] = null;
				}
				return true;
			},

			ClearEventHandlers: function(obj, aEventTypes) {
				aEvents = null;
				if (aEventTypes != null && typeof (aEventTypes) == "string") aEvents = [aEventTypes];
				if (aEventTypes == null) aEvents = ['abort', 'blur', 'change', 'error', 'focus', 'load', 'reset', 'resize', 'scroll', 'select', 'submit', 'unload', 'click', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'dblclick', 'keydown', 'keypress', 'keyup'];
				for (var i = 0; i < aEvents.length; i++) {
					RLdesign.Events.ClearEventHandler(obj, aEvents[i]);
				}
			},
			ClearEventHandler: function(o, sEventType) {
				var obj = RLdesign.Utils.DefineObject(o);
				if (obj == null) return;
				var onevent = "on" + sEventType;
				obj[onevent] = null;
			},


			// tries to add event-handlers to objects as soon as they are available to the DOM
			PreloadHandler: function() {
				var aDeletableHandlers = new Array();
				for (var i = 0; i < aHandlers.length; i++) {
					var aH = aHandlers[i];
					if (aH) {
						var oEl = RLdesign.Utils.DefineObject(aH[0]);
						if (oEl) {
							this.SetEventHandler(oEl, aH[1], aH[2]);
							aDeletableHandlers.push(i);
						}
					}
				}
				for (i = aDeletableHandlers.length; i > 0; i--) {
					RLdesign.Arrays.RemoveElement(aHandlers, RLdesign.Arrays.GetRowIndex(aDeletableHandlers[i - 1]));
				}
				if (!bPageLoaded) {
					setTimeout("RLdesign.Events.PreloadHandler()", iTIMEOUT);
				}
			},


			// sets a flag telling the rest of the class that the page has been loaded
			// also flushes all obsolete data from memory
			_load: function() {
				bPageLoaded = true;
				if (!RLdesign.Arrays.IsEmpty(aStartASAPCollection)) {
					for (var i = 0; i < aStartASAPCollection.length; i++) {
						RLdesign.Events.StartWhenLoaded(aStartASAPCollection[i][0], aStartASAPCollection[i][1]);
					}
				}
				if (!RLdesign.Arrays.IsEmpty(aHandlers)) {
					RLdesign.Events.PreloadHandler();
				}
			}
		};
	} ();
	RLdesign.Events.SetEventHandler(window, "load", RLdesign.Events._load);
	RLdesign.Events.PreloadHandler();
}



RLdesign.Events.SetEventHandler(window, "load", RLdesign.Web.UI.Forms.FoldoutMenu.Init);