/**
 * This file contains some global functions used for different kind of purposes in the application.
 * 
 * @version 2011-02-11
 * @author <a href="mailto:r.tennapel@griponservice.nl?SUBJECT=globalkit.js">R. ten Napel, ing.</a>
 **/

/**
 * A static class with all the global functions.
 **/
function GlobalKit() {}

/**
 * Check if an object is an array.
 * 
 * @see <a href="http://www.optimalworks.net/blog/2007/web-development/javascript/array-detection">Cross-browser JavaScript</a>
 * 
 * @param obj				The object to check.
 * 
 * @return					If the given object is an array.
 **/
GlobalKit.isArray = function(array) {
	//return (obj != null && obj != "undefined" && typeof obj.constructor != "undefined" && obj.constructor == Array);
	
	return !(!array || (!array.length || array.length == 0) || typeof array !== 'object' || !array.constructor || array.nodeType || array.item); 
};

/**
 * Retrieve the browser.
 * 
 * @return				The current browser.
 **/
GlobalKit.browser = function() {
	return navigator.appVersion;
};

/**
 * Retrieve the document body width.
 * Retrieved from <a href="http://www.howtocreate.co.uk/tutorials/javascript/browserwindow">Window size and scrolling</a>.
 * 
 * @return				The current body width.
 **/
GlobalKit.screenWidth = function() {
	var h = 0;
	
	// Non-IE:
	if (typeof(window.innerWidth) == "number") {
		h = window.innerWidth;
	// IE 6+ in 'standards compliant mode':
	} else if (document.documentElement && document.documentElement.clientWidth) {
		h = document.documentElement.clientWidth;
	// IE 4 compatible:
	} else if (document.body && document.body.clientWidth) {
		h = document.body.clientWidth;
	}
	
	return h;
};

/**
 * Retrieve the document body height.
 * Retrieved from <a href="http://www.howtocreate.co.uk/tutorials/javascript/browserwindow">Window size and scrolling</a>.
 * 
 * @return				The current body height.
 **/
GlobalKit.screenHeight = function() {
	var h = 0;
	
	// Non-IE:
	if (typeof(window.innerHeight) == "number") {
		w = window.innerHeight;
	// IE 6+ in 'standards compliant mode':
	} else if (document.documentElement && document.documentElement.clientHeight) {
		w = document.documentElement.clientHeight;
	// IE 4 compatible:
	} else if (document.body && document.body.clientHeight) {
		w = document.body.clientHeight;
	}
	
	return w;
};

/**
 * Retrieve the scroll position of the document.
 * 
 * @return				The X & Y positions of the scroll.
 **/
GlobalKit.scrollPos = function() {
	var iebody = (document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body;
	var dsocleft = document.all ? iebody.scrollLeft : pageXOffset;
	var dsoctop = document.all ? iebody.scrollTop : pageYOffset;
	
	return { x: dsocleft, y: dsoctop };
};

/**
 * Return the actual mouse position in the window.
 * 
 * @param e				The mouse event.
 * 
 * @return				The X & Y positions of the mouse.
 **/
GlobalKit.mousePos = function(e) {
	// If FireFox:
	if (e.pageX && e.pageY) {
		return { x: e.pageX, y: e.pageY};
	}
	
	// Define the X- & Y-coörds:
	var x = e.clientX;
	var y = e.clientY;
	
	// Adapt the coörds & check for IE6 compatibility:
	if (document.body) {
		x += (document.body.scrollLeft) ? document.body.scrollLeft : 0;
		x -= (document.body.clientLeft) ? document.body.clientLeft : 0;
		
		y += (document.body.scrollTop) ? document.body.scrollTop : 0;
		y -= (document.body.clientTop) ? document.body.clientTop : 0;
	}
	
	// IE:
	return { x: x, y: y };
};

/**
 * Stop event propagation.
 * 
 * @see <a href="http://www.openjs.com/articles/prevent_default_action/">Prevent de Defailt Action for an Event</a>
 * 
 * @return				False.
 **/
GlobalKit.stopEvent = function(e) {
	// When the event is null / not given catch the window event:
	if (!e) var e = window.event;
	
	// e.cancelBubble is supported by IE - this will kill the bubbling process:
	e.cancelBubble = true;
	e.returnValue = false;
	
	// e.stopPropagation works only in Firefox:
	if (e.stopPropagation) {
		e.stopPropagation();
		e.preventDefault();
	}
	
	return false;
};

/**
 * Converts a decimal value to hexadecimal.
 * 
 * @param value				The decimal value to convert.
 * 
 * @return					The converted value.
 **/
GlobalKit.d2h = function(value) {
	return parseInt(value).toString(16);
};

/**
 * Converts a hexadecimal value to decimal.
 * 
 * @param value				The hexadecimal value to convert.
 * 
 * @return					The converted value.
 **/
GlobalKit.h2d = function(value) {
	return parseInt(value, 16);
};

/**
 * Converts an rgb string which defines the rgb value of a background color to a hexadecimal value.
 * 
 * @param rgb				The RGB string (p.h. "rgb(23, 32, 12)").
 * 
 * @return					The hex-value (p.h. "#ff3344").
 **/
GlobalKit.rgb2h = function(rgb) {
	rgb = rgb.replace(/rgb\(|\)/g, "").split(",");
	
	// Retrieve the RGB integer values and convert to hex:
	rgb[0] = parseInt(rgb[0], 10).toString(16).toLowerCase();
	rgb[1] = parseInt(rgb[1], 10).toString(16).toLowerCase();
	rgb[2] = parseInt(rgb[2], 10).toString(16).toLowerCase();
	
	// Add 0 if the hex value is 1 in length:
	rgb[0] = (rgb[0].length == 1) ? "0" + rgb[0] : rgb[0];
	rgb[1] = (rgb[1].length == 1) ? "0" + rgb[1] : rgb[1];
	rgb[2] = (rgb[2].length == 1) ? "0" + rgb[2] : rgb[2];
	
	return "#" + rgb.join("");
};

/**
 * Returns a file size in human readable format, displaying one decimal when size is less than 10 units.
 * 
 * @param size				The (file) size to transform in words.
 * 
 * @return					The textual representation of the given size.
 **/
GlobalKit.filesize2words = function(size) {
	var count = 0;
	var format = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
	
	// Iterate through parts:
	while ((size / 1024) > 1 && count < 8) {
		size = size / 1024;
		count++;
	}
	
	// Calc the amount of decimals:
	if (size < 10) {
		decimals = 1;
	} else {
		decimals = 0;
	}
	
	return (Math.round(100 * size) / 100) + " " + format[count];
};

/**
 * Retrieve the depth of an object in the DOM.
 * 
 * @param obj				The object to scan.
 * 
 * @return					The depth of the object.
 **/
GlobalKit.getDOMDepth = function(obj) {
	var depth = 0;
	
	// Iterate through all the element's parents:
	if (obj.parentNode) {
		do {
			depth++;
		} while (obj = obj.parentNode);
	}
	
	return depth;
};

/**
 * Retrieve a textual element tree of a given element with a maximum element depth stated by maxDepth.
 * 
 * @param element			The element to search for nodes / childs.
 * @param maxDepth			The maximum recursive depth to search for nodes / childs.
 * 
 * @return					A textual representation of this element's node / child tree.
 **/
GlobalKit.getElementTree = function(element, maxDepth) {
	text = "";
	
	// If the maximum depth is set to 0 exit the function:
	if (element == null || maxDepth == 0) {
		return text;
	}
	
	// Retrieve all childs of the element and sort by key:
	/*var childs = new Array();
	for (var child in element) {
		// Ignore the debug child:
		if (key != "getElementTree" && key != "debug") {
			childs[childs.length] = child;
		}
	}
	childs.sort();*/
	
	// Write the table header:
	text += "<table style='background-color: #cccccc; border: 1px solid #000000;'>";
	
	// Iterate through all element childs:
	for (var param in element) {
		try {
			// Ignore HTML tags and linebreaks:
			var value = new String(element[param]);
			value = value.replaceAll("<script>", "");
			value = value.replaceAll("</script>", "");
			value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\n", "\n<br>");
			
			// Write the child:
			text += "<tr><td style='background-color: #eeeeee; vertical-align: top; border: 1px solid #000000; border-right: 1px solid #eeeeee; text-align: right;'>" + param + "</td><td style='background-color: #ffffff; vertical-align: top; border: 1px solid #000000; border-left: 1px solid #eeeeee;' valign='top' nowrap>";
			
			// If the child is an object go into it (recursively) else write the child value:
			if (GlobalKit.isArray(element[param])) {
				// Write the table header:
				text += "<table style='background-color: #cccccc; border: 1px solid #000000;'><tr><th colspan='2'>Array</th></tr>";
				
				for (var i = 0; i < element[param].length; i++) {
					// Write the child:
					text += "<tr><td style='background-color: #eeeeee; vertical-align: top; border: 1px solid #000000; border-right: 1px solid #eeeeee; text-align: right;'>" + i + "</td><td style='background-color: #ffffff; vertical-align: top; border: 1px solid #000000; border-left: 1px solid #eeeeee;' valign='top' nowrap>";
					text += element[param][i];
					text += "</td></tr>";
				}
				
				// Write the table footer:
				text += "</table>";
			} else if (typeof(element[param]) == "object") {
				text += GlobalKit.getElementTree(element[param], maxDepth - 1);
			} else {
				text += value;
			}
			
			// Write the child footer:
			text += "</td></tr>";
		} catch(error) {
			text += "<tr><td style='background-color: #eeeeee; vertical-align: top; border: 1px solid #000000; border-right: 1px solid #eeeeee; text-align: right;'>ERROR</td><td style='background-color: #ffffff; vertical-align: top; border: 1px solid #000000; border-left: 1px solid #eeeeee;' valign='top' nowrap>";
			text += error.descripton;
			text += "</td></tr>";
		}
	}
	
	// Write the table footer:
	text += "</table>";
	
	return text;
	
	/*// Write the table header:
	text += "<table style='background-color: #cccccc; border: 1px solid #000000;'>";
	
	// Iterate through all element childs:
	for (var i = 0; i < childs.length; i++) {
		try {
			// Retrieve the child key and value:
			var key = new String(childs[i]);
			var value = new String(element[key]);
			
			// Ignore HTML tags and linebreaks:
			value = value.replaceAll('<script>','');
			value = value.replaceAll('</script>','');
			value = value.replaceAll('</script>','');
			value = value.replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('\n','\n<br>');
			
			// Write the child:
			text += "<tr><td style='background-color: #eeeeee; vertical-align: top; border: 1px solid #000000; border-right: 1px solid #eeeeee; text-align: right;'>" + key + "</td><td style='background-color: #ffffff; vertical-align: top; border: 1px solid #000000; border-left: 1px solid #eeeeee;' valign='top' nowrap>";
			
			// If the child is an object go into it (recursively) else write the child value:
			if (typeof(element[key]) == "object") {
				text += getElementTree(element[key], maxDepth - 1);
			} else if (GlobalKit.isArray(element[key])) {
				text += "Array";
			} else {
				text += value;
			}
			
			text += "</td></tr>";
		} catch(error) {
			text += "<tr><td style='background-color: #eeeeee; vertical-align: top; border: 1px solid #000000; border-right: 1px solid #eeeeee; text-align: right;'>ERROR</td><td style='background-color: #ffffff; vertical-align: top; border: 1px solid #000000; border-left: 1px solid #eeeeee;' valign='top' nowrap>";
			text += error.descripiton;
			text += "</td></tr>";
		}
	}
	
	// Write the table footer:
	text += "</table>";
	
	return text;*/
};

/**
 * Disable text / item selection in an element.
 * 
 * @deprecated 2010-09-15
 * 
 * @param element		The element to disable the selection in.
 **/
GlobalKit.disableSelection = function(element) {
	// IE route:
	if (typeof element.onselectstart != "undefined") {
		element.onselectstart = function() {
			return false;
		};
	// Firefox route:
	} else if (typeof element.style.MozUserSelect!="undefined") {
		element.style.MozUserSelect = "none";
	// All other route (ie: Opera):
	} else {
		element.onmousedown = function() {
			return false;
		};
		
		element.style.cursor = "default";
	}
};

/**
 * Retrieve all input values, defined by tag name, of an element in the mode that is set.
 * 
 * @param element		The element.
 * @param tagName		The tag name to search for.
 * @param mode			The mode to retrieve the values (object, URL or JSON).
 * @param values		The object to append the values to.
 * 
 * @return				The object with param / values or the string (if stringify is set).
 **/
GlobalKit.getValues = function(element, tagName, mode, values) {
	// Return empty object if no element is set:
	if (!element) { return {}; }
	
	// Ensure tagName:
	tagName = !tagName ? "ALL" : tagName.toUpperCase();
	
	// Ensure mode:
	mode = !mode ? "OBJ" : mode.toUpperCase();
	
	// Create an empty values object if not set:
	var values = (!values) ? {} : values;
	
	// Search all tag names if set:
	if (tagName == "ALL") {
		// Merge all values of different tag types:
		values = GlobalKit.getValues(element, "SELECT", "OBJ", values);
		values = GlobalKit.getValues(element, "TEXTAREA", "OBJ", values);
		
		tagName = "INPUT";
	}
	
	// Retrieve all elements:
	var els = element.getElementsByTagName(tagName);	
	
	// Iterate through each element:
	for (var i = 0; i < els.length; i++) {
		var el = els[i];
		var param = el.name ? el.name : el.id;
		var value = el.value;
		
		if (el.type == "checkbox") {
			// Only set the var if checked:
			if (el.checked) {
				values[param] = value;
			}
		} else if (el.type == "hidden") {
			values[param] = value;
		} else if (el.type == "password") {
			values[param] = value;
		} else if (el.type == "radio") {
			// Only set the var if checked:
			if (el.checked) {
				values[param] = value;
			}
		} else if (el.type == "select-one") {
			values[param] = value;
		} else if (el.type == "select-multiple") {
			var options = [];
			
			// Retrieve all selected values:
			for (var j = 0; j < el.options.length; j++) {
				if (el.options[j].selected) {
					options.add(el.options[j].value);
				}
			}
			
			values[param] = options;
		} else if (el.type == "submit") {
			values[param] = value;
		} else if (el.type == "text") {
			values[param] = value;
		} else if (el.type == "textarea") {
			values[param] = value;
		}
	}
	
	// Refactor to a string if set:
	if (mode == "URL") {
		var text = "";
		
		// Create the URL:
		var first = true;
		for (param in values) {
			text += !first ? "&" : "";
			text += param + "=" + values[param].toString().escape();
			
			first = false;
		}
		
		values = text;
	// Refactor to JSON if set:
	} else if (mode == "JSON") {
		var temp = JSON.stringify(values);
		values = temp;
	}
	
	return values;
};

/**
 * @deprecated 2010-11-12
 * 
 * Retrieve all input values, defined by tag name, of an element in one string (especially used with forms).
 * 
 * @param element		The element.
 * @param tagName		The tag name to search for.
 * 
 * @return				The string.
 **/
GlobalKit.getVars = function(element, tagName) {
	var getstr = "";
	
	// Define default tag name:
	if (tagName == null) {
		tagName = "INPUT";
		
		// Retrieve all values from all input types:
		getstr = GlobalKit.getVars(element, "SELECT");
		getstr += GlobalKit.getVars(element, "TEXTAREA");
	}
	
	// Retrieve all input elements:
	inputs = element.getElementsByTagName(tagName);
	
	// Iterate through all the input elements:
	for (var i = 0; i < inputs.length; i++){
		//getstr += inputs[i].type + "\n";
		if (inputs[i].type == "checkbox") {
			if (inputs[i].checked) {
				getstr += (inputs[i].name + "=" + inputs[i].value + "&");
			}
		} else if (inputs[i].type == "hidden") {
			getstr += (inputs[i].name + "=" + inputs[i].value + "&");
		} else if (inputs[i].type == "password") {
			getstr += (inputs[i].name + "=" + inputs[i].value + "&");
		} else if (inputs[i].type == "radio") {
			if (inputs[i].checked) {
				getstr += (inputs[i].name + "=" + inputs[i].value + "&");
			}
		} else if (inputs[i].type == "select-one") {
			getstr += (inputs[i].name + "=" + inputs[i].value + "&");
		} else if (inputs[i].type == "submit") {
			getstr += ("submit=" + inputs[i].value + "&");
		} else if (inputs[i].type == "text") {
			getstr += (inputs[i].name + "=" + inputs[i].value + "&");
		} else if (inputs[i].type == "textarea") {
			getstr += (inputs[i].name + "=" + inputs[i].value.replaceAll("\n", "%0A") + "&");
		}
	}
	
	return getstr;
};

/** This variable represents all dynamically loaded files. **/
GlobalKit.loadedFiles = "";

/**
 * @deprecated 2010-01-01
 * 
 * Load a Javascript file dynamically.
 * 
 * @param				The filename.
 **/
GlobalKit._loadJS = function(filename) {
	// If file was already loaded exit:
	if (GlobalKit.loadedFiles.indexOf("[" + filename + "]") != -1) {
		return;
	}
	
	// Load the javaScript through Ajax:
	new Ajax(null, filename, null, null, function(response) { eval(response); }).request();
	
	// Store the filename:
	GlobalKit.loadedFiles += "[" + filename + "]";
};
GlobalKit.loadJS = function(filename) {
	// If file was already loaded exit:
	if (GlobalKit.loadedFiles.indexOf("[" + filename + "]") != -1) {
		return;
	}
	
	// Create a new script element:
	var fileref = document.createElement("script");
	
	// Define the script type:
	fileref.setAttribute("type", "text/javascript");
	fileref.setAttribute("src", filename);
	
	// Add to header:
	if (typeof fileref != "undefined") {
		document.getElementsByTagName("head")[0].appendChild(fileref);
		
		GlobalKit.loadedFiles += "[" + filename + "]";
	}
};

/**
 * Load a CSS file dynamically.
 * 
 * @param				The filename.
 **/
GlobalKit.loadCSS = function(filename) {
	// If file was already loaded exit:
	if (GlobalKit.loadedFiles.indexOf("[" + filename + "]") != -1) {
		return;
	}
	
	// Create a new script element:
	var fileref = document.createElement("link");
	
	// Define the script type:
	fileref.setAttribute("rel", "stylesheet");
	fileref.setAttribute("type", "text/css");
	fileref.setAttribute("href", filename);
	
	// Add to header:
	if (typeof fileref != "undefined") {
		document.getElementsByTagName("head")[0].appendChild(fileref);
		
		GlobalKit.loadedFiles += "[" + filename + "]";
	}
};

/**
 * Add a CSS rule to the current document style (p.h. css("html { background-color: red; }")).
 * 
 * @param style			The style to add to the document main style.
 **/
GlobalKit.css = function(style) {
	// Retrieve head:
	var head = document.getElementsByTagName("HEAD")[0];
	
	// Retrieve style definitions in head:
	var elements = head.getElementsByTagName("STYLE");
	
	// Retrieve main style if exists or create a new one:
	var element;
	if (elements.length == 0) {
		element = document.createElement("STYLE");
		element = head.appendChild(element);
	} else {
		element = elements[0];
	}
	
	// Add the style if not already there:
	if (element.styleSheet) {
		if (!element.styleSheet.cssText.contains(style)) {
			element.styleSheet.cssText += style;
		}
	} else if (!element.innerHTML.contains(style)) {
		element.innerHTML += style;
	}
};

/**
 * Retrieve all elements with the given class name.
 * 
 * @source <a href="http://www.dustindiaz.com/getelementsbyclass/">getElementsByClass</a>
 * 
 * @param className			The class name.
 * @param node				The element to scan in.
 * @param tag				The tags to scan.
 * 
 * @return					An array of elements.
 **/
document.getElementsByClassName = function(className, node, tag) {
	// Create the elements arrays:
	var elements = new Array();
	
	// If no class name is given:
	if (className == null) {
		return elements;
	}
	
	// Ensure a node:
	if (node == null) {
		node = document;
	}
	
	// Ensure a tag:
	if (tag == null) {
		tag = "*";
	}
	
	// Retrieve all elements:
	var totalElements = node.getElementsByTagName(tag);
	
	// Define all regular expressions to find the class names:
	var classNameRegExp = new RegExp("(^|\\s)" + className.replace(/\-/g, "\\-") + "(\\s|$)");
	
	// Scan every element for the correct class name:
	for (var i = 0; i < totalElements.length; i++) {
		if (classNameRegExp.test(totalElements[i].className)) {
			elements.push(totalElements[i]);
		}
	}
	
	return elements;
};

/** 
 * Create a DOMParser for IE (like the one used in FireFox).
 **/
if (typeof(DOMParser) == "undefined") {
	DOMParser = function() {};
	
	DOMParser.prototype.parsergbString = function(str, contentType) {
		if (typeof(ActiveXObject) != "undefined") {
			var xmldata = new ActiveXObject("MSXML.DomDocument");
			
			xmldata.async = false;
			xmldata.loadXML(str);
			
			return xmldata;
		} else if (typeof(XMLHttpRequest) != "undefined") {
			var xmldata = new XMLHttpRequest;
			
			if (!contentType) {
				contentType = "application/xml";
			}
			
			xmldata.open("GET", "data:" + contentType + ";charset=utf-8," + encodeURIComponent(str), false);
			
			if (xmldata.overrideMimeType) {
				xmldata.overrideMimeType(contentType);
			}
			
			xmldata.send(null);
			
			return xmldata.responseXML;
		}
	};
}
