/******************************************************************************
 *	Copyright (c) 2004-2008 Actuate Corporation and others.
 *	All rights reserved. This program and the accompanying materials 
 *	are made available under the terms of the Eclipse Public License v2.0
 *	which accompanies this distribution, and is available at
 *		http://www.eclipse.org/legal/epl-2.0.html
 *	
 *	Contributors:
 *		Actuate Corporation - Initial implementation.
 *****************************************************************************/

/**
 * All shared utility functions should be put here.
 */

BirtUtility = function() {
};

BirtUtility.prototype = {
	/**
	 * The input control to save 'taskid'
	 */
	__task_id : 'taskid',

	/**
	 * URL parameter to indicate the client DPI setting
	 */
	__PARAM_DPI : '__dpi',

	/**
	 * @returns true if left button was pressed
	 */
	isLeftButton : function(event) {
		return Event.isLeftClick(event); // prototype library function
	},

	/**
	 * Recursively removes all element child nodes
	 * 
	 * @param element
	 *            DOM element
	 */
	removeChildren : function(element) {
		while (element.childNodes.length > 0) {
			if (element.firstChild.childNodes.length > 0) {
				this.removeChildren(element.firstChild);
			}
			element.removeChild(element.firstChild);
		}
	},

	haveTagName : function(domTree, tag) {
		return ((domTree.getElementsByTagName(tag).length == 0) ? false : true);
	},

	/**
	 * It returns the data value of a single DOM leaf node
	 */
	getDataReponseLeaf : function(leaf) {
		return (leaf[0].firstChild.data);
	},

	/**
	 * It is constructor function for creating object based on the leaf node of
	 * SOAP response DOM tree The idea is from the Element function of
	 * WRScrollLayout.js
	 * 
	 * @param it -
	 *            leaf node of SOAP response DOM tree
	 * @returns object that consists of content of the input "item"
	 */
	extractResponseLeaf : function(item, newObject) {
		var children = item.childNodes;
		var size = children.length;
		for (var i = 0; i < size; i++) {
			if (children[i].nodeType == "1") {
				var childi = children[i];
				var nName = children[i].nodeName;
				var nData = null;
				if (children[i].firstChild && children[i].firstChild.data) {
					nData = children[i].firstChild.data;
				}
				newObject[nName] = nData;
			}
		}
	},

	/**
	 * It is recursive function to find all text contents under a DOM node.
	 * 
	 * @param node -
	 *            root node of the DOM tree. We traverse recursively from here.
	 * @param resultArray -
	 *            The result are stored in this array during tree traversal.
	 * @returns void
	 */
	findAllTextchild : function(node, resultArray) {
		if (node.nodeType == 3) {
			// Text node
			resultArray[resultArray.length] = node.data;
		} else {
			// Recursively traverse the tree
			var kids = node.childNodes;
			for (var i = 0; i < node.childNodes.length; i++) {
				this.findAllTextchild(kids[i], resultArray);
			}
		}
	},

	setTextContent : function(node, textString) {

		if (node.textContent != undefined) {
			// For example, Firefox.
			node.textContent = textString;
		} else if (node.innerText != undefined) {
			// For example, IE.
			node.innerText = textString;
		}
	},

	/**
	 * Fire a simulated mouse event on an html element
	 * 
	 * @param element
	 *            html element to fire event on
	 * @param eventName -
	 *            name of mouse event such as 'click' or 'mousedown'
	 * @param ctrlKey -
	 *            optional,if present, event will be fired with control key
	 *            pressed
	 */
	fireMouseEvent : function(element, eventName, ctrlKey) {
		var evt;
		var ctrl = ctrlKey || false;
		if (element.fireEvent) {
			evt = document.createEventObject();
			evt.ctrlKey = ctrl;
			evt.button = 1;
			element.fireEvent("on" + eventName, evt);
		} else if (element.dispatchEvent) {
			evt = document.createEvent("MouseEvents");
			evt.initMouseEvent(eventName, false, true, undefined, undefined, 0,
					0, 0, 0, ctrl, false, false, false, 1, undefined);
			element.dispatchEvent(evt);
		} else {
			return null;
		}
		return evt;
	},

	/**
	 * @returns boolean true if checked, false otherwise
	 */
	setCheckBoxChecked : function(checkbox, checked) {
		// Both IE and Firefox have defaultChecked property. But they use it
		// differently.
		// On Firefox, the GUI box is checked when checkbox.checked is true.
		// It is not true for IE. On IE, we also need to set
		// checkbox.defaultChecked.
		// I guess when the checkbox is FIRST shown, IE uses
		// checkbox.defaultChecked to determine the GUI.
		checkbox.defaultChecked = checked;
		checkbox.checked = checked;
		return checked;
	},

	/**
	 * Get a parameter specified in the URL. For example, if a URL is
	 * http://localhost:8080/iportal/wr?__report=/16.webrptdesign&iPortalID=YPTDAGCNPOYSOS
	 * getURLParameter(iPortalID) will return &iPortalID=YPTDAGCNPOYSOS
	 */
	getURLParameter : function(url, parameterName) {
		var paramString = "";
		var paramStartIndex = url.indexOf(parameterName);
		var equalSign = url.charAt(paramStartIndex + parameterName.length);

		if (paramStartIndex >= 0 && (equalSign == "=" || equalSign == "&")) {
			var paramEndIndex = url.indexOf("&", paramStartIndex);
			if (paramEndIndex >= 0) {
				paramString = "&"
						+ url.substring(paramStartIndex, paramEndIndex);
			} else {
				paramString = "&" + url.substring(paramStartIndex); // get the
																	// substring
																	// till the
																	// end.
			}
		}
		return paramString;
	},

	/**
	 * Adds a parameter to a given URL.
	 */
	addURLParameter : function(url, parameterName, parameterValue) {
		var paramPart = encodeURI(parameterName) + "="
				+ encodeURI(parameterValue)
		var lastChar = url.charAt(url.length - 1);
		if (lastChar != "&" && lastChar != "?") {
			if (url.indexOf("?") > 0) {
				paramPart = "&" + paramPart;
			} else {
				paramPart += "?" + paramPart;
			}
		}

		var anchorPos = url.lastIndexOf("#");
		if (anchorPos >= 0) {
			// insert the param part before the anchor
			url = url.substr(url, anchorPos - 1) + paramPart
					+ url.substr(anchorPos);
		} else {
			url += paramPart;
		}
		return url;
	},

	/**
	 * Deletes a parameter specified in the given URL. If for example the given
	 * URL is the following http://localhost/myUrl?param1=2&param2=3&param3=4
	 * and the value of parameterName is "param2", the resulting URL will be:
	 * http://localhost/myUrl?param1=2&param3=4
	 * 
	 * @param url
	 *            url to process
	 * @param parameterName
	 *            parameter to remove
	 * @return processed url
	 */
	deleteURLParameter : function(url, parameterName) {
		var reg = new RegExp("([&|?]{1})"
				+ escape(encodeURIComponent(parameterName)) + "\s*=[^&|^#]*",
				"gi");
		return url.replace(reg, "$1");
	},

	/**
	 * Removes the URL anchor.
	 */
	deleteURLAnchor : function(url) {
		return url.replace(/#[a-zA-Z0-9\-_\$]*$/, "");
	},

	/**
	 * Creates a hidden input form field.
	 * 
	 * @param formObj
	 *            form object
	 * @param paramName
	 *            parameter name
	 * @param paramValue
	 *            parameter value
	 * @return the newly created input element
	 */
	addHiddenFormField : function(formObj, paramName, paramValue) {
		var param = document.createElement("INPUT");
		formObj.appendChild(param);
		param.TYPE = "HIDDEN";
		param.name = paramName;
		param.value = paramValue;
		return param;
	},

	/**
	 * Insert a string into the cursor position of a textarea.<b> textarea: DOM
	 * element of textarea to be inserted. string: string to be inserted into
	 * the textarea.
	 */
	insertStringCursorPosTextarea : function(textarea, string) {
		if (textarea.selectionStart != undefined) {
			// Firefox
			var startPos = textarea.selectionStart;
			var endPos = textarea.selectionEnd;
			textarea.value = textarea.value.substring(0, startPos) + string
					+ textarea.value.substring(endPos, textarea.value.length);
		} else {
			// IE
			textarea.focus();
			var range = document.selection.createRange();
			range.text = string;
		}
	},

	/**
	 * Insert a string into the specified position of a textarea.<b> textarea:
	 * DOM element of textarea to be inserted. pos: For Firefox, it is an
	 * integer. For IE, it is an TextRange object. string: string to be inserted
	 * into the textarea.
	 */
	insertStringTextarea : function(textarea, pos, string) {
		if (textarea.selectionStart != undefined) {
			// Firefox

			if (pos == undefined || pos == null)
				pos = 0;

			textarea.value = textarea.value.substring(0, pos) + string
					+ textarea.value.substring(pos, textarea.value.length);
		} else {
			// IE

			textarea.focus();

			if (pos == undefined || pos == null)
				pos = document.selection.createRange();

			pos.text = string;
		}
	},

	// get the cursor position of the textarea
	getCursorPosTextarea : function(textarea) {
		if (textarea.selectionStart != undefined) {
			// Firefox
			return (textarea.selectionEnd);
		} else {
			// IE
			textarea.focus();
			return (document.selection.createRange());
		}
	},

	// IE and Firefox behave so differently to insert a string into cursor
	// position of text area.
	// So, we create this preferred method. This method subsequently call other
	// methods of string insertion based on browser type.
	preferredInsertStringTextarea : function(textarea, pos, string) {
		if (textarea.selectionStart != undefined) {
			// Firefox
			this.insertStringCursorPosTextarea(textarea, string);
		} else {
			// IE
			this.insertStringTextarea(textarea, pos, string);
		}
	},

	// id: tableId
	getSelectedTableColumns : function(id) {
		var handlerObject = ReportComponentIdRegistry.getObjectForId(id);
		var tableInstance = handlerObject.selectionManager
				.getTableInstanceById(id);
		var table = $(id);
		var iid = table.iid;

		// check that there is at least one column selected
		var selectedColumns = tableInstance.getRLOrderedSelectedColumns();
		if (!selectedColumns) {
			// #IV TODO integrate with IV error handling
			throw new WRError("WRReportTable",
					"Must have one or more columns to apply format");
		}

		return selectedColumns;
	},

	// trim left blanks
	ltrim : function(str) {
		return str.replace(/^\s*/, '');
	},

	// trim right blanks
	rtrim : function(str) {
		return str.replace(/\s*$/, '');
	},

	// trim left and right blanks
	trim : function(str) {
		return this.rtrim(this.ltrim(str));
	},

	// set button if disabled
	setButtonsDisabled : function(target, flag) {
		if (!target)
			return;

		var oTarget = $(target);
		var oIEC;

		if (oTarget)
			oIEC = oTarget.getElementsByTagName("INPUT");

		if (oIEC) {
			for (var i = 0; i < oIEC.length; i++) {
				oIEC[i].disabled = flag;
			}
		}
	},

	// set current task id
	setTaskId : function(id) {
		var taskid;
		if (id) {
			// if pass id
			taskid = id;
		} else {
			// use time stamp
			var d = new Date();
			taskid = d.getFullYear();
			taskid += "-" + d.getMonth();
			taskid += "-" + d.getDate();
			taskid += "-" + d.getHours();
			taskid += "-" + d.getMinutes();
			taskid += "-" + d.getSeconds();
			taskid += "-" + d.getMilliseconds();
		}

		// find taskid input control
		var oTaskId = $(this.__task_id);
		if (oTaskId)
			oTaskId.value = taskid;

		return taskid;
	},

	// get current task id
	getTaskId : function() {
		// find taskid input control
		var oTaskId = $(this.__task_id);
		if (oTaskId)
			return this.trim(oTaskId.value);

		return "";
	},

	// clear current task id
	clearTaskId : function() {
		// find taskid input control
		var oTaskId = $(this.__task_id);
		if (oTaskId)
			oTaskId.value = '';
	},

	// get current page number
	getPageNumber : function() {
		var oPage = $('pageNumber');
		var pageNum = 0;
		if (oPage)
			pageNum = parseInt(this.trim(oPage.innerHTML));

		return pageNum;
	},

	// get total page number
	getTotalPageNumber : function() {
		var pageNum = 0;
		var oPage = $('totalPage');
		if (oPage) {
			pageNum = (oPage.firstChild.data == '+') ? '+'
					: parseInt(oPage.firstChild.data);
		}

		return pageNum;
	},

	/**
	 * Checks the given page range syntax is valid, and if the page exist.
	 * 
	 * @param range
	 *            in the format "1-4,6,7"
	 * @return true if the range is correct
	 */
	checkPageRange : function(range) {
		if (!range) {
			return false;
		}
		var myRange = birtUtility.trim(range);

		// check if the range format is correct
		if (!myRange
				.match(/^[0-9]+\s*(-\s*[0-9]+)?(\s*,\s*[0-9]+(-[0-9]+)?)*$/)) {
			return false;
		}

		var lastPage = this.getTotalPageNumber();

		// split the range parts
		var parts = myRange.split(",");
		for (i = 0; i < parts.length; i++) {
			var part = parts[i];
			var boundaries = part.split("-");

			// page range
			if (boundaries.length == 2) {
				var pageStart = parseInt(boundaries[0]);
				var pageEnd = parseInt(boundaries[1]);
				if (isNaN(pageStart) || isNaN(pageEnd) || pageEnd <= pageStart
						|| pageStart < 1 || pageEnd < 1 || pageStart > lastPage
						|| pageEnd > lastPage) {
					return false;
				}
			}
			// single page number
			else if (boundaries.length == 1) {
				var pageNum = parseInt(boundaries[0]);
				if (isNaN(pageNum) || pageNum < 1 || pageNum > lastPage) {
					return false;
				}
			}
			// invalid format
			else {
				return false;
			}
		}
		return true;
	},

	/**
	 * Adds the current session id to the given url and returns it. If a session
	 * id already exists in the url, does nothing.
	 * 
	 * @return processed url
	 */
	initSessionId : function(url) {
		// remove existing session id from the URL
		url = birtUtility.deleteURLParameter(url, Constants.PARAM_SESSION_ID);

		// add session id in SOAP URL
		if (Constants.viewingSessionId) {
			url = birtUtility.addURLParameter(url, Constants.PARAM_SESSION_ID,
					Constants.viewingSessionId);
		}
		return url;
	},

	/**
	 * Initialize the client DPI setting
	 * 
	 * @param, url
	 * @return, string
	 */
	initDPI : function(url) {
		var href;

		try {
			if (url) {
				href = url;
			} else {
				href = document.location.href;
			}

			/*
			 * var dpi; if( screen.deviceXDPI ) dpi = screen.deviceXDPI;
			 */

			var tmpNode = document.createElement("DIV");
			tmpNode.style.cssText = "width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden";
			document.body.appendChild(tmpNode);
			var dpi = parseInt(tmpNode.offsetHeight);
			tmpNode.parentNode.removeChild(tmpNode);

			if (dpi) {
				/*
				 * var reg = new RegExp( "([&|?]{1}" + this.__PARAM_DPI +
				 * "\s*)=([^&|^#]*)", "gi" ); if( href.search( reg ) < 0 ) href =
				 * href + "&" + this.__PARAM_DPI + "=" + dpi;
				 */
				href = birtUtility.deleteURLParameter(href, this.__PARAM_DPI);
				href = href + "&" + this.__PARAM_DPI + "=" + dpi;
			}
		} catch (e) {
		}

		return href;
	},

	/**
	 * Move selected items up
	 */
	moveSelectedItemsUp : function(list) {
		if (!list || list.selectedIndex < 0)
			return;

		var i = 0;
		var value = "";
		var text = "";
		for (i = 0; i < (list.options.length - 1); i++) {
			if (!list.options[i].selected && list.options[i + 1].selected) {
				value = list.options[i].value;
				text = list.options[i].text;
				list.options[i] = new Option(list.options[i + 1].text,
						list.options[i + 1].value);
				list.options[i].selected = true;
				list.options[i + 1] = new Option(text, value);
			}
		}
	},

	/**
	 * Move selected items down
	 */
	moveSelectedItemsDown : function(list) {
		if (!list || list.selectedIndex < 0)
			return;

		var i = 0;
		var value = "";
		var text = "";
		for (i = list.options.length - 1; i > 0; i--) {
			if (!list.options[i].selected && list.options[i - 1].selected) {
				value = list.options[i].value;
				text = list.options[i].text;
				list.options[i] = new Option(list.options[i - 1].text,
						list.options[i - 1].value);
				list.options[i].selected = true;
				list.options[i - 1] = new Option(text, value);
			}
		}
	},

	/**
	 * Formats the given messages by putting the given values in the
	 * placeholders. The placeholder format is {0}, {1}, ...
	 * 
	 * @param message
	 *            template text containing placeholders
	 * @param params
	 *            an array of values to put into the placeholders, or a single
	 *            string
	 * @return formatted text
	 */
	formatMessage : function(message, params) {
		if (!message) {
			return;
		}

		if (!params) {
			return message;
		}

		if (!(params.constructor == Array)) {
			params = new Array(params);
		}

		for (i = 0; i < params.length; i++) {
			var pattern = new RegExp("\\\{" + i + "\\\}", "g");
			message = message.replace(pattern, params[i]);
		}
		return message;
	},

	/**
	 * This method is a workaround for a Mozilla/Firefox bug which prevents some
	 * HTML elements to be resized properly when a contained element's "display"
	 * style has been changed. The refresh is done by moving the element back
	 * and forth one pixel.
	 */
	refreshElement : function(htmlElement) {
		var currentLeft = parseInt(htmlElement.style.left);
		var currentTop = parseInt(htmlElement.style.top);
		// shake it!
		htmlElement.style.left = (currentLeft - 1) + "px";
		htmlElement.style.top = (currentTop - 1) + "px";
		htmlElement.style.left = currentLeft + "px";
		htmlElement.style.top = currentTop + "px";
	},

	/**
	 * Do html decode for input string
	 * 
	 */
	htmlDecode : function(str) {
		if (!str)
			return null;

		// Replaces all HTML encoded string with original character.
		str = str.replace(/&#09;/g, "\t");
		str = str.replace(/<br>/g, "\n");
		str = str.replace(/&#13;/g, "\r");
		str = str.replace(/&#32;/g, " ");
		str = str.replace(/&#34;/g, "\"");
		str = str.replace(/&#39;/g, "'");
		str = str.replace(/&#60;/g, "<");
		str = str.replace(/&#62;/g, ">");
		str = str.replace(/&#96;/g, "`");
		str = str.replace(/&#38;/g, "&");
		str = str.replace(/&#92;/g, "\\");
		str = str.replace(/&#47;/g, "/");

		return str;
	},

	_TABBABLE_TAGS : new Array("A", "BUTTON", "TEXTAREA", "INPUT", "IFRAME",
			"SELECT"),

	/**
	 * Disables the tab indexs for all the tabbable elements which are children
	 * of the given element.
	 * 
	 * @param element
	 *            element
	 */
	disableTabIndexes : function(element) {
		for (var j = 0; j < this._TABBABLE_TAGS.length; j++) {
			var tagElements = element
					.getElementsByTagName(this._TABBABLE_TAGS[j]);
			for (var k = 0; k < tagElements.length; k++) {
				var el = tagElements[k];
				el._tabIndexSaved = el.tabIndex;
				el.tabIndex = "-1";
			}
		}
	},

	/**
	 * Restores the tab indexs for all the tabbable elements which are children
	 * of the given element.
	 * 
	 * @param element
	 *            element
	 */
	restoreTabIndexes : function(element) {
		for (var j = 0; j < this._TABBABLE_TAGS.length; j++) {
			var tagElements = element
					.getElementsByTagName(this._TABBABLE_TAGS[j]);
			for (var k = 0; k < tagElements.length; k++) {
				var el = tagElements[k];
				if (el._tabIndexSaved) {
					el.tabIndex = el._tabIndexSaved;
					el._tabIndexSaved = null;
				} else {
					el.tabIndex = null;
				}
			}
		}
	},

	/**
	 * Returns the HEAD element of the page.
	 */
	getHeadElement : function() {
		if (!this._headElement) {
			this._headElement = document.getElementsByTagName("head")[0];
		}
		return this._headElement;
	},

	/**
	 * Adds a style sheet into the managed document.
	 * 
	 * @param styleContent
	 *            style sheet content
	 */
	addStyleSheet : function(styleContent) {

		var element = document.createElement("style");
		element.type = "text/css";
		if (element.styleSheet) {
			element.styleSheet.cssText = styleContent;
		} else {
			element.appendChild(document.createTextNode(styleContent));
		}
		this.getHeadElement().appendChild(element);
	},

	noComma : "" // just to avoid javascript syntax errors
}

var birtUtility = new BirtUtility();

/**
 * Extend prototype's Event. TODO: probably need a prototype extension.
 */
//Bug410281: Event.prototype is not writable in IE8.
if (BrowserUtility.isIE && !BrowserUtility.isIE8) {
	Event.prototype = Object.extend(Event, {
		/**
		 * Extension to prototype 'Event' since Event.stop(event) isn't stopping
		 * in ie
		 */
		stop : function(event) {
			event.cancelBubble = true;

			if (event.preventDefault) {
				event.preventDefault();
				event.stopPropagation();
			} else {
				event.returnValue = false;
			}
		},

		/**
		 * Stops click from propigating without using .bindAsEventListener
		 */
		colClickStop : function(e) {
			if (!e)
				var e = $("Document").contentWindow.event;
			debug(e.type);
			Event.stop(e);
		}
	});
}
