function escapeFrames(url)
{
	try
	{
		if(parent.frames.length)
		{
			top.location.href = url;
		}
		else
		{
			document.location = url;
		}
	}
	catch (ex)
	{
		document.location = url;
	}
}

function getStyle(el, styleProp)
{
	var x = el;
	if(document.defaultView)
	{
		var y = document.defaultView.getComputedStyle(x, null).getPropertyValue(styleProp);
	}
	else if(x.currentStyle)
	{
		var y = eval('x.currentStyle.' + styleProp);
	}
	return y;
}

var stylesMap = new Object();
function addCSS(id, css)
{
	// Remove previous
	removeElement(stylesMap[id]);

	var styleElement = document.createElement("style");
	styleElement.type = "text/css";
	stylesMap[id] = styleElement; // Cache so we can remove later

	if(styleElement.styleSheet)
	{
		styleElement.styleSheet.cssText = "#" + id + "{" + css + "}";
	}
	else
	{
		styleElement.appendChild(document.createTextNode("#" + id + "{" + css + "}"));
	}

	document.getElementsByTagName("head")[0].appendChild(styleElement);
}

function extractStyle(style, css)
{
	var m = new RegExp(style + ":\\s*(.+?)(;|\\z)").exec(css);
	if(m && m[1])
	{
		return m[1];
	}

	return "";
}

function hasClass(el, cls)
{
	if(el && el.className)
	{
		return new RegExp("(^|\\s)" + cls + "(\\s|$)").test(el.className);
	}

	return false;
}

function getDocumentElement(name)
{
	return document.getElementById(name);
}

try
{
	// Preload images
	var i1 = new Image();
	i1.src = "/mqa/button.gif";

	var i2 = new Image();
	i2.src = "/mqa/button_hover.gif";
}
catch (e)
{
}

try
{
	window.top.resetTimer();
}
catch (e)
{
}

function getPosX(obj)
{
	var curleft = 0;
	if(obj.offsetParent)
	{
		while(obj.offsetParent)
		{
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
		}
	}
	else if(obj.x)
	{
		curleft += obj.x;
	}
	return curleft;
}

function getPosY(obj)
{
	var curtop = 0;
	if(obj.offsetParent)
	{
		while(obj.offsetParent)
		{
			curtop += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if(obj.y)
	{
		curtop += obj.y;
	}
	return curtop;
}

function getPos(el)
{
	var positionX = 0;
	var positionY = 0;
	while(el != null)
	{
		positionX += el.offsetLeft;
		positionY += el.offsetTop;
		el = el.offsetParent;
	}
	return [positionX, positionY];
}

//get width of text element
function getWidth(span)
{
	return span.offsetWidth;
}

//get height of text element
function getHeight(span)
{
	return span.offsetHeight;
}

var helpTipFrame;

function showHelpTipFrame(element, name, label, text)
{
	try
	{
		document.body.removeChild(helpTipFrame);
	}
	catch (ex)
	{
	}

	helpTipFrame = document.createElement('iframe');

	helpTipFrame.frameBorder = 0;
	helpTipFrame.width = "406px";
	helpTipFrame.height = "300px";
	helpTipFrame.scrolling = "no";
	helpTipFrame.style.zIndex = 100;
	helpTipFrame.style.display = "none";
	helpTipFrame.style.borderWidth = "0px";
	helpTipFrame.style.borderStyle = "solid";
	helpTipFrame.style.position = "absolute";
	helpTipFrame.style.cursor = "help";

	var x = getPosX(element) + 15;
	var y = getPosY(element) + 25;

	var maxX = document.body.clientWidth - 406 - 5;
	if(x > maxX)
	{
		x = maxX + document.body.scrollLeft;
	}

	helpTipFrame.style.left = x;
	helpTipFrame.style.top = y;

	helpTipFrame.src = "/mqa/frameHelpTip.jsp?name=" +
	                   escape(name) + "&label=" + escape(label) + "&text=" + escape(text);

	document.body.appendChild(helpTipFrame);

	helpTipFrame.style.display = "";
}

function findFrame(name)
{
	return findFrame2(window.top, name);
}

function findFrame2(f, name)
{
	if(f)
	{
		try
		{
			if(f.frames[name])
			{
				return f.frames[name];
			}

			for(var n = 0; n < f.frames.length; n++)
			{
				var res = findFrame2(f.frames.item(n), name);
				if(res)
				{
					return res;
				}
			}
		}
		catch (e)
		{
		}
	}

	return null;
}

function findParentFrame(name)
{
	var w = window.self;

	while(w)
	{
		var res = w.frames[name];
		if(res)
		{
			return res;
		}

		w = w.parent;
	}

	return undefined;
}

function isMSIE()
{
	return new String(window.navigator.appVersion).indexOf("MSIE") != -1;
}

function htmlEncode(s)
{
	var str = new String(s);
	str = str.replace(/&/g, "&amp;");
	str = str.replace(/</g, "&lt;");
	str = str.replace(/>/g, "&gt;");
	str = str.replace(/"/g, "&quot;");
	return str;
}

var quickSearchTimeout;

function onQuickSearchChanged(value)
{
	var value = value.toLowerCase();

	if(value.length > 0)
	{
		var frm = document.createElement('iframe');
		frm.style.position = "absolute";

		var quickSearch = document.getElementById("quickSearch");
		frm.style.left = getPosX(quickSearch) - 1 - (isMSIE() ? 0 : 1) - 83;
		frm.style.top = getPosY(quickSearch) - 2 + 22 - (isMSIE() ? 0 : 1);

		frm.style.zIndex = 100;
		frm.src = "blank.html";
		frm.frameBorder = 0;
		frm.width = "233px";
		frm.scrolling = "no";
		frm.id = "ifrm";
		frm.style.display = "none";

		document.body.appendChild(frm);

		var doc = window.frames[window.frames.length - 1].document;
		doc.write("<html><body style='margin: 0px; background-color: white; cursor: pointer'><select size='2' id='boo' style='height: 100%; font: normal bolder 12pt Verdana; cursor: pointer;' onfocus=\"try { window.parent.document.getElementById('quickSearch').focus(); } catch (e) {}\" onchange='try { window.parent.document.body.focus(); } catch (e) {} window.parent.onQuickSearchSelected(this.options[this.selectedIndex].text); window.parent.removeFrame();'>");

		for(var n = 0; n < quickSearchList.length; n++)
		{
			var element = quickSearchList[n].toLowerCase();

			if(element.substr(0, value.length) == value)
			{
				doc.write("<option>" + htmlEncode(quickSearchList[n]) + "</option>");
			}
		}

		doc.write("</select></body></html>");

		frm.style.display = "";

		removeFrame();
		quickSearchFrame = frm;

		var off = 0;
		var el = doc.getElementById('boo');
		var w = el.scrollWidth;
		if(!isMSIE())
		{
			w += 15;
		}
		if(w < 150)
		{
			w = 150;
			el.style.width = "100%";
		}
		else
		{
			off = w - 150;
		}

		quickSearchFrame.width = w;
		quickSearchFrame.style.left = getPosX(quickSearch) - 1 - (isMSIE() ? 0 : 1) - off;
	}
	else
	{
		removeFrame();
	}
}

var quickSearchFrame;

function removeFrame()
{
	try
	{
		if(quickSearchFrame) quickSearchFrame.style.display = "none";
	}
	catch (e)
	{
	}

	quickSearchFrame = null;
}

function hexEncode(txt)
{
	var hex = "";

	var pos, temp;
	for(pos = 0; pos < txt.length; pos++)
	{
		temp = txt.charCodeAt(pos).toString(16).toUpperCase();

		if(temp.length == 1)
		{
			hex += '0';
		}

		hex += temp;
	}

	return hex;
}

function hexDecode(hex)
{
	var txt = "";

	var pos, temp;
	for(pos = 0; pos < hex.length; pos += 2)
	{
		temp = String.fromCharCode(parseInt(hex.substring(pos, pos + 2), 16));

		txt += temp;
	}

	return txt;
}

function synchTab(frameName, keepQuery)
{
	var elList, i;

	// Exit if no frame name was given.
	if(frameName == null)
	{
		return;
	}

	// Check all links.

	var count = 0;

	elList = document.getElementsByTagName("A");
	for(i = 0; i < elList.length; i++)
	{
		// Check if the link's target matches the frame being loaded.

		if(elList[i].target == frameName)
		{

			// If the link's URL matches the page being loaded, activate it.
			// Otherwise, make sure the tab is deactivated.

			var s1 = elList[i].href;
			var s2 = window.frames[frameName].location.href;

			if(!keepQuery)
			{
				s1 = s1.split("?")[0];
				s2 = s2.split("?")[0];
			}

			if(s1 == s2)
			{
				elList[i].className += " activeTab";
				elList[i].blur();
				count++;
			}
			else
			{
				removeClassName(elList[i], "activeTab");
			}
		}
	}

	//
	// If we have more than once active tab run this function
	// again but this time compare using the full URL
	//

	if(count > 1)
	{
		synchTab(frameName, true);
	}
}

function setTab(name)
{
	var b = false;

	var backup = [];

	var arr = document.getElementsByTagName("A");
	for(var i = 0; i < arr.length; i++)
	{
		var el = arr[i];
		if(name == el.title)
		{
			addClassName(el, "activeTab");
			el.blur();
			b = true;
		}
		else
		{
			if(hasClass(el, "activeTab"))
			{
				removeClassName(el, "activeTab");
				backup.push(el);
			}
		}
	}

	if(!b)
	{
		for(var n = 0; n < backup.length; n++)
		{
			addClassName(backup[n], "activeTab");
		}

		if(window != window.parent)
		{
			window.parent.setTab(name);
		}
	}
}

function readCookie(name)
{
	var value = "";

	if(document.cookie.length > 0)
	{
		var valStart = document.cookie.lastIndexOf(name + "=");
		if(valStart != -1)
		{
			valStart += (name.length + 1);
			var valEnd = document.cookie.indexOf(";", valStart);
			if(valEnd == -1)
			{
				valEnd = document.cookie.length;
			}
			value = document.cookie.substring(valStart, valEnd);
		}
	}

	return value;
}

function writeCookie(name, value)
{
	writeCookie(name, value, true);
}

function writeCookie(name, value, keep)
{
	writeCookie(name, value, keep, false);
}

function writeCookie(name, value, keep, local)
{
	var cookie;
	var now = new Date();

	if(keep)
	{
		cookie = name + "=" + value + "; path=" + (local ? document.location.pathname : "/") + "; expires=Fri, 1 Jan " + (now.getFullYear() + 10) + " 00:00:00 GMT;";
	}
	else
	{
		cookie = name + "=" + value + "; path=" + (local ? document.location.pathname : "/") + ";";
	}

	document.cookie = cookie;
}

function deleteCookie(name)
{
	deleteCookie(name, false);
}

function deleteCookie(name, local)
{
	document.cookie = name + "=; path=" + (local ? document.location.pathname : "/") + "; expires=Thu, 01-Jan-70 00:00:01 GMT;";
}

function writeCookieBit(name, index, value)
{
	var current = parseInt(readCookie(name));

	if(value)
	{
		current |= (1 << index);
	}
	else
	{
		current &= ~(1 << index);
	}

	writeCookie(name, current);
}

function readCookieBit(name, index)
{
	var current = parseInt(readCookie(name));
	return (current & (1 << index)) > 0;
}

function onOver(img)
{
	var s = img.src;
	s = replaceSubstring(s, "_off", "_on");
	img.src = s;
}

function onOut(img)
{
	var s = img.src;
	s = replaceSubstring(s, "_on", "_off");
	img.src = s;
}

function escapePlus(s)
{
	return replaceSubstring(s, '+', '%2B');
}

function esc(s)
{
	return replaceSubstring(escapePlus(escape(s)), "%u20AC", "%E2%82%AC"); // Browser's escape() incorrectly encodes the Euro-symbol as %u20AC (needs to be %E2%82%AC)
}

function replaceSubstring(inputString, fromString, toString)
{
	// Goes through the inputString and replaces every occurrence of fromString with toString
	var temp = inputString;
	if(fromString == "")
	{
		return inputString;
	}
	if(toString.indexOf(fromString) == -1)
	{ // If the string being replaced is not a part of the replacement string (normal situation)
		while(temp.indexOf(fromString) != -1)
		{
			var toTheLeft = temp.substring(0, temp.indexOf(fromString));
			var toTheRight = temp.substring(temp.indexOf(fromString) + fromString.length, temp.length);
			temp = toTheLeft + toString + toTheRight;
		}
	}
	else
	{ // String being replaced is part of replacement string (like "+" being replaced with "++") - prevent an infinite loop
		var midStrings = new Array("~", "`", "_", "^", "#");
		var midStringLen = 1;
		var midString = "";
		// Find a string that doesn't exist in the inputString to be used
		// as an "inbetween" string
		while(midString == "")
		{
			for(var i = 0; i < midStrings.length; i++)
			{
				var tempMidString = "";
				for(var j = 0; j < midStringLen; j++)
				{
					tempMidString += midStrings[i];
				}
				if(fromString.indexOf(tempMidString) == -1)
				{
					midString = tempMidString;
					i = midStrings.length + 1;
				}
			}
		} // Keep on going until we build an "inbetween" string that doesn't exist
		// Now go through and do two replaces - first, replace the "fromString" with the "inbetween" string
		while(temp.indexOf(fromString) != -1)
		{
			var toTheLeft = temp.substring(0, temp.indexOf(fromString));
			var toTheRight = temp.substring(temp.indexOf(fromString) + fromString.length, temp.length);
			temp = toTheLeft + midString + toTheRight;
		}
		// Next, replace the "inbetween" string with the "toString"
		while(temp.indexOf(midString) != -1)
		{
			var toTheLeft = temp.substring(0, temp.indexOf(midString));
			var toTheRight = temp.substring(temp.indexOf(midString) + midString.length, temp.length);
			temp = toTheLeft + toString + toTheRight;
		}
	} // Ends the check to see if the string being replaced is part of the replacement string or not
	return temp; // Send the updated string back to the user
} // Ends the "replaceSubstring" function

function listEval(hexScript)
{
	eval(hexDecode(hexScript));
}

function disableControlButtons()
{
	controlButtons(true);
}

function enableControlButtons()
{
	controlButtons(false);
}

function controlButtons(value)
{
	try
	{
		getDocumentElement("Save").disabled = value;
	}
	catch (ex)
	{
	}
	try
	{
		getDocumentElement("Simulate").disabled = value;
	}
	catch (ex)
	{
	}
	try
	{
		getDocumentElement("Start").disabled = value;
	}
	catch (ex)
	{
	}
	try
	{
		getDocumentElement("Pause").disabled = value;
	}
	catch (ex)
	{
	}
	try
	{
		getDocumentElement("Resume").disabled = value;
	}
	catch (ex)
	{
	}
	try
	{
		getDocumentElement("Stop").disabled = value;
	}
	catch (ex)
	{
	}
	try
	{
		getDocumentElement("Reset").disabled = value;
	}
	catch (ex)
	{
	}
}

function focus(obj)
{
	try
	{
		obj.focus();
		obj.select();
	}
	catch (e1)
	{
		obj = getDocumentElement(obj);

		try
		{
			obj.focus();
			obj.select();
		}
		catch (e2)
		{
		}
	}
}

function openHelpWindow()
{
	window.open("/mqa/livehelp.jsp", "help", "toolbar=no,location=no,directories=no,menubar=yes,resizable=yes,scrollbars=yes,status=yes", false);
}

function getNewWindowPosition(width, height)
{
	var x = (screen.availWidth - width) / 2;
	var y = (screen.availHeight - height) / 2;

	var res = new Array();
	res[0] = x;
	res[1] = y;
	return res;
}

function openWindow(url, width, height)
{
	return openWindowInternal("toolbar=no,location=no,directories=no,menubar=no,status=no,", url, width, height);
}

function openWindowAddress(url, width, height)
{
	// TODO: also shows Google toolbar
	return openWindowInternal("toolbar=no,location=yes,directories=no,menubar=no,status=yes,", url, width, height);
}

function openWindowScrollable(url, width, height)
{
	return openWindowInternal("toolbar=no,location=no,directories=no,menubar=no,status=no,scrollbars=yes,", url, width, height);
}

function openWindowResizable(url, width, height)
{
	return openWindowInternal("toolbar=no,location=no,directories=no,menubar=no,status=no,scrollbars=yes,resizable=yes,", url, width, height);
}

function openWindowResizableNotScrollable(url, width, height)
{
	return openWindowInternal("toolbar=no,location=no,directories=no,menubar=no,status=no,scrollbars=no,resizable=yes,", url, width, height);
}

function getFramesetWindow()
{
	var w = window.self;
	while(w != window.top)
	{
		try
		{
			if(w.parent.document.body.tagName == "FRAMESET")
			{
				w = w.parent;
				break;
			}
		}
		catch (e)
		{
			break;
		}

		w = w.parent;
	}

	return w;
}

function _getWindowRegistry()
{
	var top = getFramesetWindow();
	var res = top["windowRegistry"];
	if(!res)
	{
		res = new Object();
		top["windowRegistry"] = res;
	}

	return res;
}

function openWindowInternal(s, url, width, height)
{
	if(width && height)
	{
		var x = (screen.availWidth - width) / 2;
		var y = (screen.availHeight - height) / 2;

		if(s.indexOf("scrollbars=yes") == -1)
		{
			s += "scrollbars=no,";
		}

		if(s.indexOf("resizable=yes") == -1)
		{
			s += "resizable=no,";
		}

		s += "width=" + width + ",height=" + height + ",left=" + x + ",top=" + y;
	}
	else
	{
		s += "resizable=yes,scrollbars=yes,";
	}

	var name = closeWindow(url);

	var wnd = window.open(url, "_blank", s);
	if(wnd)
	{
		wnd.focus();

		if(name)
		{
			_getWindowRegistry()[name] = wnd;
		}
	}
	else
	{
		alert("Failed to open window. You may need to disable your popup blocker.");
	}

	return wnd;
}

function closeWindow(url, script)
{
	var name;
	var pos = url.indexOf("?");
	if(pos != -1)
	{
		name = url.substring(0, pos);
		name = replaceSubstring(name, ".", "");

		var oldWindow = _getWindowRegistry()[name];
		_closeWindow(oldWindow, script);
	}

	return name;
}

function _closeWindow(w, script)
{
	if(w && !w.closed)
	{
		if(script)
		{
			eval(replaceSubstring(script, "$", "oldWindow"));
		}
		else
		{
			if(!confirm("This will close the existing window '" + w.document.title + "'.\n\nPress OK to continue closing the existing window."))
			{
				w.focus();
				throw "";
			}
		}

		w.close();
	}
}

function closeWindowOnPageExit(w)
{
	addEventHandler(window, "beforeunload", "closeWindowOnPageExitHandler", w);
}

function closeWindowOnPageExitHandler(event)
{
	var w = event["parameter"];
	if(w && w.closed)
	{
		return;
	}

	try
	{
		_closeWindow(w);
	}
	catch (e)
	{
		return "Window '" + w.document.title + "' is still open.";
	}
}

function background()
{
	if(window.parent && window.parent.frames.activeTab)
	{
		try
		{
			document.body.className = "tabBody";
		}
		catch (e)
		{
		}
	}
	else
	{
		if(window.name.indexOf('mQA_') == 0)
		{
			document.body.className = "windowBody";
		}
		else if(window.frameElement != null && window.frameElement.name == "main")
		{
			document.body.className = "mainBody";
		}
	}
}

function validOA(s)
{
	if(!s || s == "" || s == "%COLLECTOR%")
	{
		return true;
	}

	if(s.match(/^[0-9]+$/))
	{
		if(s.length > 16)
		{
			return false;
		}
	}
	else
	{
		if(s.length > 11)
		{
			return false;
		}
	}

	return s.match(/^[0-9A-Za-z\.\+_\-\!\?\s\@\*]+$/);
}

function addClassName(el, sClassName)
{
	var s = el.className;
	var p = s.split(" ");
	var l = p.length;
	for(var i = 0; i < l; i++)
	{
		if(p[i] == sClassName)
		{
			return;
		}
	}
	p[p.length] = sClassName;
	el.className = p.join(" ").replace(/(^\s+)|(\s+$)/g, "");
}

function removeClassName(el, sClassName)
{
	var s = el.className;
	var p = s.split(" ");
	var np = [];
	var l = p.length;
	var j = 0;
	for(var i = 0; i < l; i++)
	{
		if(p[i] != sClassName)
		{
			np[j++] = p[i];
		}
	}
	el.className = np.join(" ").replace(/(^\s+)|(\s+$)/g, "");
}

function getRadioValue(e)
{
	for(i = 0; i < e.length; i++)
	{
		v = e.item(i);
		if(v.checked)
		{
			return v.value;
		}
	}

	return "";
}

function getRadioID(name)
{
	var res;

	var arr = document.getElementsByTagName("INPUT");
	for(var n = 0; n < arr.length; n++)
	{
		var el = arr[n];

		if((el.type == "radio" || el.type == "RADIO") && el.getAttribute("name") == name)
		{
			if(arr[n].checked)
			{
				res = arr[n].id;
				break;
			}
		}
	}

	return res;
}

function createCenterFrame(width, height, styles, name, src)
{
	var dx = (document.body.clientWidth / 2) - (width / 2);
	var dy = (document.body.clientHeight / 2) - (height / 2);

	var frm = document.createElement("iframe");
	frm.setAttribute("name", name);
	frm.setAttribute("src", src);
	frm.setAttribute("frameborder", "0");

	// only way to set style attrs in both IE and FF.
	frm.style.cssText = "z-index: 10; position: absolute; left: " + (document.body.scrollLeft + dx) +
	                    "px; width: " + width + "px; top: " + (document.body.scrollTop + dy) + "px; height: " + height + "px; " + styles;
	document.body.appendChild(frm);
}

function trim(str)
{
	if(str == undefined)
	{
		return undefined;
	}
	return str.replace(/^\s*|\s*$/g, "");
}

function startsWith(s, w)
{
	return s && s.length >= w.length && s.substr(0, w.length) == w;
}

function endsWith(s, w)
{
	return s && s.length >= w.length && s.substr(s.length - w.length) == w;
}

function normalizeKeyword(s)
{
	var s1 = "";

	var arr1 = s.split(",");
	for(var n = 0; n < arr1.length; n++)
	{
		var s2 = "";

		var arr2 = arr1[n].split("+");
		for(var m = 0; m < arr2.length; m++)
		{
			if(arr2[m])
			{
				var s = arr2[m].toUpperCase();

				var res = "";

				for(var i = 0; i < s.length; i++)
				{
					var ch = s.substring(i, i + 1);
					if(ch.match(/[A-Z0-9|_]/))
					{
						res += ch;
					}
				}

				if(res)
				{
					if(s2)
					{
						s2 += " + ";
					}
					s2 += res;
				}
			}
		}

		if(s2)
		{
			if(s1)
			{
				s1 += ", ";
			}
			s1 += s2;
		}
	}

	return s1;
}

function emptySelectList(el)
{
	try
	{
		if(el && el.tagName == "SELECT")
		{
			while(el.length > 0)
			{
				el.removeChild(el.options[0]);
			}

			el.focus();
		}
	}
	catch (e)
	{
	}
}

function fillSelectList(s, arr, currentValue)
{
	return fillSelectList2(s, arr, arr, currentValue);
}

function fillSelectList2(s, arr1, arr2, currentValue)
{
	var b = false;

	for(var n = 0; n < arr1.length; n++)
	{
		var option = document.createElement('OPTION');
		option.innerHTML = arr1[n];
		option.value = arr2[n];

		if(arr2[n] == currentValue)
		{
			option.selected = true;
			b = true;
		}

		s.appendChild(option);
	}

	return b;
}

function createOption(title, value)
{
	var option = document.createElement("OPTION");
	option.value = value ? value : title;
	option.innerHTML = title;
	return option;
}

function setSelectOption(el, value, defaultIndex)
{
	if(el)
	{
		if(el.multiple)
		{
			var o = new Object();
			var values = value.split(",");

			for(var n = 0; n < values.length; n++)
			{
				o[trim(values[n])] = "true";
			}

			var b = false;
			var arr = el.options;
			for(var n = 0; n < arr.length; n++)
			{
				if(o[arr[n].value])
				{
					arr[n].selected = true;
					b = true;
				}
				else
				{
					arr[n].selected = false;
				}
			}

			if(!b)
            {
                if(defaultIndex != undefined)
                {
                    el.selectedIndex = defaultIndex;
                }

                return false;
            }
            else
            {
                return true;
            }
		}
		else
		{
			el.value = value;
			if(el.value != value)
			{
				if(defaultIndex != undefined)
				{
					el.selectedIndex = defaultIndex;
				}

                return false;
			}
            else
            {
                return true;
            }
		}
	}
}

function getSelectOption(el, defaultValue)
{
	if(el)
	{
		if(el.multiple)
		{
			var res = "";
			var arr = el.options;
			for(var n = 0; n < arr.length; n++)
			{
				if(arr[n].selected)
				{
					if(res)
					{
						res += ", ";
					}
					res += arr[n].value;
				}
			}

			return res ? res : defaultValue;
		}
		else
		{
			return el.value ? el.value : defaultValue;
		}
	}
	else
	{
		return defaultValue;
	}
}

function appendSelectOption(el, _innerHTML, _value, _title, _style, _select)
{
	var option = document.createElement("OPTION");
	option.value = _value;
	option.innerHTML = _innerHTML;
	if(_title)
	{
		option.title = _title;
	}
    if(_style)
    {
        option.style.cssText = _style;
    }
	el.appendChild(option);

    if(_select)
    {
        el.selectedIndex = el.options.length - 1;
    }
}

function setSelectOptions(el, options)
{
	if(isMSIE())
	{ // IE bug
		var s = el.outerHTML.replace(
			/(<SELECT.+?>)(.*)(<\/SELECT>)/im, "$1" + options + "$3");
		el.outerHTML = s;
	}
	else
	{
		el.innerHTML = options;
	}
}

function getSelectOptionElement(el)
{
	var arr = el.options;
	for(var n = 0; n < arr.length; n++)
	{
		if(arr[n].selected)
		{
			return arr[n];
		}
	}

	return undefined;
}

////

function setCaretToEnd(el)
{
	if(el.createTextRange)
	{
		var v = el.value;
		var r = el.createTextRange();
		r.moveStart('character', v.length);
		r.select();
	}
}

function insertAtEnd(el, value)
{
	el.value += value;
	setCaretToEnd(el);
}

function insertAtCursor(el, value)
{
	if(document.selection)
	{
		// IE support
		el.focus();
		var sel = document.selection.createRange();
		sel.text = value;
	}
	else if(el.selectionStart || el.selectionStart == "0")
	{
		// FF support
		var startPos = el.selectionStart;
		var endPos = el.selectionEnd;
		el.value = el.value.substring(0, startPos) + value + el.value.substring(endPos, el.value.length);
	}
	else
	{
		el.value += value;
	}
}

function getCaretPos(obj)
{
	obj.focus();

	if(obj.selectionStart)
	{
		return obj.selectionStart; //Gecko
	}
	else if(document.selection)
	{ //IE
		var sel = document.selection.createRange();
		var clone = sel.duplicate();
		sel.collapse(true);
		clone.moveToElementText(obj);
		clone.setEndPoint('EndToEnd', sel);
		return clone.text.length;
	}

	return 0;
}

function selectNode(node)
{
	var selection, range, doc, win;
	if((doc = node.ownerDocument) && (win = doc.defaultView) &&
	   typeof win.getSelection != 'undefined' && typeof doc.createRange != 'undefined' &&
	   (selection = window.getSelection()) && typeof selection.removeAllRanges != 'undefined')
	{
		range = doc.createRange();
		range.selectNode(node);
		selection.removeAllRanges();
		selection.addRange(range);
	}
	else if(document.body && typeof document.body.createTextRange != 'undefined' &&
	        (range = document.body.createTextRange()))
	{
		range.moveToElementText(node);
		range.select();
	}
}

////

function getWindowScreenLeft()
{
	return isNaN(window.screenLeft) ? window.screenX : window.screenLeft; // For IE and FF
}

function getWindowScreenTop()
{
	return isNaN(window.screenTop) ? window.screenY : window.screenTop; // For IE and FF
}

////

function addEventHandler(obj, eventType, functionName, parameter)
{
	if(!functionName)
	{
		functionName = "_" + eventType;
	}

	var fn = function(event)
	{
		event = fixEvent(event);
		if(parameter)
		{
			event["parameter"] = parameter;
		}

		var res;
		var el = event.target;
		if(el)
		{
			eval("if (el." + functionName + ") { res = el." + functionName + "(event); } else { res = " + functionName + "(event); }");
		}
		else
		{
			eval("res = " + functionName + "(event)");
		}

		try
		{
			if(res)
			{
				event.returnValue = res;
			}
		}
		catch (e)
		{
		}
		return res;
	}

	eval("obj.fn_" + functionName + " = fn");

	if(obj.addEventListener)
	{
		obj.addEventListener(eventType, fn, false);
		return true;
	}
	else if(obj.attachEvent)
	{
		return obj.attachEvent("on" + eventType, fn);
	}
	else
	{
		alert("Event handler could not be attached.");
	}
}

function removeEventHandler(obj, eventType, functionName)
{
	if(!functionName)
	{
		functionName = "_" + eventType;
	}

	eval("var fn = obj.fn_" + functionName + ";");

	if(!fn)
	{
		return;
	}

	if(obj.removeEventListener)
	{
		obj.removeEventListener(eventType, fn, false);
		return true;
	}
	else if(obj.detachEvent)
	{
		return obj.detachEvent("on" + eventType, fn);
	}
	else
	{
		alert("Event handler could not be removed.");
	}
}

function fixEvent(event)
{
	if(!event)
	{
		event = window.event;
	}
	else if(!isMSIE())
	{
		// Store current event as window.event if possible (Firefox)

		try
		{
			window.event = event;
		}
		catch (e)
		{
		}
	}

	if(event.target)
	{
		if(event.target.nodeType == 3)
		{
			event.target = event.target.parentNode
		}
	}
	else if(event.srcElement)
	{
		event.target = event.srcElement
	}

	return event;
}

function onEnterKeyRun(el, script)
{
	el.onkeydown = function(event)
	{
		event = fixEvent(event);
		var el = event.target;
		if(event.keyCode == 13 && !event.shiftKey && el.value != "")
		{
			eval(script);
		}
	};
}

function matchFirst(s, exp)
{
	var re = new RegExp(exp, "");
	var res = re.exec(s);
	if(res && res.length > 1)
	{
		return res[1];
	}
}

function searchArray(arr, o)
{
	for(var n = 0; n < arr.length; n++)
	{
		if(arr[n] == o)
		{
			return n;
		}
	}

	return -1;
}

////

function showElement(el)
{
	if(el.style.display != "")
	{
		el.style.display = "";
	}
}

function hideElement(el)
{
	if(el.style.display != "none")
	{
		el.style.display = "none";
	}
}

function getQueryStringFromFieldList(list)
{
	var s = "?";

	var arr = list.split(",");
	for(var n = 0; n < arr.length; n++)
	{
		var el = getDocumentElement(arr[n]);
		if(el)
		{
			var value = el.value;
			if(el.type == "checkbox" || el.type == "radio")
			{
				value = el.checked ? "true" : "false";
			}

			s += arr[n] + "=" + replaceSubstring(escapePlus(escape(value)), "%u20AC", "%80") + "&"; // %u20AC is Euro symbol
		}
	}

	return s;
}

function documentLocationFieldList(list, loc)
{
	var documentQueryString = "";
	if(!loc)
	{
		loc = document.location.pathname;
		documentQueryString = document.location.search;
		if(startsWith(documentQueryString, "?"))
		{
			documentQueryString = documentQueryString.substring(1);
		}
	}

	var d = new Date();
	var s = loc + getQueryStringFromFieldList(list) + d + d.getMilliseconds() + "&" + documentQueryString;
	document.location = s;
}

////

var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

function encode64(input)
{
	var output = "";
	var chr1, chr2, chr3;
	var enc1, enc2, enc3, enc4;
	var i = 0;

	do {
		chr1 = input.charCodeAt(i++);
		chr2 = input.charCodeAt(i++);
		chr3 = input.charCodeAt(i++);

		enc1 = chr1 >> 2;
		enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
		enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
		enc4 = chr3 & 63;

		if(isNaN(chr2))
		{
			enc3 = enc4 = 64;
		}
		else if(isNaN(chr3))
		{
			enc4 = 64;
		}

		output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
		         keyStr.charAt(enc3) + keyStr.charAt(enc4);
	} while(i < input.length);

	return output;
}

function decode64(input)
{
	var output = "";
	var chr1, chr2, chr3;
	var enc1, enc2, enc3, enc4;
	var i = 0;

	// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
	input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

	do {
		enc1 = keyStr.indexOf(input.charAt(i++));
		enc2 = keyStr.indexOf(input.charAt(i++));
		enc3 = keyStr.indexOf(input.charAt(i++));
		enc4 = keyStr.indexOf(input.charAt(i++));

		chr1 = (enc1 << 2) | (enc2 >> 4);
		chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
		chr3 = ((enc3 & 3) << 6) | enc4;

		output = output + String.fromCharCode(chr1);

		if(enc3 != 64)
		{
			output = output + String.fromCharCode(chr2);
		}
		if(enc4 != 64)
		{
			output = output + String.fromCharCode(chr3);
		}
	} while(i < input.length);

	return output;
}

////

function objectToString(obj)
{
	var s = "";

	for(var name in obj)
	{
		if(typeof obj[name] == "string" || typeof obj[name] == "number" || typeof obj[name] == "boolean")
		{
			s += name + "," + encode64(obj[name]) + ",";
		}
	}

	return s;
}

function stringToObject(s)
{
	var res = new Object();

	var arr = s.split(",");
	for(var n = 0; n < arr.length; n += 2)
	{
		if(arr[n + 1])
		{
			res[arr[n]] = decode64(arr[n + 1]);
		}
	}

	return res;
}

function objectToQueryString(obj)
{
	var s = "";

	for(var name in obj)
	{
		if(typeof obj[name] == "string" || typeof obj[name] == "number" || typeof obj[name] == "boolean")
		{
			s += esc(name) + "=" + esc(obj[name]) + "&";
		}
	}

	return s;
}

function evalPreprocess(ev)
{
	ev = replaceSubstring(ev, "$$", "(@)");
	ev = replaceSubstring(ev, "$", "arr[n]");
	ev = replaceSubstring(ev, "(@)", "$");
	return ev;
}

var allCache;
function findDocumentElements(condition)
{
	var res = new Array();

	if(!allCache)
	{
		allCache = document.all || document.getElementsByTagName("*");
	}

	var arr = allCache;
	for(var n = 0; n < arr.length; n++)
	{
		var ev = evalPreprocess(condition);

		try
		{
			if(eval(ev))
			{
				res[res.length] = arr[n];
			}
		}
		catch (e)
		{
			alert("Error in findDocumentElements: " + e.message + "; " + ev);
		}
	}

	return res;
}

function processDocumentElements(condition, s)
{
	var arr = findDocumentElements(condition);
	if(arr)
	{
		for(var n = 0; n < arr.length; n++)
		{
			var ev = evalPreprocess(s);

			try
			{
				eval(ev);
			}
			catch (e)
			{
				alert("Error in processDocumentElements: " + e.message + "; " + ev);
			}
		}
	}
}

function findChildren(element, condition, recursive)
{
	var res = new Array();
	getChildren(res, element, condition, recursive);
	return res;
}

function processChildren(element, condition, recursive, s)
{
	var arr = new Array();
	getChildren(arr, element, condition, recursive);
	for(var n = 0; n < arr.length; n++)
	{
		var ev = evalPreprocess(s);

		try
		{
			eval(ev);
		}
		catch (e)
		{
			alert("Error in processChildren: " + e.message + "; " + ev);
		}
	}

	return arr.length;
}

function getChildren(res, element, condition, recursive)
{
	if(element)
	{
		var arr = element.childNodes;
		for(var n = 0; n < arr.length; n++)
		{
			if(arr[n])
			{
				var ev = evalPreprocess(condition);

				try
				{
					if(eval(ev))
					{
						res[res.length] = arr[n];
					}
				}
				catch (e)
				{
					alert("Error in getChildren: " + e.message + "; " + ev);
				}

				if(recursive)
				{
					getChildren(res, arr[n], condition, recursive);
				}
			}
		}
	}
}

function isParentOf(element, possibleParent)
{
	while(element)
	{
		if(element.parentNode == possibleParent)
		{
			return true;
		}

		element = element.parentNode;
	}

	return false;
}

function findParentNode(element, condition)
{
	return _getParentNode(element.parentNode, evalPreprocess(condition));
}

function _getParentNode(element, ev)
{
	if(!element)
	{
		return null;
	}

	try
	{
		var arr = new Array(element);
		var n = 0; // Required by evalPreprocess()
		if(eval(ev))
		{
			return element;
		}
	}
	catch (e)
	{
		alert("Error in getParentNode: " + e.message + "; " + ev);
	}

	return _getParentNode(element.parentNode, ev);
}

function findNextSiblings(element, condition)
{
	var res = new Array();
	_findNextSiblings(res, element.nextSibling, evalPreprocess(condition));
	return res;
}

function _findNextSiblings(res, element, ev)
{
	if(element)
	{
		var arr = new Array(element);
		var n = 0; // Required by evalPreprocess()
		if(eval(ev))
		{
			res.push(element);
		}

		_findNextSiblings(res, element.nextSibling, ev);
	}
}

function findPreviousSiblings(element, condition)
{
	var res = new Array();
	_findPreviousSiblings(res, element.previousSibling, evalPreprocess(condition));
	return res;
}

function _findPreviousSiblings(res, element, ev)
{
	if(element)
	{
		var arr = new Array(element);
		var n = 0; // Required by evalPreprocess()
		if(eval(ev))
		{
			res.push(element);
		}

		_findNextSiblings(res, element.previousSibling, ev);
	}
}

function getFocusElement()
{
	if(isMSIE())
	{
		return document.activeElement;
	}
	return window.getSelection().focusNode;
}

function setDisabled(id, disabled)
{
	if(!document.getElementById || !document.getElementsByTagName)
	{
		return;
	}

	var nodesToDisable = {button :'', input :'', optgroup :'',
		option :'', select :'', textarea :''};

	var node, nodes;
	var div = document.getElementById(id);
	if(!div)
	{
		return;
	}

	nodes = div.getElementsByTagName('*');
	if(!nodes)
	{
		return;
	}

	var i = nodes.length;
	while(i--)
	{
		node = nodes[i];
		if(node.nodeName && node.nodeName.toLowerCase() in nodesToDisable)
		{
			node.disabled = disabled;
		}
	}
}

function writeExpandCollapseButton(id, name, title, style, onExpand, onCollapse)
{
	var labelCollapsed = ">";
	var labelExpanded = "<";

	if(isMSIE())
	{
		style += "; font-family: Wingdings; font-size: 8pt;";

		labelCollapsed = "&ecirc;";
		labelExpanded = "&eacute;";
	}

	var s = "<button id='" + id + "' name='" + name + "' title='" + title + "' style='" + style + "' onclick=\"if (!this.expanded) { this.innerHTML = '" + labelExpanded + "'; this.expanded = true; " + onExpand + " } else { this.innerHTML = '" + labelCollapsed + "'; this.expanded = false; " + onCollapse + " }\">" + labelCollapsed + "</button>";

	document.write(s);
}

// JavaScript1.2 style toString() method to inspect objects, arrays, structures, etc.
function toString12(obj)
{
	var s = "";

	if(isArray(obj))
	{
		s += "[";

		for(var n = 0; n < obj.length; n++)
		{
			s += toString12(obj[n]);
			if(n < obj.length - 1)
			{
				s += "\n";
			}
		}

		s += "]";

	}
	else if(typeof obj == "object")
	{
		if(obj.constructor == Boolean)
		{
			s += obj;
		}
		else
		{
			s += "{";

			for(var name in obj)
			{
				if(obj[name] && obj[name].constructor != Function)
				{
					if(s.length > 1)
					{
						s += ", ";
					}
					s += name + ":" + toString12(obj[name]);
				}
			}

			s += "}";
		}
	}
	else if(isString(obj))
		{
			s = "\"" + obj + "\"";
		}
		else
		{
			s = "" + obj;
		}

	return s;
}

// Returns true if the first argument is of type Array
function isArray()
{
	var arg0 = arguments[0];
	if(arg0)
	{
		if(typeof arg0 == 'object')
		{
			var con = arg0.constructor;
			if(con)
			{
				var criterion = con.toString().match(/array/i);
				return criterion != null;
			}
		}
	}

	return false;
}

// Returns true if the first argument is a string (type) or String (object)
function isString()
{
	if(typeof arguments[0] == 'string')
	{
		return true;
	}

	if(typeof arguments[0] == 'object')
	{
		var criterion = arguments[0].constructor.toString().match(/string/i);
		return criterion != null;
	}

	return false;
}

function setDescription(el, description, defaultValue)
{
	el.description = description;
	el.DV = defaultValue;

	el.getValue = function()
	{
		if(this.isEmpty())
		{
			return "";
		}
		else
		{
			return this.value;
		}
	}

	el.empty = function()
	{
		this.value = "";
		this.showDescription();
	}

	el.isEmpty = function()
	{
		return "true" == this.getAttribute("descriptionShown");
	}

	el.showDescription = function()
	{
		this.value = this.description;
		this.style.color = "#aaa";
		this.style.textDecoration = "none";
		this.setAttribute("descriptionShown", "true");
	}

	el.clearDescription = function()
	{
		this.value = this.DV ? this.DV : "";
		this.style.color = "black";
		setCaretToEnd(this);
		this.removeAttribute("descriptionShown");
	}

	el.onfocus = function()
	{
		if(this.isEmpty())
		{
			this.clearDescription();
		}
	}

	el.descriptionOnBlur = function()
	{
		if(this.value == "" || this.value == this.DV)
		{
			try
			{
				el.onchange(); // Fix for FF bug when no 'onchange' is fired for empty values
			}
			catch (e)
			{
			}

			this.showDescription();
		}
	}

	addEventHandler(el, "blur", "descriptionOnBlur");

	if(el.value == "" || el.value == defaultValue || el.value == description)
	{
		el.showDescription();
	}
	else
	{
		if(el.style.color != "red")
		{
			el.style.color = "black";
		}
	}
}

function loadURL(url, cid, resetContainer, position)
{
//	try
//	{
//		log.debug("loadURL; url: " + url + "; cid: " + cid + "; resetContainer: " + resetContainer);
//	}
//	catch(e)
//	{
//	}

	var el = getDocumentElement(cid);
	if(!el)
	{
		alert("Failed to find container '" + cid + "'.");
		return;
	}

	if(resetContainer == undefined || resetContainer == true)
	{
		el.innerHTML = "";
	}

	if(!position)
	{
		position = "";
	}
	window.setTimeout("loadURL2('" + url + "', '" + cid + "', '" + position + "');", 0);
}

function loadURL2(url, cid, position)
{
	var req = getXMLHttpRequest();
	req.onreadystatechange = function()
	{
		if(req.readyState == 4)
		{
			if(req.status == 200)
			{
				var s = req.responseText;

				if(position)
				{
					document.getElementById(cid).insertAdjacentHTML(position, s);
				}
				else
				{
					document.getElementById(cid).innerHTML = s;
				}

				// Look for <script...>...</script> in source and run it
				var arr = findSections(s, "<script type=\"text/javascript\">", "</script>");
				for(var n = 0; n < arr.length; n++)
				{
					try
					{
						eval(loadURL_FixFunctions(arr[n]));
					}
					catch (e)
					{
					}
				}

				loadURL_CSS(s);
			}
			else
			{
				try
				{
					log.debug("HTTP status: " + req.status + "\n\n" + req.responseText);
				}
				catch (e)
				{
				}

				if(isMSIE())
				{
					// Nice formatting possible with innerText

					var span = document.createElement("SPAN");
					span.innerHTML = req.responseText;
					var message = span.innerText;

					// Strip off Tomcat's error message bits
					var pos = message.indexOf("type Status");
					if(pos != -1)
					{
						message = message.substring(0, pos);
					}

					alert(message);
				}
				else
				{
					alert("HTTP status: " + req.status + "\n\n" + req.responseText);
				}
			}
		}
	};

	var s = url + (url.indexOf("?") == -1 ? "?" : "&") + new Date().toString();
	if(s.length > 2083)
	{
		// If we have a query string we try a POST instead...

		var pos = s.indexOf('?');
		if(pos == -1)
		{
			alert("URL is longer than 2083 characters.");
			return;
		}

		req.open('POST', s.substring(0, pos), true);
		req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		req.send(s.substring(pos + 1, s.length));
	}
	else
	{
		req.open('GET', s, true);
		req.send(null);
	}
}

function getXMLHttpRequest()
{
	var req = false;
	if(window.XMLHttpRequest)
	{
		req = new XMLHttpRequest();
	}
	else if(window.ActiveXObject)
	{
		try
		{
			req = new ActiveXObject("Msxml2.XMLHTTP");
		}
		catch (e)
		{
			try
			{
				req = new ActiveXObject("Microsoft.XMLHTTP");
			}
			catch (e)
			{
			}
		}
	}
	else
	{
		alert("XMLHttpRequest not available.");
	}

	return req;
}

function loadURL_FixFunctions(s)
{
	// This replaces function definitions of the form:
	//   function fn(a, b) { ... }
	// by:
	//   fn = function(a, b) { ... }
	// Required to use loaded functions globally

	var re = /function\s+(\w+)(\(.*\))/gm;

	var arr;
	while((arr = re.exec(s)) != null)
	{
		s = replaceSubstring(s, arr[0], arr[1] + " = function" + arr[2]);
	}

	return s;
}

function loadURL_CSS(s)
{
	var arr = findSections(s, "<style type=\"text/css\">", "</style>");
	for(var n = 0; n < arr.length; n++)
	{
		includeCSS(arr[n]);
	}
}

function includeCSS(css)
{
	var styles = document.createElement("style");
	styles.type = "text/css";
	styles.media = "all";

	if(styles.styleSheet)
	{
		styles.styleSheet.cssText = css;

		var head = document.getElementsByTagName('head')[0];
		head.appendChild(styles);
	}
	else
	{
		var styleSheet = document.styleSheets[document.styleSheets.length - 1];

		var arr = css.split("}");
		for(var n = 0; n < arr.length; n++)
		{
			if(arr[n])
			{
				styleSheet.insertRule(arr[n] + "}", styleSheet.cssRules.length);
			}
		}

		return;
	}
}

function findSections(s, start, end)
{
	var res = new Array();

	var p = 0;
	while(true)
	{
		var p1 = s.indexOf(start, p);
		if(p1 == -1)
		{
			break;
		}

		var p2 = s.indexOf(end, p1);
		if(p2 == -1)
		{
			break;
		}

		res[res.length] = s.substring(p1 + start.length, p2);

		p = p2;
	}

	return res;
}

if(typeof $ != "function")
{ // Don't define the following if jQuery is included as it breaks jQuery
	// insertAdjacentXXX workaround for crap Firefox
	if(typeof HTMLElement != "undefined" && !
		HTMLElement.prototype.insertAdjacentElement)
	{
		HTMLElement.prototype.insertAdjacentElement = function(where, parsedNode)
		{
			switch(where)
				{
				case 'beforeBegin':
					this.parentNode.insertBefore(parsedNode, this)
					break;
				case 'afterBegin':
					this.insertBefore(parsedNode, this.firstChild);
					break;
				case 'beforeEnd':
					this.appendChild(parsedNode);
					break;
				case 'afterEnd':
					if(this.nextSibling)
					{
						this.parentNode.insertBefore(parsedNode, this.nextSibling);
					}
					else
					{
						this.parentNode.appendChild(parsedNode);
					}
					break;
			}
		}

		HTMLElement.prototype.insertAdjacentHTML = function(where, htmlStr)
		{
			var r = this.ownerDocument.createRange();
			r.setStartBefore(this);
			var parsedHTML = r.createContextualFragment(htmlStr);
			this.insertAdjacentElement(where, parsedHTML)
		}

		HTMLElement.prototype.insertAdjacentText = function(where, txtStr)
		{
			var parsedText = document.createTextNode(txtStr)
			this.insertAdjacentElement(where, parsedText)
		}
	}

	Object.prototype.length = function()
	{
		var res = 0;
		for(var name in this)
		{
			if(this[name] && this[name].constructor != Function)
			{
				res++;
			}
		}
		return res;
	}
}

function overlay(name)
{
	var el = getDocumentElement(name);
	el.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible";
}

function abbreviatedInnerHTML(el)
{
	if(!el)
	{
		return "No element provided.";
	}

	var res = el.innerHTML;
	if(res)
	{
		var pos = res.indexOf(">");
		if(pos != -1)
		{
			res = res.substring(0, pos + 1) + " ... ";
		}
		res = replaceSubstring(res, "\" ", "\"\n");

		pos = res.indexOf(" ");
		if(pos != -1)
		{
			res += replaceSubstring(res.substring(0, pos), "<", "</") + ">";
		}
	}

	return res;
}

function includeScript(scriptSource)
{
	var s = "_" + hexEncode(scriptSource); // FF requires attribute name to be encoded, e.g. ? character not allowed
	if(!document.body.getAttribute(s))
	{
		document.body.setAttribute(s, "1");
		document.write("<script type='text/javascript' src='" + scriptSource + "'></script>");
	}
	else
	{
	}
}

function setAttribute(el, name, value)
{
	if(value)
	{
		el.setAttribute(name, value);
	}
	else
	{
		el.removeAttribute(name);
	}
}

function createFrame(url, width, height)
{
	var frm = document.createElement('iframe');

	frm.frameBorder = 0;
	frm.width = width;
	frm.height = height;
	frm.scrolling = "no";
	frm.style.zIndex = 1;
	frm.style.position = "absolute";

	frm.style.left = (document.body.scrollWidth - width) / 2 + document.body.scrollLeft / 2;
	frm.style.top = (document.body.scrollHeight - height) / 2 + document.body.scrollTop / 2;

	frm.src = url;
	frm.close = function()
	{
		document.body.removeChild(this);
	};

	document.body.appendChild(frm);

	return frm;
}

function getTableBodyRowCount(table)
{
	if(table.tagName != "TABLE")
	{
		alert("getTableBodyRowCount; Not a table: " + table.tagName);
		return -1;
	}

	var b = table.tBodies[0];
	return b.rows.length;
}

function getTableBodyColumnCount(table)
{
	if(table.tagName != "TABLE")
	{
		alert("getTableBodyColumnCount; Not a table: " + table.tagName);
		return -1;
	}

	var b = table.tBodies[0];
	return b.rows[0].cells.length;
}

function addTableBodyColumn(table, index, innerHTML, callback)
{
	if(table.tagName != "TABLE")
	{
		alert("addTableBodyColumn; Not a table: " + table.tagName);
		return;
	}

	var tblBodyObj = table.tBodies[0];
	for(var i = 0; i < tblBodyObj.rows.length; i++)
	{
		var newCell = tblBodyObj.rows[i].insertCell(index == undefined ? -1 : index);
		if(innerHTML)
		{
			newCell.innerHTML = innerHTML;
		}
		if(callback)
		{
			callback(newCell);
		}
	}
}

function deleteTableBodyColumn(table, index)
{
	if(table.tagName != "TABLE")
	{
		alert("deleteTableBodyColumn; Not a table: " + table.tagName);
		return;
	}

	var tblBodyObj = table.tBodies[0];
	for(var i = 0; i < tblBodyObj.rows.length; i++)
	{
		if(tblBodyObj.rows[i].cells.length > 1)
		{
			tblBodyObj.rows[i].deleteCell(
				index == undefined ? -1 : index);
		}
	}
}

function addTableBodyRow(table, index, innerHTML, callback)
{
	if(table.tagName != "TABLE")
	{
		alert("addTableBodyRow; Not a table: " + table.tagName);
		return;
	}

	var columnCount = getTableBodyColumnCount(table);

	var tblBodyObj = table.tBodies[0];
	var row = tblBodyObj.insertRow(
		index == undefined ? -1 : index);

	for(var n = 0; n < columnCount; n++)
	{
		var newCell = row.insertCell(-1);
		if(innerHTML)
		{
			newCell.innerHTML = innerHTML;
		}
		if(callback)
		{
			callback(newCell);
		}
		row.appendChild(newCell);
	}
}

function deleteTableBodyRow(table, index)
{
	if(table.tagName != "TABLE")
	{
		alert("deleteTableBodyRow; Not a table: " + table.tagName);
		return;
	}

	var tblBodyObj = table.tBodies[0];
	tblBodyObj.deleteRow(index == undefined ? -1 : index);
}

function processTableBodyCells(table, callback)
{
	if(table.tagName != "TABLE")
	{
		alert("processTableBodyCells; Not a table: " + table.tagName);
		return;
	}

	var tblBodyObj = table.tBodies[0];
	for(var n = 0; n < tblBodyObj.rows.length; n++)
	{
		var row = tblBodyObj.rows[n];
		for(var m = 0; m < row.cells.length; m++)
		{
			callback(row.cells[m]);
		}
	}
}

function getTableBodyCell(table, column, row)
{
	if(table.tagName != "TABLE")
	{
		alert("getTableBodyCell; Not a table: " + table.tagName);
		return;
	}

	var tblBodyObj = table.tBodies[0];

	try
	{
		return tblBodyObj.rows[row].cells[column];
	}
	catch (e)
	{
		return null;
	}
}

////

function hasParentNodeWithTagName(el, tagName, stopElement)
{
	return hasParentNodeWithTagName_(el.parentNode, tagName, stopElement);
}

function hasParentNodeWithTagName_(el, tagName, stopElement)
{
	if(!el || el == stopElement)
	{
		return false;
	}
	if(el.tagName == tagName)
	{
		return true;
	}
	return hasParentNodeWithTagName_(el.parentNode, tagName, stopElement);
}

function reload()
{
	if(isMSIE())
	{
		window.location.reload(true);
	}
	else
	{
		// Firefox bug means that even with "window.location.reload(true)" inner frames on
		// the current page are not reloaded; need to do it manually using a timestamp

		var url = window.location.toString();
		var time = new Date().getTime();
		window.location = url + (url.indexOf("?") == -1 ? "?" : "&") + time;
	}
}

function removeElement(el)
{
	if(el)
	{
		var p = el.parentNode;
		if(p)
		{
			p.removeChild(el);
		}
	}
}

function insertBefore(node, referenceNode)
{
	referenceNode.parentNode.insertBefore(node, referenceNode);
}

function insertAfter(node, referenceNode)
{
	referenceNode.parentNode.insertBefore(node, referenceNode.nextSibling);
}

function validateXML(xml)
{
	if(xml)
	{
		if(isMSIE())
		{
			var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
			xmlDoc.async = "false";
			xmlDoc.loadXML(xml);
			if(xmlDoc.parseError != 0)
			{
				throw "XML validation failed (line " + xmlDoc.parseError.line + "): " + xmlDoc.parseError.reason;
			}
		}
		else
		{
			// TODO
			//			var parser=new DOMParser();
			//			var doc= parser.parseFromString(xml, "text/xml");
		}
	}
}

function resizeWindow(w, h)
{
	try
	{
		var x = (screen.availWidth - w) / 2;
		var y = (screen.availHeight - h) / 2;

		self.resizeTo(w, h);
		self.moveTo(x, y);
	}
	catch (e)
	{
	}
}

function getWindowSize()
{
	var frameWidth, frameHeight;

	if(self.innerWidth)
	{
		frameWidth = self.innerWidth;
		frameHeight = self.innerHeight;
	}
	else if(document.documentElement && document.documentElement.clientWidth)
	{
		frameWidth = document.documentElement.clientWidth;
		frameHeight = document.documentElement.clientHeight;
	}
	else if(document.body)
		{
			frameWidth = document.body.clientWidth;
			frameHeight = document.body.clientHeight;

			return { width: frameWidth, height: frameHeight };
		}
		else
		{
			return undefined;
		}
}

function setInnerText(el, text)
{
	if(document.all)
	{
		el.innerText = text;
	}
	else
	{
		el.textContent = text;
	}
}

if(typeof $ != "function")
{ // Don't define the following if jQuery is included as it breaks jQuery
	// Javascript 1.6 - Array.indexOf for pre 1.6
	if(!Array.prototype.indexOf)
	{
		Array.prototype.indexOf = function(elt /*, from*/)
		{
			var len = this.length;

			var from = Number(arguments[1]) || 0;
			from = (from < 0)
				? Math.ceil(from)
				: Math.floor(from);
			if(from < 0)
			{
				from += len;
			}

			for(; from < len; from++)
			{
				if(from in this && this[from] === elt)
				{
					return from;
				}
			}
			return -1;
		};
	}

	// Javascript 1.6 - Array.lastIndexOf for pre 1.6
	if(!Array.prototype.lastIndexOf)
	{
		Array.prototype.lastIndexOf = function(elt /*, from*/)
		{
			var len = this.length;

			var from = Number(arguments[1]);
			if(isNaN(from))
			{
				from = len - 1;
			}
			else
			{
				from = (from < 0)
					? Math.ceil(from)
					: Math.floor(from);
				if(from < 0)
				{
					from += len;
				}
				else if(from >= len)
				{
					from = len - 1;
				}
			}

			for(; from > -1; from--)
			{
				if(from in this && this[from] === elt)
				{
					return from;
				}
			}
			return -1;
		};
	}
}

// Requires: <script type="text/javascript" src="log4javascript.js"></script>
function _log(message)
{
	try
	{
		if(!__logger)
		{
			__logger = log4javascript.getLogger("");
			__logger.setLevel(log4javascript.Level.ALL);
			__logger.addAppender(new log4javascript.PopUpAppender(true));
		}

		__logger.debug(message);
	}
	catch (e)
	{
	}
}
var __logger;

function escapeApostrophe(s)
{
	s = s.split("'").join("\\'");
	s = s.split("\n").join("\\n");
	s = s.split("\r").join("\\r");
	return s;
}

var countPushMessagePartsTimerId;
function countPushMessageParts(address, title, id)
{
	if(!id)
	{
		return getXMLRPC().invoke("countPushMessageParts", address, title);
	}
	else
	{
		if(countPushMessagePartsTimerId)
		{
			window.clearTimeout(countPushMessagePartsTimerId);
		}
		var v = "getXMLRPC().invokeAsync(function(o) { try { document.getElementById('" + id + "').innerHTML = o; countPushMessagePartsTimerId = undefined; } catch (e) {} }, 'countPushMessageParts', '" + escapeApostrophe(address) + "', '" + escapeApostrophe(title) + "');";
		countPushMessagePartsTimerId = window.setTimeout(v, 300);
	}
}

var invokeLaterArguments = new Object();
function invokeLater(code, argument, timeout)
{
	var id = new String(Math.round(Math.random() * 2147483647));
	code = replaceSubstring(code, "$", "invokeLaterArguments['" + id + "']") +
	       "; invokeLaterArguments['" + id + "'] = undefined;";
	invokeLaterArguments[id] = argument;
	window.setTimeout(code, timeout ? timeout : 100);
}

function cloneArray(oldArray)
{
	var res = new Array(oldArray.length);

	for(var i = 0; i < oldArray.length; i++)
	{
		res[i] = oldArray[i];
	}

	return res;
}

function getXMLRPC()
{
	try
	{
		return document.getElementById("xmlrpc").contentWindow.xmlrpc;
	}
	catch (ex)
	{
		return xmlrpc;
	}
}

function adaptQueryString()
{
	var start = 0;
	var qs = window.location.search.substring(1);
	if(arguments.length % 2 != 0)
	{
		qs = arguments[0];
		start = 1;
	}

	qs = new String(qs);

	for(var i = start, length = arguments.length; i < length; i += 2)
	{
		var name = arguments[i];
		if(name)
		{
			var value = arguments[i + 1];
			qs = qs.replace(new RegExp(name + "=.*?(&|$)"), "");
			if(value != undefined)
			{
				qs = esc(name) + "=" + esc(value) + "&" + qs;
			}
		}
	}

	return qs;
}

function _guid()
{
	return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
function guid()
{
	return (_guid() + _guid() + "-" + _guid() + "-" + _guid() + "-" + _guid() + "-" + _guid() + _guid() + _guid());
}

function tab_to_tab(e, el, tab)
{

	if(tab == undefined)
	{
		tab = '\t';
	}

	if(!el.id)
	{
		el.id = guid();
	}

	//A function to capture a tab keypress in a textarea and insert 4 spaces and NOT change focus.
	//9 is the tab key, except maybe it's 25 in Safari? oh well for them ...
	if(e.keyCode == 9)
	{
		var oldscroll = el.scrollTop; //So the scroll won't move after a tabbing
		e.returnValue = false; //This doesn't seem to help anything, maybe it helps for IE
		//Check if we're in a firefox deal
		if(el.setSelectionRange)
		{
			var pos_to_leave_caret = el.selectionStart + tab.length;
			//Put in the tab
			el.value = el.value.substring(0, el.selectionStart) + tab + el.value.substring(el.selectionEnd, el.value.length);
			//There's no easy way to have the focus stay in the textarea, below seems to work though
			setTimeout("var t=document.getElementById('" + el.id + "'); t.focus(); t.setSelectionRange(" + pos_to_leave_caret + ", " + pos_to_leave_caret + ");", 0);
		}
		else
		{ //Handle IE
			// IE code, pretty simple really
			document.selection.createRange().text = tab;
		}
		el.scrollTop = oldscroll; //put back the scroll
	}
}

function getDate(prefix)
{
	var year = eval("document.forms.form." + prefix + "Year.value");
	var month = eval("document.forms.form." + prefix + "Month.value");
	var day = eval("document.forms.form." + prefix + "Day.value");
	var hour = eval("document.forms.form." + prefix + "Hour.value");
	var minute = eval("document.forms.form." + prefix + "Minute.value");

	return new Date(year, (month - 1), day, hour, minute);
}

var totalCount = -1;
var memberCount = -1;
var broadcastListCount = -1;
var calculatingMemberCount = false;
var previousCommunityList;
var previousBroadcastList;

function getMemberCount()
{
	if(calculatingMemberCount)
	{
		return;
	}

	var communityList = document.forms.form.broadcastCommunities.value;
	var broadcastList;

	if(document.forms.form.broadcastList)
	{
		broadcastList = document.forms.form.broadcastList.value;
	}

	if(communityList != previousCommunityList || broadcastList != previousBroadcastList)
	{
		calculatingMemberCount = true;

		if(communityList != previousCommunityList)
		{
			var communityIds = xmlrpc.getCommunityIds(communityList);

			if(communityIds.length > 0)
			{
				memberCount = xmlrpc.getMemberCountForMultiplCommunities(communityIds, true);
			}
			else
			{
				memberCount = 0;
			}

			previousCommunityList = communityList;
		}

		if(broadcastList != previousBroadcastList)
		{
			broadcastListCount = 0;

			// add any that are in list
			var memberList = broadcastList.split("\n");

			for(var i = 0; i < memberList.length; i++)
			{
				if(memberList[i].length > 0)
				{
					broadcastListCount++;
				}
			}

			previousBroadcastList = broadcastList;
		}

		totalCount = memberCount + broadcastListCount;

		calculatingMemberCount = false;
	}

	return true;
}

function calculateTrickleRate()
{
	var trickleCheckbox = document.forms.form.trickleBroadcast;

	if(!trickleCheckbox)
	{
		trickleCheckbox = getDocumentElement('trickleBroadcast');
	}

	if(!trickleCheckbox || !trickleCheckbox.checked)
	{
		return true;
	}

	getMemberCount();

	if(!calculatingMemberCount)
	{

		var startDate = getDate("broadcastSchedule");
		var endDate = getDate("broadcastEnd");

		if(startDate >= endDate)
		{
			alert("Broadcast end date must be after broadcast schedule date");
			return false;
		}

		if(totalCount <= 1)
		{
			var div = document.getElementById("trickleRateDiv");

			div.innerHTML = "N/A";
		}
		else
		{

			var difference = endDate.getTime() - startDate.getTime();
			var interval = (difference / 1000) / totalCount;
			var unit = "second";

			if(interval >= 120 || (interval % 60) == 0)
			{
				interval /= 60;
				unit = "minute";

				if(interval >= 120 || (interval % 60) == 0)
				{
					interval /= 60;
					unit = "hour";
				}
			}

			interval = Math.round(interval);

			document.forms.form.submissionInterval.value = interval + unit.charAt(0);

			var div = document.getElementById("trickleRateDiv");

			div.innerHTML = "1 recipient every " + interval + " " + unit + (interval == 1 ? "" : "s");
		}
	}

	return true;
}

function setCharAt(str,index,chr) {
	if(index > str.length-1) return str;
	return str.substr(0,index) + chr + str.substr(index+1);
}

function replaceCurlyQuotesElement(el)
{
    if(el)
    {
	    var newValue = replaceCurlyQuotes(el.value);
	    if(el.value != newValue)
	        el.value = newValue;
    }
}

function replaceCurlyQuotes(s)
{
    // Unicode to Java string literal converter: http://www.snible.org/java2/uni2java.html

    for(var n = 0; n < s.length; n++)
    {
        var ch = s.charAt(n);
        if(ch == '\u201C'/*“*/ || ch == '\u201D'/*”*/)
            s = setCharAt(s, n, '"');
        else if(ch == '\u2018'/*‘*/ || ch == '\u2019'/*’*/)
            s = setCharAt(s, n, "'");
    }

    return s;
}

function parseVersionString (str) {
    if (typeof(str) != 'string') { return false; }
    var x = str.split('.');
    // parse from string or default to 0 if can't parse
    var maj = parseInt(x[0]) || 0;
    var min = parseInt(x[1]) || 0;
    var pat = parseInt(x[2]) || 0;
    return {
        major: maj,
        minor: min,
        patch: pat
    }
}