// events hjælpe klasse
// version 1.0.2.0
//
// CHANGELOG:
//
// 22/03/2009
//     1.0.2.0 -> 1.0.2.1
//           Fixed bug in PreloadHandler prohibiting removal of loaded events
// 04/03/2009
//     1.0.1.0 -> 1.0.2.0
//           Added shortcut library
// 21/03/2008
//     1.0.0.2 -> 1.0.1.0
//           Added functions ClearEventHandlers and ClearEventHandler which removes all events of a given type from the object
//           Changed behavior of RemoveEventHandler to set handler as null instead of "" [empty string] in legacy browsers
// 11/03/2008
//     1.0.0.1 -> 1.0.0.2
//           Removed dependancy on RLdesign.Utils.js
// 23/02/2008
//     1.0.0.0 -> 1.0.0.1
//           Added function RemoveEventHandler, which removes an eventhandler the same way it is set


if (!RLdesign) {
	var RLdesign = function() { };
}

if (!RLdesign.Utils) {
	RLdesign.Utils = function() {
		return {
			// 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.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");
			}
		}
	} ();
}


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();



/*
* http://www.openjs.com/scripts/events/keyboard_shortcuts/
* Version : 2.01.B
* By Binny V A
* License : BSD
*/
RLdesign.Shortcuts = {
	'all_shortcuts': {}, //All the shortcuts are stored in this array
	'Add': function(shortcut_combination, callback, opt) {
		//Provide a set of default options
		var default_options = {
			'type': 'keydown',
			'propagate': false,
			'disable_in_input': false,
			'target': document,
			'keycode': false
		}
		if (!opt) opt = default_options;
		else {
			for (var dfo in default_options) {
				if (typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
			}
		}

		var ele = opt.target;
		if (typeof opt.target == 'string') ele = document.getElementById(opt.target);
		var ths = this;
		shortcut_combination = shortcut_combination.toLowerCase();

		//The function to be called at keypress
		var func = function(e) {
			e = e || window.event;

			if (opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
				var element;
				if (e.target) element = e.target;
				else if (e.srcElement) element = e.srcElement;
				if (element.nodeType == 3) element = element.parentNode;

				if (element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
			}

			//Find Which key is pressed
			if (e.keyCode) code = e.keyCode;
			else if (e.which) code = e.which;
			var character = String.fromCharCode(code);

			if (code == 188) character = ","; //If the user presses , when the type is onkeydown
			if (code == 190) character = "."; //If the user presses , when the type is onkeydown

			var keys = shortcut_combination.split("+");
			//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
			var kp = 0;

			//Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
			var shift_nums = {
				"`": "~",
				"1": "!",
				"2": "@",
				"3": "#",
				"4": "$",
				"5": "%",
				"6": "^",
				"7": "&",
				"8": "*",
				"9": "(",
				"0": ")",
				"-": "_",
				"=": "+",
				";": ":",
				"'": "\"",
				",": "<",
				".": ">",
				"/": "?",
				"\\": "|"
			}
			//Special Keys - and their codes
			var special_keys = {
				'esc': 27,
				'escape': 27,
				'tab': 9,
				'space': 32,
				'return': 13,
				'enter': 13,
				'backspace': 8,

				'scrolllock': 145,
				'scroll_lock': 145,
				'scroll': 145,
				'capslock': 20,
				'caps_lock': 20,
				'caps': 20,
				'numlock': 144,
				'num_lock': 144,
				'num': 144,

				'pause': 19,
				'break': 19,

				'insert': 45,
				'home': 36,
				'delete': 46,
				'end': 35,

				'pageup': 33,
				'page_up': 33,
				'pu': 33,

				'pagedown': 34,
				'page_down': 34,
				'pd': 34,

				'left': 37,
				'up': 38,
				'right': 39,
				'down': 40,

				'f1': 112,
				'f2': 113,
				'f3': 114,
				'f4': 115,
				'f5': 116,
				'f6': 117,
				'f7': 118,
				'f8': 119,
				'f9': 120,
				'f10': 121,
				'f11': 122,
				'f12': 123
			}

			var modifiers = {
				shift: { wanted: false, pressed: false },
				ctrl: { wanted: false, pressed: false },
				alt: { wanted: false, pressed: false },
				meta: { wanted: false, pressed: false}	//Meta is Mac specific
			};

			if (e.ctrlKey) modifiers.ctrl.pressed = true;
			if (e.shiftKey) modifiers.shift.pressed = true;
			if (e.altKey) modifiers.alt.pressed = true;
			if (e.metaKey) modifiers.meta.pressed = true;

			for (var i = 0; k = keys[i], i < keys.length; i++) {
				//Modifiers
				if (k == 'ctrl' || k == 'control') {
					kp++;
					modifiers.ctrl.wanted = true;

				} else if (k == 'shift') {
					kp++;
					modifiers.shift.wanted = true;

				} else if (k == 'alt') {
					kp++;
					modifiers.alt.wanted = true;
				} else if (k == 'meta') {
					kp++;
					modifiers.meta.wanted = true;
				} else if (k.length > 1) { //If it is a special key
					if (special_keys[k] == code) kp++;

				} else if (opt['keycode']) {
					if (opt['keycode'] == code) kp++;

				} else { //The special keys did not match
					if (character == k) kp++;
					else {
						if (shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
							character = shift_nums[character];
							if (character == k) kp++;
						}
					}
				}
			}

			if (kp == keys.length &&
							modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
							modifiers.shift.pressed == modifiers.shift.wanted &&
							modifiers.alt.pressed == modifiers.alt.wanted &&
							modifiers.meta.pressed == modifiers.meta.wanted) {
				callback(e);

				if (!opt['propagate']) { //Stop the event
					//e.cancelBubble is supported by IE - this will kill the bubbling process.
					e.cancelBubble = true;
					e.returnValue = false;

					//e.stopPropagation works in Firefox.
					if (e.stopPropagation) {
						e.stopPropagation();
						e.preventDefault();
					}
					return false;
				}
			}
		}
		this.all_shortcuts[shortcut_combination] = {
			'callback': func,
			'target': ele,
			'event': opt['type']
		};
		//Attach the function with the event
		if (ele.addEventListener) ele.addEventListener(opt['type'], func, false);
		else if (ele.attachEvent) ele.attachEvent('on' + opt['type'], func);
		else ele['on' + opt['type']] = func;
	},

	//Remove the shortcut - just specify the shortcut and I will remove the binding
	'Remove': function(shortcut_combination) {
		shortcut_combination = shortcut_combination.toLowerCase();
		var binding = this.all_shortcuts[shortcut_combination];
		delete (this.all_shortcuts[shortcut_combination])
		if (!binding) return;
		var type = binding['event'];
		var ele = binding['target'];
		var callback = binding['callback'];

		if (ele.detachEvent) ele.detachEvent('on' + type, callback);
		else if (ele.removeEventListener) ele.removeEventListener(type, callback, false);
		else ele['on' + type] = false;
	}
}