/**
	This JS library contains methods which are used repeatedly throughout the system.
	If a method is specific to a given page or topic area, do NOT place it here! 
*/

var LOGINPAGE_METATOKEN = "<meta name=\"description\" content=\"login page for umbrella.net\" />";
var GLOBAL_IDPATTERN = /[^\\w]id=([^&]+)/;
var re_split_url = /^([^\?]+)\?(.+)$/;
var UMBNET_AUTOURL_COOKIEKEY = 'UMBNET_AUTOURL_COOKIEKEY';
var SERVERSTATUSPOLL = 500; // poll server status every 500ms if enabled

var ERRORREPORTING_ACTIVE = true;

var global_allowedtag_array = new Array();
global_allowedtag_array[0] = "u";
global_allowedtag_array[1] = "b";
global_allowedtag_array[2] = "strong";
global_allowedtag_array[3] = "i";
global_allowedtag_array[4] = "em";

/**
 * The object class serves as a HashMap, taking an object/string pair as key
 * and an object as value.
 * Note that passing an object as key into an associative array will cause the
 * key to be stored as "[object]"
 */	
function ObjectCache() {
	var _objectList = new Array();
	var _idList = new Array();
	
	this.clear = function() {
		_objectList.length = 0;
		_idList.length = 0;
	}

	this.put = function(obj, id, value) {
		var idx = _getIndexOf(obj);
		
		if (idx < 0) {
			idx = _objectList.push(obj) - 1;
			_idList[idx] = new Array();
		}
		
		_idList[idx][id] = value;
	}
	
	this.get = function(obj, id) {
		var idx = _getIndexOf(obj);
		if (idx >= 0) {
			return _idList[idx][id];
		}
		
		return false;
	}
	
	this.allObjects = function() {
		return _objectList;
	}
	
	this.allIds = function(obj) {
		var idx = _getIndexOf(obj);
		if (idx >= 0) {
			return _idList[idx];
		}
		return false;
	}
	
	function _getIndexOf(obj) {
		for (var i = 0; i < _objectList.length; i++) {
			if (_objectList[i] == obj) {
				return i;
			}
		}
		
		return -1;
	}
	
	
}


/* **********************************************************************************
 * Methods for positioning DOM elements
 * ********************************************************************************* */

function global_getTop(element) {
	var el = element;
	var top = el.offsetTop;
	while (el.offsetParent) {
		el = el.offsetParent;
		top += el.offsetTop;
	}
	return top;
}

function global_getLeft(element) {
	var el = element;
	var left = el.offsetLeft;
	while (el.offsetParent) {
		el = el.offsetParent;
		left += el.offsetLeft;
	}
	return left;
}

/* **********************************************************************************
 * Methods handling expando attributes, caching elements
 * ********************************************************************************* */


// Takes a map and attaches expando attributes accordingly.
// Use this method to initialize forms created using the Spring
// form tag library, which doesn't support expando attributes
function global_setAttributes() {
	var arrKey;
	try {
		var el;
		for (arrKey in DYNAMIC_ATTRIBUTES) {
			el = document.getElementById(arrKey); // Returns the first object with the specified ID or NAME
			if (el) {
				var attrMap = DYNAMIC_ATTRIBUTES[arrKey].split(";");
				for (var i = 0; i < attrMap.length; i++) {
					if (attrMap[i]) {
						var keyvalue = attrMap[i].split("=");
						el[keyvalue[0]] = keyvalue[1];
					}
				}		
			}
		}
	}
	catch (e) {
		// no dynamic attributes
	}
}

var loadedElements = new Array();

// get a DOM element, possibly from a cache
// the 'loadedElements' also holds element IDs searched but not found!
function getControl(id, includeIframes) {
	var result = loadedElements[id];
	if (result) {
		return result == "undef" ? false : result;
	}
	
	result = document.getElementById(id);
	loadedElements[id] = (result ? result : "undef");
	if (result) {
		return result;
	}

	if (includeIframes) {
		// not found in main document, travers the frames
		for (var i=0; i<frames.length; i++) {
			if (frames[i].document.getElementById(id)) {
				loadedElements[id] = frames[i].document.getElementById(id);
				break;
			}
		}
		
		loadedElements[id] = (result ? result : "undef");
		if (result) {
			return result;
		}
	}
	
	return false;
}

// beware that JS does not truely support associative arrays. Rather, assigning
// loadedElements['myelement'] will extend the loadedElements object with a new
// property 'myelement'. The length of such an array will still be 0!
function resetControlCache() {
	loadedElements = [];
}


/* **********************************************************************************
 * Methods handling events
 * ********************************************************************************* */

// attach event using IE-specific routine
// binds the specified function pointer to 
// the event. 
function addEvent(obj, eventType, func)
{
	return obj.attachEvent("on"+eventType, func);
}

var _onParsedConnectors = new Array();
var _global_parsehandler_connected = false;

function global_addOnParsed(fp) {
	if (!_global_parsehandler_connected) {
		_global_parsehandler_connected = dojo.connect(dojo.parser, "parse", _global_PageParsed);
	}
	_onParsedConnectors.push(fp);
}

// JNET-3155: if 'onParsed' method runs Dijit code which itselfs calls parser again
// we get an endless loop
function _global_PageParsed() {
	if (!_global_parsehandler_connected) {
		// just to be on the safe side, in case disconnect failed
		// and we're looping
		return;
	}
	try {
		dojo.disconnect(_global_parsehandler_connected);
		_global_parsehandler_connected = false;
		
		for (var i = 0; i < _onParsedConnectors.length; i++) {
			_onParsedConnectors[i]();
		}
	}
	catch (e) {
		var msg = "Error in _global_PageParsed: " + e.description;
		if (errors_setError) {
			errors_setError(msg, e);
		}
		else {
			window.alert(msg);
			window.status = msg;
		}
	}
}
	

var _global_pollServerStatus = false;
var _global_pollDocumentStatusHandle = false;
var _global_pollingIndicatorElement = false;
var _global_pollingStatusTextElement = false;

function global_isPollingServerStatus() {
	return _global_pollServerStatus;
}

function global_enableServerStatusPolling(idOfIndicator, idOfTextelement) {
	_global_pollServerStatus = true;
	
	_global_pollingIndicatorElement = idOfIndicator ? getControl(idOfIndicator) : false;
	_global_pollingStatusTextElement = idOfTextelement ? getControl(idOfTextelement) : false;
	
	if (!_global_pollingIndicatorElement && !_global_pollingStatusTextElement) {
		// create indicator on-the-fly
		var coverScreen = getControl('dialog_coverScreen');
		if (!coverScreen) {
			coverScreen = document.createElement("DIV");
			coverScreen.id = 'dialog_coverScreen';
			coverScreen.style.display = 'none';
			document.body.appendChild(coverScreen);
		}
		
		var containerElement = document.createElement("DIV");
		containerElement.className = 'modaldialog';
		containerElement.style.display = 'none';
		// default 'modaldialog' has centered text
		containerElement.style.textAlign = 'left';
		document.body.appendChild(containerElement);
		
		_global_pollingIndicatorElement = document.createElement("IMG");
		_global_pollingIndicatorElement.src = '../img/ajax-loader.gif';
		containerElement.appendChild(_global_pollingIndicatorElement);
		
		_global_pollingStatusTextElement = document.createElement("SPAN");
		_global_pollingStatusTextElement.style.marginLeft = "10px";
		containerElement.appendChild(_global_pollingStatusTextElement);
		
		// display coverscreen
		coverScreen.style.display = '';
		
		// display indicator section
		containerElement.style.display = '';
	}
	else {
		if (_global_pollingIndicatorElement) {
			_global_pollingIndicatorElement.style.display = '';
		}
		
		if (_global_pollingStatusTextElement) {
			_global_pollingStatusTextElement.style.display = '';
		}
	}
	
	if (!_global_pollDocumentStatusHandle) {
		// this is 'onsubmit', so the controller hasn't yet received to POST data. be sure to set the initial
		// server status when loading the form
		_global_getServerStatus();
		_global_pollDocumentStatusHandle = window.setInterval(_global_getServerStatus, SERVERSTATUSPOLL);
	}
	// else: polling is already enabled
	
	
}

function _global_getServerStatus() {
	if (!_global_pollServerStatus) {
		// this method shouldn't get called, interval handler is apparentl still active
		global_disableServerStatusPolling();
	}
	
	var result = "";
	var requestParams = new Array();
	requestParams['action'] = "getprocstatus";
	
	ajax_request({
		sync: false,
		url : 'global.ajax',
		load: _global_getServerStatus_callback,
		method: "POST",
		mimetype: 'text/plain',
		content: requestParams,
		useCache: false
	});
	
	
}

function _global_getServerStatus_callback(data, e) {
	if (data && (data.indexOf(LOGINPAGE_METATOKEN) >= 0)) {
		global_error(MSG_DOCEDITOR_LOGGEDOUT);
		return;
	}
	
	if (_global_pollingStatusTextElement) {
		_global_pollingStatusTextElement.innerText = data;
	}
}

function global_disableServerStatusPolling() {
	_global_pollServerStatus = false;
	
	if (_global_pollingIndicatorElement) {
		_global_pollingIndicatorElement.style.display = 'none';
		_global_pollingIndicatorElement = false;
	}
	if (_global_pollingStatusTextElement) {
		_global_pollingStatusTextElement.style.display = 'none';
		_global_pollingStatusTextElement.innerText = "";
		_global_pollingStatusTextElement = false;
	}
	
	if (_global_pollDocumentStatusHandle) {
		window.clearInterval(_global_pollDocumentStatusHandle);
		_global_pollDocumentStatusHandle = false;
	}
}


/* **********************************************************************************
 * String and encoding utility methods
 * ********************************************************************************* */

function global_trim(s) {
	if (!s) {
		return "";
	}
	
	// note that /^\s*(.*)\s*$/ will greedily grab any trailing whitespaces
	// so use the '?' modifier for reluctant matching
	return s.replace(/^\s*(.*?)\s*$/, "$1");
}

// encode an URL in Latin1, replacing any non-ASCII characters with escape characters
// (e.g. ü -> %FC). Tomcat will decode query parameters using ISO-8859-1 by default, while
// IE will encode URLs in UTF-8 as that is the page's content type. Use this method
// to encode URLs before setting window.location or iframe.src
function global_encodeLatin1(url) {
	var urlmatch = re_split_url.exec(url);
	//get the parameters
	
	if (!urlmatch) {
	 return url;
	}
	
	var latin1_url = urlmatch[1] + "?";
	
	// split up the query string and store in an
	// associative array
	var params = urlmatch[2].split("&");
	var queryList = new Array();
	
	for(var i = 0; i < params.length; i++) {
	    var tmp = params[i].split("=");
	    queryList.push(tmp[0] + "=" + escape(tmp[1]));
	}
	
	return latin1_url + queryList.join('&');
}

// If original text contained XML special characters (<,>,&), the text was wrapped in a CDATA
// by XmlSerializer (around line 250). XSL processing will then convert a '&nbsp;' to '&amp;nbsp;'
// use this method to convert that text back to '&nbsp;' before assigning it to 'innerHTML'
function global_decodeHtmlInCdata(cdatatext) {
	var processedHtml = cdatatext;
	
	// take any &lt;b&gt; and convert it to <b>
	for (var i = 0; i < global_allowedtag_array.length; i++) {
		var expr = '&lt;' + global_allowedtag_array[i] + '&gt;';
		processedHtml = processedHtml.replace(new RegExp(expr,'ig'), 
			"<" + global_allowedtag_array[i] + ">");

		expr = '&lt;/' + global_allowedtag_array[i] + '&gt;';
		processedHtml = processedHtml.replace(new RegExp(expr,'ig'),
			"</" + global_allowedtag_array[i] + ">");
	}
	
	processedHtml = processedHtml
						.replace(/&lt;\/?p&gt;/ig, "")
						.replace(/&amp;(\w+);/g, "&$1;");

	return processedHtml;
}

/* **********************************************************************************
 * URL and window.location methods
 * ********************************************************************************* */

function global_getIdFromUrl() {
	var result = window.location.href.match(GLOBAL_IDPATTERN);
	if (result != null) {
	    return result[1];  // Contains "http"
	}
	
	return false;
}

/* ------------------------------------------------------------------------
	Function:  global_getBaseUrl

	Synopsis:  Get the base URL including host (and, if non-standard, port)
			   from the current window , e.g.
			   http://www.umbrellanet.ch

	Arguments: 

	Returns:   void

	Notes:     
	------------------------------------------------------------------------*/	
function global_getBaseUrl() {
	return 	window.location.protocol +
			"//" + 
			window.location.host;
}


// Set the event handler for the given event.
// This will create a function copy, which then
// runs in the elements context (ths = element)
function setEventHandler(obj, eventType, func)
{
	obj["on"+eventType] = func;
}

/* ------------------------------------------------------------------------
	Function:  global_openNewWindow
	
	Synopsis:  Attempt to open an URL in a new window. If this fails
			   (popup-blocker!), open the URL in the current window.

	Arguments: url         - The target URL
			   checkcookie - if set to true, will check if this is a reload of
			                 a page with an autoload-URL
			   wndName     - If set, will attempt to load URL in that window

	Returns:   none

	Notes:     none
	------------------------------------------------------------------------*/
function global_openNewWindow(url, checkcookie, wndName) {
	if (checkcookie) {
		var lasturl = _global_getLastUrlOpen();
		if (url == lasturl) {
			return;
		}	
		_global_setLastUrlOpen(url);	
	}
	
	var windowName = wndName;
	if (targetForNewWindow && (targetForNewWindow == "_self")) {
		// probably in 'integrated mode', open windows in own window
		windowName = targetForNewWindow;
	}
	if (!windowName) {
		// no window name yet, target '_blank'
		windowName = "_blank";
	}
	
	var width = document.body.clientWidth;
	var height = document.body.clientHeight;
	var nearFullscreen = false;
	
	// if user is working in (near) fullscreen mode, make 
	// 'popup' smaller than screen
	if ((screen.availWidth - width) < 150) {
		width = screen.availWidth - 150;
		nearFullscreen = true;
	}
	if ((screen.availHeight - height) < 150) { // account for leaving away menu and toolbar
		height = screen.availHeight - 250;
		nearFullscreen = true;
	}
	
	var options = "resizable=1,status=1,menubar=0,toolbar=0,scrollbars=1";
	
	if (nearFullscreen) {
		// window will often refer to an IFRAME, so use 'top' 
		options += ",top=" + top.screenTop + ",left=" + top.screenLeft + ",width=" + width + ",height=" + height;
	}
	// else: IE will open a popup with the same dimensions as the parent window
		
	// "No such interface supported" -> http://support.microsoft.com/kb/q180176/
	// "Error not specified"  in case of an invalid URL (docedit.go?type=invoice&id=964?)
	var newwin = window.open(url, windowName, options);
	if (newwin == null) {
		if (url.indexOf('?') < 0) {
			url += "?";
		}
		else {
			url += "&";
		}
		url += "inparent=true";	
		if (parent != null) {
			parent.location.href = url;
		} else {
			top.location.href = url;
		}
	}
}

var showedLinkDisclaimer = false;
function global_followLink() {
	if (window.opener) {
		// if link was in a dojo tree, 'this' is not set correctly
		var anchor = this;
		if (!anchor || anchor.tagName != 'a') {
			anchor = event.srcElement;
		}
		
		if (!anchor.followLink) {
			return;
		}
		
		if (!showedLinkDisclaimer) {
			window.alert(LINK_WINDOW_MESSAGE);
			showedLinkDisclaimer = true;
		}
		
		window.opener.location = anchor.followLink;
		// return false to cancel event
		return false;
	}
	
}

function _global_setLastUrlOpen(url) {
	document.cookie = UMBNET_AUTOURL_COOKIEKEY + '=' + url + '; path=/';
}

function _global_getLastUrlOpen() {
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		c = c.replace(/^\s*/, "");
		if (c.indexOf(UMBNET_AUTOURL_COOKIEKEY) == 0) {
			return c.substring(UMBNET_AUTOURL_COOKIEKEY.length + 1, c.length);
		}
	}
	
	return false;
}

function global_registerTabHandler(tabcontainerid, cookiekey) {
	var tabContainer = dijit.byId(tabcontainerid);
	if (!tabContainer) {
		return
	}
	
	try {
		var previousTabId = forms_getSavedTabId(cookiekey);
		if (previousTabId) {
			var previousTab = dijit.byId(previousTabId);
			if (previousTab) {
				tabContainer.selectChild(previousTab);
			}
		}
	}
	catch (e) {
		// fail silently
	}
	
	dojo.connect(tabContainer.tablist, 'onSelectChild',
		function (evt) {
			if (evt.id) {
				forms_saveSelectedTab(cookiekey, evt.id);
			}
		}
	);
	
}

// dijit.layout.ContentPane works nicely when getting HTML via XHR and then embedding that
// into the current page. But if we need an iframe, there is not method available to lazy
// set the 'src' attribute of the iframe. The hack currently is to access the 'domNode' directly.
function global_connectTabLazyload(iframeid, src) {
	var lazyframe = dijit.byId(iframeid);
	if (lazyframe) {
		dojo.connect(lazyframe, 'onShow',
			function (evt) {
				if (!this.domNode.src) {
					this.domNode.src = src;
				}
			}
		);
	}
}

/* **********************************************************************************
 * All the other stuff
 * ********************************************************************************* */


//returns the major version as int, or false if the current browser
//is not an internet explorer
function global_getExplorerVersion() {
	// note that navigator.appVersion are useless for IE
	var agent = navigator.userAgent.toLowerCase(); // mozilla (msie 8.0)
	var matches = agent.match(/msie\s*(\d+)\.\d+/i);
 
	if (matches) {
		// match found, version will be in group 1
		return parseInt(matches[1]);
	}
 
	return false;
}


//get the rate official currency -> provided currency 
function global_getFxRate(currency, officialcurrency, fxrateArray) {
	if (officialcurrency == currency) {
		return 1;
	}
	
	if (fxrateArray[currency]) {
		return fxrateArray[currency];
	}
	
	// currency is not yet used in the order!
	var fxrate = false;
	
	var requestParams = new Array();
	requestParams['action'] = "getfxrate";
	requestParams['currency'] = currency;
	
	ajax_request({
		sync: true,
		url : 'global.ajax',
		load: function(data, e) {
				if (!data) {
					global_error(MSG_DOCEDITOR_LOGGEDOUT);
					return;
				}		
				fxrate = data;
				},
		method: "POST",
		mimetype: 'text/plain',
		content: requestParams,
		useCache: false
	});
	
	if (fxrate) {
		fxrateArray[currency] = fxrate;
		return fxrate;
	}
	
	// failed to get rate, return value such that calc won't result in NaN
	return 1;
}

// display error to the user
function global_error(message, exception) {
	if (errors_setError) {
		if (message) {
			errors_setError(message, exception);
		}
		else {
			errors_clearError();
		}
	}
	else {
		// IE7 will only allow status bar changes to trusted sites!!
		window.status = message;
	}
}

// send error data to the server, then display to the user
function global_handleError(/* Script error message */ msg, /* path to JS */ url, /* Line number in JS */ linenumber) {
	if (!ERRORREPORTING_ACTIVE) {
		return;
	}
	
	try {
		
		if (url && url.indexOf("dojo/dojo.js") >= 0) {
			// JNET-3791 .. don't report Dojo errors as they can't be traced anyway
			// 'nodeType' is null or not an object on line [16] of JS source http://www.umbrellanet.ch/prod/js/dojo/dojo/dojo.js
			return;
		}
		
		if (!window || !window.document) {
			// document was already unloaded
			return;
		}
		
		var requestParams = new Array();
		requestParams['action'] = "reporterror";
		requestParams['msg'] = msg;
		requestParams['url'] = url;
		requestParams['linenumber'] = linenumber;
		if (global_handleError.caller && global_handleError.caller.caller) {
			//.caller will usually be window.onerror
			requestParams['trace'] = global_stacktrace.getTrace(global_handleError.caller.caller); 
		}
		else {
			requestParams['trace'] = "";
		}
		
		// try to get document editor data, this might cause an exception
		if (typeof(de_document) != 'undefined') {
			try {
				requestParams['deversion'] = dataContext.deVersion;
				requestParams['eventid'] = de_document._lastCmdobj ? de_document._lastCmdobj.eventid : "";
				requestParams['eventdata'] = de_document._lastCmdobj ? de_document._lastCmdobj.eventdata : "";
				requestParams['xml'] = deltaDocument.getDeltaXml();
			}
			catch (e) {
				
			}
		}
			
		ajax_request({
			url : 'global.ajax',
			load: false,
			method: "POST",
			mimetype: 'text/plain',
			content: requestParams,
			useCache: false
		});
		
		global_error(MSG_ERROR_DOCEDITOR_ERROR_GENERAL + "<br />" +
				msg + " on line " + linenumber);
	}
	catch (e) {
		window.alert(e.description);
	}

	// stop the event from bubbling up to the default window.onerror handler
	return true;
}

var global_stacktrace = {
		
		getTrace : function(fp) {
		    var stackTraceMessage = "";
			var nextCaller = fp;
		    while(nextCaller) {
		        stackTraceMessage += "at " + this._getSignature(nextCaller) + "\n";
		        nextCaller = nextCaller.caller;
		    }
		    stackTraceMessage += "\n";
		    
		    return stackTraceMessage;
		},
		
		_getSignature : function(fp){
		    var signature = this._getFunctionName(fp);  
		    signature += "(";  
		    for(var x=0; x < fp.arguments.length; x++) {
		        // trim long arguments
		        var nextArgument = fp.arguments[x];
		        // nextArgument might be 'undefined' in case of an error
		        if(nextArgument && nextArgument.length && (nextArgument.length > 30)) {
		            nextArgument = nextArgument.substring(0, 30) + "…";
		        }
		       
		        // append the next argument to the signature
		        signature += "'" + nextArgument + "'";
		       
		        // comma separator
		        if(x < fp.arguments.length - 1) {
		            signature += ", ";  
		        }
		    }
		    signature += ")";
		    return signature;
		},
		
		_getFunctionName : function(fp) {
		    // try to parse the function name from the defintion
			var msg = "";
			var key = false;
			
		    var definition = fp.toString();
		    var name = definition.substring(definition.indexOf('function') + 8,definition.indexOf('('));
		    if (name) {
		        return name;
		    }
		   
		    // do some hard work here to find the fp name
		    try {
			    for (key in de_document) {
					if (de_document[key] == fp) {
						return key;
					}
				}
			    for (key in de_propertyEditor) {
					if (de_propertyEditor[key] == fp) {
						return key;
					}
				}
		    }
		    catch (e) {
		    	// de_document and/or de_propertyEditor are not defined
		    }
		    	
		    // sometimes there won't be a function name
		    // like for dynamic functions  
		    return "anonymous";
		}
	}

/**
 * Gets a previously in the session stored attribute.
 *  
 * @param attributename
 * @param callbackfunction
 * @return
 */
function global_consumeSessionAttribute(attributename, callbackfunction) {
	if (!callbackfunction) {
		return;
	}
	var requestParams = new Array();
	requestParams['action'] = "consumesessionparam";
	requestParams['attributename'] = attributename;
		
	ajax_request({
		url: 'global.ajax',
		sync: false,
		load: callbackfunction,
		method: "POST",
		mimetype: 'text/json',
		content: requestParams,
		useCache: false
	});
}

// *********************** Override function to make more fail-safe
/*
dijit.findWidgets = function(/*DomNode* / root){
	// summary:
	//		Search subtree under root, putting found widgets in outAry.
	//		Doesn't search for nested widgets (ie, widgets inside other widgets)
	
	var outAry = [];

	function getChildrenHelper(root){
		if (!root) {return;} // root might already have been gc'ed by IE
		
		var list = dojo.isIE ? root.children : root.childNodes, i = 0, node;
		while(node = list[i++]){
			if(!node || node.nodeType != 1){ continue; } // node might already have been gc'ed by IE
			var widgetId = node.getAttribute("widgetId");
			if(widgetId){
				var widget = dijit.byId(widgetId);
				outAry.push(widget);
			}else{
				getChildrenHelper(node);
			}
		}
	}

	getChildrenHelper(root);
	return outAry;
};
*/